diff --git a/.gitignore b/.gitignore index 505c52f286..58dfab0bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -71,10 +71,18 @@ /tools/MapleInvalidItemWithNoNameFetcher/dist/ /tools/MapleInvalidItemWithNoNameFetcher/nbproject/ +/tools/MapleMapFieldLimitChecker/build/ +/tools/MapleMapFieldLimitChecker/dist/ +/tools/MapleMapFieldLimitChecker/nbproject/ + /tools/MapleMapInfoRetriever/build/ /tools/MapleMapInfoRetriever/dist/ /tools/MapleMapInfoRetriever/nbproject/ +/tools/MapleMapLootLimitChecker/build/ +/tools/MapleMapLootLimitChecker/dist/ +/tools/MapleMapLootLimitChecker/nbproject/ + /tools/MapleMesoFetcher/build/ /tools/MapleMesoFetcher/dist/ /tools/MapleMesoFetcher/nbproject/ diff --git a/Dockerfile b/Dockerfile index 70178f3236..070ffa17cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,4 +6,4 @@ WORKDIR /mnt COPY ./ ./ RUN sh ./posix-compile.sh EXPOSE 8484 7575 7576 7577 -CMD exec tini -- sh ./docker-launch.sh \ No newline at end of file +CMD exec tini -- sh ./posix-launch.sh \ No newline at end of file diff --git a/README.md b/README.md index e3411185f3..7ba64be0d3 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,14 @@ Besides myself for maintaining this repository, credits are to be given to Wizet Regarding distributability and usage of the code presented here: like it was before, this MapleStory server is open-source. By that, it is meant that anyone is **free to install, use, modify and redistribute the contents**, as long as there is **no kind of commercial trading involved** and the **credits to the original creators are maintained** within the codes. -This is a NetBeans 8.0.2 Project, that MUST be built and run on Java 7 (JDK/JRE 1.7.0_79+) in order to run properly. This means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application. +This is a NetBeans 8.2 Project, that should be built and run on Java 8 in order to run properly (used to be ran in Java 7, thanks @kolakcc for the Java 8 support!). + +Being a NetBeans 8.2 Project, this means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application. In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though! +- In other case, as fallback from the provided ones, consider using **whole clean set**. Selecting part of the provided ones to play pretty much *may eventually* lead to unexpected issues. + The main objective of this project is to try as best as possible to recreate what once was the original MapleStory v83, while adding up some flavors that spices up the gameplay. In other words, aim to get the best of the MapleStory of that era. --- @@ -19,17 +23,21 @@ Server files: https://github.com/ronancpl/HeavenMS Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V4MYVJ0TWIxd05hYUk -Java7 SDK: https://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html +Java 8 SDK & NetBeans bundle: https://www.oracle.com/technetwork/pt/java/javase/downloads/jdk-netbeans-jsp-3413153-ptb.html **Important note about localhosts**: these executables are red-flagged by antivirus tools as __potentially malicious softwares__, this happens due to the reverse engineering methods that were applied onto these software artifacts. Those depicted here have been put to use for years already and posed no harm so far, so they are soundly assumed to be safe. - Latest localhost: https://hostr.co/itrvrHapvtEg + Latest localhost: https://hostr.co/amuX5SLeeVZx The following list, in bottom-up chronological order, holds information regarding all changes that were applied from the starting localhost used in this development. Some lines have a link attached, that will lead you to a snapshot of the localhost at that version of the artifact. Naturally, later versions holds all previous changes along with the proposed changes. **Change log:** - * Fixed Monster Magnet crashing the caster when trying to pull-in bosses. + * Cleared need for administrator privileges (OS) to play the game, credits to Ubaware. + + * Set a higher cap for AP assigning with AP Reset, credits to Ubaware. + + * Fixed Monster Magnet crashing the caster when trying to pull bosses. Drawback: Dojo HPBar becomes unavailable. https://hostr.co/SvnSKrGzXhG0 * Fixed some 'rn' problems with quest icons & removed "tab" from party leader changed message. https://hostr.co/tsYsQzzV6xT0 @@ -131,18 +139,14 @@ Firstly, install all the general tools required to run the server: * mysql-query-browser.msi -> MySQL client component, visually shows the DB data and hubs queries. * hamachi.msi -> used for establishing a tunnelling route for the server/client communication. - -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. +Now install the Java 8 SDK & NetBeans bundle: +* jdk-8u111-nb-8_2-windows-x64.exe -> It's a NetBeans project, use other IDE at your own risk. Now that the tools have been installed, test if they are working. For WampServer: -* Once you're done installing it, run it and you will see the Wamp icon on the bottom right corner. -Left click it and click 'Put Online'. +* Once you're done installing it, run it and you will see the Wamp icon on the bottom right corner. Left click it and click 'Put Online'. * In case of ORANGE ICON, change port 80 at "httpd.conf" to another, as it clashes with a Windows default port. Then Left click it again and click 'Start All Services'. * The Wamp icon must look completely green (if its orange or red, you have a problem). @@ -161,7 +165,9 @@ For expediency, "HeavenMS-master" folder on this guide will be referred just as Setting up the SQL: open MySQL Query Browser, then create a new session with the parameters below, then click OK. -Server Host: localhost Port: 3306 Username: root +* Server Host: localhost +* Port: 3306 +* Username: root Now it must be done CAREFULLY: @@ -183,9 +189,7 @@ Now open NetBeans, and click "Open a project..." . Select then the "HeavenMS" fo Inside the project, you may encounter some code errors. -Firstly, a **new Java7 platform** must be defined to run the server. Click "Manage Platforms...", then "Add platform", browse through "C:\Program Files\Java" for the JDK 1.7 folder. Then, name this new platform "JDK 1.7". - -In case errors still show up, these errors probably occurs because you have yet to set the core JARs of the project. From the project hierarchy, right-click the project and select "Resolve Project Problems". +If that's the case, you have yet to set the core JARs of the project. From the project hierarchy, right-click the project and select "Resolve Project Problems". Locate the folder "cores" inside the root directory of this project and manually configure the missing files on NetBeans (mina-core, slf4j-api, ...). @@ -247,7 +251,7 @@ To change a character's GM level, make sure that character is not logged in, the --- ### Some notes about WZ/WZ.XML EDITING -NOTE: Be extremely wary when using server-side's XMLs data being reimporting into the client's WZ, as some means of synchronization between the server and client modules, this action COULD generate some kind of bugs afterwards. Client-to-server data reimporting seems to be fine, though. +NOTE: Be extremely wary when using server-side's XMLs data being reimported into the client's WZ, as some means of synchronization between the server and client modules, this action COULD generate some kind of bugs afterwards. Client-to-server data reimporting seems to be fine, though. #### Editing the v83 WZ's: diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000000..35125c96f4 --- /dev/null +++ b/config.yaml @@ -0,0 +1,459 @@ +#World Name: (0 "Scania", 1 "Bera", 2 "Broa", 3 "Windia", 4 "Khaini", 5 "Bellocan", 6 "Mardia", 7 "Kradia", 8 "Yellonde", 9 "Demethos", 10 "Galicia", 11 "El Nido", 12 "Zenith", 13 "Arcenia", 14 "Kastia", 15 "Judis", 16 "Plana", 17 "Kalluna", 18 "Stius", 19 "Croa", 20 "Medere") +#Flag types: (0 = nothing, 1 = event, 2 = new, 3 = hot) +#Recommended to use only up to 15 worlds +worlds: + #Properties for Scania 0 + - flag: 0 + server_message: Welcome to Scania! + event_message: Scania! + why_am_i_recommended: Welcome to Scania! + channels: 3 + exp_rate: 10 + meso_rate: 10 + drop_rate: 10 + boss_drop_rate: 10 #NOTE: Boss drop rate OVERRIDES common drop rate, for bosses-only. + quest_rate: 5 #Multiplier for Exp & Meso gains when completing a quest. Only available when USE_QUEST_RATE is true. Stacks with server Exp & Meso rates. + fishing_rate: 10 #Multiplier for success likelihood on meso thrown during fishing. + travel_rate: 10 #Means of transportation rides/departs using 1/N of the default time. + + #Properties for Bera 1 + - flag: 0 + server_message: Welcome to Bera! + event_message: Bera! + why_am_i_recommended: Welcome to Bera! + channels: 3 + + #Properties for Broa 2 + - flag: 0 + server_message: Welcome to Broa! + event_message: Broa! + why_am_i_recommended: Welcome to Broa! + channels: 3 + + #Properties for Windia 3 + - flag: 0 + server_message: Welcome to Windia! + event_message: Windia! + why_am_i_recommended: Welcome to Windia! + channels: 3 + + #Properties for Khaini 4 + - flag: 0 + server_message: Welcome to Khaini! + event_message: Khaini! + why_am_i_recommended: Welcome to Khaini! + channels: 3 + + #Properties for Bellocan 5 + - flag: 0 + server_message: Welcome to Bellocan! + event_message: Bellocan! + why_am_i_recommended: Welcome to Bellocan! + channels: 3 + + #Properties for Mardia 6 + - flag: 0 + server_message: Welcome to Mardia! + event_message: Mardia! + why_am_i_recommended: Welcome to Mardia! + channels: 3 + + #Properties for Kradia 7 + - flag: 0 + server_message: Welcome to Kradia! + event_message: Kradia! + why_am_i_recommended: Welcome to Kradia! + channels: 3 + + #Properties for Yellonde 8 + - flag: 0 + server_message: Welcome to Yellonde! + event_message: Yellonde! + why_am_i_recommended: Welcome to Yellonde! + channels: 3 + + #Properties for Demethos 9 + - flag: 0 + server_message: Welcome to Demethos! + event_message: Demethos! + why_am_i_recommended: Welcome to Demethos! + channels: 3 + + #Properties for Galicia 10 + - flag: 0 + server_message: Welcome to Galicia! + event_message: Galicia! + why_am_i_recommended: Welcome to Galicia! + channels: 3 + + #Properties for El Nido 11 + - flag: 0 + server_message: Welcome to El Nido! + event_message: El Nido! + why_am_i_recommended: Welcome to El Nido! + channels: 3 + + #Properties for Zenith 12 + - flag: 0 + server_message: Welcome to Zenith! + event_message: Zenith! + why_am_i_recommended: Welcome to Zenith! + channels: 3 + + #Properties for Arcenia 13 + - flag: 0 + server_message: Welcome to Arcenia! + event_message: Arcenia! + why_am_i_recommended: Welcome to Arcenia! + channels: 3 + + #Properties for Kastia 14 + - flag: 0 + server_message: Welcome to Kastia! + event_message: Kastia! + why_am_i_recommended: Welcome to Kastia! + channels: 3 + + #Properties for Judis 15 + - flag: 0 + server_message: Welcome to Judis! + event_message: Judis! + why_am_i_recommended: Welcome to Judis! + channels: 3 + + #Properties for Plana 16 + - flag: 0 + server_message: Welcome to Plana! + event_message: Plana! + why_am_i_recommended: Welcome to Plana! + channels: 3 + + #Properties for Kalluna 17 + - flag: 0 + server_message: Welcome to Kalluna! + event_message: Kalluna! + why_am_i_recommended: Welcome to Kalluna! + channels: 3 + + #Properties for Stius 18 + - flag: 0 + server_message: Welcome to Stius! + event_message: Stius! + why_am_i_recommended: Welcome to Stius! + channels: 3 + + #Properties for Croa 19 + - flag: 0 + server_message: Welcome to Croa! + event_message: Croa! + why_am_i_recommended: Welcome to Croa! + channels: 3 + + #Properties for Medere 20 + - flag: 0 + server_message: Welcome to Medere! + event_message: Medere! + why_am_i_recommended: Welcome to Medere! + channels: 3 + + +server: + #Thread Tracker Configuration + USE_THREAD_TRACKER: true #[SEVERE] This deadlock auditing thing will bloat the memory as fast as the time frame one takes to lose track of a raindrop on a tempesting day. Only for debugging purposes. + + #Database Configuration + DB_URL: "jdbc:mysql://localhost:3306/heavenms" + DB_USER: "root" + DB_PASS: "" + DB_CONNECTION_POOL: true #Installs a connection pool to hub DB connections. Set false to default. + + #Login Configuration + WORLDS: 1 #Initial number of worlds on the server. + WLDLIST_SIZE: 21 #Max possible worlds on the server. + CHANNEL_SIZE: 20 #Max possible channels per world (which is 20, based on the channel list on login phase). + CHANNEL_LOAD: 100 #Max players per channel (limit actually used to calculate the World server capacity). + CHANNEL_LOCKS: 20 #Total number of structure management locks each channel has. + + RESPAWN_INTERVAL: 10000 #10 seconds, 10000. + PURGING_INTERVAL: 300000 #5 minutes, 300000. + RANKING_INTERVAL: 3600000 #60 minutes, 3600000. + COUPON_INTERVAL: 3600000 #60 minutes, 3600000. + UPDATE_INTERVAL: 777 #Dictates the frequency on which the "centralized server time" is updated. + + ENABLE_PIC: false #Pick true/false to enable or disable Pic. Delete character requires PIC available. + ENABLE_PIN: false #Pick true/false to enable or disable Pin. + + BYPASS_PIC_EXPIRATION: 20 #Enables PIC bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable. + BYPASS_PIN_EXPIRATION: 15 #Enables PIN bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable. + + AUTOMATIC_REGISTER: true #Automatically register players when they login with a nonexistent username. + BCRYPT_MIGRATION: true #Performs a migration from old SHA-1 and SHA-512 password to bcrypt. + COLLECTIVE_CHARSLOT: false #Available character slots are contabilized globally rather than per world server. + DETERRED_MULTICLIENT: false #Enables detection of multi-client and suspicious remote IP on the login system. + #Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses. + + #Multiclient Coordinator Configuration + MAX_ALLOWED_ACCOUNT_HWID: 4 #Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login. + MAX_ACCOUNT_LOGIN_ATTEMPT: 15 #After N tries on an account, login on that account gets disabled for a short period. + LOGIN_ATTEMPT_DURATION: 120 #Period in seconds the login attempt remains registered on the system. + + #Ip Configuration + HOST: 127.0.0.1 + LOCALSERVER: true + GMSERVER: false + + #Other configuration + SHUTDOWNHOOK: true + + #Server Flags + USE_CUSTOM_KEYSET: true #Enables auto-setup of the HeavenMS's custom keybindings when creating characters. + USE_DEBUG: false #Will enable some text prints on the client, oriented for debugging purposes. + USE_DEBUG_SHOW_INFO_EQPEXP: false #Prints on the cmd all equip exp gain info. + USE_DEBUG_SHOW_RCVD_PACKET: false #Prints on the cmd all received packet ids. + USE_DEBUG_SHOW_RCVD_MVLIFE: false #Prints on the cmd all received move life content. + USE_DEBUG_SHOW_PACKET: false + USE_SUPPLY_RATE_COUPONS: true #Allows rate coupons to be sold through the Cash Shop. + USE_IP_VALIDATION: true #Enables IP checking when logging in. + USE_CHARACTER_ACCOUNT_CHECK: false #Enables one-character-per-account check when logging in. This might be resource intensive. + + USE_MAXRANGE: true #Will send and receive packets from all events on a map, rather than those of only view range. + USE_MAXRANGE_ECHO_OF_HERO: true + USE_MTS: false + USE_CPQ: true #Renders the CPQ available or not. + USE_AUTOHIDE_GM: false #When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152). + USE_BUYBACK_SYSTEM: true #Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button. + USE_FIXED_RATIO_HPMP_UPDATE: true #Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio. + USE_FAMILY_SYSTEM: true + USE_DUEY: true + USE_RANDOMIZE_HPMP_GAIN: true #Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain on level up. + USE_STORAGE_ITEM_SORT: true #Enables storage "Arrange Items" feature. + USE_ITEM_SORT: true #Enables inventory "Item Sort/Merge" feature. + USE_ITEM_SORT_BY_NAME: false #Item sorting based on name rather than id. + USE_PARTY_FOR_STARTERS: true #Players level 10 or below can create/invite other players on the given level range. + USE_AUTOASSIGN_STARTERS_AP: false #Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below. + USE_AUTOASSIGN_SECONDARY_CAP: true #Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached. + USE_STARTING_AP_4: true #Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements. + USE_AUTOBAN: false #Commands the server to detect infractors automatically. + USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled. + USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour). + USE_SERVER_AUTOASSIGNER: true #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments. + USE_REFRESH_RANK_MOVE: true + USE_ENFORCE_ADMIN_ACCOUNT: false #Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE. + USE_ENFORCE_NOVICE_EXPRATE: true #Hardsets experience rate 1x for beginners level 10 or under. Ideal for roaming on novice areas without caring too much about losing some stats. + USE_ENFORCE_HPMP_SWAP: false #Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats. + USE_ENFORCE_MOB_LEVEL_RANGE: true #Players N levels below the killed mob will gain no experience from defeating it. + USE_ENFORCE_JOB_LEVEL_RANGE: false #Caps the player level on the minimum required to advance their current jobs. + USE_ENFORCE_JOB_SP_RANGE: false #Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. + USE_ENFORCE_ITEM_SUGGESTION: false #Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players. + USE_ENFORCE_UNMERCHABLE_CASH: true #Forces players to not sell CASH items via merchants, drops of it disappears. + USE_ENFORCE_UNMERCHABLE_PET: true #Forces players to not sell pets via merchants, drops of it disappears. (since non-named pets gets dirty name and other possible DB-related issues) + USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop. + USE_ENFORCE_MDOOR_POSITION: false #Forces mystic door to be spawned near spawnpoints. + USE_SPAWN_CLEAN_MDOOR: false #Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment. + USE_SPAWN_LOOT_ON_ANIMATION: false #Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly). + USE_SPAWN_RELEVANT_LOOT: true #Forces to only spawn loots that are collectable by the player or any of their party members. + USE_ERASE_PERMIT_ON_OPENSHOP: true #Forces "shop permit" item to be consumed when player deploy his/her player shop. + USE_ERASE_UNTRADEABLE_DROP: true #Forces flagged untradeable items to disappear when dropped. + USE_ERASE_PET_ON_EXPIRATION: false #Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll. + USE_BUFF_MOST_SIGNIFICANT: true #When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats. + USE_BUFF_EVERLASTING: false #Every applied buff on players holds expiration time so high it'd be considered permanent. Suggestion thanks to Vcoc. + USE_MULTIPLE_SAME_EQUIP_DROP: true #Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data. + USE_BANISHABLE_TOWN_SCROLL: true #Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available. + USE_ENABLE_FULL_RESPAWN: true #At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there. + USE_ENABLE_CHAT_LOG: false #Write in-game chat to log + USE_REBIRTH_SYSTEM: false #Flag to enable/disable rebirth system + USE_MAP_OWNERSHIP_SYSTEM: true #Flag to enable/disable map ownership system + USE_FISHING_SYSTEM: true #Flag to enable/disable custom fishing system + USE_NPCS_SCRIPTABLE: true #Flag to enable/disable serverside predefined script NPCs. + + #Events/PQs Configuration + USE_OLD_GMS_STYLED_PQ_NPCS: true #Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in. + USE_ENABLE_SOLO_EXPEDITIONS: true #Enables start expeditions with any number of players. This will also bypass all the Zakum prequest. + USE_ENABLE_DAILY_EXPEDITIONS: false #Enables daily entry limitations in expeditions. + USE_ENABLE_RECALL_EVENT: false #Enables a disconnected player to reaccess the last event instance they were in before logging out. Recall only works if the event isn't cleared or disposed yet. Suggestion thanks to Alisson (Goukken). + + #Announcement Configuration + USE_ANNOUNCE_SHOPITEMSOLD: false #Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold. + USE_ANNOUNCE_CHANGEJOB: false #Automatic message sent to acquantainces when changing jobs. + + #Cash Shop Configuration + USE_JOINT_CASHSHOP_INVENTORY: true #Enables usage of a same cash shop inventory for explorers, cygnus and legends. Items from exclusive cash shop inventories won't show up on the shared inventory, though. + USE_CLEAR_OUTDATED_COUPONS: true #Enables deletion of older code coupon registry from the DB, freeing so-long irrelevant data. + ALLOW_CASHSHOP_NAME_CHANGE: true #Allows players to buy name changes in the cash shop. + ALLOW_CASHSHOP_WORLD_TRANSFER: true #Allows players to buy world transfers in the cash shop. + + #Maker Configuration + USE_MAKER_PERMISSIVE_ATKUP: true #Allows players to use attack-based strengthening gems on non-weapon items. + USE_MAKER_FEE_HEURISTICS: true #Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side). + + #Custom Configuration + USE_ENABLE_CUSTOM_NPC_SCRIPT: true #Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay) or quests. + USE_STARTER_MERGE: false #Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites). + + #Commands Configuration + BLOCK_GENERATE_CASH_ITEM: false #Prevents creation of cash items with the item/drop command. + USE_WHOLE_SERVER_RANKING: false #Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds. + + EQUIP_EXP_RATE: 1.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). + PQ_BONUS_EXP_RATE: 0.5 #Rate for the PQ exp reward. + + EXP_SPLIT_LEVEL_INTERVAL: 5 #Non-contributing players must be within N level between the mob to receive EXP. + EXP_SPLIT_LEECH_INTERVAL: 5 #Non-contributing players must be within N level between any contributing party member to receive EXP. + EXP_SPLIT_MVP_MOD: 0.2 + EXP_SPLIT_COMMON_MOD: 0.8 + PARTY_BONUS_EXP_RATE: 1.0 #Rate for the party exp bonus reward. + + #Miscellaneous Configuration + TIMEZONE: GMT-3 + USE_DISPLAY_NUMBERS_WITH_COMMA: true #Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values). + USE_UNITPRICE_WITH_COMMA: true #Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions. + MAX_MONITORED_BUFFSTATS: 5 #Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires. + MAX_AP: 32767 #Max AP allotted on the auto-assigner. + MAX_EVENT_LEVELS: 8 #Event has different levels of rewarding system. + BLOCK_NPC_RACE_CONDT: 500 # (0.5 * 1000) Time the player client must wait before reopening a conversation with an NPC. + TOT_MOB_QUEST_REQUIREMENT: 77 #Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default. + MOB_REACTOR_REFRESH_TIME: 30000 # (30 * 1000) Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default. + PARTY_SEARCH_REENTRY_LIMIT: 10 #Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members). + NAME_CHANGE_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for name changes, default (GMS) is 30 days. + WORLD_TRANSFER_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for world tranfers, default is same as name change (30 days). + INSTANT_NAME_CHANGE: false #Whether or not to wait for server restart to apply name changes. Does on reconnect otherwise (requires queries on every login). + + #Dangling Items/Locks Configuration + ITEM_EXPIRE_TIME: 180000 # (3 * 60 * 1000) Time before items start disappearing. Recommended to be set up to 3 minutes. + KITE_EXPIRE_TIME: 3600000 # (60 * 60 * 1000) Time before kites (cash item) disappears. + ITEM_MONITOR_TIME: 300000 # (5 * 60 * 1000) Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history. + LOCK_MONITOR_TIME: 30000 # (30 * 1000) Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present. + + #Map Monitor Configuration + ITEM_EXPIRE_CHECK: 10000 # (10 * 10000) Interval between item expiring tasks on maps, which checks and makes disappear expired items. + ITEM_LIMIT_ON_MAP: 200 #Max number of items allowed on a map. + MAP_VISITED_SIZE: 5 #Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions. + MAP_DAMAGE_OVERTIME_INTERVAL: 5000 #Interval in milliseconds between map environment damage (e.g. El Nath and Aqua Road surrondings). + + #Channel Mob Disease Monitor Configuration + MOB_STATUS_MONITOR_PROC: 200 #Frequency in milliseconds between each proc on the mob disease monitor schedule. + MOB_STATUS_MONITOR_LIFE: 84 #Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity. + MOB_STATUS_AGGRO_PERSISTENCE: 2 #Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer. + MOB_STATUS_AGGRO_INTERVAL: 5000 #Interval in milliseconds between aggro logistics update. + + #Some Gameplay Enhancing Configurations + #Scroll Configuration + USE_PERFECT_GM_SCROLL: true #Scrolls from GMs never uses up slots nor fails. + USE_PERFECT_SCROLLING: true #Scrolls doesn't use slots upon failure. + USE_ENHANCED_CHSCROLL: true #Equips even more powerful with chaos upgrade. + USE_ENHANCED_CRAFTING: true #Apply chaos scroll on every equip crafted. + USE_ENHANCED_CLNSLATE: true #Clean slates can be applied to recover successfully used slots as well. + SCROLL_CHANCE_ROLLS: 10 #Number of rolls for success on a scroll, set 1 for default. + CHSCROLL_STAT_RATE: 3 #Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default. + CHSCROLL_STAT_RANGE: 6 #Stat upgrade range (-N, N) on chaos scrolls. + + #Beginner Skills Configuration + USE_ULTRA_NIMBLE_FEET: true #Massive speed & jump upgrade. + USE_ULTRA_RECOVERY: true #Massive recovery amounts overtime. + USE_ULTRA_THREE_SNAILS: true #Massive damage on shell toss. + + #Other Skills Configuration + USE_FULL_ARAN_SKILLSET: false #Enables starter availability to all Aran job skills. Suggestion thanks to Masterrulax. + USE_FAST_REUSE_HERO_WILL: true #Greatly reduce cooldown on Hero's Will. + USE_ANTI_IMMUNITY_CRASH: true #Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial. + USE_UNDISPEL_HOLY_SHIELD: true #Holy shield buff also prevents players from suffering dispel from mobs. + USE_FULL_HOLY_SYMBOL: true #Holy symbol doesn't require EXP sharers to work in full. + + #Character Configuration + USE_ADD_SLOTS_BY_LEVEL: true #Slots are added each 20 levels. + USE_ADD_RATES_BY_LEVEL: true #Rates are added each 20 levels. + USE_STACK_COUPON_RATES: false #Multiple coupons effects builds up together. + USE_PERFECT_PITCH: true #For lvl 30 or above, each lvlup grants player 1 perfect pitch. + + #Quest Configuration + USE_QUEST_RATE: false #Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates. + + #Quest Points Configuration + QUEST_POINT_REPEATABLE_INTERVAL: 24 #Minimum interval between repeatable quest completions for quest points to be awarded. + QUEST_POINT_REQUIREMENT: 16 #Exchange factor between N quest points to +1 fame, set 0 to disable the entire quest point mechanism. + QUEST_POINT_PER_QUEST_COMPLETE: 4 #Each completed quest awards N quest points, set 0 to disable. + QUEST_POINT_PER_EVENT_CLEAR: 1 #Each completed event instance awards N quest points, set 0 to disable. + + #Guild Configuration + CREATE_GUILD_MIN_PARTNERS: 6 #Minimum number of members on Guild Headquarters to establish a new guild. + CREATE_GUILD_COST: 1500000 + CHANGE_EMBLEM_COST: 5000000 + EXPAND_GUILD_BASE_COST: 500000 + EXPAND_GUILD_TIER_COST: 1000000 + EXPAND_GUILD_MAX_COST: 5000000 + + #Family Configuration + FAMILY_REP_PER_KILL: 4 #Amount of rep gained per monster kill. + FAMILY_REP_PER_BOSS_KILL: 20 #Amount of rep gained per boss kill. + FAMILY_REP_PER_LEVELUP: 200 #Amount of rep gained upon leveling up. + FAMILY_MAX_GENERATIONS: 1000 #Maximum depth of family tree. (Distance from leader to farthest junior) + + #Equipment Configuration + USE_EQUIPMNT_LVLUP_SLOTS: true #Equips can upgrade slots at level up. + USE_EQUIPMNT_LVLUP_POWER: true #Enable more powerful stat upgrades at equip level up. + USE_EQUIPMNT_LVLUP_CASH: true #Enable equip leveling up on cash equipments as well. + USE_SPIKES_AVOID_BANISH: true #Shoes equipped with spikes prevents mobs from banishing wearer. + MAX_EQUIPMNT_LVLUP_STAT_UP: 10000 #Max stat upgrade an equipment can have on a levelup. + MAX_EQUIPMNT_STAT: 32767 #Max stat on an equipment by leveling up. + USE_EQUIPMNT_LVLUP: 7 #All equips lvlup at max level of N, set 1 to disable. + + #Map-Chair Configuration + USE_CHAIR_EXTRAHEAL: true #Enable map chairs to further recover player's HP and MP (player must have the Chair Mastery skill). + CHAIR_EXTRA_HEAL_MULTIPLIER: 10 #Due to only being able to be send up-to-255 heal values, values being actually updated is the one displayed times this. + CHAIR_EXTRA_HEAL_MAX_DELAY: 21 #Players are expected to recover fully after using this skill for N seconds. + + #Player NPC Configuration + PLAYERNPC_INITIAL_X: 262 #Map frame width for putting PlayerNPCs. + PLAYERNPC_INITIAL_Y: 262 #Map frame height for putting PlayerNPCs. + PLAYERNPC_AREA_X: 320 #Initial width gap between PlayerNPCs. + PLAYERNPC_AREA_Y: 160 #Initial height gap between PlayerNPCs. + PLAYERNPC_AREA_STEPS: 4 #Max number of times gap is shortened to comport PlayerNPCs. + PLAYERNPC_ORGANIZE_AREA: true #Automatically rearranges PlayerNPCs on the map if there is no space set the new NPC. Current distance gap between NPCs is decreased to solve this issue. + PLAYERNPC_AUTODEPLOY: true #Makes PlayerNPC automatically deployed on the Hall of Fame at the instant one reaches max level. If false, eligible players must talk to 1st job instructor to deploy a NPC. + + #Pet Auto-Pot Configuration + USE_COMPULSORY_AUTOPOT: true #Pets will consume as many potions as needed to fulfill the AUTOHP/MP ratio threshold. + USE_EQUIPS_ON_AUTOPOT: true #Player MaxHP and MaxMP check values on autopot handler will be updated by the HP/MP bonuses on equipped items. + PET_AUTOHP_RATIO: 0.99 #Will automatically consume potions until given ratio of the MaxHP/MaxMP is reached. + PET_AUTOMP_RATIO: 0.99 + + #Pet & Mount Configuration + PET_EXHAUST_COUNT: 3 #Number of proc counts (1 per minute) on the exhaust schedule for fullness. + MOUNT_EXHAUST_COUNT: 1 #Number of proc counts (1 per minute) on the exhaust schedule for tiredness. + + #Pet Hunger Configuration + PETS_NEVER_HUNGRY: false #If true, pets and mounts will never grow hungry. + GM_PETS_NEVER_HUNGRY: true #If true, pets and mounts owned by GMs will never grow hungry. + + #Event Configuration + EVENT_MAX_GUILD_QUEUE: 10 #Max number of guilds in queue for GPQ. + EVENT_LOBBY_DELAY: 10 #Cooldown duration in seconds before reopening an event lobby. + + #Dojo Configuration + USE_FAST_DOJO_UPGRADE: true #Reduced Dojo training points amount required for a belt upgrade. + USE_DEADLY_DOJO: false #Should bosses really use 1HP,1MP attacks in dojo? + DOJO_ENERGY_ATK: 100 #Dojo energy gain when deal attack + DOJO_ENERGY_DMG: 20 #Dojo energy gain when recv attack + + #Wedding Configuration + WEDDING_RESERVATION_DELAY: 3 #Minimum idle slots before processing a wedding reservation. + WEDDING_RESERVATION_TIMEOUT: 10 #Limit time in minutes for the couple to show up before cancelling the wedding reservation. + WEDDING_RESERVATION_INTERVAL: 60 #Time between wedding starts in minutes. + WEDDING_BLESS_EXP: 30000 #Exp gained per bless count. + WEDDING_GIFT_LIMIT: 1 #Max number of gifts per person to same wishlist on marriage instances. + WEDDING_BLESSER_SHOWFX: true #Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead. + + #Buyback Configuration + USE_BUYBACK_WITH_MESOS: true #Enables usage of either mesos or NX for the buyback fee. + BUYBACK_FEE: 77.70 #Sets the base amount needed to buyback (level 30 or under will use the base value). + BUYBACK_LEVEL_STACK_FEE: 85.47 #Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak). + BUYBACK_MESO_MULTIPLIER: 1000 #Sets a multiplier for the fee when using meso as the charge unit. + BUYBACK_RETURN_MINUTES: 1 #Sets the maximum amount of time the player can wait before decide to buyback. + BUYBACK_COOLDOWN_MINUTES: 7 #Sets the time the player must wait before using buyback again. + + # Login timeout by shavit + TIMEOUT_DURATION: 3600000 # Kicks clients who don't send any packet to the game server in due time (in millisseconds). + + #Event End Timestamp + EVENT_END_TIMESTAMP: 1428897600000 + diff --git a/configuration.ini b/configuration.ini deleted file mode 100644 index 01a874e45d..0000000000 --- a/configuration.ini +++ /dev/null @@ -1,6 +0,0 @@ -HOST=127.0.0.1 -URL=jdbc:mysql://localhost:3306/heavenms -DB_USER=root -DB_PASS= -JAVA8=FALSE -SHUTDOWNHOOK=true \ No newline at end of file diff --git a/cores/yamlbeans-1.13.jar b/cores/yamlbeans-1.13.jar new file mode 100644 index 0000000000..45cac40fe9 Binary files /dev/null and b/cores/yamlbeans-1.13.jar differ diff --git a/docker-launch.sh b/docker-launch.sh deleted file mode 100644 index 654a2c088c..0000000000 --- a/docker-launch.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -sed -i "s/HOST=.*/HOST=${HOST}/" configuration.ini -sed -i "s|URL=.*|URL=${URL}|" configuration.ini -sed -i "s/DB_USER=.*/DB_USER=${DB_USER}/" configuration.ini -sed -i "s/DB_PASS=.*/DB_PASS=${DB_PASS}/" configuration.ini - -exec sh ./posix-launch.sh \ No newline at end of file diff --git a/docs/area_bosses/AreaBoss.js b/docs/area_bosses/AreaBoss.js new file mode 100644 index 0000000000..834b6a68ea --- /dev/null +++ b/docs/area_bosses/AreaBoss.js @@ -0,0 +1,35 @@ +// @Author: Resinate + +importPackage(Packages.server.life); +importPackage(Packages.tools); + +var towns = new Array(800020120, 251010102, 260010201, 107000300, 200010300, 100040105, 100040106, 261030000, 110040000, 240040401, 104000400, 222010310, 230040420, 230040420, 230020100, 105090310, 101030404, 250010304, 220050100, 220050000, 220050200, 221040301); +var spawns = new Array(6090002, 5220004, 3220001, 6220000, 8220000, 5220002, 5220002, 8220002, 5220001, 8220003, 2220000, 7220001, 8510000, 8520000, 4220001, 8220008, 3220000, 7220000, 5220003, 5220003, 5220003, 6220001); +var x = new Array(560, 560, 645, 90, 208, 456, 474, -300, 200, 0, 400, 0, 527, 138, 0, -626, 800, -300, -300, 0, 0, -4224); +var y = new Array(50, 50, 275, 119, 83, 278, 278, 180, 140, 1125, 455, 33, -437, 138, 520, -604, 1280, 390, 1030, 1030, 1030, 776); +var mapObj; +var mobObj; + +function init() { + scheduleNew(); +} + +function scheduleNew() { + setupTask = em.schedule("start", 0); +} + +function cancelSchedule() { + if (setupTask != null) + setupTask.cancel(true); +} + +function start() { + for(var i = 0; i < towns.length; i++) { + mapObj = em.getChannelServer().getMapFactory().getMap(towns[i]); + mobObj = MapleLifeFactory.getMonster(spawns[i]); + if(mapObj.getMonsterById(spawns[i]) == null) { + mapObj.spawnMonsterOnGroundBelow(mobObj, new Packages.java.awt.Point(x[i],y[i])); + } + } + setupTask = em.schedule("start", 30 * 60 * 1000); +} diff --git a/docs/area_bosses/BossEvent.js b/docs/area_bosses/BossEvent.js deleted file mode 100644 index 5cd1eb0047..0000000000 --- a/docs/area_bosses/BossEvent.js +++ /dev/null @@ -1,33 +0,0 @@ -// @Author: Resinate - -var towns = new Array(800020120, 251010102, 260010201, 107000300, 200010300, 100040105, 100040106, 261030000, 110040000, 250010504, 240040401, 104000400, 222010310, 230040420, 230040420, 230020100, 105090310, 101030404, 250010304, 220050100, 220050000, 220050200, 221040301); -var spawns = new Array(6090002, 5220004, 3220001, 6220000, 8220000, 5220002, 5220002, 8220002, 5220001, 7220002, 8220003, 2220000, 7220001, 8510000, 8520000, 4220001, 8220008, 3220000, 7220000, 5220003, 5220003, 5220003, 6220001); -var x = new Array(560, 560, 645, 90, 208, 456, 474, -300, 200, 400, 0, 400, 0, 527, 138, 0, -626, 800, -300, -300, 0, 0, -4224); -var y = new Array(50, 50, 275, 119, 83, 278, 278, 180, 140, 540, 1125, 455, 33, -437, 138, 520, -604, 1280, 390, 1030, 1030, 1030, 776); -var mapObj; -var mobObj; - -function init() { - scheduleNew(); -} - -function scheduleNew() { - setupTask = em.schedule("start", 0); -} - -function cancelSchedule() { - if (setupTask != null) - setupTask.cancel(true); -} - -function start() { - var time = (Math.floor(Math.random() * 10) + 10) * (60 * 1000); - for(var i = 0; i < towns.length; i++) { - mapObj = em.getChannelServer().getMapFactory().getMap(towns[i]); - mobObj = Packages.server.life.MapleLifeFactory.getMonster(spawns[i]); - if(mapObj.getMonsterById(spawns[i]) == null) { - mapObj.spawnMonsterOnGroundBelow(mobObj, new Packages.java.awt.Point(x[i],y[i])); - } - } - em.schedule("start", time); -} \ No newline at end of file diff --git a/docs/issues.txt b/docs/issues.txt index bfd66cbfb2..4cb1acf797 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -10,12 +10,15 @@ Known issues: - If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen. - Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios. - Dragon Roar doesn't show the stun effect to players. +- Cygnus job 'Final Attack' skills not functional. +- Steal skill doesn't deduct the loot from the drop pool from a mob. - Snipe will show much higher damage value than actually applicable to the attacker. -- Some monster status such as freeze and weapon/magic reflect doesn't behave properly in certain scenarios. Freeze seems to not work on mobs with low OID or are starters from server boot time. +- Some monster status such as weapon/magic reflect doesn't behave properly in certain scenarios. - On low-end connections, things such as command summoning a player that is currently logging in (already visible to other players) may cause the player to freeze, consequently freezing the account as well since the server-side disconnection doesn't happen. - Reportedly, there are cases where mob positions fail to sync between player's client-view. - Visual equip EXP watch value will present stuttering for early levels requirement (EXP needed less than 100), and requirement at level 200 will not progress at all due to the level cap in client. - Monster Magnet will crash the player when trying to pull fixed monsters. +- Some magic skills such as "Energy Bolt" or "Holy Arrow" doesn't display damage to other players when the attacks come from close range to the targeted mob. Oddly enough, using "closeRangeAttack" packet seems to clear the issue on most skills, however in others "Error 5" crash renders this unusable. --------------------------- --------------------------- @@ -29,15 +32,13 @@ Missing features list: --------------------------- ** Others ** -- GM Events (Snowball, OX, Fitness) -- Family system - MTS --------------------------- --------------------------- ** Quest ** -- Family & Medal quests. +- Medal quests. --------------------------- @@ -48,7 +49,7 @@ Missing features list: --------------------------- ** Packet issues & advanced PQs ** -- Mystic Doors (won't deploy players properly is some situations, only destination map matches). +- Mystic Doors (won't deploy players properly in some situations, only destination map matches). - Nett's Pyramid Party Quest --------------------------- diff --git a/docs/leftover.txt b/docs/leftover.txt index 38d0fbcee6..2001688854 100644 --- a/docs/leftover.txt +++ b/docs/leftover.txt @@ -2,10 +2,7 @@ Uncoded features: NX Format -Name Change -World transfer MTS (v53) -Family system (v67) Family and Medal Quests(?) Uncoded Party Quests: diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 97c3bc975e..bfd4c72496 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1950,3 +1950,327 @@ Normalizado uso de lobbyids em início de eventos nos scripts. 15 Junho 2019, Corrigido limites de requisitos de nível para acessar expedições nos scripts de NPCs que as gerenciam. + +19 Junho 2019, +Corrigido janela congelada de Legendary Spirit ao tentar aplicar scrolls em equipamentos sem slots. +Ajustado chance de drops de skillbooks. +Revisado funcionalidade de arrendamento de mapas. Jogadores não mais perdem posse ao trocar de mapas, somente ao expirar devido à falta de atividades ou ao tentar arrendar outro mapa. +Revisado expedições retirando jogadores assim que o líder sai de mapa ou não há mais em campo a quantidade mínima de jogadores requisitada para começar o evento. +Corrigido gerenciador de mapas finalizando antes dos eventos, potencialmente levando a quebra de scripts de evento durante a fase de shutdown do servidor. +Corrigido comandos de começar/completar quests não atuando para quests não previstas pelos XMLs. +Revisado sistema de quests desnecessariamente inicializando estruturas de leitura de XMLs. +Corrigido sistema de quests no objeto do jogador não acessando corretamente certas quests devido a uso indevido de inteiros de tamanho short e int. + +20 Junho 2019, +Implementado sistema de contabilização de entradas em bosses. +Corrigido potencial exploit com expressão facial podendo crashar jogadores ao receber expressão inválida. + +21 - 22 Junho 2019, +Ajustado data no Duey, que estava atuando de forma inesperada anteriormente. +Adicionado checagem contra potenciais exploits de dano dado em mobs por mobs ou summons em seus respectivos handlers. +Adicionado checagem contra potenciais exploits por itens nos handlers de cadeira e expressão facial. +Ajustado checagem contra potenciais exploits de distância/inexistência de NPC na área onde o jogador se encontra, ao se realizar operações sobre quests. +Refatorado função que modifica estado de mob aliado no mapa (no lado servidor) sendo executada dentro de definição de pacote. +Implementado suporte para entrega rápida pelo Duey. + +24 - 25 Junho 2019, +Corrigido Horntail não dropando itens após mudança recente na lógica de drops. +Revisado sistema de comandos. Cada comando no sistema é inicializado somente uma vez, ao iniciar o servidor. +Ajustado Maker skill, agora utilizando pacotes de resultados ao realizar suas operações. +Corrigido Mini Dungeons utilizando tempo limite pré-determinado no código-fonte. +Revisado utilização de "timeLimit" em objeto MapleMap. Valor supostamente pré-determinado pela receita XML poderia ser sobrescrito por outros métodos da classe. +Ajustado evento de Gaga no espaço, evento agora funcional. +Adicionado minigame RPS de NPC, recursos implementados pelo Arnah. + +27 Junho 2019, +Corrigido contabilização de dano de auto-destruição de mobs não sendo aplicado corretamente. + +01 Julho 2019, +Corrigido contabilização de entrada em bosses não checando criação de expedições. +Corrigido caso de overflow em valor máximo calculado de dano em skills. +Implementado retirada de itens mantidos pelo Duey na DB, após dado a data de expirar. + +02 Julho 2019, +Refatorado flags de itens utilizando tamanho menor que o esperado. +Adicionado checagem por FieldLimit ao lançar itens em mapas dados como "untradeable". +Adicionado funcionalidade "Quest Item Restore". + +11 Julho 2019, +Implementado instanciação de flag "somente compartilhável dentro de mesma conta" em itens recém-gerados que possuem essa funcionalidade. +Implementado atualização de estados no portão de entrada do Papulatus. +Corrigido deslize apontado pelo Conrad, na aplicação de caixas de limites usados pelos buffs em área. +Implementado finalização de instância de minidungeon assim que o líder de party sai da área ou há troca de líderes com alguém fora da área. + +14 Julho 2019, +Refatorado atributos de HenesysPQ sendo utilizados em objetos de áreas do jogo. +Corrigido mobs aliados não realizando item drops devidamente após atualização recente no sistema de loot. +Corrigido quest de proteger hog (explorers) "completando" mesmo embora o jogador tenha tentado sair da instância ao conversar com o NPC. +Corrigido possível exploit com quest de proteger hog (explorers), onde o jogador poderia vir a tentar novamente a instância após completá-la (resultando em recompensas rápidas). +Corrigido script de Papulatus não levando os métodos de checagem de requisitos atualizados para expedições. +Corrigido diversos scripts de expedições finalizando expedições indevidamente ao realizar operações de party. +Implementado checagem por flag de FieldLimit que evita penalidade de perda de EXP em certas áreas do jogo. +Revisado limite de dano aplicável por alguns summons, cujo valor limite estava muito abaixo do esperado, levando a problemas com aplicação de ataques dos mesmos. + +15 Julho 2019, +Implementado normalização de fuso horário em pacotes enviados ao cliente. Agora o sistema utiliza mesmo fuso horário definido nas flags do servidor. +Corrigido certos casos onde grupos dentro de lobby de CPQ não conseguiam ser desafiados, geralmente ocorrendo ao se desconectar após o desafio ter sido aceito e antes de começar a instância. +Revisado script de créditos. +Adicionado checagem por GM's no método de autoban de jogador. + +17 Julho 2019, +Corrigido drops de reatores não utilizando o sistema de drops sequenciais. +Revisado uso de sincronizações em vários métodos do sistema, tais como nos métodos de colocação de novos itens no mapa, detecção de toque em reatores, tabela de convidados em casamento, aplicação de dano de jogadores em mobs, recepção de pacotes. + +18 Julho 2019, +Corrigido aplicação indevida de requisição de palavra-chave que prosseguia quest em uma das quests na questline de Aran. +Corrigido nome errado em coluna da tabela "reports". +Corrigido uso de NPC default na conversa padrão que ocorre ao se utilizar o comando "startquest" e "completequest". + +19 Julho 2019, +Corrigido quest onde mobs podem aparecer na área do NPC Grendel permitindo repetir os ganhos de quest tanto quanto respawn de mobs à vontade. +Corrigido robes de sauna e outros, que permitem ganhos bônus de HP, gerando ganhos 10x maiores que o esperado. +Ajustado limites para recuperação de HP de forma a permitir ganhos em vários casos onde há a aplicação de bônus, tais como usando sauna robe, Endure skill. + +22 Julho 2019, +Corrigido atributo de contagem de dano em mob aliado da HenesysPQ não instanciado. +Corrigido skill "Combat Step" sendo considerado um "buff" pelo sistema do servidor. Isso implicava em duplicação de efeito visual para outros jogadores. + +26 - 27 Julho 2019, +Corrigido problemas de cast de tipos que passou a ocorrer após trocar para Java 8. +Ajustado flag que permite jogadores a ganhar EXP de mob independente de diferenças de nível. +Corrigido Gaviota não sumindo após lançar ataque. +Corrigido funcionalidade de ignorar items de pets não se mantendo após trocar de mapas. +Corrigido CPQ1 campo 3 e 4 não permitindo jogadores a usar summons/protectors em campo. +Corrigido líderes de expedição recebendo pacote de timer para fase de registro em casos onde a expedição falhou em ser iniciada. +Corrigido problema de locking ocorrendo recentemente ao tentar rodar limpeza de itens no mapa (ocorre ao realizar drops de vários itens, mais antigos imediatamente sumindo), problema ocorrendo devido a um caso de loop infinito. +Corrigido várias skills de summons não utilizando o ícone de buff no canto superior direito da tela. +Corrigido alguns danos de summons sendo calculados extremamente baixos quando o jogador não equipa uma arma ou o mesmo não possui pelo menos uma dezena em ataque. + +28 Julho 2019, +Corrigido funcionalidade de loot explosivo de mobs não aplicando devidamente. +Corrigido linguagens, bastante usado na MCPQ, não utilizando o valor requisitado pelo jogador ao logar/trocar de canais. +Corrigido casos de NPE ao tentar realizar updates de posição lado-servidor em alguns summons de jogador. +Revisado reset de reatores em reatores que estão desaparecidos por um tempo, para retornar de imediato. + +31 Julho 2019, +Revisado AP inicial de jogadores, agora mantendo dois sistemas para suprir a falta de 9 AP's. Ambos com AP's começando em 4/4/4/4: 0AP inicialmente, com ganhos de 4AP na primeira mudança de classe e 5AP na segunda mudança. Se não estiver usando esse método, jogadores possuem de início 9 AP's à disposição. + +02 Agosto 2019, +Refatorado vários scripts de quests, não mais realizando disposes e envio de caixas de texto no mesmo status. + +05 Agosto 2019, +Revisado casos onde o sistema de recompensas de quests avisa "inventário cheio" mesmo embora novos espaços pudessem ser encontrados na retirada de itens. +Corrigido posicionamento não-verificado de lojas criadas por jogadores. +Corrigido ganho reduzido de EXP ao utilizar skill Mortal Blow. +Corrigido clique em "O" na UI de recepção de itens pelo Duey não realizando ação alguma. +Corrigido representação de "quick" na lista de pacotes recebidos pelo Duey não constando os pacotes enviados sem mensagem escrita. +Revisado certos casos onde itens enviados com "quick delivery" não conseguiam ser recebidos imediatamente. + +06 Agosto 2019, +Corrigido certas montarias não-ligadas a itens de inventário não atuando devidamente ao transicionar mapas (terceiros não conseguiam visualizar a montaria). +Revisado os vários scripts de quests anteriormente refatorado. Adição: último if status sinalizando disposes de caixas de texto nos penúltimos status. + +08 - 10 Agosto 2019, +Adicionado handler para aplicação de dano em mobs por objetos de ambiente. +Revisado uso de Steal ao aplicar dano, preenchendo lista (e buscando evitar novas aplicações) assim que utilizado. +Setado checagem para limites de lobbyid ao se iniciar uma instância de evento. +Revisado boss logs somente removendo os itens da tabela a partir de um dia antes, não todas as entradas, assim que a tarefa de reset é iniciada. +Refatorado elementos não-condizentes com o padrão de design Factory na classe geradora de mapas. +Revisado possível cenário de vazamento de memória ocorrido devido a exceção lançada em algum trecho entre removeKilledMonsterObject e dispatchMonsterKilled. +Aprimorado loots de reatores, agora colocando loots visíveis ao jogador no centro, similarmente a como loots de mobs ocorrem. +Ajustado frequência de loots de reatores para 200ms. + +12 - 14 Agosto 2019, +Refatorado vários casos de erros em acessos a funções estáticas a partir de scripts, que passou a ocorrer após trocar de versão Java. +Corrigido listas que mantém conteúdo dos mundos e canais esvaziando antes que os processos em execução do TimerManager terminem de executar, no momento do sinal de shutdown do servidor. +Revisado update de quests para o jogador durante script de quests, problema permite movimento enquanto o mesmo ainda está falando com o NPC. +Revisado novamente os scripts de quest! Problema detectado envolvia incidências de iniciar e completar de quests com disposes na mesma estrutura status. + +19 - 20 Agosto 2019, +Revisado autocommit antes da hora e falta de chamada a rollbacks quando ocorre exceção no método de salvar jogador na DB. +Corrigido quest scripts recém-formatados pelo caso dos updates de quest durante conversação com NPCs. +Revisado cálculo de perda de EXP em nocaute. +Corrigido caso de deadlock relacionado a party HP e manutenção de doors, ocorrendo devido a uso indevido de statLock antes de prtLock. + +22 - 23 Agosto 2019, +Revisado envio sequencial de pacotes pelo IoSession acoplado à conexão com o cliente. +Corrigido possibilidade de uso de SP Reset para aumentar skills imprevistas. +Adicionado permissão de drops de NX utilizando a mesma flag dos shops. +Adicionado comando para setar quantidade de slots em todos os inventários. + +24 Agosto 2019, +Refatorado objeto de MapleStorage, agora sendo gerado um objeto por conta, acoplado ao mundo que jogador pertence (não mais acoplado diretamente com objeto do jogador, jogador agora recebe uma visão deste objeto). + +26 - 27 Agosto 2019, +Revisado uso de objeto de jogador pelo MaplePartyCharacter, retirando acesso ao mesmo quando o jogador está desconectado (assim limpando retenção de múltiplos objetos de jogador offline, quando os mesmos se encontram em party). +Corrigido casos inesperados como Nimble Feet seguido de Morph fazendo o segundo atuar inesperadamente, aplicando outras imagens de morphs. +Corrigido buffs importantes para mecânicas do jogo sendo sobrescritas por possuir menores ganhos que outros. +Revisado diversos casos no código-fonte onde havia possibilidade de acesso ao objeto de jogador nulo a partir do MaplePartyCharacter (quando o jogador se encontra offline e está na party). +Corrigido caso de EXP ganho ocorrendo menor que o esperado, ao se usar diversos modificadores de ganhos. +Corrigido bug crítico na atualização recente relacionado a skill mounts, onde na inicialização dos mounts informações estariam sendo perdidas inesperadamente... +Corrigido informações de mount não sendo atualizado para o jogador assim que o mesmo loga, levando a certas quests com requerimento de mount não permitindo inicialização pelo lado-cliente. +Revisado sistema criado para manutenção de pacotes enviados através do IoSession, tal sistema agora atuando como uma "pool" ao invés de uma "factory". + +29 Agosto 2019, +Revisado uso de loop em espera ocupada no sistema de manutenção de pacotes enviados. Em geral, threads que solicitam anúncio de pacotes não necessitam evitar esperar até que cada pacote seja enviado. + +30 Agosto 2019, +Corrigido skill Seal não atuando devidamente para Blaze Wizard. +Corrigido skill Seal atuando em bosses. +Refatorado funções ainda utilizando invocável desabstraindo o mesmo, assim evitando múltiplos casts de tipo ao decorrer da busca pelo motor de scripts. + +02 - 03 Setembro 2019, +Revisado pontos de spawn usados em warps em vários scripts do servidor. +Refatorado métodos da CPQ buscando jogadores pelo storage de canais. Não deveria mais haver necessidade já que a partir de uma atualização recente membros offline contém objeto de jogador nulo, e ao reentrar o MPC já assume o novo objeto de jogador. + +05 - 06 Setembro 2019, +Adicionado doors em mapas de cidade como objetos visíveis em campo. +Corrigido aplicação de status de mobs, onde em várias ocasiões os mobs poderiam parecer inafetáveis aos mesmos. + +07 Setembro 2019, +Corrigido jogador não recebendo informações de debuffs em outros jogadores ao trocar de mapas. + +10 - 12 Setembro 2019, +Corrigido algumas skills de tipo "mágico", que não usam cargas, não mostrando dano para outros jogadores quando golpe ocorre suficientemente perto. +Adicionado checagem por evidência de disponibilidade de scripts em quests. +Corrigido inscrição de itens no MTS levando a certos atributos de itens sendo perdidos no processo. +Adicionado ganho de MaxHP extra da skill para Thunderbreakers, em ambos casos de aumento de nível e de MaxHP usando AP. +Refatorado ação de autopots, permitindo o mesmo a agir imediatamente após detecção de perda suficiente de HP/MP ao usar itens ou skills. +Adicionado efeito sonoro de portal ao atravessar Mystic Door. + +16 Setembro 2019, +Adicionado checagem no uso de autopots ao usar HP/MP, limites são calculados baseado em estimativa detectada de uso dos autopots pelo lado cliente. + +20 Setembro 2019, +Adicionado update de macros ao usar SP reset. +Refatorado vários pacotes Java contendo classes diversificadas. +Corrigido nomenclatura errônea de "worker" introduzida, querendo significar "task". + +21 - 24 Setembro 2019, +Corrigido ferramenta detectora de métodos estáticos não realizando varredura completa em arquivos, parando em casos onde um método estático é encontrado em cada. +Implementado ferramenta detectora de scripts de quest que estiveram sendo usados como paliativo. +Refatorado amplamente métodos que lidam com atribuição de progresso de quests. +Revisado uso de vários scripts de quest que estiveram sendo usados como paliativo. +Alterado declaração de "MapleCharacter c", considerado ambíguo à de MapleClient. +Revisado uso de progresso de quests feito pelos scripts, de forma a parear com o novo modelo. + +26 - 28 Setembro 2019, +Corrigido casos no novo modelo onde algumas quests utilizando informações de progresso não conseguiriam ser completadas. +Corrigido uso envio desnecessário de pacote ao atualizar progresso de quest causando popup inesperado de mensagens de quests para jogadores. +Corrigido alguns scripts não levando a atualização dos endereços dos pacotes alterados na última refatoração. +Corrigido script de quest de Magatia checando errado quantidade de livros adquiridos pelo jogador no progresso de quest. + +29 Setembro 2019, +Revisado Raise UI, agora utilizando o novo sistema de progressão. +Corrigido checagem por espaço no inventário, ao tentar ganhar itens via quest, não informando o jogador adequadamente sobre itens one-of-a-kind. +Corrigido uso de itens na Raise UI, quando não atualizando progresso algum (nenhum pacote enviado ao jogador), não permitindo jogador acesso a certos recursos (como Cash Shop/MTS). +Revisado aplicação geral do novo modelo de progressão de quests implementado. + +01 - 02 Outubro 2019, +Corrigido caso de exceção de modificação concorrente dentro de módulo de incremento de progresso de mob em quests. +Corrigido caixa de diálogo com conteúdo extra, ao terminar uma quest, não sendo enviada ao jogador. +Corrigido retransmissão de pacotes de movimentos com sequência vazia crashando outros jogadores. +Refatorado checagem por pacotes de movimentos com sequência vazia, agora atuando no módulo abstrato. +Corrigido progresso de mobs em quests não atualizando o infoex devidamente ao jogador, levando à falta do balão de "quest completa". +Corrigido quest de 1o job de Cygnus permitindo retornar para estado que tenta concluir quest, permitindo assim múltiplas instâncias de conclusão da mesma. +Adicionado scripting atuante ao abrir Raise UI, permitindo assim utilizar ações elaboradas, tais como EXP de combate para Mimiana. +Corrigido pets não sendo despawnados assim que se dá o tempo de expiração. +Corrigido jogadores com "hide" controlando mobs em certas situações. +Corrigido análise de alerta de HP/MP lado-servidor não contando com os atributos de HP/MP de equipamentos e buffs. + +03 Outubro 2019, +Corrigido skill "Energy Charge" levando atualização de buff ao tocar em mobs, levando a casos onde o buff nos stats poderia inesperadamente ficar retido além do tempo de atuação previsto. +Substituído utilização de "Jackson annotations + SnakeYaml" por "YamlBeans", utilizando-se assim menos artefatos JARs no processo (anteriormente 5, agora somente 1). Nota: na transição, fora constatado que a utilização da tecnologia anterior era mais permissiva perante o arquivo de configurações, porém estrutura do novo arquivo foi normalizado e está agora funcional. +Refatorado inicialização de eventos ao iniciar o servidor, em busca de melhorar o tempo de startup. + +05 - 08 Outubro 2019, +Refatorado os diversos schedulers que estavam presentes na classe de canais, agora abstraídas e atuando dentro de serviços. +Corrigido circunstância onde movimentos de mobs poderiam estar sendo processados enquanto jogador está transitando mapas, levando a possível inconsistência na aplicação de movimento do mob na área acessada. + +10 - 12 Outubro 2019, +Modificado uso de "default" de petid nos itens de inventário armazenados na DB (de -1 para nulo), assim permitindo criar chaves únicas. +Adicionado uso de chaves estrangeiras para o petid. +Implementado código de suporte para cupons de "associação" de cosméticos de cabelo. +Corrigido bug recente em bosses com atributo "link" sem HPbar sendo detectados como possuindo HPbar, se o mob base possui. +Corrigido bug recente na skill "Body Pressure" não aplicando a sua passiva com chance de neutralizar ao tocar mobs. +Corrigido quest com NPC "Shaman Rock" aplicando progresso que não corresponde com o esperado (progresso extra agora movido para um novo questid). +Adicionado método que permite executar scripts de mapa no sistema de scripts de portal. +Corrigido problema recente ao referenciar script com progresso "Touch the Sky" (script de portal utilizando métodos de scripts de mapa). +Adicionado fallback para scripts de NPC MapleTV. +Revisado aplicação e abordagem deste código-base nos arquivos XML legado. + +15 - 16 Outubro 2019, +Corrigido caso inconsistente onde objeto de jogador seria mantido no storage de jogadores caso o jogador tentasse se deregistrar de um canal (sem desconectar) e não mais retornando na mesma sessão. +Adicionado código para funcionalidade de "Safety Charm", que permite jogadores curar 30% MaxHP/MaxMP ao retornar em campo. +Implementado extensão das facilidades de serviços, agora atuando desjuntamente de canais. +Implementado serviço de mundo para salvar jogadores, assim evitando geração de novas threads para cada jogador que está sendo persistido na DB. + +18 Outubro 2019, +Revisado abordagem de inicialização de scripts de eventos em canais. Usuários não são mais bloqueados ao tentar logar devido ao canal não ter todos os eventos carregados. + +19 Outubro 2019, +Corrigido certos itens de quest não aparecendo de mobs em casos onde o mesmo não é requisito para iniciar/completar. +Corrigido NPC Pi crashando jogadores ao tentar criar flechas. +Corrigido script de reevolução de pets não atuando em pet Robos, além de agora evitar repetir pets na conversão. + +21 - 22 Outubro 2019, +Corrigido boss HPbar não desaparecendo em certos cenários. O mesmo deveria ocorrer assim que mob é retirado do mapa. +Revisado informações de mob, que não são propagáveis, sendo repassados à definição do novo mob. +Revisado carregamento de storages ao inicializar informações de conta podendo retornar nulo indevidamente em caso de exceção lançada. +Corrigido dois casos de deadlock ocorrendo ao tentar cancelar efeito de stat de jogador. +Adicionado auditoria de locks para os componentes que travam para leitura e escrita. +Adicionado clip de introdução de Cygnus. +Revisado updateBuffEffect, agora checando por determinados buffs de pirata e usando pacotes específicos para os mesmos. +Revisado geração desnecessária de objetos de mapa ao carregar nomes para comando "goto". +Corrigido buffs de mob em fakes desaparecendo assim que o mesmo muda de estado para real. + +24 Outubro 2019, +Revisado uso de scriptids na geração de PlayerNPCs. Agora somente ids existentes na base especificada vai adiante. + +27 Outubro 2019, +Revisado lógica booleana na detecção de foothold abaixo, não realizando múltiplas checagens sobre um mesmo atributo. + +28 Outubro 2019, +Revisado lógica recente de uso de petid único levando a lançamento de exceção (transações de DB implica retirada de item de pet do CS storage na DB antes de colocar o mesmo no inventário). +Corrigido portal relacionado a quest em Ariant inesperadamente levando jogadores à passagem secreta de Sleepywood, em caso de conclusão de questline da mesma. +Corrigido loop em scripts de quest de associação a Alcadno/Zenumist em Magatia. +Adicionado utilização de dispose em quest scripts para o comando homônimo. + +30 Outubro 2019, +Corrigido bug em scripts (que possivelmente passou a ocorrer ao utilizar Java8) relacionado ao tentar utilizar métodos da biblioteca Math sem corresponder parâmetros com a assinatura adequada. +Corrigido entrega de itemid inesperado em script relacionado a EllinPQ. + +01 - 03 Novembro 2019, +Corrigido deslize recente em na funcionalidade de recuperação de itens de quest. +Adicionado custom npc para MapleTVs: geradora de scrolls, provê os mesmos após trocar diversos itens ganhos durante jogo. + +04 Novembro 2019, +Revisado diversos fluxos de aquisição de locks ao longo das classes mais relevantes do código-fonte. +Corrigido problema inesperado com funcionalidade restoreLostItem. +Inserido remoção de cash item para dentro da cláusula que checa item no inventário. +Corrigido bug em pet autopot retirando quantidades negativas do inventário. +Corrigido caso de deadlock em tentativa de aquisição de lock inesperada após adquirir os de mapas, que deveriam ter prioridade baixa. + +07 Novembro 2019, +Adicionado cura de debuffs zumbificação e confusão na lista de debuffs a serem curados pela poção "cura-tudo". +Corrigido duplicação em mensagem de pós-quest enviada, em casos onde há a presença de ação que automatiza mensagem guiando jogador para a próxima quest. + +09 - 10 Novembro 2019, +Refatorado certos usos de finalização não-sucedida de quests, que poderiam compartilhar das mecânicas de desistência de quest. +Revisado aquisição de endereço remoto para checagem de transição de jogadores e multi-cliente, agora também avaliando distinção de HWID passado. +Corrigido pós-quests ainda permitindo jogadores a movimentarem enquanto a mensagem está na tela, devido a certo conflito com envio de recompensas permitindo movimentação do jogador. +Corrigido informação de skillbooks por quests não-funcional após refatoração recente. +Corrigido chance de drop de item de quest em El Nath extremamente baixa. + +11 - 12 Novembro 2019, +Corrigido algumas skills de ataque de Aran (double, triple swing) não aplicando consumo de MP devidamente. +Revisado caso com doors levando jogadores a solo abaixo inesperadamente. +Corrigido pots em MCPQ não atuando devidamente para outros jogadores no grupo ou somente para si. +Corrigido buffs random em MCPQ acertando sempre múltiplos jogadores. +Corrigido poção "cura-tudo" em MCPQ: pots pequenos curando todos os jogadores de party, pots grandes não curando slow. +Corrigido caso inesperado em 2nd job de pirata bloqueando saída de jogadores do mapa enquanto não lidarem com todos os mobs. + +13 Novembro 2019, +Corrigido problema no sistema de matching ao tentar rodar ações externas enquanto travando os recursos do sistema, ao criar match. +Corrigido caso onde novos jogadores poderiam ser agregados à party e entrar em campo na MCPQ assim que confirmação de partida e contagem de início fossem efetivados. +Adicionado cache para requerimento de scrolls, assim melhorando tempo de resposta para o novo custom NPC de geração de scrolls. diff --git a/launch.bat b/launch.bat index 076a28d634..52b0eafefa 100644 --- a/launch.bat +++ b/launch.bat @@ -1,6 +1,6 @@ @echo off @title HeavenMS -set PATH=C:\Program Files\Java\jdk1.7.0_79\bin;%PATH% +set PATH=C:\Program Files\Java\jdk1.8.0_211\bin;%PATH% set CLASSPATH=.;dist\* java -Xmx2048m -Dwzpath=wz\ net.server.Server pause \ No newline at end of file diff --git a/launch_server_linux.sh b/launch_server_linux.sh deleted file mode 100644 index 57d9b2d6ec..0000000000 --- a/launch_server_linux.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -export CLASSPATH=".:dist/*" -java -Xmx2048m -Dwzpath=wz/ net.server.Server \ No newline at end of file diff --git a/linux-compile.sh b/linux-compile.sh new file mode 100755 index 0000000000..9cfec04218 --- /dev/null +++ b/linux-compile.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# thanks to lkxyyjx +sudo ant -Dplatforms.JDK_1.7.home=/opt/jdk1.7.0_80 compile +sudo ant -Dplatforms.JDK_1.7.home=/opt/jdk1.7.0_80 jar \ No newline at end of file diff --git a/linux-launch.sh b/linux-launch.sh new file mode 100644 index 0000000000..40557cec8e --- /dev/null +++ b/linux-launch.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# cores in classpath, thanks to lkxyyjx +export CLASSPATH=".:dist/*:cores/*" +java -Xmx2048m -Dwzpath=wz/ net.server.Server \ No newline at end of file diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index 6fd0f0e472..d08448f6c7 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -46,51 +46,15 @@ is divided into following sections: - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - Must set platform.home - Must set platform.bootcp - Must set platform.java - Must set platform.javac - - The J2SE Platform is not correctly set up. - Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. - Either open the project in the IDE and setup the Platform with the same name or add it manually. - For example like this: - ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) - or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) - + @@ -112,7 +76,7 @@ is divided into following sections: - + @@ -190,6 +154,7 @@ is divided into following sections: + @@ -217,6 +182,20 @@ is divided into following sections: + + + + + + + + + + + + + + @@ -242,6 +221,7 @@ is divided into following sections: + @@ -284,7 +264,7 @@ is divided into following sections: - + @@ -324,7 +304,7 @@ is divided into following sections: - + @@ -405,7 +385,7 @@ is divided into following sections: - + @@ -428,7 +408,7 @@ is divided into following sections: - + @@ -460,7 +440,7 @@ is divided into following sections: - + @@ -540,7 +520,7 @@ is divided into following sections: - + @@ -565,7 +545,7 @@ is divided into following sections: - + @@ -707,7 +687,7 @@ is divided into following sections: - + @@ -742,9 +722,6 @@ is divided into following sections: - - - @@ -760,9 +737,7 @@ is divided into following sections: - - - + @@ -787,7 +762,7 @@ is divided into following sections: - + @@ -814,7 +789,7 @@ is divided into following sections: - + @@ -853,7 +828,7 @@ is divided into following sections: - + @@ -865,7 +840,7 @@ is divided into following sections: - + @@ -988,15 +963,15 @@ is divided into following sections: - + - + - + @@ -1004,7 +979,7 @@ is divided into following sections: - + @@ -1012,7 +987,7 @@ is divided into following sections: To run this application from the command line without Ant, try: - ${platform.java} -jar "${dist.jar.resolved}" + java -jar "${dist.jar.resolved}" @@ -1199,7 +1174,7 @@ is divided into following sections: Must select one file in the IDE or set run.class - + Must select one file in the IDE or set applet.url @@ -1221,13 +1196,10 @@ is divided into following sections: - - - - + - + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 4d5b6615f0..6db8caa016 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=92113194 +build.xml.data.CRC32=92efccf9 build.xml.script.CRC32=ff13faf5 -build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +build.xml.stylesheet.CRC32=8064a381@1.80.1.48 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=92113194 -nbproject/build-impl.xml.script.CRC32=cef58264 -nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 +nbproject/build-impl.xml.data.CRC32=92efccf9 +nbproject/build-impl.xml.script.CRC32=8cda444e +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties index 67c9c27960..c1164614bd 100644 --- a/nbproject/private/private.properties +++ b/nbproject/private/private.properties @@ -3,4 +3,4 @@ do.depend=false do.jar=true javac.debug=true javadoc.preview=true -user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties +user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.2\\build.properties diff --git a/nbproject/project.properties b/nbproject/project.properties index 861f8eb51c..4188fc88d4 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -34,6 +34,7 @@ file.reference.mina-core-2.0.19.jar=cores/mina-core-2.0.19.jar file.reference.mysql-connector-java-bin.jar=cores/mysql-connector-java-bin.jar file.reference.slf4j-api-1.7.21.jar=cores/slf4j-api-1.7.21.jar file.reference.slf4j-jdk14-1.7.5.jar=cores/slf4j-jdk14-1.7.5.jar +file.reference.yamlbeans-1.13.jar=cores/yamlbeans-1.13.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=true @@ -43,14 +44,16 @@ javac.classpath=\ ${file.reference.slf4j-api-1.7.21.jar}:\ ${file.reference.HikariCP-java7-2.4.13.jar}:\ ${file.reference.mysql-connector-java-bin.jar}:\ - ${file.reference.slf4j-jdk14-1.7.5.jar} + ${file.reference.slf4j-jdk14-1.7.5.jar}:\ + ${file.reference.yamlbeans-1.13.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false +javac.external.vm=false javac.processorpath=\ ${javac.classpath} -javac.source=1.7 -javac.target=1.7 +javac.source=1.8 +javac.target=1.8 javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir} @@ -84,7 +87,7 @@ manifest.custom.permissions= manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false -platform.active=JDK_1.7 +platform.active=default_platform project.license=gpl30_msv2 project.licensePath=./nbproject/licenseheader.txt run.classpath=\ diff --git a/nbproject/project.xml b/nbproject/project.xml index aed1c56350..980f89761c 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -4,7 +4,6 @@ HeavenMS - diff --git a/scripts/event/3rdJob_bowman.js b/scripts/event/3rdJob_bowman.js index 583e4db8a9..4388b7a27a 100644 --- a/scripts/event/3rdJob_bowman.js +++ b/scripts/event/3rdJob_bowman.js @@ -31,7 +31,7 @@ var maxMapId = 108010101; var eventTime = 20; //20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/3rdJob_magician.js b/scripts/event/3rdJob_magician.js index cd11b6d427..e7875bcea2 100644 --- a/scripts/event/3rdJob_magician.js +++ b/scripts/event/3rdJob_magician.js @@ -31,7 +31,7 @@ var maxMapId = 108010201; var eventTime = 20; //20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/3rdJob_mount.js b/scripts/event/3rdJob_mount.js index bc9bd937b1..094086c8ce 100644 --- a/scripts/event/3rdJob_mount.js +++ b/scripts/event/3rdJob_mount.js @@ -33,7 +33,7 @@ var eventMaps = [923010000]; var eventTime = 5; //5 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/3rdJob_pirate.js b/scripts/event/3rdJob_pirate.js index e79e1e2551..2b0164ba8e 100644 --- a/scripts/event/3rdJob_pirate.js +++ b/scripts/event/3rdJob_pirate.js @@ -31,7 +31,7 @@ var maxMapId = 108010501; var eventTime = 20; //20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/3rdJob_thief.js b/scripts/event/3rdJob_thief.js index 9ed7e979fc..3da091fa42 100644 --- a/scripts/event/3rdJob_thief.js +++ b/scripts/event/3rdJob_thief.js @@ -31,7 +31,7 @@ var maxMapId = 108010401; var eventTime = 20; //20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/3rdJob_warrior.js b/scripts/event/3rdJob_warrior.js index 86cd6b9e5b..f8c938d075 100644 --- a/scripts/event/3rdJob_warrior.js +++ b/scripts/event/3rdJob_warrior.js @@ -31,7 +31,7 @@ var maxMapId = 108010301; var eventTime = 20; //20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/4jaerial.js b/scripts/event/4jaerial.js index 43a0149526..d026a0fb09 100644 --- a/scripts/event/4jaerial.js +++ b/scripts/event/4jaerial.js @@ -30,7 +30,7 @@ var maxMapId = 912020000; var eventTime = 2; //2 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/4jberserk.js b/scripts/event/4jberserk.js deleted file mode 100644 index d3fd4fa747..0000000000 --- a/scripts/event/4jberserk.js +++ /dev/null @@ -1,185 +0,0 @@ -/* - 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 . -*/ -var exitMap; -var minPlayers = 3; - -function init() {} - -function monsterValue(eim, mobId) { - return 1; -} - -function setup(level, lobbyid) { - exitMap = em.getChannelServer().getMapFactory().getMap(105090800); // - - var eim = em.newInstance("4jberserk_" + lobbyid); - eim.setProperty("level", level); - - var mf = eim.getMapFactory(); - - var map = mf.getMap(910500200); - map.addMapTimer(3*60); - em.schedule("timeOut", 20 * 60000); - - //you can't warp up to the rocks until all rogs are dead, I think? - eim.setProperty("canWarp","false"); - - return eim; -} - -function afterSetup(eim) {} - -function playerEntry(eim, player) { - var map = eim.getMapInstance(910500200); - player.changeMap(map, map.getPortal(0)); - -//TODO: hold time across map changes -//player.getClient().announce(tools.MaplePacketCreator.getClock(1800)); -} - -function playerDead(eim, player) { -} - -function playerRevive(eim, player) { - //if (eim.isLeader(player)) { //check for party leader - //boot whole party and end - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -/*/} - else { //boot dead player - // If only 2 players are left, uncompletable: - var party = eim.getPlayers(); - if (party.size() <= minPlayers) { - for (var i = 0; i < party.size(); i++) { - playerExit(eim,party.get(i)); - } - eim.dispose(); - } - else - playerExit(eim, player); - }*/ -} - -function playerDisconnected(eim, player) { - //if (eim.isLeader(player)) { //check for party leader - //boot whole party and end - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - if (party.get(i).equals(player)) { - removePlayer(eim, player); - } - else { - playerExit(eim, party.get(i)); - } - } - eim.dispose(); -/*/} - else { //boot d/ced player - // If only 2 players are left, uncompletable: - var party = eim.getPlayers(); - if (party.size() < minPlayers) { - for (var i = 0; i < party.size(); i++) { - playerExit(eim,party.get(i)); - } - eim.dispose(); - } - else - playerExit(eim, player); - }*/ -} - -function leftParty(eim, player) { - // If only 2 players are left, uncompletable: - var party = eim.getPlayers(); - if (true) { - for (var i = 0; i < party.size(); i++) { - playerExit(eim,party.get(i)); - } - eim.dispose(); - } - else - playerExit(eim, player); -} - -function disbandParty(eim) { - //boot whole party and end - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -} - -function playerUnregistered(eim, player) {} - -function playerExit(eim, player) { - eim.unregisterPlayer(player); - player.changeMap(exitMap, exitMap.getPortal(0)); -} - -//for offline players -function removePlayer(eim, player) { - eim.unregisterPlayer(player); - player.getMap().removePlayer(player); - player.setMap(exitMap); -} - -function clearPQ(eim) { - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) - playerExit(eim, party.get(i)); - eim.dispose(); -} - -function monsterKilled(mob, eim) {} - -function allMonstersDead(eim) { - eim.setProperty("canWarp","true"); -} - -function cancelSchedule() {} - -function timeOut() { - var iter = em.getInstances().iterator(); - while (iter.hasNext()) { - var eim = iter.next(); - if (eim.getPlayerCount() > 0) { - var pIter = eim.getPlayers().iterator(); - while (pIter.hasNext()) - playerExit(eim, pIter.next()); - } - eim.dispose(); - } -} - - -// ---------- FILLER FUNCTIONS ---------- - -function dispose() {} - -function scheduledTimeout(eim) {} - -function changedLeader(eim, leader) {} - diff --git a/scripts/event/4jrush.js b/scripts/event/4jrush.js deleted file mode 100644 index a1258d0ec6..0000000000 --- a/scripts/event/4jrush.js +++ /dev/null @@ -1,155 +0,0 @@ -/* - 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 . -*/ -/* - * @author AngelSL - * - * 4th Job Rush Quest. - * Based on Kerning City PQ script by Stereo - */ - -var exitMap; -var minPlayers = 3; - -function init() {} - -function monsterValue(eim, mobId) { - return 1; -} - -function setup(level, lobbyid) { - exitMap = em.getChannelServer().getMapFactory().getMap(105090700); // - - var eim = em.newInstance("4jrush_" + lobbyid); - eim.setProperty("level", level); - - var mf = eim.getMapFactory(); - var map = mf.getMap(910500100); - map.addMapTimer(20*60); - em.schedule("timeOut", 20 * 60000); - return eim; -} - -function afterSetup(eim) {} - -function playerEntry(eim, player) { - var map = eim.getMapInstance(910500100); - player.changeMap(map, map.getPortal(0)); -} - -function playerDead(eim, player) { -} - -function playerRevive(eim, player) { - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -} - -function playerDisconnected(eim, player) { - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - if (party.get(i).equals(player)) { - removePlayer(eim, player); - } - else { - playerExit(eim, party.get(i)); - } - } - eim.dispose(); -} - -function leftParty(eim, player) { - // If only 2 players are left, uncompletable: - var party = eim.getPlayers(); - if (true) { - for (var i = 0; i < party.size(); i++) { - playerExit(eim,party.get(i)); - } - eim.dispose(); - } - else - playerExit(eim, player); -} - -function disbandParty(eim) { - //boot whole party and end - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -} - -function playerUnregistered(eim, player) {} - -function playerExit(eim, player) { - eim.unregisterPlayer(player); - player.changeMap(exitMap, exitMap.getPortal(0)); -} - -//for offline players -function removePlayer(eim, player) { - eim.unregisterPlayer(player); - player.getMap().removePlayer(player); - player.setMap(exitMap); -} - -function clearPQ(eim) { - //KPQ does nothing special with winners - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -} - -function monsterKilled(mob, eim) {} - -function allMonstersDead(eim) {} - -function cancelSchedule() {} - -function timeOut() { - var iter = em.getInstances().iterator(); - while (iter.hasNext()) { - var eim = iter.next(); - if (eim.getPlayerCount() > 0) { - var pIter = eim.getPlayers().iterator(); - while (pIter.hasNext()) { - playerExit(eim, pIter.next()); - } - } - eim.dispose(); - } -} - - -// ---------- FILLER FUNCTIONS ---------- - -function dispose() {} - -function scheduledTimeout(eim) {} - -function changedLeader(eim, leader) {} - diff --git a/scripts/event/4jship.js b/scripts/event/4jship.js index bd7d4370dd..ba58956743 100644 --- a/scripts/event/4jship.js +++ b/scripts/event/4jship.js @@ -30,7 +30,7 @@ var maxMapId = 912010200; var eventTime = 4; //4 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/4jsuper.js b/scripts/event/4jsuper.js index 835a79db80..322beef6d7 100644 --- a/scripts/event/4jsuper.js +++ b/scripts/event/4jsuper.js @@ -30,7 +30,7 @@ var maxMapId = 912010200; var eventTime = 4; //4 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/Aran_2ndmount.js b/scripts/event/Aran_2ndmount.js index 81cb47b67b..d9d064cc7f 100644 --- a/scripts/event/Aran_2ndmount.js +++ b/scripts/event/Aran_2ndmount.js @@ -31,7 +31,7 @@ var maxMapId = 921110000; var eventTime = 3; //3 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/Aran_3rdmount.js b/scripts/event/Aran_3rdmount.js index aab415b92d..9c2a023622 100644 --- a/scripts/event/Aran_3rdmount.js +++ b/scripts/event/Aran_3rdmount.js @@ -31,7 +31,7 @@ var maxMapId = 914030000; var eventTime = 3; //3 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/BalrogBattle.js b/scripts/event/BalrogBattle.js index 35f4af113b..0517777dda 100644 --- a/scripts/event/BalrogBattle.js +++ b/scripts/event/BalrogBattle.js @@ -170,7 +170,7 @@ function playerLeft(eim, player) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -179,17 +179,12 @@ function changedMap(eim, player, mapid) { } } -function changedLeader(eim, leader) { - var mapid = leader.getMapId(); - if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) { - end(eim); - } -} +function changedLeader(eim, leader) {} function playerDead(eim, player) {} function playerRevive(eim, player) { // player presses ok on the death pop up. - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -198,7 +193,7 @@ function playerRevive(eim, player) { // player presses ok on the death pop up. } function playerDisconnected(eim, player) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -206,19 +201,9 @@ function playerDisconnected(eim, player) { eim.unregisterPlayer(player); } -function leftParty(eim, player) { - if (eim.isEventTeamLackingNow(false, minPlayers, player)) { - end(eim); - } - else - playerLeft(eim, player); -} +function leftParty(eim, player) {} -function disbandParty(eim) { - if (!eim.isEventCleared()) { - end(eim); - } -} +function disbandParty(eim) {} function monsterValue(eim, mobId) { return 1; @@ -259,7 +244,8 @@ function monsterKilled(mob, eim) { eim.showClearEffect(); eim.clearPQ(); - eim.dispatchUpdateQuestMobCount(bossMobId, entryMap); + eim.dispatchRaiseQuestMobCount(bossMobId, entryMap); + eim.dispatchRaiseQuestMobCount(9101003, entryMap); // thanks Atoot for noticing quest not getting updated after boss kill mob.getMap().broadcastBalrogVictory(eim.getLeader().getName()); } else { if(count == 1) { diff --git a/scripts/event/BalrogBattle_Easy.js b/scripts/event/BalrogBattle_Easy.js index e345319546..1fa9b7da5f 100644 --- a/scripts/event/BalrogBattle_Easy.js +++ b/scripts/event/BalrogBattle_Easy.js @@ -170,7 +170,7 @@ function playerLeft(eim, player) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -179,17 +179,12 @@ function changedMap(eim, player, mapid) { } } -function changedLeader(eim, leader) { - var mapid = leader.getMapId(); - if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) { - end(eim); - } -} +function changedLeader(eim, leader) {} function playerDead(eim, player) {} function playerRevive(eim, player) { // player presses ok on the death pop up. - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -198,7 +193,7 @@ function playerRevive(eim, player) { // player presses ok on the death pop up. } function playerDisconnected(eim, player) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -206,19 +201,9 @@ function playerDisconnected(eim, player) { eim.unregisterPlayer(player); } -function leftParty(eim, player) { - if (eim.isEventTeamLackingNow(false, minPlayers, player)) { - end(eim); - } - else - playerLeft(eim, player); -} +function leftParty(eim, player) {} -function disbandParty(eim) { - if (!eim.isEventCleared()) { - end(eim); - } -} +function disbandParty(eim) {} function monsterValue(eim, mobId) { return 1; @@ -259,7 +244,7 @@ function monsterKilled(mob, eim) { eim.showClearEffect(); eim.clearPQ(); - eim.dispatchUpdateQuestMobCount(bossMobId, entryMap); + eim.dispatchRaiseQuestMobCount(bossMobId, entryMap); mob.getMap().broadcastBalrogVictory(eim.getLeader().getName()); } else { if(count == 1) { diff --git a/scripts/event/BalrogQuest.js b/scripts/event/BalrogQuest.js index 161aeb9d26..c25725fc24 100644 --- a/scripts/event/BalrogQuest.js +++ b/scripts/event/BalrogQuest.js @@ -31,7 +31,7 @@ var maxMapId = 910520000; var eventTime = 10; //10 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function setLobbyRange() { return lobbyRange; diff --git a/scripts/event/CWKPQ.js b/scripts/event/CWKPQ.js index c10d12ada7..ab0f49b709 100644 --- a/scripts/event/CWKPQ.js +++ b/scripts/event/CWKPQ.js @@ -241,7 +241,7 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { - if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); end(eim); @@ -297,7 +297,7 @@ function changedLeader(eim, leader) {} function playerDead(eim, player) {} function playerRevive(eim, player) { - if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); @@ -309,7 +309,7 @@ function playerRevive(eim, player) { } function playerDisconnected(eim, player) { - if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); end(eim); diff --git a/scripts/event/DelliBattle.js b/scripts/event/DelliBattle.js index 241ebc7970..327ab2778c 100644 --- a/scripts/event/DelliBattle.js +++ b/scripts/event/DelliBattle.js @@ -35,7 +35,7 @@ var maxMapId = 925010300; var eventTime = 6; // 6 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function init() { setEventRequirements(); diff --git a/scripts/event/ElementalBattle.js b/scripts/event/ElementalBattle.js index 3052293046..17ee7b3a59 100644 --- a/scripts/event/ElementalBattle.js +++ b/scripts/event/ElementalBattle.js @@ -36,7 +36,7 @@ var maxMapId = 922020100; var eventTime = 20; // 20 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function init() { setEventRequirements(); diff --git a/scripts/event/GuardianNex.js b/scripts/event/GuardianNex.js index 982ee33b59..360e414543 100644 --- a/scripts/event/GuardianNex.js +++ b/scripts/event/GuardianNex.js @@ -24,7 +24,7 @@ function respawn(eim){} function playerEntry(eim, player){ var cave = eim.getMapInstance(eventMap + 10 * eim.getIntProperty("nex")); - player.changeMap(cave); + player.changeMap(cave, 1); } function scheduledTimeout(eim){ diff --git a/scripts/event/HenesysPQ.js b/scripts/event/HenesysPQ.js index e9ae26ac9f..2b44f2a4a5 100644 --- a/scripts/event/HenesysPQ.js +++ b/scripts/event/HenesysPQ.js @@ -105,6 +105,8 @@ function setup(level, lobbyid) { var eim = em.newInstance("Henesys" + lobbyid); eim.setProperty("level", level); eim.setProperty("stage", "0"); + eim.setProperty("bunnyCake", "0"); + eim.setProperty("bunnyDamaged", "0"); eim.getInstanceMap(910010000).resetPQ(level); eim.getInstanceMap(910010000).allowSummonState(false); @@ -243,6 +245,25 @@ function friendlyKilled(mob, eim) { } } +function friendlyItemDrop(eim, mob) { + if (mob.getId() == 9300061) { + var cakes = eim.getIntProperty("bunnyCake") + 1; + eim.setIntProperty("bunnyCake", cakes); + + mob.getMap().broadcastMessage(Packages.tools.MaplePacketCreator.serverNotice(6, "The Moon Bunny made rice cake number " + cakes + ".")); + } +} + +function friendlyDamaged(eim, mob) { + if (mob.getId() == 9300061) { + var bunnyDamage = eim.getIntProperty("bunnyDamaged") + 1; + if (bunnyDamage > 5) { + broadcastMessage(Packages.tools.MaplePacketCreator.serverNotice(6, "The Moon Bunny is feeling sick. Please protect it so it can make delicious rice cakes.")); + eim.setIntProperty("bunnyDamaged", 0); + } + } +} + function allMonstersDead(eim) {} function cancelSchedule() {} diff --git a/scripts/event/HorntailBattle.js b/scripts/event/HorntailBattle.js index 3aced02c62..401014f6df 100644 --- a/scripts/event/HorntailBattle.js +++ b/scripts/event/HorntailBattle.js @@ -214,7 +214,7 @@ function monsterKilled(mob, eim) { eim.showClearEffect(mob.getMap().getId()); eim.clearPQ(); - eim.dispatchUpdateQuestMobCount(8810018, 240060200); + eim.dispatchRaiseQuestMobCount(8810018, 240060200); mob.getMap().broadcastHorntailVictory(); } else if(isHorntailHead(mob)) { var killed = eim.getIntProperty("defeatedHead"); diff --git a/scripts/event/MK_PrimeMinister.js b/scripts/event/MK_PrimeMinister.js index d29be7e5ed..0ef72ad21d 100644 --- a/scripts/event/MK_PrimeMinister.js +++ b/scripts/event/MK_PrimeMinister.js @@ -52,7 +52,7 @@ function primeMinisterCheck(eim) { var pIter = map.getAllPlayers().iterator(); while (pIter.hasNext()) { var player = pIter.next(); - if (player.getQuestStatus(2333) == 1 && player.getAbstractPlayerInteraction().getQuestProgress(2333, mobId) == 0) { + if (player.getQuestStatus(2333) == 1 && player.getAbstractPlayerInteraction().getQuestProgressInt(2333, mobId) == 0) { return true; } } diff --git a/scripts/event/MahaBattle.js b/scripts/event/MahaBattle.js index 40f731315f..6791f678ad 100644 --- a/scripts/event/MahaBattle.js +++ b/scripts/event/MahaBattle.js @@ -33,7 +33,7 @@ var maxMapId = 914020000; var eventTime = 10; // 10 minutes -var lobbyRange = [0, 0]; +var lobbyRange = [0, 7]; function init() {} diff --git a/scripts/event/PapulatusBattle.js b/scripts/event/PapulatusBattle.js index 39f1b253d3..6ff9c0dfd6 100644 --- a/scripts/event/PapulatusBattle.js +++ b/scripts/event/PapulatusBattle.js @@ -115,7 +115,9 @@ function setup(level, lobbyid) { return eim; } -function afterSetup(eim) {} +function afterSetup(eim) { + updateGateState(1); +} function respawnStages(eim) {} @@ -143,7 +145,7 @@ function playerLeft(eim, player) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -152,17 +154,12 @@ function changedMap(eim, player, mapid) { } } -function changedLeader(eim, leader) { - var mapid = leader.getMapId(); - if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) { - end(eim); - } -} +function changedLeader(eim, leader) {} function playerDead(eim, player) {} function playerRevive(eim, player) { // player presses ok on the death pop up. - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -171,7 +168,7 @@ function playerRevive(eim, player) { // player presses ok on the death pop up. } function playerDisconnected(eim, player) { - if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { eim.unregisterPlayer(player); end(eim); } @@ -179,19 +176,9 @@ function playerDisconnected(eim, player) { eim.unregisterPlayer(player); } -function leftParty(eim, player) { - if (eim.isEventTeamLackingNow(false, minPlayers, player)) { - end(eim); - } - else - playerLeft(eim, player); -} +function leftParty(eim, player) {} -function disbandParty(eim) { - if (!eim.isEventCleared()) { - end(eim); - } -} +function disbandParty(eim) {} function monsterValue(eim, mobId) { return 1; @@ -213,6 +200,7 @@ function giveRandomEventReward(eim, player) { function clearPQ(eim) { eim.stopEventTimer(); eim.setEventCleared(); + updateGateState(0); } function isPapulatus(mob) { @@ -231,5 +219,14 @@ function allMonstersDead(eim) {} function cancelSchedule() {} -function dispose(eim) {} +function updateGateState(newState) { // thanks Conrad for noticing missing gate update + em.getChannelServer().getMapFactory().getMap(220080000).getReactorById(2208001).forceHitReactor(newState); + em.getChannelServer().getMapFactory().getMap(220080000).getReactorById(2208002).forceHitReactor(newState); + em.getChannelServer().getMapFactory().getMap(220080000).getReactorById(2208003).forceHitReactor(newState); +} +function dispose(eim) { + if (!eim.isEventCleared()) { + updateGateState(0); + } +} diff --git a/scripts/event/Puppeteer.js b/scripts/event/Puppeteer.js index 35baa8c0dc..0fd8898cb6 100644 --- a/scripts/event/Puppeteer.js +++ b/scripts/event/Puppeteer.js @@ -24,7 +24,7 @@ function respawn(eim){} function playerEntry(eim, player){ var cave = eim.getMapInstance(eventMap); - player.changeMap(cave); + player.changeMap(cave, 1); } function scheduledTimeout(eim){ diff --git a/scripts/event/RescueGaga.js b/scripts/event/RescueGaga.js new file mode 100644 index 0000000000..dc9dd6b7c7 --- /dev/null +++ b/scripts/event/RescueGaga.js @@ -0,0 +1,211 @@ +/** + * @author: kevintjuh93 + * @author: Ronan +*/ + +importPackage(Packages.tools); + +var isPq = true; +var minPlayers = 1, maxPlayers = 1; +var minLevel = 12, maxLevel = 255; +var entryMap = 922240000; +var exitMap = 922240200; +var recruitMap = 922240200; + +var minMapId = 922240000; +var maxMapId = 922240100; + +var eventTime = 3; // 3 minutes + +var lobbyRange = [0, 19]; + +function init() { + setEventRequirements(); +} + +function setLobbyRange() { + return lobbyRange; +} + +function setEventRequirements() { + var reqStr = ""; + + reqStr += "\r\n Number of players: "; + if(maxPlayers - minPlayers >= 1) reqStr += minPlayers + " ~ " + maxPlayers; + else reqStr += minPlayers; + + reqStr += "\r\n Level range: "; + if(maxLevel - minLevel >= 1) reqStr += minLevel + " ~ " + maxLevel; + else reqStr += minLevel; + + reqStr += "\r\n Time limit: "; + reqStr += eventTime + " minutes"; + + em.setProperty("party", reqStr); +} + +function setEventExclusives(eim) { + var itemSet = []; + eim.setExclusiveItems(itemSet); +} + +function setEventRewards(eim) { + var itemSet, itemQty, evLevel, expStages; + + evLevel = 1; //Rewards at clear PQ + itemSet = []; + itemQty = []; + eim.setEventRewards(evLevel, itemSet, itemQty); + + expStages = []; //bonus exp given on CLEAR stage signal + eim.setEventClearStageExp(expStages); +} + +function getEligibleParty(party) { //selects, from the given party, the team that is allowed to attempt this event + var eligible = []; + var hasLeader = false; + + if(party.size() > 0) { + var partyList = party.toArray(); + + for(var i = 0; i < party.size(); i++) { + var ch = partyList[i]; + + if(ch.getMapId() == recruitMap && ch.getLevel() >= minLevel && ch.getLevel() <= maxLevel) { + if(ch.isLeader()) hasLeader = true; + eligible.push(ch); + } + } + } + + if(!(hasLeader && eligible.length >= minPlayers && eligible.length <= maxPlayers)) eligible = []; + return eligible; +} + +function setup(level, lobbyid) { + var eim = em.newInstance("RescueGaga_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("stage", "0"); + eim.setProperty("falls", "0"); + + respawnStages(eim); + eim.startEventTimer(eventTime * 60000); + setEventRewards(eim); + setEventExclusives(eim); + return eim; +} + +function afterSetup(eim) {} + +function respawnStages(eim) {} + +function playerEntry(eim, player) { + var map = eim.getMapInstance(entryMap); + player.changeMap(map, map.getPortal(0)); + + player.announce(MaplePacketCreator.showEffect("event/space/start")); + player.startMapEffect("Please rescue Gaga within the time limit.", 5120027); +} + +function scheduledTimeout(eim) { + end(eim); +} + +function playerUnregistered(eim, player) {} + +function playerExit(eim, player) { + eim.unregisterPlayer(player); + player.changeMap(exitMap, 0); +} + +function changedMap(eim, player, mapid) { + if (mapid < minMapId || mapid > maxMapId) { + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + eim.unregisterPlayer(player); + + player.changeMap(mapid, 0); + player.cancelEffect(2360002); + + end(eim); + } else { + eim.unregisterPlayer(player); + + player.changeMap(mapid, 0); + player.cancelEffect(2360002); + } + } else if (mapid == maxMapId) { + eim.clearPQ(); + + var rgaga = player.getEvents().get("rescueGaga"); + rgaga.complete(); + } +} + +function afterChangedMap(eim, player, mapid) { + if (mapid == minMapId) { + player.getAbstractPlayerInteraction().useItem(2360002);//HOORAY <3 + } else { + player.cancelEffect(2360002); + } +} + +function changedLeader(eim, leader) {} + +function playerDead(eim, player) {} + +function playerRevive(eim, player) { // player presses ok on the death pop up. + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function playerDisconnected(eim, player) { + if (eim.isEventTeamLackingNow(true, minPlayers, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function leftParty(eim, player) {} + +function disbandParty(eim) {} + +function monsterValue(eim, mobId) { + return 1; +} + +function end(eim) { + var party = eim.getPlayers(); + for (var i = 0; i < party.size(); i++) { + playerExit(eim, party.get(i)); + } + eim.dispose(); +} + +function giveRandomEventReward(eim, player) { + eim.giveEventReward(player); +} + +function clearPQ(eim) { + eim.stopEventTimer(); + eim.setEventCleared(); + + eim.schedule("spawnGrandpaBunny", 10 * 1000); +} + +function spawnGrandpaBunny(eim) { + eim.spawnNpc(9001105, new java.awt.Point(175, -20), eim.getInstanceMap(maxMapId)); +} + +function monsterKilled(mob, eim) {} + +function allMonstersDead(eim) {} + +function cancelSchedule() {} + +function dispose(eim) {} diff --git a/scripts/event/RockSpirit.js b/scripts/event/RockSpirit.js index 149ced9bd1..9bd0726582 100644 --- a/scripts/event/RockSpirit.js +++ b/scripts/event/RockSpirit.js @@ -63,7 +63,7 @@ function respawn(eim) { function playerEntry(eim, player) { var amplifierMap = eim.getMapInstance(entryMap.getId()); - player.changeMap(amplifierMap); + player.changeMap(amplifierMap, 1); eim.schedule("timeOut", timer); } diff --git a/scripts/event/RockSpiritVIP.js b/scripts/event/RockSpiritVIP.js index 0d2ecba425..60085fb57d 100644 --- a/scripts/event/RockSpiritVIP.js +++ b/scripts/event/RockSpiritVIP.js @@ -63,7 +63,7 @@ function respawn(eim) { function playerEntry(eim, player) { var amplifierMap = eim.getMapInstance(entryMap.getId()); - player.changeMap(amplifierMap); + player.changeMap(amplifierMap, 1); eim.schedule("timeOut", timer); } diff --git a/scripts/event/ZakumBattle.js b/scripts/event/ZakumBattle.js index c52de1b557..3145e5044c 100644 --- a/scripts/event/ZakumBattle.js +++ b/scripts/event/ZakumBattle.js @@ -85,7 +85,9 @@ function setEventRewards(eim) { eim.setEventClearStageMeso(mesoStages); } -function afterSetup(eim) {} +function afterSetup(eim) { + updateGateState(1); +} function setup(channel) { var eim = em.newInstance("Zakum" + channel); @@ -188,6 +190,7 @@ function giveRandomEventReward(eim, player) { function clearPQ(eim) { eim.stopEventTimer(); eim.setEventCleared(); + updateGateState(0); } function isZakum(mob) { @@ -209,4 +212,12 @@ function allMonstersDead(eim) {} function cancelSchedule() {} -function dispose(eim) {} +function updateGateState(newState) { // thanks Conrad for noticing missing gate update + em.getChannelServer().getMapFactory().getMap(211042300).getReactorById(2118002).forceHitReactor(newState); +} + +function dispose(eim) { + if (!eim.isEventCleared()) { + updateGateState(0); + } +} diff --git a/scripts/map/onFirstUserEnter/spaceGaGa_sMap.js b/scripts/map/onFirstUserEnter/spaceGaGa_sMap.js index 842f00ed6d..3ec7de09a7 100644 --- a/scripts/map/onFirstUserEnter/spaceGaGa_sMap.js +++ b/scripts/map/onFirstUserEnter/spaceGaGa_sMap.js @@ -1,5 +1,4 @@ function start(ms) { - ms.getPlayer().resetEnteredScript(); - ms.getPlayer().getMap().addMapTimer(180); + ms.getPlayer().resetEnteredScript(); ms.spawnMonster(9300331, -28, 0); } \ No newline at end of file diff --git a/scripts/map/onFirstUserEnter/spaceGaGa_start.js b/scripts/map/onFirstUserEnter/spaceGaGa_start.js deleted file mode 100644 index 57d476e830..0000000000 --- a/scripts/map/onFirstUserEnter/spaceGaGa_start.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - 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 . -*/ -/* - *@Author: kevintjuh93 -*/ - -importPackage(Packages.tools); -var player; - -function start(ms) { - player = ms.getPlayer(); - player.resetEnteredScript(); - ms.getClient().announce(MaplePacketCreator.showEffect("event/space/start")); - player.startMapEffect("Please rescue Gaga within the time limit.", 5120027); - var map = player.getMap(); - if (map.getTimeLeft() > 0) { - ms.getClient().announce(MaplePacketCreator.getClock(map.getTimeLeft())); - } else { - map.addMapTimer(180); - } - ms.useItem(2360002);//HOORAY <3 -} \ No newline at end of file diff --git a/scripts/map/onUserEnter/130030000.js b/scripts/map/onUserEnter/130030000.js index 1d94337e2e..f789e5ab3b 100644 --- a/scripts/map/onUserEnter/130030000.js +++ b/scripts/map/onUserEnter/130030000.js @@ -21,5 +21,5 @@ */ function start(ms) { - ms.getPlayer().updateQuestInfo(20010, "1"); + ms.setQuestProgress(20010, 20022, 1); } \ No newline at end of file diff --git a/scripts/map/onUserEnter/130030001.js b/scripts/map/onUserEnter/130030001.js index 1d94337e2e..f789e5ab3b 100644 --- a/scripts/map/onUserEnter/130030001.js +++ b/scripts/map/onUserEnter/130030001.js @@ -21,5 +21,5 @@ */ function start(ms) { - ms.getPlayer().updateQuestInfo(20010, "1"); + ms.setQuestProgress(20010, 20022, 1); } \ No newline at end of file diff --git a/scripts/map/onUserEnter/910510000.js b/scripts/map/onUserEnter/910510000.js index c489ca085b..51e18daa4b 100644 --- a/scripts/map/onUserEnter/910510000.js +++ b/scripts/map/onUserEnter/910510000.js @@ -5,11 +5,11 @@ function start(ms){ var map = player.getMap(); if(player.isCygnus()) { - if(ms.isQuestStarted(20730) && ms.getQuestProgress(20730, 9300285) == 0) { + if(ms.isQuestStarted(20730) && ms.getQuestProgressInt(20730, 9300285) == 0) { map.spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(9300285), new java.awt.Point(680, 258)); } } else { - if(ms.isQuestStarted(21731) && ms.getQuestProgress(21731, 9300344) == 0) { + if(ms.isQuestStarted(21731) && ms.getQuestProgressInt(21731, 9300344) == 0) { map.spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(9300344), new java.awt.Point(680, 258)); } } diff --git a/scripts/map/onUserEnter/914000100.js b/scripts/map/onUserEnter/914000100.js index 6ec1755650..108f8c451c 100644 --- a/scripts/map/onUserEnter/914000100.js +++ b/scripts/map/onUserEnter/914000100.js @@ -21,5 +21,5 @@ */ function start(ms) { - ms.getPlayer().updateQuestInfo(21000, "1"); + ms.setQuestProgress(21000, 21002, 1); } \ No newline at end of file diff --git a/scripts/map/onUserEnter/925040100.js b/scripts/map/onUserEnter/925040100.js index 14f9c3dce8..738b9c75ac 100644 --- a/scripts/map/onUserEnter/925040100.js +++ b/scripts/map/onUserEnter/925040100.js @@ -4,7 +4,7 @@ function start(ms){ var player = ms.getPlayer(); var map = player.getMap(); - if(ms.isQuestStarted(21747) && ms.getQuestProgress(21747, 9300351) == 0) { + if(ms.isQuestStarted(21747) && ms.getQuestProgressInt(21747, 9300351) == 0) { map.spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(9300351), new java.awt.Point(897, 51)); } } \ No newline at end of file diff --git a/scripts/map/onUserEnter/cygnusJobTutorial.js b/scripts/map/onUserEnter/cygnusJobTutorial.js new file mode 100644 index 0000000000..f7eb69877d --- /dev/null +++ b/scripts/map/onUserEnter/cygnusJobTutorial.js @@ -0,0 +1,25 @@ +/* + 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 . +*/ + +function start(ms) { + ms.displayCygnusIntro(); +} \ No newline at end of file diff --git a/src/constants/skills/Swordsman.java b/scripts/map/onUserEnter/highposition.js similarity index 83% rename from src/constants/skills/Swordsman.java rename to scripts/map/onUserEnter/highposition.js index 979a87696d..815a15c05c 100644 --- a/src/constants/skills/Swordsman.java +++ b/scripts/map/onUserEnter/highposition.js @@ -19,13 +19,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package constants.skills; -/** +/* + * Author: kevintjuh93 * - * @author BubblesDev - */ -public class Swordsman { - public static final int IMPROVED_MAX_HP_INCREASE = 1000001; - public static final int IRON_BODY = 1000003; -} \ No newline at end of file +*/ +function start(ms) { + ms.touchTheSky(); +} diff --git a/scripts/map/onUserEnter/startEreb.js b/scripts/map/onUserEnter/startEreb.js new file mode 100644 index 0000000000..7351fdf8dc --- /dev/null +++ b/scripts/map/onUserEnter/startEreb.js @@ -0,0 +1,5 @@ +function start(ms) { + if (ms.getJobId() == 1000 && ms.getLevel() >= 10) { + ms.unlockUI(); + } +} \ No newline at end of file diff --git a/scripts/npc/1012100.js b/scripts/npc/1012100.js index abebcb671a..5d44d1e5f1 100644 --- a/scripts/npc/1012100.js +++ b/scripts/npc/1012100.js @@ -34,7 +34,7 @@ spawnPnpcFee = 7000000; jobType = 3; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -93,7 +93,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1012103.js b/scripts/npc/1012103.js index aef9f6426b..f3508b0bdb 100644 --- a/scripts/npc/1012103.js +++ b/scripts/npc/1012103.js @@ -72,7 +72,10 @@ function action(mode, type, selection) { } else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150001)){ + if (cm.haveItem(5420002)){ // thanks MedicOP for noticing uncoded functionality for Hair Membership coupons + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150001)){ cm.gainItem(5150001, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/1022000.js b/scripts/npc/1022000.js index 4d4aaeae59..ccea9025d8 100644 --- a/scripts/npc/1022000.js +++ b/scripts/npc/1022000.js @@ -35,7 +35,7 @@ spawnPnpcFee = 7000000; jobType = 1; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -94,7 +94,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1032001.js b/scripts/npc/1032001.js index 98bb35d84a..c10656286c 100644 --- a/scripts/npc/1032001.js +++ b/scripts/npc/1032001.js @@ -35,7 +35,7 @@ spawnPnpcFee = 7000000; jobType = 2; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -94,7 +94,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1032109.js b/scripts/npc/1032109.js index 80c74e25b2..a21274ad52 100644 --- a/scripts/npc/1032109.js +++ b/scripts/npc/1032109.js @@ -9,6 +9,11 @@ var status; var mobId = 2220100; //Blue Mushroom function start(){ + if (!cm.isQuestStarted(20718)) { // thanks Stray, Ari + cm.dispose(); + return; + } + status = -1; action(1, 0, 0); } diff --git a/scripts/npc/1052001.js b/scripts/npc/1052001.js index a9ae74c161..9ab5b9f37d 100644 --- a/scripts/npc/1052001.js +++ b/scripts/npc/1052001.js @@ -34,7 +34,7 @@ spawnPnpcFee = 7000000; jobType = 4; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -95,7 +95,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1052007.js b/scripts/npc/1052007.js index 8636978a81..27a0f1a4d6 100644 --- a/scripts/npc/1052007.js +++ b/scripts/npc/1052007.js @@ -66,7 +66,7 @@ function action(mode, type, selection) { ticketSelection = selection; if (ticketSelection > -1) { cm.gainItem(4031035 + ticketSelection, -1); - cm.warp(103000897 + (ticketSelection * 3)); + cm.warp(103000897 + (ticketSelection * 3), "st00"); // thanks IxianMace for noticing a few scripts having misplaced warp SP's hasTicket = false; cm.dispose(); return; diff --git a/scripts/npc/1052008.js b/scripts/npc/1052008.js index 33176a4bf1..095fd671cf 100644 --- a/scripts/npc/1052008.js +++ b/scripts/npc/1052008.js @@ -28,6 +28,6 @@ function start() { cm.gainItem(4031039,1); else cm.gainItem(4020000 + ((Math.random()*5)|0), 1); - cm.warp(103000100); + cm.warp(103000100, 0); cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1052009.js b/scripts/npc/1052009.js index 6a83c44ff1..f1de2aab88 100644 --- a/scripts/npc/1052009.js +++ b/scripts/npc/1052009.js @@ -28,6 +28,6 @@ function start() { cm.gainItem(4031040,1); else cm.gainItem(prizes[parseInt(Math.random() * prizes.length)],1); - cm.warp(103000100); + cm.warp(103000100, 0); cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1052010.js b/scripts/npc/1052010.js index 4e072e4822..7f75e4c2ad 100644 --- a/scripts/npc/1052010.js +++ b/scripts/npc/1052010.js @@ -28,6 +28,6 @@ function start() { cm.gainItem(4031041,1); else cm.gainItem(prizes[parseInt(Math.random() * prizes.length)],1); - cm.warp(103000100); + cm.warp(103000100, 0); cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1052012.js b/scripts/npc/1052012.js index 8c03fb6da3..bb683cac66 100644 --- a/scripts/npc/1052012.js +++ b/scripts/npc/1052012.js @@ -53,7 +53,7 @@ function action(mode, type, selection) { cm.sendOk("Oh, you don't have the money, right? Sorry, I can't let you in."); } else { cm.gainMeso(-5000); - cm.warp(193000000); + cm.warp(193000000, "out00"); } cm.dispose(); diff --git a/scripts/npc/1052100.js b/scripts/npc/1052100.js index 826885bc6d..127be5cc40 100644 --- a/scripts/npc/1052100.js +++ b/scripts/npc/1052100.js @@ -78,7 +78,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150003)){ + if (cm.haveItem(5420003)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150003)){ cm.gainItem(5150003, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/1061014.js b/scripts/npc/1061014.js index 545218e1e1..d5fb6da0f1 100644 --- a/scripts/npc/1061014.js +++ b/scripts/npc/1061014.js @@ -65,8 +65,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l\r\n\#L3#I would like to see info about this expedition...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -99,8 +104,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/1061018.js b/scripts/npc/1061018.js index 1fd6b88c23..a603b7044b 100644 --- a/scripts/npc/1061018.js +++ b/scripts/npc/1061018.js @@ -28,7 +28,7 @@ function action(mode, type, selection) { } } else if(status == 1){ if(cm.getEventInstance().isEventCleared()) { - cm.warp(cm.getMapId() == 105100300 ? 105100301 : 105100401); + cm.warp(cm.getMapId() == 105100300 ? 105100301 : 105100401, 0); } else { cm.warp(105100100); } diff --git a/scripts/npc/1063012.js b/scripts/npc/1063012.js index 54b0167de3..c5d98a2b9f 100644 --- a/scripts/npc/1063012.js +++ b/scripts/npc/1063012.js @@ -21,12 +21,15 @@ */ var status = -1; +/* +Custom Quest 100300 +*/ function activateShamanRock(slot,progress) { - var active = (progress >> slot) % 2; - if(!active) { - progress |= (1 << slot); + var ch = progress[slot]; + if(ch == '0') { + var nextProgress = progress.substr(0, slot) + '1' + progress.substr(slot + 1); - cm.updateQuest(2236, progress); + cm.setQuestProgress(2236, nextProgress); cm.gainItem(4032263, -1); cm.sendOk("The seal took it's place, repelling the evil in the area."); return 1; @@ -45,7 +48,11 @@ function start() { else if(map == 105070000) activateShamanRock(2,progress); else if(map == 105090000) { // workaround... TWO SAME NPC ID ON SAME MAP - if(!activateShamanRock(3,progress)) { + var npcOid = cm.getQuestProgressInt(100300, 1); + if (npcOid == 0) { + activateShamanRock(3,progress); + cm.setQuestProgress(100300, 1, cm.getNpcObjectId()); + } else if (cm.getNpcObjectId() != npcOid) { activateShamanRock(4,progress); } } diff --git a/scripts/npc/1063013.js b/scripts/npc/1063013.js index 54b0167de3..c5d98a2b9f 100644 --- a/scripts/npc/1063013.js +++ b/scripts/npc/1063013.js @@ -21,12 +21,15 @@ */ var status = -1; +/* +Custom Quest 100300 +*/ function activateShamanRock(slot,progress) { - var active = (progress >> slot) % 2; - if(!active) { - progress |= (1 << slot); + var ch = progress[slot]; + if(ch == '0') { + var nextProgress = progress.substr(0, slot) + '1' + progress.substr(slot + 1); - cm.updateQuest(2236, progress); + cm.setQuestProgress(2236, nextProgress); cm.gainItem(4032263, -1); cm.sendOk("The seal took it's place, repelling the evil in the area."); return 1; @@ -45,7 +48,11 @@ function start() { else if(map == 105070000) activateShamanRock(2,progress); else if(map == 105090000) { // workaround... TWO SAME NPC ID ON SAME MAP - if(!activateShamanRock(3,progress)) { + var npcOid = cm.getQuestProgressInt(100300, 1); + if (npcOid == 0) { + activateShamanRock(3,progress); + cm.setQuestProgress(100300, 1, cm.getNpcObjectId()); + } else if (cm.getNpcObjectId() != npcOid) { activateShamanRock(4,progress); } } diff --git a/scripts/npc/1072002.js b/scripts/npc/1072002.js index dad871dbfa..3f2ed5c98e 100644 --- a/scripts/npc/1072002.js +++ b/scripts/npc/1072002.js @@ -69,7 +69,7 @@ function action(mode, type, selection) { cm.gainItem(4031010, -1); cm.sendOk("You will have to collect me #b30 #t4031013##k. Good luck.") } else if (status == 4) { - cm.warp(108000100); + cm.warp(108000100, 0); cm.dispose(); } else { diff --git a/scripts/npc/1072008.js b/scripts/npc/1072008.js index 78226f09d1..03a5973b8a 100644 --- a/scripts/npc/1072008.js +++ b/scripts/npc/1072008.js @@ -5,36 +5,52 @@ **/ var status; - + function start() { status = -1; - action(1,0,0); + action(1, 0, 0); } -function action(mode,type,selection) { - if (status == -1) { - if (cm.getMapId() == 108000502) { - if (!(cm.haveItem(4031856,15))) { - cm.sendNext("Go, and get me 15 #b#t4031856##k."); - cm.dispose(); - } else { - status = 2; - cm.sendNext("Wow, you have brought me 15 #b#t4031856##k! Congratulations. Let me warp you out now."); - } - } else if (cm.getMapId() == 108000501) { - if (!(cm.haveItem(4031857,15))) { - cm.sendNext("Go, and get me 15 #b#t4031857##k."); - cm.dispose(); - } else { - status = 2; - cm.sendNext("Wow, you have brought me 15 #b#t4031857##k! Congratulations. Let me warp you out now."); - } - } else { - cm.sendNext("Error. Please report this."); - cm.dispose(); - } - } else if (status == 2) { - cm.warp(120000101,0); - cm.dispose(); +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if (cm.getMapId() == 108000502) { + if (!(cm.haveItem(4031856,15))) { + cm.sendSimple("You haven't brought me all the crystals yet. I'm looking forward for your progress, mate! \r\n#b#L1#I would like to leave#l"); + } else { + status++; + cm.sendNext("Wow, you have brought me 15 #b#t4031856##k! Congratulations. Let me warp you out now."); + } + } else if (cm.getMapId() == 108000501) { + if (!(cm.haveItem(4031857,15))) { + cm.sendSimple("You haven't brought me all the crystals yet. I'm looking forward for your progress, mate! \r\n#b#L1#I would like to leave#l"); + } else { + status++; + cm.sendNext("Wow, you have brought me 15 #b#t4031857##k! Congratulations. Let me warp you out now."); + } + } else { + cm.sendNext("Error. Please report this."); + cm.dispose(); + } + } else if (status == 1) { // thanks Lame for noticing players getting stuck in area in certain scenarios + cm.removeAll(4031856); + cm.removeAll(4031857); + cm.warp(120000101,0); + cm.dispose(); + } else if (status == 2) { + cm.warp(120000101,0); + cm.dispose(); + } } -} \ No newline at end of file +} diff --git a/scripts/npc/1090000.js b/scripts/npc/1090000.js index 28a86324f4..9ddce6bbbd 100644 --- a/scripts/npc/1090000.js +++ b/scripts/npc/1090000.js @@ -39,8 +39,7 @@ function start() { if (cm.getEventInstance() != null) { // missing script for skill test found thanks to Jade™ advQuest = 5; // string visibility thanks to iPunchEm & Glvelturall cm.sendNext("Not bad at all. Let's discuss this outside!"); - cm.setQuestProgress(6330, 0, 1); - } else if (cm.getQuestProgress(6330, 0) == 0) { + } else if (cm.getQuestProgressInt(6330, 6331) == 0) { advQuest = 1; cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it."); } else { @@ -54,8 +53,7 @@ function start() { if (cm.getEventInstance() != null) { advQuest = 6; cm.sendNext("Not bad at all. Let's discuss this outside!"); - cm.setQuestProgress(6370, 0, 1); - } else if (cm.getQuestProgress(6370, 0) == 0) { + } else if (cm.getQuestProgressInt(6370, 6371) == 0) { advQuest = 2; cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it."); } else { @@ -65,7 +63,7 @@ function start() { cm.sendNext("Congratulations. You have managed to pass my test. I'll teach you a new skill called \"Battleship\".\r\n\r\n #s5221006# #b#q5221006##k"); } - } else if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + } else if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -126,6 +124,12 @@ function action(mode, type, selection) { cm.sendOk("Unlike most of the other skills you used as a Pirate, this one definitely is different. You can actually ride the 'Battleship' and attack enemies with it. Your DEF level will increase for the time you're on board, so that'll help you tremendously in combat situations. May you become the best Gunslinger out there..."); } } else { + if (advQuest < 6) { + cm.setQuestProgress(6330, 6331, 2); + } else { + cm.setQuestProgress(6370, 6371, 2); + } + cm.warp(120000101); } @@ -138,7 +142,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { @@ -222,7 +226,7 @@ function action(mode, type, selection) { cm.sendOk("All the training maps are currently in use. Please try again later."); cm.dispose(); } else { - cm.warp(map); + cm.warp(map, 0); cm.dispose(); return; } diff --git a/scripts/npc/1092019.js b/scripts/npc/1092019.js index 41b6ad999b..96e333cc35 100644 --- a/scripts/npc/1092019.js +++ b/scripts/npc/1092019.js @@ -64,7 +64,7 @@ function action(mode, type, selection) { cm.sendOk("Who are you talking to me? If you're just bored, go bother somebody else."); cm.dispose(); } else { - seagullProgress = cm.getQuestProgress(6400, 0); + seagullProgress = cm.getQuestProgressInt(6400, 1); if (seagullProgress == 0) { seagullIdx = Math.floor(Math.random() * seagullQuestion.length); @@ -90,7 +90,7 @@ function action(mode, type, selection) { var answer = cm.getText(); if (answer == seagullAnswer[seagullIdx]) { cm.sendNext("What! I can't believe how incredibly smart you are! Incredible! In the seagull world, that kind of intellingence would give you a Ph.D. and then some. You're really amazing... I can't believe it... I simply can't believe it!"); - cm.setQuestProgress(6400, 0, 1); + cm.setQuestProgress(6400, 1, 1); cm.dispose(); } else { cm.sendOk("Hmm, that's not quite how I recall it. Try again!"); @@ -99,9 +99,11 @@ function action(mode, type, selection) { } else if (seagullProgress != 2) { cm.sendNextPrev("Anyway, only one of 9 Barts is the real Bart. You know that Pirates are known for the strength of their friendships and camaraderie with their fellow pirates. If you're a true pirate, you should be able to find your own mate with ease. Alright then, I'll send you to the room where Bart is."); } else { - cm.gainExp(1000000); - cm.teachSkill(5221003, 0, 10, -1); - cm.forceCompleteQuest(6400); + //cm.gainExp(1000000); + //cm.teachSkill(5221003, 0, 10, -1); + //cm.forceCompleteQuest(6400); + + cm.sendNextPrev("You have met all my challenges, and passed! Good job!"); cm.dispose(); } } else if (status == 3) { diff --git a/scripts/npc/1092090.js b/scripts/npc/1092090.js index 2dac576325..6b1cde8950 100644 --- a/scripts/npc/1092090.js +++ b/scripts/npc/1092090.js @@ -21,7 +21,7 @@ */ function start() { - if(cm.getQuestProgress(2180, 0) == 1) { + if(cm.getQuestProgressInt(2180, 1) == 1) { cm.sendNext("You have taken milk from this cow recently, check another cow."); cm.dispose(); return; @@ -32,19 +32,19 @@ function start() { cm.gainItem(4031847, -1); cm.gainItem(4031848, 1); - cm.setQuestProgress(2180, 0, 1); + cm.setQuestProgress(2180, 1, 1); } else if (cm.canHold(4031849, 1) && cm.haveItem(4031848)) { cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); cm.gainItem(4031848, -1); cm.gainItem(4031849, 1); - cm.setQuestProgress(2180, 0, 1); + cm.setQuestProgress(2180, 1, 1); } else if (cm.canHold(4031850) && cm.haveItem(4031849)) { cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); cm.gainItem(4031849, -1); cm.gainItem(4031850, 1); - cm.setQuestProgress(2180, 0, 1); + cm.setQuestProgress(2180, 1, 1); } else { cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); } diff --git a/scripts/npc/1092091.js b/scripts/npc/1092091.js index 5d5db97d54..ec40aff0a8 100644 --- a/scripts/npc/1092091.js +++ b/scripts/npc/1092091.js @@ -21,7 +21,7 @@ */ function start() { - if(cm.getQuestProgress(2180, 0) == 2) { + if(cm.getQuestProgressInt(2180, 1) == 2) { cm.sendNext("You have taken milk from this cow recently, check another cow."); cm.dispose(); return; @@ -32,19 +32,19 @@ function start() { cm.gainItem(4031847, -1); cm.gainItem(4031848, 1); - cm.setQuestProgress(2180, 0, 2); + cm.setQuestProgress(2180, 1, 2); } else if(cm.canHold(4031849) && cm.haveItem(4031848)){ cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); cm.gainItem(4031848, -1); cm.gainItem(4031849, 1); - cm.setQuestProgress(2180, 0, 2); + cm.setQuestProgress(2180, 1, 2); } else if(cm.canHold(4031850) && cm.haveItem(4031849)){ cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); cm.gainItem(4031849, -1); cm.gainItem(4031850, 1); - cm.setQuestProgress(2180, 0, 2); + cm.setQuestProgress(2180, 1, 2); } else { cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); } diff --git a/scripts/npc/1096005.js b/scripts/npc/1096005.js index 84f971c7e2..117a444e68 100644 --- a/scripts/npc/1096005.js +++ b/scripts/npc/1096005.js @@ -18,7 +18,6 @@ function action(mode, type, selection) { cm.sendNext("All right! Let's go!"); } else if (status == 1) { cm.removeNPC(579711); - cm.removeNPC2(579711); cm.updateInfo("fire", "0"); cm.playSound("cannonshooter/fire"); cm.sendDirectionInfo("Effect/Direction4.img/effect/cannonshooter/flying/0", 7000, 0, 0, -1, -1); diff --git a/scripts/npc/1101001.js b/scripts/npc/1101001.js index e08c21fb28..138d871853 100644 --- a/scripts/npc/1101001.js +++ b/scripts/npc/1101001.js @@ -3,7 +3,7 @@ Map(s): Erev Description: 3rd job KoC Buff */ -importPackage(Packages.constants); +importPackage(Packages.constants.game); function start() { if (cm.getPlayer().isCygnus() && GameConstants.getJobBranch(cm.getJob()) > 2) { diff --git a/scripts/npc/1102003.js b/scripts/npc/1102003.js index 9436dbb648..eb9c59c847 100644 --- a/scripts/npc/1102003.js +++ b/scripts/npc/1102003.js @@ -33,7 +33,7 @@ var maxJobType = 15; function start() { var jobType = parseInt(cm.getJobId() / 100); - if (jobType >= minJobType && jobType <= maxJobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (jobType >= minJobType && jobType <= maxJobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -64,7 +64,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1104201.js b/scripts/npc/1104201.js index e4c22783ad..ee2f89b0ca 100644 --- a/scripts/npc/1104201.js +++ b/scripts/npc/1104201.js @@ -39,7 +39,7 @@ function action(mode, type, selection) { status--; if(status == 0) { - if (!(cm.isQuestCompleted(20407) || cm.isQuestStarted(20407) && cm.getQuestProgress(20407, 9001010) != 0) && cm.getMap().countMonster(9001010) == 0 && cm.getMap().getNPCById(1104002) == null) { + if (!(cm.isQuestCompleted(20407) || cm.isQuestStarted(20407) && cm.getQuestProgressInt(20407, 9001010) != 0) && cm.getMap().countMonster(9001010) == 0 && cm.getMap().getNPCById(1104002) == null) { cm.sendOk("... Hnngh... #b#h0##k, is that you...? #r#p1104002##k... She's already here... #b#h0##k, I'm truly sorry I can't help you right now in this state, just when a bigger threat appeared I could do nothing for my people.... Please I beg you, please defeat her, #b#h0##k!! ...."); cm.spawnNpc(1104002, new java.awt.Point(850, 0), cm.getMap()); } else { diff --git a/scripts/npc/1202010.js b/scripts/npc/1202010.js index da06e68e88..0978526cdf 100644 --- a/scripts/npc/1202010.js +++ b/scripts/npc/1202010.js @@ -5,7 +5,7 @@ var spawnPnpcFee = 7000000; var jobType = 21; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -36,7 +36,7 @@ function action(mode, type, selection) { return; } - if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { + if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.game.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) { cm.sendOk("There you go! Hope you will like it."); cm.gainMeso(-spawnPnpcFee); } else { diff --git a/scripts/npc/1209000.js b/scripts/npc/1209000.js index 29b94792ff..1856337261 100644 --- a/scripts/npc/1209000.js +++ b/scripts/npc/1209000.js @@ -46,7 +46,7 @@ function action(mode, type, selection) { } else if (status == 2) { cm.sendNext("The other heroes? They've left to fight the Black Mage. They're buying us time to escape. What? You want to fight with them? No! You can't! You're hurt. You must leave with us!"); } else if (status == 3) { - cm.updateQuest(21002, "1"); + //cm.setQuestProgress(21002, 1); cm.showIntro("Effect/Direction1.img/aranTutorial/Trio"); cm.dispose(); } diff --git a/scripts/npc/1300013.js b/scripts/npc/1300013.js index 29bd5ec7c8..56a7be723b 100644 --- a/scripts/npc/1300013.js +++ b/scripts/npc/1300013.js @@ -62,7 +62,7 @@ function action(mode, type, selection){ } } } else { - var questProgress = cm.getQuestProgress(2330, 3300005) + cm.getQuestProgress(2330, 3300006) + cm.getQuestProgress(2330, 3300007); //3 Yetis + var questProgress = cm.getQuestProgressInt(2330, 3300005) + cm.getQuestProgressInt(2330, 3300006) + cm.getQuestProgressInt(2330, 3300007); //3 Yetis if (!(cm.isQuestStarted(2330) && questProgress < 3)) { // thanks Vcoc for finding an exploit with boss entry through NPC cm.dispose(); return; diff --git a/scripts/npc/2010001.js b/scripts/npc/2010001.js index a1d0b976d9..d1d53104db 100644 --- a/scripts/npc/2010001.js +++ b/scripts/npc/2010001.js @@ -83,7 +83,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150005) == true){ + if (cm.haveItem(5420004)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150005) == true){ cm.gainItem(5150005, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/2010007.js b/scripts/npc/2010007.js index b70d2ca93a..5ea75f4e3a 100644 --- a/scripts/npc/2010007.js +++ b/scripts/npc/2010007.js @@ -57,8 +57,10 @@ function action(mode, type, selection) { if (cm.getPlayer().getGuildId() < 1 || cm.getPlayer().getGuildRank() != 1) { cm.sendOk("You can only increase your Guild's capacity if you are the leader."); cm.dispose(); - } else - cm.sendYesNo("Increasing your Guild capacity by #b5#k costs #b " + cm.getPlayer().getGuild().getIncreaseGuildCost(cm.getPlayer().getGuild().getCapacity()) +" mesos#k, are you sure you want to continue?"); + } else { + var MapleGuild = Java.type("net.server.guild.MapleGuild"); // thanks Conrad for noticing an issue due to call on a static method here + cm.sendYesNo("Increasing your Guild capacity by #b5#k costs #b " + MapleGuild.getIncreaseGuildCost(cm.getPlayer().getGuild().getCapacity()) +" mesos#k, are you sure you want to continue?"); + } } } else if (status == 2) { if (sel == 0 && cm.getPlayer().getGuildId() <= 0) { diff --git a/scripts/npc/2010009.js b/scripts/npc/2010009.js index 3e01a7ae29..1675483490 100644 --- a/scripts/npc/2010009.js +++ b/scripts/npc/2010009.js @@ -22,14 +22,12 @@ var status; var choice; var guildName; -var partymembers; var allianceCost = 2000000; var increaseCost = 1000000; var allianceLimit = 5; function start() { - partymembers = cm.getPartyMembers(); status = -1; action(1,0,0); } diff --git a/scripts/npc/2012027.js b/scripts/npc/2012027.js index dbc5a36f9e..9c32b31421 100644 --- a/scripts/npc/2012027.js +++ b/scripts/npc/2012027.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); // infoEx without infoNumber, must use one progress only, critical hit! - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012028.js b/scripts/npc/2012028.js index c242fd8d69..960dad19f9 100644 --- a/scripts/npc/2012028.js +++ b/scripts/npc/2012028.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012029.js b/scripts/npc/2012029.js index 7371a28642..3a242c3659 100644 --- a/scripts/npc/2012029.js +++ b/scripts/npc/2012029.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012030.js b/scripts/npc/2012030.js index f0a7eed991..05a7120a4f 100644 --- a/scripts/npc/2012030.js +++ b/scripts/npc/2012030.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012031.js b/scripts/npc/2012031.js index 5f06ac6ba2..71af8dd7db 100644 --- a/scripts/npc/2012031.js +++ b/scripts/npc/2012031.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012032.js b/scripts/npc/2012032.js index 41b41a45c2..d0d33d06ee 100644 --- a/scripts/npc/2012032.js +++ b/scripts/npc/2012032.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2012033.js b/scripts/npc/2012033.js index d066394cb3..a70af27e4f 100644 --- a/scripts/npc/2012033.js +++ b/scripts/npc/2012033.js @@ -50,13 +50,13 @@ function action(mode, type, selection) { cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027])); if(cm.isQuestStarted(3114)) { - var idx = cm.getQuestProgress(3114, 7777); + var idx = -1 * cm.getQuestProgressInt(3114); - if(idx != -1) { + if(idx > -1) { var nextNote = harpSong[idx]; if(harpNote != nextNote) { - cm.setQuestProgress(3114, 7777, 0); + cm.setQuestProgress(3114, 0); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed")); @@ -70,7 +70,7 @@ function action(mode, type, selection) { if(idx == 45) { // finished lullaby cm.message("Twinkle, twinkle, little star, how I wonder what you are."); - cm.setQuestProgress(3114, 7777, -1); + cm.setQuestProgress(3114, 42); cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear")); cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear")); @@ -88,7 +88,7 @@ function action(mode, type, selection) { } } - cm.setQuestProgress(3114, 7777, idx + 1); + cm.setQuestProgress(3114, -1 * (idx + 1)); } } } diff --git a/scripts/npc/2013001.js b/scripts/npc/2013001.js index 5ee0327ee6..699dd060bb 100644 --- a/scripts/npc/2013001.js +++ b/scripts/npc/2013001.js @@ -278,7 +278,7 @@ function action(mode, type, selection) { if(cm.getMap().countMonsters() > 0) { cm.sendNext("This is the hidden room of the tower. After eliminating all monsters on this room, talk to me to gain access to the treasure room, leaving the center tower access behind."); } else { - cm.warp(920011100); + cm.warp(920011100, "st00"); } break; } diff --git a/scripts/npc/2020008.js b/scripts/npc/2020008.js index ce8ab3516e..dcaec7b027 100644 --- a/scripts/npc/2020008.js +++ b/scripts/npc/2020008.js @@ -142,7 +142,7 @@ function action(mode, type, selection){ if (cm.getPlayer().getLevel() >= 50){ cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); - if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); + if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); diff --git a/scripts/npc/2020009.js b/scripts/npc/2020009.js index 5e759803ef..17273222ae 100644 --- a/scripts/npc/2020009.js +++ b/scripts/npc/2020009.js @@ -114,7 +114,7 @@ function action(mode, type, selection){ if (cm.getPlayer().getLevel() >= 50){ cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); - if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); + if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); diff --git a/scripts/npc/2020010.js b/scripts/npc/2020010.js index b4de6203d5..7716da2276 100644 --- a/scripts/npc/2020010.js +++ b/scripts/npc/2020010.js @@ -115,7 +115,7 @@ function action(mode, type, selection){ if (cm.getPlayer().getLevel() >= 50){ cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); - if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); + if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); diff --git a/scripts/npc/2020011.js b/scripts/npc/2020011.js index 811ca153f2..670f76d8f6 100644 --- a/scripts/npc/2020011.js +++ b/scripts/npc/2020011.js @@ -114,7 +114,7 @@ function action(mode, type, selection){ if (cm.getPlayer().getLevel() >= 50){ cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); - if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); + if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); diff --git a/scripts/npc/2020013.js b/scripts/npc/2020013.js index f18199331d..8e593311d4 100644 --- a/scripts/npc/2020013.js +++ b/scripts/npc/2020013.js @@ -113,7 +113,7 @@ function action(mode, type, selection){ if (cm.getPlayer().getLevel() >= 50){ cm.sendOk("The Chief's Residence Council grants you #bconcession#k to make part of the #rcounteroffensive team against Zakum#k. Good luck on your journey ahead."); if(!(cm.isQuestStarted(100200) || cm.isQuestCompleted(100200))) cm.startQuest(100200); - if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); + if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS && !cm.isQuestCompleted(100201)) cm.completeQuest(100201); }else cm.sendOk("You're way too weak to make part of the #rcounteroffensive team against Zakum#k. Reach at least #blevel 50#k, then talk to me."); cm.dispose(); diff --git a/scripts/npc/2030000.js b/scripts/npc/2030000.js index 77b591c37b..fc7dd3e484 100644 --- a/scripts/npc/2030000.js +++ b/scripts/npc/2030000.js @@ -33,7 +33,7 @@ var status = 0; function start() { if(cm.haveItem(4031450, 1)) { - cm.warp(921100100); + cm.warp(921100100, 1); cm.dispose(); return; } diff --git a/scripts/npc/2030013.js b/scripts/npc/2030013.js index d62e8f1ee2..70b45c4906 100644 --- a/scripts/npc/2030013.js +++ b/scripts/npc/2030013.js @@ -66,8 +66,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { // thanks Conrad for noticing exped leaders being able to still manage in-progress expeds + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -106,8 +111,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/2040016.js b/scripts/npc/2040016.js index 1c803d4cd8..3a2a99abf8 100644 --- a/scripts/npc/2040016.js +++ b/scripts/npc/2040016.js @@ -172,19 +172,7 @@ function action(mode, type, selection) { matQty = matQtySet[selectedItem]; cost = costSet[selectedItem]; } - - if (selectedType == 5){ //arrow refine - var itemSet = new Array(2060000,2061000,2060001,2061001,2060002,2061002); - var matSet = new Array(new Array (4003001,4003004),new Array (4003001,4003004),new Array (4011000,4003001,4003004),new Array (4011000,4003001,4003004), - new Array (4011001,4003001,4003005),new Array (4011001,4003001,4003005)); - var matQtySet = new Array (new Array (1,1),new Array (1,1),new Array (1,3,10),new Array (1,3,10),new Array (1,5,15),new Array (1,5,15)); - var costSet = new Array (0,0,0,0,0,0); - item = itemSet[selectedItem]; - mats = matSet[selectedItem]; - matQty = matQtySet[selectedItem]; - cost = costSet[selectedItem]; - } - + var prompt = "So, you want me to make some #t" + item + "#s? In that case, how many do you want me to make?"; cm.sendGetNumber(prompt,1,1,100) @@ -197,6 +185,19 @@ function action(mode, type, selection) { } else qty = (selection > 0) ? selection : (selection < 0 ? -selection : 1); + + // thanks kvmba for noticing arrow selection crashing players + if (selectedType == 5){ //arrow refine + var itemSet = new Array(2060000,2061000,2060001,2061001,2060002,2061002); + var matSet = new Array(new Array (4003001,4003004),new Array (4003001,4003004),new Array (4011000,4003001,4003004),new Array (4011000,4003001,4003004), + new Array (4011001,4003001,4003005),new Array (4011001,4003001,4003005)); + var matQtySet = new Array (new Array (1,1),new Array (1,1),new Array (1,3,10),new Array (1,3,10),new Array (1,5,15),new Array (1,5,15)); + var costSet = new Array (0,0,0,0,0,0); + item = itemSet[selectedItem]; + mats = matSet[selectedItem]; + matQty = matQtySet[selectedItem]; + cost = costSet[selectedItem]; + } var prompt = "You want me to make "; if (qty == 1) diff --git a/scripts/npc/2040045.js b/scripts/npc/2040045.js index 228077ff59..01f546487b 100644 --- a/scripts/npc/2040045.js +++ b/scripts/npc/2040045.js @@ -54,7 +54,7 @@ function action(mode, type, selection) { cm.sendYesNo("Would you like to leave the bonus stage?"); } else { - cm.warp(922011100); + cm.warp(922011100, "st00"); cm.dispose(); } } diff --git a/scripts/npc/2041007.js b/scripts/npc/2041007.js index 59a102cd00..05a22869a6 100644 --- a/scripts/npc/2041007.js +++ b/scripts/npc/2041007.js @@ -81,7 +81,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150007) == true){ + if (cm.haveItem(5420005)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150007) == true){ cm.gainItem(5150007, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/2042000.js b/scripts/npc/2042000.js index 010bdd7d89..1b20c94ed7 100644 --- a/scripts/npc/2042000.js +++ b/scripts/npc/2042000.js @@ -30,8 +30,8 @@ var feeMultiplier = 7.0; function start() { status = -1; - if (!Packages.constants.ServerConstants.USE_CPQ) { - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_CPQ) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { status = 0; action(1, 0, 4); } else { @@ -221,9 +221,9 @@ function action(mode, type, selection) { } } else { var party = cm.getParty().getMembers(); - if ((selection >= 0 && selection <= 3) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { + if ((selection >= 0 && selection <= 3) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { cm.sendOk("You need at least 2 players to participate in the battle!"); - } else if ((selection >= 4 && selection <= 5) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { + } else if ((selection >= 4 && selection <= 5) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { cm.sendOk("You need at least 3 players to participate in the battle!"); } else { cm.cpqLobby(selection); @@ -236,7 +236,7 @@ function action(mode, type, selection) { } else { if (status == 0) { var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l"; - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { talk += "\r\n#L4# ... Can I just refine my ores?#l"; } cm.sendSimple(talk); @@ -437,7 +437,7 @@ function action(mode, type, selection) { cm.sendNext("Oh, and do not worry about turning into a ghost. In the Monster Carnival, #byou will not lose EXP after death#k. So it's really an experience like no other!"); cm.dispose(); } else if (select == 2) { - cm.sendNext("#bProtetor#k basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector."); + cm.sendNext("#bProtector#k is basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector."); } } else if (status == 66) { cm.sendNext("Lastly, while in the Monster Carnival, #byou can not use items / recovery potions that you carry around with you. #kMeanwhile, the monsters let these items fall for good. when, and when you #bget them, the item will immediately activate#k. That's why it's important to know when to get these items."); diff --git a/scripts/npc/2042001.js b/scripts/npc/2042001.js index 35c7190b56..7d57140f9e 100644 --- a/scripts/npc/2042001.js +++ b/scripts/npc/2042001.js @@ -29,8 +29,8 @@ var feeMultiplier = 7.0; function start() { status = -1; - if (!Packages.constants.ServerConstants.USE_CPQ) { - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_CPQ) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { status = 0; action(1, 0, 4); } else { @@ -220,9 +220,9 @@ function action(mode, type, selection) { } } else { var party = cm.getParty().getMembers(); - if ((selection >= 0 && selection <= 3) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { + if ((selection >= 0 && selection <= 3) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { cm.sendOk("You need at least 2 players to participate in the battle!"); - } else if ((selection >= 4 && selection <= 5) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { + } else if ((selection >= 4 && selection <= 5) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { cm.sendOk("You need at least 3 players to participate in the battle!"); } else { cm.cpqLobby(selection); @@ -235,7 +235,7 @@ function action(mode, type, selection) { } else { if (status == 0) { var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l"; - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { talk += "\r\n#L4# ... Can I just refine my ores?#l"; } cm.sendSimple(talk); diff --git a/scripts/npc/2042002.js b/scripts/npc/2042002.js index 4a24bb8a19..8ffd18763d 100644 --- a/scripts/npc/2042002.js +++ b/scripts/npc/2042002.js @@ -29,8 +29,8 @@ var feeMultiplier = 7.0; function start() { status = -1; - if (!Packages.constants.ServerConstants.USE_CPQ) { - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_CPQ) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { status = 0; action(1, 0, 4); } else { @@ -235,7 +235,7 @@ function action(mode, type, selection) { } else { if (status == 0) { var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l"; - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { talk += "\r\n#L4# ... Can I just refine my ores?#l"; } cm.sendSimple(talk); diff --git a/scripts/npc/2042005.js b/scripts/npc/2042005.js index 5008b8ef80..efb085c16a 100644 --- a/scripts/npc/2042005.js +++ b/scripts/npc/2042005.js @@ -77,9 +77,9 @@ function action(mode, type, selection) { } } else { var party = cm.getParty().getMembers(); - if ((selection === 0 || selection === 1 ) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { + if ((selection === 0 || selection === 1 ) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) { cm.sendOk("You need at least 2 players to participate in the battle!"); - } else if ((selection === 2 ) && party.size() < (Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { + } else if ((selection === 2 ) && party.size() < (Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) { cm.sendOk("You need at least 3 players to participate in the battle!"); } else { cm.cpqLobby2(selection); diff --git a/scripts/npc/2050014.js b/scripts/npc/2050014.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050014.js +++ b/scripts/npc/2050014.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2050015.js b/scripts/npc/2050015.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050015.js +++ b/scripts/npc/2050015.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2050016.js b/scripts/npc/2050016.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050016.js +++ b/scripts/npc/2050016.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2050017.js b/scripts/npc/2050017.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050017.js +++ b/scripts/npc/2050017.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2050018.js b/scripts/npc/2050018.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050018.js +++ b/scripts/npc/2050018.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2050019.js b/scripts/npc/2050019.js index 30c337d789..26a43ecf67 100644 --- a/scripts/npc/2050019.js +++ b/scripts/npc/2050019.js @@ -45,13 +45,13 @@ function action(mode, type, selection) { if(cm.isQuestStarted(3421)) { var meteoriteId = cm.getNpc() - 2050014; - var progress = cm.getQuestProgress(3421, 0); + var progress = cm.getQuestProgressInt(3421, 1); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { if (cm.canHold(4031117, 1)) { progress |= (1 << meteoriteId); cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + cm.setQuestProgress(3421, 1, progress); } else { cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); } diff --git a/scripts/npc/2060005.js b/scripts/npc/2060005.js index d9a2009733..e2ee871223 100644 --- a/scripts/npc/2060005.js +++ b/scripts/npc/2060005.js @@ -32,12 +32,19 @@ function start() { cm.sendOk("Thanks for saving the pork."); } else if(cm.isQuestStarted(6002)) { - var em = cm.getEventManager("3rdJob_mount"); - if (em == null) - cm.sendOk("Sorry, but 3rd job advancement (mount) is closed."); - else { - if (!em.startInstance(cm.getPlayer())) { - cm.sendOk("There is currently someone in this map, come back later."); + if (cm.haveItem(4031507, 5) && cm.haveItem(4031508,5)) { + cm.sendOk("Thanks for saving the pork."); + } else { + var em = cm.getEventManager("3rdJob_mount"); + if (em == null) + cm.sendOk("Sorry, but 3rd job advancement (mount) is closed."); + else { + if (em.startInstance(cm.getPlayer())) { + cm.removeAll(4031507); + cm.removeAll(4031508); + } else { + cm.sendOk("There is currently someone in this map, come back later."); + } } } } diff --git a/scripts/npc/2071012.js b/scripts/npc/2071012.js index c50775950a..c5d24d199a 100644 --- a/scripts/npc/2071012.js +++ b/scripts/npc/2071012.js @@ -23,7 +23,7 @@ function action(mode, type, selection) { status--; if(status == 0) { - if(cm.getQuestProgress(23647, 0) != 0) { + if(cm.getQuestProgressInt(23647, 1) != 0) { cm.dispose(); return; } @@ -39,7 +39,7 @@ function action(mode, type, selection) { cm.sendNext("Teehehee~ That's your reward for taking it from me, serves you well."); cm.gainItem(4031793, -1); cm.gainFame(-5); - cm.setQuestProgress(23647, 0, 1); + cm.setQuestProgress(23647, 1, 1); cm.dispose(); } diff --git a/scripts/npc/2081009.js b/scripts/npc/2081009.js index 553e5a8596..d49ae9513a 100644 --- a/scripts/npc/2081009.js +++ b/scripts/npc/2081009.js @@ -36,7 +36,7 @@ function action(mode, type, selection) { status++; if(status == 0) { - if(cm.isQuestStarted(6180)) { + if(cm.isQuestStarted(6180) && cm.getQuestProgressInt(6180, 9300096) < 200) { cm.sendYesNo("Pay attention: during the time you stay inside the training ground make sure you #bhave equipped your #t1092041##k, it is of the utmost importance. Are you ready to proceed to the training area?"); } @@ -47,10 +47,15 @@ function action(mode, type, selection) { } else if(status == 1) { - cm.warp(924000001, 0); - cm.sendOk("Have your shield equipped until the end of the quest, or else you will need to start all over again!"); - - cm.resetQuestProgress(6180,9300096); + if (cm.getPlayer().haveItemEquipped(1092041)) { + cm.sendNext("Have your shield equipped until the end of the quest, or else you will need to start all over again!"); + } else { + cm.sendOk("Please equip the #r#t1092041##k before entering the training ground."); + cm.dispose(); + } + } + else { + cm.warp(924000001, 0); cm.dispose(); } } diff --git a/scripts/npc/2081010.js b/scripts/npc/2081010.js index 0b564e2398..019a545276 100644 --- a/scripts/npc/2081010.js +++ b/scripts/npc/2081010.js @@ -44,7 +44,7 @@ function action(mode, type, selection){ } else if(status == 1) { - cm.warp(exitMap); + cm.warp(exitMap, "st00"); cm.dispose(); } } diff --git a/scripts/npc/2082014.js b/scripts/npc/2082014.js index 1ad06b94b2..6b0eac71fd 100644 --- a/scripts/npc/2082014.js +++ b/scripts/npc/2082014.js @@ -39,7 +39,7 @@ function action(mode, type, selection) { status--; if(status == 0) { - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.openShopNPC(2082014); } else if (cm.isQuestStarted(3749)) { cm.sendOk("We've already located the enemy's ultimate weapon! Follow along the ship's bow area ahead and you will find my sister #b#p2082013##k. Report to her for futher instructions on the mission."); diff --git a/scripts/npc/2083000.js b/scripts/npc/2083000.js index f59c817e1e..ee3b07b2f6 100644 --- a/scripts/npc/2083000.js +++ b/scripts/npc/2083000.js @@ -45,7 +45,7 @@ function action(mode, type, selection) { if (status == 0) { if(cm.haveItem(4001086)) { cm.sendYesNo("Do you want to access #b#m240050400##k right now?"); - } else if(Packages.constants.ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS) { + } else if(Packages.config.YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS) { if(canBypassHTPQ()) { cm.sendYesNo("Do you want to access #b#m240050400##k right now?"); } else { diff --git a/scripts/npc/2083002.js b/scripts/npc/2083002.js index 14328826c0..c7cbd07632 100644 --- a/scripts/npc/2083002.js +++ b/scripts/npc/2083002.js @@ -34,7 +34,7 @@ function action(mode, type, selection) { cm.dispose(); else { if(cm.getMapId() > 240050400) cm.warp(240050600); - else cm.warp(240040700); + else cm.warp(240040700, "out00"); cm.dispose(); } diff --git a/scripts/npc/2083004.js b/scripts/npc/2083004.js index b0501ee64d..f13fc27bcf 100644 --- a/scripts/npc/2083004.js +++ b/scripts/npc/2083004.js @@ -64,8 +64,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -98,8 +103,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/2090004.js b/scripts/npc/2090004.js index e7dc6ff8e0..c4fd175836 100644 --- a/scripts/npc/2090004.js +++ b/scripts/npc/2090004.js @@ -217,7 +217,7 @@ function action(mode, type, selection) { complete = false; } - if(java.lang.Math.random() >= 0.9) //A lucky find! Scroll 60% + if(Math.random() >= 0.9) //A lucky find! Scroll 60% item += 1; if (!complete || !cm.canHold(item, 1)) @@ -251,7 +251,7 @@ function action(mode, type, selection) { var reward; if (rewdSet[selectedItem] instanceof Array) { var length = rewdSet[selectedItem][1] - rewdSet[selectedItem][0]; - reward = rewdSet[selectedItem][0] + java.lang.Math.round(java.lang.Math.random() * length); + reward = rewdSet[selectedItem][0] + Math.round(Math.random() * length); } else reward = rewdSet[selectedItem]; diff --git a/scripts/npc/2090100.js b/scripts/npc/2090100.js index 39b418b198..a4e7ad8b81 100644 --- a/scripts/npc/2090100.js +++ b/scripts/npc/2090100.js @@ -81,7 +81,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150025)){ + if (cm.haveItem(5420006)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150025)){ cm.gainItem(5150025, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/2091005.js b/scripts/npc/2091005.js index 65cfa36f5d..33dbc760b4 100644 --- a/scripts/npc/2091005.js +++ b/scripts/npc/2091005.js @@ -25,7 +25,7 @@ * @Map(s): Dojo Hall */ -importPackage(Packages.constants); +importPackage(Packages.config); var disabled = false; var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004); @@ -44,7 +44,7 @@ function start() { return; } - belt_points = ServerConstants.USE_FAST_DOJO_UPGRADE ? Array(10, 90, 200, 460, 850) : Array(200, 1800, 4000, 9200, 17000); + belt_points = YamlConfig.config.server.USE_FAST_DOJO_UPGRADE ? Array(10, 90, 200, 460, 850) : Array(200, 1800, 4000, 9200, 17000); belt_on_inventory = new Array(); for (var i = 0; i < belts.length; i++) { @@ -330,7 +330,7 @@ function action(mode, type, selection) { cm.sendYesNo("So, you're giving up? You're really going to leave?"); } else { if (mode == 1) { - cm.warp(925020002); + cm.warp(925020002, "st00"); } cm.dispose(); return; diff --git a/scripts/npc/2091005_old.js b/scripts/npc/2091005_old.js index ffaae970cf..028204db39 100644 --- a/scripts/npc/2091005_old.js +++ b/scripts/npc/2091005_old.js @@ -225,7 +225,7 @@ function action(mode, type, selection) { cm.sendAcceptDecline("So, you're giving up? You're really going to leave?"); } else { if (mode == 1) { - cm.warp(925020002); + cm.warp(925020002, "st00"); } cm.dispose(); } diff --git a/scripts/npc/2091009.js b/scripts/npc/2091009.js index 2e6a02cb6e..c72fb2ca9b 100644 --- a/scripts/npc/2091009.js +++ b/scripts/npc/2091009.js @@ -27,10 +27,10 @@ function action(mode, type, selection){ return; } if(cm.getText() == "Actions speak louder than words"){ - if(cm.isQuestStarted(21747) && cm.getQuestProgress(21747, 9300351) == 0) + if(cm.isQuestStarted(21747) && cm.getQuestProgressInt(21747, 9300351) == 0) cm.warp(925040100, 0); else - cm.playerMessage(5, "Although you said the right answer, some mysterious forces is blocking the way in."); + cm.playerMessage(5, "Although you said the right answer, some mysterious forces are blocking the way in."); cm.dispose(); } diff --git a/scripts/npc/2101014.js b/scripts/npc/2101014.js index 95e68a20b8..3058076035 100644 --- a/scripts/npc/2101014.js +++ b/scripts/npc/2101014.js @@ -134,9 +134,12 @@ function enterArena(arenaPlayers) { return; } else if (expedicao == null) { if (arenaPlayers != -1) { - if (cm.createExpedition(exped, true, 0, arenaPlayers)) { + var res = cm.createExpedition(exped, true, 0, arenaPlayers); + if (res == 0) { cm.warp(map, 0); cm.getPlayer().dropMessage("Your arena was created successfully. Wait for people to join the battle."); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/2101017.js b/scripts/npc/2101017.js index 03318dabce..035e2e0b94 100644 --- a/scripts/npc/2101017.js +++ b/scripts/npc/2101017.js @@ -115,7 +115,7 @@ function action(mode, type, selection) { status = 2; } } - } else if (Packages.constants.GameConstants.isAriantColiseumArena(cm.getPlayer().getMapId())) { + } else if (Packages.constants.game.GameConstants.isAriantColiseumArena(cm.getPlayer().getMapId())) { if (cm.getPlayer().getMapId() == 980010101) { exped = MapleExpeditionType.ARIANT; expedicao = cm.getExpedition(exped); diff --git a/scripts/npc/2103000.js b/scripts/npc/2103000.js index 2784b55ff9..ae5cd41956 100644 --- a/scripts/npc/2103000.js +++ b/scripts/npc/2103000.js @@ -40,9 +40,9 @@ function action(mode, type, selection) { else status--; if (status == 0 && mode == 1) { - if(cm.isQuestStarted(3900) && cm.getPlayer().getQuestInfo(3900) != 5) { + if(cm.isQuestStarted(3900) && cm.getQuestProgressInt(3900) != 5) { cm.sendOk("#b(You drink the water from the oasis and feel refreshed.)", 2); - cm.getPlayer().updateQuestInfo(3900, "5"); + cm.setQuestProgress(3900, 5); } else if(cm.isQuestCompleted(3938)) { if(cm.canHold(2210005)) { if(!cm.haveItem(2210005) && !isTigunMorphed(cm.getPlayer())) { diff --git a/scripts/npc/2103003.js b/scripts/npc/2103003.js index 50286a9e9f..98960f3301 100644 --- a/scripts/npc/2103003.js +++ b/scripts/npc/2103003.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3929)) { - if(cm.getQuestProgress(3929, 0) != 1) { - if(cm.haveItem(4031580)) { - cm.gainItem(4031580, -1); - cm.setQuestProgress(3929, 0, 1); - } + var progress = cm.getQuestProgress(3929); + var slot = 0; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031580, -1); + cm.setQuestProgress(3929, nextProgress); } } diff --git a/scripts/npc/2103004.js b/scripts/npc/2103004.js index f75ae50dbc..24d3e420f5 100644 --- a/scripts/npc/2103004.js +++ b/scripts/npc/2103004.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3929)) { - if(cm.getQuestProgress(3929, 2) != 1) { - if(cm.haveItem(4031580)) { - cm.gainItem(4031580, -1); - cm.setQuestProgress(3929, 2, 1); - } + var progress = cm.getQuestProgress(3929); + var slot = 2; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031580, -1); + cm.setQuestProgress(3929, nextProgress); } } diff --git a/scripts/npc/2103005.js b/scripts/npc/2103005.js index 8455762360..83898e072b 100644 --- a/scripts/npc/2103005.js +++ b/scripts/npc/2103005.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3929)) { - if(cm.getQuestProgress(3929, 1) != 1) { - if(cm.haveItem(4031580)) { - cm.gainItem(4031580, -1); - cm.setQuestProgress(3929, 1, 1); - } + var progress = cm.getQuestProgress(3929); + var slot = 1; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031580, -1); + cm.setQuestProgress(3929, nextProgress); } } diff --git a/scripts/npc/2103006.js b/scripts/npc/2103006.js index df5c0e26a8..d6c45109ef 100644 --- a/scripts/npc/2103006.js +++ b/scripts/npc/2103006.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3929)) { - if(cm.getQuestProgress(3929, 3) != 1) { - if(cm.haveItem(4031580)) { - cm.gainItem(4031580, -1); - cm.setQuestProgress(3929, 3, 1); - } + var progress = cm.getQuestProgress(3929); + var slot = 3; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031580, -1); + cm.setQuestProgress(3929, nextProgress); } } diff --git a/scripts/npc/2103009.js b/scripts/npc/2103009.js index dfa158a39f..33a4eaf1fa 100644 --- a/scripts/npc/2103009.js +++ b/scripts/npc/2103009.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3926)) { - if(cm.getQuestProgress(3926, 0) != 1) { - if(cm.haveItem(4031579)) { - cm.gainItem(4031579, -1); - cm.setQuestProgress(3926, 0, 1); - } + var progress = cm.getQuestProgress(3926); + var slot = 0; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031579, -1); + cm.setQuestProgress(3926, nextProgress); } } diff --git a/scripts/npc/2103010.js b/scripts/npc/2103010.js index f3026eab3c..f6c8ad13e1 100644 --- a/scripts/npc/2103010.js +++ b/scripts/npc/2103010.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3926)) { - if(cm.getQuestProgress(3926, 2) != 1) { - if(cm.haveItem(4031579)) { - cm.gainItem(4031579, -1); - cm.setQuestProgress(3926, 2, 1); - } + var progress = cm.getQuestProgress(3926); + var slot = 2; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031579, -1); + cm.setQuestProgress(3926, nextProgress); } } diff --git a/scripts/npc/2103011.js b/scripts/npc/2103011.js index 57d4b461fd..617dceae09 100644 --- a/scripts/npc/2103011.js +++ b/scripts/npc/2103011.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3926)) { - if(cm.getQuestProgress(3926, 1) != 1) { - if(cm.haveItem(4031579)) { - cm.gainItem(4031579, -1); - cm.setQuestProgress(3926, 1, 1); - } + var progress = cm.getQuestProgress(3926); + var slot = 1; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031579, -1); + cm.setQuestProgress(3926, nextProgress); } } diff --git a/scripts/npc/2103012.js b/scripts/npc/2103012.js index f520329490..63b9f4874b 100644 --- a/scripts/npc/2103012.js +++ b/scripts/npc/2103012.js @@ -43,11 +43,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3926)) { - if(cm.getQuestProgress(3926, 3) != 1) { - if(cm.haveItem(4031579)) { - cm.gainItem(4031579, -1); - cm.setQuestProgress(3926, 3, 1); - } + var progress = cm.getQuestProgress(3926); + var slot = 3; + + var ch = progress[slot]; + if(ch == '2') { + var nextProgress = progress.substr(0, slot) + '3' + progress.substr(slot + 1); + + cm.gainItem(4031579, -1); + cm.setQuestProgress(3926, nextProgress); } } diff --git a/scripts/npc/2103013.js b/scripts/npc/2103013.js index 78f339dc10..9eddad86c0 100644 --- a/scripts/npc/2103013.js +++ b/scripts/npc/2103013.js @@ -66,7 +66,7 @@ function action(mode, type, selection) { } else if (selection == 4) { cm.sendNext("Inside Pharaoh Yeti's Tomb, you can acquire a #e#b#t2022613##k#n by proving yourself capable of defeating the #bPharaoh Jr. Yeti#k, the Pharaoh's clone. Inside that box lies a very special treasure. It is the #e#b#t1132012##k#n.\r\n#i1132012:# #t1132012#\r\n\r\n And if you are somehow able to survive Hell Mode, you will receive the #e#b#t1132013##k#n.\r\n\r\n#i1132013:# #t1132013#\r\n\r\n Though, of course, Nett won't allow that to happen."); } else if (selection == 5) { - var progress = cm.getQuestProgress(29932); + var progress = cm.getQuestProgressInt(29932); if (progress >= 50000) cm.dispose(); else diff --git a/scripts/npc/2111000.js b/scripts/npc/2111000.js index 2a40b90e32..3ae1c9dba4 100644 --- a/scripts/npc/2111000.js +++ b/scripts/npc/2111000.js @@ -30,7 +30,7 @@ */ function start() { if(cm.isQuestStarted(3310) && !cm.haveItem(4031709, 1)) { - cm.warp(926120100); + cm.warp(926120100, "out00"); } else { cm.sendNext("Alchemy....and Alchemist.....both of them are important. But more importantly, it is the Magatia that tolerate everything. The honor of Magatia should be protected by me."); } diff --git a/scripts/npc/2111003.js b/scripts/npc/2111003.js index 09346b348a..82901da15b 100644 --- a/scripts/npc/2111003.js +++ b/scripts/npc/2111003.js @@ -33,7 +33,7 @@ function start() { if(cm.isQuestStarted(3335) && !cm.haveItem(4031695, 1)) { - cm.warp(926120300); + cm.warp(926120300, "out00"); cm.dispose(); } else { cm.sendOk("Emotion that I feel is real? Or just illusion coming from mechanical error?"); diff --git a/scripts/npc/2111011.js b/scripts/npc/2111011.js new file mode 100644 index 0000000000..8b8b43f8de --- /dev/null +++ b/scripts/npc/2111011.js @@ -0,0 +1,25 @@ +var status; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + cm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/npc/2111012.js b/scripts/npc/2111012.js new file mode 100644 index 0000000000..8b8b43f8de --- /dev/null +++ b/scripts/npc/2111012.js @@ -0,0 +1,25 @@ +var status; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + cm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/npc/2111013.js b/scripts/npc/2111013.js index 551ba5f46a..6a35da3229 100644 --- a/scripts/npc/2111013.js +++ b/scripts/npc/2111013.js @@ -40,7 +40,16 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3311)) { - cm.setQuestProgress(3311, 0, 1); + var progress = cm.getQuestProgressInt(3311); + + if (progress == 4) { + progress = 7; + } else { + progress = 5; + } + + cm.setQuestProgress(3311, progress); + cm.sendOk("This is a mug picture of Dr. De Lang. It seems he is adorning a locket with the emblem of the Alcadno academy, he is a retainer of the Alcadno society.", 2); } diff --git a/scripts/npc/2111014.js b/scripts/npc/2111014.js index a39a0c2c3a..5b1e454f4e 100644 --- a/scripts/npc/2111014.js +++ b/scripts/npc/2111014.js @@ -40,7 +40,16 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3311)) { - cm.setQuestProgress(3311, 1, 1); + var progress = cm.getQuestProgressInt(3311); + + if (progress == 4) { + progress = 7; + } else { + progress = 5; + } + + cm.setQuestProgress(3311, progress); + cm.sendOk("The diary of Dr. De Lang. A lot of formulas and pompous scientific texts can be found all way through the pages, but it is worth noting that in the last entry (3 weeks ago), it is written that he concluded the researches on an improvement on the blueprints for the Neo Huroids, thus making the last preparations to show it to the 'society'... No words after this...", 2); } else if(cm.isQuestStarted(3322) && !cm.haveItem(4031697, 1)) { if(cm.canHold(4031697, 1)) diff --git a/scripts/npc/2111016.js b/scripts/npc/2111016.js new file mode 100644 index 0000000000..8b8b43f8de --- /dev/null +++ b/scripts/npc/2111016.js @@ -0,0 +1,25 @@ +var status; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + cm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/npc/2111017.js b/scripts/npc/2111017.js index 49a8458d4d..95d07048f4 100644 --- a/scripts/npc/2111017.js +++ b/scripts/npc/2111017.js @@ -40,15 +40,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3339)) { - var progress = cm.getQuestProgress(3339, 0); + var progress = cm.getQuestProgressInt(23339, 1); if(progress == 3) { cm.sendGetText("The pipe reacts as the water starts flowing. A secret compartment with a keypad shows up. #bPassword#k!"); } else if(progress == 0) { - cm.setQuestProgress(3339, 0, 1); + cm.setQuestProgress(23339, 1, 1); cm.dispose(); } else if(progress < 3) { - cm.setQuestProgress(3339, 0, 0); + cm.setQuestProgress(23339, 1, 0); cm.dispose(); } else { cm.warp(261000001, 1); @@ -63,7 +63,7 @@ function action(mode, type, selection) { } } else if(status == 1) { if(cm.getText() == "my love Phyllia") { - cm.setQuestProgress(3339, 0, 4); + cm.setQuestProgress(23339, 1, 4); cm.warp(261000001, 1); cm.dispose(); } diff --git a/scripts/npc/2111018.js b/scripts/npc/2111018.js index 6065064c69..fb0bbd845e 100644 --- a/scripts/npc/2111018.js +++ b/scripts/npc/2111018.js @@ -40,15 +40,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3339)) { - var progress = cm.getQuestProgress(3339, 0); + var progress = cm.getQuestProgressInt(23339, 1); if(progress == 3) { cm.sendGetText("The pipe reacts as the water starts flowing. A secret compartment with a keypad shows up. #bPassword#k!"); } else if(progress == 2) { - cm.setQuestProgress(3339, 0, 3); + cm.setQuestProgress(23339, 1, 3); cm.sendGetText("The pipe reacts as the water starts flowing. A secret compartment with a keypad shows up. #bPassword#k!"); } else if(progress < 3) { - cm.setQuestProgress(3339, 0, 0); + cm.setQuestProgress(23339, 1, 0); cm.dispose(); } else { cm.warp(261000001, 1); @@ -63,7 +63,7 @@ function action(mode, type, selection) { } } else if(status == 1) { if(cm.getText() == "my love Phyllia") { - cm.setQuestProgress(3339, 0, 4); + cm.setQuestProgress(23339, 1, 4); cm.warp(261000001, 1); cm.dispose(); } diff --git a/scripts/npc/2111019.js b/scripts/npc/2111019.js index bde977fafc..28674aae7f 100644 --- a/scripts/npc/2111019.js +++ b/scripts/npc/2111019.js @@ -40,15 +40,15 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3339)) { - var progress = cm.getQuestProgress(3339, 0); + var progress = cm.getQuestProgressInt(23339, 1); if(progress == 3) { cm.sendGetText("The pipe reacts as the water starts flowing. A secret compartment with a keypad shows up. #bPassword#k!"); } else if(progress == 1) { - cm.setQuestProgress(3339, 0, 2); + cm.setQuestProgress(23339, 1, 2); cm.dispose(); } else if(progress < 3) { - cm.setQuestProgress(3339, 0, 0); + cm.setQuestProgress(23339, 1, 0); cm.dispose(); } else { cm.warp(261000001, 1); @@ -63,7 +63,7 @@ function action(mode, type, selection) { } } else if(status == 1) { if(cm.getText() == "my love Phyllia") { - cm.setQuestProgress(3339, 0, 4); + cm.setQuestProgress(23339, 1, 4); cm.warp(261000001, 1); cm.dispose(); } diff --git a/scripts/npc/2111020.js b/scripts/npc/2111020.js index bc49a9de13..9e6dc25e4b 100644 --- a/scripts/npc/2111020.js +++ b/scripts/npc/2111020.js @@ -40,13 +40,13 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3345)) { - var progress = cm.getQuestProgress(3345, 0); + var progress = cm.getQuestProgressInt(3345); if(progress == 0) { - cm.setQuestProgress(3345, 0, 1); + cm.setQuestProgress(3345, 1); cm.dispose(); } else if(progress < 4) { - cm.setQuestProgress(3345, 0, 0); + cm.setQuestProgress(3345, 0); cm.dispose(); } else { cm.dispose(); diff --git a/scripts/npc/2111021.js b/scripts/npc/2111021.js index 6f46abc299..d800d3c52f 100644 --- a/scripts/npc/2111021.js +++ b/scripts/npc/2111021.js @@ -40,13 +40,13 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3345)) { - var progress = cm.getQuestProgress(3345, 0); + var progress = cm.getQuestProgressInt(3345); if(progress == 1) { - cm.setQuestProgress(3345, 0, 2); + cm.setQuestProgress(3345, 2); cm.dispose(); } else if(progress < 4) { - cm.setQuestProgress(3345, 0, 0); + cm.setQuestProgress(3345, 0); cm.dispose(); } else { cm.dispose(); diff --git a/scripts/npc/2111022.js b/scripts/npc/2111022.js index 4fd1ebb28f..17d305d19b 100644 --- a/scripts/npc/2111022.js +++ b/scripts/npc/2111022.js @@ -40,13 +40,13 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3345)) { - var progress = cm.getQuestProgress(3345, 0); + var progress = cm.getQuestProgressInt(3345); if(progress == 2) { - cm.setQuestProgress(3345, 0, 3); + cm.setQuestProgress(3345, 3); cm.dispose(); } else if(progress < 4) { - cm.setQuestProgress(3345, 0, 0); + cm.setQuestProgress(3345, 0); cm.dispose(); } else { cm.dispose(); diff --git a/scripts/npc/2111023.js b/scripts/npc/2111023.js index e8cb68005a..9516d06c41 100644 --- a/scripts/npc/2111023.js +++ b/scripts/npc/2111023.js @@ -40,10 +40,10 @@ function action(mode, type, selection) { if(status == 0) { if(cm.isQuestStarted(3345)) { - var progress = cm.getQuestProgress(3345, 0); + var progress = cm.getQuestProgressInt(3345); if(progress == 3 && cm.haveItem(4031739, 1) && cm.haveItem(4031740, 1) && cm.haveItem(4031741, 1)) { - cm.setQuestProgress(3345, 0, 4); + cm.setQuestProgress(3345, 4); cm.gainItem(4031739, -1); cm.gainItem(4031740, -1); cm.gainItem(4031741, -1); @@ -51,7 +51,7 @@ function action(mode, type, selection) { cm.sendOk("(As you place the shards a light shines over the circle, repelling whatever omens were brewing inside the artifact.)", 2); cm.dispose(); } else if(progress < 4) { - cm.setQuestProgress(3345, 0, 0); + cm.setQuestProgress(3345, 0); cm.dispose(); } else { cm.dispose(); diff --git a/scripts/npc/2112003.js b/scripts/npc/2112003.js index fcf7eca6a3..6e4c8f0be5 100644 --- a/scripts/npc/2112003.js +++ b/scripts/npc/2112003.js @@ -50,7 +50,7 @@ function action(mode, type, selection) { if(status == 0) { cm.sendYesNo("We must keep fighting to save Romeo, please keep your pace. If you are not feeling so well to continue, your companions and I will understand... So, are you going to retreat?"); } else if(status == 1) { - cm.warp(926110700); + cm.warp(926110700, 0); cm.dispose(); } } else { diff --git a/scripts/npc/2112004.js b/scripts/npc/2112004.js index 1b4c8510ff..b637a6ba64 100644 --- a/scripts/npc/2112004.js +++ b/scripts/npc/2112004.js @@ -50,7 +50,7 @@ function action(mode, type, selection) { if(status == 0) { cm.sendYesNo("We must keep fighting to save Juliet, please keep your pace. If you are not feeling so well to continue, your companions and I will understand... So, are you going to retreat?"); } else if(status == 1) { - cm.warp(926100700); + cm.warp(926100700, 0); cm.dispose(); } } else { diff --git a/scripts/npc/2112005.js b/scripts/npc/2112005.js index 29860dce6b..14de113793 100644 --- a/scripts/npc/2112005.js +++ b/scripts/npc/2112005.js @@ -93,7 +93,7 @@ function action(mode, type, selection) { cm.sendYesNo("We must keep fighting to save Romeo, please keep your pace. If you are not feeling so well to continue, your companions and I will understand... So, are you going to retreat?"); } } else { - cm.warp(926110700); + cm.warp(926110700, 0); cm.dispose(); } } else { @@ -111,15 +111,15 @@ function action(mode, type, selection) { if(cm.canHold(4001160)) { cm.gainItem(4001160, 1); - if(eim.getIntProperty("normalClear") == 1) cm.warp(926110600); - else cm.warp(926110500); + if(eim.getIntProperty("normalClear") == 1) cm.warp(926110600, 0); + else cm.warp(926110500, 0); } else { cm.sendOk("Make sure you have a space on your ETC inventory."); } cm.dispose(); } else { - cm.warp(926110600); + cm.warp(926110600, 0); cm.dispose(); } } diff --git a/scripts/npc/2112006.js b/scripts/npc/2112006.js index 3ab1911949..2a1ad0c222 100644 --- a/scripts/npc/2112006.js +++ b/scripts/npc/2112006.js @@ -93,7 +93,7 @@ function action(mode, type, selection) { cm.sendYesNo("We must keep fighting to save Juliet, please keep your pace. If you are not feeling so well to continue, your companions and I will understand... So, are you going to retreat?"); } } else { - cm.warp(926100700); + cm.warp(926100700, 0); cm.dispose(); } } else { @@ -111,15 +111,15 @@ function action(mode, type, selection) { if(cm.canHold(4001159)) { cm.gainItem(4001159, 1); - if(eim.getIntProperty("normalClear") == 1) cm.warp(926100600); - else cm.warp(926100500); + if(eim.getIntProperty("normalClear") == 1) cm.warp(926100600, 0); + else cm.warp(926100500, 0); } else { cm.sendOk("Make sure you have a space on your ETC inventory."); } cm.dispose(); } else { - cm.warp(926100600); + cm.warp(926100600, 0); cm.dispose(); } } diff --git a/scripts/npc/2112011.js b/scripts/npc/2112011.js index 4e3b223f7d..e6f2cc19e9 100644 --- a/scripts/npc/2112011.js +++ b/scripts/npc/2112011.js @@ -51,7 +51,7 @@ function action(mode, type, selection) { } else { if(!cm.isQuestCompleted(7770)) cm.completeQuest(7770); - cm.warp(926110600); + cm.warp(926110600, 0); cm.dispose(); } } diff --git a/scripts/npc/2112016.js b/scripts/npc/2112016.js index dd9db1bf18..2615ab3ac6 100644 --- a/scripts/npc/2112016.js +++ b/scripts/npc/2112016.js @@ -26,15 +26,15 @@ function start() { if(cm.isQuestStarted(3367)) { - var c = cm.getQuestProgress(3367, 30); - if(c == 30) { + var c = cm.getQuestProgressInt(3367, 30); + if(c >= 30) { cm.sendNext("(All files have been organized. Report the found files to Yulete.)", 2); cm.dispose(); return; } var book = (cm.getNpcObjectId() % 30); - var prog = cm.getQuestProgress(3367, book); + var prog = cm.getQuestProgressInt(3367, book); if(prog == 0) { c++; @@ -45,7 +45,7 @@ function start() { return; } else { cm.gainItem(4031797, 1); - cm.setQuestProgress(3367, 31, cm.getQuestProgress(3367, 31) + 1); + cm.setQuestProgress(3367, 31, cm.getQuestProgressInt(3367, 31) + 1); } } diff --git a/scripts/npc/2131001.js b/scripts/npc/2131001.js index ae9629790e..0c5ed72e01 100644 --- a/scripts/npc/2131001.js +++ b/scripts/npc/2131001.js @@ -13,13 +13,14 @@ function action(mode, type, selection) { return; } if (status == 0) { - cm.sendSimple("My name is Pergen, I am the strongest magician around these parts.#b\r\n#L0#Hey, take these rubbles. You can perform your magic on them.#l"); + cm.sendSimple("My name is #p2131001#, I am the strongest magician around these parts.#b\r\n#L0#Hey, take these rubbles. You can perform your magic on them.#l"); } else if (status == 1) { if (!cm.haveItem(exchangeItem, 100)) { cm.sendNext("You don't have enough... I need at least 100."); cm.dispose(); } else { - cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100)); + // thanks yuxaij for noticing a few methods having parameters not matching the expected Math library function parameter types + cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, Math.min(300, cm.itemQuantity(exchangeItem) / 100)); } } else if (status == 2) { if (selection >= 1 && selection <= cm.itemQuantity(exchangeItem) / 100) { diff --git a/scripts/npc/2131002.js b/scripts/npc/2131002.js index b6f86ad3b1..7c253084cc 100644 --- a/scripts/npc/2131002.js +++ b/scripts/npc/2131002.js @@ -5,6 +5,6 @@ */ function start(){ - cm.sendOk("My name is Euryth... As you can see, I am a fairy. People tell me I do not act fairy-like, but... I like making things out of metal objects. Shhh, don't tell this to anyone, but I also like MMA."); + cm.sendOk("My name is #p2131002#... As you can see, I am a fairy. People tell me I do not act fairy-like, but... I like making things out of metal objects. Shhh, don't tell this to anyone, but I also like MMA."); cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/2131003.js b/scripts/npc/2131003.js index 1c69b58d66..dd5be16d12 100644 --- a/scripts/npc/2131003.js +++ b/scripts/npc/2131003.js @@ -19,7 +19,7 @@ function action(mode, type, selection) { cm.sendNext("You don't have enough... I need at least 100."); cm.dispose(); } else { - cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100)); + cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, Math.min(300, cm.itemQuantity(exchangeItem) / 100)); } } else if (status == 2) { if (selection >= 1 && selection <= cm.itemQuantity(exchangeItem) / 100) { diff --git a/scripts/npc/2131005.js b/scripts/npc/2131005.js index 4a1742962c..e51c35030a 100644 --- a/scripts/npc/2131005.js +++ b/scripts/npc/2131005.js @@ -19,7 +19,7 @@ function action(mode, type, selection) { cm.sendNext("You don't have enough... I need at least 100."); cm.dispose(); } else { - cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100)); + cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, Math.min(300, cm.itemQuantity(exchangeItem) / 100)); } } else if (status == 2) { if (selection >= 1 && selection <= cm.itemQuantity(exchangeItem) / 100) { diff --git a/scripts/npc/2131006.js b/scripts/npc/2131006.js index c2910b80f4..a19b093243 100644 --- a/scripts/npc/2131006.js +++ b/scripts/npc/2131006.js @@ -19,7 +19,7 @@ function action(mode, type, selection) { cm.sendNext("You don't have enough... I need at least 100."); cm.dispose(); } else { - cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100)); + cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, Math.min(300, cm.itemQuantity(exchangeItem) / 100)); } } else if (status == 2) { if (selection >= 1 && selection <= cm.itemQuantity(exchangeItem) / 100) { diff --git a/scripts/npc/2131007.js b/scripts/npc/2131007.js index d145bd0848..fffc7fb562 100644 --- a/scripts/npc/2131007.js +++ b/scripts/npc/2131007.js @@ -19,7 +19,7 @@ function action(mode, type, selection) { cm.sendNext("You don't have enough... I need at least 100."); cm.dispose(); } else { - cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, java.lang.Math.min(300, cm.itemQuantity(exchangeItem) / 100)); + cm.sendGetNumber("Hey, that's a good idea! I can give you #i4310000#Perfect Pitch for each 100 #i" + exchangeItem + "##t" + exchangeItem + "# you give me. How many do you want? (Current Items: " + cm.itemQuantity(exchangeItem) + ")", Math.min(300, cm.itemQuantity(exchangeItem) / 100), 1, Math.min(300, cm.itemQuantity(exchangeItem) / 100)); } } else if (status == 2) { if (selection >= 1 && selection <= cm.itemQuantity(exchangeItem) / 100) { diff --git a/scripts/npc/2133000.js b/scripts/npc/2133000.js index c342d6e69a..a91a790498 100644 --- a/scripts/npc/2133000.js +++ b/scripts/npc/2133000.js @@ -111,9 +111,9 @@ function action(mode, type, selection) { cm.dispose(); } } else if (selection == 2){ - if (cm.haveItem(1032061) && !cm.haveItem(1032101) && cm.haveItem(4001198, 10)) { + if (cm.haveItem(1032061) && !cm.haveItem(1032072) && cm.haveItem(4001198, 10)) { cm.gainItem(1032061,-1); - cm.gainItem(1032101, 1); + cm.gainItem(1032072, 1); // thanks yuxaij for noticing unexpected itemid here cm.gainItem(4001198, -10); cm.dispose(); } else { diff --git a/scripts/npc/2133001.js b/scripts/npc/2133001.js index 74a85d579e..d288555334 100644 --- a/scripts/npc/2133001.js +++ b/scripts/npc/2133001.js @@ -92,10 +92,10 @@ function action(mode, type, selection) { cm.gainItem(4001169, -20); cm.getEventInstance().warpEventTeam(930000500); } else { - cm.warp(930000800); + cm.warp(930000800, 0); } } else { - cm.warp(930000800); + cm.warp(930000800, 0); } cm.dispose(); diff --git a/scripts/npc/2133002.js b/scripts/npc/2133002.js index 1804c49b10..59c3d759cc 100644 --- a/scripts/npc/2133002.js +++ b/scripts/npc/2133002.js @@ -19,7 +19,7 @@ function action(mode, type, selection) { cm.removeAll(4001163); cm.removeAll(4001169); cm.removeAll(2270004); - cm.warp(930000800); + cm.warp(930000800, 0); cm.dispose(); } } \ No newline at end of file diff --git a/scripts/npc/2133004.js b/scripts/npc/2133004.js index 4affde77cf..ef0d283fe3 100644 --- a/scripts/npc/2133004.js +++ b/scripts/npc/2133004.js @@ -25,7 +25,7 @@ function action(mode, type, selection) { } } else if(status == 1) { if (!cm.haveItem(4001163)) { - cm.warp(930000800); + cm.warp(930000800, 0); } else { cm.getEventInstance().warpEventTeam(930000600); } diff --git a/scripts/npc/2141001.js b/scripts/npc/2141001.js index 12bdba353a..1c41cf337b 100644 --- a/scripts/npc/2141001.js +++ b/scripts/npc/2141001.js @@ -67,8 +67,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -101,8 +106,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/9000001.js b/scripts/npc/9000001.js index 55eaf9a278..c0501bf004 100644 --- a/scripts/npc/9000001.js +++ b/scripts/npc/9000001.js @@ -59,7 +59,7 @@ function action(mode, type, selection) { cm.divideTeams(); cm.getEvent().minusLimit(); - cm.warp(cm.getEvent().getMapId()); + cm.warp(cm.getEvent().getMapId(), 0); cm.dispose(); } else { cm.sendNext("Either the event has not been started, you already have the #bScroll of Secrets#k, or you have already participated in this event within the last 24 hours. Please try again later!"); diff --git a/scripts/npc/9000011.js b/scripts/npc/9000011.js index fab1981248..67dbb825bd 100644 --- a/scripts/npc/9000011.js +++ b/scripts/npc/9000011.js @@ -127,7 +127,7 @@ function action(mode, type, selection) { cm.dispose(); return; } - var rand = java.lang.Math.floor(java.lang.Math.random() * pri.length); + var rand = Math.floor(Math.random() * pri.length); if (!cm.haveItem(ite, quan)) { cm.sendOk("You need #b" + quan + " #t" + ite + "##k to exchange it with item."); } else if (cm.getInventory(1).getNextFreeSlot() <= -1 || cm.getInventory(2).getNextFreeSlot() <= -1 || cm.getInventory(3).getNextFreeSlot() <= -1 || cm.getInventory(4).getNextFreeSlot() <= -1) { diff --git a/scripts/npc/9000017.js b/scripts/npc/9000017.js index 7f94db6a5d..5f37d9b5f9 100644 --- a/scripts/npc/9000017.js +++ b/scripts/npc/9000017.js @@ -51,7 +51,7 @@ function action(mode, type, selection) { } if (status == 0) { - if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k."); cm.dispose(); return; diff --git a/scripts/npc/9000019.js b/scripts/npc/9000019.js new file mode 100644 index 0000000000..c5c20ae161 --- /dev/null +++ b/scripts/npc/9000019.js @@ -0,0 +1,32 @@ +var status; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(cm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.ETC).getNumFreeSlot() < 1) { + cm.sendNext("Check for a available slot on your ETC inventory."); + cm.dispose(); + return; + } + + cm.getClient().announce(Packages.tools.MaplePacketCreator.openRPSNPC()); + cm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/npc/9000021.js b/scripts/npc/9000021.js index c6d7bbc9d3..b2dd15c369 100644 --- a/scripts/npc/9000021.js +++ b/scripts/npc/9000021.js @@ -55,7 +55,7 @@ function action(mode, type, selection) { cm.sendOk("Very well. Remember, there you can assemble a team or take on the fightings on your own, it's up to you. Good luck!"); } else if(status == 4) { cm.getPlayer().saveLocation("BOSSPQ"); - cm.warp(970030000); + cm.warp(970030000, "out00"); cm.dispose(); } } diff --git a/scripts/npc/9000036.js b/scripts/npc/9000036.js index 1bcc769423..247353a8a4 100644 --- a/scripts/npc/9000036.js +++ b/scripts/npc/9000036.js @@ -39,7 +39,7 @@ var equip; var maxEqp = 0; function start() { - if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k."); cm.dispose(); return; diff --git a/scripts/npc/9000040.js b/scripts/npc/9000040.js index c1ed3286b3..d5b7153aab 100644 --- a/scripts/npc/9000040.js +++ b/scripts/npc/9000040.js @@ -25,7 +25,7 @@ */ importPackage(Packages.client.processor); -importPackage(Packages.constants); +importPackage(Packages.config); var status; var mergeFee = 50000; @@ -50,7 +50,7 @@ function action(mode, type, selection) { status--; if(status == 0) { - if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.sendOk("The medal ranking system is currently unavailable..."); cm.dispose(); return; @@ -59,7 +59,7 @@ function action(mode, type, selection) { var levelLimit = !cm.getPlayer().isCygnus() ? 160 : 110; var selStr = "The medal ranking system is currently unavailable... Therefore, I am providing the #bEquipment Merge#k service! "; - if (!ServerConstants.USE_STARTER_MERGE && (cm.getPlayer().getLevel() < levelLimit || MakerProcessor.getMakerSkillLevel(cm.getPlayer()) < 3)) { + if (!YamlConfig.config.server.USE_STARTER_MERGE && (cm.getPlayer().getLevel() < levelLimit || MakerProcessor.getMakerSkillLevel(cm.getPlayer()) < 3)) { selStr += "However, you must have #rMaker level 3#k and at least #rlevel 110#k (Cygnus Knight), #rlevel 160#k (other classes) and a fund of #r" + cm.numberWithCommas(mergeFee) + " mesos#k to use the service."; cm.sendOk(selStr); cm.dispose(); diff --git a/scripts/npc/9000041.js b/scripts/npc/9000041.js index 93fd16a9e7..45d4b1aeac 100644 --- a/scripts/npc/9000041.js +++ b/scripts/npc/9000041.js @@ -42,7 +42,7 @@ function action(mode, type, selection) { } if (status == 0) { - if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (!Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.sendOk("The medal ranking system is currently unavailable..."); cm.dispose(); return; diff --git a/scripts/npc/9001102.js b/scripts/npc/9001102.js new file mode 100644 index 0000000000..101c772a71 --- /dev/null +++ b/scripts/npc/9001102.js @@ -0,0 +1,54 @@ +/* + 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 . +*/ +/** +* @Author : iAkira, Kevintjuh93 +**/ +var status = 0; +var selected = 0; + +function start() { + if (cm.getPlayer().getMapId() == 100000000) { + cm.sendNext("There! Did you see that? You didn't? A UFO just passed... there!! Look, someone is getting dragged into the UFO... arrrrrrgh, it's Gaga! #rGaga just got kidnapped by a UFO!#k"); + } +} + +function action(m,t,s) { + if (m > 0) { + status++; + if (cm.getPlayer().getMapId() == 100000000) { // warper completed + if (status == 1) { + if (cm.getPlayer().getLevel() >= 12) + cm.sendYesNo("What do we do now? It's just a rumor yet, but... I've heard that scary things happen to you if you get kidnapped by aliens... may be that's what happenning to Gaga right now! Please, please rescue Gaga! \r\n #bGaga may be a bit indetermined and clueless, but#k he has a really good heart. I can't let something terrible happen to him. Right! Grandpa from the moon might know how to rescue him! I will send you to the moon, so please go meet Grandpa and rescue Gaga!!!"); + else + cm.sendOk("Oh! It seems you don't reach the level requirements to save Gaga. Please come back when you are level 12 or higher."); + + } else if (status == 2) + cm.sendNext("Thank you so much. Please rescue Gaga! Grandpa from the moon will help you."); + else if (status == 3) { + cm.warp(922240200, 0); + cm.dispose(); + } + } + } else if (m < 1) { + cm.dispose(); + } +} \ No newline at end of file diff --git a/scripts/npc/9001105.js b/scripts/npc/9001105.js new file mode 100644 index 0000000000..d648f068ee --- /dev/null +++ b/scripts/npc/9001105.js @@ -0,0 +1,77 @@ +var status; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + if (cm.getPlayer().getMapId() == 922240200) { + cm.sendOk("That's a shame, come back when your ready."); + } + + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if (cm.getMapId() == 922240200) { + cm.sendSimple("Did you have something to say...? #b\b\r\n#L0#I want to rescue Gaga.#l\r\n"); //#L1#I want to go to the Space Mine.#l + } else if (cm.getMapId() >= 922240000 && cm.getMapId() <= 922240019) { + cm.sendYesNo("Don't worry if you fail. You'll have 3 chances. Do you still want to give up?"); + } else if (cm.getMapId() >= 922240100 && cm.getMapId() <= 922240119) { + var text = "You went through so much trouble to rescue Gaga, but it looks like we're back to square one. "; + var rgaga = cm.getPlayer().getEvents().get("rescueGaga"); + if (rgaga.getCompleted() > 10) { + text += "Please don't give up until Gaga is rescued. To show you my appreciation for what you've accomplished thus far, I've given you a Spaceship. It's rather worn out, but it should still be operational. Check your #bSkill Window#k."; + rgaga.giveSkill(cm.getPlayer()); + } else + text += "Let's go back now."; + + cm.sendNext(text); + } + } else { + if (cm.getPlayer().getMapId() == 922240200) { + if (status == 1) { + if(selection == 0) { + selected = 1; + cm.sendNext("Welcome! I heard what happened from Baby Moon Bunny I'm glad you came since I was Planning on requesting some help. Gaga is a friend of mine who has helped me before and often stops by to say hello. Unfortunately, he was kidnapped by aliens."); + } else { + selected = 2; + cm.sendYesNo("At the Space Mine, you can find special ores called #bKrypto Crystals#k that contains the mysterious power of space. #bKrypto Crystals#l are usually emerald in color, but will turn brown if hit with the Spaceship's #bSpace Beam#k. Remember, in order to thwart this alien conspracy, #b10 Brown Krypto Crystal's and 10 Emerald Krypto Crystal's are needed. But since even #b1 Krypto Crystal#k can be of help, brign me as many as possible. Oh, and one more thing! The Space Mines are protected by the Space Mateons. They are extemely strong due to the power of the #Krypto Crystals#k, so don't try to defeat them. Simply concentrate on quickly collecting the crystals."); + } + } else if (status == 2) { + if(selected == 1) { + cm.sendYesNo("If we just leave Gaga with the aliens, something terrible will happen to him! I'll let you borrow a spaceship that the Moon Bunnies use for traveling so that you can rescue Gaga.#b Although he might appear a bit indecisive, slow, and immature at times#k, he's really a nice young man. Do you want to go rescue him now?"); + } else if(selected == 2) { + cm.sendOk("Not coded yet, f4."); + cm.dispose(); + } + } else if (status == 3) { + var em = cm.getEventManager("RescueGaga"); + if (em == null) { + cm.sendOk("This event is currently unavailable."); + } else if (!em.startInstance(cm.getPlayer())) { + cm.sendOk("There is currently someone in this map, come back later."); + } + + cm.dispose(); + } + } else if (cm.getPlayer().getMapId() >= 922240000 && cm.getPlayer().getMapId() <= 922240019) { + cm.warp(922240200, 0); + cm.dispose(); + } else if (cm.getPlayer().getMapId() >= 922240100 && cm.getPlayer().getMapId() <= 922240119) { + cm.warp(922240200, 0); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9001108.js b/scripts/npc/9001108.js deleted file mode 100644 index cf56b21f87..0000000000 --- a/scripts/npc/9001108.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 . -*/ -/** -* @Author : iAkira, Kevintjuh93 -**/ -var status = 0; -var selected = 0; - -function start() { - if (cm.getPlayer().getMapId() == 100000000) { - cm.sendNext("There! Did you see that? You didn't? A UFO just passed... there!! Look, someone is getting dragged into the UFO... arrrrrrgh, it's Gaga! #rGaga just got kidnapped by a UFO!#k"); - } else if (cm.getPlayer().getMapId() == 922240200) { - cm.sendSimple("Did you have something to say...? #b\b\r\n#L0#I want to rescue Gaga.#l\r\n#L1#I want to go to the Space Mine.#l"); - } else if (cm.getPlayer().getMapId() >= 922240000 && cm.getPlayer().getMapId() <= 922240019) { - cm.sendYesNo("Don't worry if you fail. You'll have 3 chances. Do you still want to give up?"); - } else if (cm.getPlayer().getMapId() >= 922240100 && cm.getPlayer().getMapId() <= 922240119) { - var text = "You went through so much trouble to rescue Gaga, but it looks like we're back to square one. "; - var rgaga = cm.getPlayer().getEvents().get("rescueGaga"); - if (rgaga.getCompleted() == 10 || rgaga.getCompleted() == 20) { - text += "Please don't give up untill Gaga is rescued. To show you my appreciation for what you've accomplished thus far, I've given you a Spaceship. It's rather worn out, but it should still be operational. Check your #bSkill Window#k."; - rgaga.giveSkill(cm.getPlayer()); - } else - text += "Let's go back now."; - - cm.sendNext(text); - } -} - -function action(m,t,s) { - if (m > 0) { - status++; - if (cm.getPlayer().getMapId() == 100000000) { // warper completed - if (status == 1) { - if (cm.getPlayer().getLevel() >= 12) - cm.sendYesNo("What do we do now? It's just a rumor yet, but... I've heard that scary things happen to you if you get kidnapped by aliens... may be that's what happenning to Gaga right now! Please, please rescue Gaga! \r\n #bGaga may be a bit indetermined and clueless, but#k he has a really good heart. I can't let something terrible happen to him. Right! Grandpa from the moon might know how to rescue him! I will send you to the moon, so please go meet Grandpa and rescue Gaga"); - else - cm.sendOk("Oh! it seems you don't reach the level requirements to save Gaga. Please come back when you are level 12 or higher."); - - } else if (status == 2) - cm.sendNext("Thank you so much. Please rescue Gaga! Grandpa from the moon will help you."); - else if (status == 3) { - cm.warp(922240200, 0); - cm.dispose(); - } - } else if (cm.getPlayer().getMapId() == 922240200) { - if (status == 1) { - if(s == 0) { - selected = 1; - cm.sendNext("Welcome! I heard what happened from Baby Moon Bunny I'm glad you came since I was Planning on requesting some help. Gaga is a friend of mine who has helped me before and often stops by to say hello. Unfortunaley, he was kidnapped by aliens."); - } else { - selected = 2; - cm.sendYesNo("At the Space Mine, you can find special ores called #bKrypto Crystals#k that contains the mysterious power of space. #bKrypto Crystals#l are usually emerald in color, but will turn brown if hit with the Spaceship's #bSpace Beam#k. Remember, in order to thwart this alien conspracy, #b10 Brown Krypto Crystal's and 10 Emerald Krypto Crystal's are needed. But since even #b1 Krypto Crystal#k can be of help, brign me as many as possible. Oh, and one more thing! The Space Mines are protected by the Space Mateons. They are extemely strong due to the power of the #Krypto Crystals#k, so don't try to defeat them. Simply concentrate on quickly collecting the crystals."); - } - } else if (status == 2) { - if(selected == 1) { - cm.sendYesNo("If we just leave Gaga with the aliens, something terrible will happen to him! I'll let you borrow a spaceship that the Moon Bunnies use for traveling so that you can rescue Gaga.#b Although he might appear a bit indecieve, slow, and immature at times#k, he's really a nice young man. Do you want to go rescue him now?"); - } else if(selected == 2) { - cm.sendOk("Not coded yet, f4."); - cm.dispose(); - } - } else if (status == 3) { - var number = -1; - for (var i = 0; i < 20; i++) { - var mapFactory = cm.getClient().getChannelServer().getMapFactory(); - if (mapFactory.getMap(922240000 + i).getCharacters().isEmpty()) { - number = i; - break; - } - } - if (number > -1) - cm.warp(922240000 + number); - else - cm.sendOk("There are currently no empty maps, please try again later."); - - cm.dispose(); - } - } else if ((cm.getPlayer().getMapId() >= 922240000 && cm.getPlayer().getMapId() <= 922240019) || (cm.getPlayer().getMapId() >= 922240100 && cm.getPlayer().getMapId() <= 922240119)) { - cm.warp(922240200, 0); - cm.dispose(); - } - } else if (m < 1) { - if(m == 0) { - if (cm.getPlayer().getMapId() == 922240200) { - cm.sendOk("That's a shame, come back when your ready."); - } - } - cm.dispose(); - } -} \ No newline at end of file diff --git a/scripts/npc/9010021.js b/scripts/npc/9010021.js index 2d1177d94d..0abd074218 100644 --- a/scripts/npc/9010021.js +++ b/scripts/npc/9010021.js @@ -26,7 +26,7 @@ function start() { status = -1; - if (!Packages.constants.ServerConstants.USE_REBIRTH_SYSTEM) { + if (!Packages.config.YamlConfig.config.server.USE_REBIRTH_SYSTEM) { cm.sendOk("... I came from distant planes to assist the fight against the #rBlack Magician#k. Right now I search my master, have you seen him?"); cm.dispose(); return; diff --git a/scripts/npc/9010022.js b/scripts/npc/9010022.js index a7c160e6e4..f4718a16d4 100644 --- a/scripts/npc/9010022.js +++ b/scripts/npc/9010022.js @@ -71,10 +71,10 @@ function action(mode, type, selection) { cm.warp(980030000, 3); break; case 5: - cm.warp(926010000); + cm.warp(926010000, 4); break; case 6: - cm.warp(910320000); + cm.warp(910320000, 2); break; } cm.dispose(); diff --git a/scripts/npc/9010022_old.js b/scripts/npc/9010022_old.js index d62b2984b5..8a9e49bdbf 100644 --- a/scripts/npc/9010022_old.js +++ b/scripts/npc/9010022_old.js @@ -66,10 +66,10 @@ function action(mode, type, selection) { cm.warp(980030000, 3); break; case 5: - cm.warp(926010000); + cm.warp(926010000, 4); break; case 6: - cm.warp(910320000); + cm.warp(910320000, 2); break; } cm.dispose(); diff --git a/scripts/npc/9020001.js b/scripts/npc/9020001.js index 55a6826e46..f6e590d54a 100644 --- a/scripts/npc/9020001.js +++ b/scripts/npc/9020001.js @@ -170,7 +170,8 @@ function action(mode, type, selection) { eim.gridInsert(cm.getPlayer(), 0); } else { - cm.sendNext("I'm sorry, but that is not the right answer! Please have the correct number of coupons in your inventory."); + var question = stage1Questions[eim.gridCheck(cm.getPlayer()) - 1]; + cm.sendNext("I'm sorry, but that is not the right answer!\r\n" + question); } } } @@ -294,4 +295,4 @@ function action(mode, type, selection) { cm.dispose(); } } -} \ No newline at end of file +} diff --git a/scripts/npc/9060000.js b/scripts/npc/9060000.js index 6120d47d68..aeb86ff6f5 100644 --- a/scripts/npc/9060000.js +++ b/scripts/npc/9060000.js @@ -20,9 +20,12 @@ along with this program. If not, see . */ var status = -1; +var completed; function start() { - if (cm.haveItem(4031508, 5) && cm.haveItem(4031507,5)) { + completed = cm.haveItem(4031508, 5) && cm.haveItem(4031507,5); + + if (completed) { cm.sendNext("Wow~ You have succeeded in collecting 5 of each #b#t4031508##k and #b#t4031507##k. Okay then, I will send you to Zoo. Please talk to me again when you get there."); } else { cm.sendYesNo("You haven't completed the requirements. Are you sure you want to leave?"); @@ -36,9 +39,15 @@ function action(mode, type, selection){ return; } - if(status == 0) cm.sendOk("Well okay, I will send you back."); - else { - cm.getEventInstance().clearPQ(); + if(status == 0) { + cm.sendOk("Well okay, I will send you back."); + } else { + if (completed) { + cm.getEventInstance().clearPQ(); + } else { + cm.warp(923010100, 0); + } + cm.dispose(); } } \ No newline at end of file diff --git a/scripts/npc/9120003.js b/scripts/npc/9120003.js index fb3e89c5e8..fb08eed273 100644 --- a/scripts/npc/9120003.js +++ b/scripts/npc/9120003.js @@ -49,7 +49,7 @@ function action(mode, type, selection) { cm.sendOk("Please check and see if you have " + price + " mesos to enter this place."); else { cm.gainMeso(-price); - cm.warp(801000100 + 100 * cm.getPlayer().getGender()); + cm.warp(801000100 + 100 * cm.getPlayer().getGender(), "out00"); } cm.dispose(); } diff --git a/scripts/npc/9120015.js b/scripts/npc/9120015.js index bb0a9642d0..f5846c1ac1 100644 --- a/scripts/npc/9120015.js +++ b/scripts/npc/9120015.js @@ -50,7 +50,7 @@ function action(mode, type, selection) { cm.dispose(); } } else { - cm.warp(801040000); + cm.warp(801040000, "in00"); cm.dispose(); } } diff --git a/scripts/npc/9120200.js b/scripts/npc/9120200.js index 4d9bb231ad..e517b07837 100644 --- a/scripts/npc/9120200.js +++ b/scripts/npc/9120200.js @@ -30,9 +30,9 @@ function action(mode, type, selection) { cm.dispose(); } else { if (mode == 0) { - cm.sendOk("If you want to return to #m801000000#, then talk to me"); + cm.sendOk("If you want to return to #m801000000#, then talk to me."); cm.dispose(); - } if (mode == 1) { + } else if (mode == 1) { status++; } if (status == 1) { diff --git a/scripts/npc/9120201.js b/scripts/npc/9120201.js index fd201e1de3..13be378b5a 100644 --- a/scripts/npc/9120201.js +++ b/scripts/npc/9120201.js @@ -65,8 +65,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -105,8 +110,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/9201002.js b/scripts/npc/9201002.js index 8a9341aa87..40faa47cff 100644 --- a/scripts/npc/9201002.js +++ b/scripts/npc/9201002.js @@ -21,7 +21,7 @@ Marriage NPC */ -importPackage(Packages.constants); +importPackage(Packages.config); importPackage(Packages.net.server.channel.handlers); importPackage(Packages.tools); importPackage(Packages.tools.packets); @@ -32,7 +32,7 @@ var eim; var weddingEventName = "WeddingCathedral"; var cathedralWedding = true; var weddingIndoors; -var weddingBlessingExp = ServerConstants.WEDDING_BLESS_EXP; +var weddingBlessingExp = YamlConfig.config.server.WEDDING_BLESS_EXP; function isWeddingIndoors(mapid) { return mapid >= 680000100 && mapid <= 680000500; @@ -304,7 +304,7 @@ function action(mode, type, selection) { if (state == 0) { // give player blessings eim.gridInsert(cm.getPlayer(), 1); - if (ServerConstants.WEDDING_BLESSER_SHOWFX) { + if (YamlConfig.config.server.WEDDING_BLESSER_SHOWFX) { var target = cm.getPlayer(); target.announce(MaplePacketCreator.showSpecialEffect(9)); target.getMap().broadcastMessage(target, MaplePacketCreator.showForeignEffect(target.getId(), 9), false); diff --git a/scripts/npc/9201003.js b/scripts/npc/9201003.js index af2a3c6ecb..2f30233c03 100644 --- a/scripts/npc/9201003.js +++ b/scripts/npc/9201003.js @@ -63,7 +63,7 @@ function action(mode, type, selection) { cm.sendOk("Hello we're Mom and Dad..."); cm.dispose(); } else { - if (cm.getQuestProgress(100400, 1) == 0) { + if (cm.getQuestProgressInt(100400, 1) == 0) { cm.sendNext("Mom, dad, I have a request to do to both of you... I wanna know more about the path you've already been walking since always, the path of loving and caring for someone dear to me.", 2); } else { if(!hasProofOfLoves(cm.getPlayer())) { diff --git a/scripts/npc/9201005.js b/scripts/npc/9201005.js index 354076b112..7e4006775b 100644 --- a/scripts/npc/9201005.js +++ b/scripts/npc/9201005.js @@ -21,6 +21,8 @@ Marriage NPC */ +importPackage(Packages.net.server.channel); + var status; var wid; var isMarrying; @@ -176,7 +178,7 @@ function action(mode, type, selection) { if(resStatus > 0) { cm.gainItem((weddingType) ? weddingEntryTicketPremium : weddingEntryTicketCommon, -1); - var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + var expirationTime = Channel.getRelativeWeddingTicketExpireTime(resStatus); cm.gainItem(weddingSendTicket,15,false,true,expirationTime); partner.getAbstractPlayerInteraction().gainItem(weddingSendTicket,15,false,true,expirationTime); @@ -251,7 +253,7 @@ function action(mode, type, selection) { if(cm.canHold(weddingSendTicket, 3)) { cm.gainItem(5251100, -1); - var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + var expirationTime = Channel.getRelativeWeddingTicketExpireTime(resStatus); cm.gainItem(weddingSendTicket,3,false,true,expirationTime); } else { cm.sendOk("Please have a free ETC slot available to get more invitations."); diff --git a/scripts/npc/9201008.js b/scripts/npc/9201008.js index b13337fc78..8d1813e23b 100644 --- a/scripts/npc/9201008.js +++ b/scripts/npc/9201008.js @@ -21,6 +21,8 @@ Marriage NPC */ +importPackage(Packages.net.server.channel); + var status; var wid; var isMarrying; @@ -176,7 +178,7 @@ function action(mode, type, selection) { if(resStatus > 0) { cm.gainItem((weddingType) ? weddingEntryTicketPremium : weddingEntryTicketCommon, -1); - var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + var expirationTime = Channel.getRelativeWeddingTicketExpireTime(resStatus); cm.gainItem(weddingSendTicket,15,false,true,expirationTime); partner.getAbstractPlayerInteraction().gainItem(weddingSendTicket,15,false,true,expirationTime); @@ -251,7 +253,7 @@ function action(mode, type, selection) { if(cm.canHold(weddingSendTicket, 3)) { cm.gainItem(5251100, -1); - var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + var expirationTime = Channel.getRelativeWeddingTicketExpireTime(resStatus); cm.gainItem(weddingSendTicket,3,false,true,expirationTime); } else { cm.sendOk("Please have a free ETC slot available to get more invitations."); diff --git a/scripts/npc/9201011.js b/scripts/npc/9201011.js index b81902eab3..0cb2c189b3 100644 --- a/scripts/npc/9201011.js +++ b/scripts/npc/9201011.js @@ -21,7 +21,7 @@ Marriage NPC */ -importPackage(Packages.constants); +importPackage(Packages.config); importPackage(Packages.net.server.channel.handlers); importPackage(Packages.tools); importPackage(Packages.tools.packets); @@ -32,7 +32,7 @@ var eim; var weddingEventName = "WeddingChapel"; var cathedralWedding = false; var weddingIndoors; -var weddingBlessingExp = ServerConstants.WEDDING_BLESS_EXP; +var weddingBlessingExp = YamlConfig.config.server.WEDDING_BLESS_EXP; function detectPlayerItemid(player) { for (var x = 4031357; x <= 4031364; x++) { @@ -174,7 +174,7 @@ function action(mode, type, selection) { if(state == 0) { // give player blessings eim.gridInsert(cm.getPlayer(), 1); - if(ServerConstants.WEDDING_BLESSER_SHOWFX) { + if(YamlConfig.config.server.WEDDING_BLESSER_SHOWFX) { var target = cm.getPlayer(); target.announce(MaplePacketCreator.showSpecialEffect(9)); target.getMap().broadcastMessage(target, MaplePacketCreator.showForeignEffect(target.getId(), 9), false); diff --git a/scripts/npc/9201015.js b/scripts/npc/9201015.js index ea75f85e19..4786e909e3 100644 --- a/scripts/npc/9201015.js +++ b/scripts/npc/9201015.js @@ -84,7 +84,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150020) == true){ + if (cm.haveItem(5420000)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150020) == true){ cm.gainItem(5150020, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/9201023.js b/scripts/npc/9201023.js index d654fed771..8e6326a9a2 100644 --- a/scripts/npc/9201023.js +++ b/scripts/npc/9201023.js @@ -99,14 +99,14 @@ function action(mode, type, selection) { if(status == 0) { if(!cm.isQuestStarted(100400)) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201023# the fairy of Love."); // thanks Periwinks (LuckyStory) for noticing Nana's introducing themselves as Nana (H) cm.dispose(); return; } nanaLoc = getNanaLocation(cm.getPlayer()); if(nanaLoc == -1) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201023# the fairy of Love."); cm.dispose(); return; } diff --git a/scripts/npc/9201024.js b/scripts/npc/9201024.js index d654fed771..271cf7a75b 100644 --- a/scripts/npc/9201024.js +++ b/scripts/npc/9201024.js @@ -99,14 +99,14 @@ function action(mode, type, selection) { if(status == 0) { if(!cm.isQuestStarted(100400)) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201024# the fairy of Love."); cm.dispose(); return; } nanaLoc = getNanaLocation(cm.getPlayer()); if(nanaLoc == -1) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201024# the fairy of Love."); cm.dispose(); return; } diff --git a/scripts/npc/9201025.js b/scripts/npc/9201025.js index d654fed771..d284706f18 100644 --- a/scripts/npc/9201025.js +++ b/scripts/npc/9201025.js @@ -99,14 +99,14 @@ function action(mode, type, selection) { if(status == 0) { if(!cm.isQuestStarted(100400)) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201025# the fairy of Love."); cm.dispose(); return; } nanaLoc = getNanaLocation(cm.getPlayer()); if(nanaLoc == -1) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201025# the fairy of Love."); cm.dispose(); return; } diff --git a/scripts/npc/9201026.js b/scripts/npc/9201026.js index d654fed771..48d97f52a3 100644 --- a/scripts/npc/9201026.js +++ b/scripts/npc/9201026.js @@ -99,14 +99,14 @@ function action(mode, type, selection) { if(status == 0) { if(!cm.isQuestStarted(100400)) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201026# the fairy of Love."); cm.dispose(); return; } nanaLoc = getNanaLocation(cm.getPlayer()); if(nanaLoc == -1) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201026# the fairy of Love."); cm.dispose(); return; } diff --git a/scripts/npc/9201027.js b/scripts/npc/9201027.js index d654fed771..e02f52039e 100644 --- a/scripts/npc/9201027.js +++ b/scripts/npc/9201027.js @@ -99,14 +99,14 @@ function action(mode, type, selection) { if(status == 0) { if(!cm.isQuestStarted(100400)) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201027# the fairy of Love."); cm.dispose(); return; } nanaLoc = getNanaLocation(cm.getPlayer()); if(nanaLoc == -1) { - cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.sendOk("Hello #b#h0##k, I'm #p9201027# the fairy of Love."); cm.dispose(); return; } diff --git a/scripts/npc/9201064.js b/scripts/npc/9201064.js index 0304261797..540e599fac 100644 --- a/scripts/npc/9201064.js +++ b/scripts/npc/9201064.js @@ -73,7 +73,10 @@ function action(mode, type, selection) { else if (status == 2){ cm.dispose(); if (beauty == 1){ - if (cm.haveItem(5150031)){ + if (cm.haveItem(5420001)){ + cm.setHair(hairnew[selection]); + cm.sendOk("Enjoy your new and improved hairstyle!"); + } else if (cm.haveItem(5150031)){ cm.gainItem(5150031, -1); cm.setHair(hairnew[selection]); cm.sendOk("Enjoy your new and improved hairstyle!"); diff --git a/scripts/npc/9201101.js b/scripts/npc/9201101.js index c2fecb4580..c424e7dcd9 100644 --- a/scripts/npc/9201101.js +++ b/scripts/npc/9201101.js @@ -4,7 +4,7 @@ */ function start() { - if (Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { cm.openShopNPC(9201101); } else { //cm.sendOk("The patrol in New Leaf City is always ready. No creatures are able to break through to the city."); diff --git a/scripts/npc/9201113.js b/scripts/npc/9201113.js index fa7b31b743..766cae86de 100644 --- a/scripts/npc/9201113.js +++ b/scripts/npc/9201113.js @@ -61,8 +61,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to attempt the #rCrimsonwood Keep Party Quest#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -89,8 +94,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(cwkpq)) { + var res = cm.createExpedition(cwkpq); + if (res == 0) { cm.sendOk("The #rCrimsonwood Keep Party Quest Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/9201115.js b/scripts/npc/9201115.js index 05405fd3fb..a8c036ad81 100644 --- a/scripts/npc/9201115.js +++ b/scripts/npc/9201115.js @@ -35,11 +35,11 @@ function action(mode, type, selection) { cm.mapMessage(6, "Engarde! Master Guardians approach!"); for (var i = 0; i < 10; i++) { var mob = eim.getMonster(9400594); - cm.getMap().spawnMonsterOnGroundBelow(mob, new java.awt.Point(-1337 + (java.lang.Math.random() * 1337), 276)); + cm.getMap().spawnMonsterOnGroundBelow(mob, new java.awt.Point(-1337 + (Math.random() * 1337), 276)); } for (var i = 0; i < 20; i++) { var mob = eim.getMonster(9400582); - cm.getMap().spawnMonsterOnGroundBelow(mob, new java.awt.Point(-1337 + (java.lang.Math.random() * 1337), 276)); + cm.getMap().spawnMonsterOnGroundBelow(mob, new java.awt.Point(-1337 + (Math.random() * 1337), 276)); } eim.setIntProperty("glpq6", 1); cm.dispose(); diff --git a/scripts/npc/9270033.js b/scripts/npc/9270033.js index b12b211261..186a9d1c41 100644 --- a/scripts/npc/9270033.js +++ b/scripts/npc/9270033.js @@ -60,7 +60,7 @@ function action(mode, type, selection) { } } - cm.warp(541010110); + cm.warp(541010110, 0); cm.dispose(); } } diff --git a/scripts/npc/9270047.js b/scripts/npc/9270047.js index a660524100..b77399be54 100644 --- a/scripts/npc/9270047.js +++ b/scripts/npc/9270047.js @@ -66,8 +66,13 @@ function action(mode, type, selection) { cm.sendSimple("#e#b\r\n#k#n" + em.getProperty("party") + "\r\n\r\nWould you like to assemble a team to take on #r" + expedBoss + "#k?\r\n#b#L1#Lets get this going!#l\r\n\#L2#No, I think I'll wait a bit...#l"); status = 1; } else if (expedition.isLeader(player)) { //If you're the leader, manage the exped - cm.sendSimple(list); - status = 2; + if (expedition.isInProgress()) { + cm.sendOk("Your expedition is already in progress, for those who remain battling lets pray for those brave souls."); + cm.dispose(); + } else { + cm.sendSimple(list); + status = 2; + } } else if (expedition.isRegistering()) { //If the expedition is registering if (expedition.contains(player)) { //If you're in it but it hasn't started, be patient cm.sendOk("You have already registered for the expedition. Please wait for #r" + expedition.getLeader().getName() + "#k to begin it."); @@ -106,8 +111,11 @@ function action(mode, type, selection) { return; } - if (cm.createExpedition(exped)) { + var res = cm.createExpedition(exped); + if (res == 0) { cm.sendOk("The #r" + expedBoss + " Expedition#k has been created.\r\n\r\nTalk to me again to view the current team, or start the fight!"); + } else if (res > 0) { + cm.sendOk("Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."); } else { cm.sendOk("An unexpected error has occurred when starting the expedition, please try again later."); } diff --git a/scripts/npc/9900000.js b/scripts/npc/9900000.js index cf5dffdb19..6407ca43f5 100644 --- a/scripts/npc/9900000.js +++ b/scripts/npc/9900000.js @@ -40,6 +40,12 @@ var facenew = Array(); var colors = Array(); var price = 100000; +function pushIfItemExists(array, itemid) { + if ((itemid = cm.getCosmeticItem(itemid)) != -1 && !cm.isCosmeticEquipped(itemid)) { // thanks Conrad for noticing NPC crashing the player when trying to display inexistent cosmetics + array.push(itemid); + } +} + function start() { if(cm.getPlayer().gmLevel() < 1) { cm.sendOk("Hey wassup?"); @@ -67,21 +73,21 @@ function action(mode, type, selection) { cm.sendStyle("Pick one?", skin); else if (selection == 1 || selection == 5) { for each(var i in selection == 1 ? hair : fhair) - hairnew.push(i); + pushIfItemExists(hairnew, i); cm.sendStyle("Pick one?", hairnew); } else if (selection == 2) { var baseHair = parseInt(cm.getPlayer().getHair() / 10) * 10; for(var k = 0; k < 8; k++) - haircolor.push(baseHair + k); + pushIfItemExists(haircolor, baseHair + k); cm.sendStyle("Pick one?", haircolor); } else if (selection == 3 || selection == 6) { for each(var j in selection == 3 ? face : fface) - facenew.push(j); + pushIfItemExists(facenew, j); cm.sendStyle("Pick one?", facenew); } else if (selection == 4) { var baseFace = parseInt(cm.getPlayer().getFace() / 1000) * 1000 + parseInt(cm.getPlayer().getFace() % 100); for(var i = 0; i < 9; i++) - colors.push(baseFace + (i*100)); + pushIfItemExists(colors, baseFace + (i*100)); cm.sendStyle("Pick one?", colors); } } else { diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index e07fedd126..fd0d44d76c 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -43,6 +43,7 @@ function writeFeatureTab_PQs() { addFeature("RnJPQ/HorntailPQ/TreasurePQ/ElnathPQ/HolidayPQ."); addFeature("CWKPQ as Expedition-based instance."); addFeature("Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean."); + addFeature("Expeditions with entry-limit system available."); addFeature("GuildPQ & queue with multi-lobby system available."); addFeature("Brand-new PQs: BossRushPQ, CafePQ."); addFeature("Mu Lung Dojo."); @@ -57,10 +58,11 @@ function writeFeatureTab_Skills() { addFeature("Reviewed many skills, such as Steal and M. Door."); addFeature("Heal GMS-like: fixed HP gain & Heal skill packet."); addFeature("Improved battleship: HP visible and map-persistent."); - addFeature("Maker skill features properly developed."); + addFeature("Maker skill features developed - pckts thanks Arnah."); addFeature("Chair Mastery - map chair boosts HP/MP rec."); addFeature("Mu Lung Dojo skills functional."); addFeature("Monster Magnet skill no longer crashes players."); + addFeature("HP/MP consumption from skills triggers pet autopot."); } function writeFeatureTab_Quests() { @@ -74,6 +76,8 @@ function writeFeatureTab_Quests() { addFeature("Rewarding system now looks up for item stacking."); addFeature("3rd job quiz with all 40-question pool available."); addFeature("Item raising functional."); + addFeature("Cleared issue with player movement during NPC talk."); + addFeature("Reviewed usage of quest progress data as requisite."); } function writeFeatureTab_PlayerSocialNetwork() { @@ -185,6 +189,7 @@ function writeFeatureTab_Playerpotentials() { addFeature("Optional cash shop inventory separated by classes."); addFeature("Players manage 'same-typed' invites exclusively."); addFeature("Player buffs with conditional active effects."); + addFeature("Rock-paper-scissors minigame - thanks Arnah!"); } function writeFeatureTab_Serverpotentials() { @@ -248,6 +253,7 @@ function writeFeatureTab_CustomNPCs() { addFeature("Dalair: automatized equipment-merger."); addFeature("Donation Box: automatized item-buyer."); addFeature("Coco & Ace of Hearts: C. scroll crafters."); + addFeature("Barry (MapleTV): fill book & exchange items for scroll."); } function writeFeatureTab_Localhostedits() { @@ -282,6 +288,7 @@ function writeFeatureTab_Project() { addFeature("Remodeled item scripts, properly using NPC dialogs."); addFeature("ThreadTracker: runtime tool for deadlock detection."); addFeature("Channel, World and Server-wide timer management."); + addFeature("Developed services as preemptive task scheduler."); addFeature("Thoroughly reviewed encapsulation for player stats."); addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager."); } @@ -293,7 +300,7 @@ function writeAllFeatures() { feature_cursor = []; var tabName = (tabs[i]).replace(re, ""); - eval("writeFeatureTab_" + tabName)(); + this["writeFeatureTab_" + tabName](); feature_tree.push(feature_cursor); } @@ -322,7 +329,7 @@ function action(mode, type, selection) { status--; if (status == 0) { - var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. I'm glad to say the development itself had continuously been agraciated by dozens of contributors and cheerers (truly thanks for the trusting vow, guys & gals!).\r\n\r\nTalking about results: many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n"; + var sendStr = "HeavenMS was developed on the timespan of 4 years, based on where Solaxia left. I'm glad to say the development itself had continuously been agraciated by dozens of contributors and cheerers (truly thanks for the trusting vow, guys & gals!).\r\n\r\nTalking about results: many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n"; for(var i = 0; i < tabs.length; i++) { sendStr += "#L" + i + "##b" + tabs[i] + "#k#l\r\n"; } diff --git a/scripts/npc/MagatiaPassword.js b/scripts/npc/MagatiaPassword.js index a6bd7bf9d4..eaab5b7a19 100644 --- a/scripts/npc/MagatiaPassword.js +++ b/scripts/npc/MagatiaPassword.js @@ -19,9 +19,10 @@ function action(mode, type, selection){ cm.sendGetText("The door reacts to the entry pass inserted. #bPassword#k!"); } else if(status == 1){ - if(cm.getText() == cm.getStringQuestProgress(3360, 0)){ - cm.setQuestProgress(3360, 1, 1); - cm.warp((cm.getMapId() == 261010000) ? 261020200 : 261010000, "secret00"); + if(cm.getText() == cm.getQuestProgress(3360)){ + cm.setQuestProgress(3360, 1); + cm.getPlayer().announce(Packages.tools.MaplePacketCreator.playPortalSound()); + cm.warp(261030000, "sp_" + ((cm.getMapId() == 261010000) ? "jenu" : "alca")); } else { cm.sendOk("#rWrong!"); diff --git a/scripts/npc/PupeteerPassword.js b/scripts/npc/PupeteerPassword.js index 462f8157fb..8d3ca92136 100644 --- a/scripts/npc/PupeteerPassword.js +++ b/scripts/npc/PupeteerPassword.js @@ -20,7 +20,7 @@ function action(mode, type, selection){ if(status == 0){ if(cm.isQuestStarted(21728)) { cm.sendOk("You search for any hints of the Puppeteer, but it seems a powerful force blocks the path... Better return to #b#p1061019##k."); - cm.setQuestProgress(21728, 0, 1); + cm.setQuestProgress(21728, 21761, 0); cm.dispose(); return; } @@ -29,12 +29,12 @@ function action(mode, type, selection){ } else if(status == 1){ if(cm.getText() == "Francis is a genius Puppeteer!"){ - if(cm.isQuestStarted(20730) && cm.getQuestProgress(20730, 9300285) == 0) + if(cm.isQuestStarted(20730) && cm.getQuestProgressInt(20730, 9300285) == 0) cm.warp(910510001, 1); - else if(cm.isQuestStarted(21731) && cm.getQuestProgress(21731, 9300346) == 0) + else if(cm.isQuestStarted(21731) && cm.getQuestProgressInt(21731, 9300346) == 0) cm.warp(910510001, 1); else - cm.playerMessage(5, "Although you said the right answer, some mysterious forces is blocking the way in."); + cm.playerMessage(5, "Although you said the right answer, some mysterious forces are blocking the way in."); cm.dispose(); } diff --git a/scripts/npc/ThiefPassword.js b/scripts/npc/ThiefPassword.js index ca6df9a429..efc0ff1db8 100644 --- a/scripts/npc/ThiefPassword.js +++ b/scripts/npc/ThiefPassword.js @@ -23,7 +23,7 @@ function action(mode, type, selection){ else if(status == 1){ if(cm.getText() == "Open Sesame"){ if(cm.isQuestCompleted(3925)) - cm.warp(260010402); + cm.warp(260010402, 1); else cm.playerMessage(5, "Although you said the right answer, the door will not budge."); diff --git a/scripts/npc/cpqchallenge.js b/scripts/npc/cpqchallenge.js index 1be873d469..c55c990717 100644 --- a/scripts/npc/cpqchallenge.js +++ b/scripts/npc/cpqchallenge.js @@ -5,7 +5,7 @@ --------------------------------------------------------------------------------------------------- **/ -importPackage(Packages.constants); +importPackage(Packages.constants.game); var status = 0; var party; diff --git a/scripts/npc/cpqchallenge2.js b/scripts/npc/cpqchallenge2.js index 1be873d469..c55c990717 100644 --- a/scripts/npc/cpqchallenge2.js +++ b/scripts/npc/cpqchallenge2.js @@ -5,7 +5,7 @@ --------------------------------------------------------------------------------------------------- **/ -importPackage(Packages.constants); +importPackage(Packages.constants.game); var status = 0; var party; diff --git a/scripts/npc/credits.js b/scripts/npc/credits.js index b683623e81..29db73a8fc 100644 --- a/scripts/npc/credits.js +++ b/scripts/npc/credits.js @@ -13,7 +13,7 @@ var name_cursor, role_cursor; // new server names are to be appended at the start of the name stack, building up the chronology. // make sure the server names are lexicograffically equivalent to their correspondent function. -var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "MetroMS", "BubblesDEV", "ThePackII", "OdinMS", "Contributors"]; +var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "MetroMS", "BubblesDEV", "OdinMS", "Contributors"]; var servers_history = []; function addPerson(name, role) { @@ -41,7 +41,7 @@ function writeServerStaff_HeavenMS() { addPerson("Masterrulax", "Contributor"); addPerson("MedicOP", "Adjunct Developer"); - setHistory(2015, 2018); + setHistory(2015, 2019); } function writeServerStaff_MapleSolaxia() { @@ -58,26 +58,28 @@ function writeServerStaff_MapleSolaxia() { } function writeServerStaff_MoopleDEV() { - addPerson("conan513", "Administrator"); addPerson("kevintjuh93", "Developer"); + addPerson("hindie93", "Contributor"); + addPerson("JuniarZ-", "Contributor"); + setHistory(2010, 2012); } function writeServerStaff_MetroMS() { - addPerson("Moogra", "Developer"); + addPerson("David!", "Developer"); + addPerson("XxOsirisxX", "Contributor"); + addPerson("Generic", "Contributor"); + setHistory(2009, 2010); } function writeServerStaff_BubblesDEV() { - addPerson("Deagan", "Administrator"); - addPerson("XxOsirisxX", "Developer"); - setHistory(2009, 2009); -} - -function writeServerStaff_ThePackII() { - addPerson("Hofer", "Developer"); + addPerson("David!", "Developer"); addPerson("Moogra", "Developer"); - setHistory(2008, 2009); + addPerson("XxOsirisxX", "Contributor"); + addPerson("MrMysterious", "Contributor"); + + setHistory(2009, 2009); } function writeServerStaff_OdinMS() { @@ -86,10 +88,14 @@ function writeServerStaff_OdinMS() { addPerson("Patrick", "Developer"); addPerson("Matze", "Developer"); addPerson("Vimes", "Developer"); + setHistory(2007, 2008); } function writeServerStaff_Contributors() { + addPerson("IxianMace", "Contributor"); + addPerson("Conrad", "Contributor"); + addPerson("inhyuk", "Contributor"); addPerson("Jayd", "Contributor"); addPerson("Dragohe4rt", "Contributor"); addPerson("Jvlaple", "Contributor"); @@ -112,7 +118,7 @@ function writeAllServerStaffs() { role_cursor = []; var srvName = servers[i]; - eval("writeServerStaff_" + srvName)(); + this["writeServerStaff_" + srvName](); name_tree.push(name_cursor); role_tree.push(role_cursor); diff --git a/scripts/npc/mapleTV.js b/scripts/npc/mapleTV.js new file mode 100644 index 0000000000..93c58b5d66 --- /dev/null +++ b/scripts/npc/mapleTV.js @@ -0,0 +1,55 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/* + Default Maple TV + */ + +var status; + +function start() { + if (Packages.config.YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.dispose(); + cm.openNpc(9201088, "scroll_generator"); + return; + } + + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + // do nothing + cm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/npc/rank_user.js b/scripts/npc/rank_user.js index 61e8c1b70c..bbe07c49da 100644 --- a/scripts/npc/rank_user.js +++ b/scripts/npc/rank_user.js @@ -21,7 +21,7 @@ * @Author Ronan * Player NPC Ranking System */ -importPackage(Packages.constants); +importPackage(Packages.constants.game); var status; diff --git a/scripts/npc/scroll_generator.js b/scripts/npc/scroll_generator.js new file mode 100644 index 0000000000..b3b3f038c7 --- /dev/null +++ b/scripts/npc/scroll_generator.js @@ -0,0 +1,408 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/* NPC: MapleTV / Larry + + Exchanger NPC: + * Scroll generator + * + * @author Ronan Lana +*/ + +importPackage(Packages.client); +importPackage(Packages.config); +importPackage(Packages.constants.game); +importPackage(Packages.server); +importPackage(Packages.server.life); + +var status; + +var jobWeaponRestricted = [[[2043000, 2043100, 2044000, 2044100, 2043200, 2044200]], [[2043000, 2043100, 2044000, 2044100], [2043000, 2043200, 2044000, 2044200], [2044300, 2044400]], [[2043700, 2043800], [2043700, 2043800], [2043700, 2043800]], [[2044500], [2044600]], [[2044700], [2043300]], [[2044800], [2044900]]]; +var aranWeaponRestricted = [jobWeaponRestricted[1][2][1]]; + +var tier1Scrolls = []; +var tier2Scrolls = [2040000, 2040400, 2040500, 2040600, 2040700, 2040800, 2040900]; +var tier3Scrolls = [2048000, 2049200, 2041000, 2041100, 2041300, 2040100, 2040200, 2040300]; + +var typeTierScrolls = [["PAD", "MAD"], ["STR", "DEX", "INT", "LUK", "ACC", "EVA", "Speed", "Jump"], ["PDD", "MDD", "MHP", "MMP"]]; + +var sgItems = [4003004, 4003005, 4001006, 4006000, 4006001, 4030012]; +var sgToBucket = [100, 50, 37.5, 37.5, 37.5, 200]; +var mesoToBucket = 2800000; + +var sgAppliedItems = [0, 0, 0, 0, 0, 0]; +var sgAppliedMeso = 0; + +var sgBuckets = 0.0; +var sgBookBuckets = 0.0; +var sgItemBuckets = 0.0; + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + cm.sendNext("This is the MapleTV Scroll Generator broadcast. Place your supplies or mesos earned throughout your adventure to redeem a prize! You can place #bany amount of supplies#k, however take note that placing #rdifferent supplies#k with #rbigger shots of any of them#k will improve the reward possibilities!"); + } else if(status == 1) { + var sendStr; + + //print("Book: " + sgBookBuckets + " Item: " + sgItemBuckets); + + if(sgItemBuckets > 0.0) sendStr = "With the items you have currently placed, you have #r" + sgBuckets + "#k buckets (#r" + (sgItemBuckets < 1.0 ? sgItemBuckets.toFixed(2) : Math.floor(sgItemBuckets)) + "#k supply buckets) for claiming a prize. Place supplies:"; + else sendStr = "You have placed no supplies yet. Place supplies:"; + + var listStr = ""; + var i; + for(i = 0; i < sgItems.length; i++) { + listStr += "#b#L" + i + "##t" + sgItems[i] + "##k"; + if(sgAppliedItems[i] > 0) listStr += " - " + sgAppliedItems[i]; + listStr += "#l\r\n"; + } + + listStr += "#b#L" + i + "#Mesos#k"; + if(sgAppliedMeso > 0) listStr += " - " + sgAppliedMeso; + listStr += "#l\r\n"; + + cm.sendSimple(sendStr + "\r\n\r\n" + listStr + "#r#L" + (sgItems.length + 2) + "#Retrieve a prize!#l#k\r\n"); + } else if(status == 2) { + if(selection == (sgItems.length + 2)) { + if(sgItemBuckets < 1.0) { + cm.sendPrev("You have set not enough supplies. Insert at least one bucket of #bsupplies#k to claim a prize."); + } else { + generateRandomScroll(); + cm.dispose(); + } + } else { + var tickSel; + if(selection < sgItems.length) { + tickSel = "of #b#t" + sgItems[selection] + "##k"; + curItemQty = cm.getItemQuantity(sgItems[selection]); + } else { + tickSel = "#bmesos#k"; + curItemQty = cm.getMeso(); + } + + curItemSel = selection; + if(curItemQty > 0) { + cm.sendGetText("How many " + tickSel + " do you want to provide? (#r" + curItemQty + "#k available)#k"); + } else { + cm.sendPrev("You have got #rnone#k " + tickSel + " to provide for Scroll Generation. Click '#rBack#k' to return to the main interface."); + } + } + } else if(status == 3) { + var text = cm.getText(); + + try { + var placedQty = parseInt(text); + if(isNaN(placedQty) || placedQty < 0) throw true; + + if(placedQty > curItemQty) { + cm.sendPrev("You cannot insert the given amount of #r" + (curItemSel < sgItems.length ? "#t" + sgItems[curItemSel] + "#" : "mesos") + "#k (#r" + curItemQty + "#k available). Click '#rBack#k' to return to the main interface."); + } else { + if(curItemSel < sgItems.length) sgApplyItem(curItemSel, placedQty); + else sgApplyMeso(placedQty); + + cm.sendPrev("Operation succeeded. Click '#rBack#k' to return to the main interface."); + } + } catch(err) { + cm.sendPrev("You must enter a positive number of supplies to insert. Click '#rBack#k' to return to the main interface."); + } + + status = 2; + } else { + cm.dispose(); + } + } +} + +function getJobTierScrolls() { + var scrolls = []; + + var job = cm.getPlayer().getJob(); + var jobScrolls = jobWeaponRestricted[Math.floor(cm.getPlayer().getJobStyle().getId() / 100)]; + + var jobBranch = GameConstants.getJobBranch(job); + if (jobBranch >= 2) { + Array.prototype.push.apply(scrolls, jobScrolls[Math.floor((job.getId() / 10) % 10) - 1]); + } else { + for (var i = 0; i < jobScrolls.length; i++) { + Array.prototype.push.apply(scrolls, jobScrolls[i]); + } + } + + return scrolls; +} + +function getScrollTypePool(rewardTier) { + var scrolls = []; + switch (rewardTier) { + case 1: + if (cm.getPlayer().isAran()) { + Array.prototype.push.apply(scrolls, aranWeaponRestricted); + } else { + Array.prototype.push.apply(scrolls, getJobTierScrolls()); + } + + Array.prototype.push.apply(scrolls, tier1Scrolls); + break; + case 2: + Array.prototype.push.apply(scrolls, tier2Scrolls); + break; + default: + Array.prototype.push.apply(scrolls, tier3Scrolls); + } + + return scrolls; +} + +function getScrollTier(scrollStats) { + for (var i = 0; i < typeTierScrolls.length; i++) { + for (var j = 0; j < typeTierScrolls[i].length; j++) { + if (scrollStats.get(typeTierScrolls[i][j]) > 0) { + return i + 1; + } + } + } + + return 4; +} + +function getScrollSuccessTier(scrollStats) { + var prop = scrollStats.get("success"); + + if (prop > 90) { + return 3; + } else if (prop < 50) { + return YamlConfig.config.server.SCROLL_CHANCE_ROLLS > 2 ? 2 : 1; + } else { + return YamlConfig.config.server.SCROLL_CHANCE_ROLLS > 2 ? 1 : 2; + } +} + +function getAvailableScrollsPool(baseScrolls, rewardTier, successTier) { + var scrolls = []; + var ii = MapleItemInformationProvider.getInstance(); + + for (var i = 0; i < baseScrolls.length; i++) { + for (var j = 0; j < 100; j++) { + var scrollid = baseScrolls[i] + j; + var scrollStats = ii.getEquipStats(scrollid); + if (scrollStats != null && ii.getScrollReqs(scrollid).isEmpty()) { + var scrollTier = getScrollTier(scrollStats); + if (scrollTier == rewardTier && successTier == getScrollSuccessTier(scrollStats)) { + scrolls.push(scrollid); + } + } + } + } + + return scrolls; +} + +// passive tier buckets... + +function getLevelTier(level) { + return Math.floor((level - 1) / 15) + 1; +} + +function getPlayerCardTierPower() { + var cardset = cm.getPlayer().getMonsterBook().getCardSet(); + var countTier = [0, 0, 0, 0, 0, 0, 0, 0, 0]; + + for (var iterator = cardset.iterator(); iterator.hasNext();) { + var ce = iterator.next(); + + var cardid = ce.getKey(); + var ceTier = Math.floor(cardid / 1000) % 10; + countTier[ceTier] += ce.getValue(); + + if (ceTier >= 8) { // is special card + var mobLevel = MapleLifeFactory.getMonsterLevel(MapleItemInformationProvider.getInstance().getCardMobId(cardid)); + var mobTier = getLevelTier(mobLevel) - 1; + + countTier[mobTier] += (ce.getValue() * 1.2); + } + } + + return countTier; +} + +function calculateMobBookTierBuckets(tierSize, playerCards, tier) { + if (tier < 1) { + return 0.0; + } + + tier--; // started at 1 + var tierHitRate = playerCards[tier] / (tierSize[tier] * 5); + if (tierHitRate > 0.5) { + tierHitRate = 0.5; + } + + return tierHitRate * 4; +} + +function calculateMobBookBuckets() { + var book = cm.getPlayer().getMonsterBook(); + var bookLevelMult = 0.9 + (0.1 * book.getBookLevel()); + + var playerLevelTier = getLevelTier(cm.getPlayer().getLevel()); + if (playerLevelTier > 8) { + playerLevelTier = 8; + } + + var tierSize = MonsterBook.getCardTierSize(); + var playerCards = getPlayerCardTierPower(); + + var prevBuckets = calculateMobBookTierBuckets(tierSize, playerCards, playerLevelTier - 1); + var currBuckets = calculateMobBookTierBuckets(tierSize, playerCards, playerLevelTier); + + return (prevBuckets + currBuckets) * bookLevelMult; +} + +function recalcBuckets() { + sgBookBuckets = calculateMobBookBuckets(); + sgItemBuckets = calculateSuppliesBuckets(); + + var buckets = sgBookBuckets + sgItemBuckets; + if (buckets > 6.0) { + sgBuckets = 6; + } else { + sgBuckets = Math.floor(buckets); + } +} + +// variable buckets... + +function sgApplyItem(idx, amount) { + if (sgAppliedItems[idx] != amount) { + sgAppliedItems[idx] = amount; + recalcBuckets(); + } +} + +function sgApplyMeso(amount) { + if (sgAppliedMeso != amount) { + sgAppliedMeso = amount; + recalcBuckets(); + } +} + +function calculateSuppliesBuckets() { + var suppliesHitRate = 0.0; + for (var i = 0; i < sgItems.length; i++) { + suppliesHitRate += sgAppliedItems[i] / sgToBucket[i]; + } + suppliesHitRate *= 2; + + suppliesHitRate += (sgAppliedMeso / mesoToBucket); + return suppliesHitRate; +} + +function calculateScrollTiers() { + var buckets = sgBuckets; + var tiers = [0, 0, 0]; + while (buckets > 0) { + var pool = []; + for (var i = 0; i < tiers.length; i++) { + if (tiers[i] < 2) { + pool.push(i); + } + } + + var rnd = pool[Math.floor(Math.random() * pool.length)]; + + tiers[rnd]++; + buckets--; + } + + // normalize tiers + for (var i = 0; i < tiers.length; i++) { + tiers[i] = 3 - tiers[i]; + } + + return tiers; +} + +function getRandomScroll(tiers) { + var typeTier = tiers[0], subtypeTier = tiers[1], successTier = tiers[2]; + var scrollTypePool = getScrollTypePool(typeTier); + var scrollPool = getAvailableScrollsPool(scrollTypePool, subtypeTier, successTier); + + if (scrollPool.length > 0) { + return scrollPool[Math.floor(Math.random() * scrollPool.length)]; + } else { + return -1; + } +} + +function performExchange(sgItemid, sgCount) { + if (cm.getMeso() < sgAppliedMeso) { + return false; + } + + for (var i = 0; i < sgItems.length; i++) { + var itemid = sgItems[i]; + var count = sgAppliedItems[i]; + if (count > 0 && !cm.haveItem(itemid, count)) { + return false; + } + } + + cm.gainMeso(-sgAppliedMeso); + + for (var i = 0; i < sgItems.length; i++) { + var itemid = sgItems[i]; + var count = sgAppliedItems[i]; + cm.gainItem(itemid, -count); + } + + cm.gainItem(sgItemid, sgCount); + return true; +} + +function generateRandomScroll() { + if (cm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.USE).getNumFreeSlot() >= 1) { + var itemid = getRandomScroll(calculateScrollTiers()); + if (itemid != -1) { + if (performExchange(itemid, 1)) { + cm.sendNext("Transaction accepted! You have received a #r#t" + itemid + "##k."); + } else { + cm.sendOk("Oh, it looks like some items are missing... Please double-check provided items in your inventory before trying to exchange."); + } + } else { + cm.sendOk("Sorry for the inconvenience, but it seems there are no scrolls on store right now... Try again later."); + } + } else { + cm.sendOk("Please look out for a slot available on your USE inventory before trying for a scroll."); + } +} diff --git a/scripts/portal/MC2revive.js b/scripts/portal/MC2revive.js index 07e4002729..283d3925f5 100644 --- a/scripts/portal/MC2revive.js +++ b/scripts/portal/MC2revive.js @@ -1,8 +1,5 @@ function enter(pi) { - if ( pi.getPlayer().getTeam() == 0 ) { - pi.warp( pi.getMapId() - 100); - } else { - pi.warp( pi.getMapId() - 100); - } + pi.playPortalSound(); + pi.warp( pi.getMapId() - 100); return true; } \ No newline at end of file diff --git a/scripts/portal/MD_drakeroom.js b/scripts/portal/MD_drakeroom.js index 96ee511d5d..86339e773a 100644 --- a/scripts/portal/MD_drakeroom.js +++ b/scripts/portal/MD_drakeroom.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_error.js b/scripts/portal/MD_error.js index 0d91482f3e..51018666a0 100644 --- a/scripts/portal/MD_error.js +++ b/scripts/portal/MD_error.js @@ -34,7 +34,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -46,7 +46,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_golem.js b/scripts/portal/MD_golem.js index b4a46ae2f9..539e980cc7 100644 --- a/scripts/portal/MD_golem.js +++ b/scripts/portal/MD_golem.js @@ -34,7 +34,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -46,7 +46,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_high.js b/scripts/portal/MD_high.js index b13aa89c32..4c429593f1 100644 --- a/scripts/portal/MD_high.js +++ b/scripts/portal/MD_high.js @@ -34,7 +34,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -46,7 +46,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_mushroom.js b/scripts/portal/MD_mushroom.js index 4d15371ba3..46f12dc1bf 100644 --- a/scripts/portal/MD_mushroom.js +++ b/scripts/portal/MD_mushroom.js @@ -34,7 +34,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -46,7 +46,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_pig.js b/scripts/portal/MD_pig.js index 21a3c676bc..31d1bbbdfb 100644 --- a/scripts/portal/MD_pig.js +++ b/scripts/portal/MD_pig.js @@ -34,7 +34,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -46,7 +46,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_protect.js b/scripts/portal/MD_protect.js index 4dd9fdc253..7bdef62c06 100644 --- a/scripts/portal/MD_protect.js +++ b/scripts/portal/MD_protect.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_rabbit.js b/scripts/portal/MD_rabbit.js index 7e9ad57ccd..0d3737702a 100644 --- a/scripts/portal/MD_rabbit.js +++ b/scripts/portal/MD_rabbit.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_remember.js b/scripts/portal/MD_remember.js index 3c4f007a6f..21d2a3ff3d 100644 --- a/scripts/portal/MD_remember.js +++ b/scripts/portal/MD_remember.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_roundTable.js b/scripts/portal/MD_roundTable.js index 44aa8bbccd..8f4050bd66 100644 --- a/scripts/portal/MD_roundTable.js +++ b/scripts/portal/MD_roundTable.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_sand.js b/scripts/portal/MD_sand.js index 217b3efed5..d459d074bd 100644 --- a/scripts/portal/MD_sand.js +++ b/scripts/portal/MD_sand.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/MD_treasure.js b/scripts/portal/MD_treasure.js index b811797c2b..cc37b21a8c 100644 --- a/scripts/portal/MD_treasure.js +++ b/scripts/portal/MD_treasure.js @@ -32,7 +32,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warpParty(dungeonid + i); + pi.warpParty(dungeonid + i, "out00"); return true; } } @@ -44,7 +44,7 @@ function enter(pi) { for (var i = 0; i < dungeons; i++) { if(pi.startDungeonInstance(dungeonid + i)) { pi.playPortalSound(); - pi.warp(dungeonid + i); + pi.warp(dungeonid + i, "out00"); return true; } } diff --git a/scripts/portal/NextMap.js b/scripts/portal/NextMap.js index c0cbd12fe4..c73dd26196 100644 --- a/scripts/portal/NextMap.js +++ b/scripts/portal/NextMap.js @@ -1,5 +1,5 @@ function enter(pi) { pi.playPortalSound(); - pi.warp(pi.getMapId() + 100); + pi.warp(pi.getMapId() + 100, 0); return true; } \ No newline at end of file diff --git a/scripts/portal/Pinkin.js b/scripts/portal/Pinkin.js index 543553d91e..886729bccf 100644 --- a/scripts/portal/Pinkin.js +++ b/scripts/portal/Pinkin.js @@ -19,7 +19,7 @@ */ /* -Vs Pink Bean - Ressurection stage portal +Vs Pink Bean - Resurrection stage portal @author Ronan */ diff --git a/scripts/portal/Populatus00.js b/scripts/portal/Populatus00.js index 20c64ee7bb..deed0cea90 100644 --- a/scripts/portal/Populatus00.js +++ b/scripts/portal/Populatus00.js @@ -50,7 +50,7 @@ function enter(pi) { } } else { pi.playPortalSound(); - pi.warp(922020300); + pi.warp(922020300, 0); return true; } } \ No newline at end of file diff --git a/scripts/portal/Spacegaga_out0.js b/scripts/portal/Spacegaga_out0.js index ed2c009242..8a89b22737 100644 --- a/scripts/portal/Spacegaga_out0.js +++ b/scripts/portal/Spacegaga_out0.js @@ -1,11 +1,15 @@ //Author: kevintjuh93 function enter(pi) { - if (pi.getPlayer().getEvents().get("rescueGaga").fallAndGet() > 3) { - pi.playPortalSound(); pi.warp(922240200, 0); - pi.getPlayer().cancelEffect(2360002); - } else + var eim = pi.getPlayer().getEventInstance(); + var fc = eim.getIntProperty("falls"); + + if (fc >= 3) { + pi.playPortalSound(); pi.warp(922240200, 0); + } else { + eim.setIntProperty("falls", fc + 1); pi.playPortalSound(); pi.warp(pi.getPlayer().getMapId(), 0); + } return true; } \ No newline at end of file diff --git a/scripts/portal/Spacegaga_out1.js b/scripts/portal/Spacegaga_out1.js index ed2c009242..e27fdabbd6 100644 --- a/scripts/portal/Spacegaga_out1.js +++ b/scripts/portal/Spacegaga_out1.js @@ -1,11 +1,15 @@ //Author: kevintjuh93 function enter(pi) { - if (pi.getPlayer().getEvents().get("rescueGaga").fallAndGet() > 3) { - pi.playPortalSound(); pi.warp(922240200, 0); - pi.getPlayer().cancelEffect(2360002); - } else + var eim = pi.getPlayer().getEventInstance(); + var fc = eim.getIntProperty("falls"); + + if (fc >= 3) { + pi.playPortalSound(); pi.warp(922240200, 0); + } else { + eim.setIntProperty("falls", fc + 1); pi.playPortalSound(); pi.warp(pi.getPlayer().getMapId(), 0); + } return true; } \ No newline at end of file diff --git a/scripts/portal/Spacegaga_out2.js b/scripts/portal/Spacegaga_out2.js index ed2c009242..e27fdabbd6 100644 --- a/scripts/portal/Spacegaga_out2.js +++ b/scripts/portal/Spacegaga_out2.js @@ -1,11 +1,15 @@ //Author: kevintjuh93 function enter(pi) { - if (pi.getPlayer().getEvents().get("rescueGaga").fallAndGet() > 3) { - pi.playPortalSound(); pi.warp(922240200, 0); - pi.getPlayer().cancelEffect(2360002); - } else + var eim = pi.getPlayer().getEventInstance(); + var fc = eim.getIntProperty("falls"); + + if (fc >= 3) { + pi.playPortalSound(); pi.warp(922240200, 0); + } else { + eim.setIntProperty("falls", fc + 1); pi.playPortalSound(); pi.warp(pi.getPlayer().getMapId(), 0); + } return true; } \ No newline at end of file diff --git a/scripts/portal/Spacegaga_out3.js b/scripts/portal/Spacegaga_out3.js index ed2c009242..8a89b22737 100644 --- a/scripts/portal/Spacegaga_out3.js +++ b/scripts/portal/Spacegaga_out3.js @@ -1,11 +1,15 @@ //Author: kevintjuh93 function enter(pi) { - if (pi.getPlayer().getEvents().get("rescueGaga").fallAndGet() > 3) { - pi.playPortalSound(); pi.warp(922240200, 0); - pi.getPlayer().cancelEffect(2360002); - } else + var eim = pi.getPlayer().getEventInstance(); + var fc = eim.getIntProperty("falls"); + + if (fc >= 3) { + pi.playPortalSound(); pi.warp(922240200, 0); + } else { + eim.setIntProperty("falls", fc + 1); pi.playPortalSound(); pi.warp(pi.getPlayer().getMapId(), 0); + } return true; } \ No newline at end of file diff --git a/scripts/portal/TD_MC_enterboss1.js b/scripts/portal/TD_MC_enterboss1.js index 0718c1730a..260a8cb2b4 100644 --- a/scripts/portal/TD_MC_enterboss1.js +++ b/scripts/portal/TD_MC_enterboss1.js @@ -1,5 +1,5 @@ function enter(pi) { - var questProgress = pi.getQuestProgress(2330, 3300005) + pi.getQuestProgress(2330, 3300006) + pi.getQuestProgress(2330, 3300007); //3 Yetis + var questProgress = pi.getQuestProgressInt(2330, 3300005) + pi.getQuestProgressInt(2330, 3300006) + pi.getQuestProgressInt(2330, 3300007); //3 Yetis if(pi.isQuestStarted(2330) && questProgress < 3){ pi.openNpc(1300013); diff --git a/scripts/portal/TD_neo_inTree.js b/scripts/portal/TD_neo_inTree.js index 382fab3263..0ee32c0ac8 100644 --- a/scripts/portal/TD_neo_inTree.js +++ b/scripts/portal/TD_neo_inTree.js @@ -10,7 +10,7 @@ function enter(pi) { for(var i = 0; i < quests.length; i++) { if (pi.isQuestActive(quests[i])) { - if(pi.getQuestProgress(quests[i], mobs[i]) != 0) { + if(pi.getQuestProgressInt(quests[i], mobs[i]) != 0) { pi.message("You already faced Nex. Complete your mission."); return false; } diff --git a/scripts/portal/Zakum05.js b/scripts/portal/Zakum05.js index 7f0f674aab..b23f2495a4 100644 --- a/scripts/portal/Zakum05.js +++ b/scripts/portal/Zakum05.js @@ -34,6 +34,17 @@ function enter(pi) { return false; } + if (!pi.haveItem(4001017)) { // thanks Conrad for pointing out missing checks for token item and unused reactor + pi.getPlayer().dropMessage(5,"You do not have the Eye of Fire. You may not face the boss."); + return false; + } + + var react = pi.getMap().getReactorById(2118002); + if (react != null && react.getState() > 0) { + pi.getPlayer().dropMessage(5,"The entrance is currently blocked."); + return false; + } + pi.playPortalSound(); pi.warp(211042400,"west00"); return true; } \ No newline at end of file diff --git a/scripts/portal/enterInfo.js b/scripts/portal/enterInfo.js index 02a1b45dec..466f53f0cd 100644 --- a/scripts/portal/enterInfo.js +++ b/scripts/portal/enterInfo.js @@ -1,7 +1,8 @@ function enter(pi) { var mapobj = pi.getWarpMap(104000004); - if(pi.isQuestActive(21733) && pi.getQuestProgress(21733, 9300345) == 0 && mapobj.countMonsters() == 0) { + if(pi.isQuestActive(21733) && pi.getQuestProgressInt(21733, 9300345) == 0 && mapobj.countMonsters() == 0) { mapobj.spawnMonsterOnGroundBelow(Packages.server.life.MapleLifeFactory.getMonster(9300345), new java.awt.Point(0, 0)); + pi.setQuestProgress(21733, 21762, 2); } pi.playPortalSound(); diff --git a/scripts/portal/enterMCave.js b/scripts/portal/enterMCave.js index dc2052f0e5..e121cc1b5f 100644 --- a/scripts/portal/enterMCave.js +++ b/scripts/portal/enterMCave.js @@ -9,8 +9,7 @@ function enter(pi) { pi.playPortalSound(); pi.warp(i, "out00"); - pi.getPlayer().updateQuestInfo(21202, "0"); - //pi.getPlayer().announce(Packages.tools.MaplePacketCreator.questProgress(21203, "21203")); + pi.setQuestProgress(21202, 21203, 0); return true; } pi.message("The mirror is blank due to many players recalling their memories. Please wait and try again."); @@ -24,7 +23,7 @@ function enter(pi) { spawnMob(-210, 454, 9001013, map); pi.playPortalSound(); - pi.getPlayer().updateQuestInfo(21203, "1"); + pi.setQuestProgress(21303, 21203, 1); pi.warp(108010701, "out00"); return true; } diff --git a/scripts/portal/enterPort.js b/scripts/portal/enterPort.js index 58147bee61..e79056d85c 100644 --- a/scripts/portal/enterPort.js +++ b/scripts/portal/enterPort.js @@ -22,7 +22,7 @@ importPackage(Packages.server.life); function enter(pi) { - if(pi.isQuestStarted(21301) && pi.getQuestProgress(21301, 9001013) == 0) { + if(pi.isQuestStarted(21301) && pi.getQuestProgressInt(21301, 9001013) == 0) { if(pi.getPlayerCount(108010700) != 0) { pi.message("The portal is blocked from the other side. I wonder if someone is already fighting the Thief Crow?"); return false; diff --git a/scripts/portal/gaga_success.js b/scripts/portal/gaga_success.js index 24d3c5d0af..c32a40e070 100644 --- a/scripts/portal/gaga_success.js +++ b/scripts/portal/gaga_success.js @@ -1,8 +1,6 @@ //Author: kevintjuh93 function enter(pi) { - pi.getPlayer().getEvents().get("rescueGaga").complete(); - pi.playPortalSound(); pi.warp(922240100 + (pi.getPlayer().getMapId() - 922240000)); - pi.getPlayer().cancelEffect(2360002); + pi.playPortalSound(); pi.warp(922240100 + (pi.getPlayer().getMapId() - 922240000), 0); return true; } \ No newline at end of file diff --git a/scripts/portal/highposition.js b/scripts/portal/highposition.js index 75b838464a..2496c7eed6 100644 --- a/scripts/portal/highposition.js +++ b/scripts/portal/highposition.js @@ -20,10 +20,9 @@ along with this program. If not, see . */ -/* - * Author: kevintjuh93 - * -*/ -function start(ms) { - ms.touchTheSky(); +// Author: Ronan +function enter(ms) { + // thanks kvmba for noticing some issues running this script + ms.runMapScript(); + return false; } \ No newline at end of file diff --git a/scripts/portal/jnr6_out.js b/scripts/portal/jnr6_out.js index ba099b9be4..aec7e9956a 100644 --- a/scripts/portal/jnr6_out.js +++ b/scripts/portal/jnr6_out.js @@ -1,6 +1,6 @@ function enter(pi) { if (pi.getMap().getReactorByName("jnr6_out").getState() == 1) { - pi.playPortalSound(); pi.warp(926110300); + pi.playPortalSound(); pi.warp(926110300, 0); return true; } else { pi.playerMessage(5, "The portal is not opened yet."); diff --git a/scripts/portal/out_pepeking.js b/scripts/portal/out_pepeking.js index 5532f8eef0..67d59554f4 100644 --- a/scripts/portal/out_pepeking.js +++ b/scripts/portal/out_pepeking.js @@ -5,7 +5,7 @@ function enter(pi) { eim.dispose(); } - var questProgress = pi.getQuestProgress(2330, 3300005) + pi.getQuestProgress(2330, 3300006) + pi.getQuestProgress(2330, 3300007); //3 Yetis + var questProgress = pi.getQuestProgressInt(2330, 3300005) + pi.getQuestProgressInt(2330, 3300006) + pi.getQuestProgressInt(2330, 3300007); //3 Yetis if(questProgress == 3 && !pi.hasItem(4032388)) { if(pi.canHold(4032388)){ pi.getPlayer().message("You have aquired a key to the Wedding Hall. King Pepe must have dropped it."); diff --git a/scripts/portal/party3_jailin.js b/scripts/portal/party3_jailin.js index f4b107c220..64847fd211 100644 --- a/scripts/portal/party3_jailin.js +++ b/scripts/portal/party3_jailin.js @@ -1,46 +1,72 @@ importPackage(Packages.tools); -function enter(pi) { - var map = pi.getMap(); +var leverSequenceExit = false; + +function enterLeverSequence(pi) { + var map = pi.getMap(); - var jailn = (pi.getMap().getId() / 10) % 10; - var maxToggles = (jailn == 1) ? 7 : 6; - - var mapProp = pi.getEventInstance().getProperty("jail" + jailn); - - if(mapProp == null) { - var seq = 0; - - for(var i = 1; i <= maxToggles; i++) { - if(Math.random() < 0.5) seq += (1 << i); - } - - pi.getEventInstance().setProperty("jail" + jailn, seq); - mapProp = seq; + var jailn = (pi.getMap().getId() / 10) % 10; + var maxToggles = (jailn == 1) ? 7 : 6; + + var mapProp = pi.getEventInstance().getProperty("jail" + jailn); + + if(mapProp == null) { + var seq = 0; + + for(var i = 1; i <= maxToggles; i++) { + if(Math.random() < 0.5) seq += (1 << i); } - - mapProp = Number(mapProp); - if(mapProp != 0) { - var countMiss = 0; - for(var i = 1; i <= maxToggles; i++) { - if(!(pi.getMap().getReactorByName("lever" + i).getState() == (mapProp >> i) % 2)) { - countMiss++; - } + + pi.getEventInstance().setProperty("jail" + jailn, seq); + mapProp = seq; + } + + mapProp = Number(mapProp); + if(mapProp != 0) { + var countMiss = 0; + for(var i = 1; i <= maxToggles; i++) { + if(!(pi.getMap().getReactorByName("lever" + i).getState() == (mapProp >> i) % 2)) { + countMiss++; } - - if(countMiss > 0) { - map.broadcastMessage(MaplePacketCreator.showEffect("quest/party/wrong_kor")); - map.broadcastMessage(MaplePacketCreator.playSound("Party1/Failed")); - - pi.playerMessage(5, "The right combination of levers is needed to pass. " + countMiss + " lever(s) are misplaced."); - return false; - } - - map.broadcastMessage(MaplePacketCreator.showEffect("quest/party/clear")); - map.broadcastMessage(MaplePacketCreator.playSound("Party1/Clear")); - pi.getEventInstance().setProperty("jail" + jailn, "0"); } - + + if(countMiss > 0) { + map.broadcastMessage(MaplePacketCreator.showEffect("quest/party/wrong_kor")); + map.broadcastMessage(MaplePacketCreator.playSound("Party1/Failed")); + + pi.playerMessage(5, "The right combination of levers is needed to pass. " + countMiss + " lever(s) are misplaced."); + return false; + } + + map.broadcastMessage(MaplePacketCreator.showEffect("quest/party/clear")); + map.broadcastMessage(MaplePacketCreator.playSound("Party1/Clear")); + pi.getEventInstance().setProperty("jail" + jailn, "0"); + } + + pi.playPortalSound(); pi.warp(pi.getMapId() + 2,0); + return true; +} + +function enterNoMobs(pi) { + var map = pi.getMap(); + var mobcount = map.countMonster(9300044); + + if (mobcount > 0) { + pi.playerMessage(5, "Please use the levers to defeat all the threats before you proceed."); + return false; + } else { pi.playPortalSound(); pi.warp(pi.getMapId() + 2,0); return true; + } +} + +function enter(pi) { + var ret; + if (leverSequenceExit) { + ret = enterLeverSequence(pi); + } else { + ret = enterNoMobs(pi); + } + + return ret; } \ No newline at end of file diff --git a/scripts/portal/party6_stage501.js b/scripts/portal/party6_stage501.js index 887accb9f1..2788e39c89 100644 --- a/scripts/portal/party6_stage501.js +++ b/scripts/portal/party6_stage501.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "02st"); diff --git a/scripts/portal/party6_stage502.js b/scripts/portal/party6_stage502.js index 259846a773..2b9dc73d03 100644 --- a/scripts/portal/party6_stage502.js +++ b/scripts/portal/party6_stage502.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "03st"); diff --git a/scripts/portal/party6_stage503.js b/scripts/portal/party6_stage503.js index 38bd839b2d..29706f649b 100644 --- a/scripts/portal/party6_stage503.js +++ b/scripts/portal/party6_stage503.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "04st"); diff --git a/scripts/portal/party6_stage504.js b/scripts/portal/party6_stage504.js index 4ec4244a85..94b7f6d513 100644 --- a/scripts/portal/party6_stage504.js +++ b/scripts/portal/party6_stage504.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "05st"); diff --git a/scripts/portal/party6_stage505.js b/scripts/portal/party6_stage505.js index afdd71b343..5ae1c318da 100644 --- a/scripts/portal/party6_stage505.js +++ b/scripts/portal/party6_stage505.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "06st"); diff --git a/scripts/portal/party6_stage506.js b/scripts/portal/party6_stage506.js index 7b57c7125f..1078adff06 100644 --- a/scripts/portal/party6_stage506.js +++ b/scripts/portal/party6_stage506.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "07st"); diff --git a/scripts/portal/party6_stage507.js b/scripts/portal/party6_stage507.js index 44e43aeb1e..e8f41aa0ac 100644 --- a/scripts/portal/party6_stage507.js +++ b/scripts/portal/party6_stage507.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "08st"); diff --git a/scripts/portal/party6_stage508.js b/scripts/portal/party6_stage508.js index 5f930d209f..acade52b73 100644 --- a/scripts/portal/party6_stage508.js +++ b/scripts/portal/party6_stage508.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "09st"); diff --git a/scripts/portal/party6_stage509.js b/scripts/portal/party6_stage509.js index 886fcf046e..57c8694fcb 100644 --- a/scripts/portal/party6_stage509.js +++ b/scripts/portal/party6_stage509.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "10st"); diff --git a/scripts/portal/party6_stage510.js b/scripts/portal/party6_stage510.js index 777fc0ded7..6632bbd4d1 100644 --- a/scripts/portal/party6_stage510.js +++ b/scripts/portal/party6_stage510.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "11st"); diff --git a/scripts/portal/party6_stage511.js b/scripts/portal/party6_stage511.js index 253bd5100b..93c7f56cfb 100644 --- a/scripts/portal/party6_stage511.js +++ b/scripts/portal/party6_stage511.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "12st"); diff --git a/scripts/portal/party6_stage512.js b/scripts/portal/party6_stage512.js index cef1f8877f..8c91b29407 100644 --- a/scripts/portal/party6_stage512.js +++ b/scripts/portal/party6_stage512.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "13st"); diff --git a/scripts/portal/party6_stage513.js b/scripts/portal/party6_stage513.js index 15828f2585..a27b6cb44e 100644 --- a/scripts/portal/party6_stage513.js +++ b/scripts/portal/party6_stage513.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "14st"); diff --git a/scripts/portal/party6_stage514.js b/scripts/portal/party6_stage514.js index a2c4ffa187..903971ec18 100644 --- a/scripts/portal/party6_stage514.js +++ b/scripts/portal/party6_stage514.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "15st"); diff --git a/scripts/portal/party6_stage515.js b/scripts/portal/party6_stage515.js index 221db8bc2e..4c3f689ad4 100644 --- a/scripts/portal/party6_stage515.js +++ b/scripts/portal/party6_stage515.js @@ -1,5 +1,5 @@ function enter(pi) { - if (java.lang.Math.random() < 0.1) { + if (Math.random() < 0.1) { pi.playPortalSound(); pi.warp(930000300,"16st"); } else { pi.playPortalSound(); pi.warp(930000300, "01st"); diff --git a/scripts/portal/q3367in.js b/scripts/portal/q3367in.js index 0e2819c85a..daf52bf2cf 100644 --- a/scripts/portal/q3367in.js +++ b/scripts/portal/q3367in.js @@ -19,8 +19,11 @@ */ function enter(pi) { if(pi.isQuestStarted(3367)) { - if(pi.getQuestProgress(3367, 31) < pi.getItemQuantity(4031797)) { - pi.gainItem(4031797, pi.getQuestProgress(3367, 31) - pi.getItemQuantity(4031797)); + var booksDone = pi.getQuestProgressInt(3367, 31); + var booksInv = pi.getItemQuantity(4031797); + + if(booksInv < booksDone) { + pi.gainItem(4031797, booksDone - booksInv); } pi.playPortalSound(); pi.warp(926130102, 0); diff --git a/scripts/portal/s4mind_end.js b/scripts/portal/s4mind_end.js index 72ce8d568d..7067e76d11 100644 --- a/scripts/portal/s4mind_end.js +++ b/scripts/portal/s4mind_end.js @@ -23,7 +23,7 @@ function enter(pi) { return false; } else { if (pi.isQuestStarted(6410)) { - pi.setQuestProgress(6410, 0, 1); + pi.setQuestProgress(6410, 6411, "p2"); } pi.playPortalSound(); diff --git a/scripts/portal/secretDoor.js b/scripts/portal/secretDoor.js index a1f00a0145..43fb733eed 100644 --- a/scripts/portal/secretDoor.js +++ b/scripts/portal/secretDoor.js @@ -30,7 +30,8 @@ function enter(pi) { if(pi.isQuestCompleted(3360)) { return doorCross(pi); } else if(pi.isQuestStarted(3360)) { - if(pi.getQuestProgress(3360, 1) == 0) { + var pw = pi.getQuestProgress(3360); + if(pw.length() > 1) { pi.openNpc(2111024, "MagatiaPassword"); return false; } else { diff --git a/scripts/portal/skyrom.js b/scripts/portal/skyrom.js index d461b1241c..dbb7c9a4d5 100644 --- a/scripts/portal/skyrom.js +++ b/scripts/portal/skyrom.js @@ -21,7 +21,7 @@ function enter(pi) { if(pi.isQuestStarted(3935) && !pi.haveItem(4031574, 1)) { if(pi.getWarpMap(926000010).countPlayers() == 0) { - pi.playPortalSound(); pi.warp(926000010); + pi.playPortalSound(); pi.warp(926000010, 0); return true; } else { pi.message("Someone is already trying this map."); diff --git a/scripts/portal/stageBogo.js b/scripts/portal/stageBogo.js index 6ec238c3c9..115efd4a7e 100644 --- a/scripts/portal/stageBogo.js +++ b/scripts/portal/stageBogo.js @@ -24,6 +24,6 @@ @Author Ronan */ function enter(pi) { - pi.playPortalSound(); pi.warp(670010800); + pi.playPortalSound(); pi.warp(670010800, 0); return true; } \ No newline at end of file diff --git a/scripts/portal/thief_in1.js b/scripts/portal/thief_in1.js index a51f5c69ab..796f9541bf 100644 --- a/scripts/portal/thief_in1.js +++ b/scripts/portal/thief_in1.js @@ -1,9 +1,6 @@ function enter(pi) { - if(pi.isQuestCompleted(20730) || pi.isQuestCompleted(21734)) { // puppeteer defeated, newfound secret path - pi.playPortalSound(); pi.warp(105040201,2); - return true; - } - - pi.openNpc(1063011, "ThiefPassword"); + // unexpected warp condition noticed thanks to IxianMace + + pi.openNpc(1063011, "ThiefPassword"); return false; } \ No newline at end of file diff --git a/scripts/quest/1021.js b/scripts/quest/1021.js index ba16526af5..7d10f5d8f6 100644 --- a/scripts/quest/1021.js +++ b/scripts/quest/1021.js @@ -45,7 +45,7 @@ function start(mode, type, selection) { status--; if (status == 0) - qm.sendNext("Hey, Man~ What's up? Haha! I am Roger who can teach you adorable new Maplers lots of information."); + qm.sendNext("Hey, " + (qm.getPlayer().getGender() == 0 ? "Man" : "Miss") + "~ What's up? Haha! I am Roger who can teach you adorable new Maplers lots of information."); else if (status == 1) qm.sendNextPrev("You are asking who made me do this? Ahahahaha!\r\nMyself! I wanted to do this and just be kind to you new travellers."); else if (status == 2) @@ -62,7 +62,7 @@ function start(mode, type, selection) { qm.forceStartQuest(); qm.sendNext("Surprised? If HP becomes 0, then you are in trouble. Now, I will give you #rRoger's Apple#k. Please take it. You will feel stronger. Open the Item window and double click to consume. Hey, it's very simple to open the Item window. Just press #bI#k on your keyboard."); } else if (status == 4) { - qm.sendNextPrev("Please take all Roger's Apples that I gave you. You will be able to see the HP bar increasing. Please talk to me again when you recover your HP 100%."); + qm.sendPrev("Please take all Roger's Apples that I gave you. You will be able to see the HP bar increasing. Please talk to me again when you recover your HP 100%."); } else if (status == 5) { qm.showInfo("UI/tutorial.img/28"); qm.dispose(); @@ -93,7 +93,7 @@ function end(mode, type, selection) { else if (status == 1) qm.sendNextPrev("Alright! Now that you have learned alot, I will give you a present. This is a must for your travel in Maple World, so thank me! Please use this under emergency cases!"); else if (status == 2) - qm.sendNextPrev("Okay, this is all I can teach you. I know it's sad but it is time to say good bye. Well take care if yourself and Good luck my friend!\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n#v2010000# 3 #t2010000#\r\n#v2010009# 3 #t2010009#\r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0# 10 exp"); + qm.sendPrev("Okay, this is all I can teach you. I know it's sad but it is time to say good bye. Well take care if yourself and Good luck my friend!\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n#v2010000# 3 #t2010000#\r\n#v2010009# 3 #t2010009#\r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0# 10 exp"); else if (status == 3) { if(qm.isQuestCompleted(1021)) qm.dropMessage(1,"Unknown Error"); diff --git a/scripts/quest/10940.js b/scripts/quest/10940.js index 67c8cc0408..6590a761e0 100644 --- a/scripts/quest/10940.js +++ b/scripts/quest/10940.js @@ -10,14 +10,15 @@ function start(mode, type, selection) { status++; else qm.dispose(); - if (status == 0) + if (status == 0) { qm.sendAcceptDecline("Hello, #h0#. Welcome to Maple World. It's currently event season, and we're welcome new characters with a gift. Would you like your gift now?"); - else if (status == 1) { + } else if (status == 1) { + qm.sendOk("Open your inventory and double-click on it! These gifts will make you look stylish. Oh, one more thing! You'll get another gift at level 30. Good luck!"); qm.forceStartQuest(); qm.forceCompleteQuest(); qm.gainItem(2430191, 1, true); - qm.sendOk("Open your inventory and double-click on it! These gifts will make you look stylish. Oh, one more thing! You'll get another gift at level 30. Good luck!"); - qm.dispose(); + } else if (status == 2) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/20002.js b/scripts/quest/20002.js index fd11746411..8284cc250d 100644 --- a/scripts/quest/20002.js +++ b/scripts/quest/20002.js @@ -44,13 +44,16 @@ function start(mode, type, selection) { else if (status == 2) qm.sendAcceptDecline("Ah, I don't know if you are aware of this, but you won't find any monsters here in Ereve. Any form of evil will not be able to set foot on this island. Don't worry, you'll still have your opportunity to train here. Shinsoo created a fantasy creature called Mimi, which will be used as your training partners. Shall we begin?"); else if (status == 3) { + qm.forceStartQuest(); + qm.forceCompleteQuest(); + qm.gainExp(60); qm.gainItem(2000020, 10); // Red Potion for Noblesse * 10 qm.gainItem(2000021, 10); // Blue Potion for Noblesse * 10 qm.gainItem(1002869, 1); // Elegant Noblesse Hat * 1 + qm.sendOk("Ha, I like your enthusiasm, but you must prepare yourself for the training first before we start things off. Make sure that you are equipped with weapons, and that your skills are calibrated and ready to be used. I also gave you some potions, so have it ready just in case. Let me know when you're ready. You're going to wish that you didn't sign up to become a Cygnus Knight."); - qm.forceStartQuest(); - qm.forceCompleteQuest(); + } else if (status == 4) { qm.dispose(); } } diff --git a/scripts/quest/20008.js b/scripts/quest/20008.js index dca016c829..e5d7d9d617 100644 --- a/scripts/quest/20008.js +++ b/scripts/quest/20008.js @@ -34,17 +34,21 @@ function start(mode, type, selection) { if (mode < 1) { qm.dispose(); } - if (mode > 0) + else if (mode > 0) status++; if (status == 0) qm.sendSimple("Are you ready to take on a mission? If you can't pass this test, then you won't be able to call yourself a real Knight. Are you sure you can do this? If you are afraid to do this, let me know. I won't tell Neinheart. \r\n #L0#I'll try this later.#l \r\n #L1#I'm not afraid. Let's do this.#l"); - else if (selection == 0) { - qm.sendNext("If you call yourself a Knight, then do not hesitate. Show everyone how much courage you have in you."); + else if (status == 1) { + if (selection == 0) { + qm.sendNext("If you call yourself a Knight, then do not hesitate. Show everyone how much courage you have in you."); + qm.dispose(); + } else if (selection == 1) { + choice1 = selection; + qm.sendSimple("I'm glad you didn't run away, but... are you sure you want to become a Knight-in-Training? What I am asking is whether you're okay with being a Cygnus Knight, and therefore being tied to the Empress at all times? She may be an Empress, but she's also still just a kid. Are you sure you can fight for her? I won't let Neinheart know so just tell me what you really feel. \r\n #L2#If the Empress wants peace in the Maple World, then I'm down for whatever.#l \r\n #L3#As long as I can become a knight I'll endure whatever #l"); + qm.forceStartQuest(); + qm.forceCompleteQuest(); + } + } else if (status == 2) { qm.dispose(); - } else if (selection == 1) { - choice1 = selection; - qm.sendSimple("I'm glad you didn't run away, but... are you sure you want to become a Knight-in-Training? What I am asking is whether you're okay with being a Cygnus Knight, and therefore being tied to the Empress at all times? She may be an Empress, but she's also still just a kid. Are you sure you can fight for her? I won't let Neinheart know so just tell me what you really feel. \r\n #L2#If the Empress wants peace in the Maple World, then I'm down for whatever.#l \r\n #L3#As long as I can become a knight I'll endure whatever #l"); - qm.forceStartQuest(); - qm.forceCompleteQuest(); } } \ No newline at end of file diff --git a/scripts/quest/20010.js b/scripts/quest/20010.js index eb3d98ac80..e68d48f35f 100644 --- a/scripts/quest/20010.js +++ b/scripts/quest/20010.js @@ -5,6 +5,12 @@ function start(mode, type, selection) { qm.sendNext("Whoa, whoa! Are you really declining my offer? Well, you'll be able to #blevel-up quicker #kwith our help, so let me know if you change your mind. Even if you've declined a Quest, you can receive the Quest again if you just come and talk to me."); qm.dispose(); } else { + if(mode == 0 && type > 0) { + qm.sendNext("Whoa, whoa! Are you really declining my offer? Well, you'll be able to #blevel-up quicker #kwith our help, so let me know if you change your mind. Even if you've declined a Quest, you can receive the Quest again if you just come and talk to me."); + qm.dispose(); + return; + } + if (mode == 1) status++; else @@ -14,7 +20,7 @@ function start(mode, type, selection) { } else if (status == 1) { qm.sendNextPrev("If you want to officially become a part of Cygnus Knights, you must first meet the Empress. She's at the center of this island, accompained by Shinsoo. My brothers and I would like to share with you a few things that are considered #bBasic Knowledge#k in Maple World before you go. Would that be okay?"); } else if (status == 2) { - qm.sendOk("Oh, let me warn you that this is a Quest. You may have noticed that NPCs around Maple World occasionally ask you for various favors. A favor of that sort is called a #bQuest#k. You will receive reward items or EXP upon completing Quests, so I strongly suggest you diligently fulfill the favors of Maple NPCs."); + qm.sendNextPrev("Oh, let me warn you that this is a Quest. You may have noticed that NPCs around Maple World occasionally ask you for various favors. A favor of that sort is called a #bQuest#k. You will receive reward items or EXP upon completing Quests, so I strongly suggest you diligently fulfill the favors of Maple NPCs."); } else if (status == 3) { qm.sendAcceptDecline("Would you like to meet #bKizan#k, who can tell you about hunting? You can find Kizan by following the arrow to the left."); } else if (status == 4) { @@ -31,7 +37,7 @@ function end(mode, type, selection) { qm.dispose(); } else { if (status == 0) { - qm.sendNext("Are you the Noblesse my brother Kimu sent? Nice to meet you! I'm Kizan. I'll give you the reward Kimu asked me to give you. Remember, you can check your Inventory by pressing the #bI key#k. Red potions help you recover HP, and blue ones help recover MP. It's a good idea to learn how to use them beforehand so you'll be ready with them when you're in danger. \r\n\r\n#fUI/UIWindow.img/Quest/reward# \r\n\r\n#v2000020# #z2000020# \r\n#v2000021# #z2000021# \r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0#15 exp"); + qm.sendOk("Are you the Noblesse my brother Kimu sent? Nice to meet you! I'm Kizan. I'll give you the reward Kimu asked me to give you. Remember, you can check your Inventory by pressing the #bI key#k. Red potions help you recover HP, and blue ones help recover MP. It's a good idea to learn how to use them beforehand so you'll be ready with them when you're in danger. \r\n\r\n#fUI/UIWindow.img/Quest/reward# \r\n\r\n#v2000020# #z2000020# \r\n#v2000021# #z2000021# \r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0#15 exp"); } else if (status == 1) { if(qm.canHold(2000022) && qm.canHold(2000023)){ if(!qm.isQuestCompleted(21010)) { diff --git a/scripts/quest/20011.js b/scripts/quest/20011.js index 1153de5c31..be08d1c3fa 100644 --- a/scripts/quest/20011.js +++ b/scripts/quest/20011.js @@ -48,7 +48,7 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Ah, it seems like you've successfully hunted a #o100120#. Pretty simple, right? Regular Attacks may be easy to use, but they are pretty weak. Don't worry, though. #p1102006# will teach you how to use more powerful skills. Wait, let me give you a well-deserved quest reward before you go."); } else if (status == 1) { - qm.sendNextPrev("This equipment is for Noblesses. It's much cooler than what you're wearing right now, isn't it? Follow the arrows to your left to meet my younger brother #b#p1102006##k. How about you change into your new Noblesse outfit before you go? \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#i1002869# #t1002869# - 1 \r\n#i1052177# #t1052177# - 1 \r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0# 30 exp"); + qm.sendPrev("This equipment is for Noblesses. It's much cooler than what you're wearing right now, isn't it? Follow the arrows to your left to meet my younger brother #b#p1102006##k. How about you change into your new Noblesse outfit before you go? \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#i1002869# #t1002869# - 1 \r\n#i1052177# #t1052177# - 1 \r\n\r\n#fUI/UIWindow.img/QuestIcon/8/0# 30 exp"); } else if (status == 2) { qm.gainItem(1002869, 1); qm.gainItem(1052177, 1); diff --git a/scripts/quest/20013.js b/scripts/quest/20013.js index 6efdb7a762..6742172b5e 100644 --- a/scripts/quest/20013.js +++ b/scripts/quest/20013.js @@ -52,7 +52,7 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Did you bring me a Building Stone and a Drape? Let's see. Ah, these are just what I need! They indeed are a #t4032267# and a #t4032268#! I'll make you a Chair right away."); } else if (status == 1) { - qm.sendNextPrev("Here it is, a #t3010060#. What do you think? Nifty, huh? You can #bquickly recover your HP by sitting in this Chair#k. It will be stored in the #bSet-up#k window in your Inventory, so confirm that you've received the chair and head over to #b#p1102008##k. You'll see him if you keep following the arrow to the left. \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#i3010060# 1 #t3010060# \r\n#fUI/UIWindow.img/QuestIcon/8/0# 95 exp"); + qm.sendPrev("Here it is, a #t3010060#. What do you think? Nifty, huh? You can #bquickly recover your HP by sitting in this Chair#k. It will be stored in the #bSet-up#k window in your Inventory, so confirm that you've received the chair and head over to #b#p1102008##k. You'll see him if you keep following the arrow to the left. \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#i3010060# 1 #t3010060# \r\n#fUI/UIWindow.img/QuestIcon/8/0# 95 exp"); } else if (status == 2) { qm.gainItem(4032267, -1); qm.gainItem(4032268, -1); diff --git a/scripts/quest/20016.js b/scripts/quest/20016.js index 770529ce08..9d6e0f1903 100644 --- a/scripts/quest/20016.js +++ b/scripts/quest/20016.js @@ -50,9 +50,10 @@ function start(mode, type, selection) { } else if (status == 11) { qm.sendNextPrev("But no one starts as a strong Knight on day one. The Empress didn't want someone strong. She wanted someone with courage whom she could develop into a strong Knight through rigorous training. So, you should first become a Knight-in-Training. We'll talk about your missions when you get to that point."); } else if (status == 12) { - qm.sendPrev("Take the portal on the left to reach the Training Forest. There, you will find #p1102000#, the Training Instructor, who will teach you how to become stronger. I don't want to find you wandering around aimlessly until you reach Lv. 10, you hear?"); qm.forceCompleteQuest(); - qm.dispose(); + qm.sendPrev("Take the portal on the left to reach the Training Forest. There, you will find #p1102000#, the Training Instructor, who will teach you how to become stronger. I don't want to find you wandering around aimlessly until you reach Lv. 10, you hear?"); + } else if (status == 13) { + qm.dispose(); } } diff --git a/scripts/quest/20020.js b/scripts/quest/20020.js new file mode 100644 index 0000000000..91b02cc2f1 --- /dev/null +++ b/scripts/quest/20020.js @@ -0,0 +1,38 @@ +/* + NPC Name: Nineheart + Description: Quest - Cygnus movie Intro +*/ +var status = -1; + +function start(mode, type, selection) { + if (mode == -1) { + qm.sendOk("Talk to me after you have decided what you really want to do. Whatever you choose, you will not miss out or lose privileges, so don't take this too seriously..."); + qm.dispose(); + } else { + if(mode == 0 && type > 0 || selection == 1) { + qm.sendOk("Talk to me after you have decided what you really want to do. Whatever you choose, you will not miss out or lose privileges, so don't take this too seriously..."); + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("I can tell you've worked really hard by seeing that you're already at Level 10. I think it's time now for you to break out as a Nobless and officially become the Knight-in-Training. Before doing that, however, I want to ask you one thing. Have you decided which Knight you'd want to beome?"); + } else if (status == 1) { + qm.sendNextPrev("There isn't a single path to becoming a Knight. In fact, there are five of them laid out for you. It's up to you to choose which path you'd like to take, but it should definitely be something you will not regret. That's why... I am offering to show you what you'll look like once you become a Knight."); + } else if (status == 2) { + qm.sendSimple("What do you think? Are you interested in seeing yourself as the leader of the Knights? If you have already decided what kind of Knight you'd like to become, then you won't necessarily have to look at it...\r\n\r\n#b#L0#Show me how I'd look like as the leader of the Knights.#l ..#b#L1#No, I'm okay."); + } else if (status == 3) { + qm.sendYesNo("Would you like to see for it yourself right now? A short clip will come out soon. Be prepared for what you are about to witness."); + } else if (status == 4) { + qm.forceStartQuest(); + qm.forceCompleteQuest(); + qm.warp(913040100, 0); + qm.dispose(); + } + } +} diff --git a/scripts/quest/20100.js b/scripts/quest/20100.js index 883ac2875b..697eba843d 100644 --- a/scripts/quest/20100.js +++ b/scripts/quest/20100.js @@ -39,9 +39,11 @@ function start(mode, type, selection) { if (status == 0) qm.sendAcceptDecline("Ahhh, you're back. I can see that you're at level 10 now. It looks like you're flashing a glimmer of hope towards becoming a Knight. The basic training has now ended, and it's time for you to make the decision."); else if (status == 1) { - qm.sendOk("Now look to the left. The leaders of the Knights will be waiting for you. There will be 5 paths for you to choose from. All you need to do is choose one of them. All 5 of them will lead you to the path of a Knight, so... I suggest you pay attention to what each path offers, and select the one you'd most like to take."); qm.forceStartQuest(); qm.forceCompleteQuest(); + + qm.sendOk("Now look to the left. The leaders of the Knights will be waiting for you. There will be 5 paths for you to choose from. All you need to do is choose one of them. All 5 of them will lead you to the path of a Knight, so... I suggest you pay attention to what each path offers, and select the one you'd most like to take."); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20101.js b/scripts/quest/20101.js index 9af9d25a2f..a53b48f168 100644 --- a/scripts/quest/20101.js +++ b/scripts/quest/20101.js @@ -6,6 +6,7 @@ importPackage(Packages.client); var status = -1; var jobType = 1; +var canTryFirstJob = true; function end(mode, type, selection) { if (mode == 0) { @@ -21,34 +22,38 @@ function end(mode, type, selection) { if (status == 0) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Dawn Warrior?"); } else if (status == 1) { - if(!qm.canGetFirstJob(jobType)) { - qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rDawn Warrior#k."); - qm.dispose(); - return; + if (canTryFirstJob) { + canTryFirstJob = false; + if (qm.getPlayer().getJob().getId() != 1100) { + if(!qm.canGetFirstJob(jobType)) { + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rDawn Warrior#k."); + qm.dispose(); + return; + } + + if (!(qm.canHoldAll([1302077, 1142066]))) { + qm.sendOk("Make some room in your inventory and talk back to me."); + qm.dispose(); + return; + } + + qm.gainItem(1302077, 1); + qm.gainItem(1142066, 1); + qm.changeJob(MapleJob.DAWNWARRIOR1); + qm.getPlayer().resetStats(); + } + qm.forceCompleteQuest(); } - - if (!(qm.canHoldAll([1302077, 1142066]))) { - qm.sendOk("Make some room in your inventory and talk back to me."); - qm.dispose(); - return; - } - - qm.sendNext("I have just molded your body to make it perfect for a Dawn Warrior. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); - if (qm.getPlayer().getJob().getId() != 1100) { - qm.gainItem(1302077, 1); - qm.gainItem(1142066, 1); - qm.changeJob(MapleJob.DAWNWARRIOR1); - qm.getPlayer().resetStats(); - } - qm.forceCompleteQuest(); + qm.sendNext("I have just molded your body to make it perfect for a Dawn Warrior. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); } else if (status == 2) { qm.sendNextPrev("I have also expanded your inventory slot counts for your equipment and etc. inventory. Use those slots wisely and fill them up with items required for Knights to carry."); } else if (status == 3) { qm.sendNextPrev("I have also given you a hint of #bSP#k, so open the #bSkill Menu#k to acquire new skills. Of course, you can't raise them at all once, and there are some skills out there where you won't be able to acquire them unless you master the basic skills first."); } else if (status == 4) { - qm.sendNextPrev("Unlike your time as a Nobless, once you become the Dawn Warrior, you will lost a portion of your EXP when you run out of HP, okay?"); + qm.sendNextPrev("Unlike your time as a Noblesse, once you become the Dawn Warrior, you will lost a portion of your EXP when you run out of HP, okay?"); } else if (status == 5) { qm.sendNextPrev("Now... I want you to go out there and show the world how the Knights of Cygnus operate."); - qm.dispose(); + } else if (status == 6) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20102.js b/scripts/quest/20102.js index cbdf5aee82..fec8015c50 100644 --- a/scripts/quest/20102.js +++ b/scripts/quest/20102.js @@ -6,6 +6,7 @@ importPackage(Packages.client); var status = -1; var jobType = 2; +var canTryFirstJob = true; function end(mode, type, selection) { if (mode == 0) { @@ -21,34 +22,38 @@ function end(mode, type, selection) { if (status == 0) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Blaze Wizard?"); } else if (status == 1) { - if(!qm.canGetFirstJob(jobType)) { - qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBlaze Wizard#k."); - qm.dispose(); - return; + if (canTryFirstJob) { + canTryFirstJob = false; + if (qm.getPlayer().getJob().getId() != 1200) { + if(!qm.canGetFirstJob(jobType)) { + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBlaze Wizard#k."); + qm.dispose(); + return; + } + + if (!(qm.canHoldAll([1372043, 1142066]))) { + qm.sendOk("Make some room in your inventory and talk back to me."); + qm.dispose(); + return; + } + + qm.gainItem(1372043, 1); + qm.gainItem(1142066, 1); + qm.changeJob(MapleJob.BLAZEWIZARD1); + qm.getPlayer().resetStats(); + } + qm.forceCompleteQuest(); } - - if (!(qm.canHoldAll([1372043, 1142066]))) { - qm.sendOk("Make some room in your inventory and talk back to me."); - qm.dispose(); - return; - } - - qm.sendNext("I have just molded your body to make it perfect for a Blaze Wizard. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); - if (qm.getPlayer().getJob().getId() != 1200) { - qm.gainItem(1372043, 1); - qm.gainItem(1142066, 1); - qm.changeJob(MapleJob.BLAZEWIZARD1); - qm.getPlayer().resetStats(); - } - qm.forceCompleteQuest(); + qm.sendNext("I have just molded your body to make it perfect for a Blaze Wizard. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); } else if (status == 2) { qm.sendNextPrev("I have also expanded your inventory slot counts for your equipment and etc. inventory. Use those slots wisely and fill them up with items required for Knights to carry."); } else if (status == 3) { qm.sendNextPrev("I have also given you a hint of #bSP#k, so open the #bSkill Menu#k to acquire new skills. Of course, you can't raise them at all once, and there are some skills out there where you won't be able to acquire them unless you master the basic skills first."); } else if (status == 4) { - qm.sendNextPrev("Unlike your time as a Nobless, once you become the Blaze Wizard, you will lost a portion of your EXP when you run out of HP, okay?"); + qm.sendNextPrev("Unlike your time as a Noblesse, once you become the Blaze Wizard, you will lost a portion of your EXP when you run out of HP, okay?"); } else if (status == 5) { qm.sendNextPrev("Now... I want you to go out there and show the world how the Knights of Cygnus operate."); - qm.dispose(); + } else if (status == 6) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20103.js b/scripts/quest/20103.js index 9d603bf429..8aadf16618 100644 --- a/scripts/quest/20103.js +++ b/scripts/quest/20103.js @@ -6,6 +6,7 @@ importPackage(Packages.client); var status = -1; var jobType = 3; +var canTryFirstJob = true; function end(mode, type, selection) { if (mode == 0) { @@ -21,35 +22,39 @@ function end(mode, type, selection) { if (status == 0) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Wind Archer?"); } else if (status == 1) { - if(!qm.canGetFirstJob(jobType)) { - qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWind Archer#k."); - qm.dispose(); - return; + if (canTryFirstJob) { + canTryFirstJob = false; + if (qm.getPlayer().getJob().getId() != 1300) { + if(!qm.canGetFirstJob(jobType)) { + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWind Archer#k."); + qm.dispose(); + return; + } + + if (!(qm.canHoldAll([1452051, 1142066]) && qm.canHold(2070000))) { + qm.sendOk("Make some room in your inventory and talk back to me."); + qm.dispose(); + return; + } + + qm.gainItem(2060000, 2000); + qm.gainItem(1452051, 1); + qm.gainItem(1142066, 1); + qm.changeJob(MapleJob.WINDARCHER1); + qm.getPlayer().resetStats(); + } + qm.forceCompleteQuest(); } - - if (!(qm.canHoldAll([1452051, 1142066]) && qm.canHold(2070000))) { - qm.sendOk("Make some room in your inventory and talk back to me."); - qm.dispose(); - return; - } - - qm.sendNext("I have just molded your body to make it perfect for a Wind Archer. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); - if (qm.getPlayer().getJob().getId() != 1300) { - qm.gainItem(2060000, 2000); - qm.gainItem(1452051, 1); - qm.gainItem(1142066, 1); - qm.changeJob(MapleJob.WINDARCHER1); - qm.getPlayer().resetStats(); - } - qm.forceCompleteQuest(); + qm.sendNext("I have just molded your body to make it perfect for a Wind Archer. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); } else if (status == 2) { qm.sendNextPrev("I have also expanded your inventory slot counts for your equipment and etc. inventory. Use those slots wisely and fill them up with items required for Knights to carry."); } else if (status == 3) { qm.sendNextPrev("I have also given you a hint of #bSP#k, so open the #bSkill Menu#k to acquire new skills. Of course, you can't raise them at all once, and there are some skills out there where you won't be able to acquire them unless you master the basic skills first."); } else if (status == 4) { - qm.sendNextPrev("Unlike your time as a Nobless, once you become the Wind Archer, you will lost a portion of your EXP when you run out of HP, okay?"); + qm.sendNextPrev("Unlike your time as a Noblesse, once you become the Wind Archer, you will lost a portion of your EXP when you run out of HP, okay?"); } else if (status == 5) { qm.sendNextPrev("Now... I want you to go out there and show the world how the Knights of Cygnus operate."); - qm.dispose(); + } else if (status == 6) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20104.js b/scripts/quest/20104.js index 232a97ad5d..2f4ee320c7 100644 --- a/scripts/quest/20104.js +++ b/scripts/quest/20104.js @@ -6,6 +6,7 @@ importPackage(Packages.client); var status = -1; var jobType = 4; +var canTryFirstJob = true; function end(mode, type, selection) { if (mode == 0) { @@ -21,35 +22,39 @@ function end(mode, type, selection) { if (status == 0) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Night Walker?"); } else if (status == 1) { - if(!qm.canGetFirstJob(jobType)) { - qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rNight Walker#k."); - qm.dispose(); - return; + if (canTryFirstJob) { + canTryFirstJob = false; + if (qm.getPlayer().getJob().getId() != 1400) { + if(!qm.canGetFirstJob(jobType)) { + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rNight Walker#k."); + qm.dispose(); + return; + } + + if (!(qm.canHoldAll([1472061, 1142066]) && qm.canHold(2070000))) { + qm.sendOk("Make some room in your inventory and talk back to me."); + qm.dispose(); + return; + } + + qm.gainItem(1472061, 1); + qm.gainItem(2070000, 800); + qm.gainItem(1142066, 1); + qm.changeJob(MapleJob.NIGHTWALKER1); + qm.getPlayer().resetStats(); + } + qm.forceCompleteQuest(); } - - if (!(qm.canHoldAll([1472061, 1142066]) && qm.canHold(2070000))) { - qm.sendOk("Make some room in your inventory and talk back to me."); - qm.dispose(); - return; - } - - qm.sendNext("I have just molded your body to make it perfect for a Night Walker. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); - if (qm.getPlayer().getJob().getId() != 1400) { - qm.gainItem(1472061, 1); - qm.gainItem(2070000, 800); - qm.gainItem(1142066, 1); - qm.changeJob(MapleJob.NIGHTWALKER1); - qm.getPlayer().resetStats(); - } - qm.forceCompleteQuest(); + qm.sendNext("I have just molded your body to make it perfect for a Night Walker. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); } else if (status == 2) { qm.sendNextPrev("I have also expanded your inventory slot counts for your equipment and etc. inventory. Use those slots wisely and fill them up with items required for Knights to carry."); } else if (status == 3) { qm.sendNextPrev("I have also given you a hint of #bSP#k, so open the #bSkill Menu#k to acquire new skills. Of course, you can't raise them at all once, and there are some skills out there where you won't be able to acquire them unless you master the basic skills first."); } else if (status == 4) { - qm.sendNextPrev("Unlike your time as a Nobless, once you become the Night Walker, you will lost a portion of your EXP when you run out of HP, okay?"); + qm.sendNextPrev("Unlike your time as a Noblesse, once you become the Night Walker, you will lost a portion of your EXP when you run out of HP, okay?"); } else if (status == 5) { qm.sendNextPrev("Now... I want you to go out there and show the world how the Knights of Cygnus operate."); - qm.dispose(); + } else if (status == 6) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20105.js b/scripts/quest/20105.js index 6580c083c4..03e933f019 100644 --- a/scripts/quest/20105.js +++ b/scripts/quest/20105.js @@ -6,6 +6,7 @@ importPackage(Packages.client); var status = -1; var jobType = 5; +var canTryFirstJob = true; function end(mode, type, selection) { if (mode == 0) { @@ -21,34 +22,38 @@ function end(mode, type, selection) { if (status == 0) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Thunder Breaker?"); } else if (status == 1) { - if(!qm.canGetFirstJob(jobType)) { - qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThunder Breaker#k."); - qm.dispose(); - return; + if (canTryFirstJob) { + canTryFirstJob = false; + if (qm.getPlayer().getJob().getId() != 1500) { + if(!qm.canGetFirstJob(jobType)) { + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThunder Breaker#k."); + qm.dispose(); + return; + } + + if (!(qm.canHoldAll([1482014, 1142066]))) { + qm.sendOk("Make some room in your inventory and talk back to me."); + qm.dispose(); + return; + } + + qm.gainItem(1482014, 1); + qm.gainItem(1142066, 1); + qm.getPlayer().changeJob(MapleJob.THUNDERBREAKER1); + qm.getPlayer().resetStats(); + } + qm.forceCompleteQuest(); } - - if (!(qm.canHoldAll([1482014, 1142066]))) { - qm.sendOk("Make some room in your inventory and talk back to me."); - qm.dispose(); - return; - } - - qm.sendNext("I have just molded your body to make it perfect for a Thunder Breaker. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); - if (qm.getPlayer().getJob().getId() != 1500) { - qm.gainItem(1482014, 1); - qm.gainItem(1142066, 1); - qm.getPlayer().changeJob(MapleJob.THUNDERBREAKER1); - qm.getPlayer().resetStats(); - } - qm.forceCompleteQuest(); + qm.sendNext("I have just molded your body to make it perfect for a Thunder Breaker. If you wish to become more powerful, use Stat Window (S) to raise the appropriate stats. If you aren't sure what to raise, just click on #bAuto#k."); } else if (status == 2) { qm.sendNextPrev("I have also expanded your inventory slot counts for your equipment and etc. inventory. Use those slots wisely and fill them up with items required for Knights to carry."); } else if (status == 3) { qm.sendNextPrev("I have also given you a hint of #bSP#k, so open the #bSkill Menu#k to acquire new skills. Of course, you can't raise them at all once, and there are some skills out there where you won't be able to acquire them unless you master the basic skills first."); } else if (status == 4) { - qm.sendNextPrev("Unlike your time as a Nobless, once you become the Thunder Breaker, you will lost a portion of your EXP when you run out of HP, okay?"); + qm.sendNextPrev("Unlike your time as a Noblesse, once you become the Thunder Breaker, you will lost a portion of your EXP when you run out of HP, okay?"); } else if (status == 5) { qm.sendNextPrev("Now... I want you to go out there and show the world how the Knights of Cygnus operate."); - qm.dispose(); + } else if (status == 6) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/20200.js b/scripts/quest/20200.js index a8e6afb236..a1a0752699 100644 --- a/scripts/quest/20200.js +++ b/scripts/quest/20200.js @@ -44,7 +44,9 @@ function start(mode, type, selection) { } else if (status == 1) { qm.startQuest(); qm.completeQuest(); + qm.sendOk("If you wish to take the Knighthood Exam, please come to Ereve. Each Chief Knight will test your abilities, and if you meet their standards, then you will officially become a Knight."); + } else if (status == 2) { qm.dispose(); } } diff --git a/scripts/quest/20311.js b/scripts/quest/20311.js index 31be876455..4075738092 100644 --- a/scripts/quest/20311.js +++ b/scripts/quest/20311.js @@ -48,19 +48,18 @@ function start(mode, type, selection) { nPSP = (qm.getPlayer().getLevel() - 70) * 3; if (qm.getPlayer().getRemainingSp() > nPSP) { qm.sendNext("You still have way too much #bSP#k with you. You can't earn a new title like that, I strongly urge you to use more SP on your 1st and 2nd level skills."); - qm.dispose(); } else { if (!qm.canHold(1142068)) { qm.sendNext("If you wish to receive the medal befitting the title, you may want to make some room in your equipment inventory."); - qm.dispose(); } else { + qm.completeQuest(); qm.gainItem(1142068, 1); qm.getPlayer().changeJob(Packages.client.MapleJob.DAWNWARRIOR3); qm.sendOk("#h #, as of this moment, you are an Advanced Knight. From this moment on, you shall carry yourself with dignity and respect befitting your new title, an Advanced Knight of Cygnus Knights. May your glory continue to shine as bright as this moment."); - qm.completeQuest(); - qm.dispose(); } } + } else if (status == 3) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/20312.js b/scripts/quest/20312.js index 26458ea860..b562dea87e 100644 --- a/scripts/quest/20312.js +++ b/scripts/quest/20312.js @@ -48,20 +48,18 @@ function start(mode, type, selection) { nPSP = (qm.getPlayer().getLevel() - 70) * 3; if (qm.getPlayer().getRemainingSp() > nPSP) { qm.sendNext("You still have way too much #bSP#k with you. You can't earn a new title like that, I strongly urge you to use more SP on your 1st and 2nd level skills."); - qm.dispose(); } else { if (!qm.canHold(1142068)) { qm.sendNext("If you wish to receive the medal befitting the title, you may want to make some room in your equipment inventory."); - qm.dispose(); } else { + qm.completeQuest(); qm.gainItem(1142068, 1); qm.getPlayer().changeJob(Packages.client.MapleJob.BLAZEWIZARD3); qm.sendOk("#h #, as of this moment, you are an Advanced Knight. From this moment on, you will train yourself with fervor as you will lead your group of Knights for the safety of this world. That fervor will provide you with plenty of courage."); - qm.completeQuest(); - qm.dispose(); } } - + } else if (status == 3) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/20313.js b/scripts/quest/20313.js index 73cb223242..58e7a89064 100644 --- a/scripts/quest/20313.js +++ b/scripts/quest/20313.js @@ -48,20 +48,18 @@ function start(mode, type, selection) { nPSP = (qm.getPlayer().getLevel() - 70) * 3; if (qm.getPlayer().getRemainingSp() > nPSP) { qm.sendNext("You still have way too much #bSP#k with you. You can't earn a new title like that, I strongly urge you to use more SP on your 1st and 2nd level skills."); - qm.dispose(); } else { if (!qm.canHold(1142068)) { qm.sendNext("If you wish to receive the medal befitting the title, you may want to make some room in your equipment inventory."); - qm.dispose(); } else { + qm.completeQuest(); qm.gainItem(1142068, 1); qm.getPlayer().changeJob(Packages.client.MapleJob.WINDARCHER3); qm.sendOk("#h #, as of this moment, you are an Advanced Knight. From this moment on, you will be carrying a while lot of responsibility befitting your new title as an Advanced Knight of Cygnus Knights. You may view the world in a carefree mode, but please remember what your mission is."); - qm.completeQuest(); - qm.dispose(); } } - + } else if (status == 3) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/20314.js b/scripts/quest/20314.js index 17bb317317..be6c955c90 100644 --- a/scripts/quest/20314.js +++ b/scripts/quest/20314.js @@ -48,20 +48,18 @@ function start(mode, type, selection) { nPSP = (qm.getPlayer().getLevel() - 70) * 3; if (qm.getPlayer().getRemainingSp() > nPSP) { qm.sendNext("You still have way too much #bSP#k with you. You can't earn a new title like that, I strongly urge you to use more SP on your 1st and 2nd level skills."); - qm.dispose(); } else { if (!qm.canHold(1142068)) { qm.sendNext("If you wish to receive the medal befitting the title, you may want to make some room in your equipment inventory."); - qm.dispose(); } else { qm.gainItem(1142068, 1); qm.getPlayer().changeJob(Packages.client.MapleJob.NIGHTWALKER3); - qm.sendOk("#h #, from here on out, you are an Advanced Knight of Cygnus Knights. The title comes with a newfound broad view on everything. You may encounter temptations here and there, but I want you to keep your faith and beliefs intact and do not succumb to the darkness."); qm.completeQuest(); - qm.dispose(); + qm.sendOk("#h #, from here on out, you are an Advanced Knight of Cygnus Knights. The title comes with a newfound broad view on everything. You may encounter temptations here and there, but I want you to keep your faith and beliefs intact and do not succumb to the darkness."); } } - + } else if (status == 3) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/20315.js b/scripts/quest/20315.js index 05b43e4c10..073fc6da8b 100644 --- a/scripts/quest/20315.js +++ b/scripts/quest/20315.js @@ -48,20 +48,18 @@ function start(mode, type, selection) { nPSP = (qm.getPlayer().getLevel() - 70) * 3; if (qm.getPlayer().getRemainingSp() > nPSP) { qm.sendNext("You still have way too much #bSP#k with you. You can't earn a new title like that, I strongly urge you to use more SP on your 1st and 2nd level skills."); - qm.dispose(); } else { if (!qm.canHold(1142068)) { qm.sendNext("If you wish to receive the medal befitting the title, you may want to make some room in your equipment inventory."); - qm.dispose(); } else { qm.gainItem(1142068, 1); qm.getPlayer().changeJob(Packages.client.MapleJob.THUNDERBREAKER3); - qm.sendOk("#h #, from here on out, you will become an Advanced Knight of the Knights of Cygnus! As your standing rises, so does the difficulty of the tasks you will be receiving. But challenge is good, right? You have to enjoy life. Enjoy what's given to you!"); qm.completeQuest(); - qm.dispose(); + qm.sendOk("#h #, from here on out, you will become an Advanced Knight of the Knights of Cygnus! As your standing rises, so does the difficulty of the tasks you will be receiving. But challenge is good, right? You have to enjoy life. Enjoy what's given to you!"); } } - + } else if (status == 3) { + qm.dispose(); } } } \ No newline at end of file diff --git a/scripts/quest/2034.js b/scripts/quest/2034.js index d123cc9aa0..37105e5b12 100644 --- a/scripts/quest/2034.js +++ b/scripts/quest/2034.js @@ -1,7 +1,6 @@ importPackage(Packages.client); var item; -var stance; var status = -1; var item; @@ -23,8 +22,7 @@ function end(mode, type, selection) { return; } - stance = qm.getPlayer().getJobStyle(); - + var stance = qm.getPlayer().getJobStyle(); if(stance == Packages.client.MapleJob.WARRIOR) item = 1072003; else if(stance == Packages.client.MapleJob.MAGICIAN) item = 1072077; else if(stance == Packages.client.MapleJob.BOWMAN || stance == Packages.client.MapleJob.CROSSBOWMAN) item = 1072081; @@ -34,11 +32,13 @@ function end(mode, type, selection) { qm.gainItem(item, 1); qm.gainItem(4000007, -150); - qm.gainExp(2200); qm.completeQuest(); qm.sendOk("Alright, if you need work sometime down the road, feel free to come back and see me. This town sure can use a person like you for help~"); + } + + else if (status == 2) { qm.dispose(); - } + } } \ No newline at end of file diff --git a/scripts/quest/20514.js b/scripts/quest/20514.js new file mode 100644 index 0000000000..045fadc608 --- /dev/null +++ b/scripts/quest/20514.js @@ -0,0 +1,52 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +// @Author Ronan + +importPackage(Packages.constants.game); +importPackage(Packages.server); + +function raiseOpen() { + var chr = qm.getPlayer(); + var questStatus = chr.getQuestStatus(qm.getQuest()); + + if (questStatus == 0) { + qm.setQuestProgress(20515, 0, chr.getLevel()); + qm.setQuestProgress(20515, 1, chr.getExp()); + } else if (questStatus == 1) { // update mimiana progress... + var diffExp = chr.getExp() - qm.getQuestProgressInt(20515, 1); + + var initLevel = qm.getQuestProgressInt(20515, 0); + for (var i = initLevel; i < chr.getLevel(); i++) { + diffExp += ExpTable.getExpNeededForLevel(i); + } + + if (diffExp > 0) { // thanks IxianMace for noticing Mimiana egg not following progress by EXP + var consItem = MapleItemInformationProvider.getInstance().getQuestConsumablesInfo(4220137); + var exp = consItem.exp; + var grade = consItem.grade; + + qm.setQuestProgress(20514, 0, Math.min(diffExp, exp * grade)); + } + } + + qm.dispose(); +} + diff --git a/scripts/quest/20520.js b/scripts/quest/20520.js index e507450baf..6f1c5e193d 100644 --- a/scripts/quest/20520.js +++ b/scripts/quest/20520.js @@ -44,6 +44,7 @@ function start(mode, type, selection) { qm.forceStartQuest(); qm.forceCompleteQuest(); qm.sendOk("There's a special mount that only the Cygnus Knights can enjoy. If you are interested, visit #bEreve#k. I will give you more information on it."); - qm.dispose(); - } + } else if (status == 3) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/20522.js b/scripts/quest/20522.js index e4e52c9894..862c2bf98f 100644 --- a/scripts/quest/20522.js +++ b/scripts/quest/20522.js @@ -54,6 +54,7 @@ function start(mode, type, selection) { qm.forceStartQuest(); if(!qm.haveItem(4220137)) qm.gainItem(4220137); qm.sendOk("Mimiana's egg can be raised by #bsharing your daily experiences with it#k. Once Mimiana fully grows up, please come see me."); + } else if (status == 4) { qm.dispose(); } } @@ -70,17 +71,15 @@ function end(mode, type, selection) { } else if (status == 1) { //pretty sure there would need to have an egg EXP condition... Whatever. if(!qm.haveItem(4220137)) { qm.sendOk("I see, you lost your egg... You need to be more careful when raising a baby Mimiana!"); - qm.dispose(); return; } - qm.sendOk("Oh, were you able to awaken Mimiana Egg? That's amazing... Most knights can't even dream of awakening it in such a short amount of time."); - - qm.forceCompleteQuest(); + qm.forceCompleteQuest(); qm.gainItem(4220137, -1); qm.gainExp(37600); - - qm.dispose(); + qm.sendOk("Oh, were you able to awaken Mimiana Egg? That's amazing... Most knights can't even dream of awakening it in such a short amount of time."); + } else if (status == 2) { + qm.dispose(); } } diff --git a/scripts/quest/20526.js b/scripts/quest/20526.js index 2b06a930dc..440581b89a 100644 --- a/scripts/quest/20526.js +++ b/scripts/quest/20526.js @@ -55,6 +55,7 @@ function start(mode, type, selection) { if(!qm.haveItem(4220137)) qm.gainItem(4220137); qm.sendOk("Mimiana's egg can be raised by #bsharing your daily experiences with it#k. Once Mimiana fully grows up, please come see me. One more thing, I talked with #p2060005# beforehand and retrieved the #b#t4032117##k for you. The price to charge remains the same: #r10,000,000 mesos#k."); + } else if (status == 4) { qm.dispose(); } } @@ -80,14 +81,13 @@ function end(mode, type, selection) { return; } - qm.sendOk("Okay, you now may mount Mimiana again. Take good care of it this time."); - + qm.forceCompleteQuest(); qm.gainItem(1902005, 1); qm.gainItem(4220137, -1); qm.gainMeso(-10000000); - - qm.forceCompleteQuest(); - qm.dispose(); + qm.sendOk("Okay, you now may mount Mimiana again. Take good care of it this time."); + } else if (status == 2) { + qm.dispose(); } } diff --git a/scripts/quest/20527.js b/scripts/quest/20527.js index ef0a578caf..07cb063c4a 100644 --- a/scripts/quest/20527.js +++ b/scripts/quest/20527.js @@ -38,12 +38,12 @@ function start(mode, type, selection) { var mount = qm.getPlayer().getMount(); if(mount != null && mount.getLevel() >= 3) { - qm.sendNext("Alright, I'll get you started in how to train Mimio, the next step for Mimianas. When you're ready, talk to me again."); qm.forceCompleteQuest(); + qm.sendNext("Alright, I'll get you started in how to train Mimio, the next step for Mimianas. When you're ready, talk to me again."); } else { qm.sendNext("It looks like your Mimiana haven't reached #rlevel 3#k yet. Please train it a bit more before trying to advance it."); } - + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/20600.js b/scripts/quest/20600.js index 76a86566ed..cee458fe5e 100644 --- a/scripts/quest/20600.js +++ b/scripts/quest/20600.js @@ -9,7 +9,7 @@ function start(mode, type, selection) { if (status == 0) { qm.sendAcceptDecline("#h0#. Have you been slacking off on training since reaching Level 100? We all know how powerful you are, but the training is not complete. Take a look at these Knight Commanders. They train day and night, preparing themselves for the possible encounter with the Black Mage."); - } else { + } else if (status == 1) { if (mode == 1) { qm.forceStartQuest(); } diff --git a/scripts/quest/20610.js b/scripts/quest/20610.js index 3da0a28c20..3f15cbab65 100644 --- a/scripts/quest/20610.js +++ b/scripts/quest/20610.js @@ -14,8 +14,10 @@ function start(mode, type, selection) { qm.sendOk("Well, what you're doing right now doesn't make you look like someone that's humble. You just look complacent by doing that, and that's never a good thing."); } else { qm.forceStartQuest(); + qm.dispose(); } - qm.dispose(); + } else if (status == 2) { + qm.dispose(); } } diff --git a/scripts/quest/21011.js b/scripts/quest/21011.js index 4275db3e1e..736aea9078 100644 --- a/scripts/quest/21011.js +++ b/scripts/quest/21011.js @@ -48,8 +48,9 @@ function start(mode, type, selection) { } else if (status == 4) { qm.forceStartQuest(); qm.sendOk("My brother #bPuir #kis just down the street, and he's been dying to meet you! I know you're busy, but could you please stop by and say hello to Puir? Please..."); + } else if (status == 5) { qm.dispose(); - } + } } function end(mode, type, selection) { @@ -83,7 +84,7 @@ function end(mode, type, selection) { qm.sendNext("#b(Your skills are nowhere close to being hero-like... But a sword? Have you ever even held a sword in your lifetime? You can't remember... How do you even equip it?)", 3); }else qm.dropMessage(1,"Your inventory is full"); - } else if (status == 6) { + } else if (status == 6) { qm.guideHint(16); qm.dispose(); } diff --git a/scripts/quest/21012.js b/scripts/quest/21012.js index 644dab447f..6b1e391063 100644 --- a/scripts/quest/21012.js +++ b/scripts/quest/21012.js @@ -71,14 +71,16 @@ function end(mode, type, selection) { if(qm.isQuestCompleted(21012)) qm.dropMessage(1,"Unknown Error"); else if(qm.canHold(2000022) && qm.canHold(2000023)){ + qm.forceCompleteQuest(); qm.gainExp(57); qm.gainItem(2000022, 10); qm.gainItem(2000023, 10); - qm.forceCompleteQuest(); qm.sendOk("#b(Even if you're really the hero everyone says you are... What good are you without any skills?)", 3); - qm.dispose(); - }else + } else { qm.dropMessage(1,"Your inventory is full"); - qm.dispose(); + qm.dispose(); + } + } else if (status == 2) { + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/21013.js b/scripts/quest/21013.js index 700a6d6afc..ae758bccc5 100644 --- a/scripts/quest/21013.js +++ b/scripts/quest/21013.js @@ -73,7 +73,7 @@ function end(mode, type, selection) { qm.gainItem(4032309, -1); qm.gainItem(4032310, -1); qm.gainItem(3010062, 1); - qm.sendNextPrev("Here, a fully-assembled chair, just for you! I've always wanted to give you a chair as a gift, because I know a hero can occasionally use some good rest. Tee hee.", 9); + qm.sendNext("Here, a fully-assembled chair, just for you! I've always wanted to give you a chair as a gift, because I know a hero can occasionally use some good rest. Tee hee.", 9); } else if (status == 2) { qm.sendNext("A hero is not invincible. A hero is human. I'm sure you will face challenges and even falter at times. But you are a hero because you have what it takes to overcome any obstacles you may encounter.", 9); } else if (status == 3) { diff --git a/scripts/quest/21100.js b/scripts/quest/21100.js index 483bc4f160..475a81af99 100644 --- a/scripts/quest/21100.js +++ b/scripts/quest/21100.js @@ -31,6 +31,7 @@ function start(mode, type, selection) { qm.sendOk("If the #p1201001# reacts to you, then we'll know that you're #bAran#k, the hero that wielded a #p1201001#.", 8); qm.showIntro("Effect/Direction1.img/aranTutorial/ClickPoleArm"); } - qm.dispose(); - } + } else if (status == 8) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/21101.js b/scripts/quest/21101.js index e59c0bb9a6..05636b3cbc 100644 --- a/scripts/quest/21101.js +++ b/scripts/quest/21101.js @@ -20,7 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); -importPackage(Packages.constants); +importPackage(Packages.config); status = -1; @@ -48,7 +48,7 @@ function start(mode, type, selection) { qm.changeJobById(2100); qm.resetStats(); - if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + if (YamlConfig.config.server.USE_FULL_ARAN_SKILLSET) { qm.teachSkill(21000000, 0, 10, -1); //combo ability qm.teachSkill(21001003, 0, 20, -1); //polearm booster } diff --git a/scripts/quest/21200.js b/scripts/quest/21200.js index c034377a62..c7e7bb101d 100644 --- a/scripts/quest/21200.js +++ b/scripts/quest/21200.js @@ -35,6 +35,7 @@ function start(mode, type, selection) { qm.sendAcceptDecline("How is the training going? Wow, you've reached such a high level! That's amazing. I knew you would do just fine on Victoria Island... Oh, look at me. I'm wasting your time. I know you're busy, but you'll have to return to the island for a bit."); else if(status == 1){ qm.sendOk("Your #b#p1201001##k in #b#m140000000##k is acting strange all of a sudden. According to the records, the Polearm acts this way when it is calling for its master. #bPerhaps it's calling for you#k. Please return to the island and check things out."); + } else if(status == 2){ qm.startQuest(); qm.dispose(); } diff --git a/scripts/quest/21201.js b/scripts/quest/21201.js index 4d8f0f8d06..f03e71968e 100644 --- a/scripts/quest/21201.js +++ b/scripts/quest/21201.js @@ -20,7 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); -importPackage(Packages.constants); +importPackage(Packages.config); var status = -1; @@ -60,14 +60,13 @@ function end(mode, type, selection) { if(!qm.isQuestCompleted(21201)) { if(!qm.canHold(1142130)) { qm.sendOk("Wow, your #bequip#k inventory is full. I need you to make at least 1 empty slot to complete this quest."); // thanks MedicOP for finding an issue here - qm.dispose(); return; } qm.gainItem(1142130, true); qm.changeJobById(2110); - if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + if (YamlConfig.config.server.USE_FULL_ARAN_SKILLSET) { qm.teachSkill(21100000, 0, 20, -1); //polearm mastery qm.teachSkill(21100002, 0, 30, -1); //final charge qm.teachSkill(21100004, 0, 20, -1); //combo smash @@ -78,6 +77,7 @@ function end(mode, type, selection) { } qm.sendNext("Your level isn't what it used to be back in your glory days, so I can't restore all of your old abilities. But the few I can restore should help you level up faster. Now hurry up and train so you can return to the old you."); + } else if (status == 9) { qm.dispose(); } } diff --git a/scripts/quest/21202.js b/scripts/quest/21202.js index bddef9d732..4d0591fb01 100644 --- a/scripts/quest/21202.js +++ b/scripts/quest/21202.js @@ -42,7 +42,7 @@ function start(mode, type, selection) { qm.sendAcceptDecline("I'm too old to make weapons now, but.. I do have a Polearm that I made way back when. It's still in excellent shape. But I can't give it to you because that Polearm is extremely sharp, so sharp it could hurt its master. Do you still want it?"); } else if(status == 5) { qm.sendOk("Well, if you say so.. I can't object to that. I'll tell you what. I'll give you a quick test, and if you pass it, the Giant Polearm is yours. Head over to the #bTraining Center#k and take on the #rScarred Bears#k that are there. Your job is to bring back #b30 Sign of Acceptances#k."); - } else { + } else if(status == 6) { qm.startQuest(); qm.dispose(); } diff --git a/scripts/quest/2124.js b/scripts/quest/2124.js index 079a61dadb..a13e40968a 100644 --- a/scripts/quest/2124.js +++ b/scripts/quest/2124.js @@ -23,16 +23,33 @@ Author : Ronan Lana */ +var status = -1; + function end(mode, type, selection) { - - if(!qm.haveItem(4031619, 1)) { - qm.sendOk("Please bring me the box with the supplies that lies with #b#p2012019##k..."); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(!qm.haveItem(4031619, 1)) { + qm.sendOk("Please bring me the box with the supplies that lies with #b#p2012019##k..."); + } + else { + qm.gainItem(4031619, -1); + qm.sendOk("Oh, you brought #p2012019#'s box! Thank you."); + qm.forceCompleteQuest(); + } + } else if (status == 1) { + qm.dispose(); + } } - else { - qm.gainItem(4031619, -1); - qm.sendOk("Oh, you brought #p2012019#'s box! Thank you."); - qm.forceCompleteQuest(); - } - - qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2126.js b/scripts/quest/2126.js index 079a61dadb..a13e40968a 100644 --- a/scripts/quest/2126.js +++ b/scripts/quest/2126.js @@ -23,16 +23,33 @@ Author : Ronan Lana */ +var status = -1; + function end(mode, type, selection) { - - if(!qm.haveItem(4031619, 1)) { - qm.sendOk("Please bring me the box with the supplies that lies with #b#p2012019##k..."); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(!qm.haveItem(4031619, 1)) { + qm.sendOk("Please bring me the box with the supplies that lies with #b#p2012019##k..."); + } + else { + qm.gainItem(4031619, -1); + qm.sendOk("Oh, you brought #p2012019#'s box! Thank you."); + qm.forceCompleteQuest(); + } + } else if (status == 1) { + qm.dispose(); + } } - else { - qm.gainItem(4031619, -1); - qm.sendOk("Oh, you brought #p2012019#'s box! Thank you."); - qm.forceCompleteQuest(); - } - - qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2127.js b/scripts/quest/2127.js index 482e0fbdaf..ced26e74c3 100644 --- a/scripts/quest/2127.js +++ b/scripts/quest/2127.js @@ -23,9 +23,27 @@ Author : Ronan Lana */ +var status = -1; + function end(mode, type, selection) { - qm.sendOk("I see you're ready for the task. Now, pay heed to the details of your mission..."); - qm.forceCompleteQuest(); - - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendOk("I see you're ready for the task. Now, pay heed to the details of your mission..."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/21300.js b/scripts/quest/21300.js index 002ea062f9..e0c454d612 100644 --- a/scripts/quest/21300.js +++ b/scripts/quest/21300.js @@ -11,13 +11,14 @@ function start(mode, type, selection) { return; } - if (status == 0) { - qm.sendNext("How's the training going? Hmmm... Level 70... That's still not much, but you have really made some strides since the first time I met you fresh out of ice. Keep training, and I am sure one day you'll be able to regain your pre-battle form."); + if (status == 0) { + qm.sendNext("How's the training going? Hmmm... Level 70... That's still not much, but you have really made some strides since the first time I met you fresh out of ice. Keep training, and I am sure one day you'll be able to regain your pre-battle form."); } else if (status == 1) { - qm.sendAcceptDecline("But before doing that, I'll need you back in Rein for a bit. #bYour pole arm is reacting strange once again. It looks like it has something it wants to tell you. #kIt might be able to awaken your hidden powers, so please come immediately."); - } else if (status == 2) { - qm.sendOk("Anyway, I thought it was really something that a weapon has its own identity, but seriously... this weapon does not stop talking. It first kept on crying because I wasn't really paying attention to its needs, and... ahh, please keep this a secret from the pole arm. I don't think it's a good idea to upset the weapon any further."); - qm.forceStartQuest(); - qm.dispose(); - } + qm.sendAcceptDecline("But before doing that, I'll need you back in Rein for a bit. #bYour pole arm is reacting strange once again. It looks like it has something it wants to tell you. #kIt might be able to awaken your hidden powers, so please come immediately."); + } else if (status == 2) { + qm.forceStartQuest(); + qm.sendOk("Anyway, I thought it was really something that a weapon has its own identity, but seriously... this weapon does not stop talking. It first kept on crying because I wasn't really paying attention to its needs, and... ahh, please keep this a secret from the pole arm. I don't think it's a good idea to upset the weapon any further."); + } else if (status == 3) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/21301.js b/scripts/quest/21301.js index f09d16ba32..e9077098d6 100644 --- a/scripts/quest/21301.js +++ b/scripts/quest/21301.js @@ -24,8 +24,9 @@ function end(mode, type, selection) { } else if (status == 5) { qm.sendNextPrev("...No hope, no dreams... Nooooo!!"); } else if (status == 6) { - qm.sendNextPrev("#b(Maha is beginning to really get hysterical. I better leave right this minute. Maybe Lirin can do something about it.)", 2); qm.completeQuest(); - qm.dispose(); - } + qm.sendNextPrev("#b(Maha is beginning to really get hysterical. I better leave right this minute. Maybe Lilin can do something about it.)", 2); + } else if (status == 7) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/21302.js b/scripts/quest/21302.js index 35556f9587..4719a4a61f 100644 --- a/scripts/quest/21302.js +++ b/scripts/quest/21302.js @@ -20,7 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); -importPackage(Packages.constants); +importPackage(Packages.config); var status = -1; @@ -51,7 +51,7 @@ function end(mode, type, selection) { qm.gainItem(1142131, true); qm.changeJobById(2111); - if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + if (YamlConfig.config.server.USE_FULL_ARAN_SKILLSET) { qm.teachSkill(21110002, 0, 20, -1); //full swing } @@ -59,6 +59,7 @@ function end(mode, type, selection) { } qm.sendNext("Come on, keep training so you can get all your abilities back, and that way we can explore together once more!"); + } else if (status == 3) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/21400.js b/scripts/quest/21400.js index 363d87b00e..2caeb2de21 100644 --- a/scripts/quest/21400.js +++ b/scripts/quest/21400.js @@ -14,8 +14,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendAcceptDecline("How is the training going? I know you're busy, but please come to #bRien#k immediately. The #bMaha#k has started to act weird again... But its even weirder now. It's different from before. It's... darker than usual."); } else if (status == 1) { - qm.sendOk("I have a bad feeling about this. Please come back here. I've never seen or herd Maha like this, but I can sense the suffering its going through. #bOnly you, the master of Maha, can do something about it!"); - qm.startQuest(); - qm.dispose(); - } + qm.startQuest(); + qm.sendOk("I have a bad feeling about this. Please come back here. I've never seen or herd Maha like this, but I can sense the suffering its going through. #bOnly you, the master of Maha, can do something about it!"); + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/21401.js b/scripts/quest/21401.js index dea993fd17..600aaded9e 100644 --- a/scripts/quest/21401.js +++ b/scripts/quest/21401.js @@ -68,7 +68,9 @@ function end(mode, type, selection) { qm.completeQuest(); } qm.sendNext("Your skills have been restored. Those skills have been dormant for so long that you'll have to re-train yourself, but you'll be as good as new once you complete your training."); - } + } else if(status == 3) { + qm.dispose(); + } } function spawnMob(x, y, id, map) { diff --git a/scripts/quest/2148.js b/scripts/quest/2148.js index e333dafc56..58c7a3512e 100644 --- a/scripts/quest/2148.js +++ b/scripts/quest/2148.js @@ -1,9 +1,26 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("Some bats seems to accompany this tree wherever it goes. Creepy..."); - qm.forceCompleteQuest(); + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("Some bats seems to accompany this tree wherever it goes. Creepy..."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } function end(mode, type, selection) { qm.dispose(); diff --git a/scripts/quest/2149.js b/scripts/quest/2149.js index 9088a2d9cd..3e10046fd8 100644 --- a/scripts/quest/2149.js +++ b/scripts/quest/2149.js @@ -1,10 +1,28 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("It is said that a old tree gets alive whenever something sinister disturbs this land... We need a hero that fends our village of that creature!"); - qm.forceCompleteQuest(); + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("It is said that a old tree gets alive whenever something sinister disturbs this land... We need a hero that fends our village of that creature!"); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } + function end(mode, type, selection) { qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2150.js b/scripts/quest/2150.js index a7d30cf455..38cb1538d0 100644 --- a/scripts/quest/2150.js +++ b/scripts/quest/2150.js @@ -1,10 +1,28 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("The tree has a scarf upon its branches, I tell you."); - qm.forceCompleteQuest(); + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("The tree has a scarf upon its branches, I tell you."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } + function end(mode, type, selection) { qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2151.js b/scripts/quest/2151.js index 49d470f73e..56facb7ee7 100644 --- a/scripts/quest/2151.js +++ b/scripts/quest/2151.js @@ -1,10 +1,28 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("The tree has a strange carving that resembles a scary face."); - qm.forceCompleteQuest(); + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("The tree has a strange carving that resembles a scary face."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } + function end(mode, type, selection) { qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2152.js b/scripts/quest/2152.js index 845e647770..a8453cbc3c 100644 --- a/scripts/quest/2152.js +++ b/scripts/quest/2152.js @@ -1,10 +1,28 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("That tree... I've heard of it before, I even studied its behavior! If I recall correctly, the #bStumpy#k comes alive when the soil deems infertile by some sort of magic, and those stumps who evolves under these conditions starts to drain these suspicious magical sources instead of water and minerals for living, which makes them very threathening to people and villages nearby."); - qm.forceCompleteQuest(); + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("That tree... I've heard of it before, I even studied its behavior! If I recall correctly, the #bStumpy#k comes alive when the soil deems infertile by some sort of magic, and those stumps who evolves under these conditions starts to drain these suspicious magical sources instead of water and minerals for living, which makes them very threathening to people and villages nearby."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } + function end(mode, type, selection) { qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/21600.js b/scripts/quest/21600.js index 8c59614bd3..756d8157ad 100644 --- a/scripts/quest/21600.js +++ b/scripts/quest/21600.js @@ -39,9 +39,9 @@ function start(mode, type, selection) { } else if (status == 1) { qm.sendAcceptDecline("Picked your interest, huh? Very well, first you must make your way to #bAqua#k, there is a person there who makes #rfood for wolf cubs#k. Bring one portion to me, and I shall deem you able to tame and take care of one. What do you say, will you try for it?"); } else if (status == 2) { - qm.sendNext("Alright. The one you must meet is #bNanuke#k, she is on top of a #rsnowy whale#k, somewhere in the ocean. Good luck!"); qm.forceStartQuest(); - + qm.sendNext("Alright. The one you must meet is #bNanuke#k, she is on top of a #rsnowy whale#k, somewhere in the ocean. Good luck!"); + } else if (status == 3) { qm.dispose(); } } diff --git a/scripts/quest/21613.js b/scripts/quest/21613.js index 04b7e3de2e..d7648c72cf 100644 --- a/scripts/quest/21613.js +++ b/scripts/quest/21613.js @@ -44,7 +44,6 @@ function start(mode, type, selection) { var em = qm.getEventManager("Aran_3rdmount"); if (em == null) { qm.sendOk("Sorry, but the 3rd mount quest (Wolves) is closed."); - qm.dispose(); return; } else { @@ -54,9 +53,9 @@ function start(mode, type, selection) { } else { qm.forceStartQuest(); } - - qm.dispose(); } + } else if (status == 4) { + qm.dispose(); } } } diff --git a/scripts/quest/21618.js b/scripts/quest/21618.js index fb37fd1269..2d8119b21e 100644 --- a/scripts/quest/21618.js +++ b/scripts/quest/21618.js @@ -71,11 +71,12 @@ function end(mode, type, selection) { } qm.sendNext("Step aside, behold the mighty prowess of Maha!!"); - } else { + } else if (status == 1) { + qm.forceCompleteQuest(); + qm.gainItem(1902017, -1); qm.gainItem(1902018, 1); - qm.forceCompleteQuest(); qm.dispose(); } } diff --git a/scripts/quest/21700.js b/scripts/quest/21700.js index 4e4b8c233b..7157a1567e 100644 --- a/scripts/quest/21700.js +++ b/scripts/quest/21700.js @@ -33,8 +33,9 @@ function start(mode, type, selection) { qm.forceStartQuest(); } } else if (status == 6) { - qm.sendPrev("You'll find a Training Center if you exit to the #bleft#k. There, you'll meet #b#p1202006##k. I'm a bit worried because I think he may be struggling with bouts of Alzheimer's, but he spent a long time researching skills to help you. I'm sure you'll learn a thing or two from him."); - qm.dispose(); + qm.sendPrev("You'll find a Training Center if you exit to the #bleft#k. There, you'll meet #b#p1202006##k. I'm a bit worried because I think he may be struggling with bouts of Alzheimer's, but he spent a long time researching skills to help you. I'm sure you'll learn a thing or two from him."); + } else if (status == 7) { + qm.dispose(); } } diff --git a/scripts/quest/21703.js b/scripts/quest/21703.js index d6116bd8c2..380e6e59f7 100644 --- a/scripts/quest/21703.js +++ b/scripts/quest/21703.js @@ -35,6 +35,7 @@ function start(mode, type, selection) { } else if (status == 7) { qm.forceStartQuest(); qm.sendOk("Now go and take on those monstrous #o9300343#s!"); + } else if (status == 8) { qm.dispose(); } } @@ -70,6 +71,7 @@ function end(mode, type, selection) { qm.sendNext("(You remembered the #bCombo Ability#k skill! You were skeptical of the training at first, since the old man suffers from Alzheimer's and all, but boy, was it effective!)", 2); } else if (status == 4) { qm.sendPrev("Now report back to #p1201000#. I know she'll be ecstatic when she sees the progress you've made!"); + } else if (status == 5) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/21712.js b/scripts/quest/21712.js index 28f43a1f02..8d4eb2b5a0 100644 --- a/scripts/quest/21712.js +++ b/scripts/quest/21712.js @@ -25,7 +25,8 @@ function start(mode, type, selection) { qm.sendNext("I wonder what triggered this in the first place. There is no way this puppet was naturally created, which means someone planned this. I should keep an eye on the #o1210102#s.", 9); } else if (status == 3) { qm.sendPrev("#b(You were able to find out what caused the changes in the #o1210102#s. You should report to #p1002104# and deliver the information you've gathered.)#k", 2); - qm.dispose(); + } else if (status == 4) { + qm.dispose(); } } diff --git a/scripts/quest/21733.js b/scripts/quest/21733.js index db01dd4d68..95bf1964a2 100644 --- a/scripts/quest/21733.js +++ b/scripts/quest/21733.js @@ -61,7 +61,7 @@ function end(mode, type, selection) { qm.sendNext("Aran, thank you very much! Somehow the Puppeteer managed to bypass the security of Lith Harbor. He was trying to seek revenge because of the other day. Luckily, you came by. Nicely done!"); } else if(status == 1) { qm.sendNext("I will teach you the #rPolearm Mastery#k skill, to reward your actions here. You will be able to improve your accuracy and the overall mastery of your polearm arts."); - } else { + } else if(status == 2) { qm.gainExp(8000); qm.teachSkill(21100000, 0, 20, -1); // polearm mastery diff --git a/scripts/quest/21734.js b/scripts/quest/21734.js index f0880766f7..fee87ea085 100644 --- a/scripts/quest/21734.js +++ b/scripts/quest/21734.js @@ -63,11 +63,12 @@ function end(mode, type, selection) { qm.sendNext("They were after the #bcrystal seal of Victoria#k. These seals are what repels the Black Mage to further taking the continents into his grasp at once. Each continent has one, Victoria's now is safe and sound."); } else if(status == 2) { qm.sendNext("For your bravery inputted on these series of missions, I will now reward you properly. Behold, the #rCombo Drain#k Skill: that let's you heal back a portion of damage dealt to the monsters."); - } else { + } else if(status == 3) { + qm.forceCompleteQuest(); + qm.gainExp(12500); qm.teachSkill(21100005, 0, 20, -1); // combo drain - qm.forceCompleteQuest(); qm.dispose(); } } diff --git a/scripts/quest/21736.js b/scripts/quest/21736.js index fbde8021e3..fdc12d5624 100644 --- a/scripts/quest/21736.js +++ b/scripts/quest/21736.js @@ -42,7 +42,7 @@ function start(mode, type, selection) { qm.sendNextPrev("It seems like something strange is happening in Orbis in Ossyria. It's a bit different from when we were dealing with the puppeteer, but my instincts tell me it has to do with the Black Wings. Please head over to Orbis."); } else if(status == 3) { qm.sendAcceptDecline("#bLisa the Fairy#k in Orbis should know a thing or two. Go see Lisa first, she knows someone that knows the whereabouts of the sealing stone. That person #rwill require a password from you#k, when requested use the #bThere's something strange going on in Orbis....#k keyword to talk to her. Understood?"); - } else { + } else if(status == 4) { qm.forceStartQuest(); qm.dispose(); } diff --git a/scripts/quest/21738.js b/scripts/quest/21738.js index 22d58a79c3..c1345fbb11 100644 --- a/scripts/quest/21738.js +++ b/scripts/quest/21738.js @@ -34,18 +34,23 @@ function start(mode, type, selection) { else status--; - if (status == 0) { - qm.sendGetText("Hm, what do you want?"); + if (status == 0) { // thanks ZERO傑洛 for noticing this quest shouldn't need a pw -- GMS-like string data thanks to skycombat + qm.sendNext("What is it? I usually don't welcome uninvited guests, but you have a mysterious aura that makes me curious about what you have to say.", 9); } else if (status == 1) { - var text = qm.getText(); - - if(text != "There's something strange going on in Orbis....") { - qm.sendNext("No business to deal with? I won't brook loitering around here, go away."); - qm.dispose(); - } else { - qm.sendNext("Oh, that's right. I can sense the power emanating from you, as well. So I shall entrust something to you."); - } - } else { + qm.sendNext("(You tell her about Giant Nependeath.)", 3); + } else if (status == 2) { + qm.sendNext("Giant Nependeath? It's definitely a big problem, but I don't think it's enough to really affect Orbis. Wait, where did you say the Giant Nependeath was, again?", 9); + } else if (status == 3) { + qm.sendNext("Neglected Strolling Path.", 3); + } else if (status == 4) { + qm.sendNext("...Neglected Strolling Path? If Giant Nependeath is there, someone is trying to enter Sealed Garden! But why? And more importantly, who?", 9); + } else if (status == 5) { + qm.sendNext("Sealed Garden?", 3); + } else if (status == 6) { + qm.sendAcceptDecline("I can't tell you about Sealed Garden. If you want to find out, I must first see whether you are worthy of the information. Do you mind if I look into your fate?", 9); + } else if (status == 7) { + qm.sendOk("Well, now let's look into your fate. Give me a second."); + } else if (status == 8) { qm.forceStartQuest(); qm.dispose(); } diff --git a/scripts/quest/21739.js b/scripts/quest/21739.js index ea97e9556b..e711d50f6e 100644 --- a/scripts/quest/21739.js +++ b/scripts/quest/21739.js @@ -36,9 +36,9 @@ function end(mode, type, selection) { if(status == 0) { qm.sendNext("So, have you defeated the giant? Oh, a Black Wing agent undercover? And he GOT THE SEAL STONE OF ORBIS?! Oh, no. That's horrible! We need to develop countermeasures as soon as possible! Tell the informant on Lith about the situation."); - } else { - qm.gainExp(29500); + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainExp(29500); qm.dispose(); } } diff --git a/scripts/quest/21740.js b/scripts/quest/21740.js index 767e00f67a..c4a4b8d64a 100644 --- a/scripts/quest/21740.js +++ b/scripts/quest/21740.js @@ -61,10 +61,9 @@ function end(mode, type, selection) { qm.sendNext("Oh, hi #h0#! You won't believe what I just uncovered. It's one of your lost skills... What, the seal of Orbis got stolen by the Black Wings? Oh my..."); } else if(status == 1) { qm.sendNext("For now, let me teach you the #bCombo Smash#k, with it you will be able to deal massive amount of damage to many monsters at once. We will need to use it if we want to stand a chance against the Black Wings now, so don't forget it!"); - } else { - qm.teachSkill(21100004, 0, 20, -1); // combo smash - + } else if(status == 2) { qm.forceCompleteQuest(); + qm.teachSkill(21100004, 0, 20, -1); // combo smash qm.dispose(); } } diff --git a/scripts/quest/21741.js b/scripts/quest/21741.js index 25285e520d..b2ed795614 100644 --- a/scripts/quest/21741.js +++ b/scripts/quest/21741.js @@ -38,9 +38,9 @@ function start(mode, type, selection) { qm.sendNext("Have you been advancing your levels? I found an interesting piece of information about the Black Wings. This time, you'll have to travel quite a bit. Do you know a town called #bMu Lung#k? You'll have to head there."); } else if (status == 1) { qm.sendAcceptDecline("Apparently, #bMr. Do#k in Mu Lung somehow met with the Black Wings. I don't know the details. Please go and find out why the Black Wings contacted Mr. Do and what exactly happened between them."); - } else { + } else if (status == 2) { qm.sendNext("Mr. Do is known to be curt, so you are going to have to remain patient while talking to him. Talk to him with the #bI heard you met the Shadow Knight of the Black Wings#k keyword."); - + } else if (status == 3) { qm.forceStartQuest(); qm.dispose(); } diff --git a/scripts/quest/21749.js b/scripts/quest/21749.js index bb1af79100..9e4d69dfaf 100644 --- a/scripts/quest/21749.js +++ b/scripts/quest/21749.js @@ -38,9 +38,9 @@ function start(mode, type, selection) { qm.sendNext("So we have lost #btwo seal stones#k so far, from the neighboring areas of #rOrbis#k and #rMu Lung#k... Things are starting to get out of control, it seems."); } else if (status == 1) { qm.sendNext("Aran, your next objective will be to use the #btime gate to Ellin#k again. This time you will be retrieving the long lost #rSeal Stone of Ellin Forest#k. According to informations our network have gathered, #b#p2131002##k of that time have a clue about that gem, #rfind her#k. Please be successful on this task, our world is relying on you more than ever!"); - } else { - qm.gainExp(500); + } else if (status == 2) { qm.forceCompleteQuest(); + qm.gainExp(500); qm.dispose(); } } diff --git a/scripts/quest/21750.js b/scripts/quest/21750.js index 5ea00ec93b..04961a9a0c 100644 --- a/scripts/quest/21750.js +++ b/scripts/quest/21750.js @@ -36,7 +36,7 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Aran, you're finally back!!! How you've been doing? Where did you go for so long? We have so much to catch up..."); - } else { + } else if (status == 1) { qm.forceCompleteQuest(); qm.dispose(); } diff --git a/scripts/quest/21753.js b/scripts/quest/21753.js index 6fb029d8d0..6812c3c79f 100644 --- a/scripts/quest/21753.js +++ b/scripts/quest/21753.js @@ -38,8 +38,8 @@ function start(mode, type, selection) { qm.sendNext("Aran, I've discovered some disturbing news... You said you've come from the eastern forest section, right? We traced and studied the magic being used to support the portal over there. It turns out that's of a #rtemporal#k-type. The garments you're using... They were never seen around before. That must mean, #ryou must have come from the future#k."); } else if (status == 1) { qm.sendNext("Now about the problem: the Seal Stone that seems to have been missing in your timeline... It is a powerful artifact, that prevents the army of the #rBlack Mage#k from laying siege on our world. If that stone goes away, nothing more can prevent him. As this is a matter of great importance, find the #rself of mine#k from the future. I'm actually a #rfairy#k with a great life expectancy, I must be alive even on your timeline. Got it, #rfetch the me from the future#k!"); + } else if (status == 2) { qm.forceStartQuest(); - qm.dispose(); } } diff --git a/scripts/quest/21757.js b/scripts/quest/21757.js index 8e6c249739..4b37da7621 100644 --- a/scripts/quest/21757.js +++ b/scripts/quest/21757.js @@ -36,11 +36,10 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Oh, a letter for the #rempress#k? From the #bheroes#k?!"); - } else { + } else if (status == 1) { + qm.forceCompleteQuest(); qm.gainExp(1000); qm.gainItem(4032330, -1); - qm.forceCompleteQuest(); - qm.dispose(); } } diff --git a/scripts/quest/21766.js b/scripts/quest/21766.js index 8c55c5323d..dbcbb141bc 100644 --- a/scripts/quest/21766.js +++ b/scripts/quest/21766.js @@ -10,14 +10,14 @@ function start(mode, type, selection) { qm.sendNext("I have a feeling there is a secret behind that wooden box. Could you stealthily look into the wooden box next to #p20000#?"); } else if (status == 3) { qm.sendNext("You know where #p20000# is, right? He's to the right. Just keep going until you see where Vikin is, then head down past the hanging shark and octopus, and you''ll see John. The box should be right next to him."); - } else { + } else if (status == 4) { qm.forceStartQuest(); qm.dispose(); } } function end(mode, type, selection) { - qm.gainExp(200); qm.forceCompleteQuest(); + qm.gainExp(200); qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/21767.js b/scripts/quest/21767.js index 5d8d774512..de63158d03 100644 --- a/scripts/quest/21767.js +++ b/scripts/quest/21767.js @@ -1,4 +1,5 @@ var status = -1; +var canStart; function start(mode, type, selection) { status++; @@ -8,12 +9,20 @@ function start(mode, type, selection) { qm.dispose(); return; } + + canStart = qm.canHold(4032423, 1); + if(!canStart) { + qm.sendNext("Please open a slot in your ETC inventory first."); + return; + } qm.sendNext("#bHm, there's a medicinal substance in the box. What could this be? You better take this to John and ask him what it is.#k"); - } else { - qm.gainItem(4032423,1); - - qm.forceStartQuest(); + } else if (status == 1) { + if(canStart) { + qm.gainItem(4032423,1); + qm.forceStartQuest(); + } + qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/2186.js b/scripts/quest/2186.js index eaaa986720..9595e7b98e 100644 --- a/scripts/quest/2186.js +++ b/scripts/quest/2186.js @@ -3,38 +3,56 @@ Quest: Abel Glasses Quest */ -function end(mode, type, selection){ - if(!qm.isQuestCompleted(2186)) { - if(qm.haveItem(4031853)){ - if(qm.canHold(2030019)) { - qm.gainItem(4031853, -1); - qm.gainExp(1700); - qm.gainItem(2030019, 10); +var status = -1; // thanks IxianMace for noticing missing status declaration - qm.sendOk("Geez, you found my glasses! Thank you, thank you so much. Now I'm able to see everything again!"); - qm.forceCompleteQuest(); - } - else { - qm.sendOk("I need you to have an USE slot available to reward you properly!"); - } - }else if(qm.haveItem(4031854) || qm.haveItem(4031855)){ //When I figure out how to make a completance with just a pickup xD - if(qm.canHold(2030019)) { - if(qm.haveItem(4031854)) - qm.gainItem(4031854, -1); - else - qm.gainItem(4031855, -1); - - qm.gainExp(1000); - qm.gainItem(2030019, 5); - - qm.sendOk("Hm, those aren't my glasses... But alas, I'll take it anyway. Thanks."); - qm.forceCompleteQuest(); - } - else { - qm.sendOk("I need you to have an USE slot available to reward you properly!"); +function end(mode, type, selection){ + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(!qm.isQuestCompleted(2186)) { + if(qm.haveItem(4031853)){ + if(qm.canHold(2030019)) { + qm.gainItem(4031853, -1); + qm.gainExp(1700); + qm.gainItem(2030019, 10); + + qm.sendOk("Geez, you found my glasses! Thank you, thank you so much. Now I'm able to see everything again!"); + qm.forceCompleteQuest(); + } + else { + qm.sendOk("I need you to have an USE slot available to reward you properly!"); + } + }else if(qm.haveItem(4031854) || qm.haveItem(4031855)){ //When I figure out how to make a completance with just a pickup xD + if(qm.canHold(2030019)) { + if(qm.haveItem(4031854)) + qm.gainItem(4031854, -1); + else + qm.gainItem(4031855, -1); + + qm.gainExp(1000); + qm.gainItem(2030019, 5); + + qm.sendOk("Hm, those aren't my glasses... But alas, I'll take it anyway. Thanks."); + qm.forceCompleteQuest(); + } + else { + qm.sendOk("I need you to have an USE slot available to reward you properly!"); + } + } } + } else if (status == 1) { + qm.dispose(); } } - - qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2197.js b/scripts/quest/2197.js index 1f924c8e93..d2fa18169c 100644 --- a/scripts/quest/2197.js +++ b/scripts/quest/2197.js @@ -1,12 +1,46 @@ var status = -1; function start(mode, type, selection) { - qm.sendNext("Oh, you already have monster book. Good luck on your journey~!"); - qm.forceCompleteQuest(); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("Oh, you already have monster book. Good luck on your journey~!"); + } else if (status == 1) { + qm.forceCompleteQuest(); + qm.dispose(); + } + } } function end(mode, type, selection) { - qm.sendNext("Oh, you already have monster book. Good luck on your journey~!"); - qm.forceCompleteQuest(); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("Oh, you already have monster book. Good luck on your journey~!"); + } else if (status == 1) { + qm.forceCompleteQuest(); + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/22001.js b/scripts/quest/22001.js index d869634657..98ce206ec0 100644 --- a/scripts/quest/22001.js +++ b/scripts/quest/22001.js @@ -25,7 +25,8 @@ function start(mode, type, selection) { qm.sendNext("Hurry up and head #bleft#k to feed #b#p1013102##k. He's been barking to be fed all morning."); } } else if (status == 4) { - qm.sendPrev("Feed #p1013102# and come back to see me."); - qm.dispose(); - } + qm.sendNextPrev("Feed #p1013102# and come back to see me."); + } else if (status == 5) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/22002.js b/scripts/quest/22002.js index 0ab83b73d6..d759a4ec9b 100644 --- a/scripts/quest/22002.js +++ b/scripts/quest/22002.js @@ -40,10 +40,10 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Did you eat your breakfast, Evan? Then, will you do me a favor? \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#i1003028# 1 #t1003028# \r\n#i2022621# 5 #t2022621#s \r\n#i2022622# 5 #t2022622# \r\n#fUI/UIWindow.img/QuestIcon/8/0# 60 exp"); } else if (status == 1) { - qm.gainItem(1003028, 1, true); + qm.forceCompleteQuest(); + qm.gainItem(1003028, 1, true); qm.gainItem(2022621, 5, true); qm.gainItem(2022622, 5, true); - qm.forceCompleteQuest(); qm.gainExp(60); qm.sendImage("UI/tutorial/evan/4/0"); qm.dispose(); diff --git a/scripts/quest/22007.js b/scripts/quest/22007.js index 9ecdb28ade..19519e5236 100644 --- a/scripts/quest/22007.js +++ b/scripts/quest/22007.js @@ -22,6 +22,7 @@ function end(mode, type, selection) { qm.gainExp(360); qm.sendImage("UI/tutorial/evan/9/0"); } - qm.dispose(); - } + } else if (status == 3) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/22008.js b/scripts/quest/22008.js index b59b031a8a..cf5431dadd 100644 --- a/scripts/quest/22008.js +++ b/scripts/quest/22008.js @@ -69,6 +69,7 @@ function end(mode, type, selection) { qm.sendNextPrev("#bThis is a weapon that Magicians use. It's a Wand#k. You probably won't really need it, but it'll make you look important if you carry it around. Hahahahaha."); } else if (status == 13) { qm.sendPrev("Anyway, the Foxes have increased, right? How weird is that? Why are they growing day by day? We should really look into it and get to the bottom of this."); - qm.dispose(); - } + } else if (status == 14) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2214.js b/scripts/quest/2214.js index e94aa34c8e..dd8800681c 100644 --- a/scripts/quest/2214.js +++ b/scripts/quest/2214.js @@ -26,6 +26,7 @@ */ var status = -1; +var canComplete; function end(mode, type, selection) { if (mode == -1) { @@ -45,20 +46,24 @@ function end(mode, type, selection) { var hourDay = qm.getHourOfDay(); if(!(hourDay >= 17 && hourDay < 20)) { qm.sendNext("(Hmm, I'm searching the trash can but can't find the #t4031894# JM was talking about, maybe it's not time yet...)"); - qm.dispose(); + canComplete = false; return; } if(!qm.canHold(4031894, 1)) { qm.sendNext("(Eh, I can't hold the #t4031894# right now, I need an ETC slot available.)"); - qm.dispose(); + canComplete = false; return; } + canComplete = true; qm.sendNext("(Ah, there is a crumbled note here... Hm, it contains details about some scheme that is about to happen, that must be what #r#p1052002##k was talking about.)"); - qm.gainItem(4031894, 1); - qm.gainExp(20000); - qm.forceCompleteQuest(); + } else if (status == 1) { + if (canComplete) { + qm.forceCompleteQuest(); + qm.gainItem(4031894, 1); + qm.gainExp(20000); + } qm.dispose(); } diff --git a/scripts/quest/2215.js b/scripts/quest/2215.js index 457c38fa8a..0c3b24bc7b 100644 --- a/scripts/quest/2215.js +++ b/scripts/quest/2215.js @@ -26,6 +26,7 @@ */ var status = -1; +var canComplete; function end(mode, type, selection) { if (mode == -1) { @@ -45,26 +46,30 @@ function end(mode, type, selection) { var hourDay = qm.getHourOfDay(); if(!(hourDay >= 17 && hourDay < 20)) { qm.sendNext("(Hmm, I'm searching the trash can but can't find the #t4031894# JM was talking about, maybe it's not time yet...)"); - qm.dispose(); + canComplete = false; return; } if(qm.getMeso() < 2000) { qm.sendNext("(Oh, I don't have the combined fee amount yet.)"); - qm.dispose(); + canComplete = false; return; } if(!qm.canHold(4031894, 1)) { qm.sendNext("(Eh, I can't hold the #t4031894# right now, I need an ETC slot available.)"); - qm.dispose(); + canComplete = false; return; } + canComplete = true; qm.sendNext("(Alright, now I will deposit the fee there and get the paper... That's it, yea, that's done.)"); - qm.gainItem(4031894, 1); - qm.gainMeso(-2000); - qm.forceCompleteQuest(); + } else if (status == 1) { + if (canComplete) { + qm.gainMeso(-2000); + qm.forceCompleteQuest(); + qm.gainItem(4031894, 1); + } qm.dispose(); } diff --git a/scripts/quest/2216.js b/scripts/quest/2216.js index d3df6a730e..0364562ca1 100644 --- a/scripts/quest/2216.js +++ b/scripts/quest/2216.js @@ -36,8 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("I've just gathered an interesting information, #rDyle looks just like regular Ligators#k, but bigger."); - qm.gainExp(7000); + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainExp(7000); if(isAllSubquestsDone() && qm.haveItem(4031894)) { qm.gainItem(4031894, -1); diff --git a/scripts/quest/2217.js b/scripts/quest/2217.js index 30c62de83d..396f4dd97a 100644 --- a/scripts/quest/2217.js +++ b/scripts/quest/2217.js @@ -36,8 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("Hey, did you notice already, it looks like some awful stench is emanating from the sewers... Ewww"); - qm.gainExp(7000); + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainExp(7000); if(isAllSubquestsDone() && qm.haveItem(4031894)) { qm.gainItem(4031894, -1); diff --git a/scripts/quest/2218.js b/scripts/quest/2218.js index 54cd1c9fcc..9b0f14bfb0 100644 --- a/scripts/quest/2218.js +++ b/scripts/quest/2218.js @@ -36,8 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("Hey did you see how strange #rLakelis#k has been acting these days? We should see what's going on aabout her, her actions have been so weird lately..."); - qm.gainExp(7000); + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainExp(7000); if(isAllSubquestsDone() && qm.haveItem(4031894)) { qm.gainItem(4031894, -1); diff --git a/scripts/quest/2219.js b/scripts/quest/2219.js index 6bfc5de0f5..833f9131bb 100644 --- a/scripts/quest/2219.js +++ b/scripts/quest/2219.js @@ -36,8 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("Did you know, they say someone from the sewers has been trying to #rdevelop a magic powder that let's one to grow#k, isn't that nice?"); - qm.gainExp(7000); + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainExp(7000); if(isAllSubquestsDone() && qm.haveItem(4031894)) { qm.gainItem(4031894, -1); diff --git a/scripts/quest/2228.js b/scripts/quest/2228.js index ecc2a60265..0d7ab84a4b 100644 --- a/scripts/quest/2228.js +++ b/scripts/quest/2228.js @@ -36,10 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("Thank you for defeating #rFaust#k. That will finally settle my spirit to rest."); - } else { - qm.gainFame(8); - + } else if (status == 1) { qm.forceCompleteQuest(); + qm.gainFame(8); qm.dispose(); } } diff --git a/scripts/quest/2230.js b/scripts/quest/2230.js index 7aa7e5d5f9..2da466673c 100644 --- a/scripts/quest/2230.js +++ b/scripts/quest/2230.js @@ -28,6 +28,7 @@ */ var status = -1; +var canComplete; function start(mode, type, selection) { if (mode == -1) { @@ -45,6 +46,8 @@ function start(mode, type, selection) { qm.sendOk("Put your hand in your pocket. I think your friend has already found you.\r\nThe purple bellflower that soaks in the sun in between the skyscraping trees...Follow the path to the unknown that leads you to the bellflower. I will wait for you here."); qm.forceStartQuest(); qm.gainItem(4032086, 1); // Mysterious Egg * 1 + } else if (status == 3) { + qm.dispose(); } } } @@ -76,11 +79,20 @@ function end(mode, type, selection) { } else if (status == 5) { qm.sendYesNo("Now do you understand? Every action comes with consequences, and pets are no exception. The egg of the snail shall hatch soon."); } else if (status == 6) { - qm.gainItem(5000054, 1, false, true, 5 * 60 * 60 * 1000); // rune snail (5hrs), missing expiration time detected thanks to cljnilsson + canComplete = qm.canHold(5000054, 1); + if (!canComplete) { + qm.sendNext("Please free a slot in your CASH inventory before you try to receive the pet..."); + return; + } - qm.gainItem(4032086, -1); // Mysterious Egg * -1 - qm.forceCompleteQuest(); qm.sendNext("This snail will only be alive for #b5 hours#k. Shower it with love. Your love will be reciprocated in the end."); + } else if (status == 7) { + if (canComplete) { + qm.gainItem(4032086, -1); // Mysterious Egg * -1 + qm.forceCompleteQuest(); + qm.gainItem(5000054, 1, false, true, 5 * 60 * 60 * 1000); // rune snail (5hrs), missing expiration time detected thanks to cljnilsson + } + qm.dispose(); } } diff --git a/scripts/quest/2232.js b/scripts/quest/2232.js new file mode 100644 index 0000000000..0a674f2c08 --- /dev/null +++ b/scripts/quest/2232.js @@ -0,0 +1,25 @@ +var status = -1; + +function start(mode, type, selection) { + var familyEntry = qm.getPlayer().getFamilyEntry(); + if (familyEntry != null && familyEntry.getJuniorCount() > 0) { + qm.forceCompleteQuest(); + qm.gainExp(3000); + qm.sendNext("Good job!"); + } else { + qm.sendNext("I see that you have not successfully find a Junior, ok?"); + } + qm.dispose(); +} + +function end(mode, type, selection) { + var familyEntry = qm.getPlayer().getFamilyEntry(); + if (familyEntry != null && familyEntry.getJuniorCount() > 0) { // script found thanks to kvmba + qm.forceCompleteQuest(); + qm.gainExp(3000); + qm.sendNext("Good job!"); + } else { + qm.sendNext("I see that you have not successfully find a Junior, ok?"); + } + qm.dispose(); +} \ No newline at end of file diff --git a/scripts/quest/2236.js b/scripts/quest/2236.js deleted file mode 100644 index e460077dd0..0000000000 --- a/scripts/quest/2236.js +++ /dev/null @@ -1,54 +0,0 @@ -/* =========================================================== - Ronan Lana - NPC Name: Chrishrama - Description: Quest - How to Shoo Away the Evil -============================================================= -Version 1.0 - Script Done.(20/3/2017) -============================================================= -*/ - -var status = -1; - -function start(mode, type, selection) { - status++; - if (mode != 1) { - if(type == 1 && mode == 0) - status -= 2; - else{ - qm.sendOk("If we don't place these Charms on the Shaman Rocks, evil might awaken..."); - qm.dispose(); - return; - } - } - if (status == 0) - qm.sendAcceptDecline("I can feel the forces of evil. They're deep inside the dungeon and they're very, very powerful. If we want to drive the evil away from this place, we must place Charms on the Shaman Rocks inside the dungeon. Will you do that for me?"); - if (status == 1){ - if(qm.haveItem(4032263)) qm.gainItem(4032263, -6); - qm.gainItem(4032263, 6); - - qm.sendOk("Take these Charms and place them on the Shaman Rocks in the dungeon. I'm giving you a total of 6 Charms."); - qm.forceStartQuest(); - qm.dispose(); - } -} - -function end(mode, type, selection) { - status++; - - if(status == 0) { - if(qm.getQuestProgress(2236) == 63) { //111111 - qm.sendOk("I, too, felt it. The force of the Shaman Rocks began to overpower the forces of evil. I think Sleepywood is safe now. The evil has been eliminated."); - qm.gainExp(60000); - qm.forceCompleteQuest(); - } - else { - if(qm.haveItem(4032263)) qm.gainItem(4032263, -6); - qm.gainItem(4032263, 6); - - qm.sendOk("Oh, not good. I still sense bad omens coming from the interior. Here, take these charms and seal them at the Shaman Rocks. We are counting on you."); - qm.updateQuest(2236, 0); - } - - qm.dispose(); - } -} \ No newline at end of file diff --git a/scripts/quest/2245.js b/scripts/quest/2245.js index 1eba8bc811..5ea4177c39 100644 --- a/scripts/quest/2245.js +++ b/scripts/quest/2245.js @@ -38,7 +38,6 @@ function start(mode, type, selection) { em = qm.getEventManager("BalrogQuest"); if (em == null) { qm.sendOk("Sorry, but the BalrogQuest is closed."); - qm.dispose(); return; } @@ -47,8 +46,9 @@ function start(mode, type, selection) { qm.sendOk("There is currently someone in this map, come back later."); } else { qm.forceStartQuest(); + qm.dispose(); } - + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/22501.js b/scripts/quest/22501.js index 129d3d1e9c..e329b8d49c 100644 --- a/scripts/quest/22501.js +++ b/scripts/quest/22501.js @@ -20,11 +20,11 @@ function start(mode, type, selection) { } else if (status == 4) { if (mode == 0) { qm.sendNext("*gasp* How can you refuse to feed your Dragon? This is child abuse! "); - qm.dispose(); } else { qm.forceStartQuest(); qm.sendOk("#b#b(#p1013000# the baby Dragon appears to be extremely hungry. You must feed him. Maybe your Dad can give you advice on what dragons eat.)"); - qm.dispose(); } - } + } else if (status == 5) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/22502.js b/scripts/quest/22502.js index af95553b7a..6abcf2a1c6 100644 --- a/scripts/quest/22502.js +++ b/scripts/quest/22502.js @@ -18,6 +18,6 @@ function start(mode, type, selection) { qm.forceStartQuest(); qm.sendImage("UI/tutorial/evan/12/0"); } - qm.dispose(); - } -} \ No newline at end of file + qm.dispose(); + } +} diff --git a/scripts/quest/22503.js b/scripts/quest/22503.js index 851808fa56..b1f931d2ba 100644 --- a/scripts/quest/22503.js +++ b/scripts/quest/22503.js @@ -22,6 +22,7 @@ function start(mode, type, selection) { qm.forceStartQuest(); qm.sendNext("#b#b(Try giving #p1013000# some #t4032453#. You have to hunt a few #o1210100#s at the farm. Ten should be plenty...)"); } - qm.dispose(); - } + } else if (status == 4) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/22504.js b/scripts/quest/22504.js index 118850099d..a77be2beb5 100644 --- a/scripts/quest/22504.js +++ b/scripts/quest/22504.js @@ -22,5 +22,7 @@ function start(mode, type, selection) { qm.forceStartQuest(); qm.sendNext("#b#b(You already asked Dad once, but you don't have any better ideas. Time to ask him again!)"); } - } + } else if (status == 4) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/22507.js b/scripts/quest/22507.js index 8e3319f496..bbd890fb0e 100644 --- a/scripts/quest/22507.js +++ b/scripts/quest/22507.js @@ -53,6 +53,7 @@ function start(mode, type, selection) { qm.sendNextPrev("#b(You're a bit confused, but you are now traveling with Mir the Dragon. Perhaps you'll go on an adventure together, like he said.)", 2); } else if (status == 17) { qm.sendPrev("#b#b(You still have an errand to run. Your dad needs to talk to you, so go and see him now.)"); - qm.dispose(); - } + } else if (status == 18) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2251.js b/scripts/quest/2251.js index 31b5cbf4f0..3de79f99d4 100644 --- a/scripts/quest/2251.js +++ b/scripts/quest/2251.js @@ -1,21 +1,38 @@ -/* +-/* Author: Kevin Quest: Zombie Mushroom Signal 3 (2251) NPC: The Rememberer (1061011) Item: Recording Charm (4032399) */ +var status = -1; // script restored thanks to kvmba + function end(mode, type, selection) { - - if(!qm.haveItem(4032399, 20)) { - qm.sendOk("Please bring me 20 #b#t4032399##k... #i4032399#"); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(!qm.haveItem(4032399, 20)) { + qm.sendOk("Please bring me 20 #b#t4032399##k... #i4032399#"); + } + else { + qm.gainItem(4032399, -20); + qm.sendOk("Oh, you brought 20 #b#t4032399##k! Thank you."); + qm.gainExp(8000); + qm.forceCompleteQuest(); + } + } else if (status == 1) { + qm.dispose(); + } } - else { - qm.gainItem(4032399, -20); - qm.sendOk("Oh, you brought 20 #b#t4032399##k! Thank you."); - qm.gainExp(8000); - qm.forceCompleteQuest(); - } - - qm.dispose(); } \ No newline at end of file diff --git a/scripts/quest/2257.js b/scripts/quest/2257.js index 3b43326858..e09bef4312 100644 --- a/scripts/quest/2257.js +++ b/scripts/quest/2257.js @@ -36,7 +36,7 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Hey there, do you want a ride to #r#m261000000##k? Oh a request from #b#p2101013##k?"); - } else { + } else if (status == 1) { qm.forceCompleteQuest(); qm.dispose(); } diff --git a/scripts/quest/2258.js b/scripts/quest/2258.js index e65390eb4d..9ab0deb1ae 100644 --- a/scripts/quest/2258.js +++ b/scripts/quest/2258.js @@ -38,7 +38,7 @@ function start(mode, type, selection) { qm.sendAcceptDecline("Meerkats spreads rumors like wildfire... By blackmailing me and my cab service, they are taking costumers away from me day after day... Hey, tell no one about this, if you clean some #rMeerkats#k from my way, I'll tell you an info about the #rMushroom Castle#k. What do you say?"); } else if (status == 1) { qm.sendNext("Great, they you have #r5 minutes#k to kill #b40 Meerkats#k within this time. Good luck!"); - } else { + } else if (status == 2) { qm.forceStartQuest(); qm.dispose(); } @@ -61,7 +61,7 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("You did it! ... Hey, #rMeerkats#k around here may listen to our conversation. I'm not going to talk about THAT right now."); - } else { + } else if (status == 1) { qm.forceCompleteQuest(); qm.dispose(); } diff --git a/scripts/quest/2259.js b/scripts/quest/2259.js index 11c961a070..7fe8f2820a 100644 --- a/scripts/quest/2259.js +++ b/scripts/quest/2259.js @@ -60,13 +60,12 @@ function end(mode, type, selection) { if (status == 0) { if(qm.getMapId() == 260020000) { qm.sendNext("Eh you're still here? To reach #b#m260020700##k, follow #reast#k from here until you reach #rMagatia#k, I will be there. Now go."); - qm.dispose(); return; } qm.sendNext("Oh there you are. There're no Meerkat's nearby, so there probably is no eavesdropping around here. Very well, you must be fit to go to the #rMushroom Castle#k. Talk to me once you've got #blevel 30#k."); - } else { qm.forceCompleteQuest(); + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/2260.js b/scripts/quest/2260.js index 2eb8d976ca..852411aaaa 100644 --- a/scripts/quest/2260.js +++ b/scripts/quest/2260.js @@ -18,7 +18,7 @@ along with this program. If not, see . */ -importPackage(Packages.constants); +importPackage(Packages.constants.game); var status = -1; @@ -38,7 +38,7 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("Once you've got #b2nd job advancement#k, I'll tell you about the #bMushroom Castle#k."); - } else { + } else if (status == 1) { qm.forceStartQuest(); qm.dispose(); } @@ -67,8 +67,8 @@ function end(mode, type, selection) { } qm.sendNext("Okay you seem ready to go to the #bMushroom Castle#k. In #rHenesys#k, climb at the tree fort at #bwest#k then enter a portal over there. On the other area, #rgo west#k. From there, a portal will be readily available to access the #bMushroom Castle#k area."); - } else { qm.forceCompleteQuest(); + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/2293.js b/scripts/quest/2293.js index c19bb7064c..dd9cefc5d0 100644 --- a/scripts/quest/2293.js +++ b/scripts/quest/2293.js @@ -37,9 +37,12 @@ function start(mode, type, selection) { if(status == 0) { qm.sendNext("Do you remember the last song that the Spirit of Rock played? I can think of a few songs that he may be imitating, so listen carefully and tell me which song it is. #bYou only get one chance,#k so please choose wisely."); + qm.forceStartQuest(); + } + else if(status == 1) + { + qm.dispose(); } - qm.forceStartQuest(); - qm.dispose(); } function end(mode, type, selection) @@ -92,19 +95,24 @@ function end(mode, type, selection) if(selection == 1) { qm.sendOk("Obviously you don't enjoy music."); - qm.dispose(); } else if(selection == 2) { qm.sendOk("I suppose you could get #b#eone#n#k more chance."); - qm.dispose(); } else if(selection == 3) { qm.sendOk("So that was the song he was playing... Well, it wasn't my song after all, but I'm glad I can know that now with certainty. Thank you so much."); - qm.gainExp(32500); qm.forceCompleteQuest(); + qm.gainExp(32500); + } + else + { qm.dispose(); } } + else if(status == 3) + { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2312.js b/scripts/quest/2312.js index 12de61c803..2111510cd7 100644 --- a/scripts/quest/2312.js +++ b/scripts/quest/2312.js @@ -25,11 +25,12 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("We need your help, noble explorer. Our kingdom is currently facing a big threat, and we are in desperate need of a courageous explorer willing to fight for us, and that's how you ended up here. Please understand, though, that since we need place our faith in you, we'll have to test your skills first before we can stand firmly behind you. Will it be okay for you to do this for us?"); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Keep moving forward, and you'll see #bRenegade Spores#k, the Spores that turned their backs on the Kingdom of Mushroom. We'd appreciate it if you can teach them a lesson or two, and bring back #b50 Mutated Spores#k in return."); - qm.dispose(); - } + } else if (status == 2){ + qm.dispose(); + } } function end(mode, type, selection) { @@ -44,12 +45,13 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Did you teach those Renegade Spores a lesson?"); - if (status == 1){ + else if (status == 1){ qm.forceCompleteQuest(); qm.gainExp(11500); qm.gainItem(4000499, -50); qm.sendOk("That was amazing. I apologize for doubting your abilities. Please save our Kingdom of Mushroom from this crisis!"); - qm.dispose(); - } + } else if (status == 2){ + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2313.js b/scripts/quest/2313.js index 8beac840f9..1deddac73e 100644 --- a/scripts/quest/2313.js +++ b/scripts/quest/2313.js @@ -25,11 +25,12 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("I have told our #bMinister of Home Affairs#k of your abilities. Please go pay a visit to him immediately."); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Save our kingdom! We believe in you!"); - qm.dispose(); - } + } else if (status == 2){ + qm.dispose(); + } } function end(mode, type, selection) { diff --git a/scripts/quest/2314.js b/scripts/quest/2314.js index d2550dc558..b6fcdd41ec 100644 --- a/scripts/quest/2314.js +++ b/scripts/quest/2314.js @@ -23,16 +23,17 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("In order to rescue the princess, you must first navigate the Mushroom Forest. King Pepe set up a powerful barrier forbidding anyone from entering the castle. Please investigate this matter for us."); - if (status == 1) + else if (status == 1) qm.sendNext("You'll run into the barrier at the Mushroom Forest by heading east of where you are standing right now. Please be careful. I hear that the area is infested with crazy, fear-inducing monsters."); - if(status == 2){ + else if(status == 2){ //qm.forceStartQuest(); //qm.forceStartQuest(2314,"1"); qm.gainExp(8300); qm.sendOk("I see, so it was indeed not a regular barrier by any means. Great work there. If not for you help, we wouldn't have had a clue as to what that was all about."); qm.forceCompleteQuest(); - qm.dispose(); - } + } else if(status == 3){ + qm.dispose(); + } } function end(mode, type, selection) { @@ -47,11 +48,12 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("I see that you have thoroughly investigated the barrier at the Mushroom Forest. What was it like?"); - if (status == 1){ + else if (status == 1){ + qm.forceCompleteQuest(); qm.gainExp(8300); qm.sendOk("I see, so it was indeed not a regular barrier by any means. Great work there. If not for you help, we wouldn't have had a clue as to what that was all about."); - qm.forceCompleteQuest(); - qm.dispose(); - } - } + } else if (status == 2){ + qm.dispose(); + } +} \ No newline at end of file diff --git a/scripts/quest/2315.js b/scripts/quest/2315.js index d48aa09cb7..2432054981 100644 --- a/scripts/quest/2315.js +++ b/scripts/quest/2315.js @@ -25,11 +25,12 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("A powerful barrier of magic, huh? Then what should we do...? If we can't find a way to break that barrier, then we can't save the princess. If it's impossible to physically break through, as you mentioned, then how about requesting help from our #bMinister of Magic#k?"); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Please go see him immediately. The #bMinister of Magic#k may seem a bit on the edge, but he's very knowledgeable, and I'm sure he'll know what to do."); - qm.dispose(); - } + } else if (status == 2){ + qm.dispose(); + } } function end(mode, type, selection) { @@ -44,11 +45,12 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("What? You investigated the barrier at the Mushroom Forest?"); - if (status == 1){ - qm.gainExp(4000); - qm.sendOk("Hmmm...this is interesting. It's a barrier set up by someone with a powerful force of magic, which means there's no way we can manually break through it."); + else if (status == 1){ qm.forceCompleteQuest(); - qm.dispose(); - } + qm.gainExp(4000); + qm.sendOk("Hmmm...this is interesting. It's a barrier set up by someone with a powerful force of magic, which means there's no way we can manually break through it."); + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2316.js b/scripts/quest/2316.js index 32c5b0df3a..f55d8af293 100644 --- a/scripts/quest/2316.js +++ b/scripts/quest/2316.js @@ -25,9 +25,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("I think i've heard of a potion that breaks these kinds of barriers. I think it's called #bKiller Mushroom Spores#k? Hmmm... outside, you'll find the Mushroom Scholar #bScarrs#k waiting outside. #bScarrs#k is an expert on mushrooms, so go talk to him."); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("I am confident #kScarrs#k will do everything to help you."); + else if (status == 2){ qm.dispose(); } } @@ -44,11 +45,12 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Ah, so you're the explorer people were talking about. I'm #bScarrs, the Royal Mushroom Scholar#k representing the Kingdom of Mushroom. So you need some #kKiller Mushroom Spores#k?"); - if (status == 1){ + } else if (status == 1){ + qm.forceCompleteQuest(); qm.gainExp(4200); qm.sendOk("#kKiller Mushroom Spores#k... I think i've heard of them before..."); - qm.forceCompleteQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2317.js b/scripts/quest/2317.js index 562f06d61e..919c75815f 100644 --- a/scripts/quest/2317.js +++ b/scripts/quest/2317.js @@ -25,9 +25,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("Ah! If I am not mistaken, I saw the #bKiller Mushroom Spores#k way back when I was a kid in a book. Now I remember... it's made out of extracts of powerful poisons from Poison Mushrooms, which means you'll need some Poison Mushroom Caps. If you can get me those, I think I'll be able to make it."); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Please defeat #bPoison Mushrooms#k and bring back #b100 Poison Mushroom Caps#k in return."); + } else if (status == 2){ qm.dispose(); } } @@ -44,12 +45,13 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Have you gathered up the 100 Poison Mushroom Caps like I asked you to get?"); - if (status == 1){ - qm.gainExp(13500); + else if (status == 1){ + qm.sendOk("I am amazed that you were able to gather up these 100 Poison Mushroom Caps, which is considered a difficult feat. I think I'll be able to make #bKiller Mushroom Spores#k our of these."); + } else if (status == 2) { + qm.forceCompleteQuest(); + qm.gainExp(13500); qm.gainItem(4000500, -100); - qm.sendOk("I am amazed that you were able to gather up these 100 Poison Mushroom Caps, which is considered a difficult feat. I think I'll be able to make #bKiller Mushroom Spores#k our of these."); - qm.forceCompleteQuest(); qm.dispose(); - } + } } \ No newline at end of file diff --git a/scripts/quest/2318.js b/scripts/quest/2318.js index be099faaed..40fc347f56 100644 --- a/scripts/quest/2318.js +++ b/scripts/quest/2318.js @@ -25,9 +25,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("Hmmm... I looked into the making of the Spores while you were gathering up the Poison Mushroom Caps, and realised that we'll need more materials for it. I want you to gather up one more set of items. Can you do it?"); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Okay, I want you to defeat the Regenade Spores and bring back #b50 Mutated Spores#k in return."); + } else if (status == 2){ qm.dispose(); } } @@ -44,15 +45,22 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Did you gather up all the necessary ingredients for it?") - if (status == 1){ - qm.gainExp(11500); + else if (status == 1){ + if (!qm.haveItem(4000499, 50)) { + qm.sendOk("Please gather all the ingredients first."); + status = 2; + return; + } + + qm.sendNext("These should be enough for me to make the #bKiller Mushroom Spores.#k Please hold on for a bit."); + } else if(status == 2){ + qm.sendOk("Okay, here are the Killer Mushroom Spores. Hopefully this will be enough for you to save our princess and help regain our kingdom. Good luck!"); + } else if(status == 3) { + qm.forceCompleteQuest(); + qm.gainExp(11500); qm.gainItem(4000499, -50); - qm.sendNext("Okay, these should be enough for me to make the #bKiller Mushroom Spores.#k Please hold on for a bit."); - qm.forceCompleteQuest(); qm.gainItem(2430014, 1); - } if(status == 2){ - qm.sendPrev("Okay, here are the Killer Mushroom Spores. Hopefully this will be enough for you to save our princess and help regain our kingdom. Good luck!"); - qm.dispose(); - } + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2319.js b/scripts/quest/2319.js index 28fa533c72..a7897a0978 100644 --- a/scripts/quest/2319.js +++ b/scripts/quest/2319.js @@ -25,10 +25,11 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("Oh, I almost forgot! What was I thinking? I need you to hand this #bSample of Killer Mushroom Spores#k to #bMinister of Magic#k and report the results."); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.gainItem(4032389, 1); qm.sendOk("The #bMinister of Magic#k told me once the #bKiller Mushroom Spores#k is complete, that he'll want a sample of it as well. I'll give you the sample; now go please hand it in to our #bMinister of Magic.#k"); + } else if (status == 2){ qm.dispose(); } } @@ -45,12 +46,13 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Are the #bKiller Mushroom Spores#k finally completed?"); - if (status == 1){ + else if (status == 1){ + qm.forceCompleteQuest(); qm.gainExp(4200); qm.gainItem(4032389, -1); qm.sendOk("Okay, so this is the #bKiller Mushroom Spores.#k Thank you, thank you, and please tell #bScarrs#k the same."); - qm.forceCompleteQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2320.js b/scripts/quest/2320.js index 87a5a68780..381d762e37 100644 --- a/scripts/quest/2320.js +++ b/scripts/quest/2320.js @@ -25,10 +25,11 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("I have just one more request for you. Would you like to take a listen?"); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.gainItem(4032389, 1); qm.sendOk("To be honest, these #bKiller Mushroom Spores#k are not completely out of my own work. Do you remember #bBruce#k from #bHenesys#k? I have been friends with him since childhood, and #bKiller Mushroom Spores#k was completed after he shared the results of his studies with me. This was all thanks to him, so I'd like for you to give this to him for me."); + } else if (status == 2){ qm.dispose(); } } @@ -45,12 +46,13 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Oh! You're here on behalf of #bScarrs#k? \r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0# \r\n#fUI/UIWindow.img/QuestIcon/8/0# 8800 exp"); - if (status == 1){ + else if (status == 1){ + qm.forceCompleteQuest(); qm.gainExp(8800); qm.gainItem(4032389, -1); qm.sendOk("Ahh, so this is the #bKiller Mushroom Spores#k that I was working on in the past. I had a tough time gathering up the ingredients, so I left it in theory only, but he was able to complete it, with a sample to show for as well. Please tell him I appreciate his good work."); - qm.forceCompleteQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2321.js b/scripts/quest/2321.js index 61a14f5a5c..b9fc263f00 100644 --- a/scripts/quest/2321.js +++ b/scripts/quest/2321.js @@ -25,9 +25,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("Now you'll be able to penetrate the spiny vine barrier of Mushroom Forest, but before that, #bMinister of Home Affairs#k wants to have a word with you. Please go see him immediately."); - if (status == 1){ + else if (status == 1){ qm.forceStartQuest(); qm.sendOk("Good luck."); + } else if (status == 2){ qm.dispose(); } } @@ -44,11 +45,12 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("I have been keeping up on your fabulour work. I am aware that you have successfully created the #bKiller Mushroom Spores#k, which penetrates through the unpenetrable barrier of the forest. Congratulations!"); - if (status == 1){ + else if (status == 1){ + qm.forceCompleteQuest(); qm.gainExp(2500); qm.sendOk("The problem now is to figure out how to enter the castle."); - qm.forceCompleteQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2322.js b/scripts/quest/2322.js index 55bb3aad54..6ce6f2309b 100644 --- a/scripts/quest/2322.js +++ b/scripts/quest/2322.js @@ -25,14 +25,15 @@ function start(mode, type, selection) { } if (status == 0) qm.sendYesNo("Like I told you, just breaking the barrier cannot be a cause for celebration. That's because our castle for the Kingdom of Mushroom completely denies entry of anyone outside our kingdom, so it'll be hard for you to do that. Hmmm... to figure out a way to enter, can you...investigate the outer walls of the castle first?"); - if (status == 1) + else if (status == 1) qm.sendNext("Walk past the Mushroom Forest and when you reach the #bSplit Road of Choice#k, just walk towards the castle. Good luck."); - if (status == 2){ + else if (status == 2){ //qm.forceStartQuest(); //qm.forceStartQuest(2322, "1"); qm.gainExp(11000); qm.sendOk("Good job navigating through the area."); qm.forceCompleteQuest(); + } else if (status == 3){ qm.dispose(); } } @@ -49,11 +50,12 @@ function end(mode, type, selection) { } if (status == 0) qm.sendOk("Hmmm I see... so they have completely shut off the entrance and everything."); - if (status == 1){ - qm.gainExp(11000); - qm.sendOk("Good job navigating through the area."); + else if (status == 1){ qm.forceCompleteQuest(); - qm.dispose(); - } + qm.gainExp(11000); + qm.sendOk("Good job navigating through the area."); + } else if (status == 2) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2325.js b/scripts/quest/2325.js index 4154deb9d5..288ab76477 100644 --- a/scripts/quest/2325.js +++ b/scripts/quest/2325.js @@ -22,9 +22,10 @@ function end(mode, type, selection){ qm.sendNextPrev("Don't be afriad, #b#p1300005##k sent me here.", 2); } else if(status == 2){ - qm.sendOk("What? My brother sent you here? Ahhh... I am safe now. Thank you so much..."); qm.forceCompleteQuest(); qm.gainExp(6000); - qm.dispose(); - } + qm.sendOk("What? My brother sent you here? Ahhh... I am safe now. Thank you so much..."); + } else if (status == 3) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2338.js b/scripts/quest/2338.js index dd29f51031..71899082da 100644 --- a/scripts/quest/2338.js +++ b/scripts/quest/2338.js @@ -37,7 +37,7 @@ function start(mode, type, selection) { if (status == 0) { if(qm.haveItem(2430014, 1)) { qm.sendNext("It looks like you already have one #b#t2430014##k on your inventory."); - qm.dispose(); + status = 1; return; } @@ -48,8 +48,9 @@ function start(mode, type, selection) { } else { qm.gainItem(2430014, 1); qm.forceCompleteQuest(); + qm.dispose(); } - + } else if (status == 2) { qm.dispose(); } } diff --git a/scripts/quest/2342.js b/scripts/quest/2342.js index 6c8aed8d0d..6d5d2ff6e4 100644 --- a/scripts/quest/2342.js +++ b/scripts/quest/2342.js @@ -6,22 +6,38 @@ var status = -1; function start(mode, type, selection){ - if(!qm.hasItem(4001318) && qm.isQuestStarted(2331) && !qm.isQuestCompleted(2331)){ - if(qm.canHold(4001318)){ - qm.forceStartQuest(); - qm.gainItem(4001318, 1); - qm.forceCompleteQuest(); - qm.sendOk("Looks like you forgot to pick up the #b#t4001318##k when you fought with the #bPrime Minister#k. This is very important to our kingdom, so please deliver this to my father as soon as possible."); - qm.dispose(); - } - else{ - qm.sendOk("Please free up one spot in your ETC inventory"); - qm.dispose(); - } - } - else{ - qm.dispose(); - } + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(!qm.hasItem(4001318) && qm.isQuestStarted(2331) && !qm.isQuestCompleted(2331)){ + if(qm.canHold(4001318)){ + qm.forceStartQuest(); + qm.gainItem(4001318, 1); + qm.forceCompleteQuest(); + qm.sendOk("Looks like you forgot to pick up the #b#t4001318##k when you fought with the #bPrime Minister#k. This is very important to our kingdom, so please deliver this to my father as soon as possible."); + } + else{ + qm.sendOk("Please free up one spot in your ETC inventory"); + } + } + else{ + qm.dispose(); + } + } else if (status == 1) { + qm.dispose(); + } + } } function end(mode, type, selection){ diff --git a/scripts/quest/2560.js b/scripts/quest/2560.js index d784079e1b..985edef5c4 100644 --- a/scripts/quest/2560.js +++ b/scripts/quest/2560.js @@ -20,8 +20,10 @@ function start(mode, type, selection) { qm.sendNext("Ook! Ook! (The monkey looks very dissatisfied.)"); } else { qm.forceStartQuest(); + qm.dispose(); } - qm.dispose(); - } + } else if (status == 4) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/2568.js b/scripts/quest/2568.js index 6f2c64f01f..846cbd3f52 100644 --- a/scripts/quest/2568.js +++ b/scripts/quest/2568.js @@ -16,7 +16,7 @@ function start(mode, type, selection) { } else { qm.forceStartQuest(); - qm.warp(912060200); + qm.warp(912060200, 0); } qm.dispose(); } diff --git a/scripts/quest/2573.js b/scripts/quest/2573.js index 25f51e1264..8d05cda8e4 100644 --- a/scripts/quest/2573.js +++ b/scripts/quest/2573.js @@ -17,10 +17,11 @@ function start(mode, type, selection) { if (mode == 0) {//decline qm.sendNext("Hey, take it easy! Sometimes you just gotta wait."); } else { - qm.sendNext("Looks like we're all set! I think this is going to be a great voyage. Let's get underway."); qm.warp(3000000, 0); qm.forceCompleteQuest(); + qm.sendNext("Looks like we're all set! I think this is going to be a great voyage. Let's get underway."); } - qm.dispose(); - } + } else if (status == 3) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/28004.js b/scripts/quest/28004.js index 72186d520c..7385fbfa47 100644 --- a/scripts/quest/28004.js +++ b/scripts/quest/28004.js @@ -44,7 +44,7 @@ function start(mode, type, selection) { qm.sendNext("Okay... so here's our plan to defeat Scrooge and his dastardly plans. The Force of the Spirit I gave you is an item packed with mana. It's an item you'll definitely use at the map I am about to send you. In order to do that, you'll have to bring your party members with you as well. You should bring your party members here or form one right now!"); } else if (status == 1) { qm.sendAcceptDecline("Would you like to move forward?"); - } else { + } else if (status == 2) { var level = qm.getPlayer().getLevel(); qm.warp(level <= 30 ? 889100000 : (level <= 40 ? 889100010 : 889100020)); diff --git a/scripts/quest/29900.js b/scripts/quest/29900.js index 5a06f3b0c6..dc129556dd 100644 --- a/scripts/quest/29900.js +++ b/scripts/quest/29900.js @@ -41,14 +41,16 @@ function end(mode, type, selection) { if (status == 0) qm.sendNext("Congratulations on earning your honorable #b#k title. I wish you the best of luck in your future endeavors! Keep up the good work.\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n #v1142107:# #t1142107# 1"); else if (status == 1) { - if (qm.canHold(1142107)) { - qm.gainItem(1142107); - qm.forceCompleteQuest(); - } else - qm.sendNext("Please make room in your inventory");//NOT GMS LIKE - - qm.dispose(); - } + if (qm.canHold(1142107)) { + qm.gainItem(1142107); + qm.forceCompleteQuest(); + qm.dispose(); + } else { + qm.sendNext("Please make room in your inventory");//NOT GMS LIKE + } + } else if (status == 2) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/29901.js b/scripts/quest/29901.js index 91f9acb3d4..b38d41c0ae 100644 --- a/scripts/quest/29901.js +++ b/scripts/quest/29901.js @@ -41,14 +41,16 @@ function end(mode, type, selection) { if (status == 0) qm.sendNext("Congratulations on earning your honorable #b#k title. I wish you the best of luck in your future endeavors! Keep up the good work.\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n #v1142108:# #t1142108# 1"); else if (status == 1) { - if (qm.canHold(1142108)) { - qm.gainItem(1142108); - qm.forceCompleteQuest(); - } else - qm.sendNext("Please make room in your inventory");//NOT GMS LIKE - - qm.dispose(); - } + if (qm.canHold(1142108)) { + qm.gainItem(1142108); + qm.forceCompleteQuest(); + qm.dispose(); + } else { + qm.sendNext("Please make room in your inventory");//NOT GMS LIKE + } + } else if (status == 2) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/29902.js b/scripts/quest/29902.js index 144dea0f8c..c665731a14 100644 --- a/scripts/quest/29902.js +++ b/scripts/quest/29902.js @@ -41,14 +41,16 @@ function end(mode, type, selection) { if (status == 0) qm.sendNext("Congratulations on earning your honorable #b#k title. I wish you the best of luck in your future endeavors! Keep up the good work.\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n #v1142109:# #t1142109# 1"); else if (status == 1) { - if (qm.canHold(1142109)) { - qm.gainItem(1142109); - qm.forceCompleteQuest(); - } else - qm.sendNext("Please make room in your inventory");//NOT GMS LIKE - - qm.dispose(); - } + if (qm.canHold(1142109)) { + qm.gainItem(1142109); + qm.forceCompleteQuest(); + qm.dispose(); + } else { + qm.sendNext("Please make room in your inventory");//NOT GMS LIKE + } + } else if (status == 2) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/29903.js b/scripts/quest/29903.js index 9b29ae4a02..999075c0f9 100644 --- a/scripts/quest/29903.js +++ b/scripts/quest/29903.js @@ -41,14 +41,16 @@ function end(mode, type, selection) { if (status == 0) qm.sendNext("Congratulations on earning your honorable #b#k title. I wish you the best of luck in your future endeavors! Keep up the good work.\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n #v1142110:# #t1142110# 1"); else if (status == 1) { - if (qm.canHold(1142110)) { - qm.gainItem(1142110); - qm.forceCompleteQuest(); - } else - qm.sendNext("Please make room in your inventory");//NOT GMS LIKE - - qm.dispose(); - } + if (qm.canHold(1142110)) { + qm.gainItem(1142110); + qm.forceCompleteQuest(); + qm.dispose(); + } else { + qm.sendNext("Please make room in your inventory");//NOT GMS LIKE + } + } else if (status == 2) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/3108.js b/scripts/quest/3108.js index b12838b03e..fc17c6b6a3 100644 --- a/scripts/quest/3108.js +++ b/scripts/quest/3108.js @@ -37,7 +37,7 @@ function start(mode, type, selection) { if (status == 0) { qm.sendNext("(As you peek into the shattered statue, you might have found a clue about what happened. Better talk to #rScadur#k about this.)"); qm.forceCompleteQuest(); - + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/3114.js b/scripts/quest/3114.js deleted file mode 100644 index 11b3c7d0f0..0000000000 --- a/scripts/quest/3114.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - if(qm.getQuestProgress(3114, 7777) != -1) { - if(!qm.haveItem(4161036, 1)) { - if(qm.canHold(4161036, 1)) { - qm.gainItem(4161036, 1); - qm.sendNext("Seems you lost a book with the notes to Little Star. Here is another one. Please play it for me.", 9); - } else { - qm.sendNext("Seems you lost a book with the notes to Little Star, but you don't have an ETC available. Please free some room.", 9); - } - } else { - qm.sendNext(".....", 9); - } - - qm.dispose(); - return; - } - - qm.sendNext("(Eliza seems to be in deep sleep.)", 3); - } else if (status == 1) { - qm.gainFame(20); - - qm.forceCompleteQuest(); - qm.dispose(); - } - } -} \ No newline at end of file diff --git a/scripts/quest/3301.js b/scripts/quest/3301.js index 38db5d56cc..3e0352f326 100644 --- a/scripts/quest/3301.js +++ b/scripts/quest/3301.js @@ -28,28 +28,39 @@ var status = -1; var oreArray; -function start(mode, type, selection) { -} - function end(mode, type, selection) { - if (mode == -1 || (mode == 0 && type > 0)) { - qm.dispose(); + if (mode == -1) { + qm.dispose(); } else { - oreArray = getOreArray(); - if (status == -1) { + if (mode == 0 && type > 0) { + qm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + oreArray = getOreArray(); if (oreArray.length > 0) { - status++; qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Zenumist so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray)); } else { - qm.sendOk("What is this, you don't have the ores with you. No ore, no deal."); + qm.sendOk("What is this, you don't have the #rjewel ores#k with you. No ore, no deal."); qm.dispose(); + return; } - } else if (status == 0) { + } else if (status == 1) { + if (!qm.haveItem(oreArray[selection], 2)) { + qm.sendNext("What's this, you haven't got the #rjewel ores#k. No ores no deal!"); + qm.dispose(); + return; + } + qm.gainItem(oreArray[selection], -2); // Take 2 ores - qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Zanumist."); + qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Zenumist."); qm.forceCompleteQuest(); - qm.dispose(); - } else { + } else if (status == 2) { qm.dispose(); } } diff --git a/scripts/quest/3303.js b/scripts/quest/3303.js index 806589be97..d6884ab614 100644 --- a/scripts/quest/3303.js +++ b/scripts/quest/3303.js @@ -28,28 +28,39 @@ var status = -1; var oreArray; -function start(mode, type, selection) { -} - function end(mode, type, selection) { - if (mode == -1 || (mode == 0 && type > 0)) { - qm.dispose(); + if (mode == -1) { + qm.dispose(); } else { - oreArray = getOreArray(); - if (status == -1) { + if (mode == 0 && type > 0) { + qm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + oreArray = getOreArray(); if (oreArray.length > 0) { - status++; - qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Alcadno so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray)); + qm.sendSimple("Oh, looks like someone's ready to make a deal. You want to join Zenumist so badly, huh? I really don't understand you, but that's just fine. What will you give me in return?\r\n" + getOreString(oreArray)); } else { - qm.sendOk("What is this, you don't have the ores with you. No ore, no deal."); + qm.sendOk("What is this, you don't have the #rjewel ores#k with you. No ore, no deal."); // script would loop undefinitely at completion, thanks IxianMace for noticing qm.dispose(); + return; } - } else if (status == 0) { + } else if (status == 1) { + if (!qm.haveItem(oreArray[selection], 2)) { // thanks resinate for noticing a function missing here + qm.sendNext("What's this, you haven't got the #rjewel ores#k. No ores no deal!"); + qm.dispose(); + return; + } + qm.gainItem(oreArray[selection], -2); // Take 2 ores - qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Alcadno."); + qm.sendNext("Then wait for awhile. I'll go and get the stuff to help you pass the test of Chief Zenumist."); qm.forceCompleteQuest(); - qm.dispose(); - } else { + } else if (status == 2) { qm.dispose(); } } diff --git a/scripts/quest/3311.js b/scripts/quest/3311.js deleted file mode 100644 index f244fb5b26..0000000000 --- a/scripts/quest/3311.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - if(qm.getQuestProgress(3311, 0) == 1 && qm.getQuestProgress(3311, 1) == 1) { - qm.sendNext("Hmm, so the Alcadno doctor wrote something about researching some vanguardist Neo Huroid machine, that could beat by far the existing one, and was about to prepare the last steps of his rehearsal? We don't have a word about him for about three weeks now, something must have gone wrong..."); - qm.gainExp(60000); - qm.forceCompleteQuest(); - } else { - qm.sendNext("Found nothing yet? Please check out Dr. De Lang's house properly, something there may give out a clue about what is going on."); - } - - qm.dispose(); - } - } -} \ No newline at end of file diff --git a/scripts/quest/3314.js b/scripts/quest/3314.js index 2d8c7591b6..e549b69e8d 100644 --- a/scripts/quest/3314.js +++ b/scripts/quest/3314.js @@ -58,7 +58,7 @@ function end(mode, type, selection) { } else { qm.sendNext("You seem pretty normal, don't you? I can't detect any possible effect from my experiment on you. Go take the pill I asked you to take and show me the effects, will you?"); } - } else { + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/3321.js b/scripts/quest/3321.js index 8daca5d652..e53fb914b4 100644 --- a/scripts/quest/3321.js +++ b/scripts/quest/3321.js @@ -48,8 +48,8 @@ function start(mode, type, selection) { qm.sendNext("Oh, and I have a personal favor to ask, if it's not too much. I am worried about my wife, #b#p2111004##k. Since the incident with the Huroids I could send a word to her, that must have made a toll on her... Please, if you could, could you get the #bSilver Pendant#k I left #bback at home#k, and give it to her in my stead? I regret not giving the item right away to her, it was her birthday... Maybe giving it now to her can get her a good sleeping night, at least."); } else if (status == 5) { qm.sendNext("#rMake sure to remember this pattern!#k I've hid the pendant in my house, in a container #bbehind the water pipes#k. The pipes must be turned #bin order#k: top, bottom, middle. And then, enter the secret password: '#rmy love Phyllia#k'."); - qm.forceStartQuest(); + } else if (status == 6) { qm.dispose(); } } diff --git a/scripts/quest/3360.js b/scripts/quest/3360.js index d9f572b8b2..78496c6381 100644 --- a/scripts/quest/3360.js +++ b/scripts/quest/3360.js @@ -24,6 +24,7 @@ Description: Quest - Verifying the password */ var status = -1; +var pass; function start(mode, type, selection) { if (mode == -1) { @@ -47,35 +48,16 @@ function start(mode, type, selection) { } else if (status == 1) { qm.sendAcceptDecline("All right, now, this key is very long and complex. I need you to memorize it very well. I won't say again, so you'd better write it down somewhere. Are you ready?"); } else if (status == 2) { - var pass = generateString(); + pass = generateString(); qm.sendOk("The key code is #b" + pass + "#k. Got that? Put the key into the door of the secret passage, and you will be able to walk around the passage freely."); - qm.forceStartQuest(); - qm.setStringQuestProgress(3360, 0, pass); + } else if (status == 3) { + qm.forceStartQuest(); + qm.setQuestProgress(3360, pass); qm.dispose(); } } } -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if (mode == 1) - status++; - else - status--; - if (status == 0) { - if(qm.getQuestProgress(3360, 1) == 0) { - qm.sendNext("What's up? You haven't opened the Secret Passage yet?"); - } else { - qm.forceCompleteQuest(); - } - - qm.dispose(); - } - } -} - function generateString() { var thestring = ""; var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/scripts/quest/3382.js b/scripts/quest/3382.js index 046e056dfb..c2814e3554 100644 --- a/scripts/quest/3382.js +++ b/scripts/quest/3382.js @@ -27,37 +27,53 @@ Quest ID: 3382 */ +var status = -1; + function end(mode, type, selection) { - if(qm.haveItem(4001159, 25) && qm.haveItem(4001160, 25) && !qm.haveItemWithId(1122010, true)) { - if(qm.canHold(1122010)) { - qm.gainItem(4001159, -25); - qm.gainItem(4001160, -25); - qm.gainItem(1122010, 1); - - qm.sendOk("Thank you for retrieving the marbles. Accept this pendant as a token of my appreciation."); - } else { - qm.sendNext("Free a slot on your EQUIP tab before claiming a prize."); - qm.dispose(); - return; - } - } else if(qm.haveItem(4001159, 10) && qm.haveItem(4001160, 10)) { - if(qm.canHold(2041212)) { - qm.gainItem(4001159, -10); - qm.gainItem(4001160, -10); - qm.gainItem(2041212, 1); - - qm.sendOk("Thank you for retrieving the marbles. This rock, that I am giving to you, can be used to improve the stats on the #b#t1122010##k. Take it as a token of my appreciation and use it wisely."); - } else { - qm.sendNext("Free a slot on your USE tab before claiming a prize."); - qm.dispose(); - return; - } - } else { - qm.sendNext("I need at least #b10 of both #t4001159# and #t4001160##k to reward you appropriately. If you happen to come with #b25 of these#k instead, I can reward you with a valuable gear. Fare well."); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { qm.dispose(); return; } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(qm.haveItem(4001159, 25) && qm.haveItem(4001160, 25) && !qm.haveItemWithId(1122010, true)) { + if(qm.canHold(1122010)) { + qm.gainItem(4001159, -25); + qm.gainItem(4001160, -25); + qm.gainItem(1122010, 1); - qm.forceCompleteQuest(); - qm.dispose(); + qm.sendOk("Thank you for retrieving the marbles. Accept this pendant as a token of my appreciation."); + } else { + qm.sendNext("Free a slot on your EQUIP tab before claiming a prize."); + return; + } + } else if(qm.haveItem(4001159, 10) && qm.haveItem(4001160, 10)) { + if(qm.canHold(2041212)) { + qm.gainItem(4001159, -10); + qm.gainItem(4001160, -10); + qm.gainItem(2041212, 1); + + qm.sendOk("Thank you for retrieving the marbles. This rock, that I am giving to you, can be used to improve the stats on the #b#t1122010##k. Take it as a token of my appreciation and use it wisely."); + } else { + qm.sendNext("Free a slot on your USE tab before claiming a prize."); + return; + } + } else { + qm.sendNext("I need at least #b10 of both #t4001159# and #t4001160##k to reward you appropriately. If you happen to come with #b25 of these#k instead, I can reward you with a valuable gear. Fare well."); + return; + } + + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } diff --git a/scripts/quest/3437.js b/scripts/quest/3437.js index 2a3f4b1d5f..6c159e63f2 100644 --- a/scripts/quest/3437.js +++ b/scripts/quest/3437.js @@ -14,9 +14,7 @@ function end(mode, type, selection) { if(status == 0) { qm.sendNext("What the? Are you telling me you've already taken out 150 #o4230120#s? And these ... yes, these really are 120 #t4000122#s. I was wondering how you were going to complete this mission all by yourself, but you took care of it just fine. Alright, here ... this is a very important item for me, but please take it."); - } - - else if(status == 1) { + } else if(status == 1) { if(qm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.EQUIP).getNumFreeSlot() < 1) { qm.sendOk("Please free a EQUIP inventory slot to receive the reward."); qm.dispose(); @@ -34,16 +32,13 @@ function end(mode, type, selection) { else item = 1082149; qm.sendNext(talkStr); - } - - if(status == 2) { + } else if(status == 2) { + qm.completeQuest(); qm.gainItem(item, 1); qm.gainItem(4000122, -120); - qm.gainExp(6100); - qm.completeQuest(); - qm.sendOk("Thank you so much for fulfilling your missions as one of the Mesorangers. I've told the Sector about your successful story, and the Sector seems to be very pleased with you, too. Hopefully you'll keep working with us. Bye~"); + } else if (status == 3) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/3452.js b/scripts/quest/3452.js index 82c02a1ab2..a87f73f566 100644 --- a/scripts/quest/3452.js +++ b/scripts/quest/3452.js @@ -17,12 +17,13 @@ function end(mode, type, selection) { qm.gainItem(2000011, 50); qm.gainExp(8000); qm.forceCompleteQuest(); + qm.dispose(); } else { qm.sendNext("Hm? It looks like your inventory is full."); } - + } else if (status == 2) { qm.dispose(); - } + } } } \ No newline at end of file diff --git a/scripts/quest/3454.js b/scripts/quest/3454.js index f37a76db3c..84f2c77bf4 100644 --- a/scripts/quest/3454.js +++ b/scripts/quest/3454.js @@ -27,27 +27,46 @@ Quest ID: 3454 */ +var status = -1; + function end(mode, type, selection) { - if(qm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.ETC).getNumFreeSlot() < 1) { - qm.sendOk("Make room on your ETC inventory first."); - qm.dispose(); - return; - } + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(qm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.ETC).getNumFreeSlot() < 1) { + qm.sendOk("Make room on your ETC inventory first."); + qm.dispose(); + return; + } - qm.gainItem(4000125, -1); - qm.gainItem(4031926, -10); - qm.gainItem(4000119, -30); - qm.gainItem(4000118, -30); + qm.gainItem(4000125, -1); + qm.gainItem(4031926, -10); + qm.gainItem(4000119, -30); + qm.gainItem(4000118, -30); - rnd = Math.random(); - if(rnd < 1.0) { - qm.gainItem(4031928, 1); - } - else { - qm.gainItem(4031927, 1); - } + rnd = Math.random(); + if(rnd < 1.0) { + qm.gainItem(4031928, 1); + } + else { + qm.gainItem(4031927, 1); + } - qm.sendOk("Now, go meet Alien Gray and use this undercover to read through their plans. If this fails, we will need to gather some materials once again."); - qm.forceCompleteQuest(); - qm.dispose(); + qm.sendOk("Now, go meet Alien Gray and use this undercover to read through their plans. If this fails, we will need to gather some materials once again."); + qm.forceCompleteQuest(); + } else if (status == 1) { + qm.dispose(); + } + } } diff --git a/scripts/quest/3507.js b/scripts/quest/3507.js index 41dab043eb..f2ab5516af 100644 --- a/scripts/quest/3507.js +++ b/scripts/quest/3507.js @@ -1,10 +1,29 @@ + +var status = -1; + function end(mode, type, selection) { - if(qm.isQuestCompleted(3523) || qm.isQuestCompleted(3524) || qm.isQuestCompleted(3525) || qm.isQuestCompleted(3526) || qm.isQuestCompleted(3527) || qm.isQuestCompleted(3529) || qm.isQuestCompleted(3539)) { - qm.completeQuest(); - qm.sendOk("You are now filled with all of your memories again.. You are now allowed to go to #m270020000#."); - } else { - qm.sendOk("You have not yet checked with your first teacher about your memories?"); - } - + if (mode == -1) { qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(qm.isQuestCompleted(3523) || qm.isQuestCompleted(3524) || qm.isQuestCompleted(3525) || qm.isQuestCompleted(3526) || qm.isQuestCompleted(3527) || qm.isQuestCompleted(3529) || qm.isQuestCompleted(3539)) { + qm.completeQuest(); + qm.sendOk("You are now filled with all of your memories again.. You are now allowed to go to #m270020000#."); + } else { + qm.sendOk("You have not yet checked with your first teacher about your memories?"); + } + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3523.js b/scripts/quest/3523.js index 5ee5f05777..4f8ab2905d 100644 --- a/scripts/quest/3523.js +++ b/scripts/quest/3523.js @@ -23,10 +23,29 @@ * In search for the lost memory - warrior */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); // thanks resinate for pointing out uncompletable quest due to non-updated progress + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3524.js b/scripts/quest/3524.js index 744ca4f701..4b740dd36b 100644 --- a/scripts/quest/3524.js +++ b/scripts/quest/3524.js @@ -23,10 +23,29 @@ * In search for the lost memory - mage */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3525.js b/scripts/quest/3525.js index 76d31b4b92..97bbae92c7 100644 --- a/scripts/quest/3525.js +++ b/scripts/quest/3525.js @@ -23,10 +23,29 @@ * In search for the lost memory - bowman */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3526.js b/scripts/quest/3526.js index 295dc91cd5..77b49a7ce1 100644 --- a/scripts/quest/3526.js +++ b/scripts/quest/3526.js @@ -23,10 +23,29 @@ * In search for the lost memory - thief */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3527.js b/scripts/quest/3527.js index 5ba2606c08..ce28f770f0 100644 --- a/scripts/quest/3527.js +++ b/scripts/quest/3527.js @@ -23,10 +23,29 @@ * In search for the lost memory - pirate */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3529.js b/scripts/quest/3529.js index c2dd001165..5d1ac8f05f 100644 --- a/scripts/quest/3529.js +++ b/scripts/quest/3529.js @@ -36,8 +36,9 @@ function start(mode, type, selection) { if (status == 0) { qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - } else if (status == 1) { + qm.setQuestProgress(3507, 7081, 1); qm.forceCompleteQuest(); + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/3539.js b/scripts/quest/3539.js index 5ba2606c08..ce28f770f0 100644 --- a/scripts/quest/3539.js +++ b/scripts/quest/3539.js @@ -23,10 +23,29 @@ * In search for the lost memory - pirate */ +var status = -1; + function start(mode, type, selection) { - qm.startQuest(); - //qm.getPlayer().updateQuestInfo(3507, "1"); - qm.completeQuest(); - qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); - qm.dispose(); + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.startQuest(); + qm.setQuestProgress(3507, 7081, 1); + qm.completeQuest(); + qm.sendOk("You have regained your memories, talk to #b#p2140001##k to get the pass."); + } else if (status == 1) { + qm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/quest/3714.js b/scripts/quest/3714.js index f168dc4d7e..913c341fb9 100644 --- a/scripts/quest/3714.js +++ b/scripts/quest/3714.js @@ -55,11 +55,10 @@ function start(mode, type, selection) { return; } + qm.forceCompleteQuest(); qm.gainItem(4001094, -1); qm.gainItem(2041200, 1); // quest not rewarding properly found thanks to MedicOP & Thora qm.gainExp(42000); - - qm.forceCompleteQuest(); qm.dispose(); } } diff --git a/scripts/quest/3833.js b/scripts/quest/3833.js index 7e1a3a482b..b01f5946fa 100644 --- a/scripts/quest/3833.js +++ b/scripts/quest/3833.js @@ -51,12 +51,14 @@ function end(mode, type, selection) { qm.gainExp(10); qm.forceCompleteQuest(); } + + qm.dispose(); } else { qm.sendOk("Could you make #b2 slots available#k on your USE inventory before receiving your reward?"); - } - - qm.dispose(); - } + } + } else if (status == 2) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/3926.js b/scripts/quest/3926.js deleted file mode 100644 index a358b11be6..0000000000 --- a/scripts/quest/3926.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -/* Screwing the Red Scorpions - */ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - var c = 0; - - for(var i = 0; i < 4; i++) { - if(qm.getQuestProgress(3926, i) == 1) { - c++; - } - } - - if(c == 4) { - qm.sendNext("You delivered all the jewels, well done!"); - qm.gainExp(6500); - qm.forceCompleteQuest(); - } else { - qm.sendNext("Have you brought all the jewels from the Red Scorpions? They have to be delivered to the Residential areas of the Sand Bandits."); - } - - qm.dispose(); - } - } -} diff --git a/scripts/quest/3927.js b/scripts/quest/3927.js deleted file mode 100644 index 5fb4319180..0000000000 --- a/scripts/quest/3927.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - 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 . -*/ -/* - Author : Ronan Lana -*/ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if (mode == 0 && status == 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - - if(qm.getQuestProgress(3927) == 0) { // didn't find the wall yet, eh? - qm.sendOk("Did you find the wall? Look closely, the wall is more near than you think!"); - qm.dispose(); - return; - } - - if (status == 0) { - qm.sendSimple("Did you find the wall?\r\n#L0##b I did, but... I have no idea what it's talking about.#l"); - } else if (status == 1) { - qm.sendSimple("What did it say?\r\n#L0##b 'If I had an iron hammer and a dagger, a bow and an arrow...'#l\r\n#L1# 'Byron S2 Sirin'#l\r\n#L2# 'Ahhh I forgot.'"); - } else if (status == 2) { - if(selection == 0) { - qm.sendOk("If I had an iron hammer and a dagger... a bow and an arrow... what does that mean? Do you want me to tell you? I don't know myself. It's something you should think about. If you need a clue... it would go something like... a weapon is just an item... until someone uses it...?"); - } else if(selection == 1) { - qm.sendOk("Man, Jiyur wrote on the wall again! Arrgh!!"); - qm.dispose(); - return; - } else { - qm.sendOk("What? You forgot? Do you remember where it was written?"); - qm.dispose(); - return; - } - } else if (status == 3) { - qm.gainExp(1000); - qm.forceCompleteQuest(); - qm.dispose(); - } - } -} \ No newline at end of file diff --git a/scripts/quest/3929.js b/scripts/quest/3929.js deleted file mode 100644 index fdbe2dddde..0000000000 --- a/scripts/quest/3929.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -/* Sejan's Test - Food delivery on Ariant - */ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - var c = 0; - - for(var i = 0; i < 4; i++) { - if(qm.getQuestProgress(3929, i) == 1) { - c++; - } - } - - if(c == 4) { - qm.sendNext("You delivered all the food, good."); - qm.gainExp(2000); - qm.forceCompleteQuest(); - } else { - var missed = (4 - qm.getItemQuantity(4031580)) - c; - if(missed > 0) { - if(qm.canHold(4031580, missed)) { - qm.gainItem(4031580, missed); - qm.sendNext("Hey, what are you trying to pull on? To pass my test you must deliver all the foods to the Residential areas."); - } else { - qm.sendNext("You don't completed the task, neither has slots available on the inventory to get the food. Free a slot on your ETC please."); - } - } else { - qm.sendNext("Hey, what are you trying to pull on? To pass my test you must to deliver all the foods to the Residential areas."); - } - } - - qm.dispose(); - } - } -} diff --git a/scripts/quest/3933.js b/scripts/quest/3933.js index 01dc695301..4ee82e96df 100644 --- a/scripts/quest/3933.js +++ b/scripts/quest/3933.js @@ -43,15 +43,15 @@ function start(mode, type, selection) { qm.sendAcceptDecline("To truly see your strength, I'll have to face you myself. Don't worry, I'll summon my other self to face off against you. Are you ready?"); } else if (status == 2) { qm.sendNext("Good, I like your confidence."); - } else { + } else if (status == 3) { if(qm.getWarpMap(926000000).getCharacters().size() > 0) { qm.sendOk("There is someone currently in this map, come back later."); + qm.dispose(); } else { - qm.warp(926000000); + qm.warp(926000000, "st00"); qm.forceStartQuest(); + qm.dispose(); } - - qm.dispose(); } } } diff --git a/scripts/quest/3941.js b/scripts/quest/3941.js index c0eb4219e7..6588efd4af 100644 --- a/scripts/quest/3941.js +++ b/scripts/quest/3941.js @@ -42,18 +42,23 @@ function start(mode, type, selection) { else status--; - if(!isTigunMorphed(qm.getPlayer())) { - qm.sendNext("What's this? I can't simply give the Queen's silk to anyone, claiming they will hand it at once to the queen. Get out of my sights."); - qm.dispose(); - return; - } - if (status == 0) { + if(!isTigunMorphed(qm.getPlayer())) { + qm.sendNext("What's this? I can't simply give the Queen's silk to anyone, claiming they will hand it at once to the queen. Get out of my sights."); + status = 1; + return; + } + qm.sendNext("Tigun, what are you doing here?"); } else if (status == 1) { + if(!isTigunMorphed(qm.getPlayer())) { + qm.sendNext("What's this? I can't simply give the Queen's silk to anyone, claiming they will hand it at once to the queen. Get out of my sights."); + return; + } + qm.sendNext("The Queen wants her silk right now? Alright, I have them here. Hold on a moment."); - - qm.forceStartQuest(); + qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } @@ -73,13 +78,13 @@ function end(mode, type, selection) { else status--; - if(!isTigunMorphed(qm.getPlayer())) { - qm.sendNext("What's this? I can't simply give the Queen's silk to anyone, claiming they will hand it at once to the queen. Get out of my sights."); - qm.dispose(); - return; - } - if (status == 0) { + if(!isTigunMorphed(qm.getPlayer())) { + qm.sendNext("What's this? I can't simply give the Queen's silk to anyone, claiming they will hand it at once to the queen. Get out of my sights."); + qm.dispose(); + return; + } + if(qm.canHold(4031571, 1)) { qm.gainItem(4031571); @@ -88,7 +93,7 @@ function end(mode, type, selection) { } else { qm.sendNext("Hey, you're lacking space to hold this, man. I will stay with it while you arrange your backpack..."); } - + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/3953.js b/scripts/quest/3953.js index f45a2643c1..1575c21706 100644 --- a/scripts/quest/3953.js +++ b/scripts/quest/3953.js @@ -43,12 +43,12 @@ function end(mode, type, selection) { } else if (status == 3) { qm.sendSimple("They have departed on an expedition to get rid of some major threats in the desert that were ravaging Ariant, for quite some time now... It's strange, they should have already returned... Thinking about it now, the last attack on the merchants was around the direction the Guardians departed... No, that can't be... Can it?\r\n\r\n#L0##bPerhaps Deo has already turned into a monster.#k"); } else if (status == 4) { - qm.gainItem(4011008, -1); - - qm.sendNext("We're in great trouble, if it is like this. And it really seems like it. If the Royal Cactus Deo has gone insane, Ariant is done for. You, can you do something to defeat Deo? We really need your help now."); - qm.gainExp(20000); - qm.forceCompleteQuest(); + qm.gainItem(4011008, -1); + qm.gainExp(20000); + + qm.sendNext("We're in great trouble, if it is like this. And it really seems like it. If the Royal Cactus Deo has gone insane, Ariant is done for. You, can you do something to defeat Deo? We really need your help now."); + } else if (status == 5) { qm.dispose(); } } diff --git a/scripts/quest/4647.js b/scripts/quest/4647.js index c26f42341b..0e18b92d34 100644 --- a/scripts/quest/4647.js +++ b/scripts/quest/4647.js @@ -40,16 +40,14 @@ function end(mode, type, selection) { status--; if (status == 0) { if(qm.haveItem(5460000)) { - qm.sendOk("You got the Pet Snack! Thanks! You can use these to feed multiple pets at once!"); + qm.completeQuest(); qm.teachSkill(8, 1, 1, -1); qm.gainItem(5460000, -1, false); - qm.completeQuest(); - qm.dispose(); + qm.sendOk("You got the Pet Snack! Thanks! You can use these to feed multiple pets at once!"); } else { qm.sendOk("Get me the Pet Snack! It can be found in a very big shop...."); - qm.dispose(); } - } else { + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/4659.js b/scripts/quest/4659.js index b2d3bb6c23..2e50f3ca01 100644 --- a/scripts/quest/4659.js +++ b/scripts/quest/4659.js @@ -52,9 +52,10 @@ function end(mode, type, selection) { qm.sendNext("Great job on finding your evolution materials. I will now give you a robot."); } else if (status == 1) { - if (qm.isQuestCompleted(4659)) + if (qm.isQuestCompleted(4659)) { qm.dropMessage(1, "how did this get here?"); - else if (qm.canHold(5000048)){ + qm.dispose(); + } else if (qm.canHold(5000048)){ var pet = 0; var after; var i; @@ -108,8 +109,10 @@ function end(mode, type, selection) { // var petId = MaplePet.createPet(rand + 5000049, level, closeness, fullness); // if (petId == -1) return; // MapleInventoryManipulator.addById(qm.getClient(), rand+5000049, 1, "", petId); - } else + qm.dispose(); + } else { qm.dropMessage(1,"Your inventory is full"); - qm.dispose(); + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/6033.js b/scripts/quest/6033.js index e410a971f7..6f0ca48ffd 100644 --- a/scripts/quest/6033.js +++ b/scripts/quest/6033.js @@ -41,19 +41,19 @@ function end(mode, type, selection) { if (status == 0) { qm.sendNext("Hm, so you claim to have brought the #b#t4260003##k? Ok, let's take a look into it."); } else if (status == 1) { - if(qm.getQuestProgress(6033) == 1 && qm.haveItem(4260003, 1)) { - qm.sendNext("You indeed have crafted a fine piece of Monster Crystal, I see. You passed! Now, I shall teach you the next steps of the Maker skill. Keep the monster crystal with you as well, it's your work."); + if(qm.getQuestProgressInt(6033) == 1 && qm.haveItem(4260003, 1)) { + qm.sendNextPrev("You indeed have crafted a fine piece of Monster Crystal, I see. You passed! Now, I shall teach you the next steps of the Maker skill. Keep the monster crystal with you as well, it's your work."); } else { qm.sendNext("Hey, what's wrong? I did tell you to make a monster crystal to pass my test, didn't I? Buying one or crafting before the start of the test is NOT part of the deal. Go craft me an #b#t4260003##k."); qm.dispose(); return; } - } else { + } else if (status == 2) { + qm.forceCompleteQuest(); + var skillid = Math.floor(qm.getPlayer().getJob().getId() / 1000) * 10000000 + 1007; qm.teachSkill(skillid, 2, 3, -1); - qm.gainExp(230000); - qm.forceCompleteQuest(); qm.dispose(); } } diff --git a/scripts/quest/6036.js b/scripts/quest/6036.js index 4e8767ca57..1407c20da6 100644 --- a/scripts/quest/6036.js +++ b/scripts/quest/6036.js @@ -48,14 +48,13 @@ function end(mode, type, selection) { qm.dispose(); return; } - } else { + } else if (status == 2) { + qm.forceCompleteQuest(); + qm.gainItem(4031980, -1); - var skillid = Math.floor(qm.getPlayer().getJob().getId() / 1000) * 10000000 + 1007; qm.teachSkill(skillid, 3, 3, -1); - qm.gainExp(300000); - qm.forceCompleteQuest(); qm.dispose(); } diff --git a/scripts/quest/6410.js b/scripts/quest/6410.js deleted file mode 100644 index 76b732b50b..0000000000 --- a/scripts/quest/6410.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -/* - Hypnotize skill quest - */ - -var status = -1; - -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; - } - - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - if (qm.getQuestProgress(6410, 0) == 0) { - qm.sendOk("You must save #r#p2095000##k first!"); - qm.dispose(); - } else { - qm.sendNext("Again, thank you so much for rescuing me. I don't know how to repay you for all this... both Shulynch and you are the nicest people I have encountered. If you approach the mobs the same way you approached me, they may all end up becoming friends with you, as well. Please never lose the kindness you have in you."); - } - } else if (status == 1) { - qm.sendNext("(Friends with the mobs... never lose the kindness.)\r\n\r\n #s5221009# #b#q5221009##k"); - } else if (status == 2) { - qm.gainExp(1200000); - qm.teachSkill(5221009, 0, 10, -1); - - qm.forceCompleteQuest(); - qm.dispose(); - } - } -} \ No newline at end of file diff --git a/scripts/quest/8185.js b/scripts/quest/8185.js index b4623a147c..dccab63ba8 100644 --- a/scripts/quest/8185.js +++ b/scripts/quest/8185.js @@ -100,7 +100,8 @@ function end(mode, type, selection) { //SpawnPetHandler.evolve(qm.getPlayer().getClient(), 5000029, after); qm.sendOk("#bSWEET! IT WORKED!#k Your dragon has grown beautifully! #rYou may find your new pet under your 'CASH' inventory.\r #kIt used to be a #b #i5000029##t5000029##k, and now it's \r a #b#i" + after + "##t" + after + "##k!\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n#v"+after+"# #t"+after+"#"); - qm.dispose(); - } + } else if (status == 5) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/8189.js b/scripts/quest/8189.js index f14d596084..bc0d038f3a 100644 --- a/scripts/quest/8189.js +++ b/scripts/quest/8189.js @@ -47,37 +47,46 @@ function end(mode, type, selection) { } else if (status == 1) { qm.sendNextPrev("Then here we go...! #rHYAHH!#k"); } else if (status == 2) { - var pet = 0; - if (qm.getPlayer().getPet(0).getItemId() >= 5000029 && qm.getPlayer().getPet(0).getItemId() <= 5000033) { - var pet = 0; - } else if (qm.getPlayer().getPet(1).getItemId() >= 5000029 && qm.getPlayer().getPet(1).getItemId() <= 5000033) { - var pet = 1; - } else if (qm.getPlayer().getPet(2).getItemId() >= 5000029 && qm.getPlayer().getPet(2).getItemId() <= 5000033) { - var pet = 2; - } else { - qm.sendOk("Something wrong, try again."); - qm.dispose(); + var petidx = -1; + var petItemid; + for (var i = 0; i < 3; i++) { + var pet = qm.getPlayer().getPet(pet); + if (pet != null) { + var id = pet.getItemId(); + if (id >= 5000029 && id <= 5000033) { + petItemid = 5000030; + petidx = i; + break; + } else if (id >= 5000048 && id <= 5000053) { // thanks Conrad for noticing Robo pets not being able to re-evolve + petItemid = 5000049; + petidx = i; + break; + } + } + } + + if (petidx == -1) { + qm.sendOk("Something wrong, try again."); + qm.dispose(); return; - } - var id = qm.getPlayer().getPet(pet).getItemId(); - if (id < 5000029 || id > 5000033) { - qm.sendOk("Something wrong, try again."); - qm.dispose(); - } - var rand = 1 + Math.floor(Math.random() * 10); - var after = 0; - if (rand >= 1 && rand <= 3) { - after = 5000030; - } else if (rand >= 4 && rand <= 6) { - after = 5000031; - } else if (rand >= 7 && rand <= 9) { - after = 5000032; - } else if (rand == 10) { - after = 5000033; - } else { - qm.sendOk("Something wrong. Try again."); - qm.dispose(); - } + } + + var pool = (petItemid == 5000030) ? 10 : 11; + do { + var rand = 1 + Math.floor(Math.random() * pool); + var after = 0; + if (rand >= 1 && rand <= 3) { + after = petItemid; + } else if (rand >= 4 && rand <= 6) { + after = petItemid + 1; + } else if (rand >= 7 && rand <= 9) { + after = petItemid + 2; + } else if (rand == 10) { + after = petItemid + 3; + } else { + after = petItemid + 4; + } + } while (after == pet.getItemId()); /*if (name.equals(MapleItemInformationProvider.getInstance().getName(id))) { name = MapleItemInformationProvider.getInstance().getName(after); @@ -85,10 +94,11 @@ function end(mode, type, selection) { qm.gainMeso(-10000); qm.gainItem(5380000, -1); - qm.evolvePet(pet, after); + qm.evolvePet(petidx, after); qm.sendOk("Woo! It worked again! #rYou may find your new pet under your 'CASH' inventory.\r #kIt used to be a #b#i" + id + "##t" + id + "##k, and now it's \r a#b #i" + after + "##t" + after + "##k! \r\n Come back with 10,000 mesos and another Rock of Evolution if you don't like it!\r\n\r\n#fUI/UIWindow.img/QuestIcon/4/0#\r\n#v"+after+"# #t"+after+"#"); - qm.dispose(); - } + } else if (status == 3) { + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/8219.js b/scripts/quest/8219.js index d99e0bfd81..ff770041d5 100644 --- a/scripts/quest/8219.js +++ b/scripts/quest/8219.js @@ -22,9 +22,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("The time is now, kid. We have all the preparations complete to further research for why all these oddities have been happening lately. I also must introduce you to my brother, Jack. "); - if (status == 1){ + else if (status == 1){ qm.sendOk("He is currently wandering around the Crimsonwood Mountain, past the sinister Phantom Forest, in the track to the Crimsonwood Keep. Your next destination is there, may your journey be a safe one."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } @@ -48,14 +49,15 @@ function end(mode, type, selection) { } else if (status == 2){ if(qm.canHold(3992040, 1)) { + qm.forceCompleteQuest(); qm.gainItem(3992040, 1); qm.gainExp(175000); - qm.forceCompleteQuest(); + qm.dispose(); } else { qm.sendOk("Hey, you don't have a slot in your SETUP inventory for what I have to give to you. Solve that minor issue of yours then talk to me."); } - + } else if (status == 3) { qm.dispose(); - } + } } diff --git a/scripts/quest/8221.js b/scripts/quest/8221.js index 07e77ed2e4..21a1976bc4 100644 --- a/scripts/quest/8221.js +++ b/scripts/quest/8221.js @@ -26,6 +26,7 @@ function start(mode, type, selection) { else if (status == 1){ qm.sendOk("Okay, I need you to have these items on hand first: #b10 #t4010006##k, #b4 #t4032005##k and #b1 #t4004000##k. Go!"); qm.forceStartQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } diff --git a/scripts/quest/8223.js b/scripts/quest/8223.js index 84481e2964..da61f86149 100644 --- a/scripts/quest/8223.js +++ b/scripts/quest/8223.js @@ -22,9 +22,10 @@ function start(mode, type, selection) { } if (status == 0) qm.sendAcceptDecline("Oh, Jack sent you here? Good timing, I'm planning alongside Jack and others to storm the Keep and retake it from the Twisted Masters what is ours by right. You seem ready to fight alongside us, right?"); - if (status == 1){ + else if (status == 1){ qm.sendOk("Great! Your mission now is to rack down some numbers of their army and weaken their defenses by all effects. Defeat 75 of each: Windraider, Firebrand and Nightshadow, then return to me to report."); qm.forceStartQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } diff --git a/scripts/quest/8224.js b/scripts/quest/8224.js index 04a3c52d39..95844d8e56 100644 --- a/scripts/quest/8224.js +++ b/scripts/quest/8224.js @@ -25,6 +25,7 @@ function start(mode, type, selection) { else if (status == 1){ qm.sendOk("Ok. I need you to hunt down #bthose fake trees#k in the forest, and collect 50 of their drops as proof that you made your part on this."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } diff --git a/scripts/quest/8225.js b/scripts/quest/8225.js index 8bb382d08d..90fadcfb70 100644 --- a/scripts/quest/8225.js +++ b/scripts/quest/8225.js @@ -25,6 +25,7 @@ function start(mode, type, selection) { else if (status == 1){ qm.sendOk("Very well. To prove your valor among our ranks, you must first pass on a little challenge: you have to be able to move extraordinaly well around here, known of all secrets these woods holds. Trace a #bmap of the Phantom Forest#k, then come talk to me. I shall then evaluate if you're worth to be with us."); qm.forceStartQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } diff --git a/scripts/quest/8226.js b/scripts/quest/8226.js index 7f84f351b9..3c4fb90ae1 100644 --- a/scripts/quest/8226.js +++ b/scripts/quest/8226.js @@ -25,6 +25,7 @@ function start(mode, type, selection) { else if (status == 1){ qm.sendOk("Your next mission is: defeat the Elderwraiths that roam this forest. These are a tough bunch though, so stay alert. I need you to bring me 100 #t4032010# as proof of your duty."); qm.forceStartQuest(); - qm.dispose(); - } + } else if (status == 2) { + qm.dispose(); + } } diff --git a/scripts/quest/8227.js b/scripts/quest/8227.js index 4781e2d169..ab61a188d1 100644 --- a/scripts/quest/8227.js +++ b/scripts/quest/8227.js @@ -30,7 +30,7 @@ function start(mode, type, selection) { } else { qm.sendOk("Hey. There's no slot on your ETC."); } - + } else if (status == 2) { qm.dispose(); } } @@ -53,7 +53,7 @@ function end(mode, type, selection) { } else { qm.sendOk("You don't brought the coded letter Jack said? Come on, kid, we need that to decipher our enemies' next step!"); } - + } else if (status == 1){ qm.dispose(); } } diff --git a/scripts/quest/8228.js b/scripts/quest/8228.js index a17465293f..ef54ea70ae 100644 --- a/scripts/quest/8228.js +++ b/scripts/quest/8228.js @@ -30,7 +30,7 @@ function start(mode, type, selection) { } else { qm.sendOk("Hey. There's no slot on your ETC."); } - + } else if (status == 2){ qm.dispose(); } } @@ -53,7 +53,7 @@ function end(mode, type, selection) { } else { qm.sendOk("I'm afraid you don't have the letter you claimed to have with you."); } - + } else if (status == 1) { qm.dispose(); } } diff --git a/scripts/quest/8230.js b/scripts/quest/8230.js index 0f3507e9a2..f1983e82d4 100644 --- a/scripts/quest/8230.js +++ b/scripts/quest/8230.js @@ -25,6 +25,7 @@ function start(mode, type, selection) { else if (status == 1) { qm.sendOk("That's the thing: the Twisted Masters, great figures that currently holds seize of the Crimsonwood Keep, have planned a large-scale attack to the New Leaf City, that may be happening on the next few days. I can't just stay here observing while they prepare for this attack. However, I can't just leave this position, I must keep an eye on their moves at all costs. There's where you enter: go find Lukan, knight of the past Crimsonwood Keep, that is currently wandering around the woods, and receive from him further orders, he knows what to do."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } @@ -41,7 +42,7 @@ function end(mode, type, selection) { } else { qm.sendOk("The folks back there on the city are counting on you on this one. Please hurry up."); } - - qm.dispose(); - } + } else if (status == 1) { + qm.dispose(); + } } \ No newline at end of file diff --git a/scripts/quest/8231.js b/scripts/quest/8231.js index 29da40c564..8d339bb87e 100644 --- a/scripts/quest/8231.js +++ b/scripts/quest/8231.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r30 #t4032031##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8232.js b/scripts/quest/8232.js index 29da40c564..8d339bb87e 100644 --- a/scripts/quest/8232.js +++ b/scripts/quest/8232.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r30 #t4032031##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8233.js b/scripts/quest/8233.js index e6d894a7ab..40ff75b5ea 100644 --- a/scripts/quest/8233.js +++ b/scripts/quest/8233.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r30 #t4032011##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8234.js b/scripts/quest/8234.js index e6d894a7ab..40ff75b5ea 100644 --- a/scripts/quest/8234.js +++ b/scripts/quest/8234.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r30 #t4032011##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8235.js b/scripts/quest/8235.js index 3ec42e6726..1ea6062dd9 100644 --- a/scripts/quest/8235.js +++ b/scripts/quest/8235.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r1 #t4031903##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8236.js b/scripts/quest/8236.js index 3ec42e6726..1ea6062dd9 100644 --- a/scripts/quest/8236.js +++ b/scripts/quest/8236.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r1 #t4031903##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8237.js b/scripts/quest/8237.js index c756159db3..d47ec715ec 100644 --- a/scripts/quest/8237.js +++ b/scripts/quest/8237.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r1 #t4032013##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/quest/8238.js b/scripts/quest/8238.js index c756159db3..d47ec715ec 100644 --- a/scripts/quest/8238.js +++ b/scripts/quest/8238.js @@ -28,6 +28,7 @@ function start(mode, type, selection) { var reqs = "#r1 #t4032013##k"; qm.sendOk("Very well. Get me #r" + reqs + "#k, asap. The NLC is counting on you."); qm.forceStartQuest(); + } else if (status == 2) { qm.dispose(); } } \ No newline at end of file diff --git a/scripts/reactor/1209000.js b/scripts/reactor/1209000.js index 2533d11888..a332e450d4 100644 --- a/scripts/reactor/1209000.js +++ b/scripts/reactor/1209000.js @@ -24,6 +24,10 @@ */ function act() { // string visibility thanks to ProXAIMeRx & Glvelturall - if (rm.isQuestStarted(6400)) rm.setQuestProgress(6400, 0, 2); - rm.message("Real Bart has found. Return to Jonathan through portal."); + if (rm.isQuestStarted(6400)) { + rm.setQuestProgress(6400, 1, 2); + rm.setQuestProgress(6400, 6401, "q3"); + } + + rm.message("Real Bart has been found. Return to Jonathan through the portal."); } \ No newline at end of file diff --git a/scripts/reactor/2008007.js b/scripts/reactor/2008007.js new file mode 100644 index 0000000000..da63c803c2 --- /dev/null +++ b/scripts/reactor/2008007.js @@ -0,0 +1,29 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +/* @Author Ronan + * + * 2008007.js: OrbisPQ jail obstacle trigger +*/ + +function hit() { + var map = rm.getMap(); + map.moveEnvironment("trap" + rm.getReactor().getName()[5], 1); +} \ No newline at end of file diff --git a/scripts/reactor/2200001.js b/scripts/reactor/2200001.js index 7c8117f80f..850a492c4d 100644 --- a/scripts/reactor/2200001.js +++ b/scripts/reactor/2200001.js @@ -28,5 +28,5 @@ function act(){ rm.playerMessage(5,"You have found a secret factory!"); - rm.warp(Math.random() < .5 ? 922000020 : 922000021); + rm.warp(Math.random() < .5 ? 922000020 : 922000021, 0); } \ No newline at end of file diff --git a/sql/db_database.sql b/sql/db_database.sql index 857857152e..c25e795167 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -1,4 +1,4 @@ -#EXECUTE THIS FIRST, THEN NEXT SQL: 'db_drops.sql' +#EXECUTE THIS FIRST, THEN NEXT SQL: 'db_drops.sql' SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; @@ -80,6 +80,22 @@ CREATE TABLE IF NOT EXISTS `area_info` ( PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `bosslog_daily` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `characterid` int(11) NOT NULL, + `bosstype` enum('ZAKUM','HORNTAIL','PINKBEAN','SCARGA','PAPULATUS') NOT NULL, + `attempttime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + +CREATE TABLE IF NOT EXISTS `bosslog_weekly` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `characterid` int(11) NOT NULL, + `bosstype` enum('ZAKUM','HORNTAIL','PINKBEAN','SCARGA','PAPULATUS') NOT NULL, + `attempttime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + CREATE TABLE IF NOT EXISTS `bbs_replies` ( `replyid` int(10) unsigned NOT NULL AUTO_INCREMENT, `threadid` int(10) unsigned NOT NULL, @@ -10423,7 +10439,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (3230104, 4031209, 1, 1, 3072, 500000), (3230306, 4031159, 1, 1, 2074, 500000), (9500400, 4031224, 1, 1, 3607, 1000000), -(9500400, 4031223, 1, 1, 3607, 1000000), +(9500400, 4031223, 1, 1, 3608, 1000000), # thanks Lame for noticing Hongbu's gourd unavailable (9420003, 4031400, 1, 1, 8761, 1000000), (9420001, 4031401, 1, 1, 8761, 1000000), (9300097, 4031472, 1, 1, 6301, 100000), @@ -12811,9 +12827,10 @@ CREATE TABLE IF NOT EXISTS `dueypackages` ( `ReceiverId` int(10) unsigned NOT NULL, `SenderName` varchar(13) NOT NULL, `Mesos` int(10) unsigned DEFAULT '0', - `TimeStamp` varchar(10) NOT NULL, - `Message` varchar(200) NOT NULL DEFAULT "", + `TimeStamp` timestamp NOT NULL DEFAULT '2015-01-01 05:00:00', + `Message` varchar(200) NULL, `Checked` tinyint(1) unsigned DEFAULT '1', + `Type` tinyint(1) unsigned DEFAULT '0', PRIMARY KEY (`PackageId`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; @@ -12836,17 +12853,26 @@ CREATE TABLE IF NOT EXISTS `famelog` ( CREATE TABLE IF NOT EXISTS `family_character` ( `cid` int(11) NOT NULL, `familyid` int(11) NOT NULL, - `rank` int(11) NOT NULL, - `reputation` int(11) NOT NULL, - `todaysrep` int(11) NOT NULL, - `totaljuniors` int(11) NOT NULL, - `name` varchar(255) NOT NULL, - `juniorsadded` int(11) NOT NULL, - `totalreputation` int(11) NOT NULL, + `seniorid` int(11) NOT NULL, + `reputation` int(11) NOT NULL DEFAULT '0', + `todaysrep` int(11) NOT NULL DEFAULT '0', + `totalreputation` int(11) NOT NULL DEFAULT '0', + `reptosenior` int(11) NOT NULL DEFAULT '0', + `precepts` varchar(200) DEFAULT NULL, + `lastresettime` BIGINT(20) NOT NULL DEFAULT '0', PRIMARY KEY (`cid`), INDEX (cid, familyid) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; +CREATE TABLE IF NOT EXISTS `family_entitlement` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `charid` int(11) NOT NULL, + `entitlementid` int(11) NOT NULL, + `timestamp` BIGINT(20) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + INDEX (charid) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CREATE TABLE IF NOT EXISTS `fredstorage` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `cid` int(10) unsigned NOT NULL, @@ -13032,36 +13058,36 @@ INSERT IGNORE INTO `makercreatedata` (`id`, `itemid`, `req_level`, `req_maker_le (0, 4250900, 45, 1, 110000, 0, 0, 0, 1, 0), (0, 4251000, 45, 1, 110000, 0, 0, 0, 1, 0), (0, 4251100, 45, 1, 110000, 0, 0, 0, 1, 0), - (0, 4251300, 75, 2, 164000, 0, 0, 0, 1, 0), - (0, 4251400, 75, 2, 164000, 0, 0, 0, 1, 0), - (0, 4250001, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250101, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250201, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250301, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250401, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250501, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250601, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250701, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250801, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4250901, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4251001, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4251101, 45, 1, 328000, 0, 0, 0, 1, 0), - (0, 4251301, 75, 2, 491000, 0, 0, 0, 1, 0), - (0, 4251401, 75, 2, 491000, 0, 0, 0, 1, 0), - (0, 4250002, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250102, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250202, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250302, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250402, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250502, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250602, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250702, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250802, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4250902, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4251002, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4251102, 45, 2, 546000, 0, 0, 0, 1, 0), - (0, 4251302, 75, 3, 819000, 0, 0, 0, 1, 0), - (0, 4251402, 75, 3, 819000, 0, 0, 0, 1, 0), + (0, 4251300, 75, 2, 165000, 0, 0, 0, 1, 0), + (0, 4251400, 75, 2, 165000, 0, 0, 0, 1, 0), + (0, 4250001, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250101, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250201, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250301, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250401, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250501, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250601, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250701, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250801, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4250901, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4251001, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4251101, 45, 1, 330000, 0, 0, 0, 1, 0), + (0, 4251301, 75, 2, 495000, 0, 0, 0, 1, 0), + (0, 4251401, 75, 2, 495000, 0, 0, 0, 1, 0), + (0, 4250002, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250102, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250202, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250302, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250402, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250502, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250602, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250702, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250802, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4250902, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4251002, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4251102, 45, 2, 550000, 0, 0, 0, 1, 0), + (0, 4251302, 75, 3, 825000, 0, 0, 0, 1, 0), + (0, 4251402, 75, 3, 825000, 0, 0, 0, 1, 0), (0, 4001174, 45, 1, 0, 4031966, 0, 0, 1, 0), (0, 4001175, 50, 1, 0, 4031967, 0, 0, 1, 0), (0, 4001176, 55, 1, 0, 4031968, 0, 0, 10, 0), @@ -13080,6 +13106,8 @@ INSERT IGNORE INTO `makercreatedata` (`id`, `itemid`, `req_level`, `req_maker_le (0, 4032312, 70, 1, 0, 0, 0, 0, 1, 0), (0, 2041058, 50, 1, 55000, 0, 1122013, 0, 1, 0), (0, 2040727, 50, 1, 55000, 0, 1122013, 0, 1, 0), + (0, 4260007, 105, 3, 2200000, 4001126, 0, 0, 5, 0), + (0, 4260008, 105, 3, 5500000, 4001126, 0, 0, 10, 0), (1, 1002028, 45, 1, 55000, 0, 0, 4130018, 1, 1), (1, 1002085, 45, 1, 50000, 0, 0, 4130018, 1, 1), (1, 1002086, 45, 1, 41000, 0, 0, 4130018, 1, 1), @@ -13963,6 +13991,10 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (2041058, 4000299, 10), (2040727, 4000159, 50), (2040727, 4000299, 10), + (4260007, 4260006, 100), + (4260007, 4001126, 5), + (4260008, 4260007, 50), + (4260008, 4001126, 5), (1002028, 4007001, 5), (1002028, 4260000, 5), (1002085, 4007002, 5), @@ -14808,11 +14840,11 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (1372016, 4011002, 3), (1372016, 4260003, 26), (1382008, 4011002, 3), - (1382008, 4260003, 26), + (1382008, 4260004, 26), (1372009, 4011002, 4), (1372009, 4260004, 28), (1382035, 4011002, 4), - (1382035, 4260004, 28), + (1382035, 4260005, 28), (1372010, 4011003, 4), (1372010, 4260005, 30), (1372032, 4011003, 5), @@ -15450,7 +15482,7 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (1072194, 4260004, 9), (1072195, 4007006, 9), (1072195, 4260004, 9), - (1072213, 4007001, 10), + (1072213, 4007003, 10), (1072213, 4260005, 10), (1072214, 4007002, 10), (1072214, 4260005, 10), @@ -16293,6 +16325,9 @@ CREATE TABLE IF NOT EXISTS `mts_items` ( `position` int(11) DEFAULT '0', `upgradeslots` int(11) DEFAULT '0', `level` int(11) DEFAULT '0', + `itemlevel` int(11) NOT NULL DEFAULT '1', + `itemexp` int(11) unsigned NOT NULL DEFAULT '0', + `ringid` int(11) NOT NULL DEFAULT '-1', `str` int(11) DEFAULT '0', `dex` int(11) DEFAULT '0', `int` int(11) DEFAULT '0', @@ -16316,9 +16351,22 @@ CREATE TABLE IF NOT EXISTS `mts_items` ( `transfer` int(2) DEFAULT '0', `vicious` int(2) unsigned NOT NULL DEFAULT '0', `flag` int(2) unsigned NOT NULL DEFAULT '0', + `expiration` bigint(20) NOT NULL DEFAULT '-1', + `giftFrom` varchar(26) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `namechanges` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `characterid` int(11) NOT NULL, + `old` varchar(13) NOT NULL, + `new` varchar(13) NOT NULL, + `requestTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `completionTime` timestamp NULL, + PRIMARY KEY (`id`), + INDEX (characterid) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + CREATE TABLE IF NOT EXISTS `newyear` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `senderid` int(10) NOT NULL DEFAULT '-1', @@ -16415,7 +16463,7 @@ INSERT INTO `nxcoupons` (`id`, `couponid`, `rate`, `activeday`, `starthour`, `en (40,5360042,2,254,0,24); CREATE TABLE IF NOT EXISTS `pets` ( - `petid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `petid` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(13) DEFAULT NULL, `level` int(10) unsigned NOT NULL, `closeness` int(10) unsigned NOT NULL, @@ -16425,11 +16473,13 @@ CREATE TABLE IF NOT EXISTS `pets` ( PRIMARY KEY (`petid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + CREATE TABLE IF NOT EXISTS `petignores` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `petid` int(10) unsigned NOT NULL , + `petid` int(11) unsigned NOT NULL , `itemid` int(10) unsigned NOT NULL , - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + CONSTRAINT `fk_petignorepetid` FOREIGN KEY (`petid`) REFERENCES `pets` (`petid`) ON DELETE CASCADE # thanks Optimist for noticing queries over petid taking too long, shavit for pointing out an improvement using foreign key ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; CREATE TABLE IF NOT EXISTS `playerdiseases` ( @@ -17383,7 +17433,7 @@ CREATE TABLE IF NOT EXISTS `reports` ( `victimid` int(11) NOT NULL, `reason` tinyint(4) NOT NULL, `chatlog` text NOT NULL, - `status` text NOT NULL, + `description` text NOT NULL, # correct field name, thanks resinate PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; @@ -20811,6 +20861,25 @@ INSERT INTO `shopitems` (`shopitemid`, `shopid`, `itemid`, `price`, `pitch`, `po (6531, 1337, 2040711, 1, 0, 62), (6532, 1337, 2340000, 1, 0, 63), (20020, 1337, 1082149, 1, 0, 64), +(20255, 1337, 2044503, 1, 0, 86), # 20255~20273: thanks to ozanrijen +(20256, 1337, 2044703, 1, 0, 87), +(20257, 1337, 2044603, 1, 0, 88), +(20258, 1337, 2043303, 1, 0, 89), +(20259, 1337, 2043103, 1, 0, 90), +(20260, 1337, 2043203, 1, 0, 91), +(20261, 1337, 2043003, 1, 0, 92), +(20262, 1337, 2044403, 1, 0, 93), +(20263, 1337, 2044303, 1, 0, 94), +(20264, 1337, 2043803, 1, 0, 95), +(20265, 1337, 2044103, 1, 0, 96), +(20266, 1337, 2044203, 1, 0, 97), +(20267, 1337, 2044003, 1, 0, 98), +(20268, 1337, 2043703, 1, 0, 99), +(20269, 1337, 2040806, 1, 0, 100), +(20270, 1337, 2040007, 1, 0, 101), +(20271, 1337, 2040506, 1, 0, 102), +(20272, 1337, 2040710, 1, 0, 103), +(20273, 1337, 2040711, 1, 0, 104), (6533, 9000069, 2022503, 0, 5, 1), (6534, 9000069, 2000004, 0, 5, 2), (6535, 9000069, 2022514, 0, 10, 3), @@ -21357,7 +21426,8 @@ CREATE TABLE IF NOT EXISTS `skills` ( `skilllevel` int(11) NOT NULL DEFAULT '0', `masterlevel` int(11) NOT NULL DEFAULT '0', `expiration` bigint(20) NOT NULL DEFAULT '-1', - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + UNIQUE INDEX `skillpair` (`skillid`, `characterid`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; CREATE TABLE IF NOT EXISTS `specialcashitems` ( @@ -21395,12 +21465,29 @@ CREATE TABLE IF NOT EXISTS `wishlists` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +CREATE TABLE IF NOT EXISTS `worldtransfers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `characterid` int(11) NOT NULL, + `from` tinyint(3) NOT NULL, + `to` tinyint(3) NOT NULL, + `requestTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `completionTime` timestamp NULL, + PRIMARY KEY (`id`), + INDEX (characterid) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; + ALTER TABLE `dueyitems` ADD CONSTRAINT `dueyitems_ibfk_1` FOREIGN KEY (`PackageId`) REFERENCES `dueypackages` (`PackageId`) ON DELETE CASCADE; ALTER TABLE `famelog` ADD CONSTRAINT `famelog_ibfk_1` FOREIGN KEY (`characterid`) REFERENCES `characters` (`id`) ON DELETE CASCADE; + +ALTER TABLE `family_character` + ADD CONSTRAINT `family_character_ibfk_1` FOREIGN KEY (`cid`) REFERENCES `characters` (`id`) ON DELETE CASCADE; + +ALTER TABLE `skills` + ADD CONSTRAINT `skills_chrid_fk` FOREIGN KEY (`characterid`) REFERENCES `characters` (`id`) ON DELETE CASCADE; # thanks Shavit /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 568041c086..d17bc0adde 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -17932,7 +17932,7 @@ USE `heavenms`; (8220005, 1002382, 1, 1, 0, 40000), (8220005, 1482012, 1, 1, 0, 40000), (8800002, 4001083, 1, 1, 0, 7000), -(8800002, 4032133, 1, 1, 0, 10000), +(8800002, 4032133, 1, 1, 0, 420000), (8800002, 2000005, 1, 4, 0, 40000), (8800002, 2020015, 1, 4, 0, 3000), (8800002, 2000006, 1, 4, 0, 40000), @@ -23926,527 +23926,528 @@ SET minimum_quantity = CASE UPDATE drop_data SET `chance`=1287 WHERE `chance`=1500; # MapleSkillbookChanceFetcher! Tuning up some skillbook drop chances in order to fit their dropper's availability (whether's a boss or not) and level. + # thanks unnqca for reporting some skillbooks having unusually high drop chances. REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES (851000, 2290132, 1, 1, 0, 3861), -(7090000, 2290087, 1, 1, 0, 40000), -(8090000, 2290045, 1, 1, 0, 40000), -(8140103, 2290044, 1, 1, 0, 2000), -(8140511, 2290009, 1, 1, 0, 2000), -(8140511, 2290050, 1, 1, 0, 2000), -(8140511, 2290083, 1, 1, 0, 2000), -(8140511, 2290134, 1, 1, 0, 6000), -(8140512, 2290013, 1, 1, 0, 2000), -(8140512, 2290067, 1, 1, 0, 2000), -(8140512, 2290082, 1, 1, 0, 2000), -(8140512, 2290097, 1, 1, 0, 2000), -(8140512, 2290116, 1, 1, 0, 2000), -(8140512, 2290131, 1, 1, 0, 6000), -(8140600, 2290132, 1, 1, 0, 6000), -(8140700, 2290106, 1, 1, 0, 2000), -(8140700, 2290126, 1, 1, 0, 6000), -(8140701, 2290122, 1, 1, 0, 2000), -(8140702, 2290112, 1, 1, 0, 2000), -(8140703, 2290088, 1, 1, 0, 2000), -(8140703, 2290099, 1, 1, 0, 2000), -(8141000, 2290082, 1, 1, 0, 2000), -(8141000, 2290097, 1, 1, 0, 2000), -(8141100, 2280005, 1, 1, 0, 2000), -(8141300, 2290098, 1, 1, 0, 2000), -(8142100, 2290032, 1, 1, 0, 2000), -(8142100, 2290082, 1, 1, 0, 2000), -(8142100, 2290114, 1, 1, 0, 2000), -(8143000, 2280004, 1, 1, 0, 2000), -(8150000, 2280013, 1, 1, 0, 120000), -(8150000, 2290070, 1, 1, 0, 40000), -(8150000, 2290091, 1, 1, 0, 40000), -(8150100, 2290042, 1, 1, 0, 2000), -(8150100, 2290053, 1, 1, 0, 2000), -(8150100, 2290073, 1, 1, 0, 2000), -(8150100, 2290102, 1, 1, 0, 2000), -(8150100, 2290118, 1, 1, 0, 2000), -(8150101, 2290017, 1, 1, 0, 2000), -(8150101, 2290021, 1, 1, 0, 2000), -(8150101, 2290035, 1, 1, 0, 2000), -(8150101, 2290042, 1, 1, 0, 2000), -(8150101, 2290052, 1, 1, 0, 2000), -(8150101, 2290102, 1, 1, 0, 2000), -(8150200, 2290024, 1, 1, 0, 2000), -(8150200, 2290100, 1, 1, 0, 2000), -(8150200, 2290135, 1, 1, 0, 6000), -(8150201, 2290004, 1, 1, 0, 2000), -(8150201, 2290006, 1, 1, 0, 2000), -(8150201, 2290024, 1, 1, 0, 2000), -(8150201, 2290036, 1, 1, 0, 2000), -(8150201, 2290056, 1, 1, 0, 2000), -(8150201, 2290072, 1, 1, 0, 2000), -(8150201, 2290078, 1, 1, 0, 2000), -(8150201, 2290117, 1, 1, 0, 2000), -(8150300, 2290003, 1, 1, 0, 2000), -(8150300, 2290033, 1, 1, 0, 2000), -(8150300, 2290111, 1, 1, 0, 2000), -(8150300, 2290120, 1, 1, 0, 2000), -(8150300, 2290127, 1, 1, 0, 6000), -(8150301, 2290023, 1, 1, 0, 2000), -(8150301, 2290029, 1, 1, 0, 2000), -(8150301, 2290101, 1, 1, 0, 2000), -(8150301, 2290107, 1, 1, 0, 2000), -(8150302, 2290010, 1, 1, 0, 2000), -(8150302, 2290019, 1, 1, 0, 2000), -(8150302, 2290026, 1, 1, 0, 2000), -(8150302, 2290076, 1, 1, 0, 2000), -(8150302, 2290085, 1, 1, 0, 2000), -(8150302, 2290096, 1, 1, 0, 2000), -(8150302, 2290113, 1, 1, 0, 2000), -(8150302, 2290119, 1, 1, 0, 2000), -(8150302, 2290128, 1, 1, 0, 6000), -(8160000, 2290017, 1, 1, 0, 2000), -(8160000, 2290045, 1, 1, 0, 2000), -(8160000, 2290065, 1, 1, 0, 2000), -(8160000, 2290067, 1, 1, 0, 2000), -(8160000, 2290081, 1, 1, 0, 2000), -(8170000, 2290012, 1, 1, 0, 2000), -(8170000, 2290086, 1, 1, 0, 2000), -(8170000, 2290087, 1, 1, 0, 2000), -(8170000, 2290134, 1, 1, 0, 6000), -(8180000, 2290002, 1, 1, 0, 40000), -(8180000, 2290003, 1, 1, 0, 40000), -(8180000, 2290014, 1, 1, 0, 40000), -(8180000, 2290015, 1, 1, 0, 40000), -(8180000, 2290030, 1, 1, 0, 40000), -(8180000, 2290035, 1, 1, 0, 40000), -(8180000, 2290036, 1, 1, 0, 40000), -(8180000, 2290063, 1, 1, 0, 40000), -(8180000, 2290080, 1, 1, 0, 40000), -(8180000, 2290098, 1, 1, 0, 40000), -(8180000, 2290101, 1, 1, 0, 40000), -(8180000, 2290117, 1, 1, 0, 40000), -(8180000, 2290130, 1, 1, 0, 120000), -(8180001, 2290018, 1, 1, 0, 40000), -(8180001, 2290019, 1, 1, 0, 40000), -(8180001, 2290032, 1, 1, 0, 40000), -(8180001, 2290042, 1, 1, 0, 40000), -(8180001, 2290058, 1, 1, 0, 40000), -(8180001, 2290059, 1, 1, 0, 40000), -(8180001, 2290068, 1, 1, 0, 40000), -(8180001, 2290069, 1, 1, 0, 40000), -(8180001, 2290072, 1, 1, 0, 40000), -(8180001, 2290092, 1, 1, 0, 40000), -(8180001, 2290099, 1, 1, 0, 40000), -(8180001, 2290100, 1, 1, 0, 40000), -(8180001, 2290102, 1, 1, 0, 40000), -(8180001, 2290119, 1, 1, 0, 40000), -(8180001, 2290128, 1, 1, 0, 120000), -(8190000, 2280016, 1, 1, 0, 6000), -(8190000, 2290030, 1, 1, 0, 2000), -(8190000, 2290044, 1, 1, 0, 2000), -(8190000, 2290054, 1, 1, 0, 2000), -(8190000, 2290066, 1, 1, 0, 2000), -(8190000, 2290075, 1, 1, 0, 2000), -(8190000, 2290092, 1, 1, 0, 2000), -(8190000, 2290103, 1, 1, 0, 2000), -(8190002, 2290000, 1, 1, 0, 2000), -(8190002, 2290008, 1, 1, 0, 2000), -(8190002, 2290018, 1, 1, 0, 2000), -(8190002, 2290038, 1, 1, 0, 2000), -(8190002, 2290060, 1, 1, 0, 2000), -(8190002, 2290080, 1, 1, 0, 2000), -(8190002, 2290124, 1, 1, 0, 2000), -(8190003, 2280013, 1, 1, 0, 6000), -(8190003, 2290007, 1, 1, 0, 2000), -(8190003, 2290012, 1, 1, 0, 2000), -(8190003, 2290014, 1, 1, 0, 2000), -(8190003, 2290033, 1, 1, 0, 2000), -(8190003, 2290045, 1, 1, 0, 2000), -(8190003, 2290050, 1, 1, 0, 2000), -(8190003, 2290055, 1, 1, 0, 2000), -(8190003, 2290062, 1, 1, 0, 2000), -(8190003, 2290063, 1, 1, 0, 2000), -(8190003, 2290070, 1, 1, 0, 2000), -(8190003, 2290086, 1, 1, 0, 2000), -(8190003, 2290108, 1, 1, 0, 2000), -(8190003, 2290133, 1, 1, 0, 6000), -(8190004, 2290002, 1, 1, 0, 2000), -(8190004, 2290009, 1, 1, 0, 2000), -(8190004, 2290021, 1, 1, 0, 2000), -(8190004, 2290034, 1, 1, 0, 2000), -(8190004, 2290041, 1, 1, 0, 2000), -(8190004, 2290052, 1, 1, 0, 2000), -(8190004, 2290053, 1, 1, 0, 2000), -(8190004, 2290058, 1, 1, 0, 2000), -(8190004, 2290068, 1, 1, 0, 2000), -(8190004, 2290071, 1, 1, 0, 2000), -(8190004, 2290073, 1, 1, 0, 2000), -(8190004, 2290090, 1, 1, 0, 2000), -(8190004, 2290112, 1, 1, 0, 2000), -(8190004, 2290121, 1, 1, 0, 2000), -(8190004, 2290130, 1, 1, 0, 6000), -(8190005, 2290000, 1, 1, 0, 2000), -(8190005, 2290008, 1, 1, 0, 2000), -(8190005, 2290018, 1, 1, 0, 2000), -(8190005, 2290038, 1, 1, 0, 2000), -(8190005, 2290060, 1, 1, 0, 2000), -(8190005, 2290080, 1, 1, 0, 2000), -(8190005, 2290124, 1, 1, 0, 2000), -(8200000, 2290005, 1, 1, 0, 2000), -(8200000, 2290011, 1, 1, 0, 2000), -(8200000, 2290114, 1, 1, 0, 2000), -(8200001, 2280015, 1, 1, 0, 6000), -(8200001, 2290050, 1, 1, 0, 2000), -(8200001, 2290059, 1, 1, 0, 2000), -(8200001, 2290065, 1, 1, 0, 2000), -(8200001, 2290129, 1, 1, 0, 6000), -(8200002, 2290062, 1, 1, 0, 2000), -(8200002, 2290066, 1, 1, 0, 2000), -(8200002, 2290070, 1, 1, 0, 2000), -(8200002, 2290131, 1, 1, 0, 6000), -(8200002, 2290139, 1, 1, 0, 6000), -(8200003, 2290012, 1, 1, 0, 2000), -(8200003, 2290056, 1, 1, 0, 2000), -(8200003, 2290071, 1, 1, 0, 2000), -(8200003, 2290101, 1, 1, 0, 2000), -(8200003, 2290136, 1, 1, 0, 6000), -(8200004, 2280016, 1, 1, 0, 6000), -(8200004, 2290069, 1, 1, 0, 2000), -(8200004, 2290072, 1, 1, 0, 2000), -(8200004, 2290073, 1, 1, 0, 2000), -(8200004, 2290127, 1, 1, 0, 6000), -(8200004, 2290134, 1, 1, 0, 6000), -(8200005, 2280014, 1, 1, 0, 6000), -(8200005, 2290078, 1, 1, 0, 2000), -(8200005, 2290079, 1, 1, 0, 2000), -(8200005, 2290095, 1, 1, 0, 2000), -(8200006, 2290003, 1, 1, 0, 2000), -(8200006, 2290064, 1, 1, 0, 2000), -(8200006, 2290076, 1, 1, 0, 2000), -(8200006, 2290077, 1, 1, 0, 2000), -(8200006, 2290129, 1, 1, 0, 6000), -(8200006, 2290138, 1, 1, 0, 6000), -(8200007, 2290006, 1, 1, 0, 2000), -(8200007, 2290007, 1, 1, 0, 2000), -(8200007, 2290011, 1, 1, 0, 2000), -(8200007, 2290016, 1, 1, 0, 2000), -(8200007, 2290125, 1, 1, 0, 2000), -(8200007, 2290136, 1, 1, 0, 6000), -(8200008, 2290006, 1, 1, 0, 2000), -(8200008, 2290051, 1, 1, 0, 2000), -(8200008, 2290121, 1, 1, 0, 2000), -(8200008, 2290122, 1, 1, 0, 2000), -(8200008, 2290133, 1, 1, 0, 6000), -(8200009, 2290013, 1, 1, 0, 2000), -(8200009, 2290016, 1, 1, 0, 2000), -(8200009, 2290031, 1, 1, 0, 2000), -(8200009, 2290039, 1, 1, 0, 2000), -(8200010, 2290026, 1, 1, 0, 2000), -(8200010, 2290059, 1, 1, 0, 2000), -(8200010, 2290088, 1, 1, 0, 2000), -(8200010, 2290089, 1, 1, 0, 2000), -(8200010, 2290127, 1, 1, 0, 6000), -(8200011, 2290001, 1, 1, 0, 3000), -(8200011, 2290040, 1, 1, 0, 3000), -(8200011, 2290046, 1, 1, 0, 3000), -(8200011, 2290048, 1, 1, 0, 3000), -(8200011, 2290049, 1, 1, 0, 3000), -(8200011, 2290114, 1, 1, 0, 3000), -(8200011, 2290137, 1, 1, 0, 9000), -(8200012, 2290041, 1, 1, 0, 3000), -(8200012, 2290092, 1, 1, 0, 3000), -(8200012, 2290093, 1, 1, 0, 3000), -(8200012, 2290115, 1, 1, 0, 3000), -(8200012, 2290137, 1, 1, 0, 9000), -(8200012, 2290139, 1, 1, 0, 9000), -(8220002, 2290020, 1, 1, 0, 40000), -(8220002, 2290081, 1, 1, 0, 40000), -(8220002, 2290085, 1, 1, 0, 40000), -(8220002, 2290133, 1, 1, 0, 120000), -(8220003, 2290006, 1, 1, 0, 40000), -(8220003, 2290030, 1, 1, 0, 40000), -(8220003, 2290031, 1, 1, 0, 40000), -(8220003, 2290032, 1, 1, 0, 40000), -(8220003, 2290033, 1, 1, 0, 40000), -(8220003, 2290060, 1, 1, 0, 40000), -(8220003, 2290061, 1, 1, 0, 40000), -(8220003, 2290076, 1, 1, 0, 40000), -(8220003, 2290077, 1, 1, 0, 40000), -(8220003, 2290104, 1, 1, 0, 40000), -(8220003, 2290105, 1, 1, 0, 40000), -(8220003, 2290117, 1, 1, 0, 40000), -(8220003, 2290118, 1, 1, 0, 40000), -(8220004, 2290018, 1, 1, 0, 40000), -(8220004, 2290019, 1, 1, 0, 40000), -(8220004, 2290024, 1, 1, 0, 40000), -(8220004, 2290025, 1, 1, 0, 40000), -(8220004, 2290058, 1, 1, 0, 40000), -(8220004, 2290059, 1, 1, 0, 40000), -(8220004, 2290076, 1, 1, 0, 40000), -(8220004, 2290077, 1, 1, 0, 40000), -(8220004, 2290106, 1, 1, 0, 40000), -(8220004, 2290127, 1, 1, 0, 120000), -(8220004, 2290134, 1, 1, 0, 120000), -(8220005, 2290002, 1, 1, 0, 60000), -(8220005, 2290003, 1, 1, 0, 60000), -(8220005, 2290036, 1, 1, 0, 60000), -(8220005, 2290037, 1, 1, 0, 60000), -(8220005, 2290055, 1, 1, 0, 60000), -(8220005, 2290080, 1, 1, 0, 60000), -(8220005, 2290099, 1, 1, 0, 60000), -(8220005, 2290131, 1, 1, 0, 180000), -(8220005, 2290136, 1, 1, 0, 180000), -(8220006, 2290012, 1, 1, 0, 80000), -(8220006, 2290013, 1, 1, 0, 80000), -(8220006, 2290042, 1, 1, 0, 80000), -(8220006, 2290043, 1, 1, 0, 80000), -(8220006, 2290060, 1, 1, 0, 80000), -(8220006, 2290061, 1, 1, 0, 80000), -(8220006, 2290090, 1, 1, 0, 80000), -(8220006, 2290119, 1, 1, 0, 80000), -(8220006, 2290120, 1, 1, 0, 80000), -(8220006, 2290135, 1, 1, 0, 240000), -(8220006, 2290138, 1, 1, 0, 240000), -(8220007, 2290035, 1, 1, 0, 40000), -(8220007, 2290091, 1, 1, 0, 40000), -(8220007, 2290108, 1, 1, 0, 40000), -(8220009, 2290031, 1, 1, 0, 40000), -(8220009, 2290129, 1, 1, 0, 120000), -(8220015, 2280004, 1, 1, 0, 40000), -(8220015, 2280005, 1, 1, 0, 40000), -(8220015, 2280006, 1, 1, 0, 40000), -(8500002, 2280007, 1, 1, 0, 60000), -(8500002, 2280008, 1, 1, 0, 60000), -(8500002, 2280009, 1, 1, 0, 60000), -(8500002, 2280010, 1, 1, 0, 60000), -(8500002, 2290006, 1, 1, 0, 60000), -(8500002, 2290010, 1, 1, 0, 60000), -(8500002, 2290011, 1, 1, 0, 60000), -(8500002, 2290013, 1, 1, 0, 60000), -(8500002, 2290028, 1, 1, 0, 60000), -(8500002, 2290037, 1, 1, 0, 60000), -(8500002, 2290043, 1, 1, 0, 60000), -(8500002, 2290051, 1, 1, 0, 60000), -(8500002, 2290056, 1, 1, 0, 60000), -(8500002, 2290061, 1, 1, 0, 60000), -(8500002, 2290066, 1, 1, 0, 60000), -(8500002, 2290071, 1, 1, 0, 60000), -(8500002, 2290078, 1, 1, 0, 60000), -(8500002, 2290089, 1, 1, 0, 60000), -(8500002, 2290091, 1, 1, 0, 60000), -(8500002, 2290104, 1, 1, 0, 60000), -(8500002, 2290107, 1, 1, 0, 60000), -(8500002, 2290121, 1, 1, 0, 60000), -(8500002, 2290123, 1, 1, 0, 60000), -(8500002, 2290126, 1, 1, 0, 180000), -(8500002, 2290129, 1, 1, 0, 180000), -(8510000, 2280007, 1, 1, 0, 40000), -(8510000, 2280008, 1, 1, 0, 40000), -(8510000, 2280009, 1, 1, 0, 40000), -(8510000, 2280010, 1, 1, 0, 40000), -(8510000, 2290000, 1, 1, 0, 40000), -(8510000, 2290001, 1, 1, 0, 40000), -(8510000, 2290004, 1, 1, 0, 40000), -(8510000, 2290005, 1, 1, 0, 40000), -(8510000, 2290024, 1, 1, 0, 40000), -(8510000, 2290025, 1, 1, 0, 40000), -(8510000, 2290026, 1, 1, 0, 40000), -(8510000, 2290027, 1, 1, 0, 40000), -(8510000, 2290052, 1, 1, 0, 40000), -(8510000, 2290053, 1, 1, 0, 40000), -(8510000, 2290054, 1, 1, 0, 40000), -(8510000, 2290055, 1, 1, 0, 40000), -(8510000, 2290076, 1, 1, 0, 40000), -(8510000, 2290077, 1, 1, 0, 40000), -(8510000, 2290082, 1, 1, 0, 40000), -(8510000, 2290083, 1, 1, 0, 40000), -(8510000, 2290097, 1, 1, 0, 40000), -(8510000, 2290099, 1, 1, 0, 40000), -(8510000, 2290106, 1, 1, 0, 40000), -(8510000, 2290108, 1, 1, 0, 40000), -(8510000, 2290112, 1, 1, 0, 40000), -(8510000, 2290114, 1, 1, 0, 40000), -(8510000, 2290122, 1, 1, 0, 40000), -(8510000, 2290124, 1, 1, 0, 40000), -(8510000, 2290132, 1, 1, 0, 120000), -(8520000, 2280007, 1, 1, 0, 40000), -(8520000, 2280008, 1, 1, 0, 40000), -(8520000, 2280009, 1, 1, 0, 40000), -(8520000, 2280010, 1, 1, 0, 40000), -(8520000, 2290000, 1, 1, 0, 40000), -(8520000, 2290001, 1, 1, 0, 40000), -(8520000, 2290004, 1, 1, 0, 40000), -(8520000, 2290005, 1, 1, 0, 40000), -(8520000, 2290024, 1, 1, 0, 40000), -(8520000, 2290025, 1, 1, 0, 40000), -(8520000, 2290026, 1, 1, 0, 40000), -(8520000, 2290027, 1, 1, 0, 40000), -(8520000, 2290052, 1, 1, 0, 40000), -(8520000, 2290053, 1, 1, 0, 40000), -(8520000, 2290054, 1, 1, 0, 40000), -(8520000, 2290055, 1, 1, 0, 40000), -(8520000, 2290076, 1, 1, 0, 40000), -(8520000, 2290077, 1, 1, 0, 40000), -(8520000, 2290082, 1, 1, 0, 40000), -(8520000, 2290083, 1, 1, 0, 40000), -(8520000, 2290097, 1, 1, 0, 40000), -(8520000, 2290099, 1, 1, 0, 40000), -(8520000, 2290106, 1, 1, 0, 40000), -(8520000, 2290108, 1, 1, 0, 40000), -(8520000, 2290112, 1, 1, 0, 40000), -(8520000, 2290114, 1, 1, 0, 40000), -(8520000, 2290122, 1, 1, 0, 40000), -(8520000, 2290124, 1, 1, 0, 40000), -(8520000, 2290132, 1, 1, 0, 120000), -(8800002, 2280007, 1, 1, 0, 80000), -(8800002, 2280008, 1, 1, 0, 80000), -(8800002, 2280009, 1, 1, 0, 80000), -(8800002, 2280010, 1, 1, 0, 80000), -(8800002, 2280013, 1, 1, 0, 240000), -(8800002, 2280014, 1, 1, 0, 240000), -(8800002, 2280015, 1, 1, 0, 240000), -(8800002, 2280016, 1, 1, 0, 240000), -(8800002, 2290006, 1, 1, 0, 80000), -(8800002, 2290007, 1, 1, 0, 80000), -(8800002, 2290016, 1, 1, 0, 80000), -(8800002, 2290020, 1, 1, 0, 80000), -(8800002, 2290022, 1, 1, 0, 80000), -(8800002, 2290024, 1, 1, 0, 80000), -(8800002, 2290028, 1, 1, 0, 80000), -(8800002, 2290029, 1, 1, 0, 80000), -(8800002, 2290040, 1, 1, 0, 80000), -(8800002, 2290046, 1, 1, 0, 80000), -(8800002, 2290048, 1, 1, 0, 80000), -(8800002, 2290056, 1, 1, 0, 80000), -(8800002, 2290057, 1, 1, 0, 80000), -(8800002, 2290058, 1, 1, 0, 80000), -(8800002, 2290064, 1, 1, 0, 80000), -(8800002, 2290067, 1, 1, 0, 80000), -(8800002, 2290074, 1, 1, 0, 80000), -(8800002, 2290079, 1, 1, 0, 80000), -(8800002, 2290084, 1, 1, 0, 80000), -(8800002, 2290094, 1, 1, 0, 80000), -(8800002, 2290110, 1, 1, 0, 80000), -(8800002, 2290115, 1, 1, 0, 80000), -(8810018, 2290017, 1, 1, 0, 100000), -(8810018, 2290021, 1, 1, 0, 100000), -(8810018, 2290023, 1, 1, 0, 100000), -(8810018, 2290041, 1, 1, 0, 100000), -(8810018, 2290047, 1, 1, 0, 100000), -(8810018, 2290049, 1, 1, 0, 100000), -(8810018, 2290065, 1, 1, 0, 100000), -(8810018, 2290075, 1, 1, 0, 100000), -(8810018, 2290085, 1, 1, 0, 100000), -(8810018, 2290095, 1, 1, 0, 100000), -(8810018, 2290096, 1, 1, 0, 100000), -(8810018, 2290111, 1, 1, 0, 100000), -(8810018, 2290116, 1, 1, 0, 100000), -(8810018, 2290125, 1, 1, 0, 100000), -(8810018, 2290133, 1, 1, 0, 300000), -(8810018, 2290137, 1, 1, 0, 300000), -(8810018, 2290139, 1, 1, 0, 300000), -(8820000, 2290010, 1, 1, 0, 120000), -(8820000, 2290022, 1, 1, 0, 120000), -(8820000, 2290040, 1, 1, 0, 120000), -(8820000, 2290046, 1, 1, 0, 120000), -(8820000, 2290048, 1, 1, 0, 120000), -(8820000, 2290052, 1, 1, 0, 120000), -(8820000, 2290084, 1, 1, 0, 120000), -(8820000, 2290090, 1, 1, 0, 120000), -(8820000, 2290106, 1, 1, 0, 120000), -(8820000, 2290119, 1, 1, 0, 120000), -(8820001, 2290010, 1, 1, 0, 120000), -(8820001, 2290022, 1, 1, 0, 120000), -(8820001, 2290040, 1, 1, 0, 120000), -(8820001, 2290046, 1, 1, 0, 120000), -(8820001, 2290048, 1, 1, 0, 120000), -(8820001, 2290052, 1, 1, 0, 120000), -(8820001, 2290084, 1, 1, 0, 120000), -(8820001, 2290090, 1, 1, 0, 120000), -(8820001, 2290106, 1, 1, 0, 120000), -(8820001, 2290119, 1, 1, 0, 120000), -(9300028, 2280015, 1, 1, 0, 120000), -(9300028, 2290026, 1, 1, 0, 40000), -(9300028, 2290064, 1, 1, 0, 40000), -(9300028, 2290075, 1, 1, 0, 40000), -(9300028, 2290093, 1, 1, 0, 40000), -(9300028, 2290111, 1, 1, 0, 40000), -(9300094, 2280004, 1, 1, 0, 40000), -(9300094, 2280005, 1, 1, 0, 40000), -(9300094, 2280006, 1, 1, 0, 40000), -(9300095, 2280004, 1, 1, 0, 2000), -(9300095, 2280005, 1, 1, 0, 2000), -(9300095, 2280006, 1, 1, 0, 2000), -(9303016, 2290006, 1, 1, 0, 2000), -(9303016, 2290030, 1, 1, 0, 2000), -(9303016, 2290032, 1, 1, 0, 2000), -(9303016, 2290060, 1, 1, 0, 2000), -(9303016, 2290076, 1, 1, 0, 2000), -(9303016, 2290104, 1, 1, 0, 2000), -(9303016, 2290117, 1, 1, 0, 2000), -(9400014, 2290053, 1, 1, 0, 40000), -(9400014, 2290087, 1, 1, 0, 40000), -(9400014, 2290112, 1, 1, 0, 40000), -(9400014, 2290122, 1, 1, 0, 40000), -(9400120, 2290045, 1, 1, 0, 40000), -(9400121, 2280014, 1, 1, 0, 180000), -(9400121, 2290081, 1, 1, 0, 60000), -(9400121, 2290087, 1, 1, 0, 60000), -(9400121, 2290101, 1, 1, 0, 60000), -(9400121, 2290103, 1, 1, 0, 60000), -(9400122, 2290007, 1, 1, 0, 40000), -(9400122, 2290062, 1, 1, 0, 40000), -(9400122, 2290116, 1, 1, 0, 40000), -(9400300, 2290045, 1, 1, 0, 120000), -(9400300, 2290055, 1, 1, 0, 120000), -(9400300, 2290063, 1, 1, 0, 120000), -(9400300, 2290079, 1, 1, 0, 120000), -(9400300, 2290081, 1, 1, 0, 120000), -(9400300, 2290096, 1, 1, 0, 120000), -(9400514, 2290023, 1, 1, 0, 40000), -(9400514, 2290057, 1, 1, 0, 40000), -(9400514, 2290088, 1, 1, 0, 40000), -(9400514, 2290095, 1, 1, 0, 40000), -(9400514, 2290115, 1, 1, 0, 40000), -(9400514, 2290139, 1, 1, 0, 120000), -(9400549, 2290001, 1, 1, 0, 40000), -(9400549, 2290020, 1, 1, 0, 40000), -(9400549, 2290045, 1, 1, 0, 40000), -(9400549, 2290057, 1, 1, 0, 40000), -(9400549, 2290086, 1, 1, 0, 40000), -(9400575, 2290009, 1, 1, 0, 40000), -(9400575, 2290051, 1, 1, 0, 40000), -(9400575, 2290081, 1, 1, 0, 40000), -(9400575, 2290087, 1, 1, 0, 40000), -(9400575, 2290107, 1, 1, 0, 40000), -(9400575, 2290123, 1, 1, 0, 40000), -(9400580, 2290004, 1, 1, 0, 2000), -(9400580, 2290024, 1, 1, 0, 2000), -(9400580, 2290083, 1, 1, 0, 2000), -(9400580, 2290087, 1, 1, 0, 2000), -(9400580, 2290103, 1, 1, 0, 2000), -(9400580, 2290121, 1, 1, 0, 2000), -(9400582, 2290005, 1, 1, 0, 2000), -(9400582, 2290010, 1, 1, 0, 2000), -(9400582, 2290029, 1, 1, 0, 2000), -(9400582, 2290047, 1, 1, 0, 2000), -(9400582, 2290049, 1, 1, 0, 2000), -(9400582, 2290074, 1, 1, 0, 2000), -(9400582, 2290079, 1, 1, 0, 2000), -(9400582, 2290081, 1, 1, 0, 2000), -(9400582, 2290135, 1, 1, 0, 6000), -(9400590, 2290088, 1, 1, 0, 60000), -(9400590, 2290125, 1, 1, 0, 60000), -(9400590, 2290135, 1, 1, 0, 180000), -(9400591, 2290039, 1, 1, 0, 60000), -(9400591, 2290074, 1, 1, 0, 60000), -(9400591, 2290113, 1, 1, 0, 60000), -(9400592, 2290047, 1, 1, 0, 60000), -(9400592, 2290123, 1, 1, 0, 60000), -(9400592, 2290131, 1, 1, 0, 180000), -(9400593, 2290069, 1, 1, 0, 60000), -(9400593, 2290093, 1, 1, 0, 60000), -(9400593, 2290138, 1, 1, 0, 180000), -(9420513, 2290039, 1, 1, 0, 40000), -(9420513, 2290100, 1, 1, 0, 40000), -(9420513, 2290108, 1, 1, 0, 40000), -(9420513, 2290118, 1, 1, 0, 40000), -(9420513, 2290138, 1, 1, 0, 120000), +(7090000, 2290087, 1, 1, 0, 10000), +(8090000, 2290045, 1, 1, 0, 10000), +(8140103, 2290044, 1, 1, 0, 500), +(8140511, 2290009, 1, 1, 0, 500), +(8140511, 2290050, 1, 1, 0, 500), +(8140511, 2290083, 1, 1, 0, 500), +(8140511, 2290134, 1, 1, 0, 1500), +(8140512, 2290013, 1, 1, 0, 500), +(8140512, 2290067, 1, 1, 0, 500), +(8140512, 2290082, 1, 1, 0, 500), +(8140512, 2290097, 1, 1, 0, 500), +(8140512, 2290116, 1, 1, 0, 500), +(8140512, 2290131, 1, 1, 0, 1500), +(8140600, 2290132, 1, 1, 0, 1500), +(8140700, 2290106, 1, 1, 0, 500), +(8140700, 2290126, 1, 1, 0, 1500), +(8140701, 2290122, 1, 1, 0, 500), +(8140702, 2290112, 1, 1, 0, 500), +(8140703, 2290088, 1, 1, 0, 500), +(8140703, 2290099, 1, 1, 0, 500), +(8141000, 2290082, 1, 1, 0, 500), +(8141000, 2290097, 1, 1, 0, 500), +(8141100, 2280005, 1, 1, 0, 500), +(8141300, 2290098, 1, 1, 0, 500), +(8142100, 2290032, 1, 1, 0, 500), +(8142100, 2290082, 1, 1, 0, 500), +(8142100, 2290114, 1, 1, 0, 500), +(8143000, 2280004, 1, 1, 0, 500), +(8150000, 2280013, 1, 1, 0, 30000), +(8150000, 2290070, 1, 1, 0, 10000), +(8150000, 2290091, 1, 1, 0, 10000), +(8150100, 2290042, 1, 1, 0, 500), +(8150100, 2290053, 1, 1, 0, 500), +(8150100, 2290073, 1, 1, 0, 500), +(8150100, 2290102, 1, 1, 0, 500), +(8150100, 2290118, 1, 1, 0, 500), +(8150101, 2290017, 1, 1, 0, 500), +(8150101, 2290021, 1, 1, 0, 500), +(8150101, 2290035, 1, 1, 0, 500), +(8150101, 2290042, 1, 1, 0, 500), +(8150101, 2290052, 1, 1, 0, 500), +(8150101, 2290102, 1, 1, 0, 500), +(8150200, 2290024, 1, 1, 0, 500), +(8150200, 2290100, 1, 1, 0, 500), +(8150200, 2290135, 1, 1, 0, 1500), +(8150201, 2290004, 1, 1, 0, 500), +(8150201, 2290006, 1, 1, 0, 500), +(8150201, 2290024, 1, 1, 0, 500), +(8150201, 2290036, 1, 1, 0, 500), +(8150201, 2290056, 1, 1, 0, 500), +(8150201, 2290072, 1, 1, 0, 500), +(8150201, 2290078, 1, 1, 0, 500), +(8150201, 2290117, 1, 1, 0, 500), +(8150300, 2290003, 1, 1, 0, 500), +(8150300, 2290033, 1, 1, 0, 500), +(8150300, 2290111, 1, 1, 0, 500), +(8150300, 2290120, 1, 1, 0, 500), +(8150300, 2290127, 1, 1, 0, 1500), +(8150301, 2290023, 1, 1, 0, 500), +(8150301, 2290029, 1, 1, 0, 500), +(8150301, 2290101, 1, 1, 0, 500), +(8150301, 2290107, 1, 1, 0, 500), +(8150302, 2290010, 1, 1, 0, 500), +(8150302, 2290019, 1, 1, 0, 500), +(8150302, 2290026, 1, 1, 0, 500), +(8150302, 2290076, 1, 1, 0, 500), +(8150302, 2290085, 1, 1, 0, 500), +(8150302, 2290096, 1, 1, 0, 500), +(8150302, 2290113, 1, 1, 0, 500), +(8150302, 2290119, 1, 1, 0, 500), +(8150302, 2290128, 1, 1, 0, 1500), +(8160000, 2290017, 1, 1, 0, 500), +(8160000, 2290045, 1, 1, 0, 500), +(8160000, 2290065, 1, 1, 0, 500), +(8160000, 2290067, 1, 1, 0, 500), +(8160000, 2290081, 1, 1, 0, 500), +(8170000, 2290012, 1, 1, 0, 500), +(8170000, 2290086, 1, 1, 0, 500), +(8170000, 2290087, 1, 1, 0, 500), +(8170000, 2290134, 1, 1, 0, 1500), +(8180000, 2290002, 1, 1, 0, 10000), +(8180000, 2290003, 1, 1, 0, 10000), +(8180000, 2290014, 1, 1, 0, 10000), +(8180000, 2290015, 1, 1, 0, 10000), +(8180000, 2290030, 1, 1, 0, 10000), +(8180000, 2290035, 1, 1, 0, 10000), +(8180000, 2290036, 1, 1, 0, 10000), +(8180000, 2290063, 1, 1, 0, 10000), +(8180000, 2290080, 1, 1, 0, 10000), +(8180000, 2290098, 1, 1, 0, 10000), +(8180000, 2290101, 1, 1, 0, 10000), +(8180000, 2290117, 1, 1, 0, 10000), +(8180000, 2290130, 1, 1, 0, 30000), +(8180001, 2290018, 1, 1, 0, 10000), +(8180001, 2290019, 1, 1, 0, 10000), +(8180001, 2290032, 1, 1, 0, 10000), +(8180001, 2290042, 1, 1, 0, 10000), +(8180001, 2290058, 1, 1, 0, 10000), +(8180001, 2290059, 1, 1, 0, 10000), +(8180001, 2290068, 1, 1, 0, 10000), +(8180001, 2290069, 1, 1, 0, 10000), +(8180001, 2290072, 1, 1, 0, 10000), +(8180001, 2290092, 1, 1, 0, 10000), +(8180001, 2290099, 1, 1, 0, 10000), +(8180001, 2290100, 1, 1, 0, 10000), +(8180001, 2290102, 1, 1, 0, 10000), +(8180001, 2290119, 1, 1, 0, 10000), +(8180001, 2290128, 1, 1, 0, 30000), +(8190000, 2280016, 1, 1, 0, 1500), +(8190000, 2290030, 1, 1, 0, 500), +(8190000, 2290044, 1, 1, 0, 500), +(8190000, 2290054, 1, 1, 0, 500), +(8190000, 2290066, 1, 1, 0, 500), +(8190000, 2290075, 1, 1, 0, 500), +(8190000, 2290092, 1, 1, 0, 500), +(8190000, 2290103, 1, 1, 0, 500), +(8190002, 2290000, 1, 1, 0, 500), +(8190002, 2290008, 1, 1, 0, 500), +(8190002, 2290018, 1, 1, 0, 500), +(8190002, 2290038, 1, 1, 0, 500), +(8190002, 2290060, 1, 1, 0, 500), +(8190002, 2290080, 1, 1, 0, 500), +(8190002, 2290124, 1, 1, 0, 500), +(8190003, 2280013, 1, 1, 0, 1500), +(8190003, 2290007, 1, 1, 0, 500), +(8190003, 2290012, 1, 1, 0, 500), +(8190003, 2290014, 1, 1, 0, 500), +(8190003, 2290033, 1, 1, 0, 500), +(8190003, 2290045, 1, 1, 0, 500), +(8190003, 2290050, 1, 1, 0, 500), +(8190003, 2290055, 1, 1, 0, 500), +(8190003, 2290062, 1, 1, 0, 500), +(8190003, 2290063, 1, 1, 0, 500), +(8190003, 2290070, 1, 1, 0, 500), +(8190003, 2290086, 1, 1, 0, 500), +(8190003, 2290108, 1, 1, 0, 500), +(8190003, 2290133, 1, 1, 0, 1500), +(8190004, 2290002, 1, 1, 0, 500), +(8190004, 2290009, 1, 1, 0, 500), +(8190004, 2290021, 1, 1, 0, 500), +(8190004, 2290034, 1, 1, 0, 500), +(8190004, 2290041, 1, 1, 0, 500), +(8190004, 2290052, 1, 1, 0, 500), +(8190004, 2290053, 1, 1, 0, 500), +(8190004, 2290058, 1, 1, 0, 500), +(8190004, 2290068, 1, 1, 0, 500), +(8190004, 2290071, 1, 1, 0, 500), +(8190004, 2290073, 1, 1, 0, 500), +(8190004, 2290090, 1, 1, 0, 500), +(8190004, 2290112, 1, 1, 0, 500), +(8190004, 2290121, 1, 1, 0, 500), +(8190004, 2290130, 1, 1, 0, 1500), +(8190005, 2290000, 1, 1, 0, 500), +(8190005, 2290008, 1, 1, 0, 500), +(8190005, 2290018, 1, 1, 0, 500), +(8190005, 2290038, 1, 1, 0, 500), +(8190005, 2290060, 1, 1, 0, 500), +(8190005, 2290080, 1, 1, 0, 500), +(8190005, 2290124, 1, 1, 0, 500), +(8200000, 2290005, 1, 1, 0, 500), +(8200000, 2290011, 1, 1, 0, 500), +(8200000, 2290114, 1, 1, 0, 500), +(8200001, 2280015, 1, 1, 0, 1500), +(8200001, 2290050, 1, 1, 0, 500), +(8200001, 2290059, 1, 1, 0, 500), +(8200001, 2290065, 1, 1, 0, 500), +(8200001, 2290129, 1, 1, 0, 1500), +(8200002, 2290062, 1, 1, 0, 500), +(8200002, 2290066, 1, 1, 0, 500), +(8200002, 2290070, 1, 1, 0, 500), +(8200002, 2290131, 1, 1, 0, 1500), +(8200002, 2290139, 1, 1, 0, 1500), +(8200003, 2290012, 1, 1, 0, 500), +(8200003, 2290056, 1, 1, 0, 500), +(8200003, 2290071, 1, 1, 0, 500), +(8200003, 2290101, 1, 1, 0, 500), +(8200003, 2290136, 1, 1, 0, 1500), +(8200004, 2280016, 1, 1, 0, 1500), +(8200004, 2290069, 1, 1, 0, 500), +(8200004, 2290072, 1, 1, 0, 500), +(8200004, 2290073, 1, 1, 0, 500), +(8200004, 2290127, 1, 1, 0, 1500), +(8200004, 2290134, 1, 1, 0, 1500), +(8200005, 2280014, 1, 1, 0, 1500), +(8200005, 2290078, 1, 1, 0, 500), +(8200005, 2290079, 1, 1, 0, 500), +(8200005, 2290095, 1, 1, 0, 500), +(8200006, 2290003, 1, 1, 0, 500), +(8200006, 2290064, 1, 1, 0, 500), +(8200006, 2290076, 1, 1, 0, 500), +(8200006, 2290077, 1, 1, 0, 500), +(8200006, 2290129, 1, 1, 0, 1500), +(8200006, 2290138, 1, 1, 0, 1500), +(8200007, 2290006, 1, 1, 0, 500), +(8200007, 2290007, 1, 1, 0, 500), +(8200007, 2290011, 1, 1, 0, 500), +(8200007, 2290016, 1, 1, 0, 500), +(8200007, 2290125, 1, 1, 0, 500), +(8200007, 2290136, 1, 1, 0, 1500), +(8200008, 2290006, 1, 1, 0, 500), +(8200008, 2290051, 1, 1, 0, 500), +(8200008, 2290121, 1, 1, 0, 500), +(8200008, 2290122, 1, 1, 0, 500), +(8200008, 2290133, 1, 1, 0, 1500), +(8200009, 2290013, 1, 1, 0, 500), +(8200009, 2290016, 1, 1, 0, 500), +(8200009, 2290031, 1, 1, 0, 500), +(8200009, 2290039, 1, 1, 0, 500), +(8200010, 2290026, 1, 1, 0, 500), +(8200010, 2290059, 1, 1, 0, 500), +(8200010, 2290088, 1, 1, 0, 500), +(8200010, 2290089, 1, 1, 0, 500), +(8200010, 2290127, 1, 1, 0, 1500), +(8200011, 2290001, 1, 1, 0, 750), +(8200011, 2290040, 1, 1, 0, 750), +(8200011, 2290046, 1, 1, 0, 750), +(8200011, 2290048, 1, 1, 0, 750), +(8200011, 2290049, 1, 1, 0, 750), +(8200011, 2290114, 1, 1, 0, 750), +(8200011, 2290137, 1, 1, 0, 2250), +(8200012, 2290041, 1, 1, 0, 750), +(8200012, 2290092, 1, 1, 0, 750), +(8200012, 2290093, 1, 1, 0, 750), +(8200012, 2290115, 1, 1, 0, 750), +(8200012, 2290137, 1, 1, 0, 2250), +(8200012, 2290139, 1, 1, 0, 2250), +(8220002, 2290020, 1, 1, 0, 10000), +(8220002, 2290081, 1, 1, 0, 10000), +(8220002, 2290085, 1, 1, 0, 10000), +(8220002, 2290133, 1, 1, 0, 30000), +(8220003, 2290006, 1, 1, 0, 10000), +(8220003, 2290030, 1, 1, 0, 10000), +(8220003, 2290031, 1, 1, 0, 10000), +(8220003, 2290032, 1, 1, 0, 10000), +(8220003, 2290033, 1, 1, 0, 10000), +(8220003, 2290060, 1, 1, 0, 10000), +(8220003, 2290061, 1, 1, 0, 10000), +(8220003, 2290076, 1, 1, 0, 10000), +(8220003, 2290077, 1, 1, 0, 10000), +(8220003, 2290104, 1, 1, 0, 10000), +(8220003, 2290105, 1, 1, 0, 10000), +(8220003, 2290117, 1, 1, 0, 10000), +(8220003, 2290118, 1, 1, 0, 10000), +(8220004, 2290018, 1, 1, 0, 10000), +(8220004, 2290019, 1, 1, 0, 10000), +(8220004, 2290024, 1, 1, 0, 10000), +(8220004, 2290025, 1, 1, 0, 10000), +(8220004, 2290058, 1, 1, 0, 10000), +(8220004, 2290059, 1, 1, 0, 10000), +(8220004, 2290076, 1, 1, 0, 10000), +(8220004, 2290077, 1, 1, 0, 10000), +(8220004, 2290106, 1, 1, 0, 10000), +(8220004, 2290127, 1, 1, 0, 30000), +(8220004, 2290134, 1, 1, 0, 30000), +(8220005, 2290002, 1, 1, 0, 15000), +(8220005, 2290003, 1, 1, 0, 15000), +(8220005, 2290036, 1, 1, 0, 15000), +(8220005, 2290037, 1, 1, 0, 15000), +(8220005, 2290055, 1, 1, 0, 15000), +(8220005, 2290080, 1, 1, 0, 15000), +(8220005, 2290099, 1, 1, 0, 15000), +(8220005, 2290131, 1, 1, 0, 45000), +(8220005, 2290136, 1, 1, 0, 45000), +(8220006, 2290012, 1, 1, 0, 20000), +(8220006, 2290013, 1, 1, 0, 20000), +(8220006, 2290042, 1, 1, 0, 20000), +(8220006, 2290043, 1, 1, 0, 20000), +(8220006, 2290060, 1, 1, 0, 20000), +(8220006, 2290061, 1, 1, 0, 20000), +(8220006, 2290090, 1, 1, 0, 20000), +(8220006, 2290119, 1, 1, 0, 20000), +(8220006, 2290120, 1, 1, 0, 20000), +(8220006, 2290135, 1, 1, 0, 60000), +(8220006, 2290138, 1, 1, 0, 60000), +(8220007, 2290035, 1, 1, 0, 10000), +(8220007, 2290091, 1, 1, 0, 10000), +(8220007, 2290108, 1, 1, 0, 10000), +(8220009, 2290031, 1, 1, 0, 10000), +(8220009, 2290129, 1, 1, 0, 30000), +(8220015, 2280004, 1, 1, 0, 10000), +(8220015, 2280005, 1, 1, 0, 10000), +(8220015, 2280006, 1, 1, 0, 10000), +(8500002, 2280007, 1, 1, 0, 15000), +(8500002, 2280008, 1, 1, 0, 15000), +(8500002, 2280009, 1, 1, 0, 15000), +(8500002, 2280010, 1, 1, 0, 15000), +(8500002, 2290006, 1, 1, 0, 15000), +(8500002, 2290010, 1, 1, 0, 15000), +(8500002, 2290011, 1, 1, 0, 15000), +(8500002, 2290013, 1, 1, 0, 15000), +(8500002, 2290028, 1, 1, 0, 15000), +(8500002, 2290037, 1, 1, 0, 15000), +(8500002, 2290043, 1, 1, 0, 15000), +(8500002, 2290051, 1, 1, 0, 15000), +(8500002, 2290056, 1, 1, 0, 15000), +(8500002, 2290061, 1, 1, 0, 15000), +(8500002, 2290066, 1, 1, 0, 15000), +(8500002, 2290071, 1, 1, 0, 15000), +(8500002, 2290078, 1, 1, 0, 15000), +(8500002, 2290089, 1, 1, 0, 15000), +(8500002, 2290091, 1, 1, 0, 15000), +(8500002, 2290104, 1, 1, 0, 15000), +(8500002, 2290107, 1, 1, 0, 15000), +(8500002, 2290121, 1, 1, 0, 15000), +(8500002, 2290123, 1, 1, 0, 15000), +(8500002, 2290126, 1, 1, 0, 45000), +(8500002, 2290129, 1, 1, 0, 45000), +(8510000, 2280007, 1, 1, 0, 10000), +(8510000, 2280008, 1, 1, 0, 10000), +(8510000, 2280009, 1, 1, 0, 10000), +(8510000, 2280010, 1, 1, 0, 10000), +(8510000, 2290000, 1, 1, 0, 10000), +(8510000, 2290001, 1, 1, 0, 10000), +(8510000, 2290004, 1, 1, 0, 10000), +(8510000, 2290005, 1, 1, 0, 10000), +(8510000, 2290024, 1, 1, 0, 10000), +(8510000, 2290025, 1, 1, 0, 10000), +(8510000, 2290026, 1, 1, 0, 10000), +(8510000, 2290027, 1, 1, 0, 10000), +(8510000, 2290052, 1, 1, 0, 10000), +(8510000, 2290053, 1, 1, 0, 10000), +(8510000, 2290054, 1, 1, 0, 10000), +(8510000, 2290055, 1, 1, 0, 10000), +(8510000, 2290076, 1, 1, 0, 10000), +(8510000, 2290077, 1, 1, 0, 10000), +(8510000, 2290082, 1, 1, 0, 10000), +(8510000, 2290083, 1, 1, 0, 10000), +(8510000, 2290097, 1, 1, 0, 10000), +(8510000, 2290099, 1, 1, 0, 10000), +(8510000, 2290106, 1, 1, 0, 10000), +(8510000, 2290108, 1, 1, 0, 10000), +(8510000, 2290112, 1, 1, 0, 10000), +(8510000, 2290114, 1, 1, 0, 10000), +(8510000, 2290122, 1, 1, 0, 10000), +(8510000, 2290124, 1, 1, 0, 10000), +(8510000, 2290132, 1, 1, 0, 30000), +(8520000, 2280007, 1, 1, 0, 10000), +(8520000, 2280008, 1, 1, 0, 10000), +(8520000, 2280009, 1, 1, 0, 10000), +(8520000, 2280010, 1, 1, 0, 10000), +(8520000, 2290000, 1, 1, 0, 10000), +(8520000, 2290001, 1, 1, 0, 10000), +(8520000, 2290004, 1, 1, 0, 10000), +(8520000, 2290005, 1, 1, 0, 10000), +(8520000, 2290024, 1, 1, 0, 10000), +(8520000, 2290025, 1, 1, 0, 10000), +(8520000, 2290026, 1, 1, 0, 10000), +(8520000, 2290027, 1, 1, 0, 10000), +(8520000, 2290052, 1, 1, 0, 10000), +(8520000, 2290053, 1, 1, 0, 10000), +(8520000, 2290054, 1, 1, 0, 10000), +(8520000, 2290055, 1, 1, 0, 10000), +(8520000, 2290076, 1, 1, 0, 10000), +(8520000, 2290077, 1, 1, 0, 10000), +(8520000, 2290082, 1, 1, 0, 10000), +(8520000, 2290083, 1, 1, 0, 10000), +(8520000, 2290097, 1, 1, 0, 10000), +(8520000, 2290099, 1, 1, 0, 10000), +(8520000, 2290106, 1, 1, 0, 10000), +(8520000, 2290108, 1, 1, 0, 10000), +(8520000, 2290112, 1, 1, 0, 10000), +(8520000, 2290114, 1, 1, 0, 10000), +(8520000, 2290122, 1, 1, 0, 10000), +(8520000, 2290124, 1, 1, 0, 10000), +(8520000, 2290132, 1, 1, 0, 30000), +(8800002, 2280007, 1, 1, 0, 20000), +(8800002, 2280008, 1, 1, 0, 20000), +(8800002, 2280009, 1, 1, 0, 20000), +(8800002, 2280010, 1, 1, 0, 20000), +(8800002, 2280013, 1, 1, 0, 60000), +(8800002, 2280014, 1, 1, 0, 60000), +(8800002, 2280015, 1, 1, 0, 60000), +(8800002, 2280016, 1, 1, 0, 60000), +(8800002, 2290006, 1, 1, 0, 20000), +(8800002, 2290007, 1, 1, 0, 20000), +(8800002, 2290016, 1, 1, 0, 20000), +(8800002, 2290020, 1, 1, 0, 20000), +(8800002, 2290022, 1, 1, 0, 20000), +(8800002, 2290024, 1, 1, 0, 20000), +(8800002, 2290028, 1, 1, 0, 20000), +(8800002, 2290029, 1, 1, 0, 20000), +(8800002, 2290040, 1, 1, 0, 20000), +(8800002, 2290046, 1, 1, 0, 20000), +(8800002, 2290048, 1, 1, 0, 20000), +(8800002, 2290056, 1, 1, 0, 20000), +(8800002, 2290057, 1, 1, 0, 20000), +(8800002, 2290058, 1, 1, 0, 20000), +(8800002, 2290064, 1, 1, 0, 20000), +(8800002, 2290067, 1, 1, 0, 20000), +(8800002, 2290074, 1, 1, 0, 20000), +(8800002, 2290079, 1, 1, 0, 20000), +(8800002, 2290084, 1, 1, 0, 20000), +(8800002, 2290094, 1, 1, 0, 20000), +(8800002, 2290110, 1, 1, 0, 20000), +(8800002, 2290115, 1, 1, 0, 20000), +(8810018, 2290017, 1, 1, 0, 25000), +(8810018, 2290021, 1, 1, 0, 25000), +(8810018, 2290023, 1, 1, 0, 25000), +(8810018, 2290041, 1, 1, 0, 25000), +(8810018, 2290047, 1, 1, 0, 25000), +(8810018, 2290049, 1, 1, 0, 25000), +(8810018, 2290065, 1, 1, 0, 25000), +(8810018, 2290075, 1, 1, 0, 25000), +(8810018, 2290085, 1, 1, 0, 25000), +(8810018, 2290095, 1, 1, 0, 25000), +(8810018, 2290096, 1, 1, 0, 25000), +(8810018, 2290111, 1, 1, 0, 25000), +(8810018, 2290116, 1, 1, 0, 25000), +(8810018, 2290125, 1, 1, 0, 25000), +(8810018, 2290133, 1, 1, 0, 75000), +(8810018, 2290137, 1, 1, 0, 75000), +(8810018, 2290139, 1, 1, 0, 75000), +(8820000, 2290010, 1, 1, 0, 30000), +(8820000, 2290022, 1, 1, 0, 30000), +(8820000, 2290040, 1, 1, 0, 30000), +(8820000, 2290046, 1, 1, 0, 30000), +(8820000, 2290048, 1, 1, 0, 30000), +(8820000, 2290052, 1, 1, 0, 30000), +(8820000, 2290084, 1, 1, 0, 30000), +(8820000, 2290090, 1, 1, 0, 30000), +(8820000, 2290106, 1, 1, 0, 30000), +(8820000, 2290119, 1, 1, 0, 30000), +(8820001, 2290010, 1, 1, 0, 30000), +(8820001, 2290022, 1, 1, 0, 30000), +(8820001, 2290040, 1, 1, 0, 30000), +(8820001, 2290046, 1, 1, 0, 30000), +(8820001, 2290048, 1, 1, 0, 30000), +(8820001, 2290052, 1, 1, 0, 30000), +(8820001, 2290084, 1, 1, 0, 30000), +(8820001, 2290090, 1, 1, 0, 30000), +(8820001, 2290106, 1, 1, 0, 30000), +(8820001, 2290119, 1, 1, 0, 30000), +(9300028, 2280015, 1, 1, 0, 30000), +(9300028, 2290026, 1, 1, 0, 10000), +(9300028, 2290064, 1, 1, 0, 10000), +(9300028, 2290075, 1, 1, 0, 10000), +(9300028, 2290093, 1, 1, 0, 10000), +(9300028, 2290111, 1, 1, 0, 10000), +(9300094, 2280004, 1, 1, 0, 10000), +(9300094, 2280005, 1, 1, 0, 10000), +(9300094, 2280006, 1, 1, 0, 10000), +(9300095, 2280004, 1, 1, 0, 500), +(9300095, 2280005, 1, 1, 0, 500), +(9300095, 2280006, 1, 1, 0, 500), +(9303016, 2290006, 1, 1, 0, 500), +(9303016, 2290030, 1, 1, 0, 500), +(9303016, 2290032, 1, 1, 0, 500), +(9303016, 2290060, 1, 1, 0, 500), +(9303016, 2290076, 1, 1, 0, 500), +(9303016, 2290104, 1, 1, 0, 500), +(9303016, 2290117, 1, 1, 0, 500), +(9400014, 2290053, 1, 1, 0, 10000), +(9400014, 2290087, 1, 1, 0, 10000), +(9400014, 2290112, 1, 1, 0, 10000), +(9400014, 2290122, 1, 1, 0, 10000), +(9400120, 2290045, 1, 1, 0, 10000), +(9400121, 2280014, 1, 1, 0, 45000), +(9400121, 2290081, 1, 1, 0, 15000), +(9400121, 2290087, 1, 1, 0, 15000), +(9400121, 2290101, 1, 1, 0, 15000), +(9400121, 2290103, 1, 1, 0, 15000), +(9400122, 2290007, 1, 1, 0, 10000), +(9400122, 2290062, 1, 1, 0, 10000), +(9400122, 2290116, 1, 1, 0, 10000), +(9400300, 2290045, 1, 1, 0, 30000), +(9400300, 2290055, 1, 1, 0, 30000), +(9400300, 2290063, 1, 1, 0, 30000), +(9400300, 2290079, 1, 1, 0, 30000), +(9400300, 2290081, 1, 1, 0, 30000), +(9400300, 2290096, 1, 1, 0, 30000), +(9400514, 2290023, 1, 1, 0, 10000), +(9400514, 2290057, 1, 1, 0, 10000), +(9400514, 2290088, 1, 1, 0, 10000), +(9400514, 2290095, 1, 1, 0, 10000), +(9400514, 2290115, 1, 1, 0, 10000), +(9400514, 2290139, 1, 1, 0, 30000), +(9400549, 2290001, 1, 1, 0, 10000), +(9400549, 2290020, 1, 1, 0, 10000), +(9400549, 2290045, 1, 1, 0, 10000), +(9400549, 2290057, 1, 1, 0, 10000), +(9400549, 2290086, 1, 1, 0, 10000), +(9400575, 2290009, 1, 1, 0, 10000), +(9400575, 2290051, 1, 1, 0, 10000), +(9400575, 2290081, 1, 1, 0, 10000), +(9400575, 2290087, 1, 1, 0, 10000), +(9400575, 2290107, 1, 1, 0, 10000), +(9400575, 2290123, 1, 1, 0, 10000), +(9400580, 2290004, 1, 1, 0, 500), +(9400580, 2290024, 1, 1, 0, 500), +(9400580, 2290083, 1, 1, 0, 500), +(9400580, 2290087, 1, 1, 0, 500), +(9400580, 2290103, 1, 1, 0, 500), +(9400580, 2290121, 1, 1, 0, 500), +(9400582, 2290005, 1, 1, 0, 500), +(9400582, 2290010, 1, 1, 0, 500), +(9400582, 2290029, 1, 1, 0, 500), +(9400582, 2290047, 1, 1, 0, 500), +(9400582, 2290049, 1, 1, 0, 500), +(9400582, 2290074, 1, 1, 0, 500), +(9400582, 2290079, 1, 1, 0, 500), +(9400582, 2290081, 1, 1, 0, 500), +(9400582, 2290135, 1, 1, 0, 1500), +(9400590, 2290088, 1, 1, 0, 15000), +(9400590, 2290125, 1, 1, 0, 15000), +(9400590, 2290135, 1, 1, 0, 45000), +(9400591, 2290039, 1, 1, 0, 15000), +(9400591, 2290074, 1, 1, 0, 15000), +(9400591, 2290113, 1, 1, 0, 15000), +(9400592, 2290047, 1, 1, 0, 15000), +(9400592, 2290123, 1, 1, 0, 15000), +(9400592, 2290131, 1, 1, 0, 45000), +(9400593, 2290069, 1, 1, 0, 15000), +(9400593, 2290093, 1, 1, 0, 15000), +(9400593, 2290138, 1, 1, 0, 45000), +(9420513, 2290039, 1, 1, 0, 10000), +(9420513, 2290100, 1, 1, 0, 10000), +(9420513, 2290108, 1, 1, 0, 10000), +(9420513, 2290118, 1, 1, 0, 10000), +(9420513, 2290138, 1, 1, 0, 30000), (9420514, 2290099, 1, 1, 0, 1287), (9420517, 2290000, 1, 1, 0, 1287), (9420517, 2290008, 1, 1, 0, 1287), @@ -24470,75 +24471,75 @@ SET minimum_quantity = CASE (9420522, 2290089, 1, 1, 0, 1287), (9420522, 2290091, 1, 1, 0, 1287), (9420522, 2290107, 1, 1, 0, 1287), -(9420540, 2280006, 1, 1, 0, 2000), -(9420540, 2290119, 1, 1, 0, 2000), -(9420540, 2290120, 1, 1, 0, 2000), -(9420544, 2280007, 1, 1, 0, 80000), -(9420544, 2280008, 1, 1, 0, 80000), -(9420544, 2280009, 1, 1, 0, 80000), -(9420544, 2280010, 1, 1, 0, 80000), -(9420544, 2290002, 1, 1, 0, 80000), -(9420544, 2290015, 1, 1, 0, 80000), -(9420544, 2290022, 1, 1, 0, 80000), -(9420544, 2290027, 1, 1, 0, 80000), -(9420544, 2290034, 1, 1, 0, 80000), -(9420544, 2290052, 1, 1, 0, 80000), -(9420544, 2290054, 1, 1, 0, 80000), -(9420544, 2290089, 1, 1, 0, 80000), -(9420544, 2290094, 1, 1, 0, 80000), -(9420544, 2290098, 1, 1, 0, 80000), -(9420544, 2290105, 1, 1, 0, 80000), -(9420544, 2290110, 1, 1, 0, 80000), -(9420544, 2290119, 1, 1, 0, 80000), -(9420549, 2280007, 1, 1, 0, 80000), -(9420549, 2280008, 1, 1, 0, 80000), -(9420549, 2280009, 1, 1, 0, 80000), -(9420549, 2280010, 1, 1, 0, 80000), -(9420549, 2290002, 1, 1, 0, 80000), -(9420549, 2290015, 1, 1, 0, 80000), -(9420549, 2290022, 1, 1, 0, 80000), -(9420549, 2290027, 1, 1, 0, 80000), -(9420549, 2290034, 1, 1, 0, 80000), -(9420549, 2290052, 1, 1, 0, 80000), -(9420549, 2290054, 1, 1, 0, 80000), -(9420549, 2290089, 1, 1, 0, 80000), -(9420549, 2290094, 1, 1, 0, 80000), -(9420549, 2290098, 1, 1, 0, 80000), -(9420549, 2290105, 1, 1, 0, 80000), -(9420549, 2290110, 1, 1, 0, 80000), -(9420549, 2290119, 1, 1, 0, 80000), -(9500166, 2290044, 1, 1, 0, 2000), -(9500173, 2290018, 1, 1, 0, 40000), -(9500173, 2290019, 1, 1, 0, 40000), -(9500173, 2290032, 1, 1, 0, 40000), -(9500173, 2290042, 1, 1, 0, 40000), -(9500173, 2290058, 1, 1, 0, 40000), -(9500173, 2290068, 1, 1, 0, 40000), -(9500173, 2290072, 1, 1, 0, 40000), -(9500173, 2290092, 1, 1, 0, 40000), -(9500173, 2290099, 1, 1, 0, 40000), -(9500173, 2290102, 1, 1, 0, 40000), -(9500173, 2290119, 1, 1, 0, 40000), -(9500173, 2290128, 1, 1, 0, 120000), -(9500174, 2290002, 1, 1, 0, 40000), -(9500174, 2290014, 1, 1, 0, 40000), -(9500174, 2290030, 1, 1, 0, 40000), -(9500174, 2290080, 1, 1, 0, 40000), -(9500174, 2290130, 1, 1, 0, 120000), -(9500180, 2290010, 1, 1, 0, 40000), -(9500180, 2290028, 1, 1, 0, 40000), -(9500180, 2290126, 1, 1, 0, 120000), -(9500181, 2290010, 1, 1, 0, 40000), -(9500181, 2290028, 1, 1, 0, 40000), -(9500181, 2290126, 1, 1, 0, 120000), -(9500331, 2290010, 1, 1, 0, 40000), -(9500331, 2290028, 1, 1, 0, 40000), -(9500331, 2290126, 1, 1, 0, 120000), -(9500332, 2290132, 1, 1, 0, 120000), -(9500333, 2290006, 1, 1, 0, 40000), -(9500333, 2290030, 1, 1, 0, 40000), -(9500333, 2290032, 1, 1, 0, 40000), -(9500333, 2290060, 1, 1, 0, 40000), -(9500333, 2290076, 1, 1, 0, 40000), -(9500333, 2290104, 1, 1, 0, 40000), -(9500333, 2290117, 1, 1, 0, 40000); \ No newline at end of file +(9420540, 2280006, 1, 1, 0, 500), +(9420540, 2290119, 1, 1, 0, 500), +(9420540, 2290120, 1, 1, 0, 500), +(9420544, 2280007, 1, 1, 0, 20000), +(9420544, 2280008, 1, 1, 0, 20000), +(9420544, 2280009, 1, 1, 0, 20000), +(9420544, 2280010, 1, 1, 0, 20000), +(9420544, 2290002, 1, 1, 0, 20000), +(9420544, 2290015, 1, 1, 0, 20000), +(9420544, 2290022, 1, 1, 0, 20000), +(9420544, 2290027, 1, 1, 0, 20000), +(9420544, 2290034, 1, 1, 0, 20000), +(9420544, 2290052, 1, 1, 0, 20000), +(9420544, 2290054, 1, 1, 0, 20000), +(9420544, 2290089, 1, 1, 0, 20000), +(9420544, 2290094, 1, 1, 0, 20000), +(9420544, 2290098, 1, 1, 0, 20000), +(9420544, 2290105, 1, 1, 0, 20000), +(9420544, 2290110, 1, 1, 0, 20000), +(9420544, 2290119, 1, 1, 0, 20000), +(9420549, 2280007, 1, 1, 0, 20000), +(9420549, 2280008, 1, 1, 0, 20000), +(9420549, 2280009, 1, 1, 0, 20000), +(9420549, 2280010, 1, 1, 0, 20000), +(9420549, 2290002, 1, 1, 0, 20000), +(9420549, 2290015, 1, 1, 0, 20000), +(9420549, 2290022, 1, 1, 0, 20000), +(9420549, 2290027, 1, 1, 0, 20000), +(9420549, 2290034, 1, 1, 0, 20000), +(9420549, 2290052, 1, 1, 0, 20000), +(9420549, 2290054, 1, 1, 0, 20000), +(9420549, 2290089, 1, 1, 0, 20000), +(9420549, 2290094, 1, 1, 0, 20000), +(9420549, 2290098, 1, 1, 0, 20000), +(9420549, 2290105, 1, 1, 0, 20000), +(9420549, 2290110, 1, 1, 0, 20000), +(9420549, 2290119, 1, 1, 0, 20000), +(9500166, 2290044, 1, 1, 0, 500), +(9500173, 2290018, 1, 1, 0, 10000), +(9500173, 2290019, 1, 1, 0, 10000), +(9500173, 2290032, 1, 1, 0, 10000), +(9500173, 2290042, 1, 1, 0, 10000), +(9500173, 2290058, 1, 1, 0, 10000), +(9500173, 2290068, 1, 1, 0, 10000), +(9500173, 2290072, 1, 1, 0, 10000), +(9500173, 2290092, 1, 1, 0, 10000), +(9500173, 2290099, 1, 1, 0, 10000), +(9500173, 2290102, 1, 1, 0, 10000), +(9500173, 2290119, 1, 1, 0, 10000), +(9500173, 2290128, 1, 1, 0, 30000), +(9500174, 2290002, 1, 1, 0, 10000), +(9500174, 2290014, 1, 1, 0, 10000), +(9500174, 2290030, 1, 1, 0, 10000), +(9500174, 2290080, 1, 1, 0, 10000), +(9500174, 2290130, 1, 1, 0, 30000), +(9500180, 2290010, 1, 1, 0, 10000), +(9500180, 2290028, 1, 1, 0, 10000), +(9500180, 2290126, 1, 1, 0, 30000), +(9500181, 2290010, 1, 1, 0, 10000), +(9500181, 2290028, 1, 1, 0, 10000), +(9500181, 2290126, 1, 1, 0, 30000), +(9500331, 2290010, 1, 1, 0, 10000), +(9500331, 2290028, 1, 1, 0, 10000), +(9500331, 2290126, 1, 1, 0, 30000), +(9500332, 2290132, 1, 1, 0, 30000), +(9500333, 2290006, 1, 1, 0, 10000), +(9500333, 2290030, 1, 1, 0, 10000), +(9500333, 2290032, 1, 1, 0, 10000), +(9500333, 2290060, 1, 1, 0, 10000), +(9500333, 2290076, 1, 1, 0, 10000), +(9500333, 2290104, 1, 1, 0, 10000), +(9500333, 2290117, 1, 1, 0, 10000); \ No newline at end of file diff --git a/src/client/AbstractCharacterListener.java b/src/client/AbstractCharacterListener.java index 53c9794ca0..407a1d3461 100644 --- a/src/client/AbstractCharacterListener.java +++ b/src/client/AbstractCharacterListener.java @@ -26,5 +26,6 @@ package client; public interface AbstractCharacterListener { public void onHpChanged(int oldHp); public void onHpmpPoolUpdate(); + public void onStatUpdate(); public void onAnnounceStatPoolUpdate(); } diff --git a/src/client/AbstractMapleCharacterObject.java b/src/client/AbstractMapleCharacterObject.java index 74655e5cf1..12eb374b81 100644 --- a/src/client/AbstractMapleCharacterObject.java +++ b/src/client/AbstractMapleCharacterObject.java @@ -19,18 +19,19 @@ */ package client; -import constants.GameConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.GameConstants; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import server.maps.AbstractAnimatedMapleMapObject; import server.maps.MapleMap; @@ -50,13 +51,13 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple protected Map statUpdates = new HashMap<>(); protected Lock effLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EFF, true); - protected ReadLock statRlock; - protected WriteLock statWlock; + protected MonitoredReadLock statRlock; + protected MonitoredWriteLock statWlock; protected AbstractMapleCharacterObject() { - ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true); - statRlock = locks.readLock(); - statWlock = locks.writeLock(); + MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true); + statRlock = MonitoredReadLockFactory.createLock(locks); + statWlock = MonitoredWriteLockFactory.createLock(locks); for (int i = 0; i < remainingSp.length; i++) { remainingSp[i] = 0; @@ -220,6 +221,10 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple listener.onHpmpPoolUpdate(); } + private void dispatchStatUpdated() { + listener.onStatUpdate(); + } + private void dispatchStatPoolUpdateAnnounced() { listener.onAnnounceStatPoolUpdate(); } @@ -299,6 +304,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple try { statUpdates.clear(); boolean poolUpdate = false; + boolean statUpdate = false; if (hpMpPool != null) { short newHp = (short) (hpMpPool >> 48); @@ -370,7 +376,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple statUpdates.put(MapleStat.AVAILABLEAP, remainingAp); } - poolUpdate = true; // recalc stats + statUpdate = true; } if (newSp != null) { @@ -385,6 +391,10 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple if (poolUpdate) { dispatchHpmpPoolUpdated(); } + + if (statUpdate) { + dispatchStatUpdated(); + } if (!silent) { dispatchStatPoolUpdateAnnounced(); @@ -626,19 +636,19 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple } int newStr = str + deltaStr, newDex = dex + deltaDex, newInt = int_ + deltaInt, newLuk = luk + deltaLuk; - if (newStr < 4 && deltaStr != Short.MIN_VALUE || newStr > ServerConstants.MAX_AP) { + if (newStr < 4 && deltaStr != Short.MIN_VALUE || newStr > YamlConfig.config.server.MAX_AP) { return false; } - if (newDex < 4 && deltaDex != Short.MIN_VALUE || newDex > ServerConstants.MAX_AP) { + if (newDex < 4 && deltaDex != Short.MIN_VALUE || newDex > YamlConfig.config.server.MAX_AP) { return false; } - if (newInt < 4 && deltaInt != Short.MIN_VALUE || newInt > ServerConstants.MAX_AP) { + if (newInt < 4 && deltaInt != Short.MIN_VALUE || newInt > YamlConfig.config.server.MAX_AP) { return false; } - if (newLuk < 4 && deltaLuk != Short.MIN_VALUE || newLuk > ServerConstants.MAX_AP) { + if (newLuk < 4 && deltaLuk != Short.MIN_VALUE || newLuk > YamlConfig.config.server.MAX_AP) { return false; } diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 9c52ef0594..4f046b0a26 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -52,10 +52,13 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; +import config.YamlConfig; import net.server.PlayerBuffValueHolder; import net.server.PlayerCoolDownValueHolder; import net.server.Server; -import net.server.coordinator.MapleInviteCoordinator; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.coordinator.world.MapleInviteCoordinator; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; @@ -67,11 +70,11 @@ import net.server.world.PartyOperation; import net.server.world.World; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; +import scripting.item.ItemScriptManager; import server.CashShop; import server.MapleItemInformationProvider; import server.MapleItemInformationProvider.ScriptedItem; import server.MapleMarriage; -import server.MaplePortal; import server.MapleShop; import server.MapleStatEffect; import server.MapleStorage; @@ -83,24 +86,29 @@ import server.events.RescueGaga; import server.events.gm.MapleFitness; import server.events.gm.MapleOla; import server.life.MapleMonster; +import server.life.MaplePlayerNPC; import server.life.MobSkill; +import server.life.MobSkillFactory; +import server.maps.FieldLimit; import server.maps.MapleHiredMerchant; import server.maps.MapleDoor; +import server.maps.MapleDoorObject; import server.maps.MapleDragon; import server.maps.MapleMap; import server.maps.MapleMapEffect; import server.maps.MapleMapManager; +import server.maps.MapleMapItem; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import server.maps.MapleMiniGame; import server.maps.MapleMiniGame.MiniGameResult; import server.maps.MaplePlayerShop; import server.maps.MaplePlayerShopItem; +import server.maps.MaplePortal; import server.maps.MapleSummon; -import server.life.MaplePlayerNPC; -import server.life.MonsterDropEntry; import server.maps.SavedLocation; import server.maps.SavedLocationType; +import server.minigame.MapleRockPaperScissor; import server.partyquest.AriantColiseum; import server.partyquest.MonsterCarnival; import server.partyquest.MonsterCarnivalParty; @@ -129,16 +137,17 @@ import client.inventory.PetDataFactory; import client.inventory.manipulator.MapleCashidGenerator; import client.inventory.manipulator.MapleInventoryManipulator; import client.newyear.NewYearCardRecord; -import client.processor.FredrickProcessor; -import constants.ExpTable; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import client.processor.npc.FredrickProcessor; +import client.processor.action.PetAutopotProcessor; +import constants.game.ExpTable; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; import constants.skills.Aran; import constants.skills.Beginner; import constants.skills.Bishop; import constants.skills.BlazeWizard; import constants.skills.Bowmaster; +import constants.skills.Brawler; import constants.skills.Buccaneer; import constants.skills.Corsair; import constants.skills.Crusader; @@ -160,13 +169,13 @@ import constants.skills.Priest; import constants.skills.Ranger; import constants.skills.Shadower; import constants.skills.Sniper; -import constants.skills.Swordsman; +import constants.skills.Warrior; import constants.skills.ThunderBreaker; -import scripting.item.ItemScriptManager; -import server.life.MobSkillFactory; -import server.maps.MapleMapItem; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.FaceExpressionService; +import net.server.services.task.world.CharacterSaveService; +import net.server.services.type.WorldServices; +import org.apache.mina.core.session.IoSession; import org.apache.mina.util.ConcurrentHashSet; public class MapleCharacter extends AbstractMapleCharacterObject { @@ -191,7 +200,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private int energybar; private int gmLevel; private int ci = 0; - private MapleFamily family; + private MapleFamilyEntry familyEntry; private int familyId; private int bookCover; private int battleshipHp = 0; @@ -207,6 +216,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private int localchairrate; private boolean hidden, equipchanged = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false, canRecvPartySearchInvite = true; private boolean equippedMesoMagnet = false, equippedItemPouch = false, equippedPetItemIgnore = false; + private boolean usedSafetyCharm = false; + private float autopotHpAlert, autopotMpAlert; private int linkedLevel = 0; private String linkedName = null; private boolean finishedDojoTutorial; @@ -221,7 +232,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private AtomicInteger exp = new AtomicInteger(); private AtomicInteger gachaexp = new AtomicInteger(); private AtomicInteger meso = new AtomicInteger(); - private AtomicInteger chair = new AtomicInteger(); + private AtomicInteger chair = new AtomicInteger(-1); private int merchantmeso; private BuddyList buddylist; private EventInstanceManager eventInstance = null; @@ -233,6 +244,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private MapleJob job = MapleJob.BEGINNER; private MapleMessenger messenger = null; private MapleMiniGame miniGame; + private MapleRockPaperScissor rps; private MapleMount maplemount; private MapleParty party; private MaplePet[] pets = new MaplePet[3]; @@ -248,6 +260,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private SkillMacro[] skillMacros = new SkillMacro[5]; private List lastmonthfameids; private List> lastVisitedMaps = new LinkedList<>(); + private WeakReference ownedMap = new WeakReference<>(null); private final Map quests; private Set controlled = new LinkedHashSet<>(); private Map entered = new LinkedHashMap<>(); @@ -302,6 +315,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private List viptrockmaps = new ArrayList<>(); private Map events = new LinkedHashMap<>(); private PartyQuest partyQuest = null; + private List> npcUpdateQuests = new LinkedList<>(); private MapleDragon dragon = null; private MapleRing marriageRing; private int marriageItemid = -1; @@ -311,7 +325,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private boolean loggedIn = false; private boolean useCS; //chaos scroll upon crafting item. private long npcCd; - private long petLootCd; private long lastHpDec = 0; private int newWarpMap = -1; private boolean canWarpMap = true; //only one "warp" must be used per call, and this will define the right one. @@ -325,6 +338,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private int banishSp = -1; private long banishTime = 0; private long lastExpGainTime; + private boolean pendingNameChange; //only used to change name on logout, not to be relied upon elsewhere + private long loginTime; private MapleCharacter() { super.setListener(new AbstractCharacterListener() { @@ -351,6 +366,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + @Override + public void onStatUpdate() { + recalcLocalStats(); + } + @Override public void onAnnounceStatPoolUpdate() { List> statup = new ArrayList<>(8); @@ -382,8 +402,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } quests = new LinkedHashMap<>(); setPosition(new Point(0, 0)); - - petLootCd = Server.getInstance().getCurrentTime(); } private static MapleJob getJobStyleInternal(int jobid, byte opt) { @@ -423,7 +441,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public static MapleCharacter getDefault(MapleClient c) { MapleCharacter ret = new MapleCharacter(); ret.client = c; - ret.gmLevel = 0; + ret.setGMLevel(0); ret.hp = 50; ret.setMaxHp(50); ret.mp = 5; @@ -448,7 +466,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int[] selectedType; int[] selectedAction; - if(ServerConstants.USE_CUSTOM_KEYSET) { + if(YamlConfig.config.server.USE_CUSTOM_KEYSET) { selectedKey = GameConstants.getCustomKey(true); selectedType = GameConstants.getCustomType(true); selectedAction = GameConstants.getCustomAction(true); @@ -550,15 +568,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void setSessionTransitionState() { - client.getSession().setAttribute(MapleClient.CLIENT_TRANSITION); - } - - public long getPetLootCd() { - return petLootCd; - } - - public void setPetLootCd(long cd) { - petLootCd = cd; + client.setCharacterOnSessionTransitionState(this.getId()); } public boolean getCS() { @@ -789,28 +799,31 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } return false; } + + public int calculateMaxBaseDamage(int watk, MapleWeaponType weapon) { + int mainstat, secondarystat; + if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) { + weapon = MapleWeaponType.DAGGER_THIEVES; + } + + if (weapon == MapleWeaponType.BOW || weapon == MapleWeaponType.CROSSBOW || weapon == MapleWeaponType.GUN) { + mainstat = localdex; + secondarystat = localstr; + } else if (weapon == MapleWeaponType.CLAW || weapon == MapleWeaponType.DAGGER_THIEVES) { + mainstat = localluk; + secondarystat = localdex + localstr; + } else { + mainstat = localstr; + secondarystat = localdex; + } + return (int) Math.ceil(((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk); + } public int calculateMaxBaseDamage(int watk) { int maxbasedamage; Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11); if (weapon_item != null) { - MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId()); - int mainstat, secondarystat; - if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) { - weapon = MapleWeaponType.DAGGER_THIEVES; - } - - if (weapon == MapleWeaponType.BOW || weapon == MapleWeaponType.CROSSBOW || weapon == MapleWeaponType.GUN) { - mainstat = localdex; - secondarystat = localstr; - } else if (weapon == MapleWeaponType.CLAW || weapon == MapleWeaponType.DAGGER_THIEVES) { - mainstat = localluk; - secondarystat = localdex + localstr; - } else { - mainstat = localstr; - secondarystat = localdex; - } - maxbasedamage = (int) (((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk); + maxbasedamage = calculateMaxBaseDamage(watk, ii.getWeaponType(weapon_item.getItemId())); } else { if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { double weapMulti = 3; @@ -819,13 +832,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } int attack = (int) Math.min(Math.floor((2 * getLevel() + 31) / 3), 31); - maxbasedamage = (int) (localstr * weapMulti + localdex) * attack / 100; + maxbasedamage = (int) Math.ceil((localstr * weapMulti + localdex) * attack / 100.0); } else { maxbasedamage = 1; } } return maxbasedamage; } + + public int calculateMaxBaseMagicDamage(int matk) { + int maxbasedamage = matk; + int totalint = getTotalInt(); + + if (totalint > 2000) { + maxbasedamage -= 2000; + maxbasedamage += (int) ((0.09033024267 * totalint) + 3823.8038); + } else { + maxbasedamage -= totalint; + + if (totalint > 1700) { + maxbasedamage += (int) (0.1996049769 * Math.pow(totalint, 1.300631341)); + } else { + maxbasedamage += (int) (0.1996049769 * Math.pow(totalint, 1.290631341)); + } + } + + return (maxbasedamage * 107) / 100; + } public void setCombo(short count) { if (count < combocounter) { @@ -907,6 +940,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { for(MapleSummon ms: this.getSummonsValues()) { getMap().broadcastNONGMMessage(this, MaplePacketCreator.spawnSummon(ms, false), false); } + + for (MapleMapObject mo : this.getMap().getMonsters()) { + MapleMonster m = (MapleMonster) mo; + m.aggroUpdateController(); + } } else { this.hidden = true; announce(MaplePacketCreator.getGMEffect(0x10, (byte) 1)); @@ -1087,6 +1125,29 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } } + + private void broadcastChangeJob() { + for (MapleCharacter chr : map.getAllPlayers()) { + MapleClient chrC = chr.getClient(); + + if (chrC != null) { // propagate new job 3rd-person effects (FJ, Aran 1st strike, etc) + this.sendDestroyData(chrC); + this.sendSpawnData(chrC); + } + } + + TimerManager.getInstance().schedule(new Runnable() { // need to delay to ensure clientside has finished reloading character data + @Override + public void run() { + MapleCharacter thisChr = MapleCharacter.this; + MapleMap map = thisChr.getMap(); + + if (map != null) { + map.broadcastMessage(thisChr, MaplePacketCreator.showForeignEffect(thisChr.getId(), 8), false); + } + } + }, 777); + } public synchronized void changeJob(MapleJob newJob) { if (newJob == null) { @@ -1109,7 +1170,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { spGain += 2; } - if (ServerConstants.USE_ENFORCE_JOB_SP_RANGE) { + if (YamlConfig.config.server.USE_ENFORCE_JOB_SP_RANGE) { spGain = getChangedJobSp(newJob); } } @@ -1123,7 +1184,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (this.isCygnus()) { gainAp(7, true); } else { - gainAp(5, true); + if (YamlConfig.config.server.USE_STARTING_AP_4 || newJob.getId() % 10 >= 1) { + gainAp(5, true); + } + } + } else { // thanks Periwinks for noticing an AP shortage from lower levels + if (YamlConfig.config.server.USE_STARTING_AP_4 && newJob.getId() % 1000 >= 1) { + gainAp(4, true); } } @@ -1196,10 +1263,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (this.guildid > 0) { getGuild().broadcast(MaplePacketCreator.jobMessage(0, job.getId(), name), this.getId()); } + MapleFamily family = getFamily(); + if(family != null) { + family.broadcast(MaplePacketCreator.jobMessage(1, job.getId(), name), this.getId()); + } setMasteries(this.job.getId()); guildUpdate(); - getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(this.getId(), 8), false); + broadcastChangeJob(); if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) { if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) { @@ -1208,7 +1279,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { createDragon(); } - if (ServerConstants.USE_ANNOUNCE_CHANGEJOB) { + if (YamlConfig.config.server.USE_ANNOUNCE_CHANGEJOB) { if (!this.isGM()) { broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + GameConstants.getJobName(this.job.getId()) + "."); // thanks Vcoc for noticing job name appearing in uppercase here } @@ -1221,9 +1292,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void broadcastAcquaintances(byte[] packet) { buddylist.broadcast(packet, getWorldServer().getPlayerStorage()); - + MapleFamily family = getFamily(); if(family != null) { - //family.broadcast(packet, id); not yet implemented + family.broadcast(packet, id); } MapleGuild guild = getGuild(); @@ -1253,7 +1324,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void broadcastStance() { - map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, this.getIdleMovement()), false); + map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, this.getIdleMovement(), getIdleMovementDataLength()), false); } public MapleMap getWarpMap(int map) { @@ -1309,7 +1380,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void changeMapBanish(int mapid, String portal, String msg) { - if(ServerConstants.USE_SPIKES_AVOID_BANISH) { + if(YamlConfig.config.server.USE_SPIKES_AVOID_BANISH) { for(Item it: this.getInventory(MapleInventoryType.EQUIPPED).list()) { if((it.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) { return; @@ -1520,7 +1591,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } List partyMembers = new LinkedList<>(); - for(MapleCharacter mc : (exPartyMembers != null) ? exPartyMembers : this.getPartyMembers()) { + for(MapleCharacter mc : (exPartyMembers != null) ? exPartyMembers : this.getPartyMembersOnline()) { if(mc.isLoggedinWorld()) { partyMembers.add(mc); } @@ -1585,7 +1656,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { for(MapleDoor door : partyDoors.values()) { for(MapleCharacter pchar : partyMembers) { - door.getTownDoor().sendDestroyData(pchar.getClient(), true); + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendDestroyData(pchar.getClient(), true); + pchar.removeVisibleMapObject(mdo); } } @@ -1593,7 +1666,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { Collection leaverDoors = partyLeaver.getDoors(); for(MapleDoor door : leaverDoors) { for(MapleCharacter pchar : partyMembers) { - door.getTownDoor().sendDestroyData(pchar.getClient(), true); + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendDestroyData(pchar.getClient(), true); + pchar.removeVisibleMapObject(mdo); } } } @@ -1604,7 +1679,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if(door != null) { for(MapleCharacter pchar : partyMembers) { - door.getTownDoor().sendSpawnData(pchar.getClient()); + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendSpawnData(pchar.getClient()); + pchar.addVisibleMapObject(mdo); } } } @@ -1615,17 +1692,24 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if(partyDoors != null) { for(MapleDoor door : partyDoors.values()) { - door.getTownDoor().sendDestroyData(partyLeaver.getClient(), true); + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendDestroyData(partyLeaver.getClient(), true); + partyLeaver.removeVisibleMapObject(mdo); } } for(MapleDoor door : leaverDoors) { - door.getTownDoor().sendDestroyData(partyLeaver.getClient(), true); + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendDestroyData(partyLeaver.getClient(), true); + partyLeaver.removeVisibleMapObject(mdo); } for(MapleDoor door : leaverDoors) { door.updateDoorPortal(partyLeaver); - door.getTownDoor().sendSpawnData(partyLeaver.getClient()); + + MapleDoorObject mdo = door.getTownDoor(); + mdo.sendSpawnData(partyLeaver.getClient()); + partyLeaver.addVisibleMapObject(mdo); } } } @@ -1650,7 +1734,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int idx = getVisitedMapIndex(map); if(idx == -1) { - if(lastVisitedMaps.size() == ServerConstants.MAP_VISITED_SIZE) { + if(lastVisitedMaps.size() == YamlConfig.config.server.MAP_VISITED_SIZE) { lastVisitedMaps.remove(0); } } else { @@ -1664,6 +1748,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { petLock.unlock(); } } + + public void setOwnedMap(MapleMap map) { + ownedMap = new WeakReference<>(map); + } + + public MapleMap getOwnedMap() { + return ownedMap.get(); + } public void notifyMapTransferToPartner(int mapid) { if(partnerId > 0) { @@ -2248,6 +2340,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ps.setInt(1, cid); ps.executeUpdate(); } + try (PreparedStatement ps = con.prepareStatement("DELETE FROM family_character WHERE cid = ?")) { + ps.setInt(1, cid); + ps.executeUpdate(); + } try (PreparedStatement ps = con.prepareStatement("DELETE FROM famelog WHERE characterid_to = ?")) { ps.setInt(1, cid); ps.executeUpdate(); @@ -2285,7 +2381,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } int petid = rs.getInt("petid"); - if(petid > -1) { + if(!rs.wasNull()) { try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM pets WHERE petid = ?")) { ps2.setInt(1, petid); ps2.executeUpdate(); @@ -2377,10 +2473,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private static Pair> getChairTaskIntervalRate(int maxhp, int maxmp) { float toHeal = Math.max(maxhp, maxmp); - float maxDuration = ServerConstants.CHAIR_EXTRA_HEAL_MAX_DELAY * 1000; + float maxDuration = YamlConfig.config.server.CHAIR_EXTRA_HEAL_MAX_DELAY * 1000; int rate = 0; - int minRegen = 1, maxRegen = (256 * ServerConstants.CHAIR_EXTRA_HEAL_MULTIPLIER) - 1, midRegen = 1; + int minRegen = 1, maxRegen = (256 * YamlConfig.config.server.CHAIR_EXTRA_HEAL_MULTIPLIER) - 1, midRegen = 1; while (minRegen < maxRegen) { midRegen = (int) ((minRegen + maxRegen) * 0.94); @@ -2435,7 +2531,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } private void startChairTask() { - if (chair.get() == 0) { + if (chair.get() < 0) { return; } @@ -2462,7 +2558,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { final int healMP = localchairmp; if(MapleCharacter.this.getHp() < localmaxhp) { - byte recHP = (byte) (healHP / ServerConstants.CHAIR_EXTRA_HEAL_MULTIPLIER); + byte recHP = (byte) (healHP / YamlConfig.config.server.CHAIR_EXTRA_HEAL_MULTIPLIER); client.announce(MaplePacketCreator.showOwnRecovery(recHP)); getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, recHP), false); @@ -2534,7 +2630,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void dispel() { - if(!(ServerConstants.USE_UNDISPEL_HOLY_SHIELD && this.isActiveBuffedValue(Bishop.HOLY_SHIELD))) { + if(!(YamlConfig.config.server.USE_UNDISPEL_HOLY_SHIELD && this.hasActiveBuff(Bishop.HOLY_SHIELD))) { List mbsvhList = getAllStatups(); for (MapleBuffStatValueHolder mbsvh : mbsvhList) { if (mbsvh.effect.isSkill()) { @@ -2627,10 +2723,28 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public void collectDiseases() { + for (MapleCharacter chr : map.getAllPlayers()) { + int cid = chr.getId(); + + for (Entry> di : chr.getAllDiseases().entrySet()) { + MapleDisease disease = di.getKey(); + MobSkill skill = di.getValue().getRight(); + final List> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX()))); + + if (disease != MapleDisease.SLOW) { + this.announce(MaplePacketCreator.giveForeignDebuff(cid, debuff, skill)); + } else { + this.announce(MaplePacketCreator.giveForeignSlowDebuff(cid, debuff, skill)); + } + } + } + } + public void giveDebuff(final MapleDisease disease, MobSkill skill) { if (!hasDisease(disease) && getDiseasesSize() < 2) { if (!(disease == MapleDisease.SEDUCE || disease == MapleDisease.STUN)) { - if (isActiveBuffedValue(Bishop.HOLY_SHIELD)) { + if (hasActiveBuff(Bishop.HOLY_SHIELD)) { return; } } @@ -2644,8 +2758,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { chrLock.unlock(); } - if (disease == MapleDisease.SEDUCE && chair.get() != 0) { - sitChair(0); + if (disease == MapleDisease.SEDUCE && chair.get() < 0) { + sitChair(-1); } final List> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX()))); @@ -2687,6 +2801,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { dispelDebuff(MapleDisease.SEAL); dispelDebuff(MapleDisease.WEAKEN); dispelDebuff(MapleDisease.SLOW); + dispelDebuff(MapleDisease.ZOMBIFY); + dispelDebuff(MapleDisease.CONFUSE); } public void cancelAllDebuffs() { @@ -2734,7 +2850,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { long timeNow = Server.getInstance().getCurrentTime(); if(timeNow - lastExpression > 2000) { lastExpression = timeNow; - client.getChannelServer().registerFaceExpression(map, this, emote); + + FaceExpressionService service = (FaceExpressionService) client.getChannelServer().getServiceAccess(ChannelServices.FACE_EXPRESSION); + service.registerFaceExpression(map, this, emote); } } @@ -2751,7 +2869,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void run() { doHurtHp(); } - }, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL - lastHpTask); + }, YamlConfig.config.server.MAP_DAMAGE_OVERTIME_INTERVAL, YamlConfig.config.server.MAP_DAMAGE_OVERTIME_INTERVAL - lastHpTask); } public void resetHpDecreaseTask() { @@ -2760,7 +2878,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } long lastHpTask = Server.getInstance().getCurrentTime() - lastHpDec; - startHpDecreaseTask((lastHpTask > ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL) ? ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL : lastHpTask); + startHpDecreaseTask((lastHpTask > YamlConfig.config.server.MAP_DAMAGE_OVERTIME_INTERVAL) ? YamlConfig.config.server.MAP_DAMAGE_OVERTIME_INTERVAL : lastHpTask); } public void dropMessage(String message) { @@ -2926,9 +3044,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { expiration = item.getExpiration(); if (expiration != -1 && (expiration < currenttime) && ((item.getFlag() & ItemConstants.LOCK) == ItemConstants.LOCK)) { - byte aids = item.getFlag(); - aids &= ~(ItemConstants.LOCK); - item.setFlag(aids); //Probably need a check, else people can make expiring items into permanent items... + short lock = item.getFlag(); + lock &= ~(ItemConstants.LOCK); + item.setFlag(lock); //Probably need a check, else people can make expiring items into permanent items... item.setExpiration(-1); forceUpdateItem(item); //TEST :3 } else if (expiration != -1 && expiration < currenttime) { @@ -2939,6 +3057,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { deletedCoupon = true; } } else { + MaplePet pet = item.getPet(); // thanks Lame for noticing pets not getting despawned after expiration time + if (pet != null) { + unequipPet(pet, true); + } + if (ItemConstants.isExpirablePet(item.getItemId())) { client.announce(MaplePacketCreator.itemExpired(item.getItemId())); toberemove.add(item); @@ -3409,17 +3532,44 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public boolean hasActiveBuff(int sourceid) { + LinkedList allBuffs; + + effLock.lock(); + chrLock.lock(); + try { + allBuffs = new LinkedList<>(effects.values()); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + + for (MapleBuffStatValueHolder mbsvh : allBuffs) { + if (mbsvh.effect.getBuffSourceId() == sourceid) { + return true; + } + } + return false; + } + private List> getActiveStatupsFromSourceid(int sourceid) { // already under effLock & chrLock List> ret = new ArrayList<>(); - + List> singletonStatups = new ArrayList<>(); for(Entry bel : buffEffects.get(sourceid).entrySet()) { MapleBuffStat mbs = bel.getKey(); MapleBuffStatValueHolder mbsvh = effects.get(bel.getKey()); + Pair p; if(mbsvh != null) { - ret.add(new Pair<>(mbs, mbsvh.value)); + p = new Pair<>(mbs, mbsvh.value); } else { - ret.add(new Pair<>(mbs, 0)); + p = new Pair<>(mbs, 0); + } + + if (!isSingletonStatup(mbs)) { // thanks resinate, Egg Daddy for pointing out morph issues when updating it along with other statups + ret.add(p); + } else { + singletonStatups.add(p); } } @@ -3430,6 +3580,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } }); + if (!singletonStatups.isEmpty()) { + Collections.sort(singletonStatups, new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getLeft().compareTo(p2.getLeft()); + } + }); + + ret.addAll(singletonStatups); + } + return ret; } @@ -3705,12 +3866,31 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) { + boolean ret; + + prtLock.lock(); effLock.lock(); try { - return cancelEffect(effect, overwrite, startTime, true); + ret = cancelEffect(effect, overwrite, startTime, true); } finally { effLock.unlock(); + prtLock.unlock(); } + + if (effect.isMagicDoor() && ret) { + prtLock.lock(); + effLock.lock(); + try { + if (!hasBuffFromSourceid(Priest.MYSTIC_DOOR)) { + MapleDoor.attemptRemoveDoor(this); + } + } finally { + effLock.unlock(); + prtLock.unlock(); + } + } + + return ret; } private static MapleStatEffect getEffectFromBuffSource(Map buffSource) { @@ -3811,18 +3991,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { buffstats = extractLeastRelevantStatEffectsIfFull(effect); } - if (effect.isMagicDoor()) { - MapleDoor.attemptRemoveDoor(this); - } else if (effect.isMapChair()) { + if (effect.isMapChair()) { stopChairTask(); } List> toCancel = deregisterBuffStats(buffstats); if (effect.isMonsterRiding()) { - if (effect.getSourceId() != Corsair.BATTLE_SHIP) { - this.getClient().getWorldServer().unregisterMountHunger(this); - this.getMount().setActive(false); - } + this.getClient().getWorldServer().unregisterMountHunger(this); + this.getMount().setActive(false); } if (!overwrite) { @@ -3930,7 +4106,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { for(Entry it : stats.entrySet()) { boolean uniqueBuff = isSingletonStatup(it.getKey()); - if(it.getValue() >= (!uniqueBuff ? ServerConstants.MAX_MONITORED_BUFFSTATS : 1) && effectStatups.contains(it.getKey())) { + if(it.getValue() >= (!uniqueBuff ? YamlConfig.config.server.MAX_MONITORED_BUFFSTATS : 1) && effectStatups.contains(it.getKey())) { MapleBuffStatValueHolder mbsvh = minStatBuffs.get(it.getKey()); Map lpbe = buffEffects.get(mbsvh.effect.getBuffSourceId()); @@ -4094,6 +4270,44 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return topologicalSortEffects(buffEffects); } + private List>> propagatePriorityBuffEffectUpdates(Set retrievedStats) { + List>> priorityUpdateEffects = new LinkedList<>(); + Map yokeStats = new LinkedHashMap<>(); + + // priority buffsources: override buffstats for the client to perceive those as "currently buffed" + Set mbsvhList = new LinkedHashSet<>(); + for (MapleBuffStatValueHolder mbsvh : getAllStatups()) { + mbsvhList.add(mbsvh); + } + + for (MapleBuffStatValueHolder mbsvh : mbsvhList) { + MapleStatEffect mse = mbsvh.effect; + int buffSourceId = mse.getBuffSourceId(); + if (isPriorityBuffSourceid(buffSourceId) && !hasActiveBuff(buffSourceId)) { + for (Pair ps : mse.getStatups()) { + MapleBuffStat mbs = ps.getLeft(); + if (retrievedStats.contains(mbs)) { + MapleBuffStatValueHolder mbsvhe = effects.get(mbs); + + // this shouldn't even be null... + //if (mbsvh != null) { + yokeStats.put(mbsvh, mbsvhe.effect); + //} + } + } + } + } + + for (Entry e : yokeStats.entrySet()) { + MapleBuffStatValueHolder mbsvhPriority = e.getKey(); + MapleStatEffect mseActive = e.getValue(); + + priorityUpdateEffects.add(new Pair<>(mseActive.getBuffSourceId(), new Pair<>(mbsvhPriority.effect, mbsvhPriority.startTime))); + } + + return priorityUpdateEffects; + } + private void propagateBuffEffectUpdates(Map> retrievedEffects, Set retrievedStats, Set removedStats) { cancelInactiveBuffStats(retrievedStats, removedStats); if (retrievedStats.isEmpty()) { @@ -4182,6 +4396,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { activeStatups.clear(); } + List>> priorityEffects = propagatePriorityBuffEffectUpdates(retrievedStats); + for(Pair> lmse: priorityEffects) { + Pair msel = lmse.getRight(); + + for(Pair statup : getActiveStatupsFromSourceid(lmse.getLeft())) { + activeStatups.add(statup); + } + + msel.getLeft().updateBuffEffect(this, activeStatups, msel.getRight()); + activeStatups.clear(); + } + if (this.isRidingBattleship()) { List> statups = new ArrayList<>(1); statups.add(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0)); @@ -4230,6 +4456,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + private static boolean isPriorityBuffSourceid(int sourceid) { + switch(sourceid) { + case -2022631: + case -2022632: + case -2022633: + return true; + + default: + return false; + } + } + private void addItemEffectHolderCount(MapleBuffStat stat) { Byte val = buffEffectsCount.get(stat); if (val != null) { @@ -4292,7 +4530,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { }, buffInterval, buffInterval); } } else if (effect.isRecovery()) { - int healInterval = (ServerConstants.USE_ULTRA_RECOVERY) ? 2000 : 5000; + int healInterval = (YamlConfig.config.server.USE_ULTRA_RECOVERY) ? 2000 : 5000; final byte heal = (byte) effect.getX(); chrLock.lock(); @@ -4349,6 +4587,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { startChairTask(); } + prtLock.lock(); effLock.lock(); chrLock.lock(); try { @@ -4361,11 +4600,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } boolean active = effect.isActive(this); - if(ServerConstants.USE_BUFF_MOST_SIGNIFICANT) { + if(YamlConfig.config.server.USE_BUFF_MOST_SIGNIFICANT) { toDeploy = new LinkedHashMap<>(); Map> retrievedEffects = new LinkedHashMap<>(); Set retrievedStats = new LinkedHashSet<>(); - for (Entry statup : appliedStatups.entrySet()) { MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey()); MapleBuffStatValueHolder statMbsvh = statup.getValue(); @@ -4385,6 +4623,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { addItemEffectHolderCount(statup.getKey()); } + // should also propagate update from buffs shared with priority sourceids + Set updated = appliedStatups.keySet(); + for (MapleBuffStatValueHolder mbsvh : this.getAllStatups()) { + if (isPriorityBuffSourceid(mbsvh.effect.getBuffSourceId())) { + for (Pair p : mbsvh.effect.getStatups()) { + if (updated.contains(p.getLeft())) { + retrievedStats.add(p.getLeft()); + } + } + } + } + if(!isSilent) { addItemEffectHolder(sourceid, expirationtime, appliedStatups); for (Entry statup : toDeploy.entrySet()) { @@ -4412,6 +4662,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } finally { chrLock.unlock(); effLock.unlock(); + prtLock.unlock(); } updateLocalStats(); @@ -4429,7 +4680,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean unregisterChairBuff() { - if (!ServerConstants.USE_CHAIR_EXTRAHEAL) { + if (!YamlConfig.config.server.USE_CHAIR_EXTRAHEAL) { return false; } @@ -4444,7 +4695,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean registerChairBuff() { - if (!ServerConstants.USE_CHAIR_EXTRAHEAL) { + if (!YamlConfig.config.server.USE_CHAIR_EXTRAHEAL) { return false; } @@ -4475,19 +4726,23 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return client.getAbstractPlayerInteraction(); } - public final List getCompletedQuests() { + private List getQuests() { synchronized (quests) { - List ret = new LinkedList<>(); - for (MapleQuestStatus q : quests.values()) { - if (q.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) { - ret.add(q); - } - } - - return Collections.unmodifiableList(ret); + return new ArrayList<>(quests.values()); } } + public final List getCompletedQuests() { + List ret = new LinkedList<>(); + for (MapleQuestStatus qs : getQuests()) { + if (qs.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) { + ret.add(qs); + } + } + + return Collections.unmodifiableList(ret); + } + public List getCrushRings() { Collections.sort(crushRings); return crushRings; @@ -4720,7 +4975,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean hasNoviceExpRate() { - return ServerConstants.USE_ENFORCE_NOVICE_EXPRATE && isBeginnerJob() && level < 11; + return YamlConfig.config.server.USE_ENFORCE_NOVICE_EXPRATE && isBeginnerJob() && level < 11; } public int getExpRate() { @@ -4769,6 +5024,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public int getQuestExpRate() { + if (hasNoviceExpRate()) { + return 1; + } + World w = getWorldServer(); return w.getExpRate() * w.getQuestRate(); } @@ -4805,11 +5064,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public MapleFamily getFamily() { - return family; + if(familyEntry != null) return familyEntry.getFamily(); + else return null; } - - public void setFamily(MapleFamily f) { - this.family = f; + + public MapleFamilyEntry getFamilyEntry() { + return familyEntry; + } + + public void setFamilyEntry(MapleFamilyEntry entry) { + if(entry != null) setFamilyId(entry.getFamily().getID()); + this.familyEntry = entry; } public int getFamilyId() { @@ -5080,7 +5345,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public int getTotalMagic() { return localmagic; } - + public int getTotalWatk() { return localwatk; } @@ -5090,7 +5355,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public int getMaxLevel() { - if(!ServerConstants.USE_ENFORCE_JOB_LEVEL_RANGE || isGmJob()) { + if(!YamlConfig.config.server.USE_ENFORCE_JOB_LEVEL_RANGE || isGmJob()) { return getMaxClassLevel(); } @@ -5282,14 +5547,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } - public List getPartyMembers() { + public List getPartyMembersOnline() { List list = new LinkedList<>(); prtLock.lock(); try { if(party != null) { - for(MaplePartyCharacter partyMembers: party.getMembers()) { - list.add(partyMembers.getPlayer()); + for(MaplePartyCharacter mpc: party.getMembers()) { + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + list.add(mc); + } } } } finally { @@ -5306,11 +5574,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { prtLock.lock(); try { if(party != null) { - for(MaplePartyCharacter partyMembers: party.getMembers()) { - MapleCharacter chr = partyMembers.getPlayer(); - MapleMap chrMap = chr.getMap(); - if(chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) { - list.add(chr); + for(MaplePartyCharacter mpc: party.getMembers()) { + MapleCharacter chr = mpc.getPlayer(); + if (chr != null) { + MapleMap chrMap = chr.getMap(); + if(chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) { + list.add(chr); + } } } } @@ -5326,10 +5596,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean isPartyMember(int cid) { - for(MapleCharacter mpcu: getPartyMembers()) { - if(mpcu.getId() == cid) { - return true; + prtLock.lock(); + try { + if(party != null) { + return party.getMemberById(cid) != null; } + } finally { + prtLock.unlock(); } return false; @@ -5339,9 +5612,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return playerShop; } + public MapleRockPaperScissor getRPS() { // thanks inhyuk for suggesting RPS addition + return rps; + } + public void setGMLevel(int level) { this.gmLevel = Math.min(level, 6); this.gmLevel = Math.max(level, 0); + + whiteChat = gmLevel >= 4; // thanks ozanrijen for suggesting default white chat } public void closePartySearchInteractions() { @@ -5356,6 +5635,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { closeTrade(); closePlayerShop(); closeMiniGame(true); + closeRPS(); closeHiredMerchant(false); closePlayerMessenger(); @@ -5512,32 +5792,28 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public final byte getQuestStatus(final int quest) { synchronized (quests) { - for (final MapleQuestStatus q : quests.values()) { - if (q.getQuest().getId() == quest) { - return (byte) q.getStatus().getId(); - } + MapleQuestStatus mqs = quests.get((short) quest); + if (mqs != null) { + return (byte) mqs.getStatus().getId(); + } else { + return 0; } - return 0; } } - public final MapleQuestStatus getMapleQuestStatus(final int quest) { - synchronized (quests) { - for (final MapleQuestStatus q : quests.values()) { - if (q.getQuest().getId() == quest) { - return q; - } - } - return null; - } + public MapleQuestStatus getQuest(final int quest) { + return getQuest(MapleQuest.getInstance(quest)); } - + public MapleQuestStatus getQuest(MapleQuest quest) { synchronized (quests) { - if (!quests.containsKey(quest.getId())) { - return new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED); + short questid = quest.getId(); + MapleQuestStatus qs = quests.get(questid); + if (qs == null) { + qs = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED); + quests.put(questid, qs); } - return quests.get(quest.getId()); + return qs; } } @@ -5586,13 +5862,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int amountNeeded, questStatus = this.getQuestStatus(questid); if (questStatus == 0) { amountNeeded = MapleQuest.getInstance(questid).getStartItemAmountNeeded(itemid); + if (amountNeeded == Integer.MIN_VALUE) { + return false; + } } else if (questStatus != 1) { return false; } else { amountNeeded = MapleQuest.getInstance(questid).getCompleteItemAmountNeeded(itemid); + if (amountNeeded == Integer.MAX_VALUE) { + return true; + } } - return amountNeeded > 0 && getInventory(ItemConstants.getInventoryType(itemid)).countById(itemid) < amountNeeded; + return getInventory(ItemConstants.getInventoryType(itemid)).countById(itemid) < amountNeeded; } public int getRank() { @@ -5673,32 +5955,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public final List getStartedQuests() { - synchronized (quests) { - List ret = new LinkedList<>(); - for (MapleQuestStatus q : quests.values()) { - if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) { - ret.add(q); - } + List ret = new LinkedList<>(); + for (MapleQuestStatus qs : getQuests()) { + if (qs.getStatus().equals(MapleQuestStatus.Status.STARTED)) { + ret.add(qs); } - return Collections.unmodifiableList(ret); } + return Collections.unmodifiableList(ret); } - - public final int getStartedQuestsSize() { - synchronized (quests) { - int i = 0; - for (MapleQuestStatus q : quests.values()) { - if (q.getStatus().equals(MapleQuestStatus.Status.STARTED)) { - if (q.getQuest().getInfoNumber() > 0) { - i++; - } - i++; - } - } - return i; - } - } - + public MapleStatEffect getStatForBuff(MapleBuffStat effect) { effLock.lock(); chrLock.lock(); @@ -5849,12 +6114,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean hasEntered(String script, int mapId) { - if (entered.containsKey(mapId)) { - if (entered.get(mapId).equals(script)) { - return true; - } - } - return false; + String e = entered.get(mapId); + return script.equals(e); } public void hasGivenFame(MapleCharacter to) { @@ -5908,26 +6169,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { dropMessage(1, "Your guild already reached the maximum capacity of players."); } } - - 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) { - if (mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skillid) { - return true; - } - } - return false; - } private boolean canBuyback(int fee, boolean usingMesos) { return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee; @@ -5942,7 +6183,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } private long getNextBuybackTime() { - return lastBuyback + ServerConstants.BUYBACK_COOLDOWN_MINUTES * 60 * 1000; + return lastBuyback + YamlConfig.config.server.BUYBACK_COOLDOWN_MINUTES * 60 * 1000; } private boolean isBuybackInvincible() { @@ -5950,30 +6191,30 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } private int getBuybackFee() { - float fee = ServerConstants.BUYBACK_FEE; + float fee = YamlConfig.config.server.BUYBACK_FEE; int grade = Math.min(Math.max(level, 30), 120) - 30; - fee += (grade * ServerConstants.BUYBACK_LEVEL_STACK_FEE); - if (ServerConstants.USE_BUYBACK_WITH_MESOS) { - fee *= ServerConstants.BUYBACK_MESO_MULTIPLIER; + fee += (grade * YamlConfig.config.server.BUYBACK_LEVEL_STACK_FEE); + if (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS) { + fee *= YamlConfig.config.server.BUYBACK_MESO_MULTIPLIER; } return (int) Math.floor(fee); } public void showBuybackInfo() { - String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (ServerConstants.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n"; + String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n"; long timeNow = Server.getInstance().getCurrentTime(); boolean avail = true; if (!isAlive()) { long timeLapsed = timeNow - lastDeathtime; - long timeRemaining = ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow)); + long timeRemaining = YamlConfig.config.server.BUYBACK_RETURN_MINUTES * 60 * 1000 - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow)); if (timeRemaining < 1) { s += "Buyback #e#rUNAVAILABLE#k#n"; avail = false; } else { - s += "Buyback countdown: #e#b" + getTimeRemaining(ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - timeLapsed) + "#k#n"; + s += "Buyback countdown: #e#b" + getTimeRemaining(YamlConfig.config.server.BUYBACK_RETURN_MINUTES * 60 * 1000 - timeLapsed) + "#k#n"; } s += "\r\n"; } @@ -5981,6 +6222,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (timeNow < getNextBuybackTime() && avail) { s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k"; s += "\r\n"; + } else { + s += "Buyback #bavailable#k"; } this.showHint(s); @@ -5996,7 +6239,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public boolean couldBuyback() { // Ronan's buyback system long timeNow = Server.getInstance().getCurrentTime(); - if (timeNow - lastDeathtime > ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000) { + if (timeNow - lastDeathtime > YamlConfig.config.server.BUYBACK_RETURN_MINUTES * 60 * 1000) { this.dropMessage(5, "The period of time to decide has expired, therefore you are unable to buyback."); return false; } @@ -6008,7 +6251,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return false; } - boolean usingMesos = ServerConstants.USE_BUYBACK_WITH_MESOS; + boolean usingMesos = YamlConfig.config.server.USE_BUYBACK_WITH_MESOS; int fee = getBuybackFee(); if (!canBuyback(fee, usingMesos)) { @@ -6068,7 +6311,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public boolean isPartyLeader() { prtLock.lock(); try { - return party.getLeaderId() == getId(); + MapleParty party = getParty(); + return party != null && party.getLeaderId() == getId(); } finally { prtLock.unlock(); } @@ -6079,21 +6323,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean attemptCatchFish(int baitLevel) { - return ServerConstants.USE_FISHING_SYSTEM && GameConstants.isFishingArea(mapid) && this.getPosition().getY() > 0 && ItemConstants.isFishingChair(chair.get()) && this.getWorldServer().registerFisherPlayer(this, baitLevel); + return YamlConfig.config.server.USE_FISHING_SYSTEM && GameConstants.isFishingArea(mapid) && this.getPosition().getY() > 0 && ItemConstants.isFishingChair(chair.get()) && this.getWorldServer().registerFisherPlayer(this, baitLevel); } public void leaveMap() { releaseControlledMonsters(); visibleMapObjects.clear(); - setChair(0); + setChair(-1); if (hpDecreaseTask != null) { hpDecreaseTask.cancel(false); } - if (map.unclaimOwnership(this)) { - map.dropMessage(5, "This lawn is now free real estate."); - } - AriantColiseum arena = this.getAriantColiseum(); if (arena != null) { arena.leaveArena(this); @@ -6108,7 +6348,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { spGain += (expectedSp - curSp); } - return getSpGain(spGain, curSp, job); + return getSpGain(spGain, curSp, newJob); } private int getUsedSp(MapleJob job) { @@ -6169,7 +6409,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } int spGain = 3; - if (ServerConstants.USE_ENFORCE_JOB_SP_RANGE && !GameConstants.hasSPTable(job)) { + if (YamlConfig.config.server.USE_ENFORCE_JOB_SP_RANGE && !GameConstants.hasSPTable(job)) { spGain = getSpGain(spGain, job); } @@ -6185,7 +6425,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int improvingMaxMPLevel = 0; boolean isBeginner = isBeginnerJob(); - if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP && isBeginner && level < 11) { + if (YamlConfig.config.server.USE_AUTOASSIGN_STARTERS_AP && isBeginner && level < 11) { effLock.lock(); statWlock.lock(); try { @@ -6225,7 +6465,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { addhp += Randomizer.rand(12, 16); addmp += Randomizer.rand(10, 12); } else if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) { - improvingMaxHP = isCygnus() ? SkillFactory.getSkill(DawnWarrior.MAX_HP_INCREASE) : SkillFactory.getSkill(Swordsman.IMPROVED_MAX_HP_INCREASE); + improvingMaxHP = isCygnus() ? SkillFactory.getSkill(DawnWarrior.MAX_HP_INCREASE) : SkillFactory.getSkill(Warrior.IMPROVED_MAXHP); if (job.isA(MapleJob.CRUSADER)) { improvingMaxMP = SkillFactory.getSkill(1210000); } else if (job.isA(MapleJob.DAWNWARRIOR2)) { @@ -6246,7 +6486,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { addhp += 30000; addmp += 30000; } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - improvingMaxHP = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.IMPROVE_MAX_HP) : SkillFactory.getSkill(5100000); + improvingMaxHP = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.IMPROVE_MAX_HP) : SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP); improvingMaxHPLevel = getSkillLevel(improvingMaxHP); addhp += Randomizer.rand(22, 28); addmp += Randomizer.rand(18, 23); @@ -6255,14 +6495,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int aids = Randomizer.rand(4, 8); addmp += aids + Math.floor(aids * 0.1); } - if (improvingMaxHPLevel > 0 && (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.DAWNWARRIOR1))) { + if (improvingMaxHPLevel > 0 && (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.THUNDERBREAKER1))) { addhp += improvingMaxHP.getEffect(improvingMaxHPLevel).getX(); } if (improvingMaxMPLevel > 0 && (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.CRUSADER) || job.isA(MapleJob.BLAZEWIZARD1))) { addmp += improvingMaxMP.getEffect(improvingMaxMPLevel).getX(); } - if (ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (getJobStyle() == MapleJob.MAGICIAN) { addmp += localint_ / 20; } else { @@ -6286,7 +6526,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int maxClassLevel = getMaxClassLevel(); if (level == maxClassLevel) { if (!this.isGM()) { - if (ServerConstants.PLAYERNPC_AUTODEPLOY) { + if (YamlConfig.config.server.PLAYERNPC_AUTODEPLOY) { ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { @@ -6338,7 +6578,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } if (level % 20 == 0) { - if (ServerConstants.USE_ADD_SLOTS_BY_LEVEL == true) { + if (YamlConfig.config.server.USE_ADD_SLOTS_BY_LEVEL == true) { if (!isGM()) { for (byte i = 1; i < 5; i++) { gainSlots(i, 4, true); @@ -6347,14 +6587,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { this.yellowMessage("You reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit."); } } - if (ServerConstants.USE_ADD_RATES_BY_LEVEL == true) { //For the rate upgrade + if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL == true) { //For the rate upgrade revertLastPlayerRates(); setPlayerRates(); this.yellowMessage("You managed to get level " + level + "! Getting experience and items seems a little easier now, huh?"); } } - if (ServerConstants.USE_PERFECT_PITCH && level >= 30) { + if (YamlConfig.config.server.USE_PERFECT_PITCH && level >= 30) { //milestones? if (MapleInventoryManipulator.checkSpace(client, 4310000, (short) 1, "")) { MapleInventoryManipulator.addById(client, 4310000, (short) 1, "", -1); @@ -6374,6 +6614,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject { levelUpMessages(); guildUpdate(); + + MapleFamilyEntry familyEntry = getFamilyEntry(); + if(familyEntry != null) { + familyEntry.giveReputationToSenior(YamlConfig.config.server.FAMILY_REP_PER_LEVELUP, true); + MapleFamilyEntry senior = familyEntry.getSenior(); + if(senior != null) { //only send the message to direct senior + MapleCharacter seniorChr = senior.getChr(); + if(seniorChr != null) seniorChr.announce(MaplePacketCreator.levelUpMessage(1, level, getName())); + } + } } public boolean leaveParty() { @@ -6383,7 +6633,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { prtLock.lock(); try { party = getParty(); - partyLeader = party != null && isPartyLeader(); + partyLeader = isPartyLeader(); } finally { prtLock.unlock(); } @@ -6542,20 +6792,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject { MapleInventory cashInv = this.getInventory(MapleInventoryType.CASH); if (cashInv == null) return; - if (cpnLock.tryLock()) { - effLock.lock(); - chrLock.lock(); - cashInv.lockInventory(); - try { - revertCouponRates(); - setCouponRates(); - } finally { - cpnLock.unlock(); - - cashInv.unlockInventory(); - chrLock.unlock(); - effLock.unlock(); - } + effLock.lock(); + chrLock.lock(); + cashInv.lockInventory(); + try { + revertCouponRates(); + setCouponRates(); + } finally { + cashInv.unlockInventory(); + chrLock.unlock(); + effLock.unlock(); } } @@ -6597,7 +6843,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private List activateCouponsEffects() { List toCommitEffect = new LinkedList<>(); - if(ServerConstants.USE_STACK_COUPON_RATES) { + if(YamlConfig.config.server.USE_STACK_COUPON_RATES) { for(Entry coupon: activeCoupons.entrySet()) { int couponId = coupon.getKey(); int couponQty = coupon.getValue(); @@ -6740,8 +6986,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ret.gachaexp.set(rs.getInt("gachaexp")); ret.mapid = rs.getInt("map"); ret.initialSpawnPoint = rs.getInt("spawnpoint"); - - ret.gmLevel = rs.getInt("gm"); + ret.setGMLevel(rs.getInt("gm")); ret.world = rs.getByte("world"); ret.rank = rs.getInt("rank"); ret.rankMove = rs.getInt("rankMove"); @@ -6794,7 +7039,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ret.inventory[MapleInventoryType.EQUIPPED.ordinal()] = this.getInventory(MapleInventoryType.EQUIPPED); - ret.gmLevel = this.gmLevel(); + ret.setGMLevel(this.gmLevel()); ret.world = this.getWorld(); ret.rank = this.getRank(); ret.rankMove = this.getRankMove(); @@ -6856,7 +7101,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ret.loadCharSkillPoints(rs.getString("sp").split(",")); ret.meso.set(rs.getInt("meso")); ret.merchantmeso = rs.getInt("MerchantMesos"); - ret.gmLevel = rs.getInt("gm"); + ret.setGMLevel(rs.getInt("gm")); ret.skinColor = MapleSkinColor.getById(rs.getInt("skincolor")); ret.gender = rs.getInt("gender"); ret.job = MapleJob.getById(rs.getInt("job")); @@ -6905,7 +7150,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ret.getInventory(MapleInventoryType.SETUP).setSlotLimit(rs.getByte("setupslots")); ret.getInventory(MapleInventoryType.ETC).setSlotLimit(rs.getByte("etcslots")); - byte sandboxCheck = 0x0; + short sandboxCheck = 0x0; for (Pair item : ItemFactory.INVENTORY.loadItems(ret.id, !channelserver)) { sandboxCheck |= item.getLeft().getFlag(); @@ -6952,11 +7197,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { PreparedStatement ps2, ps3; ResultSet rs2, rs3; - ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid > -1"); + ps3 = con.prepareStatement("SELECT petid FROM inventoryitems WHERE characterid = ? AND petid IS NOT NULL"); ps3.setInt(1, charid); rs3 = ps3.executeQuery(); while(rs3.next()) { int petId = rs3.getInt("petid"); + if (rs3.wasNull()) { + petId = -1; + } ps2 = con.prepareStatement("SELECT itemid FROM petignores WHERE petid = ?"); ps2.setInt(1, petId); @@ -7035,7 +7283,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } rs.close(); ps.close(); - ps = con.prepareStatement("SELECT name, characterslots FROM accounts WHERE id = ?", Statement.RETURN_GENERATED_KEYS); + ps = con.prepareStatement("SELECT name, characterslots, language FROM accounts WHERE id = ?", Statement.RETURN_GENERATED_KEYS); ps.setInt(1, ret.accountid); rs = ps.executeQuery(); if (rs.next()) { @@ -7043,6 +7291,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { retClient.setAccountName(rs.getString("name")); retClient.setCharacterSlots(rs.getByte("characterslots")); + retClient.setLanguage(rs.getInt("language")); // thanks Zein for noticing user language not overriding default once player is in-game } rs.close(); ps.close(); @@ -7135,7 +7384,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ps.setInt(1, charid); rs = ps.executeQuery(); while (rs.next()) { - ret.skills.put(SkillFactory.getSkill(rs.getInt("skillid")), new SkillEntry(rs.getByte("skilllevel"), rs.getInt("masterlevel"), rs.getLong("expiration"))); + Skill pSkill = SkillFactory.getSkill(rs.getInt("skillid")); + if(pSkill != null) // edit reported by shavit, thanks Zein for noticing an NPE here + { + ret.skills.put(pSkill, new SkillEntry(rs.getByte("skilllevel"), rs.getInt("masterlevel"), rs.getLong("expiration"))); + } } rs.close(); ps.close(); @@ -7225,7 +7478,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { rs.close(); ps.close(); ret.buddylist.loadFromDb(charid); - ret.storage = MapleStorage.loadOrCreateFromDB(ret.accountid, ret.world); + ret.storage = wserv.getAccountStorage(ret.accountid); int startHp = ret.hp, startMp = ret.mp; ret.reapplyLocalStats(); @@ -7252,7 +7505,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void reloadQuestExpirations() { - for(MapleQuestStatus mqs: quests.values()) { + for(MapleQuestStatus mqs: getStartedQuests()) { if(mqs.getExpirationTime() > 0) { questTimeLimit2(mqs.getQuest(), mqs.getExpirationTime()); } @@ -7306,31 +7559,31 @@ public class MapleCharacter extends AbstractMapleCharacterObject { announce(MaplePacketCreator.sendYellowTip(m)); } - public void updateQuestMobCount(int id) { + public void raiseQuestMobCount(int id) { // It seems nexon uses monsters that don't exist in the WZ (except string) to merge multiple mobs together for these 3 monsters. // We also want to run mobKilled for both since there are some quest that don't use the updated ID... if (id == 1110100 || id == 1110130) { - updateQuestMobCount(9101000); + raiseQuestMobCount(9101000); } else if (id == 2230101 || id == 2230131) { - updateQuestMobCount(9101001); + raiseQuestMobCount(9101001); } else if (id == 1140100 || id == 1140130) { - updateQuestMobCount(9101002); + raiseQuestMobCount(9101002); } int lastQuestProcessed = 0; try { synchronized (quests) { - for (MapleQuestStatus q : quests.values()) { - lastQuestProcessed = q.getQuest().getId(); - if (q.getStatus() == MapleQuestStatus.Status.COMPLETED || q.getQuest().canComplete(this, null)) { + for (MapleQuestStatus qs : getQuests()) { + lastQuestProcessed = qs.getQuest().getId(); + if (qs.getStatus() == MapleQuestStatus.Status.COMPLETED || qs.getQuest().canComplete(this, null)) { continue; } - String progress = q.getProgress(id); - if (!progress.isEmpty() && Integer.parseInt(progress) >= q.getQuest().getMobAmountNeeded(id)) { - continue; - } - if (q.progress(id)) { - client.announce(MaplePacketCreator.updateQuest(q, false)); + + if (qs.progress(id)) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); + if (qs.getInfoNumber() > 0) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); + } } } } @@ -7339,8 +7592,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } - public void mount(int id, int skillid) { - maplemount = new MapleMount(this, id, skillid); + public MapleMount mount(int id, int skillid) { + MapleMount mount = maplemount; + mount.setItemId(id); + mount.setSkillId(skillid); + return mount; } private void playerDead() { @@ -7372,31 +7628,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject { break; } } - if (possesed > 0) { + if (possesed > 0 && !GameConstants.isDojo(getMapId())) { message("You have used a safety charm, so your EXP points have not been decreased."); MapleInventoryManipulator.removeById(client, ItemConstants.getInventoryType(charmID[i]), charmID[i], 1, true, false); - } else if (mapid > 925020000 && mapid < 925030000) { - this.dojoStage = 0; + usedSafetyCharm = true; } else if (getJob() != MapleJob.BEGINNER) { //Hmm... - int XPdummy = ExpTable.getExpNeededForLevel(getLevel()); - if (getMap().isTown()) { - XPdummy /= 100; - } - if (XPdummy == ExpTable.getExpNeededForLevel(getLevel())) { - if (getLuk() <= 100 && getLuk() > 8) { - XPdummy *= (200 - getLuk()) / 2000; - } else if (getLuk() < 8) { - XPdummy /= 10; + if (!FieldLimit.NO_EXP_DECREASE.check(getMap().getFieldLimit())) { // thanks Conrad for noticing missing FieldLimit check + int XPdummy = ExpTable.getExpNeededForLevel(getLevel()); + + if (getMap().isTown()) { // thanks MindLove, SIayerMonkey, HaItsNotOver for noting players only lose 1% on town maps + XPdummy /= 100; } else { - XPdummy /= 20; + if (getLuk() < 50) { // thanks Taiketo, Quit, Fishanelli for noting player EXP loss are fixed, 50-LUK threshold + XPdummy /= 10; + } else { + XPdummy /= 20; + } + } + + int curExp = getExp(); + if (curExp > XPdummy) { + loseExp(XPdummy, false, false); + } else { + loseExp(curExp, false, false); } } - if (getExp() > XPdummy) { - loseExp(XPdummy, false, false); - } else { - loseExp(getExp(), false, false); - } } + if (getBuffedValue(MapleBuffStat.MORPH) != null) { cancelEffectFromBuffStat(MapleBuffStat.MORPH); } @@ -7411,12 +7669,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private void unsitChairInternal() { int chairid = chair.get(); - if (chairid != 0) { + if (chairid >= 0) { if (ItemConstants.isFishingChair(chairid)) { this.getWorldServer().unregisterFisherPlayer(this); } - setChair(0); + setChair(-1); if (unregisterChairBuff()) { getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignChairSkillEffect(this.getId()), false); } @@ -7428,27 +7686,23 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void sitChair(int itemId) { - if (client.tryacquireClient()) { - try { - if (itemId >= 1000000) { // sit on item chair - if (chair.get() == 0) { - setChair(itemId); - getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false); - } - announce(MaplePacketCreator.enableActions()); - } else if (itemId != 0) { // sit on map chair - if (chair.get() == 0) { - setChair(itemId); - if (registerChairBuff()) { - getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false); - } - announce(MaplePacketCreator.cancelChair(itemId)); - } - } else { // stand up - unsitChairInternal(); + if (this.isLoggedinWorld()) { + if (itemId >= 1000000) { // sit on item chair + if (chair.get() < 0) { + setChair(itemId); + getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false); } - } finally { - client.releaseClient(); + announce(MaplePacketCreator.enableActions()); + } else if (itemId >= 0) { // sit on map chair + if (chair.get() < 0) { + setChair(itemId); + if (registerChairBuff()) { + getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false); + } + announce(MaplePacketCreator.cancelChair(itemId)); + } + } else { // stand up + unsitChairInternal(); } } } @@ -7468,7 +7722,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { changeMap(returnMap); cancelAllBuffs(false); // thanks Oblivium91 for finding out players still could revive in area and take damage before returning to town - updateHp(50); + + if (usedSafetyCharm) { // thanks kvmba for noticing safety charm not providing 30% HP/MP + addMPHP((int) Math.ceil(this.getClientMaxHp() * 0.3), (int) Math.ceil(this.getClientMaxMp() * 0.3)); + } else { + updateHp(50); + } + setStance(0); } @@ -7665,7 +7925,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { reapplyLocalStats(); - if (ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) { + if (YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { if (localmaxhp != oldlocalmaxhp) { Pair hpUpdate; @@ -7700,6 +7960,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } private void updateLocalStats() { + prtLock.lock(); effLock.lock(); statWlock.lock(); try { @@ -7711,12 +7972,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { client.announce(MaplePacketCreator.updatePlayerStats(hpmpupdate, true, this)); } - if (oldmaxhp != localmaxhp) { + if (oldmaxhp != localmaxhp) { // thanks Wh1SK3Y for pointing out a deadlock occuring related to party members HP updatePartyMemberHP(); } } finally { statWlock.unlock(); effLock.unlock(); + prtLock.unlock(); } } @@ -7804,7 +8066,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public synchronized void resetStats() { - if(!ServerConstants.USE_AUTOASSIGN_STARTERS_AP) { + if(!YamlConfig.config.server.USE_AUTOASSIGN_STARTERS_AP) { return; } @@ -7862,15 +8124,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void resetEnteredScript() { - if (entered.containsKey(map.getId())) { - entered.remove(map.getId()); - } + entered.remove(map.getId()); } public void resetEnteredScript(int mapId) { - if (entered.containsKey(mapId)) { - entered.remove(mapId); - } + entered.remove(mapId); } public void resetEnteredScript(String script) { @@ -8057,7 +8315,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int[] selectedType; int[] selectedAction; - if(ServerConstants.USE_CUSTOM_KEYSET) { + if(YamlConfig.config.server.USE_CUSTOM_KEYSET) { selectedKey = GameConstants.getCustomKey(true); selectedType = GameConstants.getCustomType(true); selectedAction = GameConstants.getCustomAction(true); @@ -8125,7 +8383,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void saveCharToDB() { - if(ServerConstants.USE_AUTOSAVE) { + if(YamlConfig.config.server.USE_AUTOSAVE) { Runnable r = new Runnable() { @Override public void run() { @@ -8133,7 +8391,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } }; - ThreadManager.getInstance().newTask(r); //spawns a new thread to deal with this + CharacterSaveService service = (CharacterSaveService) getWorldServer().getServiceAccess(WorldServices.SAVE_CHARACTER); + service.registerSaveCharacter(this.getId(), r); } else { saveCharToDB(true); } @@ -8355,8 +8614,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ItemFactory.INVENTORY.saveItems(itemsWithType, id, con); - deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?"); - ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)"); + ps = con.prepareStatement("REPLACE INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)"); ps.setInt(1, id); for (Entry skill : skills.entrySet()) { ps.setInt(2, skill.getKey().getId()); @@ -8450,43 +8708,50 @@ public class MapleCharacter extends AbstractMapleCharacterObject { psf = con.prepareStatement("INSERT INTO medalmaps VALUES (DEFAULT, ?, ?, ?)"); ps.setInt(1, id); - synchronized (quests) { - for (MapleQuestStatus q : quests.values()) { - ps.setInt(2, q.getQuest().getId()); - ps.setInt(3, q.getStatus().getId()); - ps.setInt(4, (int) (q.getCompletionTime() / 1000)); - ps.setLong(5, q.getExpirationTime()); - ps.setInt(6, q.getForfeited()); - ps.setInt(7, q.getCompleted()); - ps.executeUpdate(); - try (ResultSet rs = ps.getGeneratedKeys()) { - rs.next(); - for (int mob : q.getProgress().keySet()) { - pse.setInt(1, id); - pse.setInt(2, rs.getInt(1)); - pse.setInt(3, mob); - pse.setString(4, q.getProgress(mob)); - pse.addBatch(); - } - for (int i = 0; i < q.getMedalMaps().size(); i++) { - psf.setInt(1, id); - psf.setInt(2, rs.getInt(1)); - psf.setInt(3, q.getMedalMaps().get(i)); - psf.addBatch(); - } - pse.executeBatch(); - psf.executeBatch(); + for (MapleQuestStatus qs : getQuests()) { + ps.setInt(2, qs.getQuest().getId()); + ps.setInt(3, qs.getStatus().getId()); + ps.setInt(4, (int) (qs.getCompletionTime() / 1000)); + ps.setLong(5, qs.getExpirationTime()); + ps.setInt(6, qs.getForfeited()); + ps.setInt(7, qs.getCompleted()); + ps.executeUpdate(); + try (ResultSet rs = ps.getGeneratedKeys()) { + rs.next(); + for (int mob : qs.getProgress().keySet()) { + pse.setInt(1, id); + pse.setInt(2, rs.getInt(1)); + pse.setInt(3, mob); + pse.setString(4, qs.getProgress(mob)); + pse.addBatch(); } + for (int i = 0; i < qs.getMedalMaps().size(); i++) { + psf.setInt(1, id); + psf.setInt(2, rs.getInt(1)); + psf.setInt(3, qs.getMedalMaps().get(i)); + psf.addBatch(); + } + pse.executeBatch(); + psf.executeBatch(); } } - } psf.close(); ps.close(); - con.commit(); - con.setAutoCommit(true); - + MapleFamilyEntry familyEntry = getFamilyEntry(); //save family rep + if(familyEntry != null) { + if(familyEntry.saveReputation(con)) familyEntry.savedSuccessfully(); + MapleFamilyEntry senior = familyEntry.getSenior(); + if(senior != null && senior.getChr() == null) { //only save for offline family members + if(senior.saveReputation(con)) senior.savedSuccessfully(); + senior = senior.getSenior(); //save one level up as well + if(senior != null && senior.getChr() == null) { + if(senior.saveReputation(con)) senior.savedSuccessfully(); + } + } + + } if (cashshop != null) { cashshop.save(con); @@ -8497,6 +8762,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { usedStorage = false; } + con.commit(); + con.setAutoCommit(true); // only commit after finishing all "con" usages, thanks Zygon + } catch (SQLException | RuntimeException t) { FilePrinter.printError(FilePrinter.SAVE_CHAR, t, "Error saving " + name + " Level: " + level + " Job: " + job.getId()); try { @@ -8506,6 +8774,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } catch (Exception e) { FilePrinter.printError(FilePrinter.SAVE_CHAR, e, "Error saving " + name + " Level: " + level + " Job: " + job.getId()); + try { + con.rollback(); // thanks Zygon + } catch (SQLException se) { + FilePrinter.printError(FilePrinter.SAVE_CHAR, se, "Error trying to rollback " + name); + } } finally { try { con.setAutoCommit(true); @@ -8556,6 +8829,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { // Always send the macro packet to fix a client side bug when switching characters. client.announce(MaplePacketCreator.getMacros(skillMacros)); } + + public SkillMacro[] getMacros() { + return skillMacros; + } public void sendNote(String to, String msg, byte fame) throws SQLException { sendNote(to, this.getName(), msg, fame); @@ -8875,11 +9152,39 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } updateHpMp(nextHp, nextMp); - return true; } finally { statWlock.unlock(); effLock.unlock(); } + + // autopot on HPMP deplete... thanks shavit for finding out D. Roar doesn't trigger autopot request + if (hpchange < 0) { + MapleKeyBinding autohpPot = this.getKeymap().get(91); + if (autohpPot != null) { + int autohpItemid = autohpPot.getAction(); + if (((float) this.getHp()) / this.getCurrentMaxHp() <= this.getAutopotHpAlert()) { // try within user settings... thanks Lame, Optimist, Stealth2800 + Item autohpItem = this.getInventory(MapleInventoryType.USE).findById(autohpItemid); + if (autohpItem != null) { + PetAutopotProcessor.runAutopotAction(client, autohpItem.getPosition(), autohpItemid); + } + } + } + } + + if (mpchange < 0) { + MapleKeyBinding autompPot = this.getKeymap().get(92); + if (autompPot != null) { + int autompItemid = autompPot.getAction(); + if (((float) this.getMp()) / this.getCurrentMaxMp() <= this.getAutopotMpAlert()) { + Item autompItem = this.getInventory(MapleInventoryType.USE).findById(autompItemid); + if (autompItem != null) { + PetAutopotProcessor.runAutopotAction(client, autompItem.getPosition(), autompItemid); + } + } + } + } + + return true; } public void setInventory(MapleInventoryType type, MapleInventory inv) { @@ -8955,6 +9260,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public void setName(String name) { this.name = name; } + + public void setRPS(MapleRockPaperScissor rps) { + this.rps = rps; + } + + public void closeRPS() { + MapleRockPaperScissor rps = this.rps; + if (rps != null) { + rps.dispose(client); + setRPS(null); + } + } public void changeName(String name) { FredrickProcessor.removeFredrickReminders(this.getId()); @@ -9127,10 +9444,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } private static void setMergeFlag(Item item) { - int flag = item.getFlag(); + short flag = item.getFlag(); flag |= ItemConstants.MERGE_UNTRADEABLE; flag |= ItemConstants.UNTRADEABLE; - item.setFlag((byte) flag); + item.setFlag(flag); } private List getUpgradeableEquipped() { @@ -9541,30 +9858,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } } - - public String getQuestInfo(int quest) { - MapleQuestStatus qs = getQuest(MapleQuest.getInstance(quest)); - return qs.getInfo(); - } - - public void updateQuestInfo(int quest, String info) { - MapleQuest q = MapleQuest.getInstance(quest); + + public void setQuestProgress(int id, int infoNumber, String progress) { + MapleQuest q = MapleQuest.getInstance(id); MapleQuestStatus qs = getQuest(q); - qs.setInfo(info); - - synchronized (quests) { - quests.put(q.getId(), qs); + + if (qs.getInfoNumber() == infoNumber && infoNumber > 0) { + MapleQuest iq = MapleQuest.getInstance(infoNumber); + MapleQuestStatus iqs = getQuest(iq); + iqs.setProgress(0, progress); + } else { + qs.setProgress(infoNumber, progress); // quest progress is thoroughly a string match, infoNumber is actually another questid } - announce(MaplePacketCreator.updateQuest(qs, false)); - if (qs.getQuest().getInfoNumber() > 0) { - announce(MaplePacketCreator.updateQuest(qs, true)); + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); + if (qs.getInfoNumber() > 0) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); } - announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc())); } public void awardQuestPoint(int awardedPoints) { - if (ServerConstants.QUEST_POINT_REQUIREMENT < 1 || awardedPoints < 1) { + if (YamlConfig.config.server.QUEST_POINT_REQUIREMENT < 1 || awardedPoints < 1) { return; } @@ -9572,8 +9886,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { synchronized (quests) { quest_fame += awardedPoints; - delta = quest_fame / ServerConstants.QUEST_POINT_REQUIREMENT; - quest_fame %= ServerConstants.QUEST_POINT_REQUIREMENT; + delta = quest_fame / YamlConfig.config.server.QUEST_POINT_REQUIREMENT; + quest_fame %= YamlConfig.config.server.QUEST_POINT_REQUIREMENT; } if(delta > 0) { @@ -9581,45 +9895,91 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } - public void updateQuest(MapleQuestStatus quest) { - synchronized (quests) { - quests.put(quest.getQuestID(), quest); - } - if (quest.getStatus().equals(MapleQuestStatus.Status.STARTED)) { - announce(MaplePacketCreator.updateQuest(quest, false)); - if (quest.getQuest().getInfoNumber() > 0) { - announce(MaplePacketCreator.updateQuest(quest, true)); - } - announce(MaplePacketCreator.updateQuestInfo((short) quest.getQuest().getId(), quest.getNpc())); - } else if (quest.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) { - MapleQuest mquest = quest.getQuest(); - short questid = mquest.getId(); - if (!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) { - awardQuestPoint(ServerConstants.QUEST_POINT_PER_QUEST_COMPLETE); - } - quest.setCompleted(quest.getCompleted() + 1); // Jayd's idea - count quest completed - - announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime())); - } else if (quest.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) { - announce(MaplePacketCreator.updateQuest(quest, false)); - if (quest.getQuest().getInfoNumber() > 0) { - announce(MaplePacketCreator.updateQuest(quest, true)); - } + public enum DelayedQuestUpdate { // quest updates allow player actions during NPC talk... + UPDATE, FORFEIT, COMPLETE, INFO + } + + private void announceUpdateQuestInternal(MapleCharacter chr, Pair questUpdate) { + Object[] objs = questUpdate.getRight(); + + switch (questUpdate.getLeft()) { + case UPDATE: + announce(MaplePacketCreator.updateQuest(chr, (MapleQuestStatus) objs[0], (Boolean) objs[1])); + break; + + case FORFEIT: + announce(MaplePacketCreator.forfeitQuest((Short) objs[0])); + break; + + case COMPLETE: + announce(MaplePacketCreator.completeQuest((Short) objs[0], (Long) objs[1])); + break; + + case INFO: + MapleQuestStatus qs = (MapleQuestStatus) objs[0]; + announce(MaplePacketCreator.updateQuestInfo(qs.getQuest().getId(), qs.getNpc())); + break; } } - - private void expireQuest(MapleQuest quest) { - if(getQuestStatus(quest.getId()) == MapleQuestStatus.Status.COMPLETED.getId()) { - return; + + public void announceUpdateQuest(DelayedQuestUpdate questUpdateType, Object... params) { + Pair p = new Pair<>(questUpdateType, params); + MapleClient c = this.getClient(); + if (c.getQM() != null || c.getCM() != null) { + synchronized (npcUpdateQuests) { + npcUpdateQuests.add(p); + } + } else { + announceUpdateQuestInternal(this, p); } - if(System.currentTimeMillis() < getMapleQuestStatus(quest.getId()).getExpirationTime()) { - return; + } + + public void flushDelayedUpdateQuests() { + List> qmQuestUpdateList; + + synchronized (npcUpdateQuests) { + qmQuestUpdateList = new ArrayList<>(npcUpdateQuests); + npcUpdateQuests.clear(); } - announce(MaplePacketCreator.questExpire(quest.getId())); - MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED); - newStatus.setForfeited(getQuest(quest).getForfeited() + 1); - updateQuest(newStatus); + for (Pair q : qmQuestUpdateList) { + announceUpdateQuestInternal(this, q); + } + } + + public void updateQuestStatus(MapleQuestStatus qs) { + synchronized (quests) { + quests.put(qs.getQuestID(), qs); + } + if (qs.getStatus().equals(MapleQuestStatus.Status.STARTED)) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); + if (qs.getInfoNumber() > 0) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); + } + announceUpdateQuest(DelayedQuestUpdate.INFO, qs); + } else if (qs.getStatus().equals(MapleQuestStatus.Status.COMPLETED)) { + MapleQuest mquest = qs.getQuest(); + short questid = mquest.getId(); + if (!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) { + awardQuestPoint(YamlConfig.config.server.QUEST_POINT_PER_QUEST_COMPLETE); + } + qs.setCompleted(qs.getCompleted() + 1); // count quest completed Jayd's idea + + announceUpdateQuest(DelayedQuestUpdate.COMPLETE, questid, qs.getCompletionTime()); + //announceUpdateQuest(DelayedQuestUpdate.INFO, qs); // happens after giving rewards, for non-next quests only + } else if (qs.getStatus().equals(MapleQuestStatus.Status.NOT_STARTED)) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); + if (qs.getInfoNumber() > 0) { + announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); + } + // reminder: do not reset quest progress of infoNumbers, some quests cannot backtrack + } + } + + private void expireQuest(MapleQuest quest) { + if (quest.forfeit(this)) { + announce(MaplePacketCreator.questExpire(quest.getId())); + } } public void cancelQuestExpirationTask() { @@ -9758,7 +10118,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (!this.isHidden() || client.getPlayer().gmLevel() > 1) { client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false)); - if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock this function + if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock sendSpawnData client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id)); } } @@ -9870,6 +10230,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void autoban(String reason) { + if (this.isGM() || this.isBanned()){ // thanks RedHat for noticing GM's being able to get banned + return; + } + this.ban(reason); announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for HACK reason.#k", "HeavenMS"))); TimerManager.getInstance().schedule(new Runnable() { @@ -10049,7 +10413,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private Collection getUpgradeableEquipList() { Collection fullList = getInventory(MapleInventoryType.EQUIPPED).list(); - if (ServerConstants.USE_EQUIPMNT_LVLUP_CASH) { + if (YamlConfig.config.server.USE_EQUIPMNT_LVLUP_CASH) { return fullList; } @@ -10193,7 +10557,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { mpc = null; mgc = null; party = null; - family = null; + MapleFamilyEntry familyEntry = getFamilyEntry(); + if(familyEntry != null) { + familyEntry.setCharacter(null); + setFamilyEntry(null); + } getWorldServer().registerTimedMapObject(new Runnable() { @Override @@ -10217,6 +10585,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject { e.printStackTrace(); } } + + public void setLoginTime(long time) { + this.loginTime = time; + } + + public long getLoginTime() { + return loginTime; + } + + public long getLoggedInTime() { + return System.currentTimeMillis() - loginTime; + } public boolean isLoggedin() { return loggedIn; @@ -10267,6 +10647,22 @@ public class MapleCharacter extends AbstractMapleCharacterObject { this.dragon = dragon; } + public void setAutopotHpAlert(float hpPortion) { + autopotHpAlert = hpPortion; + } + + public float getAutopotHpAlert() { + return autopotHpAlert; + } + + public void setAutopotMpAlert(float mpPortion) { + autopotMpAlert = mpPortion; + } + + public float getAutopotMpAlert() { + return autopotMpAlert; + } + public long getJailExpirationTimeLeft() { return jailExpiration - System.currentTimeMillis(); } @@ -10289,6 +10685,379 @@ public class MapleCharacter extends AbstractMapleCharacterObject { jailExpiration = 0; } + public boolean registerNameChange(String newName) { + try (Connection con = DatabaseConnection.getConnection()) { + //check for pending name change + long currentTimeMillis = System.currentTimeMillis(); + try (PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM namechanges WHERE characterid=?")) { //double check, just in case + ps.setInt(1, getId()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + Timestamp completedTimestamp = rs.getTimestamp("completionTime"); + if(completedTimestamp == null) return false; //pending + else if(completedTimestamp.getTime() + YamlConfig.config.server.NAME_CHANGE_COOLDOWN > currentTimeMillis) return false; + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to register name change for character " + getName() + "."); + return false; + } + try (PreparedStatement ps = con.prepareStatement("INSERT INTO namechanges (characterid, old, new) VALUES (?, ?, ?)")){ + ps.setInt(1, getId()); + ps.setString(2, getName()); + ps.setString(3, newName); + ps.executeUpdate(); + this.pendingNameChange = true; + return true; + } catch (SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to register name change for character " + getName() + "."); + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to get DB connection."); + } + return false; + } + + public boolean cancelPendingNameChange() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("DELETE FROM namechanges WHERE characterid=? AND completionTime IS NULL")) { + ps.setInt(1, getId()); + int affectedRows = ps.executeUpdate(); + if(affectedRows > 0) pendingNameChange = false; + return affectedRows > 0; //rows affected + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to cancel name change for character " + getName() + "."); + return false; + } + } + + public void doPendingNameChange() { //called on logout + if(!pendingNameChange) return; + try (Connection con = DatabaseConnection.getConnection()) { + int nameChangeId = -1; + String newName = null; + try (PreparedStatement ps = con.prepareStatement("SELECT * FROM namechanges WHERE characterid = ? AND completionTime IS NULL")) { + ps.setInt(1, getId()); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) return; + nameChangeId = rs.getInt("id"); + newName = rs.getString("new"); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to retrieve pending name changes for character " + getName() + "."); + } + con.setAutoCommit(false); + boolean success = doNameChange(con, getId(), getName(), newName, nameChangeId); + if(!success) con.rollback(); + else FilePrinter.print(FilePrinter.CHANGE_CHARACTER_NAME, "Name change applied : from \"" + getName() + "\" to \"" + newName + "\" at " + Calendar.getInstance().getTime().toString()); + con.setAutoCommit(true); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to get DB connection."); + } + } + + public static void doNameChange(int characterId, String oldName, String newName, int nameChangeId) { //Don't do this while player is online + try (Connection con = DatabaseConnection.getConnection()) { + con.setAutoCommit(false); + boolean success = doNameChange(con, characterId, oldName, newName, nameChangeId); + if(!success) con.rollback(); + con.setAutoCommit(true); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to get DB connection."); + } + } + + public static boolean doNameChange(Connection con, int characterId, String oldName, String newName, int nameChangeId) { + try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET name = ? WHERE id = ?")) { + ps.setString(1, newName); + ps.setInt(2, characterId); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE rings SET partnername = ? WHERE partnername = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + /*try (PreparedStatement ps = con.prepareStatement("UPDATE playernpcs SET name = ? WHERE name = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE gifts SET `from` = ? WHERE `from` = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE dueypackages SET SenderName = ? WHERE SenderName = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE dueypackages SET SenderName = ? WHERE SenderName = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE inventoryitems SET owner = ? WHERE owner = ?")) { //GMS doesn't do this + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE mts_items SET owner = ? WHERE owner = ?")) { //GMS doesn't do this + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE newyear SET sendername = ? WHERE sendername = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE newyear SET receivername = ? WHERE receivername = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE notes SET `to` = ? WHERE `to` = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE notes SET `from` = ? WHERE `from` = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE nxcode SET retriever = ? WHERE retriever = ?")) { + ps.setString(1, newName); + ps.setString(2, oldName); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + }*/ + if(nameChangeId != -1) { + try (PreparedStatement ps = con.prepareStatement("UPDATE namechanges SET completionTime = ? WHERE id = ?")) { + ps.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + ps.setInt(2, nameChangeId); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Character ID : " + characterId); + return false; + } + } + return true; + } + + public int checkWorldTransferEligibility() { + if(getLevel() < 20) { + return 2; + } else if(getClient().getTempBanCalendar() != null && getClient().getTempBanCalendar().getTimeInMillis() + (30*24*60*60*1000) < Calendar.getInstance().getTimeInMillis()) { + return 3; + } else if(isMarried()) { + return 4; + } else if(getGuildRank() < 2) { + return 5; + } else if(getFamily() != null) { + return 8; + } else { + return 0; + } + } + + public static String checkWorldTransferEligibility(Connection con, int characterId, int oldWorld, int newWorld) { + if(!YamlConfig.config.server.ALLOW_CASHSHOP_WORLD_TRANSFER) return "World transfers disabled."; + int accountId = -1; + try (PreparedStatement ps = con.prepareStatement("SELECT accountid, level, guildid, guildrank, partnerId, familyId FROM characters WHERE id = ?")) { + ps.setInt(1, characterId); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) return "Character does not exist."; + accountId = rs.getInt("accountid"); + if(rs.getInt("level") < 20) return "Character is under level 20."; + if(rs.getInt("familyId") != -1) return "Character is in family."; + if(rs.getInt("partnerId") != 0) return "Character is married."; + if(rs.getInt("guildid") != 0 && rs.getInt("guildrank") < 2) return "Character is the leader of a guild."; + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e); + return "SQL Error"; + } + try (PreparedStatement ps = con.prepareStatement("SELECT tempban FROM accounts WHERE id = ?")) { + ps.setInt(1, accountId); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) return "Account does not exist."; + if(rs.getLong("tempban") != 0 && !rs.getString("tempban").equals("2018-06-20 00:00:00.0")) return "Account has been banned."; + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e); + return "SQL Error"; + } + try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) AS rowcount FROM characters WHERE accountid = ? AND world = ?")) { + ps.setInt(1, accountId); + ps.setInt(2, newWorld); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) return "SQL Error"; + if(rs.getInt("rowcount") >= 3) return "Too many characters on destination world."; + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e); + return "SQL Error"; + } + return null; + } + + public boolean registerWorldTransfer(int newWorld) { + try (Connection con = DatabaseConnection.getConnection()) { + //check for pending world transfer + long currentTimeMillis = System.currentTimeMillis(); + try (PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM worldtransfers WHERE characterid=?")) { //double check, just in case + ps.setInt(1, getId()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + Timestamp completedTimestamp = rs.getTimestamp("completionTime"); + if(completedTimestamp == null) return false; //pending + else if(completedTimestamp.getTime() + YamlConfig.config.server.WORLD_TRANSFER_COOLDOWN > currentTimeMillis) return false; + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to register world transfer for character " + getName() + "."); + return false; + } + try (PreparedStatement ps = con.prepareStatement("INSERT INTO worldtransfers (characterid, `from`, `to`) VALUES (?, ?, ?)")){ + ps.setInt(1, getId()); + ps.setInt(2, getWorld()); + ps.setInt(3, newWorld); + ps.executeUpdate(); + return true; + } catch (SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to register world transfer for character " + getName() + "."); + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to get DB connection."); + } + return false; + } + + public boolean cancelPendingWorldTranfer() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("DELETE FROM worldtransfers WHERE characterid=? AND completionTime IS NULL")) { + ps.setInt(1, getId()); + int affectedRows = ps.executeUpdate(); + return affectedRows > 0; //rows affected + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to cancel pending world transfer for character " + getName() + "."); + return false; + } + } + + public static boolean doWorldTransfer(Connection con, int characterId, int oldWorld, int newWorld, int worldTransferId) { + int mesos = 0; + try (PreparedStatement ps = con.prepareStatement("SELECT meso FROM characters WHERE id = ?")) { + ps.setInt(1, characterId); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) { + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, "Character data invalid? (charid " + characterId + ")"); + return false; + } + mesos = rs.getInt("meso"); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET world = ?, meso = ?, guildid = ?, guildrank = ? WHERE id = ?")) { + ps.setInt(1, newWorld); + ps.setInt(2, Math.min(mesos, 1000000)); // might want a limit in "YamlConfig.config.server" for this + ps.setInt(3, 0); + ps.setInt(4, 5); + ps.setInt(5, characterId); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Character ID : " + characterId); + return false; + } + try (PreparedStatement ps = con.prepareStatement("DELETE FROM buddies WHERE characterid = ? OR buddyid = ?")) { + ps.setInt(1, characterId); + ps.setInt(2, characterId); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Character ID : " + characterId); + return false; + } + if(worldTransferId != -1) { + try (PreparedStatement ps = con.prepareStatement("UPDATE worldtransfers SET completionTime = ? WHERE id = ?")) { + ps.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + ps.setInt(2, worldTransferId); + ps.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Character ID : " + characterId); + return false; + } + } + return true; + } + public String getLastCommandMessage() { return this.commandtext; } @@ -10337,7 +11106,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void setReborns(int value) { - if (!ServerConstants.USE_REBIRTH_SYSTEM) { + if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { yellowMessage("Rebirth system is not enabled!"); throw new NotEnabledException(); } @@ -10362,7 +11131,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public int getReborns() { - if (!ServerConstants.USE_REBIRTH_SYSTEM) { + if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { yellowMessage("Rebirth system is not enabled!"); throw new NotEnabledException(); } @@ -10385,7 +11154,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void executeReborn() { - if (!ServerConstants.USE_REBIRTH_SYSTEM) { + if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) { yellowMessage("Rebirth system is not enabled!"); throw new NotEnabledException(); } diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 67589efabb..aad0849b22 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -44,13 +44,13 @@ import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; +import config.YamlConfig; +import jdk.nashorn.api.scripting.NashornScriptEngine; import tools.*; -import javax.script.ScriptEngine; - import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.channel.Channel; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; @@ -63,8 +63,7 @@ import net.server.world.World; import org.apache.mina.core.session.IoSession; import client.inventory.MapleInventoryType; -import constants.GameConstants; -import constants.ServerConstants; +import constants.game.GameConstants; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.event.EventManager; @@ -73,18 +72,16 @@ import scripting.npc.NPCScriptManager; import scripting.quest.QuestActionManager; import scripting.quest.QuestScriptManager; import server.life.MapleMonster; -import server.MapleTrade; import server.ThreadManager; import server.maps.*; -import server.quest.MapleQuest; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.MapleLoginBypassCoordinator; +import net.server.coordinator.login.MapleLoginBypassCoordinator; public class MapleClient { - public static final int LOGIN_NOTLOGGEDIN = 0; + public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; public static final int LOGIN_LOGGEDIN = 2; public static final String CLIENT_KEY = "CLIENT"; @@ -95,7 +92,7 @@ public class MapleClient { private MapleAESOFB send; private MapleAESOFB receive; private final IoSession session; - private MapleCharacter player; + private MapleCharacter player; private int channel = 1; private int accId = -4; private boolean loggedIn = false; @@ -106,7 +103,7 @@ public class MapleClient { private long lastPong; private int gmlevel; private Set macs = new HashSet<>(); - private Map engines = new HashMap<>(); + private Map engines = new HashMap<>(); private byte characterSlots = 3; private byte loginattempt = 0; private String pin = ""; @@ -120,19 +117,31 @@ public class MapleClient { private final Semaphore actionsSemaphore = new Semaphore(7); private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true); private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true); - private static final Lock loginLocks[] = new Lock[200]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here + private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true); + private static final int lockCount = 200; + private static final Lock loginLocks[] = new Lock[lockCount]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here + private Calendar tempBanCalendar; private int votePoints; private int voteTime = -1; private int visibleWorlds; private long lastNpcClick; private long sessionId; + private long lastPacket = System.currentTimeMillis(); private int lang = 0; static { - for (int i = 0; i < 200; i++) { + for (int i = 0; i < lockCount; i++) { loginLocks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true); } } + + public void updateLastPacket() { + lastPacket = System.currentTimeMillis(); + } + + public long getLastPacket() { + return lastPacket; + } public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { this.send = send; @@ -444,14 +453,14 @@ public class MapleClient { } public int finishLogin() { - Lock loginLock = loginLocks[this.getAccID() % 200]; + Lock loginLock = loginLocks[this.getAccID() % lockCount]; loginLock.lock(); try { if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN loggedIn = false; return 7; } - updateLoginState(LOGIN_LOGGEDIN); + updateLoginState(MapleClient.LOGIN_LOGGEDIN); } finally { loginLock.unlock(); } @@ -480,7 +489,7 @@ public class MapleClient { } public boolean checkPin(String other) { - if (!(ServerConstants.ENABLE_PIN && !canBypassPin())) { + if (!(YamlConfig.config.server.ENABLE_PIN && !canBypassPin())) { return true; } @@ -517,7 +526,7 @@ public class MapleClient { } public boolean checkPic(String other) { - if (!(ServerConstants.ENABLE_PIC && !canBypassPic())) { + if (!(YamlConfig.config.server.ENABLE_PIC && !canBypassPic())) { return true; } @@ -583,7 +592,7 @@ public class MapleClient { loginok = (tos == 0) ? 23 : 0; } else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd)) { // thanks GabrielSin for detecting some no-bcrypt inconsistencies here - loginok = (tos == 0) ? (!ServerConstants.BCRYPT_MIGRATION ? 23 : -23) : (!ServerConstants.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt + loginok = (tos == 0) ? (!YamlConfig.config.server.BCRYPT_MIGRATION ? 23 : -23) : (!YamlConfig.config.server.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt } else { loggedIn = false; loginok = 4; @@ -640,7 +649,7 @@ public class MapleClient { } } - public Calendar getTempBanCalendar() { + public Calendar getTempBanCalendarFromDB() { Connection con = null; PreparedStatement ps = null; ResultSet rs = null; @@ -654,10 +663,12 @@ public class MapleClient { return null; } long blubb = rs.getLong("tempban"); - if (blubb == 0) { // basically if timestamp in db is 0000-00-00 + + if (blubb == 0 || rs.getString("tempban").equals("2018-06-20 00:00:00.0")) { // 0000-00-00 or 2018-06-20 (default set in LoginPasswordHandler) return null; } lTempban.setTimeInMillis(rs.getTimestamp("tempban").getTime()); + tempBanCalendar = lTempban; return lTempban; } catch (SQLException e) { e.printStackTrace(); @@ -678,6 +689,14 @@ public class MapleClient { } return null;//why oh why!?! } + + public Calendar getTempBanCalendar() { + return tempBanCalendar; + } + + public boolean hasBeenBanned() { + return tempBanCalendar != null; + } public static long dottedQuadToLong(String dottedQuad) throws RuntimeException { String[] quads = dottedQuad.split("\\."); @@ -831,7 +850,7 @@ public class MapleClient { if (rs.getTimestamp("lastlogin").getTime() + 30000 < Server.getInstance().getCurrentTime()) { int accountId = accId; state = LOGIN_NOTLOGGEDIN; - updateLoginState(LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa this.setAccID(accountId); } } @@ -865,9 +884,9 @@ public class MapleClient { MapleMap map = player.getMap(); final MapleParty party = player.getParty(); final int idz = player.getId(); - final MaplePartyCharacter chrp = new MaplePartyCharacter(player); if (party != null) { + final MaplePartyCharacter chrp = new MaplePartyCharacter(player); chrp.setOnline(false); wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); if (party.getLeader().getId() == idz && map != null) { @@ -977,14 +996,9 @@ public class MapleClient { family. } */ - for (MapleQuestStatus status : player.getStartedQuests()) { //This is for those quests that you have to stay logged in for a certain amount of time - MapleQuest quest = status.getQuest(); - if (quest.getTimeLimit() > 0) { - MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED); - newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1); - player.updateQuest(newStatus); - } - } + + player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time + if (guild != null) { final Server server = Server.getInstance(); server.setGuildMemberOnline(player, false, player.getClient().getChannel()); @@ -1017,6 +1031,7 @@ public class MapleClient { player.saveCharToDB(true); player.logOff(); + if(YamlConfig.config.server.INSTANT_NAME_CHANGE) player.doPendingNameChange(); clear(); } else { getChannelServer().removePlayer(player); @@ -1038,7 +1053,8 @@ public class MapleClient { MapleSessionCoordinator.getInstance().closeSession(session, false); session.removeAttribute(MapleClient.CLIENT_KEY); } - if (!Server.getInstance().hasCharacteridInTransition(session)) { + + if (!Server.getInstance().hasCharacteridInTransition(this)) { updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } @@ -1064,6 +1080,12 @@ public class MapleClient { this.send = null; //this.session = null; } + + public void setCharacterOnSessionTransitionState(int cid) { + this.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); + session.setAttribute(MapleClient.CLIENT_TRANSITION); + Server.getInstance().setCharacteridInTransition(this, cid); + } public int getChannel() { return channel; @@ -1162,11 +1184,11 @@ public class MapleClient { gmlevel = level; } - public void setScriptEngine(String name, ScriptEngine e) { + public void setScriptEngine(String name, NashornScriptEngine e) { engines.put(name, e); } - public ScriptEngine getScriptEngine(String name) { + public NashornScriptEngine getScriptEngine(String name) { return engines.get(name); } @@ -1210,6 +1232,22 @@ public class MapleClient { } return disconnectForBeingAFaggot; } + + public void checkChar(int accid) { /// issue with multiple chars from same account login found by shavit, resinate + if (!YamlConfig.config.server.USE_CHARACTER_ACCOUNT_CHECK) { + return; + } + + for (World w : Server.getInstance().getWorlds()) { + for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) { + if (accid == chr.getAccountID()) { + FilePrinter.print(FilePrinter.EXPLOITS, "Player: " + chr.getName() + " has been removed from " + GameConstants.WORLD_NAMES[w.getId()] + ". Possible Dupe attempt."); + chr.getClient().forceDisconnect(); + w.getPlayerStorage().removePlayer(chr.getId()); + } + } + } + } public int getVotePoints(){ int points = 0; @@ -1285,12 +1323,18 @@ public class MapleClient { actionsSemaphore.release(); } - public void lockEncoder() { - encoderLock.lock(); + public boolean tryacquireEncoder() { + if (actionsSemaphore.tryAcquire()) { + encoderLock.lock(); + return true; + } else { + return false; + } } public void unlockEncoder() { encoderLock.unlock(); + actionsSemaphore.release(); } private static class CharNameAndId { @@ -1323,6 +1367,10 @@ public class MapleClient { return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world)); } + public short getAvailableCharacterWorldSlots(int world) { + return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world)); + } + public short getCharacterSlots() { return characterSlots; } @@ -1436,8 +1484,13 @@ public class MapleClient { } } - public synchronized void announce(final byte[] packet) {//MINA CORE IS A FUCKING BITCH AND I HATE IT <3 - session.write(packet); + public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce" + announcerLock.lock(); + try { + session.write(packet); + } finally { + announcerLock.unlock(); + } } public void announceHint(String msg, int length) { @@ -1492,8 +1545,7 @@ public class MapleClient { player.saveCharToDB(); - player.getClient().updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - player.setSessionTransitionState(); + player.setSessionTransitionState(); try { announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]))); } catch (IOException e) { diff --git a/src/client/MapleDisease.java b/src/client/MapleDisease.java index 14984e7293..4dd866f050 100644 --- a/src/client/MapleDisease.java +++ b/src/client/MapleDisease.java @@ -21,7 +21,7 @@ */ package client; -import constants.GameConstants; +import constants.game.GameConstants; public enum MapleDisease { NULL(0x0), diff --git a/src/client/MapleFamily.java b/src/client/MapleFamily.java index 51eb441159..bae907b941 100644 --- a/src/client/MapleFamily.java +++ b/src/client/MapleFamily.java @@ -25,75 +25,279 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import net.server.Server; +import net.server.world.World; import tools.DatabaseConnection; +import tools.FilePrinter; +import tools.MaplePacketCreator; +import tools.Pair; /** * * @author Jay Estrella - Mr.Trash :3 + * @author Ubaware */ public class MapleFamily { - private static int id; - private static Map members = new HashMap(); - public MapleFamily(int cid) { - try { - Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT familyid FROM family_character WHERE cid = ?"); - ps.setInt(1, cid); - ResultSet rs = ps.executeQuery(); - if (rs.next()) { - id = rs.getInt("familyid"); - } - ps.close(); - rs.close(); - con.close(); - getMapleFamily(); - } catch (SQLException ex) { - ex.printStackTrace(); - } - } + private static final AtomicInteger familyIDCounter = new AtomicInteger(); - private static void getMapleFamily() { - try { - Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT * FROM family_character WHERE familyid = ?"); - ps.setInt(1, id); - ResultSet rs = ps.executeQuery(); - while (rs.next()) { - MapleFamilyEntry ret = new MapleFamilyEntry(); - ret.setFamilyId(id); - ret.setRank(rs.getInt("rank")); - ret.setReputation(rs.getInt("reputation")); - ret.setTotalJuniors(rs.getInt("totaljuniors")); - ret.setFamilyName(rs.getString("name")); - ret.setJuniors(rs.getInt("juniorsadded")); - ret.setTodaysRep(rs.getInt("todaysrep")); - int cid = rs.getInt("cid"); - ret.setChrId(cid); - members.put(cid, ret); - } - rs.close(); - ps.close(); - con.close(); - } catch (SQLException sqle) { - sqle.printStackTrace(); - } - } + private final int id, world; + private final Map members = new ConcurrentHashMap(); + private MapleFamilyEntry leader; + private String name; + private String preceptsMessage = ""; + private int totalGenerations; - public MapleFamilyEntry getMember(int cid) { - if (members.containsKey(cid)){ - return members.get(cid); - } - return null; - } - - public Map getMembers() { - return members; - } - - public void broadcast(byte[] packet) { - // family currently not developed + public MapleFamily(int id, int world) { + int newId = id; + if(id == -1) { + // get next available family id + while(idInUse(newId = familyIDCounter.incrementAndGet())) { + } } + this.id = newId; + this.world = world; + } + + private static boolean idInUse(int id) { + for(World world : Server.getInstance().getWorlds()) { + if(world.getFamily(id) != null) return true; + } + return false; + } + + public int getID() { + return id; + } + + public int getWorld() { + return world; + } + + public void setLeader(MapleFamilyEntry leader) { + this.leader = leader; + setName(leader.getName()); + } + + public MapleFamilyEntry getLeader() { + return leader; + } + + private void setName(String name) { + this.name = name; + } + + public int getTotalMembers() { + return members.size(); + } + + public int getTotalGenerations() { + return totalGenerations; + } + + public void setTotalGenerations(int generations) { + this.totalGenerations = generations; + } + + public String getName() { + return this.name; + } + + public void setMessage(String message, boolean save) { + this.preceptsMessage = message; + if(save) { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE family_character SET precepts = ? WHERE cid = ?")) { + ps.setString(1, message); + ps.setInt(2, getLeader().getChrId()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not save new precepts for family " + getID() + "."); + e.printStackTrace(); + } + } + } + + public String getMessage() { + return preceptsMessage; + } + + public void addEntry(MapleFamilyEntry entry) { + members.put(entry.getChrId(), entry); + } + + public void removeEntryBranch(MapleFamilyEntry root) { + members.remove(root.getChrId()); + for(MapleFamilyEntry junior : root.getJuniors()) { + if(junior != null) removeEntryBranch(junior); + } + } + + public void addEntryTree(MapleFamilyEntry root) { + members.put(root.getChrId(), root); + for(MapleFamilyEntry junior : root.getJuniors()) { + if(junior != null) addEntryTree(junior); + } + } + + public MapleFamilyEntry getEntryByID(int cid) { + return members.get(cid); + } + + public void broadcast(byte[] packet) { + broadcast(packet, -1); + } + + public void broadcast(byte[] packet, int ignoreID) { + for(MapleFamilyEntry entry : members.values()) { + MapleCharacter chr = entry.getChr(); + if(chr != null) { + if(chr.getId() == ignoreID) continue; + chr.getClient().announce(packet); + } + } + } + + public void broadcastFamilyInfoUpdate() { + for(MapleFamilyEntry entry : members.values()) { + MapleCharacter chr = entry.getChr(); + if(chr != null) { + chr.getClient().announce(MaplePacketCreator.getFamilyInfo(entry)); + } + } + } + + public void resetDailyReps() { + for(MapleFamilyEntry entry : members.values()) { + entry.setTodaysRep(0); + entry.setRepsToSenior(0); + entry.resetEntitlementUsages(); + } + } + + public static void loadAllFamilies() { + try(Connection con = DatabaseConnection.getConnection()) { + List, MapleFamilyEntry>> unmatchedJuniors = new ArrayList, MapleFamilyEntry>>(200); // < familyEntry> + try(PreparedStatement psEntries = con.prepareStatement("SELECT * FROM family_character")) { + ResultSet rsEntries = psEntries.executeQuery(); + while(rsEntries.next()) { // can be optimized + int cid = rsEntries.getInt("cid"); + String name = null; + int level = -1; + int jobID = -1; + int world = -1; + try(PreparedStatement ps = con.prepareStatement("SELECT world, name, level, job FROM characters WHERE id = ?")) { + ps.setInt(1, cid); + ResultSet rs = ps.executeQuery(); + if(rs.next()) { + world = rs.getInt("world"); + name = rs.getString("name"); + level = rs.getInt("level"); + jobID = rs.getInt("job"); + } else { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not load character information of " + cid + " in loadAllFamilies(). (RECORD DOES NOT EXIST)"); + continue; + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not load character information of " + cid + " in loadAllFamilies(). (SQL ERROR)"); + continue; + } + int familyid = rsEntries.getInt("familyid"); + int seniorid = rsEntries.getInt("seniorid"); + int reputation = rsEntries.getInt("reputation"); + int todaysRep = rsEntries.getInt("todaysrep"); + int totalRep = rsEntries.getInt("totalreputation"); + int repsToSenior = rsEntries.getInt("reptosenior"); + String precepts = rsEntries.getString("precepts"); + //Timestamp lastResetTime = rsEntries.getTimestamp("lastresettime"); //taken care of by FamilyDailyResetTask + World wserv = Server.getInstance().getWorld(world); + if (wserv == null) { + continue; + } + MapleFamily family = wserv.getFamily(familyid); + if(family == null) { + family = new MapleFamily(familyid, world); + Server.getInstance().getWorld(world).addFamily(familyid, family); + } + MapleFamilyEntry familyEntry = new MapleFamilyEntry(family, cid, name, level, MapleJob.getById(jobID)); + family.addEntry(familyEntry); + if(seniorid <= 0) { + family.setLeader(familyEntry); + family.setMessage(precepts, false); + } + MapleFamilyEntry senior = family.getEntryByID(seniorid); + if(senior != null) { + familyEntry.setSenior(family.getEntryByID(seniorid), false); + } else { + if(seniorid > 0) unmatchedJuniors.add(new Pair, MapleFamilyEntry>(new Pair(world, seniorid), familyEntry)); + } + familyEntry.setReputation(reputation); + familyEntry.setTodaysRep(todaysRep); + familyEntry.setTotalReputation(totalRep); + familyEntry.setRepsToSenior(repsToSenior); + //load used entitlements + try (PreparedStatement ps = con.prepareStatement("SELECT entitlementid FROM family_entitlement WHERE charid = ?")) { + ps.setInt(1, familyEntry.getChrId()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + familyEntry.setEntitlementUsed(rs.getInt("entitlementid")); + } + } + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get family_character entries."); + e.printStackTrace(); + } + // link missing ones (out of order) + for(Pair, MapleFamilyEntry> unmatchedJunior : unmatchedJuniors) { + int world = unmatchedJunior.getLeft().getLeft(); + int seniorid = unmatchedJunior.getLeft().getRight(); + MapleFamilyEntry junior = unmatchedJunior.getRight(); + MapleFamilyEntry senior = Server.getInstance().getWorld(world).getFamily(junior.getFamily().getID()).getEntryByID(seniorid); + if(senior != null) { + junior.setSenior(senior, false); + } else { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Missing senior for character " + junior.getName() + " in world " + world); + } + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get DB connection."); + e.printStackTrace(); + } + for(World world : Server.getInstance().getWorlds()) { + for(MapleFamily family : world.getFamilies()) { + family.getLeader().doFullCount(); + } + } + } + + public void saveAllMembersRep() { //was used for autosave task, but character autosave should be enough + try(Connection con = DatabaseConnection.getConnection()) { + con.setAutoCommit(false); + boolean success = true; + for(MapleFamilyEntry entry : members.values()) { + success = entry.saveReputation(con); + if(!success) break; + } + if(!success) { + con.rollback(); + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Family rep autosave failed for family " + getID() + " on " + Calendar.getInstance().getTime().toString() + "."); + } + con.setAutoCommit(true); + //reset repChanged after successful save + for(MapleFamilyEntry entry : members.values()) { + entry.savedSuccessfully(); + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + } + } } diff --git a/src/client/MapleFamilyEntitlement.java b/src/client/MapleFamilyEntitlement.java new file mode 100644 index 0000000000..fed32c302f --- /dev/null +++ b/src/client/MapleFamilyEntitlement.java @@ -0,0 +1,41 @@ +package client; + +public enum MapleFamilyEntitlement { + FAMILY_REUINION(1, 300, "Family Reunion", "[Target] Me\\n[Effect] Teleport directly to the Family member of your choice."), + SUMMON_FAMILY(1, 500, "Summon Family", "[Target] 1 Family member\\n[Effect] Summon a Family member of choice to the map you're in."), + SELF_DROP_1_5(1, 700, "My Drop Rate 1.5x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] Monster drop rate will be increased #c1.5x#.\\n* If the Drop Rate event is in progress, this will be nullified."), + SELF_EXP_1_5(1, 800, "My EXP 1.5x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] EXP earned from hunting will be increased #c1.5x#.\\n* If the EXP event is in progress, this will be nullified."), + FAMILY_BONDING(1, 1000, "Family Bonding (30 min)", "[Target] At least 6 Family members online that are below me in the Pedigree\\n[Time] 30 min.\\n[Effect] Monster drop rate and EXP earned will be increased #c2x#. \\n* If the EXP event is in progress, this will be nullified."), + SELF_DROP_2(1, 1200, "My Drop Rate 2x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."), + SELF_EXP_2(1, 1500, "My EXP 2x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] EXP earned from hunting will be increased #c2x#.\\n* If the EXP event is in progress, this will be nullified."), + SELF_DROP_2_30MIN(1, 2000, "My Drop Rate 2x (30 min)", "[Target] Me\\n[Time] 30 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."), + SELF_EXP_2_30MIN(1, 2500, "My EXP 2x (30 min)", "[Target] Me\\n[Time] 30 min.\\n[Effect] EXP earned from hunting will be increased #c2x#. \\n* If the EXP event is in progress, this will be nullified."), + PARTY_DROP_2_30MIN(1, 4000, "My Party Drop Rate 2x (30 min)", "[Target] My party\\n[Time] 30 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."), + PARTY_EXP_2_30MIN(1, 5000, "My Party EXP 2x (30 min)", "[Target] My party\\n[Time] 30 min.\\n[Effect] EXP earned from hunting will be increased #c2x#.\\n* If the EXP event is in progress, this will be nullified."); + + private final int usageLimit, repCost; + private final String name, description; + + private MapleFamilyEntitlement(int usageLimit, int repCost, String name, String description) { + this.usageLimit = usageLimit; + this.repCost = repCost; + this.name = name; + this.description = description; + } + + public int getUsageLimit() { + return usageLimit; + } + + public int getRepCost() { + return repCost; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } +} diff --git a/src/client/MapleFamilyEntry.java b/src/client/MapleFamilyEntry.java index af8ac7e37a..82858ae131 100644 --- a/src/client/MapleFamilyEntry.java +++ b/src/client/MapleFamilyEntry.java @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -21,33 +19,210 @@ */ package client; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import net.server.Server; +import tools.DatabaseConnection; +import tools.FilePrinter; +import tools.MaplePacketCreator; +import tools.Pair; + +/** + * @author Ubaware + */ + public class MapleFamilyEntry { - private int familyId; - private int rank, reputation, totalReputation, todaysRep, totalJuniors, juniors, chrid; - private String familyName; + private final int characterID; + private volatile MapleFamily family; + private volatile MapleCharacter character; - public int getId() { - return familyId; + private volatile MapleFamilyEntry senior; + private final MapleFamilyEntry[] juniors = new MapleFamilyEntry[2]; + private final int[] entitlements = new int[11]; + private volatile int reputation, totalReputation; + private volatile int todaysRep, repsToSenior; //both are daily values + private volatile int totalJuniors, totalSeniors; + + private volatile int generation; + + private volatile boolean repChanged; //used to ignore saving unchanged rep values + + // cached values for offline players + private String charName; + private int level; + private MapleJob job; + + public MapleFamilyEntry(MapleFamily family, int characterID, String charName, int level, MapleJob job) { + this.family = family; + this.characterID = characterID; + this.charName = charName; + this.level = level; + this.job = job; } - public void setFamilyId(int familyId) { - this.familyId = familyId; + public MapleCharacter getChr() { + return character; } - public int getRank() { - return rank; + public void setCharacter(MapleCharacter newCharacter) { + if(newCharacter == null) cacheOffline(newCharacter); + else newCharacter.setFamilyEntry(this); + this.character = newCharacter; } - public void setRank(int rank) { - this.rank = rank; + private void cacheOffline(MapleCharacter chr) { + if(chr != null) { + charName = chr.getName(); + level = chr.getLevel(); + job = chr.getJob(); + } + } + + public synchronized void join(MapleFamilyEntry senior) { + if(senior == null || getSenior() != null) return; + MapleFamily oldFamily = getFamily(); + MapleFamily newFamily = senior.getFamily(); + setSenior(senior, false); + addSeniorCount(newFamily.getTotalGenerations(), newFamily); //count will be overwritten by doFullCount() + newFamily.getLeader().doFullCount(); //easier than keeping track of numbers + oldFamily.setMessage(null, true); + newFamily.addEntryTree(this); + Server.getInstance().getWorld(oldFamily.getWorld()).removeFamily(oldFamily.getID()); + + //db + try(Connection con = DatabaseConnection.getConnection()) { + con.setAutoCommit(false); + boolean success = updateDBChangeFamily(con, getChrId(), newFamily.getID(), senior.getChrId()); + for(MapleFamilyEntry junior : juniors) { // better to duplicate this than the SQL code + if(junior != null) { + success = junior.updateNewFamilyDB(con); // recursively updates juniors in db + if(!success) break; + } + } + if(!success) { + con.rollback(); + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not absorb " + oldFamily.getName() + " family into " + newFamily.getName() + " family. (SQL ERROR)"); + } + con.setAutoCommit(true); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + } + } + + public synchronized void fork() { + MapleFamily oldFamily = getFamily(); + MapleFamilyEntry oldSenior = getSenior(); + family = new MapleFamily(-1, oldFamily.getWorld()); + Server.getInstance().getWorld(family.getWorld()).addFamily(family.getID(), family); + setSenior(null, false); + family.setLeader(this); + addSeniorCount(-getTotalSeniors(), family); + setTotalSeniors(0); + if(oldSenior != null) { + oldSenior.addJuniorCount(-getTotalJuniors()); + oldSenior.removeJunior(this); + oldFamily.getLeader().doFullCount(); + } + oldFamily.removeEntryBranch(this); + family.addEntryTree(this); + this.repsToSenior = 0; + this.repChanged = true; + family.setMessage("", true); + doFullCount(); //to make sure all counts are correct + // update db + try(Connection con = DatabaseConnection.getConnection()) { + con.setAutoCommit(false); + + boolean success = updateDBChangeFamily(con, getChrId(), getFamily().getID(), 0); + + for(MapleFamilyEntry junior : juniors) { // better to duplicate this than the SQL code + if(junior != null) { + success = junior.updateNewFamilyDB(con); // recursively updates juniors in db + if(!success) break; + } + } + if(!success) { + con.rollback(); + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not fork family with new leader " + getName() + ". (Old senior : " + oldSenior.getName() + ", leader :" + oldFamily.getLeader().getName() + ")"); + } + con.setAutoCommit(true); + + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + } + } + + private synchronized boolean updateNewFamilyDB(Connection con) { + if(!updateFamilyEntryDB(con, getChrId(), getFamily().getID())) return false; + if(!updateCharacterFamilyDB(con, getChrId(), getFamily().getID(), true)) return false; + + for(MapleFamilyEntry junior : juniors) { + if(junior != null) { + if(!junior.updateNewFamilyDB(con)) return false; + } + } + return true; + } + + private static boolean updateFamilyEntryDB(Connection con, int cid, int familyid) { + try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET familyid = ? WHERE cid = ?")) { + ps.setInt(1, familyid); + ps.setInt(2, cid); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update family id in 'family_character' for character id " + cid + ". (fork)"); + e.printStackTrace(); + return false; + } + return true; + } + + private synchronized void addSeniorCount(int seniorCount, MapleFamily newFamily) { // traverses tree and subtracts seniors and updates family + if(newFamily != null) this.family = newFamily; + setTotalSeniors(getTotalSeniors() + seniorCount); + this.generation += seniorCount; + for(MapleFamilyEntry junior : juniors) { + if(junior != null) junior.addSeniorCount(seniorCount, newFamily); + } + } + + private synchronized void addJuniorCount(int juniorCount) { // climbs tree and adds junior count + setTotalJuniors(getTotalJuniors() + juniorCount); + MapleFamilyEntry senior = getSenior(); + if(senior != null) senior.addJuniorCount(juniorCount); + } + + public MapleFamily getFamily() { + return family; } public int getChrId() { - return chrid; + return characterID; } - public void setChrId(int chrid) { - this.chrid = chrid; + public String getName() { + MapleCharacter chr = character; + if(chr != null) return chr.getName(); + else return charName; + } + + public int getLevel() { + MapleCharacter chr = character; + if(chr != null) return chr.getLevel(); + else return level; + } + + public MapleJob getJob() { + MapleCharacter chr = character; + if(chr != null) return chr.getJob(); + else return job; } public int getReputation() { @@ -59,16 +234,182 @@ public class MapleFamilyEntry { } public void setReputation(int reputation) { + if(reputation != this.reputation) this.repChanged = true; this.reputation = reputation; } public void setTodaysRep(int today) { + if(today != todaysRep) this.repChanged = true; this.todaysRep = today; } - - public void gainReputation(int gain) { + + public int getRepsToSenior() { + return repsToSenior; + } + + public void setRepsToSenior(int reputation) { + if(reputation != this.repsToSenior) this.repChanged = true; + this.repsToSenior = reputation; + } + + public void gainReputation(int gain, boolean countTowardsTotal) { + gainReputation(gain, countTowardsTotal, this); + } + + private void gainReputation(int gain, boolean countTowardsTotal, MapleFamilyEntry from) { + if(gain != 0) repChanged = true; this.reputation += gain; - this.totalReputation += gain; + this.todaysRep += gain; + if(gain > 0 && countTowardsTotal) { + this.totalReputation += gain; + } + MapleCharacter chr = getChr(); + if(chr != null) chr.announce(MaplePacketCreator.sendGainRep(gain, from != null ? from.getName() : "")); + } + + public void giveReputationToSenior(int gain, boolean includeSuperSenior) { + int actualGain = gain; + MapleFamilyEntry senior = getSenior(); + if(senior != null && senior.getLevel() < getLevel() && gain > 0) actualGain /= 2; //don't halve negative values + if(senior != null) { + senior.gainReputation(actualGain, true, this); + if(actualGain > 0) { + this.repsToSenior += actualGain; + this.repChanged = true; + } + if(includeSuperSenior) { + senior = senior.getSenior(); + if(senior != null) { + senior.gainReputation(actualGain, true, this); + } + } + } + } + + public int getTotalReputation() { + return totalReputation; + } + + public void setTotalReputation(int totalReputation) { + if(totalReputation != this.totalReputation) this.repChanged = true; + this.totalReputation = totalReputation; + } + + public MapleFamilyEntry getSenior() { + return senior; + } + + public synchronized boolean setSenior(MapleFamilyEntry senior, boolean save) { + if(this.senior == senior) return false; + MapleFamilyEntry oldSenior = this.senior; + this.senior = senior; + if(senior != null) { + if(senior.addJunior(this)) { + if(save) { + updateDBChangeFamily(getChrId(), senior.getFamily().getID(), senior.getChrId()); + } + if(this.repsToSenior != 0) this.repChanged = true; + this.repsToSenior = 0; + this.addSeniorCount(1, null); + this.setTotalSeniors(senior.getTotalSeniors() + 1); + return true; + } + } else { + if(oldSenior != null) { + oldSenior.removeJunior(this); + } + } + return false; + } + + private static boolean updateDBChangeFamily(int cid, int familyid, int seniorid) { + try(Connection con = DatabaseConnection.getConnection()) { + return updateDBChangeFamily(con, cid, familyid, seniorid); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + return false; + } + } + + private static boolean updateDBChangeFamily(Connection con, int cid, int familyid, int seniorid) { + try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET familyid = ?, seniorid = ?, reptosenior = 0 WHERE cid = ?")) { + ps.setInt(1, familyid); + ps.setInt(2, seniorid); + ps.setInt(3, cid); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update seniorid in 'family_character' for character id " + cid + "."); + e.printStackTrace(); + return false; + } + return updateCharacterFamilyDB(con, cid, familyid, false); + } + + private static boolean updateCharacterFamilyDB(Connection con, int charid, int familyid, boolean fork) { + try(PreparedStatement ps = con.prepareStatement("UPDATE characters SET familyid = ? WHERE id = ?")) { + ps.setInt(1, familyid); + ps.setInt(2, charid); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update familyid in 'characters' for character id " + charid + " when changing family. " + (fork ? "(fork)" : "")); + e.printStackTrace(); + return false; + } + return true; + } + + public List getJuniors() { + return Collections.unmodifiableList(Arrays.asList(juniors)); + } + + public MapleFamilyEntry getOtherJunior(MapleFamilyEntry junior) { + if(juniors[0] == junior) return juniors[1]; + else if(juniors[1] == junior) return juniors[0]; + return null; + } + + public int getJuniorCount() { //close enough to be relatively consistent to multiple threads (and the result is not vital) + int juniorCount = 0; + if(juniors[0] != null) juniorCount++; + if(juniors[1] != null) juniorCount++; + return juniorCount; + } + + public synchronized boolean addJunior(MapleFamilyEntry newJunior) { + for(int i = 0; i < juniors.length; i++) { + if(juniors[i] == null) { // successfully add new junior to family + juniors[i] = newJunior; + addJuniorCount(1); + getFamily().addEntry(newJunior); + return true; + } + } + return false; + } + + public synchronized boolean isJunior(MapleFamilyEntry entry) { //require locking since result accuracy is vital + if(juniors[0] == entry) return true; + else if(juniors[1] == entry) return true; + return false; + } + + public synchronized boolean removeJunior(MapleFamilyEntry junior) { + for(int i = 0; i < juniors.length; i++) { + if(juniors[i] == junior) { + juniors[i] = null; + return true; + } + } + return false; + } + + public int getTotalSeniors() { + return totalSeniors; + } + + public void setTotalSeniors(int totalSeniors) { + this.totalSeniors = totalSeniors; } public int getTotalJuniors() { @@ -78,28 +419,134 @@ public class MapleFamilyEntry { public void setTotalJuniors(int totalJuniors) { this.totalJuniors = totalJuniors; } - - public int getJuniors() { - return juniors; + + public void announceToSenior(byte[] packet, boolean includeSuperSenior) { + MapleFamilyEntry senior = getSenior(); + if(senior != null) { + MapleCharacter seniorChr = senior.getChr(); + if(seniorChr != null) seniorChr.announce(packet); + senior = senior.getSenior(); + if(includeSuperSenior && senior != null) { + seniorChr = senior.getChr(); + if(seniorChr != null) seniorChr.announce(packet); + } + } + } + + public void updateSeniorFamilyInfo(boolean includeSuperSenior) { + MapleFamilyEntry senior = getSenior(); + if(senior != null) { + MapleCharacter seniorChr = senior.getChr(); + if(seniorChr != null) seniorChr.announce(MaplePacketCreator.getFamilyInfo(senior)); + senior = senior.getSenior(); + if(includeSuperSenior && senior != null) { + seniorChr = senior.getChr(); + if(seniorChr != null) seniorChr.announce(MaplePacketCreator.getFamilyInfo(senior)); + } + } } - public void setJuniors(int juniors) { - this.juniors = juniors; + /** + * Traverses entire family tree to update senior/junior counts. Call on leader. + */ + public synchronized void doFullCount() { + Pair counts = this.traverseAndUpdateCounts(0); + getFamily().setTotalGenerations(counts.getLeft() + 1); } - public void setFamilyName(String familyName) { - this.familyName = familyName; + private Pair traverseAndUpdateCounts(int seniors) { // recursion probably limits family size, but it should handle a depth of a few thousand + setTotalSeniors(seniors); + this.generation = seniors; + int juniorCount = 0; + int highestGeneration = this.generation; + for(MapleFamilyEntry entry : juniors) { + if(entry != null) { + Pair counts = entry.traverseAndUpdateCounts(seniors + 1); + juniorCount += counts.getRight(); //total juniors + if(counts.getLeft() > highestGeneration) highestGeneration = counts.getLeft(); + } + } + setTotalJuniors(juniorCount); + return new Pair<>(highestGeneration, juniorCount); //creating new objects to return is a bit inefficient, but cleaner than packing into a long } - public String getFamilyName() { - return familyName; + public boolean useEntitlement(MapleFamilyEntitlement entitlement) { + int id = entitlement.ordinal(); + if(entitlements[id] >= 1) return false; + try(Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("INSERT INTO family_entitlement (entitlementid, charid, timestamp) VALUES (?, ?, ?)")) { + ps.setInt(1, id); + ps.setInt(2, getChrId()); + ps.setLong(3, System.currentTimeMillis()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not insert new row in 'family_entitlement' for character " + getName() + "."); + e.printStackTrace(); + } + entitlements[id]++; + return true; + } + + public boolean refundEntitlement(MapleFamilyEntitlement entitlement) { + int id = entitlement.ordinal(); + try(Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("DELETE FROM family_entitlement WHERE entitlementid = ? AND charid = ?")) { + ps.setInt(1, id); + ps.setInt(2, getChrId()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not refund family entitlement \"" + entitlement.getName() + "\" for character " + getName() + "."); + e.printStackTrace(); + } + entitlements[id] = 0; + return true; } - public int getTotalReputation() { - return totalReputation; + public boolean isEntitlementUsed(MapleFamilyEntitlement entitlement) { + return entitlements[entitlement.ordinal()] >= 1; } - - public void setTotalReputation(int totalReputation) { - this.totalReputation = totalReputation; + + public int getEntitlementUsageCount(MapleFamilyEntitlement entitlement) { + return entitlements[entitlement.ordinal()]; + } + + public void setEntitlementUsed(int id) { + entitlements[id]++; + } + + public void resetEntitlementUsages() { + for(MapleFamilyEntitlement entitlement : MapleFamilyEntitlement.values()) { + entitlements[entitlement.ordinal()] = 0; + } + } + + public boolean saveReputation() { + if(!repChanged) return true; + try(Connection con = DatabaseConnection.getConnection()) { + return saveReputation(con); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + return false; + } + } + + public boolean saveReputation(Connection con) { + if(!repChanged) return true; + try (PreparedStatement ps = con.prepareStatement("UPDATE family_character SET reputation = ?, todaysrep = ?, totalreputation = ?, reptosenior = ? WHERE cid = ?")) { + ps.setInt(1, getReputation()); + ps.setInt(2, getTodaysRep()); + ps.setInt(3, getTotalReputation()); + ps.setInt(4, getRepsToSenior()); + ps.setInt(5, getChrId()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Failed to autosave rep to 'family_character' for charid " + getChrId()); + e.printStackTrace(); + return false; + } + return true; + } + + public void savedSuccessfully() { + this.repChanged = false; } } diff --git a/src/client/MapleMount.java b/src/client/MapleMount.java index 41934014c1..864a54a279 100644 --- a/src/client/MapleMount.java +++ b/src/client/MapleMount.java @@ -107,6 +107,10 @@ public class MapleMount { public void setItemId(int newitemid) { this.itemid = newitemid; } + + public void setSkillId(int newskillid) { + this.skillid = newskillid; + } public void setActive(boolean set) { this.active = set; diff --git a/src/client/MapleQuestStatus.java b/src/client/MapleQuestStatus.java index 9e0ccc9dd1..e7ce741d57 100644 --- a/src/client/MapleQuestStatus.java +++ b/src/client/MapleQuestStatus.java @@ -151,14 +151,20 @@ public class MapleQuestStatus { } public boolean progress(int id) { - if (progress.get(id) != null) { - int current = Integer.parseInt(progress.get(id)); - String str = StringUtil.getLeftPaddedStr(Integer.toString(current + 1), '0', 3); - progress.put(id, str); - //this.setUpdated(); - return true; + String currentStr = progress.get(id); + if (currentStr == null) { + return false; } - return false; + + int current = Integer.parseInt(currentStr); + if (current >= this.getQuest().getMobAmountNeeded(id)) { + return false; + } + + String str = StringUtil.getLeftPaddedStr(Integer.toString(++current), '0', 3); + progress.put(id, str); + //this.setUpdated(); + return true; } public void setProgress(int id, String pr) { @@ -169,15 +175,14 @@ public class MapleQuestStatus { public boolean madeProgress() { return progress.size() > 0; } - - public Integer getAnyProgressKey() { - if (!progress.isEmpty()) return progress.entrySet().iterator().next().getKey(); - return 0; - } public String getProgress(int id) { - if (progress.get(id) == null) return ""; - return progress.get(id); + String ret = progress.get(id); + if (ret == null) { + return ""; + } else { + return ret; + } } public void resetProgress(int id) { @@ -193,6 +198,27 @@ public class MapleQuestStatus { public Map getProgress() { return Collections.unmodifiableMap(progress); } + + public short getInfoNumber() { + MapleQuest q = this.getQuest(); + Status s = this.getStatus(); + + return q.getInfoNumber(s); + } + + public String getInfoEx(int index) { + MapleQuest q = this.getQuest(); + Status s = this.getStatus(); + + return q.getInfoEx(s, index); + } + + public List getInfoEx() { + MapleQuest q = this.getQuest(); + Status s = this.getStatus(); + + return q.getInfoEx(s); + } public long getCompletionTime() { return completionTime; @@ -217,18 +243,6 @@ public class MapleQuestStatus { public int getCompleted() { return completed; } - - public String getInfo() { - if(!progress.containsKey(0) && !getMedalMaps().isEmpty()) { - return Integer.toString(getMedalProgress()); - } - return getProgress(0); - } - - public void setInfo(String newInfo) { - progress.put(0, newInfo); - //this.setUpdated(); - } public void setForfeited(int forfeited) { if (forfeited >= this.forfeited) { @@ -254,11 +268,11 @@ public class MapleQuestStatus { return customData; } - public String getQuestData() { + public String getProgressData() { StringBuilder str = new StringBuilder(); for (String ps : progress.values()) { str.append(ps); } return str.toString(); } -} \ No newline at end of file +} diff --git a/src/client/MonsterBook.java b/src/client/MonsterBook.java index 1d2c6e41e3..235362d78f 100644 --- a/src/client/MonsterBook.java +++ b/src/client/MonsterBook.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.Semaphore; @@ -46,10 +47,10 @@ public final class MonsterBook { private Map cards = new LinkedHashMap<>(); private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BOOK); - private Set> getCardSet() { + public Set> getCardSet() { lock.lock(); try { - return Collections.unmodifiableSet(cards.entrySet()); + return new HashSet<>(cards.entrySet()); } finally { lock.unlock(); } @@ -82,7 +83,9 @@ public final class MonsterBook { } if(qty < 5) { - calculateLevel(); // current leveling system only accounts unique cards... + if (qty == 0) { // leveling system only accounts unique cards + calculateLevel(); + } c.announce(MaplePacketCreator.addCard(false, cardid, qty + 1)); c.announce(MaplePacketCreator.showGainCard()); @@ -94,7 +97,15 @@ public final class MonsterBook { private void calculateLevel() { lock.lock(); try { - bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5)); + int collectionExp = (normalCard + specialCard); + + int level = 0, expToNextlevel = 1; + do { + level++; + expToNextlevel += level * 10; + } while (collectionExp >= expToNextlevel); + + bookLevel = level; // thanks IxianMace for noticing book level differing between book UI and character info UI } finally { lock.unlock(); } @@ -232,4 +243,29 @@ public final class MonsterBook { e.printStackTrace(); } } + + public static int[] getCardTierSize() { + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM monstercarddata GROUP BY floor(cardid / 1000);"); + ResultSet rs = ps.executeQuery(); + + rs.last(); + int[] tierSizes = new int[rs.getRow()]; + rs.beforeFirst(); + + while (rs.next()) { + tierSizes[rs.getRow() - 1] = rs.getInt(1); + } + + rs.close(); + ps.close(); + con.close(); + + return tierSizes; + } catch (SQLException e) { + e.printStackTrace(); + return new int[0]; + } + } } diff --git a/src/client/SkillFactory.java b/src/client/SkillFactory.java index 8e0e5398c4..7b542c0e2d 100644 --- a/src/client/SkillFactory.java +++ b/src/client/SkillFactory.java @@ -68,7 +68,7 @@ import constants.skills.Shadower; import constants.skills.Sniper; import constants.skills.Spearman; import constants.skills.SuperGM; -import constants.skills.Swordsman; +import constants.skills.Warrior; import constants.skills.ThunderBreaker; import constants.skills.WhiteKnight; import constants.skills.WindArcher; @@ -180,6 +180,7 @@ public class SkillFactory { case NightWalker.POISON_BOMB: case NightWalker.VAMPIRE: case ChiefBandit.CHAKRA: + case Aran.COMBAT_STEP: case Evan.RECOVERY_AURA: isBuff = false; break; @@ -188,7 +189,7 @@ public class SkillFactory { case Beginner.MONSTER_RIDER: case Beginner.ECHO_OF_HERO: case Beginner.MAP_CHAIR: - case Swordsman.IRON_BODY: + case Warrior.IRON_BODY: case Fighter.AXE_BOOSTER: case Fighter.POWER_GUARD: case Fighter.RAGE: diff --git a/src/client/SkillMacro.java b/src/client/SkillMacro.java index 7f4e38c319..b129c5feee 100644 --- a/src/client/SkillMacro.java +++ b/src/client/SkillMacro.java @@ -49,6 +49,18 @@ public class SkillMacro { public int getSkill3() { return skill3; } + + public void setSkill1(int skill) { + skill1 = skill; + } + + public void setSkill2(int skill) { + skill2 = skill; + } + + public void setSkill3(int skill) { + skill3 = skill; + } public String getName() { return name; diff --git a/src/client/autoban/AutobanFactory.java b/src/client/autoban/AutobanFactory.java index 04f0f672c5..dbca257ea4 100644 --- a/src/client/autoban/AutobanFactory.java +++ b/src/client/autoban/AutobanFactory.java @@ -23,7 +23,7 @@ along with this program. If not, see . package client.autoban; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; import net.server.Server; import tools.FilePrinter; import tools.MapleLogger; @@ -84,19 +84,19 @@ public enum AutobanFactory { } public void alert(MapleCharacter chr, String reason) { - if(ServerConstants.USE_AUTOBAN == true) { + if(YamlConfig.config.server.USE_AUTOBAN == true) { if (chr != null && MapleLogger.ignored.contains(chr.getId())){ return; } Server.getInstance().broadcastGMMessage((chr != null ? chr.getWorld() : 0), MaplePacketCreator.sendYellowTip((chr != null ? MapleCharacter.makeMapleReadable(chr.getName()) : "") + " caused " + this.name() + " " + reason)); } - if (ServerConstants.USE_AUTOBAN_LOG) { + if (YamlConfig.config.server.USE_AUTOBAN_LOG) { FilePrinter.print(FilePrinter.AUTOBAN_WARNING, (chr != null ? MapleCharacter.makeMapleReadable(chr.getName()) : "") + " caused " + this.name() + " " + reason); } } public void autoban(MapleCharacter chr, String value) { - if(ServerConstants.USE_AUTOBAN == true) { + if(YamlConfig.config.server.USE_AUTOBAN == true) { chr.autoban("Autobanned for (" + this.name() + ": " + value + ")"); //chr.sendPolice("You will be disconnected for (" + this.name() + ": " + value + ")"); } diff --git a/src/client/autoban/AutobanManager.java b/src/client/autoban/AutobanManager.java index 14d8f25370..1e46a0d6d7 100644 --- a/src/client/autoban/AutobanManager.java +++ b/src/client/autoban/AutobanManager.java @@ -6,7 +6,7 @@ package client.autoban; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; import java.util.HashMap; import java.util.Map; import net.server.Server; @@ -33,10 +33,11 @@ public class AutobanManager { } public void addPoint(AutobanFactory fac, String reason) { - if (chr.isGM() || chr.isBanned()){ - return; - } - if (ServerConstants.USE_AUTOBAN) { + if (YamlConfig.config.server.USE_AUTOBAN) { + if (chr.isGM() || chr.isBanned()){ + return; + } + if (lastTime.containsKey(fac)) { if (lastTime.get(fac) < (Server.getInstance().getCurrentTime() - fac.getExpire())) { points.put(fac, points.get(fac) / 2); //So the points are not completely gone. @@ -56,7 +57,7 @@ public class AutobanManager { //chr.sendPolice("You have been blocked by #bMooplePolice for the HACK reason#k."); } } - if (ServerConstants.USE_AUTOBAN_LOG) { + if (YamlConfig.config.server.USE_AUTOBAN_LOG) { // Lets log every single point too. FilePrinter.print(FilePrinter.AUTOBAN_WARNING, MapleCharacter.makeMapleReadable(chr.getName()) + " caused " + fac.name() + " " + reason); } @@ -113,7 +114,7 @@ public class AutobanManager { if (this.timestamp[type] == time) { this.timestampcounter[type]++; if (this.timestampcounter[type] >= times) { - if (ServerConstants.USE_AUTOBAN) { + if (YamlConfig.config.server.USE_AUTOBAN) { chr.getClient().disconnect(false, false); } diff --git a/src/client/command/Command.java b/src/client/command/Command.java index 7cd309e6af..fac0158ce1 100644 --- a/src/client/command/Command.java +++ b/src/client/command/Command.java @@ -27,6 +27,7 @@ import client.MapleClient; public abstract class Command { + protected int rank; protected String description; public abstract void execute(MapleClient client, String[] params); @@ -38,6 +39,14 @@ public abstract class Command { protected void setDescription(String description) { this.description = description; } + + public int getRank() { + return rank; + } + + public void setRank(int rank) { + this.rank = rank; + } protected String joinStringFrom(String arr[], int start) { StringBuilder builder = new StringBuilder(); diff --git a/src/client/command/CommandsExecutor.java b/src/client/command/CommandsExecutor.java index cc889cbfa1..e30c90adc2 100644 --- a/src/client/command/CommandsExecutor.java +++ b/src/client/command/CommandsExecutor.java @@ -62,7 +62,7 @@ public class CommandsExecutor { return heading == USER_HEADING; } - private HashMap registeredCommands = new HashMap<>(); + private HashMap registeredCommands = new HashMap<>(); private Pair, List> levelCommandsCursor; private List, List>> commandsNameDesc = new ArrayList<>(); @@ -94,7 +94,7 @@ public class CommandsExecutor { private void handleInternal(MapleClient client, String message){ if (client.getPlayer().getMapId() == 300000012) { - client.getPlayer().yellowMessage("You not have permission to use this command while in jail."); + client.getPlayer().yellowMessage("You do not have permission to use commands while in jail."); return; } final String splitRegex = "[ ]"; @@ -107,13 +107,13 @@ public class CommandsExecutor { final String commandName = splitedMessage[0].toLowerCase(); final String[] lowercaseParams = splitedMessage[1].toLowerCase().split(splitRegex); - final RegisteredCommand command = registeredCommands.get(commandName); + final Command command = registeredCommands.get(commandName); if (command == null){ client.getPlayer().yellowMessage("Command '" + commandName + "' is not available. See @commands for a list of available commands."); return; } if (client.getPlayer().gmLevel() < command.getRank()){ - client.getPlayer().yellowMessage("You not have permission to use this command."); + client.getPlayer().yellowMessage("You do not have permission to use this command."); return; } String[] params; @@ -122,16 +122,9 @@ public class CommandsExecutor { } else { params = new String[]{}; } - try { - Command commandInstance = command.getCommandClass().newInstance(); - commandInstance.execute(client, params); - writeLog(client, message); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - + + command.execute(client, params); + writeLog(client, message); } private void writeLog(MapleClient client, String command){ @@ -172,11 +165,19 @@ public class CommandsExecutor { return; } - RegisteredCommand registeredCommand = new RegisteredCommand(commandClass, rank); - String commandName = syntax.toLowerCase(); addCommandInfo(commandName, commandClass); - registeredCommands.put(commandName, registeredCommand); + + try { + Command commandInstance = commandClass.newInstance(); // thanks Halcyon for noticing commands getting reinstanced every call + commandInstance.setRank(rank); + + registeredCommands.put(commandName, commandInstance); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } } private void registerLv0Commands(){ @@ -253,6 +254,7 @@ public class CommandsExecutor { addCommand("drop", 2, ItemDropCommand.class); addCommand("level", 2, LevelCommand.class); addCommand("levelpro", 2, LevelProCommand.class); + addCommand("setslot", 2, SetSlotCommand.class); addCommand("setstat", 2, SetStatCommand.class); addCommand("maxstat", 2, MaxStatCommand.class); addCommand("maxskill", 2, MaxSkillCommand.class); @@ -264,6 +266,7 @@ public class CommandsExecutor { addCommand("unbug", 2, UnBugCommand.class); addCommand("id", 2, IdCommand.class); addCommand("gachalist", GachaListCommand.class); + addCommand("loot", LootCommand.class); commandsNameDesc.add(levelCommandsCursor); } diff --git a/src/client/command/commands/gm0/BuyBackCommand.java b/src/client/command/commands/gm0/BuyBackCommand.java index 67b0cd835f..3a8364d408 100644 --- a/src/client/command/commands/gm0/BuyBackCommand.java +++ b/src/client/command/commands/gm0/BuyBackCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm0; import client.MapleClient; import client.command.Command; -import client.processor.BuybackProcessor; +import client.processor.action.BuybackProcessor; public class BuyBackCommand extends Command { { diff --git a/src/client/command/commands/gm0/DisposeCommand.java b/src/client/command/commands/gm0/DisposeCommand.java index 01e2b7de68..16d08eddb2 100644 --- a/src/client/command/commands/gm0/DisposeCommand.java +++ b/src/client/command/commands/gm0/DisposeCommand.java @@ -26,6 +26,7 @@ package client.command.commands.gm0; import client.command.Command; import client.MapleClient; import scripting.npc.NPCScriptManager; +import scripting.quest.QuestScriptManager; import tools.MaplePacketCreator; public class DisposeCommand extends Command { @@ -36,6 +37,7 @@ public class DisposeCommand extends Command { @Override public void execute(MapleClient c, String[] params) { NPCScriptManager.getInstance().dispose(c); + QuestScriptManager.getInstance().dispose(c); c.announce(MaplePacketCreator.enableActions()); c.removeClickedNPC(); c.getPlayer().message("You've been disposed."); diff --git a/src/client/command/commands/gm0/DropLimitCommand.java b/src/client/command/commands/gm0/DropLimitCommand.java index 1e579b93cc..efb5097a07 100644 --- a/src/client/command/commands/gm0/DropLimitCommand.java +++ b/src/client/command/commands/gm0/DropLimitCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm0; import client.MapleClient; import client.command.Command; -import constants.ServerConstants; +import config.YamlConfig; public class DropLimitCommand extends Command { { @@ -35,10 +35,10 @@ public class DropLimitCommand extends Command { @Override public void execute(MapleClient c, String[] params) { int dropCount = c.getPlayer().getMap().getDroppedItemCount(); - if(((float) dropCount) / ServerConstants.ITEM_LIMIT_ON_MAP < 0.75f) { - c.getPlayer().showHint("Current drop count: #b" + dropCount + "#k / #e" + ServerConstants.ITEM_LIMIT_ON_MAP + "#n", 300); + if(((float) dropCount) / YamlConfig.config.server.ITEM_LIMIT_ON_MAP < 0.75f) { + c.getPlayer().showHint("Current drop count: #b" + dropCount + "#k / #e" + YamlConfig.config.server.ITEM_LIMIT_ON_MAP + "#n", 300); } else { - c.getPlayer().showHint("Current drop count: #r" + dropCount + "#k / #e" + ServerConstants.ITEM_LIMIT_ON_MAP + "#n", 300); + c.getPlayer().showHint("Current drop count: #r" + dropCount + "#k / #e" + YamlConfig.config.server.ITEM_LIMIT_ON_MAP + "#n", 300); } } diff --git a/src/client/command/commands/gm0/EnableAuthCommand.java b/src/client/command/commands/gm0/EnableAuthCommand.java index f8ed615b88..67659906c5 100644 --- a/src/client/command/commands/gm0/EnableAuthCommand.java +++ b/src/client/command/commands/gm0/EnableAuthCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm0; import client.command.Command; import client.MapleClient; -import net.server.coordinator.MapleLoginBypassCoordinator; +import net.server.coordinator.login.MapleLoginBypassCoordinator; public class EnableAuthCommand extends Command { { diff --git a/src/client/command/commands/gm0/MapOwnerClaimCommand.java b/src/client/command/commands/gm0/MapOwnerClaimCommand.java index 5818b1c5ff..5377217768 100644 --- a/src/client/command/commands/gm0/MapOwnerClaimCommand.java +++ b/src/client/command/commands/gm0/MapOwnerClaimCommand.java @@ -26,7 +26,8 @@ package client.command.commands.gm0; import client.command.Command; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; +import server.maps.MapleMap; public class MapOwnerClaimCommand extends Command { { @@ -39,14 +40,27 @@ public class MapOwnerClaimCommand extends Command { try { MapleCharacter chr = c.getPlayer(); - if (ServerConstants.USE_MAP_OWNERSHIP_SYSTEM) { + if (YamlConfig.config.server.USE_MAP_OWNERSHIP_SYSTEM) { if (chr.getEventInstance() == null) { - if (chr.getMap().unclaimOwnership(chr)) { - chr.dropMessage(5, "This lawn is now free real estate."); - } else if (chr.getMap().claimOwnership(chr)) { - chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity."); + MapleMap map = chr.getMap(); + if (map.countBosses() == 0) { // thanks Conrad for suggesting bosses prevent map leasing + MapleMap ownedMap = chr.getOwnedMap(); // thanks Conrad for suggesting not unlease a map as soon as player exits it + if (ownedMap != null) { + ownedMap.unclaimOwnership(chr); + + if (map == ownedMap) { + chr.dropMessage(5, "This lawn is now free real estate."); + return; + } + } + + if (map.claimOwnership(chr)) { + chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity."); + } else { + chr.dropMessage(5, "This lawn has already been leased by a player."); + } } else { - chr.dropMessage(5, "This lawn has already been leased by another player."); + chr.dropMessage(5, "This lawn is currently under a boss siege."); } } else { chr.dropMessage(5, "This lawn cannot be leased."); diff --git a/src/client/command/commands/gm0/RatesCommand.java b/src/client/command/commands/gm0/RatesCommand.java index 8498efaa23..b297c99969 100644 --- a/src/client/command/commands/gm0/RatesCommand.java +++ b/src/client/command/commands/gm0/RatesCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.MapleCharacter; import client.command.Command; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; public class RatesCommand extends Command { { @@ -43,7 +43,7 @@ public class RatesCommand extends Command { showMsg_ += "MESO Rate: #e#b" + player.getMesoRate() + "x#k#n" + "\r\n"; showMsg_ += "DROP Rate: #e#b" + player.getDropRate() + "x#k#n" + "\r\n"; showMsg_ += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n"; - if(ServerConstants.USE_QUEST_RATE) showMsg_ += "QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n"; + if(YamlConfig.config.server.USE_QUEST_RATE) showMsg_ += "QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n"; player.showHint(showMsg_, 300); } diff --git a/src/client/command/commands/gm0/ShowRatesCommand.java b/src/client/command/commands/gm0/ShowRatesCommand.java index 9b7b95dfa1..7e4ce9eb54 100644 --- a/src/client/command/commands/gm0/ShowRatesCommand.java +++ b/src/client/command/commands/gm0/ShowRatesCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.MapleCharacter; import client.command.Command; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; public class ShowRatesCommand extends Command { { @@ -60,7 +60,7 @@ public class ShowRatesCommand extends Command { if(player.getCouponDropRate() != 1) showMsg += "Coupon DROP Rate: #k" + player.getCouponDropRate() + "x#k" + "\r\n"; showMsg += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n"; - if(ServerConstants.USE_QUEST_RATE) { + if(YamlConfig.config.server.USE_QUEST_RATE) { showMsg += "\r\n" + "#eQUEST RATE#n" + "\r\n"; showMsg += "World QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n"; } diff --git a/src/client/command/commands/gm0/StatDexCommand.java b/src/client/command/commands/gm0/StatDexCommand.java index 065d89fd92..ce3e1728ba 100644 --- a/src/client/command/commands/gm0/StatDexCommand.java +++ b/src/client/command/commands/gm0/StatDexCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.MapleCharacter; import client.MapleClient; import client.command.Command; -import constants.ServerConstants; +import config.YamlConfig; public class StatDexCommand extends Command { { @@ -47,10 +47,10 @@ public class StatDexCommand extends Command { return; } } else { - amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getDex()); + amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getDex()); } if (!player.assignDex(Math.max(amount, 0))) { - player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute."); + player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute."); } } } diff --git a/src/client/command/commands/gm0/StatIntCommand.java b/src/client/command/commands/gm0/StatIntCommand.java index 6a39f4030c..1a69382e96 100644 --- a/src/client/command/commands/gm0/StatIntCommand.java +++ b/src/client/command/commands/gm0/StatIntCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class StatIntCommand extends Command { { @@ -47,10 +47,10 @@ public class StatIntCommand extends Command { return; } } else { - amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getInt()); + amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getInt()); } if (!player.assignInt(Math.max(amount, 0))) { - player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute."); + player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute."); } } } diff --git a/src/client/command/commands/gm0/StatLukCommand.java b/src/client/command/commands/gm0/StatLukCommand.java index c5567febb8..8582c78587 100644 --- a/src/client/command/commands/gm0/StatLukCommand.java +++ b/src/client/command/commands/gm0/StatLukCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.MapleCharacter; import client.MapleClient; import client.command.Command; -import constants.ServerConstants; +import config.YamlConfig; public class StatLukCommand extends Command { { @@ -47,10 +47,10 @@ public class StatLukCommand extends Command { return; } } else { - amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getLuk()); + amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getLuk()); } if (!player.assignLuk(Math.max(amount, 0))) { - player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute."); + player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute."); } } } diff --git a/src/client/command/commands/gm0/StatStrCommand.java b/src/client/command/commands/gm0/StatStrCommand.java index 1b7a782ba5..ad88a84a98 100644 --- a/src/client/command/commands/gm0/StatStrCommand.java +++ b/src/client/command/commands/gm0/StatStrCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm0; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class StatStrCommand extends Command { { @@ -46,11 +46,11 @@ public class StatStrCommand extends Command { return; } } else { - amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getStr()); + amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getStr()); } if (!player.assignStr(Math.max(amount, 0))) { - player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute."); + player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute."); } } } diff --git a/src/client/command/commands/gm0/TimeCommand.java b/src/client/command/commands/gm0/TimeCommand.java index 9a9351c392..d91a3b8a01 100644 --- a/src/client/command/commands/gm0/TimeCommand.java +++ b/src/client/command/commands/gm0/TimeCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm0; import client.MapleClient; import client.command.Command; -import constants.ServerConstants; +import constants.net.ServerConstants; import java.text.DateFormat; import java.text.SimpleDateFormat; diff --git a/src/client/command/commands/gm1/GotoCommand.java b/src/client/command/commands/gm1/GotoCommand.java index 95f2b66aa1..986bc279bf 100644 --- a/src/client/command/commands/gm1/GotoCommand.java +++ b/src/client/command/commands/gm1/GotoCommand.java @@ -26,14 +26,13 @@ package client.command.commands.gm1; import client.MapleCharacter; import client.command.Command; import client.MapleClient; -import constants.GameConstants; +import constants.game.GameConstants; import java.util.ArrayList; import java.util.Collections; -import net.server.Server; -import server.MaplePortal; +import server.maps.MaplePortal; import server.maps.FieldLimit; import server.maps.MapleMap; -import server.maps.MapleMapManager; +import server.maps.MapleMapFactory; import server.maps.MapleMiniDungeonInfo; import java.util.Comparator; @@ -47,19 +46,28 @@ public class GotoCommand extends Command { { setDescription(""); - MapleMapManager mapManager = Server.getInstance().getWorlds().get(0).getChannels().get(0).getMapFactory(); - List> towns = new ArrayList<>(GameConstants.GOTO_TOWNS.entrySet()); sortGotoEntries(towns); - for (Map.Entry e : towns) { - GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n"); + + try { + // thanks shavit for noticing goto areas getting loaded from wz needlessly, only for the name retrieval + + for (Map.Entry e : towns) { + GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n"); + } + + List> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet()); + sortGotoEntries(areas); + for (Map.Entry e : areas) { + GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n"); + } + } catch (Exception e) { + e.printStackTrace(); + + GOTO_TOWNS_INFO = "(none)"; + GOTO_AREAS_INFO = "(none)"; } - List> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet()); - sortGotoEntries(areas); - for (Map.Entry e : areas) { - GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n"); - } } public static String GOTO_TOWNS_INFO = ""; @@ -105,7 +113,7 @@ public class GotoCommand extends Command { gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc gotomaps.putAll(GameConstants.GOTO_TOWNS); // thanks Halcyon (UltimateMors) for pointing out duplicates on listed entries functionality } else { - gotomaps = new HashMap<>(GameConstants.GOTO_TOWNS); + gotomaps = GameConstants.GOTO_TOWNS; } if (gotomaps.containsKey(params[0])) { diff --git a/src/client/command/commands/gm2/ApCommand.java b/src/client/command/commands/gm2/ApCommand.java index d408e3cec6..baa72b0ad7 100644 --- a/src/client/command/commands/gm2/ApCommand.java +++ b/src/client/command/commands/gm2/ApCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm2; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class ApCommand extends Command { { @@ -44,7 +44,7 @@ public class ApCommand extends Command { if (params.length < 2) { int newAp = Integer.parseInt(params[0]); if (newAp < 0) newAp = 0; - else if (newAp > ServerConstants.MAX_AP) newAp = ServerConstants.MAX_AP; + else if (newAp > YamlConfig.config.server.MAX_AP) newAp = YamlConfig.config.server.MAX_AP; player.changeRemainingAp(newAp, false); } else { @@ -52,7 +52,7 @@ public class ApCommand extends Command { if (victim != null) { int newAp = Integer.parseInt(params[1]); if (newAp < 0) newAp = 0; - else if (newAp > ServerConstants.MAX_AP) newAp = ServerConstants.MAX_AP; + else if (newAp > YamlConfig.config.server.MAX_AP) newAp = YamlConfig.config.server.MAX_AP; victim.changeRemainingAp(newAp, false); } else { diff --git a/src/client/command/commands/gm2/IdCommand.java b/src/client/command/commands/gm2/IdCommand.java index 304f19c62a..d1153fc8ce 100644 --- a/src/client/command/commands/gm2/IdCommand.java +++ b/src/client/command/commands/gm2/IdCommand.java @@ -55,7 +55,7 @@ public class IdCommand extends Command { } sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n"); - player.getClient().getAbstractPlayerInteraction().npcTalk(9010000, sb.toString()); + player.getAbstractPlayerInteraction().npcTalk(9010000, sb.toString()); } else { player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0])); } diff --git a/src/client/command/commands/gm2/ItemCommand.java b/src/client/command/commands/gm2/ItemCommand.java index e0e38a7b2e..e1ed732924 100644 --- a/src/client/command/commands/gm2/ItemCommand.java +++ b/src/client/command/commands/gm2/ItemCommand.java @@ -28,8 +28,8 @@ import client.MapleClient; import client.MapleCharacter; import client.inventory.MaplePet; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class ItemCommand extends Command { @@ -57,7 +57,7 @@ public class ItemCommand extends Command { short quantity = 1; if(params.length >= 2) quantity = Short.parseShort(params[1]); - if (ServerConstants.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) { + if (YamlConfig.config.server.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) { player.yellowMessage("You cannot create a cash item with this command."); return; } @@ -77,12 +77,12 @@ public class ItemCommand extends Command { } } - byte flag = 0; + short flag = 0; if(player.gmLevel() < 3) { flag |= ItemConstants.ACCOUNT_SHARING; flag |= ItemConstants.UNTRADEABLE; } - + MapleInventoryManipulator.addById(c, itemId, quantity, player.getName(), -1, flag, -1); } } diff --git a/src/client/command/commands/gm2/ItemDropCommand.java b/src/client/command/commands/gm2/ItemDropCommand.java index 4a35be2207..d7b8bbb3c6 100644 --- a/src/client/command/commands/gm2/ItemDropCommand.java +++ b/src/client/command/commands/gm2/ItemDropCommand.java @@ -29,8 +29,8 @@ import client.MapleCharacter; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class ItemDropCommand extends Command { @@ -58,7 +58,7 @@ public class ItemDropCommand extends Command { short quantity = 1; if(params.length >= 2) quantity = Short.parseShort(params[1]); - if (ServerConstants.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) { + if (YamlConfig.config.server.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) { player.yellowMessage("You cannot create a cash item with this command."); return; } @@ -75,12 +75,12 @@ public class ItemDropCommand extends Command { toDrop.setOwner(""); if(player.gmLevel() < 3) { - byte b = toDrop.getFlag(); - b |= ItemConstants.ACCOUNT_SHARING; - b |= ItemConstants.UNTRADEABLE; - b |= ItemConstants.SANDBOX; + short f = toDrop.getFlag(); + f |= ItemConstants.ACCOUNT_SHARING; + f |= ItemConstants.UNTRADEABLE; + f |= ItemConstants.SANDBOX; - toDrop.setFlag(b); + toDrop.setFlag(f); toDrop.setOwner("TRIAL-MODE"); } @@ -102,12 +102,12 @@ public class ItemDropCommand extends Command { toDrop.setOwner(player.getName()); if(player.gmLevel() < 3) { - byte b = toDrop.getFlag(); - b |= ItemConstants.ACCOUNT_SHARING; - b |= ItemConstants.UNTRADEABLE; - b |= ItemConstants.SANDBOX; + short f = toDrop.getFlag(); + f |= ItemConstants.ACCOUNT_SHARING; + f |= ItemConstants.UNTRADEABLE; + f |= ItemConstants.SANDBOX; - toDrop.setFlag(b); + toDrop.setFlag(f); toDrop.setOwner("TRIAL-MODE"); } diff --git a/src/client/command/commands/gm2/JailCommand.java b/src/client/command/commands/gm2/JailCommand.java index 122dc79c43..a159cfb599 100644 --- a/src/client/command/commands/gm2/JailCommand.java +++ b/src/client/command/commands/gm2/JailCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm2; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import server.MaplePortal; +import server.maps.MaplePortal; import server.maps.MapleMap; public class JailCommand extends Command { diff --git a/src/client/command/commands/gm2/LevelCommand.java b/src/client/command/commands/gm2/LevelCommand.java index 5d1037652b..e532131880 100644 --- a/src/client/command/commands/gm2/LevelCommand.java +++ b/src/client/command/commands/gm2/LevelCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm2; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class LevelCommand extends Command { { @@ -45,7 +45,7 @@ public class LevelCommand extends Command { player.setLevel(Math.min(Integer.parseInt(params[0]), player.getMaxClassLevel()) - 1); player.resetPlayerRates(); - if (ServerConstants.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates(); + if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates(); player.setWorldRates(); player.levelUp(false); diff --git a/src/client/command/commands/gm2/LootCommand.java b/src/client/command/commands/gm2/LootCommand.java new file mode 100644 index 0000000000..fb43fac434 --- /dev/null +++ b/src/client/command/commands/gm2/LootCommand.java @@ -0,0 +1,51 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +/* + @Author: Resinate +*/ +package client.command.commands.gm2; + +import client.MapleClient; +import client.command.Command; +import java.util.Arrays; +import java.util.List; +import server.maps.MapleMapItem; +import server.maps.MapleMapObject; +import server.maps.MapleMapObjectType; + +public class LootCommand extends Command { + + { + setDescription("Loots all items that belong to you."); + } + + @Override + public void execute(MapleClient c, String[] params) { + List items = c.getPlayer().getMap().getMapObjectsInRange(c.getPlayer().getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.ITEM)); + for (MapleMapObject item : items) { + MapleMapItem mapItem = (MapleMapItem) item; + if (mapItem.getOwnerId() == c.getPlayer().getId() || mapItem.getOwnerId() == c.getPlayer().getPartyId()) { + c.getPlayer().pickupItem(mapItem); + } + } + + } +} diff --git a/src/client/command/commands/gm2/MaxStatCommand.java b/src/client/command/commands/gm2/MaxStatCommand.java index e7586c8f66..2b3dc1a492 100644 --- a/src/client/command/commands/gm2/MaxStatCommand.java +++ b/src/client/command/commands/gm2/MaxStatCommand.java @@ -27,7 +27,7 @@ import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class MaxStatCommand extends Command { { @@ -40,7 +40,7 @@ public class MaxStatCommand extends Command { player.loseExp(player.getExp(), false, false); player.setLevel(255); player.resetPlayerRates(); - if (ServerConstants.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates(); + if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates(); player.setWorldRates(); player.updateStrDexIntLuk(Short.MAX_VALUE); player.setFame(13337); diff --git a/src/client/command/commands/gm2/RechargeCommand.java b/src/client/command/commands/gm2/RechargeCommand.java index 56bb6c3b0d..d5a99a4ffd 100644 --- a/src/client/command/commands/gm2/RechargeCommand.java +++ b/src/client/command/commands/gm2/RechargeCommand.java @@ -28,7 +28,7 @@ import client.command.Command; import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class RechargeCommand extends Command { diff --git a/scripts/quest/21728.js b/src/client/command/commands/gm2/SetSlotCommand.java similarity index 53% rename from scripts/quest/21728.js rename to src/client/command/commands/gm2/SetSlotCommand.java index d5ff8db4fd..06e4b46a17 100644 --- a/scripts/quest/21728.js +++ b/src/client/command/commands/gm2/SetSlotCommand.java @@ -1,5 +1,5 @@ /* - This file is part of the HeavenMS MapleStory Server + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based Copyleft (L) 2016 - 2019 RonanLana This program is free software: you can redistribute it and/or modify @@ -18,32 +18,37 @@ along with this program. If not, see . */ -var status = -1; +/* + @Author: Ronan +*/ +package client.command.commands.gm2; -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); +import client.*; +import client.command.Command; + +public class SetSlotCommand extends Command { + { + setDescription(""); + } + + @Override + public void execute(MapleClient c, String[] params) { + MapleCharacter player = c.getPlayer(); + if (params.length < 1) { + player.yellowMessage("Syntax: !setslot "); return; } - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - if(qm.getQuestProgress(21728, 0) == 0) { - qm.sendNext("You haven't found the #rPuppeteer's cave#k yet, did you?"); - } else { - qm.sendNext("Hm, so the entrance is blocked by a powerful force? I see, gimme a time to think now..."); - qm.gainExp(200); - qm.forceCompleteQuest(); + int slots = (Integer.parseInt(params[0]) / 4) * 4; + for (int i = 1; i < 5; i++) { + int curSlots = player.getSlots(i); + if (slots <= -curSlots) { + continue; } - qm.dispose(); + player.gainSlots(i, slots - curSlots, true); } + + player.yellowMessage("Slots updated."); } -} \ No newline at end of file +} diff --git a/src/client/command/commands/gm2/SpCommand.java b/src/client/command/commands/gm2/SpCommand.java index 57e9d2502e..18776dd01e 100644 --- a/src/client/command/commands/gm2/SpCommand.java +++ b/src/client/command/commands/gm2/SpCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm2; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; public class SpCommand extends Command { { @@ -44,7 +44,7 @@ public class SpCommand extends Command { if (params.length == 1) { int newSp = Integer.parseInt(params[0]); if (newSp < 0) newSp = 0; - else if (newSp > ServerConstants.MAX_AP) newSp = ServerConstants.MAX_AP; + else if (newSp > YamlConfig.config.server.MAX_AP) newSp = YamlConfig.config.server.MAX_AP; player.updateRemainingSp(newSp); } else { @@ -52,7 +52,7 @@ public class SpCommand extends Command { if (victim != null) { int newSp = Integer.parseInt(params[1]); if (newSp < 0) newSp = 0; - else if (newSp > ServerConstants.MAX_AP) newSp = ServerConstants.MAX_AP; + else if (newSp > YamlConfig.config.server.MAX_AP) newSp = YamlConfig.config.server.MAX_AP; victim.updateRemainingSp(newSp); diff --git a/src/client/command/commands/gm3/FaceCommand.java b/src/client/command/commands/gm3/FaceCommand.java index 7b9496805f..018854b110 100644 --- a/src/client/command/commands/gm3/FaceCommand.java +++ b/src/client/command/commands/gm3/FaceCommand.java @@ -27,7 +27,7 @@ import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class FaceCommand extends Command { diff --git a/src/client/command/commands/gm3/HairCommand.java b/src/client/command/commands/gm3/HairCommand.java index 5d08e8dec9..187ad4db63 100644 --- a/src/client/command/commands/gm3/HairCommand.java +++ b/src/client/command/commands/gm3/HairCommand.java @@ -27,7 +27,7 @@ import client.MapleStat; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class HairCommand extends Command { diff --git a/src/client/command/commands/gm3/MusicCommand.java b/src/client/command/commands/gm3/MusicCommand.java index 2dc0cb4e12..acd71dd211 100644 --- a/src/client/command/commands/gm3/MusicCommand.java +++ b/src/client/command/commands/gm3/MusicCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm3; import client.command.Command; import client.MapleClient; import client.MapleCharacter; -import constants.GameConstants; +import constants.game.GameConstants; import tools.MaplePacketCreator; public class MusicCommand extends Command { diff --git a/src/client/command/commands/gm3/QuestCompleteCommand.java b/src/client/command/commands/gm3/QuestCompleteCommand.java index b092d3fb20..8595b37b6e 100644 --- a/src/client/command/commands/gm3/QuestCompleteCommand.java +++ b/src/client/command/commands/gm3/QuestCompleteCommand.java @@ -46,13 +46,13 @@ public class QuestCompleteCommand extends Command { if (player.getQuestStatus(questId) == 1) { MapleQuest quest = MapleQuest.getInstance(questId); - if (quest != null) { - int npcid = quest.getNpcRequirement(true); - quest.forceComplete(player, npcid); - player.dropMessage(5, "QUEST " + questId + " completed."); - } else { // should not occur - player.dropMessage(5, "QUESTID " + questId + " is invalid."); + if (quest != null && quest.getNpcRequirement(true) != -1) { + c.getAbstractPlayerInteraction().forceCompleteQuest(questId, quest.getNpcRequirement(true)); + } else { + c.getAbstractPlayerInteraction().forceCompleteQuest(questId); } + + player.dropMessage(5, "QUEST " + questId + " completed."); } else { player.dropMessage(5, "QUESTID " + questId + " not started or already completed."); } diff --git a/src/client/command/commands/gm3/QuestStartCommand.java b/src/client/command/commands/gm3/QuestStartCommand.java index 4c0ad74cc3..5ad3caef31 100644 --- a/src/client/command/commands/gm3/QuestStartCommand.java +++ b/src/client/command/commands/gm3/QuestStartCommand.java @@ -46,13 +46,13 @@ public class QuestStartCommand extends Command { if (player.getQuestStatus(questid) == 0) { MapleQuest quest = MapleQuest.getInstance(questid); - if (quest != null) { - int npcid = quest.getNpcRequirement(false); - quest.forceStart(player, npcid); - player.dropMessage(5, "QUEST " + questid + " started."); + if (quest != null && quest.getNpcRequirement(false) != -1) { + c.getAbstractPlayerInteraction().forceStartQuest(questid, quest.getNpcRequirement(false)); } else { - player.dropMessage(5, "QUESTID " + questid + " is invalid."); + c.getAbstractPlayerInteraction().forceStartQuest(questid); } + + player.dropMessage(5, "QUEST " + questid + " started."); } else { player.dropMessage(5, "QUESTID " + questid + " already started/completed."); } diff --git a/src/client/command/commands/gm4/PapCommand.java b/src/client/command/commands/gm4/PapCommand.java index bbeed4e137..b6207ea9d8 100644 --- a/src/client/command/commands/gm4/PapCommand.java +++ b/src/client/command/commands/gm4/PapCommand.java @@ -36,6 +36,8 @@ public class PapCommand extends Command { @Override public void execute(MapleClient c, String[] params) { MapleCharacter player = c.getPlayer(); - player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8510000), player.getPosition()); + + // thanks Conrad for noticing mobid typo here + player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8500001), player.getPosition()); } } diff --git a/src/client/command/commands/gm4/ProItemCommand.java b/src/client/command/commands/gm4/ProItemCommand.java index 6c74f4d985..c1502da63f 100644 --- a/src/client/command/commands/gm4/ProItemCommand.java +++ b/src/client/command/commands/gm4/ProItemCommand.java @@ -30,7 +30,7 @@ import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; public class ProItemCommand extends Command { @@ -84,7 +84,7 @@ public class ProItemCommand extends Command { equip.setHp(stat); equip.setMp(stat); - byte flag = equip.getFlag(); + short flag = equip.getFlag(); flag |= ItemConstants.UNTRADEABLE; equip.setFlag(flag); } diff --git a/src/client/command/commands/gm4/SetEqStatCommand.java b/src/client/command/commands/gm4/SetEqStatCommand.java index 7a3a0f562d..667f1848b1 100644 --- a/src/client/command/commands/gm4/SetEqStatCommand.java +++ b/src/client/command/commands/gm4/SetEqStatCommand.java @@ -29,7 +29,7 @@ import client.MapleCharacter; import client.inventory.Equip; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; public class SetEqStatCommand extends Command { { @@ -68,7 +68,7 @@ public class SetEqStatCommand extends Command { eq.setStr(newStat); eq.setLuk(newStat); - byte flag = eq.getFlag(); + short flag = eq.getFlag(); flag |= ItemConstants.UNTRADEABLE; eq.setFlag(flag); diff --git a/src/client/command/commands/gm5/DebugCommand.java b/src/client/command/commands/gm5/DebugCommand.java index 4bfaaf7ca6..d062e63eb7 100644 --- a/src/client/command/commands/gm5/DebugCommand.java +++ b/src/client/command/commands/gm5/DebugCommand.java @@ -27,14 +27,13 @@ import client.command.Command; import client.MapleClient; import client.MapleCharacter; import net.server.Server; -import server.MaplePortal; +import server.maps.MaplePortal; import server.TimerManager; import server.life.MapleMonster; import server.life.SpawnPoint; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import server.maps.MapleReactor; -import tools.MaplePacketCreator; import java.awt.*; import java.util.Arrays; diff --git a/src/client/command/commands/gm5/IpListCommand.java b/src/client/command/commands/gm5/IpListCommand.java index 65413d6370..081493e92c 100644 --- a/src/client/command/commands/gm5/IpListCommand.java +++ b/src/client/command/commands/gm5/IpListCommand.java @@ -23,7 +23,7 @@ import java.util.Collection; import client.MapleClient; import client.MapleCharacter; import client.command.Command; -import constants.GameConstants; +import constants.game.GameConstants; import net.server.Server; import net.server.world.World; diff --git a/src/client/command/commands/gm5/SetCommand.java b/src/client/command/commands/gm5/SetCommand.java index 542093ce6a..a49408b1c5 100644 --- a/src/client/command/commands/gm5/SetCommand.java +++ b/src/client/command/commands/gm5/SetCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm5; import client.command.Command; import client.MapleClient; -import constants.ServerConstants; +import constants.net.ServerConstants; public class SetCommand extends Command { { diff --git a/src/client/command/commands/gm5/ShowMoveLifeCommand.java b/src/client/command/commands/gm5/ShowMoveLifeCommand.java index 92ec84358a..7e88b91dff 100644 --- a/src/client/command/commands/gm5/ShowMoveLifeCommand.java +++ b/src/client/command/commands/gm5/ShowMoveLifeCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm5; import client.command.Command; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; public class ShowMoveLifeCommand extends Command { { @@ -34,6 +34,6 @@ public class ShowMoveLifeCommand extends Command { @Override public void execute(MapleClient c, String[] params) { - ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE = !ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE; + YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE = !YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE; } } diff --git a/src/client/command/commands/gm5/ShowPacketsCommand.java b/src/client/command/commands/gm5/ShowPacketsCommand.java index 24bea650f9..25346e1468 100644 --- a/src/client/command/commands/gm5/ShowPacketsCommand.java +++ b/src/client/command/commands/gm5/ShowPacketsCommand.java @@ -25,7 +25,7 @@ package client.command.commands.gm5; import client.command.Command; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; public class ShowPacketsCommand extends Command { { @@ -34,6 +34,6 @@ public class ShowPacketsCommand extends Command { @Override public void execute(MapleClient c, String[] params) { - ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET = !ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET; + YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET = !YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET; } } diff --git a/src/client/command/commands/gm5/ShowSessionsCommand.java b/src/client/command/commands/gm5/ShowSessionsCommand.java index da853f3c9e..5ae15f609b 100644 --- a/src/client/command/commands/gm5/ShowSessionsCommand.java +++ b/src/client/command/commands/gm5/ShowSessionsCommand.java @@ -21,7 +21,7 @@ package client.command.commands.gm5; import client.MapleClient; import client.command.Command; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; /** * diff --git a/src/client/command/commands/gm6/SupplyRateCouponCommand.java b/src/client/command/commands/gm6/SupplyRateCouponCommand.java index 8e8e0a3ea9..e9742e8ea7 100644 --- a/src/client/command/commands/gm6/SupplyRateCouponCommand.java +++ b/src/client/command/commands/gm6/SupplyRateCouponCommand.java @@ -22,7 +22,7 @@ package client.command.commands.gm6; import client.MapleCharacter; import client.MapleClient; import client.command.Command; -import constants.ServerConstants; +import config.YamlConfig; public class SupplyRateCouponCommand extends Command { { @@ -37,7 +37,7 @@ public class SupplyRateCouponCommand extends Command { return; } - ServerConstants.USE_SUPPLY_RATE_COUPONS = params[0].compareToIgnoreCase("no") != 0; - player.dropMessage(5, "Rate coupons are now " + (ServerConstants.USE_SUPPLY_RATE_COUPONS ? "enabled" : "disabled") + " for purchase at the Cash Shop."); + YamlConfig.config.server.USE_SUPPLY_RATE_COUPONS = params[0].compareToIgnoreCase("no") != 0; + player.dropMessage(5, "Rate coupons are now " + (YamlConfig.config.server.USE_SUPPLY_RATE_COUPONS ? "enabled" : "disabled") + " for purchase at the Cash Shop."); } } diff --git a/src/client/command/commands/gm6/WarpWorldCommand.java b/src/client/command/commands/gm6/WarpWorldCommand.java index 2c5f9f0f9d..d760e2fbc8 100644 --- a/src/client/command/commands/gm6/WarpWorldCommand.java +++ b/src/client/command/commands/gm6/WarpWorldCommand.java @@ -52,7 +52,6 @@ public class WarpWorldCommand extends Command { String[] socket = server.getInetSocket(worldb, c.getChannel()); c.getWorldServer().removePlayer(player); player.getMap().removePlayer(player);//LOL FORGOT THIS >< - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); player.setSessionTransitionState(); player.setWorld(worldb); player.saveCharToDB();//To set the new world :O (true because else 2 player instances are created, one in both worlds) diff --git a/src/client/creator/CharacterFactory.java b/src/client/creator/CharacterFactory.java index f155688ae5..fe65ab98b5 100644 --- a/src/client/creator/CharacterFactory.java +++ b/src/client/creator/CharacterFactory.java @@ -25,7 +25,7 @@ import client.MapleSkinColor; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; -import constants.ServerConstants; +import config.YamlConfig; import net.server.Server; import server.MapleItemInformationProvider; import tools.FilePrinter; @@ -38,7 +38,7 @@ import tools.MaplePacketCreator; public abstract class CharacterFactory { protected synchronized static int createNewCharacter(MapleClient c, String name, int face, int hair, int skin, int gender, CharacterFactoryRecipe recipe) { - if (ServerConstants.COLLECTIVE_CHARSLOT ? c.getAvailableCharacterSlots() <= 0 : c.getAvailableCharacterWorldSlots() <= 0) { + if (YamlConfig.config.server.COLLECTIVE_CHARSLOT ? c.getAvailableCharacterSlots() <= 0 : c.getAvailableCharacterWorldSlots() <= 0) { return -3; } diff --git a/src/client/creator/CharacterFactoryRecipe.java b/src/client/creator/CharacterFactoryRecipe.java index 74521f1478..7ce5ac9b61 100644 --- a/src/client/creator/CharacterFactoryRecipe.java +++ b/src/client/creator/CharacterFactoryRecipe.java @@ -23,6 +23,7 @@ import client.MapleJob; import client.Skill; import client.inventory.Item; import client.inventory.MapleInventoryType; +import config.YamlConfig; import java.util.concurrent.atomic.AtomicInteger; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -54,6 +55,15 @@ public class CharacterFactoryRecipe { this.bottom = bottom; this.shoes = shoes; this.weapon = weapon; + + if (!YamlConfig.config.server.USE_STARTING_AP_4) { + if (YamlConfig.config.server.USE_AUTOASSIGN_STARTERS_AP) { + str = 12; + dex = 5; + } else { + ap = 9; + } + } } public void setStr(int v) { diff --git a/src/client/inventory/Equip.java b/src/client/inventory/Equip.java index 71a600f547..91036d9287 100644 --- a/src/client/inventory/Equip.java +++ b/src/client/inventory/Equip.java @@ -22,9 +22,9 @@ package client.inventory; import client.MapleClient; -import constants.ServerConstants; -import constants.ExpTable; -import constants.ItemConstants; +import config.YamlConfig; +import constants.game.ExpTable; +import constants.inventory.ItemConstants; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -64,7 +64,8 @@ public class Equip extends Item { } private byte upgradeSlots; - private byte level, flag, itemLevel; + private byte level, itemLevel; + private short flag; private short str, dex, _int, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, vicious; private float itemExp; private int ringid = -1; @@ -117,7 +118,7 @@ public class Equip extends Item { } @Override - public byte getFlag() { + public short getFlag() { return flag; } @@ -195,7 +196,7 @@ public class Equip extends Item { } @Override - public void setFlag(byte flag) { + public void setFlag(short flag) { this.flag = flag; } @@ -278,7 +279,7 @@ public class Equip extends Item { private static int getStatModifier(boolean isAttribute) { // each set of stat points grants a chance for a bonus stat point upgrade at equip level up. - if(ServerConstants.USE_EQUIPMNT_LVLUP_POWER) { + if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_POWER) { if(isAttribute) return 2; else return 4; } @@ -289,7 +290,7 @@ public class Equip extends Item { } private static int randomizeStatUpgrade(int top) { - int limit = Math.min(top, ServerConstants.MAX_EQUIPMNT_LVLUP_STAT_UP); + int limit = Math.min(top, YamlConfig.config.server.MAX_EQUIPMNT_LVLUP_STAT_UP); int poolCount = (limit * (limit + 1) / 2) + limit; int rnd = Randomizer.rand(0, poolCount); @@ -382,7 +383,7 @@ public class Equip extends Item { public Pair> gainStats(List> stats) { boolean gotSlot = false, gotVicious = false; String lvupStr = ""; - Integer statUp, maxStat = ServerConstants.MAX_EQUIPMNT_STAT; + Integer statUp, maxStat = YamlConfig.config.server.MAX_EQUIPMNT_STAT; for (Pair stat : stats) { switch (stat.getLeft()) { case incDEX: @@ -482,7 +483,7 @@ public class Equip extends Item { } if(!stats.isEmpty()) { - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) { if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); getUnitSlotUpgrade(stats, StatUpgrade.incSlot); } @@ -490,7 +491,7 @@ public class Equip extends Item { isUpgradeable = false; improveDefaultStats(stats); - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) { if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); getUnitSlotUpgrade(stats, StatUpgrade.incSlot); } @@ -498,7 +499,7 @@ public class Equip extends Item { if(isUpgradeable) { while(stats.isEmpty()) { improveDefaultStats(stats); - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) { if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); getUnitSlotUpgrade(stats, StatUpgrade.incSlot); } @@ -562,14 +563,14 @@ public class Equip extends Item { return; } - int equipMaxLevel = Math.min(30, Math.max(ii.getEquipLevel(this.getItemId(), true), ServerConstants.USE_EQUIPMNT_LVLUP)); + int equipMaxLevel = Math.min(30, Math.max(ii.getEquipLevel(this.getItemId(), true), YamlConfig.config.server.USE_EQUIPMNT_LVLUP)); if (itemLevel >= equipMaxLevel) { return; } int reqLevel = ii.getEquipLevelReq(this.getItemId()); - float masteryModifier = (float)(ServerConstants.EQUIP_EXP_RATE * ExpTable.getExpNeededForLevel(1)) / (float)normalizedMasteryExp(reqLevel); + float masteryModifier = (float)(YamlConfig.config.server.EQUIP_EXP_RATE * ExpTable.getExpNeededForLevel(1)) / (float)normalizedMasteryExp(reqLevel); float elementModifier = (isElemental) ? 0.85f : 0.6f; float baseExpGain = gain * elementModifier * masteryModifier; @@ -577,7 +578,7 @@ public class Equip extends Item { itemExp += baseExpGain; int expNeeded = ExpTable.getEquipExpNeededForLevel(itemLevel); - if(ServerConstants.USE_DEBUG_SHOW_INFO_EQPEXP) System.out.println("'" + ii.getName(this.getItemId()) + "' -> EXP Gain: " + gain + " Mastery: " + masteryModifier + " Base gain: " + baseExpGain + " exp: " + itemExp + " / " + expNeeded + ", Kills TNL: " + expNeeded / (baseExpGain / c.getPlayer().getExpRate())); + if(YamlConfig.config.server.USE_DEBUG_SHOW_INFO_EQPEXP) System.out.println("'" + ii.getName(this.getItemId()) + "' -> EXP Gain: " + gain + " Mastery: " + masteryModifier + " Base gain: " + baseExpGain + " exp: " + itemExp + " / " + expNeeded + ", Kills TNL: " + expNeeded / (baseExpGain / c.getPlayer().getExpRate())); if (itemExp >= expNeeded) { while(itemExp >= expNeeded) { @@ -594,7 +595,7 @@ public class Equip extends Item { } c.getPlayer().forceUpdateItem(this); - //if(ServerConstants.USE_DEBUG) c.getPlayer().dropMessage("'" + ii.getName(this.getItemId()) + "': " + itemExp + " / " + expNeeded); + //if(YamlConfig.config.server.USE_DEBUG) c.getPlayer().dropMessage("'" + ii.getName(this.getItemId()) + "': " + itemExp + " / " + expNeeded); } private boolean reachedMaxLevel() { @@ -604,7 +605,7 @@ public class Equip extends Item { } } - return itemLevel >= ServerConstants.USE_EQUIPMNT_LVLUP; + return itemLevel >= YamlConfig.config.server.USE_EQUIPMNT_LVLUP; } public String showEquipFeatures(MapleClient c) { diff --git a/src/client/inventory/Item.java b/src/client/inventory/Item.java index 74ca97932e..5da563f29d 100644 --- a/src/client/inventory/Item.java +++ b/src/client/inventory/Item.java @@ -21,7 +21,7 @@ along with this program. If not, see . */ package client.inventory; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -40,7 +40,7 @@ public class Item implements Comparable { private MaplePet pet = null; private String owner = ""; protected List log; - private byte flag; + private short flag; private long expiration = -1; private String giftFrom = ""; @@ -126,7 +126,7 @@ public class Item implements Comparable { public int getPetId() { return petid; } - + @Override public int compareTo(Item other) { if (this.id < other.getItemId()) { @@ -146,11 +146,16 @@ public class Item implements Comparable { return Collections.unmodifiableList(log); } - public byte getFlag() { + public short getFlag() { return flag; } - public void setFlag(byte b) { + public void setFlag(short b) { + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); + if (ii.isAccountRestricted(id)) { + b |= ItemConstants.ACCOUNT_SHARING; // thanks Shinigami15 for noticing ACCOUNT_SHARING flag not being applied properly to items server-side + } + this.flag = b; } diff --git a/src/client/inventory/ItemFactory.java b/src/client/inventory/ItemFactory.java index 4dab3c2a7b..7ece9b334b 100644 --- a/src/client/inventory/ItemFactory.java +++ b/src/client/inventory/ItemFactory.java @@ -97,7 +97,7 @@ public enum ItemFactory { equip.setInt((short) rs.getInt("int")); equip.setJump((short) rs.getInt("jump")); equip.setVicious((short) rs.getInt("vicious")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setFlag((short) rs.getInt("flag")); equip.setLuk((short) rs.getInt("luk")); equip.setMatk((short) rs.getInt("matk")); equip.setMdef((short) rs.getInt("mdef")); @@ -173,11 +173,16 @@ public enum ItemFactory { if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) { items.add(new Pair(loadEquipFromResultSet(rs), mit)); } else { - Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), rs.getInt("petid")); + int petid = rs.getInt("petid"); + if (rs.wasNull()) { + petid = -1; + } + + Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), petid); item.setOwner(rs.getString("owner")); item.setExpiration(rs.getLong("expiration")); item.setGiftFrom(rs.getString("giftFrom")); - item.setFlag((byte) rs.getInt("flag")); + item.setFlag((short) rs.getInt("flag")); items.add(new Pair<>(item, mit)); } } @@ -229,7 +234,7 @@ public enum ItemFactory { ps.setInt(6, item.getPosition()); ps.setInt(7, item.getQuantity()); ps.setString(8, item.getOwner()); - ps.setInt(9, item.getPetId()); + ps.setInt(9, item.getPetId()); // thanks Daddy Egg for alerting a case of unique petid constraint breach getting raised ps.setInt(10, item.getFlag()); ps.setLong(11, item.getExpiration()); ps.setString(12, item.getGiftFrom()); @@ -329,11 +334,16 @@ public enum ItemFactory { items.add(new Pair(loadEquipFromResultSet(rs), mit)); } else { if(bundles > 0) { - Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid")); + int petid = rs.getInt("petid"); + if (rs.wasNull()) { + petid = -1; + } + + Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), petid); item.setOwner(rs.getString("owner")); item.setExpiration(rs.getLong("expiration")); item.setGiftFrom(rs.getString("giftFrom")); - item.setFlag((byte) rs.getInt("flag")); + item.setFlag((short) rs.getInt("flag")); items.add(new Pair<>(item, mit)); } } diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index 7255677ddc..a5d6529aed 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -37,7 +37,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import tools.Pair; import client.MapleCharacter; import client.MapleClient; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import server.MapleItemInformationProvider; import client.inventory.manipulator.MapleInventoryManipulator; import tools.FilePrinter; @@ -83,6 +83,19 @@ public class MapleInventory implements Iterable { public void setSlotLimit(int newLimit) { lock.lock(); try { + if (newLimit < slotLimit) { + List toRemove = new LinkedList<>(); + for (Item it : list()) { + if (it.getPosition() > newLimit) { + toRemove.add(it.getPosition()); + } + } + + for (Short slot : toRemove) { + removeSlot(slot); + } + } + slotLimit = (byte) newLimit; } finally { lock.unlock(); diff --git a/src/client/inventory/MaplePet.java b/src/client/inventory/MaplePet.java index acaa12c9bd..f26944c5fa 100644 --- a/src/client/inventory/MaplePet.java +++ b/src/client/inventory/MaplePet.java @@ -21,7 +21,7 @@ */ package client.inventory; -import constants.ExpTable; +import constants.game.ExpTable; import java.awt.Point; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -98,75 +98,11 @@ public class MaplePet extends Item { } } - private static void unreferenceMissingPetsFromInventoryDb() { - PreparedStatement ps = null; - Connection con = null; - try { - con = DatabaseConnection.getConnection(); - - ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)"); - ps.executeUpdate(); - - ps.close(); - con.close(); - } catch(SQLException ex) { - ex.printStackTrace(); - } finally { - try { - if(ps != null && !ps.isClosed()) { - ps.close(); - } - if(con != null && !con.isClosed()) { - con.close(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - } - - private static void deleteMissingPetsFromDb() { - PreparedStatement ps = null; - Connection con = null; - try { - con = DatabaseConnection.getConnection(); - - ps = con.prepareStatement("DELETE FROM pets WHERE petid NOT IN (SELECT petid FROM inventoryitems WHERE petid != -1)"); - ps.executeUpdate(); - - ps.close(); - con.close(); - } catch(SQLException ex) { - ex.printStackTrace(); - } finally { - try { - if(ps != null && !ps.isClosed()) { - ps.close(); - } - if(con != null && !con.isClosed()) { - con.close(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - } - - public static void clearMissingPetsFromDb() { - unreferenceMissingPetsFromInventoryDb(); - deleteMissingPetsFromDb(); - } - public static void deleteFromDb(MapleCharacter owner, int petid) { try { Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?"); - ps.setInt(1, petid); - ps.executeUpdate(); - ps.close(); - - ps = con.prepareStatement("DELETE FROM petignores WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion + PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion ps.setInt(1, petid); ps.executeUpdate(); ps.close(); diff --git a/src/client/inventory/manipulator/MapleCashidGenerator.java b/src/client/inventory/manipulator/MapleCashidGenerator.java index 45af01f768..4345518d43 100644 --- a/src/client/inventory/manipulator/MapleCashidGenerator.java +++ b/src/client/inventory/manipulator/MapleCashidGenerator.java @@ -41,7 +41,10 @@ public class MapleCashidGenerator { ResultSet rs = ps.executeQuery(); while (rs.next()) { - existentCashids.add(rs.getInt(1)); + int id = rs.getInt(1); + if (!rs.wasNull()) { + existentCashids.add(id); + } } rs.close(); diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java index d2f41d82a4..133b3a58b9 100644 --- a/src/client/inventory/manipulator/MapleInventoryManipulator.java +++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java @@ -31,8 +31,8 @@ import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import client.inventory.ModifyInventory; import client.newyear.NewYearCardRecord; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import java.awt.Point; import java.util.ArrayList; @@ -68,29 +68,20 @@ public class MapleInventoryManipulator { return addById(c, itemId, quantity, owner, petid, (byte) 0, expiration); } - public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, byte flag, long expiration) { + public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, short flag, long expiration) { MapleCharacter chr = c.getPlayer(); MapleInventoryType type = ItemConstants.getInventoryType(itemId); - if (c.tryacquireClient()) { - try { - MapleInventory inv = chr.getInventory(type); - inv.lockInventory(); - try { - return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration); - } finally { - inv.unlockInventory(); - } - } finally { - c.releaseClient(); - } - } else { - c.announce(MaplePacketCreator.enableActions()); - return false; + MapleInventory inv = chr.getInventory(type); + inv.lockInventory(); + try { + return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration); + } finally { + inv.unlockInventory(); } } - private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, byte flag, long expiration) { + private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, short flag, long expiration) { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); if (!type.equals(MapleInventoryType.EQUIP)) { short slotMax = ii.getSlotMax(c, itemId); @@ -187,21 +178,12 @@ public class MapleInventoryManipulator { MapleCharacter chr = c.getPlayer(); MapleInventoryType type = item.getInventoryType(); - if (c.tryacquireClient()) { - try { - MapleInventory inv = chr.getInventory(type); - inv.lockInventory(); - try { - return addFromDropInternal(c, chr, type, inv, item, show, petId); - } finally { - inv.unlockInventory(); - } - } finally { - c.releaseClient(); - } - } else { - c.announce(MaplePacketCreator.enableActions()); - return false; + MapleInventory inv = chr.getInventory(type); + inv.lockInventory(); + try { + return addFromDropInternal(c, chr, type, inv, item, show, petId); + } finally { + inv.unlockInventory(); } } @@ -683,9 +665,27 @@ public class MapleInventoryManipulator { c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(2, source, src)))); chr.equipChanged(); } + + private static boolean isDisappearingItemDrop(Item it) { + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); + if (ii.isDropRestricted(it.getItemId())) { + return true; + } else if (ii.isCash(it.getItemId())) { + if (YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_CASH) { // thanks Ari for noticing cash drops not available server-side + return true; + } else if (ItemConstants.isPet(it.getItemId()) && YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_PET) { + return true; + } + } else if (isDroppedItemRestricted(it)) { + return true; + } else if (ItemConstants.isWeddingRing(it.getItemId())) { + return true; + } + + return false; + } public static void drop(MapleClient c, MapleInventoryType type, short src, short quantity) { - MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); if (src < 0) { type = MapleInventoryType.EQUIPPED; } @@ -698,14 +698,21 @@ public class MapleInventoryManipulator { return; } int itemId = source.getItemId(); - if (ItemConstants.isPet(itemId)) { - return; - } MapleMap map = chr.getMap(); if ((!ItemConstants.isRechargeable(itemId) && source.getQuantity() < quantity) || quantity < 0) { return; } + + int petid = source.getPetId(); + if (petid > -1) { + int petIdx = chr.getPetIndex(petid); + if(petIdx > -1) { + MaplePet pet = chr.getPet(petIdx); + chr.unequipPet(pet, true); + } + } + Point dropPos = new Point(chr.getPosition()); if (quantity < source.getQuantity() && !ItemConstants.isRechargeable(itemId)) { Item target = source.copy(); @@ -721,11 +728,9 @@ public class MapleInventoryManipulator { NewYearCardRecord.removeAllNewYearCard(false, chr); c.getAbstractPlayerInteraction().removeAll(4301000); } - } else if (ItemConstants.isWeddingRing(source.getItemId())) { - map.disappearingItemDrop(chr, chr, target, dropPos); } - if (ii.isDropRestricted(target.getItemId()) || ii.isCash(target.getItemId()) || isDroppedItemRestricted(target)) { + if (isDisappearingItemDrop(target)) { map.disappearingItemDrop(chr, chr, target, dropPos); } else { map.spawnItemDrop(chr, chr, target, dropPos, true, true); @@ -754,11 +759,9 @@ public class MapleInventoryManipulator { NewYearCardRecord.removeAllNewYearCard(false, chr); c.getAbstractPlayerInteraction().removeAll(4301000); } - } else if (ItemConstants.isWeddingRing(source.getItemId())) { - map.disappearingItemDrop(chr, chr, source, dropPos); } - if (ii.isDropRestricted(itemId) || ii.isCash(itemId) || isDroppedItemRestricted(source)) { + if (isDisappearingItemDrop(source)) { map.disappearingItemDrop(chr, chr, source, dropPos); } else { map.spawnItemDrop(chr, chr, source, dropPos, true, true); @@ -781,7 +784,7 @@ public class MapleInventoryManipulator { } private static boolean isDroppedItemRestricted(Item it) { - return ServerConstants.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable(); + return YamlConfig.config.server.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable(); } public static boolean isSandboxItem(Item it) { diff --git a/src/client/inventory/manipulator/MapleKarmaManipulator.java b/src/client/inventory/manipulator/MapleKarmaManipulator.java index 92444187bc..c0cb0838d3 100644 --- a/src/client/inventory/manipulator/MapleKarmaManipulator.java +++ b/src/client/inventory/manipulator/MapleKarmaManipulator.java @@ -19,7 +19,7 @@ */ package client.inventory.manipulator; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import client.inventory.Item; /** @@ -27,18 +27,18 @@ import client.inventory.Item; * @author RonanLana */ public class MapleKarmaManipulator { - private static int getKarmaFlag(Item item) { + private static short getKarmaFlag(Item item) { return item.getItemType() == 1 ? ItemConstants.KARMA_EQP : ItemConstants.KARMA_USE; } public static boolean hasKarmaFlag(Item item) { - int karmaFlag = getKarmaFlag(item); + short karmaFlag = getKarmaFlag(item); return (item.getFlag() & karmaFlag) == karmaFlag; } public static void toggleKarmaFlagToUntradeable(Item item) { - int karmaFlag = getKarmaFlag(item); - int flag = item.getFlag(); + short karmaFlag = getKarmaFlag(item); + short flag = item.getFlag(); if ((flag & karmaFlag) == karmaFlag) { flag ^= karmaFlag; @@ -49,8 +49,8 @@ public class MapleKarmaManipulator { } public static void setKarmaFlag(Item item) { - int karmaFlag = getKarmaFlag(item); - int flag = item.getFlag(); + short karmaFlag = getKarmaFlag(item); + short flag = item.getFlag(); flag |= karmaFlag; flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE); diff --git a/src/client/processor/BuybackProcessor.java b/src/client/processor/action/BuybackProcessor.java similarity index 95% rename from src/client/processor/BuybackProcessor.java rename to src/client/processor/action/BuybackProcessor.java index f031c9c9ea..cacd0067be 100644 --- a/src/client/processor/BuybackProcessor.java +++ b/src/client/processor/action/BuybackProcessor.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.action; // thanks Alex for pointing out some package structures containing broad modules import client.MapleClient; import client.MapleCharacter; diff --git a/src/client/processor/MakerProcessor.java b/src/client/processor/action/MakerProcessor.java similarity index 70% rename from src/client/processor/MakerProcessor.java rename to src/client/processor/action/MakerProcessor.java index 88f2694482..c9816ccbcf 100644 --- a/src/client/processor/MakerProcessor.java +++ b/src/client/processor/action/MakerProcessor.java @@ -17,22 +17,23 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.action; import client.MapleClient; import client.MapleCharacter; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; -import constants.ServerConstants; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.GameConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; +import constants.game.GameConstants; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import server.MakerItemFactory; +import server.MakerItemFactory.MakerItemCreateEntry; import server.MapleItemInformationProvider; import tools.FilePrinter; import tools.MaplePacketCreator; @@ -44,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor; * @author Ronan */ public class MakerProcessor { + private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); public static void makerAction(SeekableLittleEndianAccessor slea, MapleClient c) { @@ -54,7 +56,7 @@ public class MakerProcessor { int toDisassemble = -1, pos = -1; boolean makerSucceeded = true; - MakerItemFactory.MakerItemCreateEntry recipe; + MakerItemCreateEntry recipe; Map reagentids = new LinkedHashMap<>(); int stimulantid = -1; @@ -62,29 +64,31 @@ public class MakerProcessor { int fromLeftover = toCreate; toCreate = ii.getMakerCrystalFromLeftover(toCreate); if(toCreate == -1) { - c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal conversion.")); + c.announce(MaplePacketCreator.serverNotice(1, ii.getName(fromLeftover) + " is unavailable for Monster Crystal conversion.")); + c.announce(MaplePacketCreator.makerEnableActions()); return; } - - recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover); + + recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover, toCreate); } else if(type == 4) { // disassembling slea.readInt(); // 1... probably inventory type pos = slea.readInt(); Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos); if(it != null && it.getItemId() == toCreate) { - Pair p; - - if((p = generateDisassemblyInfo(toCreate)) != null) { - recipe = MakerItemFactory.generateDisassemblyCrystalEntry(p.getLeft(), p.getRight()); - toDisassemble = toCreate; - toCreate = ii.getMakerCrystalFromEquip(toCreate); + toDisassemble = toCreate; + + Pair>> p = generateDisassemblyInfo(toDisassemble); + if(p != null) { + recipe = MakerItemFactory.generateDisassemblyCrystalEntry(toDisassemble, p.getLeft(), p.getRight()); } else { c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly.")); + c.announce(MaplePacketCreator.makerEnableActions()); return; } } else { c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly.")); + c.announce(MaplePacketCreator.makerEnableActions()); return; } } else { @@ -132,6 +136,7 @@ public class MakerProcessor { if(!reagentids.isEmpty()) { if(!removeOddMakerReagents(toCreate, reagentids)) { c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items.")); + c.announce(MaplePacketCreator.makerEnableActions()); return; } } @@ -146,65 +151,80 @@ public class MakerProcessor { case -1:// non-available for Maker itemid has been tried to forge FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill."); c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation.")); + c.announce(MaplePacketCreator.makerEnableActions()); break; case 1: // no items - c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + ".")); + c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + ii.getName(toCreate) + ".")); + c.announce(MaplePacketCreator.makerEnableActions()); break; case 2: // no meso c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation.")); + c.announce(MaplePacketCreator.makerEnableActions()); break; case 3: // no req level c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation.")); + c.announce(MaplePacketCreator.makerEnableActions()); break; case 4: // no req skill level c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation.")); + c.announce(MaplePacketCreator.makerEnableActions()); + break; + + case 5: // inventory full + c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full.")); + c.announce(MaplePacketCreator.makerEnableActions()); break; default: - if (MapleInventoryManipulator.checkSpace(c, toCreate, (short) recipe.getRewardAmount(), "")) { + if(toDisassemble != -1) { + MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false); + } else { for (Pair p : recipe.getReqItems()) { - c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight()); + c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight(), false); } + } - if(toDisassemble != -1) { - MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false); - c.announce(MaplePacketCreator.getShowItemGain(toDisassemble, (short) -1, true)); - } - - int cost = recipe.getCost(); - if(stimulantid == -1 && reagentids.isEmpty()) { - if(cost > 0) c.getPlayer().gainMeso(-cost); - + int cost = recipe.getCost(); + if(stimulantid == -1 && reagentids.isEmpty()) { + if(cost > 0) c.getPlayer().gainMeso(-cost, false); + + for (Pair p : recipe.getGainItems()) { c.getPlayer().setCS(true); - c.getAbstractPlayerInteraction().gainItem(toCreate, (short) recipe.getRewardAmount()); + c.getAbstractPlayerInteraction().gainItem(p.getLeft(), p.getRight().shortValue(), false); c.getPlayer().setCS(false); - } else { - if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1); - if(!reagentids.isEmpty()) { - for(Map.Entry r : reagentids.entrySet()) { - c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue())); - } - } - - if(cost > 0) c.getPlayer().gainMeso(-cost); - makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids); - } - - if(makerSucceeded) c.announce(MaplePacketCreator.serverNotice(1, "You have successfully created " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + ".")); - else c.getPlayer().dropMessage(5, "The Maker skill lights up, but the skill winds up as if nothing happened."); - - c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded)); - c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false); - - if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) { - c.getAbstractPlayerInteraction().setQuestProgress(6033, 1); } } else { - c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full.")); + toCreate = recipe.getGainItems().get(0).getLeft(); + + if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1, false); + if(!reagentids.isEmpty()) { + for(Map.Entry r : reagentids.entrySet()) { + c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()), false); + } + } + + if(cost > 0) c.getPlayer().gainMeso(-cost, false); + makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids); + } + + // thanks inhyuk for noticing missing MAKER_RESULT packets + if (type == 3) { + c.announce(MaplePacketCreator.makerResultCrystal(recipe.getGainItems().get(0).getLeft(), recipe.getReqItems().get(0).getLeft())); + } else if (type == 4) { + c.announce(MaplePacketCreator.makerResultDesynth(recipe.getReqItems().get(0).getLeft(), recipe.getCost(), recipe.getGainItems())); + } else { + c.announce(MaplePacketCreator.makerResult(makerSucceeded, recipe.getGainItems().get(0).getLeft(), recipe.getGainItems().get(0).getRight(), recipe.getCost(), recipe.getReqItems(), stimulantid, new LinkedList<>(reagentids.keySet()))); + } + + c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded)); + c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false); + + if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) { + c.getAbstractPlayerInteraction().setQuestProgress(6033, 1); } } } finally { @@ -218,7 +238,7 @@ public class MakerProcessor { Map reagentType = new LinkedHashMap<>(); List toRemove = new LinkedList<>(); - boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon + boolean isWeapon = ItemConstants.isWeapon(toCreate) || YamlConfig.config.server.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon for(Map.Entry r : reagentids.entrySet()) { int curRid = r.getKey(); @@ -271,12 +291,12 @@ public class MakerProcessor { } } - private static Pair generateDisassemblyInfo(int itemId) { + private static Pair>> generateDisassemblyInfo(int itemId) { int recvFee = ii.getMakerDisassembledFee(itemId); if(recvFee > -1) { - int recvQty = ii.getMakerDisassembledQuantity(itemId); - if(recvQty > 0) { - return new Pair<>(recvFee, recvQty); + List> gains = ii.getMakerDisassembledItems(itemId); + if(!gains.isEmpty()) { + return new Pair<>(recvFee, gains); } } @@ -287,7 +307,7 @@ public class MakerProcessor { return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007); } - private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) { + private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) { if(recipe == null) { return -1; } @@ -308,10 +328,29 @@ public class MakerProcessor { return 4; } + List addItemids = new LinkedList<>(); + List addQuantity = new LinkedList<>(); + List rmvItemids = new LinkedList<>(); + List rmvQuantity = new LinkedList<>(); + + for (Pair p : recipe.getReqItems()) { + rmvItemids.add(p.getLeft()); + rmvQuantity.add(p.getRight()); + } + + for (Pair p : recipe.getGainItems()) { + addItemids.add(p.getLeft()); + addQuantity.add(p.getRight()); + } + + if (!c.getAbstractPlayerInteraction().canHoldAllAfterRemoving(addItemids, addQuantity, rmvItemids, rmvQuantity)) { + return 5; + } + return 0; } - private static boolean hasItems(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) { + private static boolean hasItems(MapleClient c, MakerItemCreateEntry recipe) { for (Pair p : recipe.getReqItems()) { int itemId = p.getLeft(); if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).countById(itemId) < p.getRight()) { @@ -332,8 +371,8 @@ public class MakerProcessor { Equip eqp = (Equip)item; if(ItemConstants.isAccessory(item.getItemId()) && eqp.getUpgradeSlots() <= 0) eqp.setUpgradeSlots(3); - if(ServerConstants.USE_ENHANCED_CRAFTING == true) { - if(!(c.getPlayer().isGM() && ServerConstants.USE_PERFECT_GM_SCROLL)) { + if(YamlConfig.config.server.USE_ENHANCED_CRAFTING == true) { + if(!(c.getPlayer().isGM() && YamlConfig.config.server.USE_PERFECT_GM_SCROLL)) { eqp.setUpgradeSlots((byte)(eqp.getUpgradeSlots() + 1)); } item = MapleItemInformationProvider.getInstance().scrollEquipWithId(eqp, 2049100, true, 2049100, c.getPlayer().isGM()); @@ -397,7 +436,6 @@ public class MakerProcessor { } MapleInventoryManipulator.addFromDrop(c, item, false, -1); - c.announce(MaplePacketCreator.getShowItemGain(itemid, (short) 1, true)); return true; } } diff --git a/src/client/processor/action/PetAutopotProcessor.java b/src/client/processor/action/PetAutopotProcessor.java new file mode 100644 index 0000000000..0bb2c79c30 --- /dev/null +++ b/src/client/processor/action/PetAutopotProcessor.java @@ -0,0 +1,190 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + Copyleft (L) 2016 - 2019 RonanLana (HeavenMS) + + 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 client.processor.action; + +import client.MapleCharacter; +import client.MapleClient; +import client.inventory.Item; +import client.inventory.MapleInventory; +import client.inventory.MapleInventoryType; +import client.inventory.manipulator.MapleInventoryManipulator; +import config.YamlConfig; +import java.util.List; +import server.MapleItemInformationProvider; +import server.MapleStatEffect; +import tools.MaplePacketCreator; + +/** + * + * @author Ronan - multi-pot consumption feature + */ +public class PetAutopotProcessor { + + private static class AutopotAction { + + private MapleClient c; + private short slot; + private int itemId; + + private Item toUse; + private List toUseList; + + private boolean hasHpGain, hasMpGain; + private int maxHp, maxMp, curHp, curMp; + private double incHp, incMp; + + private boolean cursorOnNextAvailablePot(MapleCharacter chr) { + if(toUseList == null) { + toUseList = chr.getInventory(MapleInventoryType.USE).linkedListById(itemId); + } + + toUse = null; + while(!toUseList.isEmpty()) { + Item it = toUseList.remove(0); + + if(it.getQuantity() > 0) { + toUse = it; + slot = it.getPosition(); + + return true; + } + } + + return false; + } + + public AutopotAction(MapleClient c, short slot, int itemId) { + this.c = c; + this.slot = slot; + this.itemId = itemId; + } + + public void run() { + MapleClient c = this.c; + MapleCharacter chr = c.getPlayer(); + if (!chr.isAlive()) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + + int useCount = 0, qtyCount = 0; + MapleStatEffect stat = null; + + maxHp = chr.getCurrentMaxHp(); + maxMp = chr.getCurrentMaxMp(); + + curHp = chr.getHp(); + curMp = chr.getMp(); + + MapleInventory useInv = chr.getInventory(MapleInventoryType.USE); + useInv.lockInventory(); + try { + toUse = useInv.getItem(slot); + if (toUse != null) { + if (toUse.getItemId() != itemId) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + + toUseList = null; + + // from now on, toUse becomes the "cursor" for the current pot being used + if (toUse.getQuantity() <= 0) { + if (!cursorOnNextAvailablePot(chr)) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + } + + stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId()); + hasHpGain = stat.getHp() > 0 || stat.getHpRate() > 0.0; + hasMpGain = stat.getMp() > 0 || stat.getMpRate() > 0.0; + + incHp = stat.getHp(); + if(incHp <= 0 && hasHpGain) incHp = Math.ceil(maxHp * stat.getHpRate()); + + incMp = stat.getMp(); + if(incMp <= 0 && hasMpGain) incMp = Math.ceil(maxMp * stat.getMpRate()); + + if (YamlConfig.config.server.USE_COMPULSORY_AUTOPOT) { + if (hasHpGain) { + double hpRatio = (YamlConfig.config.server.PET_AUTOHP_RATIO * maxHp) - curHp; + if (hpRatio > 0.0) { + qtyCount = (int) Math.ceil(hpRatio / incHp); + } + } + + if (hasMpGain) { + double mpRatio = ((YamlConfig.config.server.PET_AUTOMP_RATIO * maxMp) - curMp); + if (mpRatio > 0.0) { + qtyCount = Math.max(qtyCount, (int) Math.ceil(mpRatio / incMp)); + } + } + + if (qtyCount < 0) { // thanks Flint, Kevs for noticing an issue where negative counts were getting achieved + qtyCount = 0; + } + } else { + qtyCount = 1; // non-compulsory autopot concept thanks to marcuswoon + } + + while (true) { + short qtyToUse = (short) Math.min(qtyCount, toUse.getQuantity()); + MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, qtyToUse, false); + + curHp += (incHp * qtyToUse); + curMp += (incMp * qtyToUse); + + useCount += qtyToUse; + qtyCount -= qtyToUse; + + if(toUse.getQuantity() == 0 && qtyCount > 0) { + // depleted out the current slot, fetch for more + + if(!cursorOnNextAvailablePot(chr)) { + break; // no more pots available + } + } else { + break; // gracefully finished it's job, quit the loop + } + } + } + } finally { + useInv.unlockInventory(); + } + + if (stat != null) { + for (int i = 0; i < useCount; i++) { + stat.applyTo(chr); + } + } + + chr.announce(MaplePacketCreator.enableActions()); + } + } + + public static void runAutopotAction(MapleClient c, short slot, int itemid) { + AutopotAction action = new AutopotAction(c, slot, itemid); + action.run(); + } + +} diff --git a/src/client/processor/SpawnPetProcessor.java b/src/client/processor/action/SpawnPetProcessor.java similarity index 98% rename from src/client/processor/SpawnPetProcessor.java rename to src/client/processor/action/SpawnPetProcessor.java index f3d12966c8..c8c74dcf86 100644 --- a/src/client/processor/SpawnPetProcessor.java +++ b/src/client/processor/action/SpawnPetProcessor.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.action; import client.MapleCharacter; import java.awt.Point; @@ -62,7 +62,7 @@ public class SpawnPetProcessor { long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration(); MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false); MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration); - MaplePet.deleteFromDb(chr, petid); + c.announce(MaplePacketCreator.enableActions()); return; } diff --git a/src/client/processor/DueyProcessor.java b/src/client/processor/npc/DueyProcessor.java similarity index 82% rename from src/client/processor/DueyProcessor.java rename to src/client/processor/npc/DueyProcessor.java index 230e5927f6..0cc17d4636 100644 --- a/src/client/processor/DueyProcessor.java +++ b/src/client/processor/npc/DueyProcessor.java @@ -21,7 +21,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.npc; import client.MapleCharacter; import client.MapleClient; @@ -32,13 +32,14 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; import java.util.Calendar; import java.util.Collections; import java.util.LinkedList; @@ -59,6 +60,7 @@ import tools.Pair; public class DueyProcessor { public enum Actions { + TOSERVER_RECV_ITEM(0x00), TOSERVER_SEND_ITEM(0x02), TOSERVER_CLAIM_PACKAGE(0x04), TOSERVER_REMOVE_PACKAGE(0x05), @@ -112,19 +114,6 @@ public class DueyProcessor { return null; } - private static String getCurrentDate() { - String date = ""; - Calendar cal = Calendar.getInstance(); - int day = cal.get(Calendar.DATE) - 1; // instant duey ? - int month = cal.get(Calendar.MONTH) + 1; // its an array of months. - int year = cal.get(Calendar.YEAR); - date += day <= 9 ? "0" + day + "-" : "" + day + "-"; - date += month <= 9 ? "0" + month + "-" : "" + month + "-"; - date += year; - - return date; - } - private static void showDueyNotification(MapleClient c, MapleCharacter player) { Connection con = null; PreparedStatement ps = null; @@ -132,7 +121,7 @@ public class DueyProcessor { ResultSet rs = null; try { con = DatabaseConnection.getConnection(); - ps = con.prepareStatement("SELECT Mesos FROM dueypackages WHERE ReceiverId = ? and Checked = 1"); + ps = con.prepareStatement("SELECT SenderName, Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC"); ps.setInt(1, player.getId()); rs = ps.executeQuery(); if (rs.next()) { @@ -143,11 +132,11 @@ public class DueyProcessor { pss.executeUpdate(); pss.close(); con2.close(); + + c.announce(MaplePacketCreator.sendDueyParcelReceived(rs.getString("SenderName"), rs.getInt("Type") == 1)); } catch (SQLException e) { e.printStackTrace(); } - - c.announce(MaplePacketCreator.sendDueyNotification(false)); } } catch (SQLException e) { e.printStackTrace(); @@ -207,7 +196,7 @@ public class DueyProcessor { dueypack.setSender(rs.getString("SenderName")); dueypack.setMesos(rs.getInt("Mesos")); - dueypack.setSentTime(rs.getString("TimeStamp")); + dueypack.setSentTime(rs.getTimestamp("TimeStamp"), rs.getBoolean("Type")); dueypack.setMessage(rs.getString("Message")); return dueypack; @@ -241,7 +230,7 @@ public class DueyProcessor { return packages; } - private static int createPackage(int mesos, String message, String sender, int toCid) { + private static int createPackage(int mesos, String message, String sender, int toCid, boolean quick) { try { Connection con = null; PreparedStatement ps = null; @@ -249,16 +238,17 @@ public class DueyProcessor { try { con = DatabaseConnection.getConnection(); - ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Checked) VALUES (?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS); + ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Type, Checked) VALUES (?, ?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS); ps.setInt(1, toCid); ps.setString(2, sender); ps.setInt(3, mesos); - ps.setString(4, getCurrentDate()); + ps.setTimestamp(4, new Timestamp(System.currentTimeMillis())); ps.setString(5, message); + ps.setInt(6, quick ? 1 : 0); int updateRows = ps.executeUpdate(); if (updateRows < 1) { - FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]"); + FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]"); return -1; } @@ -267,7 +257,7 @@ public class DueyProcessor { if (rs.next()) { packageId = rs.getInt(1); } else { - FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]"); + FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]"); return -1; } @@ -348,10 +338,18 @@ public class DueyProcessor { return 0; } - public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient) { + public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) { if (c.tryacquireClient()) { try { - final int fee = 5000 + MapleTrade.getFee(sendMesos); + int fee = MapleTrade.getFee(sendMesos); + if (!quick) { + fee += 5000; + } else if (!c.getPlayer().haveItem(5330000)) { + AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey."); + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with Quick Delivery, mesos " + sendMesos + " and amount " + amount); + c.disconnect(true, false); + return; + } long finalcost = (long) sendMesos + fee; if (finalcost < 0 || finalcost > Integer.MAX_VALUE || (amount < 1 && sendMesos == 0)) { @@ -385,7 +383,11 @@ public class DueyProcessor { return; } - int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid); + if (quick) { + MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5330000, (short) 1, false, false); + } + + int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid, quick); if (packageId == -1) { c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_ENABLE_ACTIONS.getCode())); return; @@ -451,11 +453,16 @@ public class DueyProcessor { } con.close(); - if(dp == null) { + if (dp == null) { c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode())); FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to receive package from duey with id " + packageId); return; } + + if (dp.isDeliveringTime()) { + c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode())); + return; + } Item dpItem = dp.getItem(); if (dpItem != null) { @@ -490,10 +497,21 @@ public class DueyProcessor { } } - public static void dueySendTalk(MapleClient c) { + public static void dueySendTalk(MapleClient c, boolean quickDelivery) { if (c.tryacquireClient()) { try { - c.announce(MaplePacketCreator.sendDuey((byte) 8, loadPackages(c.getPlayer()))); + long timeNow = System.currentTimeMillis(); + if(timeNow - c.getPlayer().getNpcCooldown() < YamlConfig.config.server.BLOCK_NPC_RACE_CONDT) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + c.getPlayer().setNpcCooldown(timeNow); + + if (quickDelivery) { + c.announce(MaplePacketCreator.sendDuey(0x1A, null)); + } else { + c.announce(MaplePacketCreator.sendDuey(0x8, loadPackages(c.getPlayer()))); + } } finally { c.releaseClient(); } @@ -501,9 +519,43 @@ public class DueyProcessor { } public static void dueyCreatePackage(Item item, int mesos, String sender, int recipientCid) { - int packageId = createPackage(mesos, "", sender, recipientCid); + int packageId = createPackage(mesos, null, sender, recipientCid, false); if (packageId != -1) { insertPackageItem(packageId, item); } } + + public static void runDueyExpireSchedule() { + try { + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, -30); + + Timestamp ts = new Timestamp(c.getTime().getTime()); + + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT `PackageId` FROM dueypackages WHERE `TimeStamp` < ?"); + ps.setTimestamp(1, ts); + + List toRemove = new LinkedList<>(); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + toRemove.add(rs.getInt("PackageId")); + } + } + ps.close(); + + for (Integer pid : toRemove) { + removePackageFromDB(pid); + } + + ps = con.prepareStatement("DELETE FROM dueypackages WHERE `TimeStamp` < ?"); + ps.setTimestamp(1, ts); + ps.executeUpdate(); + ps.close(); + + con.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } } diff --git a/src/client/processor/FredrickProcessor.java b/src/client/processor/npc/FredrickProcessor.java similarity index 99% rename from src/client/processor/FredrickProcessor.java rename to src/client/processor/npc/FredrickProcessor.java index e359949b6b..3b807b6552 100644 --- a/src/client/processor/FredrickProcessor.java +++ b/src/client/processor/npc/FredrickProcessor.java @@ -21,7 +21,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.npc; import client.MapleCharacter; import client.MapleClient; @@ -37,7 +37,6 @@ import java.sql.Timestamp; import java.util.LinkedList; import java.util.List; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.ServerConstants; import java.util.Collections; import net.server.Server; import net.server.world.World; diff --git a/src/client/processor/StorageProcessor.java b/src/client/processor/npc/StorageProcessor.java similarity index 85% rename from src/client/processor/StorageProcessor.java rename to src/client/processor/npc/StorageProcessor.java index b2f5f4aa0e..8251c9791c 100644 --- a/src/client/processor/StorageProcessor.java +++ b/src/client/processor/npc/StorageProcessor.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 client.processor; +package client.processor.npc; import client.MapleClient; import client.MapleCharacter; @@ -28,8 +28,8 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import client.inventory.manipulator.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MapleStorage; @@ -45,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor; public class StorageProcessor { public static void storageAction(SeekableLittleEndianAccessor slea, MapleClient c) { + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); MapleCharacter chr = c.getPlayer(); MapleStorage storage = chr.getStorage(); byte mode = slea.readByte(); @@ -69,7 +70,7 @@ public class StorageProcessor { slot = storage.getSlot(MapleInventoryType.getByType(type), slot); Item item = storage.getItem(slot); if (item != null) { - if (MapleItemInformationProvider.getInstance().isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) { + if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) { c.announce(MaplePacketCreator.getStorageError((byte) 0x0C)); return; } @@ -83,13 +84,20 @@ public class StorageProcessor { } if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) { - item = storage.takeOut(slot);//actually the same but idc - String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId()); - FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")"); - chr.setUsedStorage(); - MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item); - MapleInventoryManipulator.addFromDrop(c, item, false); - storage.sendTakenOut(c, item.getInventoryType()); + if (storage.takeOut(item)) { + chr.setUsedStorage(); + + MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item); + MapleInventoryManipulator.addFromDrop(c, item, false); + + String itemName = ii.getName(item.getItemId()); + FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")"); + + storage.sendTakenOut(c, item.getInventoryType()); + } else { + c.announce(MaplePacketCreator.enableActions()); + return; + } } else { c.announce(MaplePacketCreator.getStorageError((byte) 0x0A)); } @@ -149,14 +157,17 @@ public class StorageProcessor { MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item); item.setQuantity(quantity); - storage.store(item); - storage.sendStored(c, ItemConstants.getInventoryType(itemId)); - String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId()); - FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")"); + + storage.store(item); // inside a critical section, "!(storage.isFull())" is still in effect... chr.setUsedStorage(); + + String itemName = ii.getName(item.getItemId()); + FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")"); + + storage.sendStored(c, ItemConstants.getInventoryType(itemId)); } } else if (mode == 6) { // arrange items - if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c); + if(YamlConfig.config.server.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c); c.announce(MaplePacketCreator.enableActions()); } else if (mode == 7) { // meso int meso = slea.readInt(); @@ -178,14 +189,14 @@ public class StorageProcessor { } storage.setMeso(storageMesos - meso); chr.gainMeso(meso, false, true, false); - FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos"); chr.setUsedStorage(); + FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos"); + storage.sendMeso(c); } else { c.announce(MaplePacketCreator.enableActions()); return; } - storage.sendMeso(c); - } else if (mode == 8) {// close + } else if (mode == 8) {// close... unless the player decides to enter cash shop! storage.close(); } } finally { diff --git a/src/client/processor/AssignAPProcessor.java b/src/client/processor/stat/AssignAPProcessor.java similarity index 91% rename from src/client/processor/AssignAPProcessor.java rename to src/client/processor/stat/AssignAPProcessor.java index 74390eb1bc..3c2edb6fd8 100644 --- a/src/client/processor/AssignAPProcessor.java +++ b/src/client/processor/stat/AssignAPProcessor.java @@ -21,7 +21,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.stat; import client.MapleCharacter; import client.MapleClient; @@ -33,11 +33,12 @@ import client.autoban.AutobanFactory; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ServerConstants; +import config.YamlConfig; import constants.skills.BlazeWizard; import constants.skills.Brawler; import constants.skills.DawnWarrior; import constants.skills.Magician; +import constants.skills.ThunderBreaker; import constants.skills.Warrior; import java.util.ArrayList; import java.util.Collection; @@ -69,7 +70,7 @@ public class AssignAPProcessor { int remainingAp = chr.getRemainingAp(); slea.skip(8); - if(ServerConstants.USE_SERVER_AUTOASSIGNER) { + if(YamlConfig.config.server.USE_SERVER_AUTOASSIGNER) { // --------- Ronan Lana's AUTOASSIGNER --------- // This method excels for assigning APs in such a way to cover all equipments AP requirements. byte opt = slea.readByte(); // useful for pirate autoassigning @@ -134,7 +135,7 @@ public class AssignAPProcessor { luk = scStat; str = 0; dex = 0; - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) { temp = luk + chr.getLuk() - CAP; scStat -= temp; prStat += temp; @@ -160,7 +161,7 @@ public class AssignAPProcessor { str = scStat; int_ = 0; luk = 0; - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { temp = str + chr.getStr() - CAP; scStat -= temp; prStat += temp; @@ -186,7 +187,7 @@ public class AssignAPProcessor { str = scStat; int_ = 0; luk = 0; - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { temp = str + chr.getStr() - CAP; scStat -= temp; prStat += temp; @@ -240,12 +241,12 @@ public class AssignAPProcessor { str = trStat; int_ = 0; - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) { temp = dex + chr.getDex() - CAP; scStat -= temp; prStat += temp; } - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) { temp = str + chr.getStr() - CAP; trStat -= temp; prStat += temp; @@ -312,7 +313,7 @@ public class AssignAPProcessor { dex = scStat; int_ = 0; luk = 0; - if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) { + if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) { temp = dex + chr.getDex() - CAP; scStat -= temp; prStat += temp; @@ -347,14 +348,7 @@ public class AssignAPProcessor { if(slea.available() < 16) { AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign."); - final MapleClient client = c; - ThreadManager.getInstance().newTask(new Runnable() { - @Override - public void run() { - client.disconnect(false, false); - } - }); - + c.disconnect(true, false); return; } @@ -386,44 +380,44 @@ public class AssignAPProcessor { int newVal = 0; if (type.equals(MapleStat.STR)) { newVal = statUpdate[0] + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[0] += (gain - (newVal - ServerConstants.MAX_AP)); - statUpdate[0] = ServerConstants.MAX_AP; + if (newVal > YamlConfig.config.server.MAX_AP) { + statGain[0] += (gain - (newVal - YamlConfig.config.server.MAX_AP)); + statUpdate[0] = YamlConfig.config.server.MAX_AP; } else { statGain[0] += gain; statUpdate[0] = newVal; } } else if (type.equals(MapleStat.INT)) { newVal = statUpdate[3] + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[3] += (gain - (newVal - ServerConstants.MAX_AP)); - statUpdate[3] = ServerConstants.MAX_AP; + if (newVal > YamlConfig.config.server.MAX_AP) { + statGain[3] += (gain - (newVal - YamlConfig.config.server.MAX_AP)); + statUpdate[3] = YamlConfig.config.server.MAX_AP; } else { statGain[3] += gain; statUpdate[3] = newVal; } } else if (type.equals(MapleStat.LUK)) { newVal = statUpdate[2] + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[2] += (gain - (newVal - ServerConstants.MAX_AP)); - statUpdate[2] = ServerConstants.MAX_AP; + if (newVal > YamlConfig.config.server.MAX_AP) { + statGain[2] += (gain - (newVal - YamlConfig.config.server.MAX_AP)); + statUpdate[2] = YamlConfig.config.server.MAX_AP; } else { statGain[2] += gain; statUpdate[2] = newVal; } } else if (type.equals(MapleStat.DEX)) { newVal = statUpdate[1] + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[1] += (gain - (newVal - ServerConstants.MAX_AP)); - statUpdate[1] = ServerConstants.MAX_AP; + if (newVal > YamlConfig.config.server.MAX_AP) { + statGain[1] += (gain - (newVal - YamlConfig.config.server.MAX_AP)); + statUpdate[1] = YamlConfig.config.server.MAX_AP; } else { statGain[1] += gain; statUpdate[1] = newVal; } } - if (newVal > ServerConstants.MAX_AP) { - return newVal - ServerConstants.MAX_AP; + if (newVal > YamlConfig.config.server.MAX_AP) { + return newVal - YamlConfig.config.server.MAX_AP; } return 0; } @@ -488,7 +482,7 @@ public class AssignAPProcessor { } break; case 2048: // HP - if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { + if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) { if (APTo != 8192) { player.message("You can only swap HP ability points to MP."); c.announce(MaplePacketCreator.enableActions()); @@ -513,13 +507,13 @@ public class AssignAPProcessor { int curHp = player.getHp(); int hplose = -takeHp(player.getJob()); player.assignHP(hplose, -1); - if (!ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) { + if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { player.updateHp(Math.max(1, curHp + hplose)); } break; case 8192: // MP - if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { + if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) { if (APTo != 2048) { player.message("You can only swap MP ability points to HP."); c.announce(MaplePacketCreator.enableActions()); @@ -557,7 +551,7 @@ public class AssignAPProcessor { int curMp = player.getMp(); int mplose = -takeMp(job); player.assignMP(mplose, -1); - if (!ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) { + if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) { player.updateMp(Math.max(0, curMp + mplose)); } break; @@ -646,7 +640,7 @@ public class AssignAPProcessor { MaxHP += increaseHP.getEffect(sLvl).getY(); } - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 20; } else { @@ -656,7 +650,7 @@ public class AssignAPProcessor { MaxHP += 20; } } else if(job.isA(MapleJob.ARAN1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 20; } else { @@ -666,7 +660,7 @@ public class AssignAPProcessor { MaxHP += 28; } } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 6; } else { @@ -676,7 +670,7 @@ public class AssignAPProcessor { MaxHP += 6; } } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 16; } else { @@ -686,7 +680,7 @@ public class AssignAPProcessor { MaxHP += 16; } } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 16; } else { @@ -697,14 +691,14 @@ public class AssignAPProcessor { } } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { if(!usedAPReset) { - Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP); + Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.PIRATE) ? Brawler.IMPROVE_MAX_HP : ThunderBreaker.IMPROVE_MAX_HP); int sLvl = player.getSkillLevel(increaseHP); if(sLvl > 0) MaxHP += increaseHP.getEffect(sLvl).getY(); } - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if (usedAPReset) { MaxHP += 18; } else { @@ -716,7 +710,7 @@ public class AssignAPProcessor { } else if (usedAPReset) { MaxHP += 8; } else { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { MaxHP += Randomizer.rand(8, 12); } else { MaxHP += 10; @@ -731,7 +725,7 @@ public class AssignAPProcessor { int MaxMP = 0; if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10)); } else { @@ -749,7 +743,7 @@ public class AssignAPProcessor { MaxMP += increaseMP.getEffect(sLvl).getY(); } - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20)); } else { @@ -759,7 +753,7 @@ public class AssignAPProcessor { MaxMP += 18; } } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); } else { @@ -769,7 +763,7 @@ public class AssignAPProcessor { MaxMP += 10; } } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); } else { @@ -779,7 +773,7 @@ public class AssignAPProcessor { MaxMP += 10; } } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10)); } else { @@ -789,7 +783,7 @@ public class AssignAPProcessor { MaxMP += 14; } } else { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) { if(!usedAPReset) { MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10)); } else { diff --git a/src/client/processor/AssignSPProcessor.java b/src/client/processor/stat/AssignSPProcessor.java similarity index 77% rename from src/client/processor/AssignSPProcessor.java rename to src/client/processor/stat/AssignSPProcessor.java index b209cad6a5..fc1cf37870 100644 --- a/src/client/processor/AssignSPProcessor.java +++ b/src/client/processor/stat/AssignSPProcessor.java @@ -21,14 +21,14 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package client.processor; +package client.processor.stat; import client.MapleCharacter; import client.MapleClient; import client.Skill; import client.SkillFactory; import client.autoban.AutobanFactory; -import constants.GameConstants; +import constants.game.GameConstants; import constants.skills.Aran; import server.ThreadManager; import tools.FilePrinter; @@ -40,31 +40,35 @@ import tools.MaplePacketCreator; */ public class AssignSPProcessor { + public static boolean canSPAssign(MapleClient c, int skillid) { + if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) { + c.announce(MaplePacketCreator.enableActions()); + return false; + } + + MapleCharacter player = c.getPlayer(); + if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) { + AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp."); + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job."); + + c.disconnect(true, false); + return false; + } + + return true; + } + public static void SPAssignAction(MapleClient c, int skillid) { c.lockClient(); try { - if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) { - c.announce(MaplePacketCreator.enableActions()); + if (!canSPAssign(c, skillid)) { return; } - + MapleCharacter player = c.getPlayer(); int remainingSp = player.getRemainingSps()[GameConstants.getSkillBook(skillid/10000)]; boolean isBeginnerSkill = false; - if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) { - AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp."); - FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job."); - - final MapleClient client = c; - ThreadManager.getInstance().newTask(new Runnable() { - @Override - public void run() { - client.disconnect(true, false); - } - }); - - return; - } + if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) { int total = 0; for (int i = 0; i < 3; i++) { diff --git a/src/config/ServerConfig.java b/src/config/ServerConfig.java new file mode 100644 index 0000000000..ea40b0b2ad --- /dev/null +++ b/src/config/ServerConfig.java @@ -0,0 +1,304 @@ +package config; + +public class ServerConfig { + //Thread Tracker Configuration + public boolean USE_THREAD_TRACKER; + + //Database Configuration + public String DB_URL; + public String DB_USER; + public String DB_PASS; + public boolean DB_CONNECTION_POOL; + + //Login Configuration + public int WORLDS; + public int WLDLIST_SIZE; + public int CHANNEL_SIZE; + public int CHANNEL_LOAD; + public int CHANNEL_LOCKS; + + public long RESPAWN_INTERVAL; + public long PURGING_INTERVAL; + public long RANKING_INTERVAL; + public long COUPON_INTERVAL; + public long UPDATE_INTERVAL; + + public boolean ENABLE_PIC; + public boolean ENABLE_PIN; + + public int BYPASS_PIC_EXPIRATION; + public int BYPASS_PIN_EXPIRATION; + + public boolean AUTOMATIC_REGISTER; + public boolean BCRYPT_MIGRATION; + public boolean COLLECTIVE_CHARSLOT; + public boolean DETERRED_MULTICLIENT; + + //Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses. + + //Multiclient Coordinator Configuration + public int MAX_ALLOWED_ACCOUNT_HWID; + public int MAX_ACCOUNT_LOGIN_ATTEMPT; + public int LOGIN_ATTEMPT_DURATION; + + //Ip Configuration + public String HOST; + public boolean LOCALSERVER; + public boolean GMSERVER; + + //Other configuration + public boolean SHUTDOWNHOOK; + + //Server Flags + public boolean USE_CUSTOM_KEYSET; + public boolean USE_DEBUG; + public boolean USE_DEBUG_SHOW_INFO_EQPEXP; + public boolean USE_DEBUG_SHOW_RCVD_PACKET; + public boolean USE_DEBUG_SHOW_RCVD_MVLIFE; + public boolean USE_DEBUG_SHOW_PACKET; + public boolean USE_SUPPLY_RATE_COUPONS; + public boolean USE_IP_VALIDATION; + public boolean USE_CHARACTER_ACCOUNT_CHECK; + + public boolean USE_MAXRANGE; + public boolean USE_MAXRANGE_ECHO_OF_HERO; + public boolean USE_MTS; + public boolean USE_CPQ; + public boolean USE_AUTOHIDE_GM; + public boolean USE_BUYBACK_SYSTEM; + public boolean USE_FIXED_RATIO_HPMP_UPDATE; + public boolean USE_FAMILY_SYSTEM; + public boolean USE_DUEY; + public boolean USE_RANDOMIZE_HPMP_GAIN; + public boolean USE_STORAGE_ITEM_SORT; + public boolean USE_ITEM_SORT; + public boolean USE_ITEM_SORT_BY_NAME; + public boolean USE_PARTY_FOR_STARTERS; + public boolean USE_AUTOASSIGN_STARTERS_AP; + public boolean USE_AUTOASSIGN_SECONDARY_CAP; + public boolean USE_STARTING_AP_4; + public boolean USE_AUTOBAN; + public boolean USE_AUTOBAN_LOG; + public boolean USE_AUTOSAVE; + public boolean USE_SERVER_AUTOASSIGNER; + public boolean USE_REFRESH_RANK_MOVE; + public boolean USE_ENFORCE_ADMIN_ACCOUNT; + public boolean USE_ENFORCE_NOVICE_EXPRATE; + public boolean USE_ENFORCE_HPMP_SWAP; + public boolean USE_ENFORCE_MOB_LEVEL_RANGE; + public boolean USE_ENFORCE_JOB_LEVEL_RANGE; + public boolean USE_ENFORCE_JOB_SP_RANGE; + public boolean USE_ENFORCE_ITEM_SUGGESTION; + public boolean USE_ENFORCE_UNMERCHABLE_CASH; + public boolean USE_ENFORCE_UNMERCHABLE_PET; + public boolean USE_ENFORCE_MERCHANT_SAVE; + public boolean USE_ENFORCE_MDOOR_POSITION; + public boolean USE_SPAWN_CLEAN_MDOOR; + public boolean USE_SPAWN_LOOT_ON_ANIMATION; + public boolean USE_SPAWN_RELEVANT_LOOT; + public boolean USE_ERASE_PERMIT_ON_OPENSHOP; + public boolean USE_ERASE_UNTRADEABLE_DROP; + public boolean USE_ERASE_PET_ON_EXPIRATION; + public boolean USE_BUFF_MOST_SIGNIFICANT; + public boolean USE_BUFF_EVERLASTING; + public boolean USE_MULTIPLE_SAME_EQUIP_DROP; + public boolean USE_BANISHABLE_TOWN_SCROLL; + public boolean USE_ENABLE_FULL_RESPAWN; + public boolean USE_ENABLE_CHAT_LOG; + public boolean USE_REBIRTH_SYSTEM; + public boolean USE_MAP_OWNERSHIP_SYSTEM; + public boolean USE_FISHING_SYSTEM; + public boolean USE_NPCS_SCRIPTABLE; + + //Events/PQs Configuration + public boolean USE_OLD_GMS_STYLED_PQ_NPCS; + public boolean USE_ENABLE_SOLO_EXPEDITIONS; + public boolean USE_ENABLE_DAILY_EXPEDITIONS; + public boolean USE_ENABLE_RECALL_EVENT; + + //Announcement Configuration + public boolean USE_ANNOUNCE_SHOPITEMSOLD; + public boolean USE_ANNOUNCE_CHANGEJOB; + + //Cash Shop Configuration + public boolean USE_JOINT_CASHSHOP_INVENTORY; + public boolean USE_CLEAR_OUTDATED_COUPONS; + public boolean ALLOW_CASHSHOP_NAME_CHANGE; + public boolean ALLOW_CASHSHOP_WORLD_TRANSFER;//Allows players to buy world transfers in the cash shop. + + //Maker Configuration + public boolean USE_MAKER_PERMISSIVE_ATKUP; + public boolean USE_MAKER_FEE_HEURISTICS; + + //Custom Configuration + public boolean USE_ENABLE_CUSTOM_NPC_SCRIPT; + public boolean USE_STARTER_MERGE; + + //Commands Configuration + public boolean BLOCK_GENERATE_CASH_ITEM; + public boolean USE_WHOLE_SERVER_RANKING; + + public double EQUIP_EXP_RATE; + public double PQ_BONUS_EXP_RATE; + + public byte EXP_SPLIT_LEVEL_INTERVAL; + public byte EXP_SPLIT_LEECH_INTERVAL; + public float EXP_SPLIT_MVP_MOD; + public float EXP_SPLIT_COMMON_MOD; + public float PARTY_BONUS_EXP_RATE; + + //Miscellaneous Configuration + public String TIMEZONE; + public boolean USE_DISPLAY_NUMBERS_WITH_COMMA; + public boolean USE_UNITPRICE_WITH_COMMA; + public byte MAX_MONITORED_BUFFSTATS; + public int MAX_AP; + public int MAX_EVENT_LEVELS; + public long BLOCK_NPC_RACE_CONDT; + public int TOT_MOB_QUEST_REQUIREMENT; + public int MOB_REACTOR_REFRESH_TIME; + public int PARTY_SEARCH_REENTRY_LIMIT; + public long NAME_CHANGE_COOLDOWN; + public long WORLD_TRANSFER_COOLDOWN=NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days). + public boolean INSTANT_NAME_CHANGE; + + //Dangling Items/Locks Configuration + public int ITEM_EXPIRE_TIME ; + public int KITE_EXPIRE_TIME ; + public int ITEM_MONITOR_TIME; + public int LOCK_MONITOR_TIME; + + //Map Monitor Configuration + public int ITEM_EXPIRE_CHECK; + public int ITEM_LIMIT_ON_MAP; + public int MAP_VISITED_SIZE; + public int MAP_DAMAGE_OVERTIME_INTERVAL; + + //Channel Mob Disease Monitor Configuration + public int MOB_STATUS_MONITOR_PROC; + public int MOB_STATUS_MONITOR_LIFE; + public int MOB_STATUS_AGGRO_PERSISTENCE; + public int MOB_STATUS_AGGRO_INTERVAL; + + //Some Gameplay Enhancing Configurations + //Scroll Configuration + public boolean USE_PERFECT_GM_SCROLL; + public boolean USE_PERFECT_SCROLLING; + public boolean USE_ENHANCED_CHSCROLL; + public boolean USE_ENHANCED_CRAFTING; + public boolean USE_ENHANCED_CLNSLATE; + public int SCROLL_CHANCE_ROLLS; + public int CHSCROLL_STAT_RATE; + public int CHSCROLL_STAT_RANGE; + + //Beginner Skills Configuration + public boolean USE_ULTRA_NIMBLE_FEET; + public boolean USE_ULTRA_RECOVERY; + public boolean USE_ULTRA_THREE_SNAILS; + + //Other Skills Configuration + public boolean USE_FULL_ARAN_SKILLSET; + public boolean USE_FAST_REUSE_HERO_WILL; + public boolean USE_ANTI_IMMUNITY_CRASH; + public boolean USE_UNDISPEL_HOLY_SHIELD; + public boolean USE_FULL_HOLY_SYMBOL; + + //Character Configuration + public boolean USE_ADD_SLOTS_BY_LEVEL; + public boolean USE_ADD_RATES_BY_LEVEL; + public boolean USE_STACK_COUPON_RATES; + public boolean USE_PERFECT_PITCH; + + //Quest Configuration + public boolean USE_QUEST_RATE; + + //Quest Points Configuration + public int QUEST_POINT_REPEATABLE_INTERVAL; + public int QUEST_POINT_REQUIREMENT; + public int QUEST_POINT_PER_QUEST_COMPLETE; + public int QUEST_POINT_PER_EVENT_CLEAR; + + //Guild Configuration + public int CREATE_GUILD_MIN_PARTNERS; + public int CREATE_GUILD_COST; + public int CHANGE_EMBLEM_COST; + public int EXPAND_GUILD_BASE_COST; + public int EXPAND_GUILD_TIER_COST; + public int EXPAND_GUILD_MAX_COST; + + //Family Configuration + public int FAMILY_REP_PER_KILL; + public int FAMILY_REP_PER_BOSS_KILL; + public int FAMILY_REP_PER_LEVELUP; + public int FAMILY_MAX_GENERATIONS; + + //Equipment Configuration + public boolean USE_EQUIPMNT_LVLUP_SLOTS; + public boolean USE_EQUIPMNT_LVLUP_POWER; + public boolean USE_EQUIPMNT_LVLUP_CASH; + public boolean USE_SPIKES_AVOID_BANISH; + public int MAX_EQUIPMNT_LVLUP_STAT_UP; + public int MAX_EQUIPMNT_STAT; + public int USE_EQUIPMNT_LVLUP; + + //Map-Chair Configuration + public boolean USE_CHAIR_EXTRAHEAL; + public byte CHAIR_EXTRA_HEAL_MULTIPLIER; + public int CHAIR_EXTRA_HEAL_MAX_DELAY; + + //Player NPC Configuration + public int PLAYERNPC_INITIAL_X; + public int PLAYERNPC_INITIAL_Y; + public int PLAYERNPC_AREA_X; + public int PLAYERNPC_AREA_Y; + public int PLAYERNPC_AREA_STEPS; + public boolean PLAYERNPC_ORGANIZE_AREA; + public boolean PLAYERNPC_AUTODEPLOY; + + //Pet Auto-Pot Configuration + public boolean USE_COMPULSORY_AUTOPOT; + public boolean USE_EQUIPS_ON_AUTOPOT; + public double PET_AUTOHP_RATIO; + public double PET_AUTOMP_RATIO; + + //Pet & Mount Configuration + public byte PET_EXHAUST_COUNT; + public byte MOUNT_EXHAUST_COUNT; + + //Pet Hunger Configuration + public boolean PETS_NEVER_HUNGRY; + public boolean GM_PETS_NEVER_HUNGRY; + + //Event Configuration + public int EVENT_MAX_GUILD_QUEUE; + public long EVENT_LOBBY_DELAY; + + //Dojo Configuration + public boolean USE_FAST_DOJO_UPGRADE; + public boolean USE_DEADLY_DOJO; + public int DOJO_ENERGY_ATK; + public int DOJO_ENERGY_DMG; + + //Wedding Configuration + public int WEDDING_RESERVATION_DELAY; + public int WEDDING_RESERVATION_TIMEOUT; + public int WEDDING_RESERVATION_INTERVAL; + public int WEDDING_BLESS_EXP; + public int WEDDING_GIFT_LIMIT; + public boolean WEDDING_BLESSER_SHOWFX; + + //Buyback Configuration + public boolean USE_BUYBACK_WITH_MESOS; + public float BUYBACK_FEE; + public float BUYBACK_LEVEL_STACK_FEE; + public int BUYBACK_MESO_MULTIPLIER; + public int BUYBACK_RETURN_MINUTES; + public int BUYBACK_COOLDOWN_MINUTES; + + // Login timeout by shavit + public long TIMEOUT_DURATION; + + //Event End Timestamp + public long EVENT_END_TIMESTAMP; + +} diff --git a/src/config/WorldConfig.java b/src/config/WorldConfig.java new file mode 100644 index 0000000000..075282bf50 --- /dev/null +++ b/src/config/WorldConfig.java @@ -0,0 +1,16 @@ +package config; + +public class WorldConfig { + public int flag = 0; + public String server_message = "Welcome!"; + public String event_message = ""; + public String why_am_i_recommended = ""; + public int channels = 1; + public int exp_rate = 1; + public int meso_rate = 1; + public int drop_rate = 1; + public int boss_drop_rate = 1; + public int quest_rate = 1; + public int travel_rate = 1; + public int fishing_rate = 1; +} diff --git a/src/config/YamlConfig.java b/src/config/YamlConfig.java new file mode 100644 index 0000000000..c8adc338d1 --- /dev/null +++ b/src/config/YamlConfig.java @@ -0,0 +1,32 @@ +package config; + +import com.esotericsoftware.yamlbeans.YamlReader; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; + + +public class YamlConfig { + + public static final YamlConfig config = fromFile("config.yaml"); + + public List worlds; + public ServerConfig server; + + public static YamlConfig fromFile(String filename) { + try { + YamlReader reader = new YamlReader(new FileReader(filename)); + YamlConfig config = reader.read(YamlConfig.class); + reader.close(); + return config; + } catch (FileNotFoundException e) { + String message = "Could not read config file " + filename + ": " + e.getMessage(); + throw new RuntimeException(message); + } catch (IOException e) { + String message = "Could not successfully parse config file " + filename + ": " + e.getMessage(); + throw new RuntimeException(message); + } + } +} diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java deleted file mode 100644 index 8147fa1ffe..0000000000 --- a/src/constants/ServerConstants.java +++ /dev/null @@ -1,331 +0,0 @@ -package constants; - -import java.io.FileInputStream; -import java.util.Properties; - -public class ServerConstants { - //Thread Tracker Configuration - public static final boolean USE_THREAD_TRACKER = true; //[SEVERE] This deadlock auditing thing will bloat the memory as fast as the time frame one takes to lose track of a raindrop on a tempesting day. Only for debugging purposes. - - //Database Configuration - public static String DB_URL = ""; - public static String DB_USER = ""; - public static String DB_PASS = ""; - public static final boolean DB_CONNECTION_POOL = true; //Installs a connection pool to hub DB connections. Set false to default. - - //Server Version - public static short VERSION = 83; - - //Login Configuration - public static final int WLDLIST_SIZE = 21; //Max possible worlds on the server. - public static final int CHANNEL_SIZE = 20; //Max possible channels per world (which is 20, based on the channel list on login phase). - public static final int CHANNEL_LOAD = 100; //Max players per channel (limit actually used to calculate the World server capacity). - public static final int CHANNEL_LOCKS = 20; //Total number of structure management locks each channel has. - - public static final long RESPAWN_INTERVAL = 10 * 1000; //10 seconds, 10000. - public static final long PURGING_INTERVAL = 5 * 60 * 1000; - public static final long RANKING_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000. - public static final long COUPON_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000. - public static final long UPDATE_INTERVAL = 777; //Dictates the frequency on which the "centralized server time" is updated. - - public static final boolean ENABLE_PIC = false; //Pick true/false to enable or disable Pic. Delete character requires PIC available. - public static final boolean ENABLE_PIN = false; //Pick true/false to enable or disable Pin. - - public static final int BYPASS_PIC_EXPIRATION = 20; //Enables PIC bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable. - public static final int BYPASS_PIN_EXPIRATION = 15; //Enables PIN bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable. - - public static final boolean AUTOMATIC_REGISTER = true; //Automatically register players when they login with a nonexistent username. - public static final boolean BCRYPT_MIGRATION = true; //Performs a migration from old SHA-1 and SHA-512 password to bcrypt. - public static final boolean COLLECTIVE_CHARSLOT = false; //Available character slots are contabilized globally rather than per world server. - public static final boolean DETERRED_MULTICLIENT = false; //Enables detection of multi-client and suspicious remote IP on the login system. - - //Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses. - - //Multiclient Coordinator Configuration - public static final int MAX_ALLOWED_ACCOUNT_HWID = 4; //Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login. - public static final int MAX_ACCOUNT_LOGIN_ATTEMPT = 15; //After N tries on an account, login on that account gets disabled for a short period. - public static final int LOGIN_ATTEMPT_DURATION = 120; //Period in seconds the login attempt remains registered on the system. - - //Ip Configuration - public static String HOST; - public static boolean LOCALSERVER; - - //Other Configuration - public static boolean JAVA_8; - public static boolean SHUTDOWNHOOK; - // JAVA_8: every static function in AbstractPlayerInteraction are to be made non-static, and code comment sections uncommented after enabling this functionality. - - - //Server Flags - public static final boolean USE_CUSTOM_KEYSET = true; //Enables auto-setup of the HeavenMS's custom keybindings when creating characters. - 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_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info. - public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids. - public static boolean USE_DEBUG_SHOW_RCVD_MVLIFE = false; //Prints on the cmd all received move life content. - public static final boolean USE_DEBUG_SHOW_PACKET = false; - public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop. - public static final boolean USE_IP_VALIDATION = true; //Enables IP checking when logging in. - - 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_MAXRANGE_ECHO_OF_HERO = true; - public static final boolean USE_MTS = false; - public static final boolean USE_CPQ = true; //Renders the CPQ available or not. - public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152). - public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, dead players can use it by clicking the MTS button. - public static final boolean USE_FIXED_RATIO_HPMP_UPDATE = true; //Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio. - public static final boolean USE_FAMILY_SYSTEM = false; - public static final boolean USE_DUEY = true; - public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain on level up. - public static final boolean USE_STORAGE_ITEM_SORT = true; //Enables storage "Arrange Items" feature. - public static final boolean USE_ITEM_SORT = true; //Enables inventory "Item Sort/Merge" feature. - public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id. - public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range. - public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below. - public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached. - public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically. - public static final boolean USE_AUTOBAN_LOG = true; //Log autoban related messages. Still logs even with USE_AUTOBAN disabled. - 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; //HeavenMS-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_ADMIN_ACCOUNT = false; //Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE. - public static final boolean USE_ENFORCE_NOVICE_EXPRATE = false; //Hardsets experience rate 1x for beginners level 10 or under. Ideal for roaming on novice areas without caring too much about losing some stats. - public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats. - public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it. - public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs. - public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. - public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured from players. - public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = false;//Forces players to not sell CASH items via merchants. - public static final boolean USE_ENFORCE_UNMERCHABLE_PET = false; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) - public static final boolean USE_ENFORCE_MERCHANT_SAVE = true; //Forces automatic DB save on merchant owners, at every item movement on shop. - public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints. - public static final boolean USE_SPAWN_CLEAN_MDOOR = false; //Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment. - public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly). - public static final boolean USE_SPAWN_RELEVANT_LOOT = true; //Forces to only spawn loots that are collectable by the player or any of their party members. - public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop. - public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped. - public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll. - 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. - public static final boolean USE_BUFF_EVERLASTING = false; //Every applied buff on players holds expiration time so high it'd be considered permanent. Thanks Vcoc for this suggestion. - public static final boolean USE_MULTIPLE_SAME_EQUIP_DROP = true;//Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data. - public static final boolean USE_BANISHABLE_TOWN_SCROLL = true; //Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available. - public static final boolean USE_ENABLE_FULL_RESPAWN = true; //At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there. - public static final boolean USE_ENABLE_CHAT_LOG = false; //Write in-game chat to log - public static final boolean USE_REBIRTH_SYSTEM = false; //Flag to enable/disable rebirth system - public static final boolean USE_MAP_OWNERSHIP_SYSTEM = true; //Flag to enable/disable map ownership system - public static final boolean USE_FISHING_SYSTEM = true; //Flag to enable/disable fishing system - public static final boolean USE_NPCS_SCRIPTABLE = true; //Flag to enable/disable serverside predefined script NPCs. - - //Events/PQs Configuration - public static final boolean USE_OLD_GMS_STYLED_PQ_NPCS = true; //Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in. - public static final boolean USE_ENABLE_SOLO_EXPEDITIONS = true; //Enables start expeditions with any number of players. This will also bypass all the Zakum prequest. - public static final boolean USE_ENABLE_RECALL_EVENT = true; //Enables a disconnected player to reaccess the last event instance they were in before logging out. Recall only works if the event isn't cleared or disposed yet. Suggestion thanks to Alisson (Goukken). - - //Announcement Configuration - public static final boolean USE_ANNOUNCE_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold. - public static final boolean USE_ANNOUNCE_CHANGEJOB = false; //Automatic message sent to acquantainces when changing jobs. - - //Cash Shop Configuration - public static final boolean USE_JOINT_CASHSHOP_INVENTORY = true;//Enables usage of a same cash shop inventory for explorers, cygnus and legends. Items from exclusive cash shop inventories won't show up on the shared inventory, though. - public static final boolean USE_CLEAR_OUTDATED_COUPONS = true; //Enables deletion of older code coupon registry from the DB, freeing so-long irrelevant data. - - //Maker Configuration - public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items. - public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than the client-side requests). - - //Custom Configuration - public static final boolean USE_ENABLE_CUSTOM_NPC_SCRIPT = true;//Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay), quests or NPC shops. - public static final boolean USE_STARTER_MERGE = false; //Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites). - - //Commands Configuration - public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command. - public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of split by worlds. - - //Server Rates And Experience - public static final int EXP_RATE = 10; //NOTE: World-specific rates within "world.ini" OVERRIDES the default rates from here. - public static final int MESO_RATE = 10; - public static final int DROP_RATE = 10; - public static final int BOSS_DROP_RATE = 10; //NOTE: Boss drop rate OVERRIDES common drop rate, for bosses-only. - public static final int QUEST_RATE = 5; //Multiplier for Exp & Meso gains when completing a quest. Only available when USE_QUEST_RATE is true. Stacks with server Exp & Meso rates. - public static final int FISHING_RATE = 10; //Multiplier for success likelihood on meso thrown during fishing. - public static final int TRAVEL_RATE = 10; //Means of transportation rides/departs using 1/N of the default time. - - public static final double EQUIP_EXP_RATE = 1.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 double PQ_BONUS_EXP_RATE = 0.5; //Rate for the PQ exp reward. - - public static final byte EXP_SPLIT_LEVEL_INTERVAL = 5; //Non-contributing players must be within N level between the mob to receive EXP. - public static final byte EXP_SPLIT_LEECH_INTERVAL = 5; //Non-contributing players must be within N level between any contributing party member to receive EXP. - public static final float EXP_SPLIT_MVP_MOD = 0.2f; - public static final float EXP_SPLIT_COMMON_MOD = 0.8f; - public static final float PARTY_BONUS_EXP_RATE = 1.0f; //Rate for the party exp bonus reward. - - //Miscellaneous Configuration - public static String TIMEZONE = "GMT-3"; - public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values). - public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions. - public static final byte 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. - public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up. - public static final int TOT_MOB_QUEST_REQUIREMENT = 77; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default. - public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default. - public static final int PARTY_SEARCH_REENTRY_LIMIT = 10; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members). - - //Dangling Items/Locks Configuration - public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes. - public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears. - public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history. - public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present. - - //Map Monitor Configuration - public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items. - public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map. - public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids a player visits. This is used to recover and update drops on these maps accordingly with player actions. - public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in milliseconds between map environment damage (e.g. El Nath and Aqua Road surrondings). - - //Channel Mob Disease Monitor Configuration - public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule. - public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity. - public static final int MOB_STATUS_AGGRO_PERSISTENCE = 2; //Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer. - public static final int MOB_STATUS_AGGRO_INTERVAL = 5000; //Interval in milliseconds between aggro logistics update. - - //Some Gameplay Enhancing Configurations - //Scroll Configuration - public static final boolean USE_PERFECT_GM_SCROLL = true; //Scrolls from GMs never uses up slots nor fails. - public static final boolean USE_PERFECT_SCROLLING = true; //Scrolls doesn't use slots upon failure. - public static final boolean USE_ENHANCED_CHSCROLL = true; //Equips even more powerful with chaos upgrade. - public static final boolean USE_ENHANCED_CRAFTING = true; //Apply chaos scroll on every equip crafted. - public static final boolean USE_ENHANCED_CLNSLATE = true; //Clean slates can be applied to recover successfully used slots as well. - public static final int SCROLL_CHANCE_RATE = 10; //Number of rolls for success on a scroll, set 1 for default. - public static final int CHSCROLL_STAT_RATE = 3; //Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default. - public static final int CHSCROLL_STAT_RANGE = 6; //Stat upgrade range (-N, N) on chaos scrolls. - - //Beginner Skills Configuration - public static final boolean USE_ULTRA_NIMBLE_FEET = true; //Massive speed & jump upgrade. - public static final boolean USE_ULTRA_RECOVERY = true; //Massive recovery amounts overtime. - public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss. - - //Other Skills Configuration - public static final boolean USE_FULL_ARAN_SKILLSET = false; //Enables starter availability to all Aran job skills. Thanks Masterrulax for this suggestion. - public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will. - public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Thanks Celestial for this suggestion. - public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs. - - //Character Configuration - public static final boolean USE_ADD_SLOTS_BY_LEVEL = true; //Slots are added each 20 levels. - public static final boolean USE_ADD_RATES_BY_LEVEL = true; //Rates are added each 20 levels. - public static final boolean USE_STACK_COUPON_RATES = false; //Multiple coupons effects builds up together. - public static final boolean USE_PERFECT_PITCH = true; //For lvl 30 or above, each lvlup grants player 1 perfect pitch. - - //Quest Configuration - public static final boolean USE_QUEST_RATE = false; //Exp/Meso gain by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates. - - //Quest Points Configuration - public static final int QUEST_POINT_REPEATABLE_INTERVAL = 24;//Minimum interval between repeatable quest completions for quest points to be awarded. - public static final int QUEST_POINT_REQUIREMENT = 16; //Exchange factor between N quest points to +1 fame, set 0 to disable the entire quest point mechanism. - public static final int QUEST_POINT_PER_QUEST_COMPLETE = 4; //Each completed quest awards N quest points, set 0 to disable. - public static final int QUEST_POINT_PER_EVENT_CLEAR = 1; //Each completed event instance awards N quest points, set 0 to disable. - - //Guild Configuration - public static final int CREATE_GUILD_MIN_PARTNERS = 6; //Minimum number of members on Guild Headquarters to establish a new guild. - public static final int CREATE_GUILD_COST = 1500000; - public static final int CHANGE_EMBLEM_COST = 5000000; - public static final int EXPAND_GUILD_BASE_COST = 500000; - public static final int EXPAND_GUILD_TIER_COST = 1000000; - public static final int EXPAND_GUILD_MAX_COST = 5000000; - - //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 stat upgrades at equip level up. - public static final boolean USE_EQUIPMNT_LVLUP_CASH = true; //Enable equip leveling up on cash equipments as well. - public static final boolean USE_SPIKES_AVOID_BANISH = true; //Shoes equipped with spikes prevents mobs from banishing wearer. - public static final int MAX_EQUIPMNT_LVLUP_STAT_UP = 10000; //Max stat upgrade an equipment can have on a levelup. - public static final int MAX_EQUIPMNT_STAT = 32767; //Max stat on an equipment by leveling up. - public static final int USE_EQUIPMNT_LVLUP = 7; //All equips lvlup at max level of N, set 1 to disable. - - //Map-Chair Configuration - public static final boolean USE_CHAIR_EXTRAHEAL = true; //Enable map chairs to further recover player's HP and MP (player must have the Chair Mastery skill). - public static final byte CHAIR_EXTRA_HEAL_MULTIPLIER = 10; //Due to only being able to be send up-to-255 heal values, values being actually updated is the one displayed times this. - public static final int CHAIR_EXTRA_HEAL_MAX_DELAY = 21; //Players are expected to recover fully after using this skill for N seconds. - - //Player NPC Configuration - public static final int PLAYERNPC_INITIAL_X = 262; //Map frame width for putting PlayerNPCs. - public static final int PLAYERNPC_INITIAL_Y = 262; //Map frame height for putting PlayerNPCs. - public static final int PLAYERNPC_AREA_X = 320; //Initial width gap between PlayerNPCs. - public static final int PLAYERNPC_AREA_Y = 160; //Initial height gap between PlayerNPCs. - public static final int PLAYERNPC_AREA_STEPS = 4; //Max number of times gap is shortened to comport PlayerNPCs. - public static final boolean PLAYERNPC_ORGANIZE_AREA = true; //Automatically rearranges PlayerNPCs on the map if there is no space set the new NPC. Current distance gap between NPCs is decreased to solve this issue. - public static final boolean PLAYERNPC_AUTODEPLOY = true; //Makes PlayerNPC automatically deployed on the Hall of Fame at the instant one reaches max level. If false, eligible players must talk to 1st job instructor to deploy a NPC. - - //Pet Auto-Pot Configuration - public static final boolean USE_COMPULSORY_AUTOPOT = true; //Pets will consume as many potions as needed to fulfill the AUTOHP/MP ratio threshold. - public static final boolean USE_EQUIPS_ON_AUTOPOT = true; //Player MaxHP and MaxMP check values on autopot handler will change according to HP/MP bonuses on equipped items. - public static final double PET_AUTOHP_RATIO = 0.99; //Will automatically consume potions until given ratio of the MaxHP/MaxMP is reached. - public static final double PET_AUTOMP_RATIO = 0.99; - - //Pet & Mount Configuration - public static final byte PET_EXHAUST_COUNT = 3; //Number of proc counts (1 per minute) on the exhaust schedule for fullness. - public static final byte MOUNT_EXHAUST_COUNT = 1; //Number of proc counts (1 per minute) on the exhaust schedule for tiredness. - - //Pet Hunger Configuration - public static final boolean PETS_NEVER_HUNGRY = false; //If true, pets and mounts will never grow hungry. - public static final boolean GM_PETS_NEVER_HUNGRY = true; //If true, pets and mounts own by GMs will never grow hungry. - - //Event Configuration - public static final int EVENT_MAX_GUILD_QUEUE = 10; //Max number of guilds in queue for GPQ. - public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby. - - //Dojo Configuration - public static final boolean USE_FAST_DOJO_UPGRADE = true; //Reduced Dojo training points amount required for a belt upgrade. - public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo? - public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack - public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack - - //Wedding Configuration - public static final int WEDDING_RESERVATION_DELAY = 3; //Minimum idle slots before processing a wedding reservation. - public static final int WEDDING_RESERVATION_TIMEOUT = 10; //Limit time in minutes for the couple to show up before cancelling the wedding reservation. - public static final int WEDDING_RESERVATION_INTERVAL = 60; //Time between wedding starts in minutes. - public static final int WEDDING_BLESS_EXP = 30000; //Exp gained per bless count. - public static final int WEDDING_GIFT_LIMIT = 1; //Max number of gifts per person to same wishlist on marriage instances. - public static final boolean WEDDING_BLESSER_SHOWFX = true; //Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead. - - //Buyback Configuration - public static final boolean USE_BUYBACK_WITH_MESOS = true; //Enables usage of either mesos or NX for the buyback fee. - public static final float BUYBACK_FEE = 77.70f; //Sets the base amount needed to buyback (level 30 or under will use the base value). - public static final float BUYBACK_LEVEL_STACK_FEE = 85.47f; //Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak). - public static final int BUYBACK_MESO_MULTIPLIER = 1000; //Sets a multiplier for the fee when using meso as the charge unit. - public static final int BUYBACK_RETURN_MINUTES = 1; //Sets the maximum amount of time the player can wait before decide to buyback. - public static final int BUYBACK_COOLDOWN_MINUTES = 7; //Sets the time the player must wait before using buyback again. - - //Event End Timestamp - public static final long EVENT_END_TIMESTAMP = 1428897600000L; - - //Debug Variables - public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes - - //Properties - static { - Properties p = new Properties(); - try { - p.load(new FileInputStream("configuration.ini")); - - //Server Host - ServerConstants.HOST = p.getProperty("HOST"); - ServerConstants.LOCALSERVER = ServerConstants.HOST.startsWith("127.") || ServerConstants.HOST.startsWith("localhost"); - - //Sql Database - ServerConstants.DB_URL = p.getProperty("URL"); - ServerConstants.DB_USER = p.getProperty("DB_USER"); - ServerConstants.DB_PASS = p.getProperty("DB_PASS"); - - //java8 And Shutdownhook - ServerConstants.JAVA_8 = p.getProperty("JAVA8").equalsIgnoreCase("TRUE"); - ServerConstants.SHUTDOWNHOOK = p.getProperty("SHUTDOWNHOOK").equalsIgnoreCase("true"); - - } catch (Exception e) { - e.printStackTrace(); - System.out.println("Failed to load configuration.ini."); - System.exit(0); - } - } -} diff --git a/src/constants/ExpTable.java b/src/constants/game/ExpTable.java similarity index 99% rename from src/constants/ExpTable.java rename to src/constants/game/ExpTable.java index a7b4d2f22d..1b963bf68c 100644 --- a/src/constants/ExpTable.java +++ b/src/constants/game/ExpTable.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 constants; +package constants.game; public final class ExpTable { private static final int[] exp = {15, 15, 34, 57, 92, 135, 372, 560, 840, 1144, 1242, 1573, 2144, 2800, 3640, 4700, 5893, 7360, 9144, 11120, 13477, 16268, 19320, 22880, 27008, 31477, 36600, 42444, 48720, 55813, 63800, 86784, 98208, 110932, 124432, 139372, 155865, 173280, 192400, 213345, 235372, 259392, 285532, 312928, 342624, 374760, 408336, 445544, 483532, 524160, 567772, 598886, 631704, 666321, 702836, 741351, 781976, 824828, 870028, 917625, 967995, 1021041, 1076994, 1136013, 1198266, 1263930, 1333194, 1406252, 1483314, 1564600, 1650340, 1740778, 1836173, 1936794, 2042930, 2154882, 2272970, 2397528, 2528912, 2667496, 2813674, 2967863, 3130502, 3302053, 3483005, 3673873, 3875201, 4087562, 4311559, 4547832, 4797053, 5059931, 5337215, 5629694, 5938202, 6263614, 6606860, 6968915, 7350811, 7753635, 8178534, 8626718, 9099462, 9598112, 10124088, 10678888, 11264090, 11881362, 12532461, 13219239, 13943653, 14707765, 15513750, 16363902, 17260644, 18206527, 19204245, 20256637, 21366700, 22537594, 23772654, 25075395, 26449526, 27898960, 29427822, 31040466, 32741483, 34535716, 36428273, 38424542, 40530206, 42751262, 45094030, 47565183, 50171755, 52921167, 55821246, 58880250, 62106888, 65510344, 69100311, 72887008, 76881216, 81094306, 85594273, 90225770, 95170142, 100385466, 105886589, 111689174, 117809740, 124265714, 131075474, 138258410, 145834970, 153826726, 162256430, 171148082, 180526997, 190419876, 200854885, 211861732, 223471711, 223471711, 248635353, 262260570, 276632449, 291791906, 307782102, 324648562, 342439302, 361204976, 380999008, 401877754, 423900654, 447130410, 471633156, 497478653, 524740482, 553496261, 583827855, 615821622, 649568646, 685165008, 722712050, 762316670, 804091623, 848155844, 894634784, 943660770, 995373379, 1049919840, 1107455447, 1168144006, 1232158297, 1299680571, 1370903066, 1446028554, 1525246918, 1608855764, 1697021059}; diff --git a/src/constants/GameConstants.java b/src/constants/game/GameConstants.java similarity index 93% rename from src/constants/GameConstants.java rename to src/constants/game/GameConstants.java index 5575e2169c..929a63a460 100644 --- a/src/constants/GameConstants.java +++ b/src/constants/game/GameConstants.java @@ -1,4 +1,4 @@ -package constants; +package constants.game; import client.MapleDisease; import java.util.ArrayList; @@ -6,10 +6,18 @@ import java.util.List; import java.util.HashMap; import java.util.Map; import client.MapleJob; +import config.YamlConfig; import constants.skills.Aran; +import java.io.File; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Locale; +import provider.MapleData; +import provider.MapleDataDirectoryEntry; +import provider.MapleDataFileEntry; +import provider.MapleDataProvider; +import provider.MapleDataProviderFactory; +import provider.MapleDataTool; import server.maps.MapleMap; import server.maps.FieldLimit; import server.quest.MapleQuest; @@ -33,11 +41,13 @@ public class GameConstants { private static final int[] jobUpgradeSpUp = {0, 1, 2, 3, 6}; private final static Map jobNames = new HashMap<>(); private final static NumberFormat nfFormatter = new DecimalFormat("#,###,###,###"); - private final static NumberFormat nfParser = NumberFormat.getInstance(ServerConstants.USE_UNITPRICE_WITH_COMMA ? Locale.FRANCE : Locale.UK); + private final static NumberFormat nfParser = NumberFormat.getInstance(YamlConfig.config.server.USE_UNITPRICE_WITH_COMMA ? Locale.FRANCE : Locale.UK); public static final MapleDisease[] CPQ_DISEASES = {MapleDisease.SLOW, MapleDisease.SEDUCE, MapleDisease.STUN, MapleDisease.POISON, MapleDisease.SEAL, MapleDisease.DARKNESS, MapleDisease.WEAKEN, MapleDisease.CURSE}; + public static final int MAX_FIELD_MOB_DAMAGE = getMaxObstacleMobDamageFromWz() * 2; + public static int getPlayerBonusDropRate(int slot) { return(DROP_RATE_GAIN[slot]); } @@ -480,18 +490,6 @@ public class GameConstants { } } - public static int getHiddenSkill(final int skill) { - switch (skill) { - case Aran.HIDDEN_FULL_DOUBLE: - case Aran.HIDDEN_FULL_TRIPLE: - return Aran.FULL_SWING; - case Aran.HIDDEN_OVER_DOUBLE: - case Aran.HIDDEN_OVER_TRIPLE: - return Aran.OVER_SWING; - } - return skill; - } - public static int getSkillBook(final int job) { if (job >= 2210 && job <= 2218) { return job - 2209; @@ -563,7 +561,7 @@ public class GameConstants { } public static boolean isFreeMarketRoom(int mapid) { - return mapid > 910000000 && mapid < 910000023; + return mapid / 1000000 == 910 && mapid > 910000000; // FM rooms subset, thanks to shavit } public static boolean isMerchantLocked(MapleMap map) { @@ -657,7 +655,7 @@ public class GameConstants { } public synchronized static String numberWithCommas(int i) { - if(!ServerConstants.USE_DISPLAY_NUMBERS_WITH_COMMA) { + if(!YamlConfig.config.server.USE_DISPLAY_NUMBERS_WITH_COMMA) { return nfFormatter.format(i); // will display number on whatever locale is currently assigned on NumberFormat } else { return NumberFormat.getNumberInstance(Locale.UK).format(i); @@ -672,4 +670,32 @@ public class GameConstants { return 0.0f; } } + + private static int getMaxObstacleMobDamageFromWz() { + MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz")); + int maxMobDmg = 0; + + MapleDataDirectoryEntry root = mapSource.getRoot(); + for (MapleDataDirectoryEntry objData : root.getSubdirectories()) { + if (!objData.getName().contentEquals("Obj")) { + continue; + } + + for (MapleDataFileEntry obj : objData.getFiles()) { + for (MapleData l0 : mapSource.getData(objData.getName() + "/" + obj.getName()).getChildren()) { + for (MapleData l1 : l0.getChildren()) { + for (MapleData l2 : l1.getChildren()) { + int objDmg = MapleDataTool.getIntConvert("s1/mobdamage", l2, 0); + if (maxMobDmg < objDmg) { + maxMobDmg = objDmg; + } + } + } + } + } + } + + return maxMobDmg; + } + } diff --git a/src/constants/ScriptableNPCConstants.java b/src/constants/game/ScriptableNPCConstants.java similarity index 78% rename from src/constants/ScriptableNPCConstants.java rename to src/constants/game/ScriptableNPCConstants.java index cfddd8563b..df8a255cf9 100644 --- a/src/constants/ScriptableNPCConstants.java +++ b/src/constants/game/ScriptableNPCConstants.java @@ -1,4 +1,4 @@ -package constants; +package constants.game; /** * @brief ScriptableNPCConstants @@ -16,7 +16,8 @@ import tools.Pair; public class ScriptableNPCConstants { public static final Set> SCRIPTABLE_NPCS = new HashSet>(){{ - add(new Pair<>(9200000, "Cody")); + //add(new Pair<>(9200000, "Cody")); + add(new Pair<>(9001105, "Grandpa Moon Bunny")); }}; } diff --git a/src/constants/EquipSlot.java b/src/constants/inventory/EquipSlot.java similarity index 98% rename from src/constants/EquipSlot.java rename to src/constants/inventory/EquipSlot.java index 178759d1d3..63c7f7b3a9 100644 --- a/src/constants/EquipSlot.java +++ b/src/constants/inventory/EquipSlot.java @@ -1,4 +1,4 @@ -package constants; +package constants.inventory; /** * diff --git a/src/constants/EquipType.java b/src/constants/inventory/EquipType.java similarity index 98% rename from src/constants/EquipType.java rename to src/constants/inventory/EquipType.java index f1c6b5045d..afcc627420 100644 --- a/src/constants/EquipType.java +++ b/src/constants/inventory/EquipType.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package constants; +package constants.inventory; import java.util.HashMap; import java.util.Map; diff --git a/src/constants/ItemConstants.java b/src/constants/inventory/ItemConstants.java similarity index 85% rename from src/constants/ItemConstants.java rename to src/constants/inventory/ItemConstants.java index fbbbfc900b..dae6df6b89 100644 --- a/src/constants/ItemConstants.java +++ b/src/constants/inventory/ItemConstants.java @@ -19,9 +19,12 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package constants; +package constants.inventory; +import constants.net.ServerConstants; import client.inventory.MapleInventoryType; +import config.YamlConfig; + import java.util.HashSet; import java.util.Set; import java.util.HashMap; @@ -35,16 +38,16 @@ import java.util.Map; public final class ItemConstants { protected static Map inventoryTypeCache = new HashMap<>(); - public final static int LOCK = 0x01; - public final static int SPIKES = 0x02; - public final static int KARMA_USE = 0x02; - public final static int COLD = 0x04; - public final static int UNTRADEABLE = 0x08; - public final static int KARMA_EQP = 0x10; - public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this - public final static int PET_COME = 0x80; - public final static int ACCOUNT_SHARING = 0x100; - public final static int MERGE_UNTRADEABLE = 0x200; + public final static short LOCK = 0x01; + public final static short SPIKES = 0x02; + public final static short KARMA_USE = 0x02; + public final static short COLD = 0x04; + public final static short UNTRADEABLE = 0x08; + public final static short KARMA_EQP = 0x10; + public final static short SANDBOX = 0x40; // let 0x40 until it's proven something uses this + public final static short PET_COME = 0x80; + public final static short ACCOUNT_SHARING = 0x100; + public final static short MERGE_UNTRADEABLE = 0x200; public final static boolean EXPIRING_ITEMS = true; public final static Set permanentItemids = new HashSet<>(); @@ -107,7 +110,7 @@ public final class ItemConstants { } public static boolean isExpirablePet(int itemId) { - return ServerConstants.USE_ERASE_PET_ON_EXPIRATION || itemId == 5000054; + return YamlConfig.config.server.USE_ERASE_PET_ON_EXPIRATION || itemId == 5000054; } public static boolean isPermanentItem(int itemId) { @@ -147,7 +150,7 @@ public final class ItemConstants { return scrollId == 2040727 || scrollId == 2041058; } - public static boolean isFlagModifier(int scrollId, byte flag) { + public static boolean isFlagModifier(int scrollId, short flag) { if(scrollId == 2041058 && ((flag & ItemConstants.COLD) == ItemConstants.COLD)) return true; if(scrollId == 2040727 && ((flag & ItemConstants.SPIKES) == ItemConstants.SPIKES)) return true; return false; @@ -167,11 +170,11 @@ public final class ItemConstants { } public static boolean isPartyItem(int itemId) { - return itemId >= 2022430 && itemId <= 2022433; + return itemId >= 2022430 && itemId <= 2022433 || itemId >= 2022160 && itemId <= 2022163; } public static boolean isPartyAllcure(int itemId) { - return itemId == 2022433; + return itemId == 2022433 || itemId == 2022163; } public static boolean isHiredMerchant(int itemId) { @@ -247,4 +250,12 @@ public final class ItemConstants { public static boolean isHair(int itemId) { return itemId >= 30000 && itemId < 35000; } + + public static boolean isFaceExpression(int itemId) { + return itemId / 10000 == 516; + } + + public static boolean isChair(int itemId) { + return itemId / 10000 == 301; + } } diff --git a/src/constants/OpcodeConstants.java b/src/constants/net/OpcodeConstants.java similarity index 98% rename from src/constants/OpcodeConstants.java rename to src/constants/net/OpcodeConstants.java index 114b9339ba..d37bff7331 100644 --- a/src/constants/OpcodeConstants.java +++ b/src/constants/net/OpcodeConstants.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package constants; +package constants.net; import java.util.Map; import java.util.HashMap; diff --git a/src/constants/net/ServerConstants.java b/src/constants/net/ServerConstants.java new file mode 100644 index 0000000000..869a135f5d --- /dev/null +++ b/src/constants/net/ServerConstants.java @@ -0,0 +1,35 @@ +package constants.net; + +public class ServerConstants { + + //Server Version + public static short VERSION = 83; + + //Java Configuration + public static final boolean JAVA_8 = getJavaVersion() >= 8; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members). + + //Debug Variables + public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes + + // https://github.com/openstreetmap/josm/blob/a3a6e8a6b657cf4c5b4c64ea14d6e87be6280d65/src/org/openstreetmap/josm/tools/Utils.java#L1566-L1585 + /** + * Returns the Java version as an int value. + * @return the Java version as an int value (8, 9, etc.) + * @since 12130 + */ + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2); + } + // Allow these formats: + // 1.8.0_72-ea + // 9-ea + // 9 + // 9.0.1 + int dotPos = version.indexOf('.'); + int dashPos = version.indexOf('-'); + return Integer.parseInt(version.substring(0, + dotPos > -1 ? dotPos : dashPos > -1 ? dashPos : 1)); + } +} diff --git a/src/constants/skills/Aran.java b/src/constants/skills/Aran.java index 75a857c2bb..7a78c8ff69 100644 --- a/src/constants/skills/Aran.java +++ b/src/constants/skills/Aran.java @@ -29,11 +29,13 @@ public class Aran { public static final int DOUBLE_SWING = 21000002; public static final int TRIPLE_SWING = 21100001; public static final int COMBO_ABILITY = 21000000; + public static final int COMBAT_STEP = 21001001; public static final int POLEARM_BOOSTER = 21001003; public static final int MAPLE_WARRIOR = 21121000; public static final int FREEZE_STANDING = 21121003; public static final int SNOW_CHARGE = 21111005; public static final int HEROS_WILL = 21121008; + public static final int HIGH_DEFENSE = 21120004; public static final int BODY_PRESSURE = 21101003; public static final int COMBO_DRAIN = 21100005; public static final int COMBO_SMASH = 21100004; diff --git a/src/constants/skills/FPWizard.java b/src/constants/skills/FPWizard.java index 2025c05924..b62b90208c 100644 --- a/src/constants/skills/FPWizard.java +++ b/src/constants/skills/FPWizard.java @@ -29,5 +29,6 @@ public class FPWizard { public static final int MP_EATER = 2100000; public static final int MEDITATION = 2101001; public static final int SLOW = 2101003; + public static final int FIRE_ARROW = 2101004; public static final int POISON_BREATH = 2101005; } diff --git a/src/constants/skills/Warrior.java b/src/constants/skills/Warrior.java index 050f8a4f15..7bb463fa70 100644 --- a/src/constants/skills/Warrior.java +++ b/src/constants/skills/Warrior.java @@ -11,4 +11,5 @@ package constants.skills; public class Warrior { public static final int IMPROVED_HPREC = 1000000; public static final int IMPROVED_MAXHP = 1000001; + public static final int IRON_BODY = 1000003; } diff --git a/src/constants/CharsetConstants.java b/src/constants/string/CharsetConstants.java similarity index 97% rename from src/constants/CharsetConstants.java rename to src/constants/string/CharsetConstants.java index a00bc90bd2..45c9c6ce4e 100644 --- a/src/constants/CharsetConstants.java +++ b/src/constants/string/CharsetConstants.java @@ -4,7 +4,7 @@ * and open the template in the editor. */ -package constants; +package constants.string; /* * Thanks to GabrielSin (EllinMS) - gabrielsin@playellin.net diff --git a/src/constants/LanguageConstants.java b/src/constants/string/LanguageConstants.java similarity index 99% rename from src/constants/LanguageConstants.java rename to src/constants/string/LanguageConstants.java index 152fd78509..2170961aad 100644 --- a/src/constants/LanguageConstants.java +++ b/src/constants/string/LanguageConstants.java @@ -1,4 +1,4 @@ -package constants; +package constants.string; import client.MapleCharacter; diff --git a/src/net/MapleServerHandler.java b/src/net/MapleServerHandler.java index 58a43e75ed..3922c847c8 100644 --- a/src/net/MapleServerHandler.java +++ b/src/net/MapleServerHandler.java @@ -28,19 +28,20 @@ import java.util.HashSet; import java.util.Calendar; import java.util.concurrent.atomic.AtomicLong; +import config.YamlConfig; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import client.MapleClient; -import constants.ServerConstants; +import constants.net.ServerConstants; import java.net.InetSocketAddress; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import tools.FilePrinter; import tools.MapleAESOFB; @@ -188,7 +189,7 @@ public class MapleServerHandler extends IoHandlerAdapter { short packetId = slea.readShort(); MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - if(ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(packetId)) System.out.println("Received packet id " + packetId); + if(YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(packetId)) System.out.println("Received packet id " + packetId); final MaplePacketHandler packetHandler = processor.getHandler(packetId); if (packetHandler != null && packetHandler.validateState(client)) { try { @@ -198,6 +199,7 @@ public class MapleServerHandler extends IoHandlerAdapter { FilePrinter.printError(FilePrinter.PACKET_HANDLER + packetHandler.getClass().getName() + ".txt", t, "Error for " + (client.getPlayer() == null ? "" : "player ; " + client.getPlayer() + " on map ; " + client.getPlayer().getMapId() + " - ") + "account ; " + client.getAccountName() + "\r\n" + slea.toString()); //client.announce(MaplePacketCreator.enableActions());//bugs sometimes } + client.updateLastPacket(); } } @@ -240,14 +242,14 @@ public class MapleServerHandler extends IoHandlerAdapter { long timeNow = Server.getInstance().getCurrentTime(); long timeThen = timeNow - 15000; + Set pingClients = new HashSet<>(); idleLock.lock(); try { for(Entry mc : idleSessions.entrySet()) { if(timeNow - mc.getValue() >= 15000) { - mc.getKey().testPing(timeThen); + pingClients.add(mc.getKey()); } } - idleSessions.clear(); if(!tempIdleSessions.isEmpty()) { @@ -265,6 +267,10 @@ public class MapleServerHandler extends IoHandlerAdapter { } finally { idleLock.unlock(); } + + for(MapleClient c : pingClients) { + c.testPing(timeThen); + } } private void idleManagerTask() { diff --git a/src/net/PacketProcessor.java b/src/net/PacketProcessor.java index 0e03f0d0e7..ad7dade738 100644 --- a/src/net/PacketProcessor.java +++ b/src/net/PacketProcessor.java @@ -163,6 +163,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.CANCEL_BUFF, new CancelBuffHandler()); registerHandler(RecvOpcode.CANCEL_ITEM_EFFECT, new CancelItemEffectHandler()); registerHandler(RecvOpcode.PLAYER_INTERACTION, new PlayerInteractionHandler()); + registerHandler(RecvOpcode.RPS_ACTION, new RPSActionHandler()); registerHandler(RecvOpcode.DISTRIBUTE_AP, new DistributeAPHandler()); registerHandler(RecvOpcode.DISTRIBUTE_SP, new DistributeSPHandler()); registerHandler(RecvOpcode.CHANGE_KEYMAP, new KeymapChangeHandler()); @@ -222,8 +223,14 @@ public final class PacketProcessor { registerHandler(RecvOpcode.MONSTER_BOOK_COVER, new MonsterBookCoverHandler()); registerHandler(RecvOpcode.AUTO_DISTRIBUTE_AP, new AutoAssignHandler()); registerHandler(RecvOpcode.MAKER_SKILL, new MakerSkillHandler()); + registerHandler(RecvOpcode.OPEN_FAMILY_PEDIGREE, new OpenFamilyPedigreeHandler()); + registerHandler(RecvOpcode.OPEN_FAMILY, new OpenFamilyHandler()); registerHandler(RecvOpcode.ADD_FAMILY, new FamilyAddHandler()); + registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_SENIOR, new FamilySeparateHandler()); + registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_JUNIOR, new FamilySeparateHandler()); registerHandler(RecvOpcode.USE_FAMILY, new FamilyUseHandler()); + registerHandler(RecvOpcode.CHANGE_FAMILY_MESSAGE, new FamilyPreceptsHandler()); + registerHandler(RecvOpcode.FAMILY_SUMMON_RESPONSE, new FamilySummonResponseHandler()); registerHandler(RecvOpcode.USE_HAMMER, new UseHammerHandler()); registerHandler(RecvOpcode.SCRIPTED_ITEM, new ScriptedItemHandler()); registerHandler(RecvOpcode.TOUCHING_REACTOR, new TouchReactorHandler()); @@ -244,6 +251,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler()); registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler()); registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler()); + registerHandler(RecvOpcode.FIELD_DAMAGE_MOB, new FieldDamageMobHandler()); registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler()); registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler()); registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler()); diff --git a/src/net/mina/MaplePacketDecoder.java b/src/net/mina/MaplePacketDecoder.java index 782406ba62..30695ab3f8 100644 --- a/src/net/mina/MaplePacketDecoder.java +++ b/src/net/mina/MaplePacketDecoder.java @@ -21,10 +21,10 @@ */ package net.mina; -import constants.ServerConstants; +import config.YamlConfig; import client.MapleClient; -import constants.OpcodeConstants; -import net.server.coordinator.MapleSessionCoordinator; +import constants.net.OpcodeConstants; +import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; @@ -33,7 +33,6 @@ import tools.HexTool; import tools.MapleAESOFB; import tools.data.input.ByteArrayByteStream; import tools.data.input.GenericLittleEndianAccessor; -import net.opcodes.RecvOpcode; import tools.FilePrinter; public class MaplePacketDecoder extends CumulativeProtocolDecoder { @@ -75,7 +74,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder { rcvdCrypto.crypt(decryptedPacket); MapleCustomEncryption.decryptData(decryptedPacket); out.write(decryptedPacket); - if (ServerConstants.USE_DEBUG_SHOW_PACKET){ // Atoot's idea: packet traffic log, applied using auto-identation thanks to lrenex + if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET){ // packet traffic log: Atoot's idea, applied using auto-identation thanks to lrenex int packetLen = decryptedPacket.length; int pHeader = readFirstShort(decryptedPacket); String pHeaderStr = Integer.toHexString(pHeader).toUpperCase(); diff --git a/src/net/mina/MaplePacketEncoder.java b/src/net/mina/MaplePacketEncoder.java index 9887244c1d..2bd7fda9b4 100644 --- a/src/net/mina/MaplePacketEncoder.java +++ b/src/net/mina/MaplePacketEncoder.java @@ -21,11 +21,10 @@ along with this program. If not, see . */ package net.mina; -import constants.ServerConstants; +import config.YamlConfig; import client.MapleClient; -import constants.OpcodeConstants; -import net.opcodes.SendOpcode; -import net.server.coordinator.MapleSessionCoordinator; +import constants.net.OpcodeConstants; +import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolEncoder; @@ -43,40 +42,41 @@ public class MaplePacketEncoder implements ProtocolEncoder { final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); try { - client.lockEncoder(); - try { - final MapleAESOFB send_crypto = client.getSendCrypto(); - final byte[] input = (byte[]) message; - if (ServerConstants.USE_DEBUG_SHOW_PACKET) { - int packetLen = input.length; - int pHeader = readFirstShort(input); - String pHeaderStr = Integer.toHexString(pHeader).toUpperCase(); - String op = lookupRecv(pHeader); - String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n"; - if (packetLen <= 50000) { - String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input); - System.out.println(RecvTo); - if (op == null) { - System.out.println("UnknownPacket:" + RecvTo); + if (client.tryacquireEncoder()) { + try { + final MapleAESOFB send_crypto = client.getSendCrypto(); + final byte[] input = (byte[]) message; + if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET) { + int packetLen = input.length; + int pHeader = readFirstShort(input); + String pHeaderStr = Integer.toHexString(pHeader).toUpperCase(); + String op = lookupRecv(pHeader); + String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n"; + if (packetLen <= 50000) { + String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input); + System.out.println(RecvTo); + if (op == null) { + System.out.println("UnknownPacket:" + RecvTo); + } + } else { + FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ..."); } - } else { - FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ..."); } + + final byte[] unencrypted = new byte[input.length]; + System.arraycopy(input, 0, unencrypted, 0, input.length); + final byte[] ret = new byte[unencrypted.length + 4]; + final byte[] header = send_crypto.getPacketHeader(unencrypted.length); + MapleCustomEncryption.encryptData(unencrypted); + + send_crypto.crypt(unencrypted); + System.arraycopy(header, 0, ret, 0, 4); + System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); + + out.write(IoBuffer.wrap(ret)); + } finally { + client.unlockEncoder(); } - - final byte[] unencrypted = new byte[input.length]; - System.arraycopy(input, 0, unencrypted, 0, input.length); - final byte[] ret = new byte[unencrypted.length + 4]; - final byte[] header = send_crypto.getPacketHeader(unencrypted.length); - MapleCustomEncryption.encryptData(unencrypted); - - send_crypto.crypt(unencrypted); - System.arraycopy(header, 0, ret, 0, 4); - System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); - - out.write(IoBuffer.wrap(ret)); - } finally { - client.unlockEncoder(); } // System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); // out.write(ByteBuffer.wrap(ret)); diff --git a/src/net/opcodes/RecvOpcode.java b/src/net/opcodes/RecvOpcode.java index 3d1a942f09..98fab2ed94 100644 --- a/src/net/opcodes/RecvOpcode.java +++ b/src/net/opcodes/RecvOpcode.java @@ -145,10 +145,15 @@ public enum RecvOpcode { WEDDING_TALK_MORE(0x8B), ALLIANCE_OPERATION(0x8F), DENY_ALLIANCE_REQUEST(0x90), + OPEN_FAMILY_PEDIGREE(0x91), OPEN_FAMILY(0x92), ADD_FAMILY(0x93), + SEPARATE_FAMILY_BY_SENIOR(0x94), + SEPARATE_FAMILY_BY_JUNIOR(0x95), ACCEPT_FAMILY(0x96), USE_FAMILY(0x97), + CHANGE_FAMILY_MESSAGE(0x98), + FAMILY_SUMMON_RESPONSE(0x99), BBS_OPERATION(0x9B), ENTER_MTS(0x9C), USE_SOLOMON_ITEM(0x9D), @@ -170,6 +175,7 @@ public enum RecvOpcode { MOVE_DRAGON(0xB5), MOVE_LIFE(0xBC), AUTO_AGGRO(0xBD), + FIELD_DAMAGE_MOB(0xBF), MOB_DAMAGE_MOB_FRIENDLY(0xC0), MONSTER_BOMB(0xC1), MOB_DAMAGE_MOB(0xC2), diff --git a/src/net/opcodes/SendOpcode.java b/src/net/opcodes/SendOpcode.java index 42e14c9b8f..bcbeb2eb19 100644 --- a/src/net/opcodes/SendOpcode.java +++ b/src/net/opcodes/SendOpcode.java @@ -124,7 +124,7 @@ public enum SendOpcode { FAMILY_JOIN_REQUEST_RESULT(0x62), FAMILY_JOIN_ACCEPTED(0x63), FAMILY_PRIVILEGE_LIST(0x64), - FAMILY_FAMOUS_POINT_INC_RESULT(0x65), + FAMILY_REP_GAIN(0x65), FAMILY_NOTIFY_LOGIN_OR_LOGOUT(0x66), //? is logged in. LOLWUT FAMILY_SET_PRIVILEGE(0x67), FAMILY_SUMMON_REQUEST(0x68), @@ -182,7 +182,7 @@ public enum SendOpcode { CONTI_STATE(0x95), SET_QUEST_CLEAR(0x96), SET_QUEST_TIME(0x97), - WARN_MESSAGE(0x98), + ARIANT_RESULT(0x98), // thanks lrenex SET_OBJECT_STATE(0x99), STOP_CLOCK(0x9A), ARIANT_ARENA_SHOW_RESULT(0x9B), @@ -239,6 +239,7 @@ public enum SendOpcode { MESO_BAG_MESSAGE(0xD2), UPDATE_QUEST_INFO(0xD3), PLAYER_HINT(0xD6), + MAKER_RESULT(0xD9), KOREAN_EVENT(0xDB), OPEN_UI(0xDC), LOCK_UI(0xDD), @@ -301,7 +302,7 @@ public enum SendOpcode { ARIANT_ARENA_USER_SCORE(0x129), SHEEP_RANCH_INFO(0x12B), SHEEP_RANCH_CLOTHES(0x12C), - ARIANT_SCORE(0x12D), + WITCH_TOWER_SCORE_UPDATE(0x12D), // thanks lrenex HORNTAIL_CAVE(0x12E), ZAKUM_SHRINE(0x12F), NPC_TALK(0x130), diff --git a/src/net/server/PlayerStorage.java b/src/net/server/PlayerStorage.java index 2db15fc0e2..9576cad510 100644 --- a/src/net/server/PlayerStorage.java +++ b/src/net/server/PlayerStorage.java @@ -28,18 +28,19 @@ import java.util.Collection; import java.util.List; import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; public class PlayerStorage { - private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true); + private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true); private final Map storage = new LinkedHashMap<>(); private final Map nameStorage = new LinkedHashMap<>(); - private ReadLock rlock = locks.readLock(); - private WriteLock wlock = locks.writeLock(); + private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks); + private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks); public void addPlayer(MapleCharacter chr) { wlock.lock(); diff --git a/src/net/server/Server.java b/src/net/server/Server.java index b32e2d12ca..b8f0e62a50 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -21,7 +21,6 @@ */ package net.server; -import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.security.Security; @@ -42,31 +41,36 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import config.YamlConfig; import net.server.audit.ThreadTracker; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.MapleServerHandler; import net.mina.MapleCodecFactory; import net.server.channel.Channel; +import net.server.coordinator.session.MapleSessionCoordinator; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; -import net.server.worker.CharacterDiseaseWorker; -import net.server.worker.CouponWorker; -import net.server.worker.EventRecallCoordinatorWorker; -import net.server.worker.FredrickWorker; -import net.server.worker.InvitationWorker; -import net.server.worker.LoginCoordinatorWorker; -import net.server.worker.LoginStorageWorker; -import net.server.worker.RankingCommandWorker; -import net.server.worker.RankingLoginWorker; -import net.server.worker.ReleaseLockWorker; +import net.server.task.BossLogTask; +import net.server.task.CharacterDiseaseTask; +import net.server.task.CouponTask; +import net.server.task.EventRecallCoordinatorTask; +import net.server.task.DueyFredrickTask; +import net.server.task.InvitationTask; +import net.server.task.LoginCoordinatorTask; +import net.server.task.LoginStorageTask; +import net.server.task.RankingCommandTask; +import net.server.task.RankingLoginTask; +import net.server.task.ReleaseLockTask; +import net.server.task.RespawnTask; import net.server.world.World; import org.apache.mina.core.buffer.IoBuffer; @@ -78,30 +82,30 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import client.MapleClient; +import client.MapleFamily; import client.MapleCharacter; import client.SkillFactory; import client.command.CommandsExecutor; import client.inventory.Item; import client.inventory.ItemFactory; -import client.inventory.MaplePet; import client.inventory.manipulator.MapleCashidGenerator; import client.newyear.NewYearCardRecord; -import constants.ItemConstants; -import constants.GameConstants; -import constants.OpcodeConstants; -import constants.ServerConstants; +import constants.inventory.ItemConstants; +import constants.game.GameConstants; +import constants.net.OpcodeConstants; +import constants.net.ServerConstants; import java.util.TimeZone; -import net.server.coordinator.MapleSessionCoordinator; import server.CashShop.CashItemFactory; import server.MapleSkillbookInformationProvider; import server.ThreadManager; import server.TimerManager; +import server.expeditions.MapleExpeditionBossLog; import server.life.MaplePlayerNPCFactory; import server.quest.MapleQuest; import tools.AutoJCE; import tools.DatabaseConnection; +import tools.FilePrinter; import tools.Pair; -import org.apache.mina.core.session.IoSession; public class Server { @@ -141,13 +145,13 @@ public class Server { private final Lock srvLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER); private final Lock disLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_DISEASES); - private final ReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true); - private final ReadLock wldRLock = wldLock.readLock(); - private final WriteLock wldWLock = wldLock.writeLock(); + private final MonitoredReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true); + private final MonitoredReadLock wldRLock = MonitoredReadLockFactory.createLock(wldLock); + private final MonitoredWriteLock wldWLock = MonitoredWriteLockFactory.createLock(wldLock); - private final ReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true); - private final ReadLock lgnRLock = lgnLock.readLock(); - private final WriteLock lgnWLock = lgnLock.writeLock(); + private final MonitoredReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true); + private final MonitoredReadLock lgnRLock = MonitoredReadLockFactory.createLock(lgnLock); + private final MonitoredWriteLock lgnWLock = MonitoredWriteLockFactory.createLock(lgnLock); private final AtomicLong currentTime = new AtomicLong(0); private long serverCurrentTime = 0; @@ -165,7 +169,7 @@ public class Server { } public void updateCurrentTime() { - serverCurrentTime = currentTime.addAndGet(ServerConstants.UPDATE_INTERVAL); + serverCurrentTime = currentTime.addAndGet(YamlConfig.config.server.UPDATE_INTERVAL); } public long forceUpdateCurrentTime() { @@ -337,18 +341,13 @@ public class Server { if(worldChannels == null) return -3; int channelid = worldChannels.size(); - if(channelid >= ServerConstants.CHANNEL_SIZE) return -2; - - Properties p = loadWorldINI(); - if(p == null) { - return -1; - } + if(channelid >= YamlConfig.config.server.CHANNEL_SIZE) return -2; channelid++; World world = this.getWorld(worldid); Channel channel = new Channel(worldid, channelid, getCurrentTime()); - - channel.setServerMessage(p.getProperty("whyamirecommended" + worldid)); + + channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended); world.addChannel(channel); worldChannels.put(channelid, channel.getIP()); @@ -360,10 +359,7 @@ public class Server { } public int addWorld() { - Properties p = loadWorldINI(); - if(p == null) return -2; - - int newWorld = initWorld(p); + int newWorld = initWorld(); if(newWorld > -1) { installWorldPlayerRanking(newWorld); @@ -383,40 +379,40 @@ public class Server { return newWorld; } - private static int getWorldProperty(Properties p, String property, int wid, int defaultValue) { - String content = p.getProperty(property + wid); - return content != null ? Integer.parseInt(content) : defaultValue; - } - - private int initWorld(Properties p) { + private int initWorld() { wldWLock.lock(); try { int i = worlds.size(); - if(i >= ServerConstants.WLDLIST_SIZE) { + if(i >= YamlConfig.config.server.WLDLIST_SIZE) { return -1; } System.out.println("Starting world " + i); - int exprate = getWorldProperty(p, "exprate", i, ServerConstants.EXP_RATE); - int mesorate = getWorldProperty(p, "mesorate", i, ServerConstants.MESO_RATE); - int droprate = getWorldProperty(p, "droprate", i, ServerConstants.DROP_RATE); - int bossdroprate = getWorldProperty(p, "bossdroprate", i, ServerConstants.BOSS_DROP_RATE); - int questrate = getWorldProperty(p, "questrate", i, ServerConstants.QUEST_RATE); - int travelrate = getWorldProperty(p, "travelrate", i, ServerConstants.TRAVEL_RATE); - int fishingrate = getWorldProperty(p, "fishrate", i, ServerConstants.FISHING_RATE); + + int exprate = YamlConfig.config.worlds.get(i).exp_rate; + int mesorate = YamlConfig.config.worlds.get(i).meso_rate; + int droprate = YamlConfig.config.worlds.get(i).drop_rate; + int bossdroprate = YamlConfig.config.worlds.get(i).boss_drop_rate; + int questrate = YamlConfig.config.worlds.get(i).quest_rate; + int travelrate = YamlConfig.config.worlds.get(i).travel_rate; + int fishingrate = YamlConfig.config.worlds.get(i).fishing_rate; + + int flag = YamlConfig.config.worlds.get(i).flag; + String event_message = YamlConfig.config.worlds.get(i).event_message; + String why_am_i_recommended = YamlConfig.config.worlds.get(i).why_am_i_recommended; World world = new World(i, - Integer.parseInt(p.getProperty("flag" + i)), - p.getProperty("eventmessage" + i), + flag, + event_message, exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate); - worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i))); + worldRecommendedList.add(new Pair<>(i, why_am_i_recommended)); worlds.add(world); Map channelInfo = new HashMap<>(); long bootTime = getCurrentTime(); - for (int j = 1; j <= Integer.parseInt(p.getProperty("channels" + i)); j++) { + for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) { int channelid = j; Channel channel = new Channel(i, channelid, bootTime); @@ -426,7 +422,7 @@ public class Server { channels.add(i, channelInfo); - world.setServerMessage(p.getProperty("servermessage" + i)); + world.setServerMessage(YamlConfig.config.worlds.get(i).server_message); System.out.println("Finished loading world " + i + "\r\n"); return i; @@ -506,18 +502,6 @@ public class Server { } } - public static Properties loadWorldINI() { - Properties p = new Properties(); - try { - p.load(new FileInputStream("world.ini")); - return p; - } catch (Exception e) { - e.printStackTrace(); - System.out.println("[SEVERE] Could not find/open 'world.ini'."); - return null; - } - } - private static long getTimeLeftForNextHour() { Calendar nextHour = Calendar.getInstance(); nextHour.add(Calendar.HOUR, 1); @@ -527,12 +511,22 @@ public class Server { return Math.max(0, nextHour.getTimeInMillis() - System.currentTimeMillis()); } + public static long getTimeLeftForNextDay() { + Calendar nextDay = Calendar.getInstance(); + nextDay.add(Calendar.DAY_OF_MONTH, 1); + nextDay.set(Calendar.HOUR_OF_DAY, 0); + nextDay.set(Calendar.MINUTE, 0); + nextDay.set(Calendar.SECOND, 0); + + return Math.max(0, nextDay.getTimeInMillis() - System.currentTimeMillis()); + } + public Map getCouponRates() { return couponRates; } public static void cleanNxcodeCoupons(Connection con) throws SQLException { - if (!ServerConstants.USE_CLEAR_OUTDATED_COUPONS) return; + if (!YamlConfig.config.server.USE_CLEAR_OUTDATED_COUPONS) return; long timeClear = System.currentTimeMillis() - 14 * 24 * 60 * 60 * 1000; @@ -662,6 +656,7 @@ public class Server { MapleCharacter player = c.getPlayer(); if(player != null && player.isLoggedinWorld()) { player.announceDiseases(); + player.collectDiseases(); } } @@ -689,7 +684,7 @@ public class Server { public List> getWorldPlayerRanking(int worldid) { wldRLock.lock(); try { - return new ArrayList<>(playerRanking.get(!ServerConstants.USE_WHOLE_SERVER_RANKING ? worldid : 0)); + return new ArrayList<>(playerRanking.get(!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING ? worldid : 0)); } finally { wldRLock.unlock(); } @@ -700,7 +695,7 @@ public class Server { if(!ranking.isEmpty()) { wldWLock.lock(); try { - if (!ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { for(int i = playerRanking.size(); i <= worldid; i++) { playerRanking.add(new ArrayList>(0)); } @@ -716,7 +711,7 @@ public class Server { } private void removeWorldPlayerRanking() { - if (!ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { wldWLock.lock(); try { if(playerRanking.size() < this.getWorldsSize()) { @@ -744,7 +739,7 @@ public class Server { if(!rankUpdates.isEmpty()) { wldWLock.lock(); try { - if (!ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { for(int i = playerRanking.size(); i <= rankUpdates.get(rankUpdates.size() - 1).getLeft(); i++) { playerRanking.add(new ArrayList>(0)); } @@ -762,7 +757,7 @@ public class Server { } private void initWorldPlayerRanking() { - if (ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { playerRanking.add(new ArrayList>(0)); } updateWorldPlayerRanking(); @@ -779,7 +774,7 @@ public class Server { con = DatabaseConnection.getConnection(); String worldQuery; - if (!ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { if(worldid >= 0) { worldQuery = (" AND `characters`.`world` = " + worldid); } else { @@ -789,10 +784,10 @@ public class Server { worldQuery = (" AND `characters`.`world` >= 0 AND `characters`.`world` <= " + Math.abs(worldid)); } - ps = con.prepareStatement("SELECT `characters`.`name`, `characters`.`level`, `characters`.`world` FROM `characters` LEFT JOIN accounts ON accounts.id = characters.accountid WHERE `characters`.`gm` < 2 AND `accounts`.`banned` = '0'" + worldQuery + " ORDER BY " + (!ServerConstants.USE_WHOLE_SERVER_RANKING ? "world, " : "") + "level DESC, exp DESC, lastExpGainTime ASC LIMIT 50"); + ps = con.prepareStatement("SELECT `characters`.`name`, `characters`.`level`, `characters`.`world` FROM `characters` LEFT JOIN accounts ON accounts.id = characters.accountid WHERE `characters`.`gm` < 2 AND `accounts`.`banned` = '0'" + worldQuery + " ORDER BY " + (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING ? "world, " : "") + "level DESC, exp DESC, lastExpGainTime ASC LIMIT 50"); rs = ps.executeQuery(); - if (!ServerConstants.USE_WHOLE_SERVER_RANKING) { + if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { int currentWorld = -1; while(rs.next()) { int rsWorld = rs.getInt("world"); @@ -838,17 +833,12 @@ public class Server { } public void init() { - Properties p = loadWorldINI(); - if(p == null) { - System.exit(0); - } - System.out.println("HeavenMS v" + ServerConstants.VERSION + " starting up.\r\n"); - if(ServerConstants.SHUTDOWNHOOK) + if(YamlConfig.config.server.SHUTDOWNHOOK) Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false))); - TimeZone.setDefault(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE)); Connection c = null; try { @@ -868,8 +858,9 @@ public class Server { } catch (SQLException sqle) { sqle.printStackTrace(); } - - MaplePet.clearMissingPetsFromDb(); + applyAllNameChanges(); //name changes can be missed by INSTANT_NAME_CHANGE + applyAllWorldTransfers(); + //MaplePet.clearMissingPetsFromDb(); // thanks Optimist for noticing this taking too long to run MapleCashidGenerator.loadExistentCashIdsFromDb(); IoBuffer.setUseDirectBuffer(false); @@ -880,20 +871,25 @@ public class Server { ThreadManager.getInstance().start(); TimerManager tMan = TimerManager.getInstance(); tMan.start(); - tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw... + tMan.register(tMan.purge(), YamlConfig.config.server.PURGING_INTERVAL);//Purging ftw... disconnectIdlesOnLoginTask(); long timeLeft = getTimeLeftForNextHour(); - tMan.register(new CharacterDiseaseWorker(), ServerConstants.UPDATE_INTERVAL, ServerConstants.UPDATE_INTERVAL); - tMan.register(new ReleaseLockWorker(), 2 * 60 * 1000, 2 * 60 * 1000); - tMan.register(new CouponWorker(), ServerConstants.COUPON_INTERVAL, timeLeft); - tMan.register(new RankingCommandWorker(), 5 * 60 * 1000, 5 * 60 * 1000); - tMan.register(new RankingLoginWorker(), ServerConstants.RANKING_INTERVAL, timeLeft); - tMan.register(new LoginCoordinatorWorker(), 60 * 60 * 1000, timeLeft); - tMan.register(new EventRecallCoordinatorWorker(), 60 * 60 * 1000, timeLeft); - tMan.register(new LoginStorageWorker(), 2 * 60 * 1000, 2 * 60 * 1000); - tMan.register(new FredrickWorker(), 60 * 60 * 1000, 60 * 60 * 1000); - tMan.register(new InvitationWorker(), 30 * 1000, 30 * 1000); + tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL); + tMan.register(new ReleaseLockTask(), 2 * 60 * 1000, 2 * 60 * 1000); + tMan.register(new CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft); + tMan.register(new RankingCommandTask(), 5 * 60 * 1000, 5 * 60 * 1000); + tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft); + tMan.register(new LoginCoordinatorTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new EventRecallCoordinatorTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new LoginStorageTask(), 2 * 60 * 1000, 2 * 60 * 1000); + tMan.register(new DueyFredrickTask(), 60 * 60 * 1000, timeLeft); + tMan.register(new InvitationTask(), 30 * 1000, 30 * 1000); + tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL); + + timeLeft = getTimeLeftForNextDay(); + MapleExpeditionBossLog.resetBossLogTable(); + tMan.register(new BossLogTask(), 24 * 60 * 60 * 1000, timeLeft); long timeToTake = System.currentTimeMillis(); SkillFactory.loadAllSkills(); @@ -910,13 +906,13 @@ public class Server { NewYearCardRecord.startPendingNewYearCardRequests(); - if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask(); + if(YamlConfig.config.server.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask(); try { - Integer worldCount = Math.min(GameConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds"))); + Integer worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS); for (int i = 0; i < worldCount; i++) { - initWorld(p); + initWorld(); } initWorldPlayerRanking(); @@ -927,7 +923,17 @@ public class Server { System.out.println("[SEVERE] Syntax error in 'world.ini'."); System.exit(0); } - + + System.out.println(); + + if(YamlConfig.config.server.USE_FAMILY_SYSTEM) { + timeToTake = System.currentTimeMillis(); + MapleFamily.loadAllFamilies(); + System.out.println("Families loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n"); + } + + System.out.println(); + acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); acceptor.setHandler(new MapleServerHandler()); try { @@ -937,13 +943,17 @@ public class Server { } System.out.println("Listening on port 8484\r\n\r\n"); - + System.out.println("HeavenMS is now online.\r\n"); online = true; MapleSkillbookInformationProvider.getInstance(); OpcodeConstants.generateOpcodeNames(); CommandsExecutor.getInstance(); + + for (Channel ch : this.getAllChannels()) { + ch.reloadEventScriptManager(); + } } public static void main(String args[]) { @@ -1080,18 +1090,25 @@ public class Server { public MapleGuild getGuild(int id, int world, MapleCharacter mc) { synchronized (guilds) { - if (guilds.get(id) != null) { - return guilds.get(id); + MapleGuild g = guilds.get(id); + if (g != null) { + return g; } - MapleGuild g = new MapleGuild(id, world); + + g = new MapleGuild(id, world); if (g.getId() == -1) { return null; } if(mc != null) { - mc.setMGC(g.getMGC(mc.getId())); - if(g.getMGC(mc.getId()) == null) System.out.println("null for " + mc.getName() + " when loading guild " + id); - g.getMGC(mc.getId()).setCharacter(mc); + MapleGuildCharacter mgc = g.getMGC(mc.getId()); + if (mgc != null) { + mc.setMGC(mgc); + mgc.setCharacter(mc); + } else { + FilePrinter.printError(FilePrinter.GUILD_CHAR_ERROR, "Could not find " + mc.getName() + " when loading guild " + id + "."); + } + g.setOnline(mc.getId(), true, mc.getClient().getChannel()); } @@ -1419,6 +1436,11 @@ public class Server { } finally { lgnWLock.unlock(); } + + for (World wserv : this.getWorlds()) { + wserv.clearAccountCharacterView(accountid); + wserv.unregisterAccountStorage(accountid); + } } */ @@ -1536,6 +1558,82 @@ public class Server { } } + private static void applyAllNameChanges() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM namechanges WHERE completionTime IS NULL")) { + ResultSet rs = ps.executeQuery(); + List> changedNames = new LinkedList>(); //logging only + while(rs.next()) { + con.setAutoCommit(false); + int nameChangeId = rs.getInt("id"); + int characterId = rs.getInt("characterId"); + String oldName = rs.getString("old"); + String newName = rs.getString("new"); + boolean success = MapleCharacter.doNameChange(con, characterId, oldName, newName, nameChangeId); + if(!success) con.rollback(); //discard changes + else changedNames.add(new Pair(oldName, newName)); + con.setAutoCommit(true); + } + //log + for(Pair namePair : changedNames) { + FilePrinter.print(FilePrinter.CHANGE_CHARACTER_NAME, "Name change applied : from \"" + namePair.getLeft() + "\" to \"" + namePair.getRight() + "\" at " + Calendar.getInstance().getTime().toString()); + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.CHANGE_CHARACTER_NAME, e, "Failed to retrieve list of pending name changes."); + } + } + + private static void applyAllWorldTransfers() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT * FROM worldtransfers WHERE completionTime IS NULL")) { + ResultSet rs = ps.executeQuery(); + List removedTransfers = new LinkedList(); + while(rs.next()) { + int nameChangeId = rs.getInt("id"); + int characterId = rs.getInt("characterId"); + int oldWorld = rs.getInt("from"); + int newWorld = rs.getInt("to"); + String reason = MapleCharacter.checkWorldTransferEligibility(con, characterId, oldWorld, newWorld); //check if character is still eligible + if(reason != null) { + removedTransfers.add(nameChangeId); + FilePrinter.print(FilePrinter.WORLD_TRANSFER, "World transfer cancelled : Character ID " + characterId + " at " + Calendar.getInstance().getTime().toString() + ", Reason : " + reason); + try (PreparedStatement delPs = con.prepareStatement("DELETE FROM worldtransfers WHERE id = ?")) { + delPs.setInt(1, nameChangeId); + delPs.executeUpdate(); + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to delete world transfer for character ID " + characterId); + } + } + } + rs.beforeFirst(); + List>> worldTransfers = new LinkedList>>(); //logging only > + while(rs.next()) { + con.setAutoCommit(false); + int nameChangeId = rs.getInt("id"); + if(removedTransfers.contains(nameChangeId)) continue; + int characterId = rs.getInt("characterId"); + int oldWorld = rs.getInt("from"); + int newWorld = rs.getInt("to"); + boolean success = MapleCharacter.doWorldTransfer(con, characterId, oldWorld, newWorld, nameChangeId); + if(!success) con.rollback(); + else worldTransfers.add(new Pair>(characterId, new Pair(oldWorld, newWorld))); + con.setAutoCommit(true); + } + //log + for(Pair> worldTransferPair : worldTransfers) { + int charId = worldTransferPair.getLeft(); + int oldWorld = worldTransferPair.getRight().getLeft(); + int newWorld = worldTransferPair.getRight().getRight(); + FilePrinter.print(FilePrinter.WORLD_TRANSFER, "World transfer applied : Character ID " + charId + " from World " + oldWorld + " to World " + newWorld + " at " + Calendar.getInstance().getTime().toString()); + } + } catch(SQLException e) { + e.printStackTrace(); + FilePrinter.printError(FilePrinter.WORLD_TRANSFER, e, "Failed to retrieve list of pending world transfers."); + } + } + public void loadAccountCharacters(MapleClient c) { Integer accId = c.getAccID(); if (!isFirstAccountLogin(accId)) { @@ -1605,12 +1703,38 @@ public class Server { return gmLevel; } - private static String getRemoteIp(IoSession session) { - return MapleSessionCoordinator.getSessionRemoteAddress(session); + public void loadAccountStorages(MapleClient c) { + int accountId = c.getAccID(); + Set accWorlds = new HashSet<>(); + lgnWLock.lock(); + try { + Set chars = accountChars.get(accountId); + + for (Integer cid : chars) { + Integer worldid = worldChars.get(cid); + if (worldid != null) { + accWorlds.add(worldid); + } + } + } finally { + lgnWLock.unlock(); + } + + List worldList = this.getWorlds(); + for (Integer worldid : accWorlds) { + if (worldid < worldList.size()) { + World wserv = worldList.get(worldid); + wserv.registerAccountStorage(accountId); + } + } } - public void setCharacteridInTransition(IoSession session, int charId) { - String remoteIp = getRemoteIp(session); + private static String getRemoteHost(MapleClient client) { + return MapleSessionCoordinator.getSessionRemoteHost(client.getSession()); + } + + public void setCharacteridInTransition(MapleClient client, int charId) { + String remoteIp = getRemoteHost(client); lgnWLock.lock(); try { @@ -1620,12 +1744,12 @@ public class Server { } } - public boolean validateCharacteridInTransition(IoSession session, int charId) { - if (!ServerConstants.USE_IP_VALIDATION) { + public boolean validateCharacteridInTransition(MapleClient client, int charId) { + if (!YamlConfig.config.server.USE_IP_VALIDATION) { return true; } - String remoteIp = getRemoteIp(session); + String remoteIp = getRemoteHost(client); lgnWLock.lock(); try { @@ -1636,12 +1760,12 @@ public class Server { } } - public Integer freeCharacteridInTransition(IoSession session) { - if (!ServerConstants.USE_IP_VALIDATION) { + public Integer freeCharacteridInTransition(MapleClient client) { + if (!YamlConfig.config.server.USE_IP_VALIDATION) { return null; } - String remoteIp = getRemoteIp(session); + String remoteIp = getRemoteHost(client); lgnWLock.lock(); try { @@ -1651,13 +1775,13 @@ public class Server { } } - public boolean hasCharacteridInTransition(IoSession session) { - if (!ServerConstants.USE_IP_VALIDATION) { + public boolean hasCharacteridInTransition(MapleClient client) { + if (!YamlConfig.config.server.USE_IP_VALIDATION) { return true; } - String remoteIp = getRemoteIp(session); - + String remoteIp = getRemoteHost(client); + lgnRLock.lock(); try { return transitioningChars.containsKey(remoteIp); @@ -1759,7 +1883,7 @@ public class Server { List allChannels = getAllChannels(); - if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask(); + if(YamlConfig.config.server.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask(); for (Channel ch : allChannels) { while (!ch.finishedShutdown()) { @@ -1771,13 +1895,13 @@ public class Server { } } } - + resetServerWorlds(); - + ThreadManager.getInstance().stop(); TimerManager.getInstance().purge(); TimerManager.getInstance().stop(); - + System.out.println("Worlds + Channels are offline."); acceptor.unbind(); acceptor = null; diff --git a/src/net/server/audit/locks/MonitoredLockType.java b/src/net/server/audit/locks/MonitoredLockType.java index 8aa3eb773c..53d78f062f 100644 --- a/src/net/server/audit/locks/MonitoredLockType.java +++ b/src/net/server/audit/locks/MonitoredLockType.java @@ -35,7 +35,9 @@ public enum MonitoredLockType { CHARACTER_EVT, CHARACTER_STA, CLIENT, + CLIENT_ANNOUNCER, CLIENT_ENCODER, + CLIENT_SESSION, CLIENT_LOGIN, BOOK, ITEM, @@ -76,6 +78,7 @@ public enum MonitoredLockType { WORLD_PSHOPS, WORLD_MERCHS, WORLD_MAPOBJS, + WORLD_SAVECHARS, WORLD_SUGGEST, EIM, EIM_PARTY, diff --git a/src/net/server/audit/locks/active/TrackerReadLock.java b/src/net/server/audit/locks/active/TrackerReadLock.java index 732cba27e1..fbdb6c7b3c 100644 --- a/src/net/server/audit/locks/active/TrackerReadLock.java +++ b/src/net/server/audit/locks/active/TrackerReadLock.java @@ -19,7 +19,7 @@ */ package net.server.audit.locks.active; -import constants.ServerConstants; +import config.YamlConfig; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -59,7 +59,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements @Override public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getDefault()); @@ -77,7 +77,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements @Override public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { unregisterLocking(); } @@ -87,7 +87,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements @Override public boolean tryLock() { if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState)); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); @@ -114,7 +114,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements public void run() { issueDeadlock(t); } - }, ServerConstants.LOCK_MONITOR_TIME); + }, YamlConfig.config.server.LOCK_MONITOR_TIME); } } finally { state.unlock(); diff --git a/src/net/server/audit/locks/active/TrackerReentrantLock.java b/src/net/server/audit/locks/active/TrackerReentrantLock.java index 09bf1f75b9..6d87a8739c 100644 --- a/src/net/server/audit/locks/active/TrackerReentrantLock.java +++ b/src/net/server/audit/locks/active/TrackerReentrantLock.java @@ -23,7 +23,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.ScheduledFuture; -import constants.ServerConstants; + +import config.YamlConfig; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -61,7 +62,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen @Override public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getDefault()); @@ -79,7 +80,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen @Override public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { unregisterLocking(); } @@ -89,7 +90,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen @Override public boolean tryLock() { if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState)); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); @@ -116,7 +117,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen public void run() { issueDeadlock(t); } - }, ServerConstants.LOCK_MONITOR_TIME); + }, YamlConfig.config.server.LOCK_MONITOR_TIME); } } finally { state.unlock(); diff --git a/src/net/server/audit/locks/active/TrackerWriteLock.java b/src/net/server/audit/locks/active/TrackerWriteLock.java index ac45afa3a7..3bf895a3f1 100644 --- a/src/net/server/audit/locks/active/TrackerWriteLock.java +++ b/src/net/server/audit/locks/active/TrackerWriteLock.java @@ -19,7 +19,7 @@ */ package net.server.audit.locks.active; -import constants.ServerConstants; +import config.YamlConfig; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -57,7 +57,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement @Override public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getDefault()); @@ -75,7 +75,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement @Override public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { unregisterLocking(); } @@ -85,7 +85,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement @Override public boolean tryLock() { if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { + if(YamlConfig.config.server.USE_THREAD_TRACKER) { if(deadlockedState != null) { //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState)); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); @@ -112,7 +112,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement public void run() { issueDeadlock(t); } - }, ServerConstants.LOCK_MONITOR_TIME); + }, YamlConfig.config.server.LOCK_MONITOR_TIME); } } finally { state.unlock(); diff --git a/src/net/server/audit/locks/empty/EmptyReadLock.java b/src/net/server/audit/locks/empty/EmptyReadLock.java index 6eafbce887..b0a0cb78df 100644 --- a/src/net/server/audit/locks/empty/EmptyReadLock.java +++ b/src/net/server/audit/locks/empty/EmptyReadLock.java @@ -19,7 +19,7 @@ */ package net.server.audit.locks.empty; -import constants.ServerConstants; +import constants.net.ServerConstants; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/src/net/server/audit/locks/empty/EmptyReentrantLock.java b/src/net/server/audit/locks/empty/EmptyReentrantLock.java index 64be5f9c1e..eda1e527c8 100644 --- a/src/net/server/audit/locks/empty/EmptyReentrantLock.java +++ b/src/net/server/audit/locks/empty/EmptyReentrantLock.java @@ -19,7 +19,7 @@ */ package net.server.audit.locks.empty; -import constants.ServerConstants; +import constants.net.ServerConstants; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/src/net/server/audit/locks/empty/EmptyWriteLock.java b/src/net/server/audit/locks/empty/EmptyWriteLock.java index b4343fc4e6..36a8ed8d59 100644 --- a/src/net/server/audit/locks/empty/EmptyWriteLock.java +++ b/src/net/server/audit/locks/empty/EmptyWriteLock.java @@ -19,7 +19,7 @@ */ package net.server.audit.locks.empty; -import constants.ServerConstants; +import constants.net.ServerConstants; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java index 17ffdc03a7..df6b7440f0 100644 --- a/src/net/server/channel/Channel.java +++ b/src/net/server/channel/Channel.java @@ -34,9 +34,13 @@ import java.util.Map.Entry; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import net.server.audit.locks.MonitoredReadLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; + +import config.YamlConfig; import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; @@ -48,7 +52,6 @@ import net.mina.MapleCodecFactory; import net.server.PlayerStorage; import net.server.Server; -import net.server.channel.worker.*; import net.server.world.World; import net.server.world.MapleParty; @@ -63,6 +66,10 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.SocketSessionConfig; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import client.MapleCharacter; +import net.server.services.ServicesManager; +import net.server.services.BaseService; +import net.server.services.type.ChannelServices; import scripting.event.EventScriptManager; import server.TimerManager; import server.events.gm.MapleEvent; @@ -72,12 +79,9 @@ import server.maps.MapleHiredMerchant; import server.maps.MapleMap; import server.maps.MapleMapManager; import server.maps.MapleMiniDungeon; +import server.maps.MapleMiniDungeonInfo; import tools.MaplePacketCreator; import tools.Pair; -import client.MapleCharacter; -import client.status.MonsterStatusEffect; -import constants.ServerConstants; -import server.maps.MapleMiniDungeonInfo; public final class Channel { @@ -88,13 +92,7 @@ public final class Channel { private String ip, serverMessage; private MapleMapManager mapManager; private EventScriptManager eventSM; - private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[ServerConstants.CHANNEL_LOCKS]; - private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[ServerConstants.CHANNEL_LOCKS]; - private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[ServerConstants.CHANNEL_LOCKS]; - private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[ServerConstants.CHANNEL_LOCKS]; - private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[ServerConstants.CHANNEL_LOCKS]; - private EventScheduler eventSchedulers[] = new EventScheduler[ServerConstants.CHANNEL_LOCKS]; - private OverallScheduler channelSchedulers[] = new OverallScheduler[ServerConstants.CHANNEL_LOCKS]; + private ServicesManager services; private Map hiredMerchants = new HashMap<>(); private final Map storedVars = new HashMap<>(); private Set playersAway = new HashSet<>(); @@ -125,11 +123,11 @@ public final class Channel { private Set ongoingCathedralGuests = null; private long ongoingStartTime; - private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true); - private ReadLock merchRlock = merchantLock.readLock(); - private WriteLock merchWlock = merchantLock.writeLock(); + private MonitoredReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true); + private MonitoredReadLock merchRlock = MonitoredReadLockFactory.createLock(merchantLock); + private MonitoredWriteLock merchWlock = MonitoredWriteLockFactory.createLock(merchantLock); - private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[ServerConstants.CHANNEL_LOCKS]; + private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[YamlConfig.config.server.CHANNEL_LOCKS]; private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL, true); @@ -140,10 +138,9 @@ public final class Channel { this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever this.mapManager = new MapleMapManager(null, world, channel); try { - eventSM = new EventScriptManager(this, getEvents()); port = 7575 + this.channel - 1; port += (world * 100); - ip = ServerConstants.HOST + ":" + port; + ip = YamlConfig.config.server.HOST + ":" + port; IoBuffer.setUseDirectBuffer(false); IoBuffer.setAllocator(new SimpleBufferAllocator()); acceptor = new NioSocketAcceptor(); @@ -155,7 +152,14 @@ public final class Channel { for (MapleExpeditionType exped : MapleExpeditionType.values()) { expedType.add(exped); } - eventSM.init(); + + if (Server.getInstance().isOnline()) { // postpone event loading to improve boot time... thanks Riizade, daronhudson for noticing slow startup times + eventSM = new EventScriptManager(this, getEvents()); + eventSM.init(); + } else { + String[] ev = {"0_EXAMPLE"}; + eventSM = new EventScriptManager(this, ev); + } dojoStage = new int[20]; dojoFinishTime = new long[20]; @@ -166,17 +170,7 @@ public final class Channel { dojoTask[i] = null; } - for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) { - faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true); - - mobStatusSchedulers[i] = new MobStatusScheduler(); - mobAnimationSchedulers[i] = new MobAnimationScheduler(); - mobClearSkillSchedulers[i] = new MobClearSkillScheduler(); - mobMistSchedulers[i] = new MobMistScheduler(); - faceExpressionSchedulers[i] = new FaceExpressionScheduler(faceLock[i]); - eventSchedulers[i] = new EventScheduler(); - channelSchedulers[i] = new OverallScheduler(); - } + services = new ServicesManager(ChannelServices.OVERALL); System.out.println(" Channel " + getId() + ": Listening on port " + port); } catch (Exception e) { @@ -184,27 +178,34 @@ public final class Channel { } } - public void reloadEventScriptManager(){ - eventSM.cancel(); + public synchronized void reloadEventScriptManager(){ + if (finishedShutdown) { + return; + } + + eventSM.cancel(); eventSM = null; eventSM = new EventScriptManager(this, getEvents()); - eventSM.init(); } - public final void shutdown() { + public final synchronized void shutdown() { try { + if (finishedShutdown) { + return; + } + System.out.println("Shutting down Channel " + channel + " on World " + world); closeAllMerchants(); disconnectAwayPlayers(); players.disconnectAll(); + eventSM.dispose(); + eventSM = null; + mapManager.dispose(); mapManager = null; - eventSM.cancel(); - eventSM = null; - closeChannelSchedules(); players = null; @@ -220,6 +221,10 @@ public final class Channel { } } + private void closeChannelServices() { + services.shutdown(); + } + private void closeChannelSchedules() { for(int i = 0; i < 20; i++) { if(dojoTask[i] != null) { @@ -227,44 +232,8 @@ public final class Channel { dojoTask[i] = null; } } - - for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) { - if(mobStatusSchedulers[i] != null) { - mobStatusSchedulers[i].dispose(); - mobStatusSchedulers[i] = null; - } - - if(mobAnimationSchedulers[i] != null) { - mobAnimationSchedulers[i].dispose(); - mobAnimationSchedulers[i] = null; - } - - if(mobClearSkillSchedulers[i] != null) { - mobClearSkillSchedulers[i].dispose(); - mobClearSkillSchedulers[i] = null; - } - - if(mobMistSchedulers[i] != null) { - mobMistSchedulers[i].dispose(); - mobMistSchedulers[i] = null; - } - - if(faceExpressionSchedulers[i] != null) { - faceExpressionSchedulers[i].dispose(); - faceExpressionSchedulers[i] = null; - } - - if(eventSchedulers[i] != null) { - eventSchedulers[i].dispose(); - eventSchedulers[i] = null; - } - - if(channelSchedulers[i] != null) { - channelSchedulers[i].dispose(); - channelSchedulers[i] = null; - } - } + closeChannelServices(); disposeLocks(); } @@ -278,7 +247,7 @@ public final class Channel { } private void emptyLocks() { - for(int i = 0; i < ServerConstants.CHANNEL_LOCKS; i++) { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { faceLock[i] = faceLock[i].dispose(); } @@ -308,7 +277,11 @@ public final class Channel { public MapleMapManager getMapFactory() { return mapManager; } - + + public BaseService getServiceAccess(ChannelServices sv) { + return services.getAccess(sv).getService(); + } + public int getWorld() { return world; } @@ -335,7 +308,7 @@ public final class Channel { } public int getChannelCapacity() { - return (int)(Math.ceil(((float) players.getAllCharacters().size() / ServerConstants.CHANNEL_LOAD) * 800)); + return (int)(Math.ceil(((float) players.getAllCharacters().size() / YamlConfig.config.server.CHANNEL_LOAD) * 800)); } public void broadcastPacket(final byte[] data) { @@ -402,7 +375,7 @@ public final class Channel { for (Integer cid : playersAway) { MapleCharacter chr = wserv.getPlayerStorage().getCharacterById(cid); if (chr != null && chr.isLoggedin()) { - chr.getClient().disconnect(true, false); + chr.getClient().forceDisconnect(); } } } @@ -460,6 +433,7 @@ public final class Channel { } expeditions.put(exped.getType(), exped); + exped.beginRegistration(); // thanks Conrad for noticing leader still receiving packets on failure-to-register cases return true; } } @@ -484,6 +458,11 @@ public final class Channel { return getPlayerStorage().getCharacterByName(name) != null; } + public boolean isActive() { + EventScriptManager esm = this.getEventSM(); + return esm != null && esm.isActive(); + } + public boolean finishedShutdown() { return finishedShutdown; } @@ -683,7 +662,7 @@ public final class Channel { if(dungeons.containsKey(dungeonid)) return false; MapleMiniDungeonInfo mmdi = MapleMiniDungeonInfo.getDungeon(dungeonid); - MapleMiniDungeon mmd = new MapleMiniDungeon(mmdi.getBase(), 30); // all minidungeons timeout on 30 mins + MapleMiniDungeon mmd = new MapleMiniDungeon(mmdi.getBase(), this.getMapFactory().getMap(mmdi.getDungeonId()).getTimeLimit()); // thanks Conrad for noticing hardcoded time limit for minidungeons dungeons.put(dungeonid, mmd); return true; @@ -785,7 +764,7 @@ public final class Channel { try { List weddingReservationQueue = (cathedral ? cathedralReservationQueue : chapelReservationQueue); - int delay = ServerConstants.WEDDING_RESERVATION_DELAY - 1 - weddingReservationQueue.size(); + int delay = YamlConfig.config.server.WEDDING_RESERVATION_DELAY - 1 - weddingReservationQueue.size(); for(int i = 0; i < delay; i++) { weddingReservationQueue.add(null); // push empty slots to fill the waiting time } @@ -868,7 +847,7 @@ public final class Channel { public void run() { closeOngoingWedding(cathedral); } - }, ServerConstants.WEDDING_RESERVATION_TIMEOUT * 60 * 1000); + }, YamlConfig.config.server.WEDDING_RESERVATION_TIMEOUT * 60 * 1000); if(cathedral) { cathedralReservationTask = weddingTask; @@ -932,7 +911,7 @@ public final class Channel { } public static long getRelativeWeddingTicketExpireTime(int resSlot) { - return (resSlot * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000); + return (resSlot * YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000); } public String getWeddingReservationTimeLeft(Integer weddingId) { @@ -958,7 +937,7 @@ public final class Channel { return venue + " - RIGHT NOW"; } - return venue + " - " + getTimeLeft(ongoingStartTime + (resStatus * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000)) + " from now"; + return venue + " - " + getTimeLeft(ongoingStartTime + (resStatus * YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000)) + " from now"; } finally { lock.unlock(); } @@ -1017,84 +996,6 @@ public final class Channel { return !usedMC.contains(getMonsterCarnivalRoom(cpq1, field)); } - private static int getChannelSchedulerIndex(int mapid) { - int section = 1000000000 / ServerConstants.CHANNEL_LOCKS; - return mapid / section; - } - - public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) { - registerMobStatus(mapid, mse, cancelAction, duration, null, -1); - } - - public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) { - mobStatusSchedulers[getChannelSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay); - } - - public void interruptMobStatus(int mapid, MonsterStatusEffect mse) { - mobStatusSchedulers[getChannelSchedulerIndex(mapid)].interruptMobStatus(mse); - } - - public boolean registerMobOnAnimationEffect(int mapid, int mobHash, long delay) { - return mobAnimationSchedulers[getChannelSchedulerIndex(mapid)].registerAnimationMode(mobHash, delay); - } - - public void registerMobClearSkillAction(int mapid, Runnable runAction, long delay) { - mobClearSkillSchedulers[getChannelSchedulerIndex(mapid)].registerClearSkillAction(runAction, delay); - } - - public void registerMobMistCancelAction(int mapid, Runnable runAction, long delay) { - mobMistSchedulers[getChannelSchedulerIndex(mapid)].registerMistCancelAction(runAction, delay); - } - - public void registerEventAction(int mapid, Runnable runAction, long delay) { - eventSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay); - } - - public void registerOverallAction(int mapid, Runnable runAction, long delay) { - channelSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay); - } - - public void forceRunOverallAction(int mapid, Runnable runAction) { - channelSchedulers[getChannelSchedulerIndex(mapid)].forceRunDelayedAction(runAction); - } - - public void registerFaceExpression(final MapleMap map, final MapleCharacter chr, int emote) { - int lockid = getChannelSchedulerIndex(map.getId()); - - Runnable cancelAction = new Runnable() { - @Override - public void run() { - if(chr.isLoggedinWorld()) { - map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, 0), false); - } - } - }; - - faceLock[lockid].lock(); - try { - if(!chr.isLoggedinWorld()) { - return; - } - - faceExpressionSchedulers[lockid].registerFaceExpression(chr.getId(), cancelAction); - } finally { - faceLock[lockid].unlock(); - } - - map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, emote), false); - } - - public void unregisterFaceExpression(int mapid, MapleCharacter chr) { - int lockid = getChannelSchedulerIndex(mapid); - - faceLock[lockid].lock(); - try { - faceExpressionSchedulers[lockid].unregisterFaceExpression(chr.getId()); - } finally { - faceLock[lockid].unlock(); - } - } - public void debugMarriageStatus() { System.out.println(" ----- WORLD DATA -----"); getWorldServer().debugMarriageStatus(); diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java index 38ab0d1218..d0455c3c99 100644 --- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import server.MapleStatEffect; import server.TimerManager; @@ -49,14 +50,13 @@ import tools.data.input.LittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleJob; -import client.MapleStat; import client.Skill; import client.SkillFactory; import client.autoban.AutobanFactory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.GameConstants; -import constants.ServerConstants; +import constants.game.GameConstants; +import constants.net.ServerConstants; import constants.skills.Aran; import constants.skills.Assassin; import constants.skills.Bandit; @@ -113,10 +113,11 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl public boolean ranged, magic; public int speed = 4; public Point position = new Point(); + public MapleStatEffect getAttackEffect(MapleCharacter chr, Skill theSkill) { Skill mySkill = theSkill; if (mySkill == null) { - mySkill = SkillFactory.getSkill(GameConstants.getHiddenSkill(skill)); + mySkill = SkillFactory.getSkill(skill); } int skillLevel = chr.getSkillLevel(mySkill); @@ -135,8 +136,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } - protected synchronized void applyAttack(AttackInfo attack, final MapleCharacter player, int attackCount) { - if (player.getMap().isOwnershipRestricted(player)) { + protected void applyAttack(AttackInfo attack, final MapleCharacter player, int attackCount) { + final MapleMap map = player.getMap(); + if (map.isOwnershipRestricted(player)) { return; } @@ -148,10 +150,10 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl return; } if (attack.skill != 0) { - theSkill = SkillFactory.getSkill(GameConstants.getHiddenSkill(attack.skill)); //returns back the skill id if its not a hidden skill so we are gucci - attackEffect = attack.getAttackEffect(player, theSkill); + theSkill = SkillFactory.getSkill(attack.skill); // thanks Conrad for noticing some Aran skills not consuming MP + attackEffect = attack.getAttackEffect(player, theSkill); //returns back the player's attack effect so we are gucci if (attackEffect == null) { - player.getClient().announce(MaplePacketCreator.enableActions()); + player.announce(MaplePacketCreator.enableActions()); return; } @@ -162,9 +164,11 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl int mobCount = attackEffect.getMobCount(); if (attack.skill != Cleric.HEAL) { if (player.isAlive()) { - if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb + if(attack.skill == Aran.BODY_PRESSURE || attack.skill == Marauder.ENERGY_CHARGE || attack.skill == ThunderBreaker.ENERGY_CHARGE) { // thanks IxianMace for noticing Energy Charge skills refreshing on touch, leading to misleading buff applies + // prevent touch dmg skills refreshing + } else if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y)); - } else if(attack.skill != Aran.BODY_PRESSURE) {// prevent BP refreshing + } else { attackEffect.applyTo(player); if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD @@ -177,7 +181,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } } else { - player.getClient().announce(MaplePacketCreator.enableActions()); + player.announce(MaplePacketCreator.enableActions()); } } @@ -196,7 +200,6 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl }*/ int totDamage = 0; - final MapleMap map = player.getMap(); if (attack.skill == ChiefBandit.MESO_EXPLOSION) { int delay = 0; @@ -309,7 +312,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { - player.getMap().spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, (double) 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2); + map.spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, (double) 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2); } }, delay); delay += 100; @@ -322,8 +325,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl Skill steal = SkillFactory.getSkill(Bandit.STEAL); if (monster.getStolen().size() < 1) { // One steal per mob <3 if (steal.getEffect(player.getSkillLevel(steal)).makeChanceResult()) { - MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance(); + monster.addStolen(0); + MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance(); List dropPool = mi.retrieveDropPool(monster.getId()); if(!dropPool.isEmpty()) { Integer rndPool = (int) Math.floor(Math.random() * dropPool.get(dropPool.size() - 1)); @@ -334,7 +338,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl List toSteal = new ArrayList<>(); toSteal.add(mi.retrieveDrop(monster.getId()).get(i)); - player.getMap().dropItemsFromMonster(toSteal, player, monster); + map.dropItemsFromMonster(toSteal, player, monster); monster.addStolen(toSteal.get(0).itemId); } } @@ -442,11 +446,13 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } else { mortalBlow = SkillFactory.getSkill(Sniper.MORTAL_BLOW); } - if (player.getSkillLevel(mortalBlow) > 0) { - MapleStatEffect mortal = mortalBlow.getEffect(player.getSkillLevel(mortalBlow)); + + int skillLevel = player.getSkillLevel(mortalBlow); + if (skillLevel > 0) { + MapleStatEffect mortal = mortalBlow.getEffect(skillLevel); if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) { if (Randomizer.rand(1, 100) <= mortal.getY()) { - monster.getMap().killMonster(monster, player, true); + map.damageMonster(player, monster, Integer.MAX_VALUE); // thanks Conrad for noticing reduced EXP gain from skill kill } } } @@ -460,7 +466,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl int threeSnailsId = player.getJobType() * 10000000 + 1000; if(attack.skill == threeSnailsId) { - if(ServerConstants.USE_ULTRA_THREE_SNAILS) { + if(YamlConfig.config.server.USE_ULTRA_THREE_SNAILS) { int skillLv = player.getSkillLevel(threeSnailsId); if(skillLv > 0) { @@ -522,7 +528,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl map.damageMonster(player, monster, totDamageToOneMonster); } - if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT)) { + if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) { List> mobSkills = monster.getSkills(); for (Pair ms : mobSkills) { @@ -533,13 +539,14 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } } - if (monster.isBuffed(MonsterStatus.MAGIC_REFLECT)) { + if (monster.isBuffed(MonsterStatus.MAGIC_REFLECT) && attack.magic) { List> mobSkills = monster.getSkills(); for (Pair ms : mobSkills) { if (ms.left == 145) { MobSkill toUse = MobSkillFactory.getMobSkill(ms.left, ms.right); - player.addMP(-toUse.getY()); + player.addHP(-toUse.getY()); + map.broadcastMessage(player, MaplePacketCreator.damagePlayer(0, monster.getId(), player.getId(), toUse.getY(), 0, 0, false, 0, true, monster.getObjectId(), 0, 0), true); } } } @@ -650,16 +657,16 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl // Find the base damage to base futher calculations on. // Several skills have their own formula in this section. - int calcDmgMax = 0; + long calcDmgMax; - if(magic && ret.skill != 0) { - calcDmgMax = (chr.getTotalMagic() * chr.getTotalMagic() / 1000 + chr.getTotalMagic()) / 30 + chr.getTotalInt() / 200; + if(magic && ret.skill != 0) { // thanks onechord for noticing a few false positives stemming from maxdmg as 0 + calcDmgMax = (long) (Math.ceil((chr.getTotalMagic() * Math.ceil(chr.getTotalMagic() / 1000.0) + chr.getTotalMagic()) / 30.0) + Math.ceil(chr.getTotalInt() / 200.0)); } else if(ret.skill == 4001344 || ret.skill == NightWalker.LUCKY_SEVEN || ret.skill == NightLord.TRIPLE_THROW) { - calcDmgMax = (chr.getTotalLuk() * 5) * chr.getTotalWatk() / 100; + calcDmgMax = (long) ((chr.getTotalLuk() * 5) * Math.ceil(chr.getTotalWatk() / 100.0)); } else if(ret.skill == DragonKnight.DRAGON_ROAR) { - calcDmgMax = (chr.getTotalStr() * 4 + chr.getTotalDex()) * chr.getTotalWatk() / 100; + calcDmgMax = (long) ((chr.getTotalStr() * 4 + chr.getTotalDex()) * Math.ceil(chr.getTotalWatk() / 100.0)); } else if(ret.skill == NightLord.VENOMOUS_STAR || ret.skill == Shadower.VENOMOUS_STAB) { - calcDmgMax = (int) (18.5 * (chr.getTotalStr() + chr.getTotalLuk()) + chr.getTotalDex() * 2) / 100 * chr.calculateMaxBaseDamage(chr.getTotalWatk()); + calcDmgMax = (long) (Math.ceil((18.5 * (chr.getTotalStr() + chr.getTotalLuk()) + chr.getTotalDex() * 2) / 100.0) * chr.calculateMaxBaseDamage(chr.getTotalWatk())); } else { calcDmgMax = chr.calculateMaxBaseDamage(chr.getTotalWatk()); } @@ -714,7 +721,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl if(comboBuff > 6) { // Advanced Combo MapleStatEffect ceffect = SkillFactory.getSkill(advcomboid).getEffect(chr.getSkillLevel(advcomboid)); - calcDmgMax = (int) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + 0.20 + (comboBuff - 5) * 0.04); + calcDmgMax = (long) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + 0.20 + (comboBuff - 5) * 0.04); } else { // Normal Combo int skillLv = chr.getSkillLevel(oid); @@ -722,7 +729,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl if(skillLv > 0) { MapleStatEffect ceffect = SkillFactory.getSkill(oid).getEffect(skillLv); - calcDmgMax = (int) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + Math.floor((comboBuff - 1) * (skillLv / 6)) / 100); + calcDmgMax = (long) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + Math.floor((comboBuff - 1) * (skillLv / 6)) / 100); } } @@ -840,7 +847,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } else if (ret.skill == Aran.BODY_PRESSURE) { if (monster != null) { - int bodyPressureDmg = monster.getMaxHp() * SkillFactory.getSkill(Aran.BODY_PRESSURE).getEffect(ret.skilllevel).getDamage() / 100; + int bodyPressureDmg = (int) Math.ceil(monster.getMaxHp() * SkillFactory.getSkill(Aran.BODY_PRESSURE).getEffect(ret.skilllevel).getDamage() / 100.0); if (bodyPressureDmg > calcDmgMax) { calcDmgMax = bodyPressureDmg; } @@ -850,7 +857,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl for (int j = 0; j < ret.numDamage; j++) { int damage = lea.readInt(); - int hitDmgMax = calcDmgMax; + long hitDmgMax = calcDmgMax; if(ret.skill == Buccaneer.BARRAGE || ret.skill == ThunderBreaker.BARRAGE) { if(j > 3) hitDmgMax *= Math.pow(2, (j - 3)); @@ -870,7 +877,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl hitDmgMax = 82569000; // 30% of Max HP of strongest Dojo boss } - int maxWithCrit = hitDmgMax; + long maxWithCrit = hitDmgMax; if(canCrit) // They can crit, so up the max. maxWithCrit *= 2; diff --git a/src/net/server/channel/handlers/AbstractMovementPacketHandler.java b/src/net/server/channel/handlers/AbstractMovementPacketHandler.java index 47f2852c60..2c56a360da 100644 --- a/src/net/server/channel/handlers/AbstractMovementPacketHandler.java +++ b/src/net/server/channel/handlers/AbstractMovementPacketHandler.java @@ -30,17 +30,18 @@ import server.maps.AnimatedMapleMapObject; import server.movement.AbsoluteLifeMovement; import server.movement.ChangeEquip; import server.movement.JumpDownMovement; -import server.movement.LifeMovement; import server.movement.LifeMovementFragment; import server.movement.RelativeLifeMovement; import server.movement.TeleportMovement; import tools.data.input.LittleEndianAccessor; +import tools.exceptions.EmptyMovementException; public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketHandler { - protected List parseMovement(LittleEndianAccessor lea) { + protected List parseMovement(LittleEndianAccessor lea) throws EmptyMovementException { List res = new ArrayList<>(); byte numCommands = lea.readByte(); + if (numCommands < 1) throw new EmptyMovementException(lea); for (byte i = 0; i < numCommands; i++) { byte command = lea.readByte(); switch (command) { @@ -139,21 +140,104 @@ public abstract class AbstractMovementPacketHandler extends AbstractMaplePacketH } default: System.out.println("Unhandled Case:" + command); - return null; + throw new EmptyMovementException(lea); } } + + if (res.isEmpty()) { + throw new EmptyMovementException(lea); + } return res; } - - protected void updatePosition(List movement, AnimatedMapleMapObject target, int yoffset) { - for (LifeMovementFragment move : movement) { - if (move instanceof LifeMovement) { - if (move instanceof AbsoluteLifeMovement) { - Point position = ((LifeMovement) move).getPosition(); - position.y += yoffset; - target.setPosition(position); + + protected void updatePosition(LittleEndianAccessor lea, AnimatedMapleMapObject target, int yOffset) throws EmptyMovementException { + + byte numCommands = lea.readByte(); + if (numCommands < 1) throw new EmptyMovementException(lea); + for (byte i = 0; i < numCommands; i++) { + byte command = lea.readByte(); + switch (command) { + case 0: // normal move + case 5: + case 17: { // Float + //Absolute movement - only this is important for the server, other movement can be passed to the client + short xpos = lea.readShort(); //is signed fine here? + short ypos = lea.readShort(); + target.setPosition(new Point(xpos, ypos + yOffset)); + lea.skip(6); //xwobble = lea.readShort(); ywobble = lea.readShort(); fh = lea.readShort(); + byte newstate = lea.readByte(); + target.setStance(newstate); + lea.readShort(); //duration + break; } - target.setStance(((LifeMovement) move).getNewstate()); + case 1: + case 2: + case 6: // fj + case 12: + case 13: // Shot-jump-back thing + case 16: // Float + case 18: + case 19: // Springs on maps + case 20: // Aran Combat Step + case 22: { + //Relative movement - server only cares about stance + lea.skip(4); //xpos = lea.readShort(); ypos = lea.readShort(); + byte newstate = lea.readByte(); + target.setStance(newstate); + lea.readShort(); //duration + break; + } + case 3: + case 4: // tele... -.- + case 7: // assaulter + case 8: // assassinate + case 9: // rush + case 11: //chair + { +// case 14: { + //Teleport movement - same as above + lea.skip(8); //xpos = lea.readShort(); ypos = lea.readShort(); xwobble = lea.readShort(); ywobble = lea.readShort(); + byte newstate = lea.readByte(); + target.setStance(newstate); + break; + } + case 14: + lea.skip(9); // jump down (?) + break; + case 10: // Change Equip + //ignored by server + lea.readByte(); + break; + /*case 11: { // Chair + short xpos = lea.readShort(); + short ypos = lea.readShort(); + short fh = lea.readShort(); + byte newstate = lea.readByte(); + short duration = lea.readShort(); + ChairMovement cm = new ChairMovement(command, new Point(xpos, ypos), duration, newstate); + cm.setFh(fh); + res.add(cm); + break; + }*/ + case 15: { + //Jump down movement - stance only + lea.skip(12); //short xpos = lea.readShort(); ypos = lea.readShort(); xwobble = lea.readShort(); ywobble = lea.readShort(); fh = lea.readShort(); ofh = lea.readShort(); + byte newstate = lea.readByte(); + target.setStance(newstate); + lea.readShort(); // duration + break; + } + case 21: {//Causes aran to do weird stuff when attacking o.o + /*byte newstate = lea.readByte(); + short unk = lea.readShort(); + AranMovement am = new AranMovement(command, null, unk, newstate); + res.add(am);*/ + lea.skip(3); + break; + } + default: + System.out.println("Unhandled Case:" + command); + throw new EmptyMovementException(lea); } } } diff --git a/src/net/server/channel/handlers/AcceptFamilyHandler.java b/src/net/server/channel/handlers/AcceptFamilyHandler.java index b87b124ad6..ebe120fb11 100644 --- a/src/net/server/channel/handlers/AcceptFamilyHandler.java +++ b/src/net/server/channel/handlers/AcceptFamilyHandler.java @@ -21,31 +21,133 @@ */ package net.server.channel.handlers; -import constants.ServerConstants; +import config.YamlConfig; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + import client.MapleCharacter; import client.MapleClient; +import client.MapleFamily; +import client.MapleFamilyEntry; import net.AbstractMaplePacketHandler; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; +import tools.DatabaseConnection; +import tools.FilePrinter; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** * * @author Jay Estrella + * @author Ubaware */ public final class AcceptFamilyHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if (!ServerConstants.USE_FAMILY_SYSTEM){ - return; - } - //System.out.println(slea.toString()); + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) { + return; + } + MapleCharacter chr = c.getPlayer(); int inviterId = slea.readInt(); - //String inviterName = slea.readMapleAsciiString(); + slea.readMapleAsciiString(); + boolean accept = slea.readByte() != 0; + // String inviterName = slea.readMapleAsciiString(); MapleCharacter inviter = c.getWorldServer().getPlayerStorage().getCharacterById(inviterId); - if (inviter != null) { - inviter.getClient().announce(MaplePacketCreator.sendFamilyJoinResponse(true, c.getPlayer().getName())); + if(inviter != null) { + MapleInviteResult inviteResult = MapleInviteCoordinator.answerInvite(InviteType.FAMILY, c.getPlayer().getId(), c.getPlayer(), accept); + if(inviteResult.result == InviteResult.NOT_FOUND) return; //was never invited. (or expired on server only somehow?) + if(accept) { + if(inviter.getFamily() != null) { + if(chr.getFamily() == null) { + MapleFamilyEntry newEntry = new MapleFamilyEntry(inviter.getFamily(), chr.getId(), chr.getName(), chr.getLevel(), chr.getJob()); + newEntry.setCharacter(chr); + if(!newEntry.setSenior(inviter.getFamilyEntry(), true)) { + inviter.announce(MaplePacketCreator.sendFamilyMessage(1, 0)); + return; + } else { + // save + inviter.getFamily().addEntry(newEntry); + insertNewFamilyRecord(chr.getId(), inviter.getFamily().getID(), inviter.getId(), false); + } + } else { //absorb target family + MapleFamilyEntry targetEntry = chr.getFamilyEntry(); + MapleFamily targetFamily = targetEntry.getFamily(); + if(targetFamily.getLeader() != targetEntry) return; + if(inviter.getFamily().getTotalGenerations() + targetFamily.getTotalGenerations() <= YamlConfig.config.server.FAMILY_MAX_GENERATIONS) { + targetEntry.join(inviter.getFamilyEntry()); + } else { + inviter.announce(MaplePacketCreator.sendFamilyMessage(76, 0)); + chr.announce(MaplePacketCreator.sendFamilyMessage(76, 0)); + return; + } + } + } else { // create new family + if(chr.getFamily() != null && inviter.getFamily() != null && chr.getFamily().getTotalGenerations() + inviter.getFamily().getTotalGenerations() >= YamlConfig.config.server.FAMILY_MAX_GENERATIONS) { + inviter.announce(MaplePacketCreator.sendFamilyMessage(76, 0)); + chr.announce(MaplePacketCreator.sendFamilyMessage(76, 0)); + return; + } + MapleFamily newFamily = new MapleFamily(-1, c.getWorld()); + c.getWorldServer().addFamily(newFamily.getID(), newFamily); + MapleFamilyEntry inviterEntry = new MapleFamilyEntry(newFamily, inviter.getId(), inviter.getName(), inviter.getLevel(), inviter.getJob()); + inviterEntry.setCharacter(inviter); + newFamily.setLeader(inviter.getFamilyEntry()); + newFamily.addEntry(inviterEntry); + if(chr.getFamily() == null) { //completely new family + MapleFamilyEntry newEntry = new MapleFamilyEntry(newFamily, chr.getId(), chr.getName(), chr.getLevel(), chr.getJob()); + newEntry.setCharacter(chr); + newEntry.setSenior(inviterEntry, true); + // save new family + insertNewFamilyRecord(inviter.getId(), newFamily.getID(), 0, true); + insertNewFamilyRecord(chr.getId(), newFamily.getID(), inviter.getId(), false); // char was already saved by setSenior() above + newFamily.setMessage("", true); + } else { //new family for inviter, absorb invitee family + insertNewFamilyRecord(inviter.getId(), newFamily.getID(), 0 , true); + newFamily.setMessage("", true); + chr.getFamilyEntry().join(inviterEntry); + } + } + c.getPlayer().getFamily().broadcast(MaplePacketCreator.sendFamilyJoinResponse(true, c.getPlayer().getName()), c.getPlayer().getId()); + c.announce(MaplePacketCreator.getSeniorMessage(inviter.getName())); + c.announce(MaplePacketCreator.getFamilyInfo(chr.getFamilyEntry())); + chr.getFamilyEntry().updateSeniorFamilyInfo(true); + } else { + inviter.announce(MaplePacketCreator.sendFamilyJoinResponse(false, c.getPlayer().getName())); + } } c.announce(MaplePacketCreator.sendFamilyMessage(0, 0)); } + + private static void insertNewFamilyRecord(int characterID, int familyID, int seniorID, boolean updateChar) { + try(Connection con = DatabaseConnection.getConnection()) { + try(PreparedStatement ps = con.prepareStatement("INSERT INTO family_character (cid, familyid, seniorid) VALUES (?, ?, ?)")) { + ps.setInt(1, characterID); + ps.setInt(2, familyID); + ps.setInt(3, seniorID); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not save new family record for char id " + characterID + "."); + e.printStackTrace(); + } + if(updateChar) { + try(PreparedStatement ps = con.prepareStatement("UPDATE characters SET familyid = ? WHERE id = ?")) { + ps.setInt(1, familyID); + ps.setInt(2, characterID); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update 'characters' 'familyid' record for char id " + characterID + "."); + e.printStackTrace(); + } + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + } + } } diff --git a/src/net/server/channel/handlers/AdminChatHandler.java b/src/net/server/channel/handlers/AdminChatHandler.java index a27edfa2d0..40acfc9259 100644 --- a/src/net/server/channel/handlers/AdminChatHandler.java +++ b/src/net/server/channel/handlers/AdminChatHandler.java @@ -1,7 +1,7 @@ package net.server.channel.handlers; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.LogHelper; import tools.MaplePacketCreator; @@ -25,19 +25,19 @@ public class AdminChatHandler extends AbstractMaplePacketHandler { switch (mode) { case 0:// /alertall, /noticeall, /slideall c.getWorldServer().broadcastPacket(packet); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Alert All", message); } break; case 1:// /alertch, /noticech, /slidech c.getChannelServer().broadcastPacket(packet); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Alert Ch", message); } break; case 2:// /alertm /alertmap, /noticem /noticemap, /slidem /slidemap c.getPlayer().getMap().broadcastMessage(packet); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Alert Map", message); } break; diff --git a/src/net/server/channel/handlers/AllianceOperationHandler.java b/src/net/server/channel/handlers/AllianceOperationHandler.java index 0c74c76142..f24956088e 100644 --- a/src/net/server/channel/handlers/AllianceOperationHandler.java +++ b/src/net/server/channel/handlers/AllianceOperationHandler.java @@ -124,7 +124,11 @@ public final class AllianceOperationHandler extends AbstractMaplePacketHandler { Server.getInstance().resetAllianceGuildPlayersRank(guildid); chr.getMGC().setAllianceRank(2); - Server.getInstance().getGuild(chr.getGuildId()).getMGC(chr.getId()).setAllianceRank(2); + MapleGuild g = Server.getInstance().getGuild(chr.getGuildId()); + if (g != null) { + g.getMGC(chr.getId()).setAllianceRank(2); + } + chr.saveGuildStatus(); Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.addGuildToAlliance(alliance, guildid, c), -1, -1); diff --git a/src/net/server/channel/handlers/AranComboHandler.java b/src/net/server/channel/handlers/AranComboHandler.java index 5490843b6a..1f5de34084 100644 --- a/src/net/server/channel/handlers/AranComboHandler.java +++ b/src/net/server/channel/handlers/AranComboHandler.java @@ -24,7 +24,7 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; import client.SkillFactory; -import constants.GameConstants; +import constants.game.GameConstants; import constants.skills.Aran; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/AutoAssignHandler.java b/src/net/server/channel/handlers/AutoAssignHandler.java index b9e2717c4f..41b5582263 100644 --- a/src/net/server/channel/handlers/AutoAssignHandler.java +++ b/src/net/server/channel/handlers/AutoAssignHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.AssignAPProcessor; +import client.processor.stat.AssignAPProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/BBSOperationHandler.java b/src/net/server/channel/handlers/BBSOperationHandler.java index 153ec2423c..6f97a56e92 100644 --- a/src/net/server/channel/handlers/BBSOperationHandler.java +++ b/src/net/server/channel/handlers/BBSOperationHandler.java @@ -56,7 +56,7 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { String text = correctLength(slea.readMapleAsciiString(), 600); int icon = slea.readInt(); if (icon >= 0x64 && icon <= 0x6a) { - if (c.getPlayer().haveItemWithId(5290000 + icon - 0x64, false)) { + if (!c.getPlayer().haveItemWithId(5290000 + icon - 0x64, false)) { return; } } else if (icon < 0 || icon > 3) { @@ -149,8 +149,8 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { } private static void editBBSThread(MapleClient client, String title, String text, int icon, int localthreadid) { - MapleCharacter c = client.getPlayer(); - if (c.getGuildId() < 1) { + MapleCharacter chr = client.getPlayer(); + if (chr.getGuildId() < 1) { return; } try { @@ -160,10 +160,10 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { ps.setLong(2, currentServerTime()); ps.setInt(3, icon); ps.setString(4, text); - ps.setInt(5, c.getGuildId()); + ps.setInt(5, chr.getGuildId()); ps.setInt(6, localthreadid); - ps.setInt(7, c.getId()); - ps.setBoolean(8, c.getGuildRank() < 3); + ps.setInt(7, chr.getId()); + ps.setBoolean(8, chr.getGuildRank() < 3); ps.execute(); } con.close(); @@ -174,8 +174,8 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { } private static void newBBSThread(MapleClient client, String title, String text, int icon, boolean bNotice) { - MapleCharacter c = client.getPlayer(); - if (c.getGuildId() <= 0) { + MapleCharacter chr = client.getPlayer(); + if (chr.getGuildId() <= 0) { return; } int nextId = 0; @@ -184,7 +184,7 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { PreparedStatement ps; if (!bNotice) { ps = con.prepareStatement("SELECT MAX(localthreadid) AS lastLocalId FROM bbs_threads WHERE guildid = ?"); - ps.setInt(1, c.getGuildId()); + ps.setInt(1, chr.getGuildId()); try (ResultSet rs = ps.executeQuery()) { rs.next(); nextId = rs.getInt("lastLocalId") + 1; @@ -192,12 +192,12 @@ public final class BBSOperationHandler extends AbstractMaplePacketHandler { ps.close(); } ps = con.prepareStatement("INSERT INTO bbs_threads " + "(`postercid`, `name`, `timestamp`, `icon`, `startpost`, " + "`guildid`, `localthreadid`) " + "VALUES(?, ?, ?, ?, ?, ?, ?)"); - ps.setInt(1, c.getId()); + ps.setInt(1, chr.getId()); ps.setString(2, title); ps.setLong(3, currentServerTime()); ps.setInt(4, icon); ps.setString(5, text); - ps.setInt(6, c.getGuildId()); + ps.setInt(6, chr.getGuildId()); ps.setInt(7, nextId); ps.execute(); ps.close(); diff --git a/src/net/server/channel/handlers/CancelChairHandler.java b/src/net/server/channel/handlers/CancelChairHandler.java index 0b6693be51..74a4cdbae9 100644 --- a/src/net/server/channel/handlers/CancelChairHandler.java +++ b/src/net/server/channel/handlers/CancelChairHandler.java @@ -32,8 +32,17 @@ public final class CancelChairHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int id = slea.readShort(); MapleCharacter mc = c.getPlayer(); - if(!mc.isLoggedinWorld()) return; - mc.sitChair(id == -1 ? 0 : id); + if (id >= mc.getMap().getSeats()) { + return; + } + + if (c.tryacquireClient()) { + try { + mc.sitChair(id); + } finally { + c.releaseClient(); + } + } } } diff --git a/src/net/server/channel/handlers/CashOperationHandler.java b/src/net/server/channel/handlers/CashOperationHandler.java index 9ebb36ccb6..ea776e240e 100644 --- a/src/net/server/channel/handlers/CashOperationHandler.java +++ b/src/net/server/channel/handlers/CashOperationHandler.java @@ -28,17 +28,18 @@ import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import java.sql.SQLException; import java.util.Calendar; import java.util.List; import java.util.Map; import net.AbstractMaplePacketHandler; +import net.server.Server; import server.CashShop; import server.CashShop.CashItem; import server.CashShop.CashItemFactory; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.ServerConstants; import server.MapleItemInformationProvider; import tools.FilePrinter; import tools.MaplePacketCreator; @@ -75,7 +76,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { if (ItemConstants.isCashStore(cItem.getItemId()) && chr.getLevel() < 16) { c.enableCSActions(); return; - } else if (ItemConstants.isRateCoupon(cItem.getItemId()) && !ServerConstants.USE_SUPPLY_RATE_COUPONS) { + } else if (ItemConstants.isRateCoupon(cItem.getItemId()) && !YamlConfig.config.server.USE_SUPPLY_RATE_COUPONS) { chr.dropMessage(1, "Rate coupons are currently unavailable to purchase."); c.enableCSActions(); return; @@ -343,7 +344,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { slea.readByte(); MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(sentTo); if (partner == null) { - chr.dropMessage(5, "The partner you specified cannot be found. Please make sure your partner is online and in the same channel."); + c.announce(MaplePacketCreator.showCashShopMessage((byte)0xBE)); } else { // Need to check to make sure its actually an equip and the right SN... if(itemRing.toItem() instanceof Equip) { @@ -351,7 +352,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { Pair rings = MapleRing.createRing(itemRing.getItemId(), chr, partner); eqp.setRingId(rings.getLeft()); cs.addToInventory(eqp); - c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID())); + c.announce(MaplePacketCreator.showBoughtCashRing(eqp, partner.getName(), c.getAccID())); cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight()); cs.gainCash(payment, -itemRing.getPrice()); chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft())); @@ -368,6 +369,65 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } c.announce(MaplePacketCreator.showCash(c.getPlayer())); + } else if (action == 0x2E) { //name change + CashItem cItem = CashItemFactory.getItem(slea.readInt()); + if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + c.enableCSActions(); + return; + } + if(cItem.getSN() == 50600000 && YamlConfig.config.server.ALLOW_CASHSHOP_NAME_CHANGE) { + slea.readMapleAsciiString(); //old name + String newName = slea.readMapleAsciiString(); + if(!MapleCharacter.canCreateChar(newName) || chr.getLevel() < 10) { //(longest ban duration isn't tracked currently) + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + c.enableCSActions(); + return; + } else if(c.getTempBanCalendar() != null && c.getTempBanCalendar().getTimeInMillis() + (30*24*60*60*1000) > Calendar.getInstance().getTimeInMillis()) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + c.enableCSActions(); + return; + } + if(chr.registerNameChange(newName)) { //success + Item item = cItem.toItem(); + c.announce(MaplePacketCreator.showNameChangeSuccess(item, c.getAccID())); + cs.addToInventory(item); + cs.gainCash(4, cItem, chr.getWorld()); + } else { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + } + } + c.enableCSActions(); + } else if(action == 0x31) { //world transfer + CashItem cItem = CashItemFactory.getItem(slea.readInt()); + if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + c.enableCSActions(); + return; + } + if(cItem.getSN() == 50600001 && YamlConfig.config.server.ALLOW_CASHSHOP_WORLD_TRANSFER) { + int newWorldSelection = slea.readInt(); + + int worldTransferError = chr.checkWorldTransferEligibility(); + if(worldTransferError != 0 || newWorldSelection >= Server.getInstance().getWorldsSize() || Server.getInstance().getWorldsSize() <= 1) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + return; + } else if(newWorldSelection == c.getWorld()) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0xDC)); + return; + } else if(c.getAvailableCharacterWorldSlots(newWorldSelection) < 1 || Server.getInstance().getAccountWorldCharacterCount(c.getAccID(), newWorldSelection) >= 3) { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0xDF)); + return; + } else if(chr.registerWorldTransfer(newWorldSelection)) { + Item item = cItem.toItem(); + c.announce(MaplePacketCreator.showWorldTransferSuccess(item, c.getAccID())); + cs.addToInventory(item); + cs.gainCash(4, cItem, chr.getWorld()); + } else { + c.announce(MaplePacketCreator.showCashShopMessage((byte)0)); + } + } + c.enableCSActions(); } else { System.out.println("Unhandled action: " + action + "\n" + slea); } diff --git a/src/net/server/channel/handlers/CashShopSurpriseHandler.java b/src/net/server/channel/handlers/CashShopSurpriseHandler.java index 2b625377da..f5df6a4699 100644 --- a/src/net/server/channel/handlers/CashShopSurpriseHandler.java +++ b/src/net/server/channel/handlers/CashShopSurpriseHandler.java @@ -40,12 +40,10 @@ public class CashShopSurpriseHandler extends AbstractMaplePacketHandler { Pair cssResult = cs.openCashShopSurprise(); if(cssResult != null) { - //Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight(); - //c.announce(MaplePacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); - c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA4)); + Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight(); + c.announce(MaplePacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true)); } else { - //c.announce(MaplePacketCreator.onCashItemGachaponOpenFailed()); - c.announce(MaplePacketCreator.showCashShopMessage((byte) 0x00)); + c.announce(MaplePacketCreator.onCashItemGachaponOpenFailed()); } } } diff --git a/src/net/server/channel/handlers/ChangeMapHandler.java b/src/net/server/channel/handlers/ChangeMapHandler.java index 206b86594a..d3be7548ab 100644 --- a/src/net/server/channel/handlers/ChangeMapHandler.java +++ b/src/net/server/channel/handlers/ChangeMapHandler.java @@ -30,7 +30,7 @@ import client.MapleCharacter; import client.MapleClient; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; -import server.MaplePortal; +import server.maps.MaplePortal; import server.MapleTrade; import server.maps.MapleMap; import tools.FilePrinter; @@ -62,7 +62,6 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler { String[] socket = c.getChannelServer().getIP().split(":"); chr.getCashShop().open(false); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); chr.setSessionTransitionState(); try { c.announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]))); diff --git a/src/net/server/channel/handlers/ChangeMapSpecialHandler.java b/src/net/server/channel/handlers/ChangeMapSpecialHandler.java index fdafc0d709..1c1f6368df 100644 --- a/src/net/server/channel/handlers/ChangeMapSpecialHandler.java +++ b/src/net/server/channel/handlers/ChangeMapSpecialHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import net.AbstractMaplePacketHandler; -import server.MaplePortal; +import server.maps.MaplePortal; import server.MapleTrade; import server.MapleTrade.TradeResult; import tools.MaplePacketCreator; diff --git a/src/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/net/server/channel/handlers/CloseRangeDamageHandler.java index 00dc153da2..d7792beec8 100644 --- a/src/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -25,19 +25,14 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import server.MapleStatEffect; -import tools.MaplePacketCreator; -import tools.Pair; -import tools.data.input.SeekableLittleEndianAccessor; +import config.YamlConfig; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; import client.MapleJob; -import client.MapleStat; import client.Skill; import client.SkillFactory; -import constants.GameConstants; -import constants.ServerConstants; +import constants.game.GameConstants; import constants.skills.Crusader; import constants.skills.DawnWarrior; import constants.skills.DragonKnight; @@ -45,12 +40,16 @@ import constants.skills.Hero; import constants.skills.NightWalker; import constants.skills.Rogue; import constants.skills.WindArcher; +import server.MapleStatEffect; +import tools.MaplePacketCreator; +import tools.Pair; +import tools.data.input.SeekableLittleEndianAccessor; public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - //chr.setPetLootCd(currentServerTime()); /*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8); if(timeElapsed < 300) { @@ -70,7 +69,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { if (chr.getDojoEnergy() < 10000 && (attack.skill == 1009 || attack.skill == 10001009 || attack.skill == 20001009)) // PE hacking or maybe just lagging return; if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { - chr.setDojoEnergy(chr.getDojoEnergy() + ServerConstants.DOJO_ENERGY_ATK); + chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/CouponCodeHandler.java b/src/net/server/channel/handlers/CouponCodeHandler.java index 35b3ee6c5b..2dfb6f86e4 100644 --- a/src/net/server/channel/handlers/CouponCodeHandler.java +++ b/src/net/server/channel/handlers/CouponCodeHandler.java @@ -31,6 +31,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -63,7 +64,6 @@ public final class CouponCodeHandler extends AbstractMaplePacketHandler { ResultSet rs = ps.executeQuery(); while (rs.next()) { int type = rs.getInt("type"), quantity = rs.getInt("quantity"); - if (type < 5) { Integer i = couponPoints.get(type); if (i != null) { @@ -195,8 +195,13 @@ public final class CouponCodeHandler extends AbstractMaplePacketHandler { if (type < 0) { c.announce(MaplePacketCreator.showCashShopMessage((byte) parseCouponResult(type))); } else { - List couponPackage = new LinkedList<>(); - + List cashItems = new LinkedList(); + List> items = new LinkedList>(); + int nxCredit = 0; + int maplePoints = 0; + int nxPrepaid = 0; + int mesos = 0; + for (Pair> p : codeRes.getRight()) { type = p.getLeft(); int quantity = p.getRight().getRight(); @@ -204,18 +209,26 @@ public final class CouponCodeHandler extends AbstractMaplePacketHandler { CashShop cs = c.getPlayer().getCashShop(); switch (type) { case 0: + c.getPlayer().gainMeso(quantity, false); //mesos + mesos += quantity; + break; case 4: cs.gainCash(1, quantity); //nxCredit + nxCredit += quantity; break; case 1: cs.gainCash(2, quantity); //maplePoint + maplePoints += quantity; break; case 2: cs.gainCash(4, quantity); //nxPrepaid + nxPrepaid += quantity; break; case 3: cs.gainCash(1, quantity); + nxCredit += quantity; cs.gainCash(4, (quantity / 5000)); + nxPrepaid += quantity / 5000; break; default: @@ -234,22 +247,28 @@ public final class CouponCodeHandler extends AbstractMaplePacketHandler { Item it = CashShop.generateCouponItem(item, qty); cs.addToInventory(it); - couponPackage.add(it); + cashItems.add(it); } else { MapleInventoryManipulator.addById(c, item, qty, "", -1); + items.add(new Pair((int)qty, item)); } - - //c.announce(MaplePacketCreator.showCouponRedeemedItem(item)); break; } } - - if (!couponPackage.isEmpty()) { - c.announce(MaplePacketCreator.showBoughtCashPackage(couponPackage, c.getAccID())); - } else { - c.announce(MaplePacketCreator.showBoughtQuestItem(0)); + if(cashItems.size() > 255) { + List oldList = cashItems; + cashItems = Arrays.asList(new Item[255]); + int index = 0; + for(Item item : oldList) { + cashItems.set(index, item); + index++; + } + } + if (nxCredit != 0 || nxPrepaid != 0) { //coupon packet can only show maple points (afaik) + c.announce(MaplePacketCreator.showBoughtQuestItem(0)); + } else { + c.announce(MaplePacketCreator.showCouponRedeemedItems(c.getAccID(), maplePoints, mesos, cashItems, items)); } - c.enableCSActions(); } } finally { diff --git a/src/net/server/channel/handlers/DenyPartyRequestHandler.java b/src/net/server/channel/handlers/DenyPartyRequestHandler.java index b6f12b4b71..748804377d 100644 --- a/src/net/server/channel/handlers/DenyPartyRequestHandler.java +++ b/src/net/server/channel/handlers/DenyPartyRequestHandler.java @@ -24,9 +24,9 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -41,7 +41,7 @@ public final class DenyPartyRequestHandler extends AbstractMaplePacketHandler { if (cfrom != null) { MapleCharacter chr = c.getPlayer(); - if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, chr.getId(), cfrom.getPartyId(), false).getLeft() == InviteResult.DENIED) { + if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, chr.getId(), cfrom.getPartyId(), false).result == InviteResult.DENIED) { chr.updatePartySearchAvailability(chr.getParty() == null); cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, chr.getName())); } diff --git a/src/net/server/channel/handlers/DistributeAPHandler.java b/src/net/server/channel/handlers/DistributeAPHandler.java index 3831a15e5d..c83c64158b 100644 --- a/src/net/server/channel/handlers/DistributeAPHandler.java +++ b/src/net/server/channel/handlers/DistributeAPHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.AssignAPProcessor; +import client.processor.stat.AssignAPProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/DistributeSPHandler.java b/src/net/server/channel/handlers/DistributeSPHandler.java index 8e1e570c03..22b6042445 100644 --- a/src/net/server/channel/handlers/DistributeSPHandler.java +++ b/src/net/server/channel/handlers/DistributeSPHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.AssignSPProcessor; +import client.processor.stat.AssignSPProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/DueyHandler.java b/src/net/server/channel/handlers/DueyHandler.java index ae2fc17a6e..0e67dc43e7 100644 --- a/src/net/server/channel/handlers/DueyHandler.java +++ b/src/net/server/channel/handlers/DueyHandler.java @@ -22,8 +22,8 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.DueyProcessor; -import constants.ServerConstants; +import client.processor.npc.DueyProcessor; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.MaplePacketCreator; @@ -33,20 +33,24 @@ public final class DueyHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if (!ServerConstants.USE_DUEY){ + if (!YamlConfig.config.server.USE_DUEY){ c.announce(MaplePacketCreator.enableActions()); return; } - + byte operation = slea.readByte(); - if (operation == DueyProcessor.Actions.TOSERVER_SEND_ITEM.getCode()) { + if (operation == DueyProcessor.Actions.TOSERVER_RECV_ITEM.getCode()) { // on click 'O' Button, thanks inhyuk + DueyProcessor.dueySendTalk(c, false); + } else if (operation == DueyProcessor.Actions.TOSERVER_SEND_ITEM.getCode()) { byte inventId = slea.readByte(); short itemPos = slea.readShort(); short amount = slea.readShort(); int mesos = slea.readInt(); String recipient = slea.readMapleAsciiString(); - String message = slea.readByte() != 0 ? slea.readMapleAsciiString() : ""; - DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient); + boolean quick = slea.readByte() != 0; + String message = quick ? slea.readMapleAsciiString() : null; + + DueyProcessor.dueySendItem(c, inventId, itemPos, amount, mesos, message, recipient, quick); } else if (operation == DueyProcessor.Actions.TOSERVER_REMOVE_PACKAGE.getCode()) { int packageid = slea.readInt(); @@ -55,6 +59,8 @@ public final class DueyHandler extends AbstractMaplePacketHandler { int packageid = slea.readInt(); DueyProcessor.dueyClaimPackage(c, packageid); + } else if (operation == DueyProcessor.Actions.TOSERVER_CLAIM_PACKAGE.getCode()) { + DueyProcessor.dueySendTalk(c, false); } } } diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index b62781026e..402e84ead9 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -28,12 +28,12 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -import constants.ServerConstants; +import config.YamlConfig; import client.MapleCharacter; import client.MapleClient; import client.inventory.Equip; import client.inventory.Item; -import client.processor.BuybackProcessor; +import client.processor.action.BuybackProcessor; import net.AbstractMaplePacketHandler; import net.server.Server; import server.MTSItemInfo; @@ -49,11 +49,11 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - if(!chr.isAlive() && ServerConstants.USE_BUYBACK_SYSTEM) { + if(!chr.isAlive() && YamlConfig.config.server.USE_BUYBACK_SYSTEM) { BuybackProcessor.processBuyback(c); c.announce(MaplePacketCreator.enableActions()); } else { - if (!ServerConstants.USE_MTS) { + if (!YamlConfig.config.server.USE_MTS) { c.announce(MaplePacketCreator.enableActions()); return; } @@ -141,7 +141,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { equip.setInt((short) rs.getInt("int")); equip.setJump((short) rs.getInt("jump")); equip.setVicious((short) rs.getInt("vicious")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setFlag((short) rs.getInt("flag")); equip.setLuk((short) rs.getInt("luk")); equip.setMatk((short) rs.getInt("matk")); equip.setMdef((short) rs.getInt("mdef")); @@ -152,6 +152,12 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); + items.add(new MTSItemInfo((Item) equip, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } @@ -209,7 +215,12 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } @@ -256,7 +267,12 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } diff --git a/src/net/server/channel/handlers/FaceExpressionHandler.java b/src/net/server/channel/handlers/FaceExpressionHandler.java index dd6d0cb577..6a463dd726 100644 --- a/src/net/server/channel/handlers/FaceExpressionHandler.java +++ b/src/net/server/channel/handlers/FaceExpressionHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.MapleCharacter; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; @@ -34,15 +34,17 @@ public final class FaceExpressionHandler extends AbstractMaplePacketHandler { int emote = slea.readInt(); if (emote > 7) { - int emoteid = 5159992 + emote; - if (chr.getInventory(ItemConstants.getInventoryType(emoteid)).findById(emoteid) == null) { + int itemid = 5159992 + emote; // thanks Rajan (Darter) for reporting unchecked emote itemid + if (!ItemConstants.isFaceExpression(itemid) || chr.getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) == null) { return; } + } else if (emote < 1) { + return; } if(c.tryacquireClient()) { try { // expecting players never intends to wear the emote 0 (default face, that changes back after 5sec timeout) - if (emote != 0 && chr.isLoggedinWorld()) { + if (chr.isLoggedinWorld()) { chr.changeFaceExpression(emote); } } finally { diff --git a/src/net/server/channel/handlers/FamilyAddHandler.java b/src/net/server/channel/handlers/FamilyAddHandler.java index cbbf17d308..6c9e0d7fcd 100644 --- a/src/net/server/channel/handlers/FamilyAddHandler.java +++ b/src/net/server/channel/handlers/FamilyAddHandler.java @@ -21,33 +21,50 @@ */ package net.server.channel.handlers; -import constants.ServerConstants; +import config.YamlConfig; import client.MapleCharacter; import client.MapleClient; import net.AbstractMaplePacketHandler; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** * * @author Jay Estrella + * @author Ubaware */ public final class FamilyAddHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if (!ServerConstants.USE_FAMILY_SYSTEM){ - return; - } - System.out.println(slea.toString()); + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) { + return; + } String toAdd = slea.readMapleAsciiString(); MapleCharacter addChr = c.getChannelServer().getPlayerStorage().getCharacterByName(toAdd); - if (addChr != null) { - addChr.getClient().announce(MaplePacketCreator.sendFamilyInvite(c.getPlayer().getId(), toAdd)); - c.getPlayer().dropMessage("The invite has been sent."); + MapleCharacter chr = c.getPlayer(); + if(addChr == null) { + c.announce(MaplePacketCreator.sendFamilyMessage(65, 0)); + } else if(addChr == chr) { //only possible through packet editing/client editing i think? + c.announce(MaplePacketCreator.enableActions()); + } else if(addChr.getMap() != chr.getMap() || (addChr.isHidden()) && chr.gmLevel() < addChr.gmLevel()) { + c.announce(MaplePacketCreator.sendFamilyMessage(69, 0)); + } else if(addChr.getLevel() <= 10) { + c.announce(MaplePacketCreator.sendFamilyMessage(77, 0)); + } else if(Math.abs(addChr.getLevel() - chr.getLevel()) > 20) { + c.announce(MaplePacketCreator.sendFamilyMessage(72, 0)); + } else if(addChr.getFamily() != null && addChr.getFamily() == chr.getFamily()) { //same family + c.announce(MaplePacketCreator.enableActions()); + } else if(MapleInviteCoordinator.hasInvite(InviteType.FAMILY, addChr.getId())) { + c.announce(MaplePacketCreator.sendFamilyMessage(73, 0)); + } else if(chr.getFamily() != null && addChr.getFamily() != null && addChr.getFamily().getTotalGenerations() + chr.getFamily().getTotalGenerations() > YamlConfig.config.server.FAMILY_MAX_GENERATIONS) { + c.announce(MaplePacketCreator.sendFamilyMessage(76, 0)); } else { - c.getPlayer().dropMessage("The player cannot be found!"); + MapleInviteCoordinator.createInvite(InviteType.FAMILY, chr, addChr, addChr.getId()); + addChr.getClient().announce(MaplePacketCreator.sendFamilyInvite(chr.getId(), chr.getName())); + chr.dropMessage("The invite has been sent."); + c.announce(MaplePacketCreator.enableActions()); } - c.announce(MaplePacketCreator.enableActions()); } } - diff --git a/src/net/server/channel/handlers/FamilyPreceptsHandler.java b/src/net/server/channel/handlers/FamilyPreceptsHandler.java new file mode 100644 index 0000000000..7da34c29f2 --- /dev/null +++ b/src/net/server/channel/handlers/FamilyPreceptsHandler.java @@ -0,0 +1,23 @@ +package net.server.channel.handlers; + +import client.MapleClient; +import client.MapleFamily; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +public class FamilyPreceptsHandler extends AbstractMaplePacketHandler { + + @Override + public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + MapleFamily family = c.getPlayer().getFamily(); + if(family == null) return; + if(family.getLeader().getChr() != c.getPlayer()) return; //only the leader can set the precepts + String newPrecepts = slea.readMapleAsciiString(); + if(newPrecepts.length() > 200) return; + family.setMessage(newPrecepts, true); + //family.broadcastFamilyInfoUpdate(); //probably don't need to broadcast for this? + c.announce(MaplePacketCreator.getFamilyInfo(c.getPlayer().getFamilyEntry())); + } + +} diff --git a/src/net/server/channel/handlers/FamilySeparateHandler.java b/src/net/server/channel/handlers/FamilySeparateHandler.java new file mode 100644 index 0000000000..93597f53d1 --- /dev/null +++ b/src/net/server/channel/handlers/FamilySeparateHandler.java @@ -0,0 +1,78 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.channel.handlers; + +import client.MapleClient; +import client.MapleFamily; +import client.MapleFamilyEntry; +import config.YamlConfig; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +public class FamilySeparateHandler extends AbstractMaplePacketHandler { + + @Override + public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) return; + MapleFamily oldFamily = c.getPlayer().getFamily(); + if(oldFamily == null) return; + MapleFamilyEntry forkOn = null; + boolean isSenior; + if(slea.available() > 0) { //packet 0x95 doesn't send id, since there is only one senior + forkOn = c.getPlayer().getFamily().getEntryByID(slea.readInt()); + if(!c.getPlayer().getFamilyEntry().isJunior(forkOn)) return; //packet editing? + isSenior = true; + } else { + forkOn = c.getPlayer().getFamilyEntry(); + isSenior = false; + } + if(forkOn == null) return; + + MapleFamilyEntry senior = forkOn.getSenior(); + if(senior == null) return; + int levelDiff = Math.abs(c.getPlayer().getLevel() - senior.getLevel()); + int cost = 2500 * levelDiff; + cost += levelDiff * levelDiff; + if(c.getPlayer().getMeso() < cost) { + c.announce(MaplePacketCreator.sendFamilyMessage(isSenior ? 81 : 80, cost)); + return; + } + c.getPlayer().gainMeso(-cost); + int repCost = separateRepCost(forkOn); + senior.gainReputation(-repCost, false); + if(senior.getSenior() != null) senior.getSenior().gainReputation(-(repCost/2), false); + forkOn.announceToSenior(MaplePacketCreator.serverNotice(5, forkOn.getName() + " has left the family."), true); + forkOn.fork(); + c.announce(MaplePacketCreator.getFamilyInfo(forkOn)); //pedigree info will be requested by the client if the window is open + forkOn.updateSeniorFamilyInfo(true); + c.announce(MaplePacketCreator.sendFamilyMessage(1, 0)); + } + + + private static int separateRepCost(MapleFamilyEntry junior) { + int level = junior.getLevel(); + int ret = level / 20; + ret += 10; + ret *= level; + ret *= 2; + return ret; + } +} diff --git a/src/net/server/channel/handlers/FamilySummonResponseHandler.java b/src/net/server/channel/handlers/FamilySummonResponseHandler.java new file mode 100644 index 0000000000..9f39b745f8 --- /dev/null +++ b/src/net/server/channel/handlers/FamilySummonResponseHandler.java @@ -0,0 +1,40 @@ +package net.server.channel.handlers; + +import client.MapleCharacter; +import client.MapleClient; +import client.MapleFamilyEntitlement; +import client.MapleFamilyEntry; +import config.YamlConfig; +import net.AbstractMaplePacketHandler; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; +import server.maps.MapleMap; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +public class FamilySummonResponseHandler extends AbstractMaplePacketHandler { + + @Override + public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) return; + slea.readMapleAsciiString(); //family name + boolean accept = slea.readByte() != 0; + MapleInviteResult inviteResult = MapleInviteCoordinator.answerInvite(InviteType.FAMILY_SUMMON, c.getPlayer().getId(), c.getPlayer(), accept); + if(inviteResult.result == InviteResult.NOT_FOUND) return; + MapleCharacter inviter = inviteResult.from; + MapleFamilyEntry inviterEntry = inviter.getFamilyEntry(); + if(inviterEntry == null) return; + MapleMap map = (MapleMap) inviteResult.params[0]; + if(accept && inviter.getMap() == map) { //cancel if inviter has changed maps + c.getPlayer().changeMap(map, map.getPortal(0)); + } else { + inviterEntry.refundEntitlement(MapleFamilyEntitlement.SUMMON_FAMILY); + inviterEntry.gainReputation(MapleFamilyEntitlement.SUMMON_FAMILY.getRepCost(), false); //refund rep cost if declined + inviter.announce(MaplePacketCreator.getFamilyInfo(inviterEntry)); + inviter.dropMessage(5, c.getPlayer().getName() + " has denied the summon request."); + } + } + +} diff --git a/src/net/server/channel/handlers/FamilyUseHandler.java b/src/net/server/channel/handlers/FamilyUseHandler.java index 38af1f49da..344f8b541d 100644 --- a/src/net/server/channel/handlers/FamilyUseHandler.java +++ b/src/net/server/channel/handlers/FamilyUseHandler.java @@ -21,82 +21,121 @@ */ package net.server.channel.handlers; -import constants.ServerConstants; import client.MapleCharacter; import client.MapleClient; +import client.MapleFamilyEntitlement; +import client.MapleFamilyEntry; +import config.YamlConfig; import net.AbstractMaplePacketHandler; -import net.opcodes.SendOpcode; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import server.maps.FieldLimit; +import server.maps.MapleMap; +import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; -import tools.data.output.MaplePacketLittleEndianWriter; /** * * @author Moogra + * @author Ubaware */ public final class FamilyUseHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if (!ServerConstants.USE_FAMILY_SYSTEM){ - return; - } - int[] repCost = {3, 5, 7, 8, 10, 12, 15, 20, 25, 40, 50}; - final int type = slea.readInt(); + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) { + return; + } + MapleFamilyEntitlement type = MapleFamilyEntitlement.values()[slea.readInt()]; + int cost = type.getRepCost(); + MapleFamilyEntry entry = c.getPlayer().getFamilyEntry(); + if(entry.getReputation() < cost || entry.isEntitlementUsed(type)) { + return; // shouldn't even be able to request it + } + c.announce(MaplePacketCreator.getFamilyInfo(entry)); MapleCharacter victim; - if (type == 0 || type == 1) { + if(type == MapleFamilyEntitlement.FAMILY_REUINION || type == MapleFamilyEntitlement.SUMMON_FAMILY) { victim = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString()); - if (victim != null) { - if (type == 0) { - c.getPlayer().changeMap(victim.getMap(), victim.getMap().getPortal(0)); + if(victim != null && victim != c.getPlayer()) { + if(victim.getFamily() == c.getPlayer().getFamily()) { + MapleMap targetMap = victim.getMap(); + MapleMap ownMap = c.getPlayer().getMap(); + if(targetMap != null) { + if(type == MapleFamilyEntitlement.FAMILY_REUINION) { + if(!FieldLimit.CANNOTMIGRATE.check(ownMap.getFieldLimit()) && !FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) + && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000) && targetMap.getEventInstance() == null) { + + c.getPlayer().changeMap(victim.getMap(), victim.getMap().getPortal(0)); + useEntitlement(entry, type); + } else { + c.announce(MaplePacketCreator.sendFamilyMessage(75, 0)); // wrong message, but close enough. (client should check this first anyway) + return; + } + } else { + if(!FieldLimit.CANNOTMIGRATE.check(targetMap.getFieldLimit()) && !FieldLimit.CANNOTVIPROCK.check(ownMap.getFieldLimit()) + && (ownMap.getForcedReturnId() == 999999999 || ownMap.getId() < 100000000) && ownMap.getEventInstance() == null) { + + if(MapleInviteCoordinator.hasInvite(InviteType.FAMILY_SUMMON, victim.getId())) { + c.announce(MaplePacketCreator.sendFamilyMessage(74, 0)); + return; + } + MapleInviteCoordinator.createInvite(InviteType.FAMILY_SUMMON, c.getPlayer(), victim, victim.getId(), c.getPlayer().getMap()); + victim.announce(MaplePacketCreator.sendFamilySummonRequest(c.getPlayer().getFamily().getName(), c.getPlayer().getName())); + useEntitlement(entry, type); + } else { + c.announce(MaplePacketCreator.sendFamilyMessage(75, 0)); + return; + } + } + } } else { - victim.changeMap(c.getPlayer().getMap(), c.getPlayer().getMap().getPortal(0)); + c.announce(MaplePacketCreator.sendFamilyMessage(67, 0)); } - } else { - return; } + } else if(type == MapleFamilyEntitlement.FAMILY_BONDING) { + //not implemented } else { - int erate = type == 3 ? 150 : (type == 4 || type == 6 || type == 8 || type == 10 ? 200 : 100); - int drate = type == 2 ? 150 : (type == 4 || type == 5 || type == 7 || type == 9 ? 200 : 100); - if (type > 8) { - } else { - c.announce(useRep(drate == 100 ? 2 : (erate == 100 ? 3 : 4), type, erate, drate, ((type > 5 || type == 4) ? 2 : 1) * 15 * 60 * 1000)); - } + boolean party = false; + boolean isExp = false; + float rate = 1.5f; + int duration = 15; + do { + switch(type) { + case PARTY_EXP_2_30MIN: + party = true; + isExp = true; + type = MapleFamilyEntitlement.SELF_EXP_2_30MIN; + continue; + case PARTY_DROP_2_30MIN: + party = true; + type = MapleFamilyEntitlement.SELF_DROP_2_30MIN; + continue; + case SELF_DROP_2_30MIN: + duration = 30; + case SELF_DROP_2: + rate = 2.0f; + case SELF_DROP_1_5: + break; + case SELF_EXP_2_30MIN: + duration = 30; + case SELF_EXP_2: + rate = 2.0f; + case SELF_EXP_1_5: + isExp = true; + default: + break; + } + break; + } while(true); + //not implemented } - c.getPlayer().getFamily().getMember(c.getPlayer().getId()).gainReputation(repCost[type]); } - - /** - * [65 00][02][08 00 00 00][C8 00 00 00][00 00 00 00][00][40 77 1B 00] - */ - private static byte[] useRep(int mode, int type, int erate, int drate, int time) { - MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(0x60);//noty - mplew.write(mode); - mplew.writeInt(type); - if (mode < 4) { - mplew.writeInt(erate); - mplew.writeInt(drate); + + private boolean useEntitlement(MapleFamilyEntry entry, MapleFamilyEntitlement entitlement) { + if(entry.useEntitlement(entitlement)) { + entry.gainReputation(-entitlement.getRepCost(), false); + entry.getChr().announce(MaplePacketCreator.getFamilyInfo(entry)); + return true; } - mplew.write(0); - mplew.writeInt(time); - return mplew.getPacket(); - } - - //20 00 - //00 00 00 00 - //00 00 00 00 00 00 00 00 - //80 01 - //00 00 28 00 - //8C 93 3E 00 - //40 0D - //03 00 14 00 - //8C 93 3E 00 - //40 0D 03 00 00 00 00 00 02 - private static byte[] giveBuff() { - MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.GIVE_BUFF.getValue()); - mplew.writeInt(0); - mplew.writeLong(0); - - return null; + return false; } } diff --git a/src/net/server/channel/handlers/FieldDamageMobHandler.java b/src/net/server/channel/handlers/FieldDamageMobHandler.java new file mode 100644 index 0000000000..0a6942319d --- /dev/null +++ b/src/net/server/channel/handlers/FieldDamageMobHandler.java @@ -0,0 +1,59 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.channel.handlers; + +import client.MapleCharacter; +import client.MapleClient; +import constants.game.GameConstants; +import net.AbstractMaplePacketHandler; +import server.life.MapleMonster; +import server.life.MapleMonsterInformationProvider; +import server.maps.MapleMap; +import tools.FilePrinter; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +public class FieldDamageMobHandler extends AbstractMaplePacketHandler { + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + int mobOid = slea.readInt(); // packet structure found thanks to Darter (Rajan) + int dmg = slea.readInt(); + + MapleCharacter chr = c.getPlayer(); + MapleMap map = chr.getMap(); + + if (map.getEnvironment().isEmpty()) { // no environment objects activated to actually hit the mob + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use an obstacle on mapid " + map.getId() + " to attack."); + return; + } + + MapleMonster mob = map.getMonsterByOid(mobOid); + if (mob != null) { + if (dmg < 0 || dmg > GameConstants.MAX_FIELD_MOB_DAMAGE) { + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use an obstacle on mapid " + map.getId() + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(mob.getId()) + " with damage " + dmg); + return; + } + + map.broadcastMessage(chr, MaplePacketCreator.damageMonster(mobOid, dmg), true); + map.damageMonster(chr, mob, dmg); + } + } +} diff --git a/src/net/server/channel/handlers/FredrickHandler.java b/src/net/server/channel/handlers/FredrickHandler.java index ef516098a0..1e035ded11 100644 --- a/src/net/server/channel/handlers/FredrickHandler.java +++ b/src/net/server/channel/handlers/FredrickHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; -import client.processor.FredrickProcessor; +import client.processor.npc.FredrickProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/GeneralChatHandler.java b/src/net/server/channel/handlers/GeneralChatHandler.java index 5fbea4eb43..6f56b1a16b 100644 --- a/src/net/server/channel/handlers/GeneralChatHandler.java +++ b/src/net/server/channel/handlers/GeneralChatHandler.java @@ -25,7 +25,7 @@ import client.MapleCharacter; import client.MapleClient; import client.autoban.AutobanFactory; import client.command.CommandsExecutor; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.FilePrinter; import tools.LogHelper; @@ -59,12 +59,12 @@ public final class GeneralChatHandler extends AbstractMaplePacketHandler { if (!chr.isHidden()) { chr.getMap().broadcastMessage(MaplePacketCreator.getChatText(chr.getId(), s, chr.getWhiteChat(), show)); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "General", s); } } else { chr.getMap().broadcastGMMessage(MaplePacketCreator.getChatText(chr.getId(), s, chr.getWhiteChat(), show)); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "GM General", s); } } diff --git a/src/net/server/channel/handlers/GuildOperationHandler.java b/src/net/server/channel/handlers/GuildOperationHandler.java index cb1a41e189..92991f4269 100644 --- a/src/net/server/channel/handlers/GuildOperationHandler.java +++ b/src/net/server/channel/handlers/GuildOperationHandler.java @@ -21,10 +21,11 @@ */ package net.server.channel.handlers; +import config.YamlConfig; import net.server.guild.MapleGuildResponse; import net.server.guild.MapleGuild; -import constants.GameConstants; -import constants.ServerConstants; +import constants.game.GameConstants; +import constants.net.ServerConstants; import client.MapleClient; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; @@ -65,8 +66,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler { mc.dropMessage(1, "You cannot create a new Guild while in one."); return; } - if (mc.getMeso() < ServerConstants.CREATE_GUILD_COST) { - mc.dropMessage(1, "You do not have " + GameConstants.numberWithCommas(ServerConstants.CREATE_GUILD_COST) + " mesos to create a Guild."); + if (mc.getMeso() < YamlConfig.config.server.CREATE_GUILD_COST) { + mc.dropMessage(1, "You do not have " + GameConstants.numberWithCommas(YamlConfig.config.server.CREATE_GUILD_COST) + " mesos to create a Guild."); return; } String guildName = slea.readMapleAsciiString(); @@ -76,8 +77,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler { } Set eligibleMembers = new HashSet<>(MapleGuild.getEligiblePlayersForGuild(mc)); - if (eligibleMembers.size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) { - if (mc.getMap().getAllPlayers().size() < ServerConstants.CREATE_GUILD_MIN_PARTNERS) { + if (eligibleMembers.size() < YamlConfig.config.server.CREATE_GUILD_MIN_PARTNERS) { + if (mc.getMap().getAllPlayers().size() < YamlConfig.config.server.CREATE_GUILD_MIN_PARTNERS) { // thanks NovaStory for noticing message in need of smoother info mc.dropMessage(1, "Your Guild doesn't have enough cofounders present here and therefore cannot be created at this time."); } else { @@ -211,8 +212,8 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler { System.out.println("[Hack] " + mc.getName() + " tried to change guild emblem without being the guild leader."); return; } - if (mc.getMeso() < ServerConstants.CHANGE_EMBLEM_COST) { - c.announce(MaplePacketCreator.serverNotice(1, "You do not have " + GameConstants.numberWithCommas(ServerConstants.CHANGE_EMBLEM_COST) + " mesos to change the Guild emblem.")); + if (mc.getMeso() < YamlConfig.config.server.CHANGE_EMBLEM_COST) { + c.announce(MaplePacketCreator.serverNotice(1, "You do not have " + GameConstants.numberWithCommas(YamlConfig.config.server.CHANGE_EMBLEM_COST) + " mesos to change the Guild emblem.")); return; } short bg = slea.readShort(); @@ -226,7 +227,7 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler { Server.getInstance().allianceMessage(alliance.getId(), MaplePacketCreator.getGuildAlliances(alliance, c.getWorld()), -1, -1); } - mc.gainMeso(-ServerConstants.CHANGE_EMBLEM_COST, true, false, true); + mc.gainMeso(-YamlConfig.config.server.CHANGE_EMBLEM_COST, true, false, true); mc.getGuild().broadcastNameChanged(); mc.getGuild().broadcastEmblemChanged(); break; diff --git a/src/net/server/channel/handlers/HealOvertimeHandler.java b/src/net/server/channel/handlers/HealOvertimeHandler.java index 70c5157c48..35ab9233d0 100644 --- a/src/net/server/channel/handlers/HealOvertimeHandler.java +++ b/src/net/server/channel/handlers/HealOvertimeHandler.java @@ -27,7 +27,7 @@ import client.autoban.AutobanFactory; import client.autoban.AutobanManager; import net.AbstractMaplePacketHandler; import net.server.Server; -import server.maps.MapleMapFactory; +import server.maps.MapleMap; import tools.data.input.SeekableLittleEndianAccessor; import tools.MaplePacketCreator; @@ -46,7 +46,8 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { abm.setTimestamp(8, timestamp, 28); // thanks Vcoc & Thora for pointing out d/c happening here if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing"); - int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa... + MapleMap map = chr.getMap(); + int abHeal = (int)(77 * map.getRecovery() * 1.5); // thanks Ari for noticing players not getting healed in sauna in certain cases if (healHP > abHeal) { AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + "."); return; @@ -59,7 +60,10 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { short healMP = slea.readShort(); if (healMP != 0 && healMP < 1000) { abm.setTimestamp(9, timestamp, 28); - if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing"); + if ((abm.getLastSpam(1) + 1500) > timestamp) { + AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing"); + return; // thanks resinate for noticing mp being gained even after detection + } chr.addMP(healMP); abm.spam(1, timestamp); } diff --git a/src/net/server/channel/handlers/HiredMerchantRequest.java b/src/net/server/channel/handlers/HiredMerchantRequest.java index 29fe4a2b48..160cdfff0d 100644 --- a/src/net/server/channel/handlers/HiredMerchantRequest.java +++ b/src/net/server/channel/handlers/HiredMerchantRequest.java @@ -26,9 +26,13 @@ import client.MapleCharacter; import java.sql.SQLException; import java.util.Arrays; import client.MapleClient; -import constants.GameConstants; +import constants.game.GameConstants; +import java.awt.Point; import net.AbstractMaplePacketHandler; +import server.maps.MaplePortal; +import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; +import server.maps.MaplePlayerShop; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -40,7 +44,34 @@ public final class HiredMerchantRequest extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && (GameConstants.isFreeMarketRoom(chr.getMapId()))) { + + try { + for (MapleMapObject mmo : chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT, MapleMapObjectType.PLAYER))) { + if (mmo instanceof MapleCharacter) { + MapleCharacter mc = (MapleCharacter) mmo; + + MaplePlayerShop shop = mc.getPlayerShop(); + if (shop != null && shop.isOwner(mc)) { + chr.announce(MaplePacketCreator.getMiniRoomError(13)); + return; + } + } else { + chr.announce(MaplePacketCreator.getMiniRoomError(13)); + return; + } + } + + Point cpos = chr.getPosition(); + MaplePortal portal = chr.getMap().findClosestTeleportPortal(cpos); + if (portal != null && portal.getPosition().distance(cpos) < 120.0) { + chr.announce(MaplePacketCreator.getMiniRoomError(10)); + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + + if (GameConstants.isFreeMarketRoom(chr.getMapId())) { if (!chr.hasMerchant()) { try { if (ItemFactory.MERCHANT.loadItems(chr.getId(), false).isEmpty() && chr.getMerchantMeso() == 0) { diff --git a/src/net/server/channel/handlers/InventoryMergeHandler.java b/src/net/server/channel/handlers/InventoryMergeHandler.java index 43ec9f437e..ae8c2feb3b 100644 --- a/src/net/server/channel/handlers/InventoryMergeHandler.java +++ b/src/net/server/channel/handlers/InventoryMergeHandler.java @@ -21,7 +21,7 @@ */ package net.server.channel.handlers; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; import tools.MaplePacketCreator; @@ -42,7 +42,7 @@ public final class InventoryMergeHandler extends AbstractMaplePacketHandler { slea.readInt(); chr.getAutobanManager().setTimestamp(2, Server.getInstance().getCurrentTimestamp(), 4); - if(!ServerConstants.USE_ITEM_SORT) { + if(!YamlConfig.config.server.USE_ITEM_SORT) { c.announce(MaplePacketCreator.enableActions()); return; } diff --git a/src/net/server/channel/handlers/InventorySortHandler.java b/src/net/server/channel/handlers/InventorySortHandler.java index c48a8d9390..d7510405f8 100644 --- a/src/net/server/channel/handlers/InventorySortHandler.java +++ b/src/net/server/channel/handlers/InventorySortHandler.java @@ -24,6 +24,7 @@ package net.server.channel.handlers; import java.util.ArrayList; import java.util.List; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -34,7 +35,7 @@ import client.inventory.Equip; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.ModifyInventory; -import constants.ServerConstants; +import constants.net.ServerConstants; import server.MapleItemInformationProvider; import net.server.Server; @@ -265,7 +266,7 @@ public final class InventorySortHandler extends AbstractMaplePacketHandler { slea.readInt(); chr.getAutobanManager().setTimestamp(3, Server.getInstance().getCurrentTimestamp(), 4); - if(!ServerConstants.USE_ITEM_SORT) { + if(!YamlConfig.config.server.USE_ITEM_SORT) { c.announce(MaplePacketCreator.enableActions()); return; } @@ -295,7 +296,7 @@ public final class InventorySortHandler extends AbstractMaplePacketHandler { } int invTypeCriteria = (MapleInventoryType.getByType(invType) == MapleInventoryType.EQUIP) ? 3 : 1; - int sortCriteria = (ServerConstants.USE_ITEM_SORT_BY_NAME == true) ? 2 : 0; + int sortCriteria = (YamlConfig.config.server.USE_ITEM_SORT_BY_NAME == true) ? 2 : 0; PairedQuicksort pq = new PairedQuicksort(itemarray, sortCriteria, invTypeCriteria); for (Item item : itemarray) { diff --git a/src/net/server/channel/handlers/ItemRewardHandler.java b/src/net/server/channel/handlers/ItemRewardHandler.java index 2794c1aedd..6fc7586ecf 100644 --- a/src/net/server/channel/handlers/ItemRewardHandler.java +++ b/src/net/server/channel/handlers/ItemRewardHandler.java @@ -24,7 +24,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import java.util.List; import net.AbstractMaplePacketHandler; import net.server.Server; diff --git a/src/net/server/channel/handlers/KeymapChangeHandler.java b/src/net/server/channel/handlers/KeymapChangeHandler.java index 5613ddaef6..7d97aa108c 100644 --- a/src/net/server/channel/handlers/KeymapChangeHandler.java +++ b/src/net/server/channel/handlers/KeymapChangeHandler.java @@ -21,7 +21,7 @@ */ package net.server.channel.handlers; -import constants.GameConstants; +import constants.game.GameConstants; import client.MapleClient; import client.MapleKeyBinding; import client.Skill; diff --git a/src/net/server/channel/handlers/MTSHandler.java b/src/net/server/channel/handlers/MTSHandler.java index 054e6f7261..50ca166f63 100644 --- a/src/net/server/channel/handlers/MTSHandler.java +++ b/src/net/server/channel/handlers/MTSHandler.java @@ -44,7 +44,7 @@ import client.MapleClient; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; public final class MTSHandler extends AbstractMaplePacketHandler { @@ -160,48 +160,55 @@ public final class MTSHandler extends AbstractMaplePacketHandler { } if (!i.getInventoryType().equals(MapleInventoryType.EQUIP)) { Item item = (Item) i; - ps = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, seller, price, owner, sellername, sell_ends) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + ps = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, expiration, giftFrom, seller, price, owner, sellername, sell_ends) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); ps.setInt(1, 1); ps.setInt(2, (int) invType.getType()); ps.setInt(3, item.getItemId()); ps.setInt(4, quantity); - ps.setInt(5, c.getPlayer().getId()); - ps.setInt(6, price); - ps.setString(7, item.getOwner()); - ps.setString(8, c.getPlayer().getName()); - ps.setString(9, date); + ps.setLong(5, item.getExpiration()); + ps.setString(6, item.getGiftFrom()); + ps.setInt(7, c.getPlayer().getId()); + ps.setInt(8, price); + ps.setString(9, item.getOwner()); + ps.setString(10, c.getPlayer().getName()); + ps.setString(11, date); } else { Equip equip = (Equip) i; - ps = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, seller, price, upgradeslots, level, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, locked, owner, sellername, sell_ends, vicious, flag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + ps = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, expiration, giftFrom, seller, price, upgradeslots, level, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, locked, owner, sellername, sell_ends, vicious, flag, itemexp, itemlevel, ringid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); ps.setInt(1, 1); ps.setInt(2, (int) invType.getType()); ps.setInt(3, equip.getItemId()); ps.setInt(4, quantity); - ps.setInt(5, c.getPlayer().getId()); - ps.setInt(6, price); - ps.setInt(7, equip.getUpgradeSlots()); - ps.setInt(8, equip.getLevel()); - ps.setInt(9, equip.getStr()); - ps.setInt(10, equip.getDex()); - ps.setInt(11, equip.getInt()); - ps.setInt(12, equip.getLuk()); - ps.setInt(13, equip.getHp()); - ps.setInt(14, equip.getMp()); - ps.setInt(15, equip.getWatk()); - ps.setInt(16, equip.getMatk()); - ps.setInt(17, equip.getWdef()); - ps.setInt(18, equip.getMdef()); - ps.setInt(19, equip.getAcc()); - ps.setInt(20, equip.getAvoid()); - ps.setInt(21, equip.getHands()); - ps.setInt(22, equip.getSpeed()); - ps.setInt(23, equip.getJump()); - ps.setInt(24, 0); - ps.setString(25, equip.getOwner()); - ps.setString(26, c.getPlayer().getName()); - ps.setString(27, date); - ps.setInt(28, equip.getVicious()); - ps.setInt(29, equip.getFlag()); + ps.setLong(5, equip.getExpiration()); + ps.setString(6, equip.getGiftFrom()); + ps.setInt(7, c.getPlayer().getId()); + ps.setInt(8, price); + ps.setInt(9, equip.getUpgradeSlots()); + ps.setInt(10, equip.getLevel()); + ps.setInt(11, equip.getStr()); + ps.setInt(12, equip.getDex()); + ps.setInt(13, equip.getInt()); + ps.setInt(14, equip.getLuk()); + ps.setInt(15, equip.getHp()); + ps.setInt(16, equip.getMp()); + ps.setInt(17, equip.getWatk()); + ps.setInt(18, equip.getMatk()); + ps.setInt(19, equip.getWdef()); + ps.setInt(20, equip.getMdef()); + ps.setInt(21, equip.getAcc()); + ps.setInt(22, equip.getAvoid()); + ps.setInt(23, equip.getHands()); + ps.setInt(24, equip.getSpeed()); + ps.setInt(25, equip.getJump()); + ps.setInt(26, 0); + ps.setString(27, equip.getOwner()); + ps.setString(28, c.getPlayer().getName()); + ps.setString(29, date); + ps.setInt(30, equip.getVicious()); + ps.setInt(31, equip.getFlag()); + ps.setInt(32, equip.getItemExp()); + ps.setByte(33, equip.getItemLevel()); // thanks Jefe for noticing missing itemlevel labels + ps.setInt(34, equip.getRingId()); } ps.executeUpdate(); ps.close(); @@ -320,8 +327,13 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); equip.setVicious((byte) rs.getInt("vicious")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); equip.setPosition(c.getPlayer().getInventory(ItemConstants.getInventoryType(rs.getInt("itemid"))).getNextFreeSlot()); i = equip.copy(); } @@ -568,7 +580,12 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setFlag((short) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } @@ -623,7 +640,12 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rse.getInt("wdef")); equip.setUpgradeSlots((byte) rse.getInt("upgradeslots")); equip.setLevel((byte) rse.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rse.getInt("price"), rse.getInt("id"), rse.getInt("seller"), rse.getString("sellername"), rse.getString("sell_ends"))); } } @@ -686,7 +708,12 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } @@ -747,7 +774,12 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } @@ -841,7 +873,12 @@ public final class MTSHandler extends AbstractMaplePacketHandler { equip.setWdef((short) rs.getInt("wdef")); equip.setUpgradeSlots((byte) rs.getInt("upgradeslots")); equip.setLevel((byte) rs.getInt("level")); - equip.setFlag((byte) rs.getInt("flag")); + equip.setItemLevel(rs.getByte("itemlevel")); + equip.setItemExp(rs.getInt("itemexp")); + equip.setRingId(rs.getInt("ringid")); + equip.setFlag((short) rs.getInt("flag")); + equip.setExpiration(rs.getLong("expiration")); + equip.setGiftFrom(rs.getString("giftFrom")); items.add(new MTSItemInfo((Item) equip, rs.getInt("price"), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends"))); } } diff --git a/src/net/server/channel/handlers/MagicDamageHandler.java b/src/net/server/channel/handlers/MagicDamageHandler.java index b2288be8a6..fa82dbfd1c 100644 --- a/src/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/net/server/channel/handlers/MagicDamageHandler.java @@ -21,25 +21,24 @@ */ package net.server.channel.handlers; -import server.MapleStatEffect; -import tools.MaplePacketCreator; -import tools.data.input.SeekableLittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; import client.Skill; import client.SkillFactory; -import constants.ServerConstants; +import config.YamlConfig; import constants.skills.Bishop; import constants.skills.Evan; import constants.skills.FPArchMage; import constants.skills.ILArchMage; +import server.MapleStatEffect; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; public final class MagicDamageHandler extends AbstractDealDamageHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - //chr.setPetLootCd(currentServerTime()); /*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8); if(timeElapsed < 300) { @@ -58,7 +57,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler { } if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { - chr.setDojoEnergy(chr.getDojoEnergy() + + ServerConstants.DOJO_ENERGY_ATK); + chr.setDojoEnergy(chr.getDojoEnergy() + + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/MakerSkillHandler.java b/src/net/server/channel/handlers/MakerSkillHandler.java index 77062f96fb..2a108e64d0 100644 --- a/src/net/server/channel/handlers/MakerSkillHandler.java +++ b/src/net/server/channel/handlers/MakerSkillHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.MakerProcessor; +import client.processor.action.MakerProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/MessengerHandler.java b/src/net/server/channel/handlers/MessengerHandler.java index 3b2539afbf..e30b9a8aca 100644 --- a/src/net/server/channel/handlers/MessengerHandler.java +++ b/src/net/server/channel/handlers/MessengerHandler.java @@ -24,14 +24,14 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; import net.server.world.MapleMessenger; import net.server.world.MapleMessengerCharacter; import net.server.world.World; import tools.MaplePacketCreator; -import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; public final class MessengerHandler extends AbstractMaplePacketHandler { @@ -58,8 +58,8 @@ public final class MessengerHandler extends AbstractMaplePacketHandler { } else { messenger = world.getMessenger(messengerid); if (messenger != null) { - Pair inviteRes = MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, true); - InviteResult res = inviteRes.getLeft(); + MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, true); + InviteResult res = inviteRes.result; if (res == InviteResult.ACCEPTED) { int position = messenger.getLowestPosition(); MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, position); diff --git a/src/net/server/channel/handlers/MobDamageMobFriendlyHandler.java b/src/net/server/channel/handlers/MobDamageMobFriendlyHandler.java index 2ef5516842..a94184d620 100644 --- a/src/net/server/channel/handlers/MobDamageMobFriendlyHandler.java +++ b/src/net/server/channel/handlers/MobDamageMobFriendlyHandler.java @@ -22,6 +22,7 @@ package net.server.channel.handlers; import net.AbstractMaplePacketHandler; +import scripting.event.EventInstanceManager; import server.life.MapleMonster; import server.maps.MapleMap; import tools.MaplePacketCreator; @@ -40,9 +41,11 @@ public final class MobDamageMobFriendlyHandler extends AbstractMaplePacketHandle int attacker = slea.readInt(); slea.readInt(); int damaged = slea.readInt(); - MapleMonster monster = c.getPlayer().getMap().getMonsterByOid(damaged); + + MapleMap map = c.getPlayer().getMap(); + MapleMonster monster = map.getMonsterByOid(damaged); - if (monster == null || c.getPlayer().getMap().getMonsterByOid(attacker) == null) { + if (monster == null || map.getMonsterByOid(attacker) == null) { return; } @@ -50,30 +53,37 @@ public final class MobDamageMobFriendlyHandler extends AbstractMaplePacketHandle if (monster.getHp() - damage < 1) { // friendly dies if(monster.getId() == 9300102) { - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "The Watch Hog has been injured by the aliens. Better luck next time...")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "The Watch Hog has been injured by the aliens. Better luck next time...")); } else if (monster.getId() == 9300061) { //moon bunny - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "The Moon Bunny went home because he was sick.")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "The Moon Bunny went home because he was sick.")); } else if(monster.getId() == 9300093) { //tylus - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Tylus has fallen by the overwhelming forces of the ambush.")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "Tylus has fallen by the overwhelming forces of the ambush.")); } else if(monster.getId() == 9300137) { //juliet - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Juliet has fainted in the middle of the combat.")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "Juliet has fainted in the middle of the combat.")); } else if(monster.getId() == 9300138) { //romeo - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Romeo has fainted in the middle of the combat.")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "Romeo has fainted in the middle of the combat.")); } else if(monster.getId() == 9400322 || monster.getId() == 9400327 || monster.getId() == 9400332) { //snowman - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "The Snowman has melted on the heat of the battle.")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "The Snowman has melted on the heat of the battle.")); } else if(monster.getId() == 9300162) { //delli - monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Delli vanished after the ambush, sheets still laying on the ground...")); + map.broadcastMessage(MaplePacketCreator.serverNotice(6, "Delli vanished after the ambush, sheets still laying on the ground...")); } - c.getPlayer().getMap().killFriendlies(monster); + map.killFriendlies(monster); } else { - if (monster.getId() == 9300061) { - MapleMap map = c.getPlayer().getEventInstance().getMapInstance(monster.getMap().getId()); - map.addBunnyHit(); + EventInstanceManager eim = map.getEventInstance(); + if (eim != null) { + eim.friendlyDamaged(monster); } } + + monster.applyAndGetHpDamage(damage, false); + int remainingHp = monster.getHp(); + if(remainingHp <= 0) { + remainingHp = 0; + map.removeMapObject(monster); + } - c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.MobDamageMobFriendly(monster, damage), monster.getPosition()); + map.broadcastMessage(MaplePacketCreator.MobDamageMobFriendly(monster, damage, remainingHp), monster.getPosition()); c.announce(MaplePacketCreator.enableActions()); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/MobDamageMobHandler.java b/src/net/server/channel/handlers/MobDamageMobHandler.java index e3e11cebac..f6d8ac7b0d 100644 --- a/src/net/server/channel/handlers/MobDamageMobHandler.java +++ b/src/net/server/channel/handlers/MobDamageMobHandler.java @@ -21,16 +21,24 @@ */ package net.server.channel.handlers; +import java.util.Map; import client.MapleClient; import client.MapleCharacter; +import client.autoban.AutobanFactory; +import client.status.MonsterStatus; +import client.status.MonsterStatusEffect; import net.AbstractMaplePacketHandler; +import server.life.MapleMonster; +import server.life.MapleMonsterInformationProvider; import server.maps.MapleMap; +import tools.FilePrinter; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** * * @author Jay Estrella + * @author Ronan */ public final class MobDamageMobHandler extends AbstractMaplePacketHandler { @Override @@ -38,14 +46,69 @@ public final class MobDamageMobHandler extends AbstractMaplePacketHandler { int from = slea.readInt(); slea.readInt(); int to = slea.readInt(); - slea.readByte(); + boolean magic = slea.readByte() == 0; int dmg = slea.readInt(); MapleCharacter chr = c.getPlayer(); MapleMap map = chr.getMap(); - if (map.getMonsterByOid(from) != null && map.getMonsterByOid(to) != null) { - map.damageMonster(chr, map.getMonsterByOid(to), dmg); - map.broadcastMessage(MaplePacketCreator.damageMonster(to, dmg)); + MapleMonster attacker = map.getMonsterByOid(from); + MapleMonster damaged = map.getMonsterByOid(to); + + if (attacker != null && damaged != null) { + int maxDmg = calcMaxDamage(attacker, damaged, magic); // thanks Darter (YungMoozi) for reporting unchecked dmg + + if (dmg > maxDmg) { + AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing hypnotize damage exploit."); // thanks Rien dev team + + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " had hypnotized " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId()) + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId()) + " with damage " + dmg + " (max: " + maxDmg + ")"); + dmg = maxDmg; + } + + map.damageMonster(chr, damaged, dmg); + map.broadcastMessage(chr, MaplePacketCreator.damageMonster(to, dmg), false); } } + + private static int calcMaxDamage(MapleMonster attacker, MapleMonster damaged, boolean magic) { + int attackerAtk, damagedDef, attackerLevel = attacker.getLevel(); + double maxDamage; + if (magic) { + int atkRate = calcModifier(attacker, MonsterStatus.MAGIC_ATTACK_UP, MonsterStatus.MATK); + attackerAtk = (attacker.getStats().getMADamage() * atkRate) / 100; + + int defRate = calcModifier(damaged, MonsterStatus.MAGIC_DEFENSE_UP, MonsterStatus.MDEF); + damagedDef = (damaged.getStats().getMDDamage() * defRate) / 100; + + maxDamage = ((attackerAtk * (1.15 + (0.025 * attackerLevel))) - (0.75 * damagedDef)) * (Math.log(Math.abs(damagedDef - attackerAtk)) / Math.log(12)); + } else { + int atkRate = calcModifier(attacker, MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WATK); + attackerAtk = (attacker.getStats().getPADamage() * atkRate) / 100; + + int defRate = calcModifier(damaged, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.WDEF); + damagedDef = (damaged.getStats().getPDDamage() * defRate) / 100; + + maxDamage = ((attackerAtk * (1.15 + (0.025 * attackerLevel))) - (0.75 * damagedDef)) * (Math.log(Math.abs(damagedDef - attackerAtk)) / Math.log(17)); + } + + return (int) maxDamage; + } + + private static int calcModifier(MapleMonster monster, MonsterStatus buff, MonsterStatus nerf) { + int atkModifier; + final Map monsterStati = monster.getStati(); + + MonsterStatusEffect atkBuff = monsterStati.get(buff); + if (atkBuff != null) { + atkModifier = atkBuff.getStati().get(buff); + } else { + atkModifier = 100; + } + + MonsterStatusEffect atkNerf = monsterStati.get(nerf); + if (atkNerf != null) { + atkModifier -= atkNerf.getStati().get(nerf); + } + + return atkModifier; + } } diff --git a/src/net/server/channel/handlers/MonsterCarnivalHandler.java b/src/net/server/channel/handlers/MonsterCarnivalHandler.java index 9b723af165..7878dd3254 100644 --- a/src/net/server/channel/handlers/MonsterCarnivalHandler.java +++ b/src/net/server/channel/handlers/MonsterCarnivalHandler.java @@ -101,16 +101,19 @@ public final class MonsterCarnivalHandler extends AbstractMaplePacketHandler { final MapleDisease dis = skill.getDisease(); MapleParty enemies = c.getPlayer().getParty().getEnemy(); if (skill.targetsAll) { - int chanceAcerto = 0; + int hitChance = 0; if (dis.getDisease() == 121 || dis.getDisease() == 122 || dis.getDisease() == 125 || dis.getDisease() == 126) { - chanceAcerto = (int) (Math.random() * 100); + hitChance = (int) (Math.random() * 100); } - if (chanceAcerto <= 80) { - for (MaplePartyCharacter chrS : enemies.getPartyMembers()) { - if (dis == null) { - chrS.getPlayer().dispel(); - } else { - chrS.getPlayer().giveDebuff(dis, skill.getSkill()); + if (hitChance <= 80) { + for (MaplePartyCharacter mpc : enemies.getPartyMembers()) { + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + if (dis == null) { + mc.dispel(); + } else { + mc.giveDebuff(dis, skill.getSkill()); + } } } } diff --git a/src/net/server/channel/handlers/MoveDragonHandler.java b/src/net/server/channel/handlers/MoveDragonHandler.java index c806b82d25..d66e0abac0 100644 --- a/src/net/server/channel/handlers/MoveDragonHandler.java +++ b/src/net/server/channel/handlers/MoveDragonHandler.java @@ -21,14 +21,14 @@ along with this program. If not, see . */ package net.server.channel.handlers; +import java.awt.Point; + import client.MapleCharacter; import client.MapleClient; -import java.awt.Point; -import java.util.List; import server.maps.MapleDragon; -import server.movement.LifeMovementFragment; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.exceptions.EmptyMovementException; public class MoveDragonHandler extends AbstractMovementPacketHandler { @@ -36,15 +36,20 @@ public class MoveDragonHandler extends AbstractMovementPacketHandler { public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { final MapleCharacter chr = c.getPlayer(); final Point startPos = new Point(slea.readShort(), slea.readShort()); - List res = parseMovement(slea); final MapleDragon dragon = chr.getDragon(); - if (dragon != null && res != null && res.size() > 0) { - updatePosition(res, dragon, 0); - if (chr.isHidden()) { - chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, res)); - } else { - chr.getMap().broadcastMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, res), dragon.getPosition()); - } + if (dragon != null) { + try { + long movementDataStart = slea.getPosition(); + updatePosition(slea, dragon, 0); + long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition + slea.seek(movementDataStart); + + if (chr.isHidden()) { + chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength)); + } else { + chr.getMap().broadcastMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength), dragon.getPosition()); + } + } catch (EmptyMovementException e) {} } } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/MoveLifeHandler.java b/src/net/server/channel/handlers/MoveLifeHandler.java index 727859343d..ebfd0a20ff 100644 --- a/src/net/server/channel/handlers/MoveLifeHandler.java +++ b/src/net/server/channel/handlers/MoveLifeHandler.java @@ -26,7 +26,8 @@ import client.MapleClient; import java.awt.Point; import java.util.LinkedList; import java.util.List; -import constants.ServerConstants; + +import config.YamlConfig; import server.life.MapleMonster; import server.life.MapleMonsterInformationProvider; //import server.life.MobAttackInfo; @@ -36,11 +37,11 @@ import server.life.MobSkillFactory; import server.maps.MapleMap; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; -import server.movement.LifeMovementFragment; import tools.MaplePacketCreator; import tools.Pair; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; +import tools.exceptions.EmptyMovementException; /** * @author Danny (Leifde) @@ -54,6 +55,10 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { MapleCharacter player = c.getPlayer(); MapleMap map = player.getMap(); + if (player.isChangingMaps()) { // thanks Lame for noticing mob movement shuffle (mob OID on different maps) happening on map transitions + return; + } + int objectid = slea.readInt(); short moveid = slea.readShort(); MapleMapObject mmo = map.getMapObject(objectid); @@ -141,26 +146,32 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { short start_x = slea.readShort(); // hmm.. startpos? short start_y = slea.readShort(); // hmm... Point startPos = new Point(start_x, start_y - 2); - List res = parseMovement(slea); - + Point serverStartPos = new Point(monster.getPosition()); + Boolean aggro = monster.aggroMoveLifeUpdate(player); if (aggro == null) return; - + if (nextUse != null) { c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, nextSkillId, nextSkillLevel)); } else { c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro)); } - if (res != null) { - if (ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE) { + + try { + long movementDataStart = slea.getPosition(); + updatePosition(slea, monster, -2); // Thanks Doodle and ZERO傑洛 for noticing sponge-based bosses moving out of stage in case of no-offset applied + long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition + slea.seek(movementDataStart); + + if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE) { System.out.println((isSkill ? "SKILL " : (isAttack ? "ATTCK " : " ")) + "castPos: " + castPos + " rawAct: " + rawActivity + " opt: " + pOption + " skillID: " + useSkillId + " skillLV: " + useSkillLevel + " " + "allowSkill: " + nextMovementCouldBeSkill + " mobMp: " + mobMp); } - map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition()); - updatePosition(res, monster, -2); - map.moveMonster(monster, monster.getPosition()); - } + map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, slea, movementDataLength), serverStartPos); + //updatePosition(res, monster, -2); //does this need to be done after the packet is broadcast? + map.moveMonster(monster, monster.getPosition()); + } catch (EmptyMovementException e) {} if (banishPlayers != null) { for (MapleCharacter chr : banishPlayers) { diff --git a/src/net/server/channel/handlers/MovePetHandler.java b/src/net/server/channel/handlers/MovePetHandler.java index 005f265604..fd8b120ab7 100644 --- a/src/net/server/channel/handlers/MovePetHandler.java +++ b/src/net/server/channel/handlers/MovePetHandler.java @@ -27,6 +27,7 @@ import client.MapleClient; import server.movement.LifeMovementFragment; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.exceptions.EmptyMovementException; public final class MovePetHandler extends AbstractMovementPacketHandler { @Override @@ -34,8 +35,11 @@ public final class MovePetHandler extends AbstractMovementPacketHandler { int petId = slea.readInt(); slea.readLong(); // Point startPos = StreamUtil.readShortPoint(slea); - List res = parseMovement(slea); - if (res.isEmpty()) { + List res; + + try { + res = parseMovement(slea); + } catch (EmptyMovementException e) { return; } MapleCharacter player = c.getPlayer(); diff --git a/src/net/server/channel/handlers/MovePlayerHandler.java b/src/net/server/channel/handlers/MovePlayerHandler.java index 6e6f4aa811..85fc546951 100644 --- a/src/net/server/channel/handlers/MovePlayerHandler.java +++ b/src/net/server/channel/handlers/MovePlayerHandler.java @@ -22,24 +22,26 @@ package net.server.channel.handlers; import client.MapleClient; -import java.util.List; -import server.movement.LifeMovementFragment; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.exceptions.EmptyMovementException; public final class MovePlayerHandler extends AbstractMovementPacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { slea.skip(9); - final List res = parseMovement(slea); - if (res != null) { - updatePosition(res, c.getPlayer(), 0); + try { // thanks Sa for noticing empty movement sequences crashing players + long movementDataStart = slea.getPosition(); + updatePosition(slea, c.getPlayer(), 0); + long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition + slea.seek(movementDataStart); + c.getPlayer().getMap().movePlayer(c.getPlayer(), c.getPlayer().getPosition()); if (c.getPlayer().isHidden()) { - c.getPlayer().getMap().broadcastGMMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), res), false); + c.getPlayer().getMap().broadcastGMMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), slea, movementDataLength), false); } else { - c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), res), false); + c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.movePlayer(c.getPlayer().getId(), slea, movementDataLength), false); } - } + } catch (EmptyMovementException e) {} } } diff --git a/src/net/server/channel/handlers/MoveSummonHandler.java b/src/net/server/channel/handlers/MoveSummonHandler.java index 1e5762705f..a6cf0a4420 100644 --- a/src/net/server/channel/handlers/MoveSummonHandler.java +++ b/src/net/server/channel/handlers/MoveSummonHandler.java @@ -23,20 +23,19 @@ package net.server.channel.handlers; import java.awt.Point; import java.util.Collection; -import java.util.List; + import client.MapleCharacter; import client.MapleClient; import server.maps.MapleSummon; -import server.movement.LifeMovementFragment; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.exceptions.EmptyMovementException; public final class MoveSummonHandler extends AbstractMovementPacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int oid = slea.readInt(); Point startPos = new Point(slea.readShort(), slea.readShort()); - List res = parseMovement(slea); MapleCharacter player = c.getPlayer(); Collection summons = player.getSummonsValues(); MapleSummon summon = null; @@ -47,8 +46,14 @@ public final class MoveSummonHandler extends AbstractMovementPacketHandler { } } if (summon != null) { - updatePosition(res, summon, 0); - player.getMap().broadcastMessage(player, MaplePacketCreator.moveSummon(player.getId(), oid, startPos, res), summon.getPosition()); + try { + long movementDataStart = slea.getPosition(); + updatePosition(slea, summon, 0); + long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition + slea.seek(movementDataStart); + + player.getMap().broadcastMessage(player, MaplePacketCreator.moveSummon(player.getId(), oid, startPos, slea, movementDataLength), summon.getPosition()); + } catch (EmptyMovementException e) {} } } } diff --git a/src/net/server/channel/handlers/MultiChatHandler.java b/src/net/server/channel/handlers/MultiChatHandler.java index 8924774c32..87515bb2c8 100644 --- a/src/net/server/channel/handlers/MultiChatHandler.java +++ b/src/net/server/channel/handlers/MultiChatHandler.java @@ -24,7 +24,7 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; import client.autoban.AutobanFactory; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.world.World; @@ -57,24 +57,24 @@ public final class MultiChatHandler extends AbstractMaplePacketHandler { World world = c.getWorldServer(); if (type == 0) { world.buddyChat(recipients, player.getId(), player.getName(), chattext); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Buddy", chattext); } } else if (type == 1 && player.getParty() != null) { world.partyChat(player.getParty(), chattext, player.getName()); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Party", chattext); } } else if (type == 2 && player.getGuildId() > 0) { Server.getInstance().guildChat(player.getGuildId(), player.getName(), player.getId(), chattext); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Guild", chattext); } } else if (type == 3 && player.getGuild() != null) { int allianceId = player.getGuild().getAllianceId(); if (allianceId > 0) { Server.getInstance().allianceMessage(allianceId, MaplePacketCreator.multiChat(player.getName(), chattext, 3), player.getId(), -1); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Ally", chattext); } } diff --git a/src/net/server/channel/handlers/NPCAnimationHandler.java b/src/net/server/channel/handlers/NPCAnimationHandler.java index d12960167b..5be8060028 100644 --- a/src/net/server/channel/handlers/NPCAnimationHandler.java +++ b/src/net/server/channel/handlers/NPCAnimationHandler.java @@ -30,12 +30,17 @@ import tools.data.output.MaplePacketLittleEndianWriter; public final class NPCAnimationHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + if (c.getPlayer().isChangingMaps()) { // possible cause of error 38 in some map transition scenarios, thanks Arnah + return; + } + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); int length = (int) slea.available(); if (length == 6) { // NPC Talk mplew.writeShort(SendOpcode.NPC_ACTION.getValue()); mplew.writeInt(slea.readInt()); - mplew.writeShort(slea.readShort()); + mplew.write(slea.readByte()); // 2 bytes, thanks resinate + mplew.write(slea.readByte()); c.announce(mplew.getPacket()); } else if (length > 6) { // NPC Move byte[] bytes = slea.read(length - 9); diff --git a/src/net/server/channel/handlers/NPCShopHandler.java b/src/net/server/channel/handlers/NPCShopHandler.java index 1598df933c..48475b441f 100644 --- a/src/net/server/channel/handlers/NPCShopHandler.java +++ b/src/net/server/channel/handlers/NPCShopHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.autoban.AutobanFactory; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import tools.FilePrinter; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/NPCTalkHandler.java b/src/net/server/channel/handlers/NPCTalkHandler.java index 64f5a3b7fe..71f71c345c 100644 --- a/src/net/server/channel/handlers/NPCTalkHandler.java +++ b/src/net/server/channel/handlers/NPCTalkHandler.java @@ -22,8 +22,8 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.DueyProcessor; -import constants.ServerConstants; +import client.processor.npc.DueyProcessor; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import scripting.npc.NPCScriptManager; import server.life.MapleNPC; @@ -41,7 +41,7 @@ public final class NPCTalkHandler extends AbstractMaplePacketHandler { return; } - if(currentServerTime() - c.getPlayer().getNpcCooldown() < ServerConstants.BLOCK_NPC_RACE_CONDT) { + if(currentServerTime() - c.getPlayer().getNpcCooldown() < YamlConfig.config.server.BLOCK_NPC_RACE_CONDT) { c.announce(MaplePacketCreator.enableActions()); return; } @@ -50,26 +50,28 @@ public final class NPCTalkHandler extends AbstractMaplePacketHandler { MapleMapObject obj = c.getPlayer().getMap().getMapObject(oid); if (obj instanceof MapleNPC) { MapleNPC npc = (MapleNPC) obj; - if(ServerConstants.USE_DEBUG == true) c.getPlayer().dropMessage(5, "Talking to NPC " + npc.getId()); + if(YamlConfig.config.server.USE_DEBUG == true) c.getPlayer().dropMessage(5, "Talking to NPC " + npc.getId()); if (npc.getId() == 9010009) { //is duey - c.getPlayer().setNpcCooldown(currentServerTime()); - DueyProcessor.dueySendTalk(c); + DueyProcessor.dueySendTalk(c, false); } else { if (c.getCM() != null || c.getQM() != null) { c.announce(MaplePacketCreator.enableActions()); return; } - if(npc.getId() >= 9100100 && npc.getId() <= 9100200) { - // Custom handling for gachapon scripts to reduce the amount of scripts needed. + + // Custom handling to reduce the amount of scripts needed. + if (npc.getId() >= 9100100 && npc.getId() <= 9100200) { NPCScriptManager.getInstance().start(c, npc.getId(), "gachapon", null); + } else if (npc.getName().endsWith("Maple TV")) { + NPCScriptManager.getInstance().start(c, npc.getId(), "mapleTV", null); } else { boolean hasNpcScript = NPCScriptManager.getInstance().start(c, npc.getId(), oid, null); if (!hasNpcScript) { if (!npc.hasShop()) { FilePrinter.printError(FilePrinter.NPC_UNCODED, "NPC " + npc.getName() + "(" + npc.getId() + ") is not coded."); return; - } else if(c.getPlayer().getShop() != null) { + } else if (c.getPlayer().getShop() != null) { c.announce(MaplePacketCreator.enableActions()); return; } diff --git a/src/net/server/channel/handlers/NewYearCardHandler.java b/src/net/server/channel/handlers/NewYearCardHandler.java index 8d223aa461..af7d3102bb 100644 --- a/src/net/server/channel/handlers/NewYearCardHandler.java +++ b/src/net/server/channel/handlers/NewYearCardHandler.java @@ -19,7 +19,7 @@ */ package net.server.channel.handlers; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import client.MapleCharacter; import client.MapleClient; diff --git a/src/net/server/channel/handlers/OpenFamilyHandler.java b/src/net/server/channel/handlers/OpenFamilyHandler.java new file mode 100644 index 0000000000..39dcdeeaf4 --- /dev/null +++ b/src/net/server/channel/handlers/OpenFamilyHandler.java @@ -0,0 +1,41 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.channel.handlers; + +import config.YamlConfig; +import client.MapleCharacter; +import client.MapleClient; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +/** + * + * @author Ubaware + */ +public final class OpenFamilyHandler extends AbstractMaplePacketHandler { + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) return; + MapleCharacter chr = c.getPlayer(); + c.announce(MaplePacketCreator.getFamilyInfo(chr.getFamilyEntry())); + } +} + diff --git a/src/net/server/channel/handlers/OpenFamilyPedigreeHandler.java b/src/net/server/channel/handlers/OpenFamilyPedigreeHandler.java new file mode 100644 index 0000000000..2a24be3cda --- /dev/null +++ b/src/net/server/channel/handlers/OpenFamilyPedigreeHandler.java @@ -0,0 +1,43 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.channel.handlers; + +import config.YamlConfig; +import client.MapleCharacter; +import client.MapleClient; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +/** + * + * @author Ubaware + */ +public final class OpenFamilyPedigreeHandler extends AbstractMaplePacketHandler { + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + if(!YamlConfig.config.server.USE_FAMILY_SYSTEM) return; + MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString()); + if(target != null && target.getFamily() != null) { + c.announce(MaplePacketCreator.showPedigree(target.getFamilyEntry())); + } + } +} + diff --git a/src/net/server/channel/handlers/OwlWarpHandler.java b/src/net/server/channel/handlers/OwlWarpHandler.java index 1582cc2685..5eab87fa5b 100644 --- a/src/net/server/channel/handlers/OwlWarpHandler.java +++ b/src/net/server/channel/handlers/OwlWarpHandler.java @@ -19,7 +19,7 @@ */ package net.server.channel.handlers; -import constants.GameConstants; +import constants.game.GameConstants; import client.MapleClient; import net.AbstractMaplePacketHandler; import server.maps.MaplePlayerShop; diff --git a/src/net/server/channel/handlers/PartyOperationHandler.java b/src/net/server/channel/handlers/PartyOperationHandler.java index 605ee0f173..e73fe55966 100644 --- a/src/net/server/channel/handlers/PartyOperationHandler.java +++ b/src/net/server/channel/handlers/PartyOperationHandler.java @@ -21,6 +21,7 @@ */ package net.server.channel.handlers; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; @@ -30,11 +31,11 @@ import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; -import tools.Pair; +import constants.net.ServerConstants; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; import java.util.List; @@ -53,7 +54,7 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler { } case 2: { // leave/disband if (party != null) { - List partymembers = player.getPartyMembers(); + List partymembers = player.getPartyMembersOnline(); MapleParty.leaveParty(party, c); player.updatePartySearchAvailability(true); @@ -64,8 +65,8 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler { case 3: { // join int partyid = slea.readInt(); - Pair inviteRes = MapleInviteCoordinator.answerInvite(InviteType.PARTY, player.getId(), partyid, true); - InviteResult res = inviteRes.getLeft(); + MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.PARTY, player.getId(), partyid, true); + InviteResult res = inviteRes.result; if (res == InviteResult.ACCEPTED) { MapleParty.joinParty(player, partyid, false); } else { @@ -77,11 +78,11 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler { String name = slea.readMapleAsciiString(); MapleCharacter invited = world.getPlayerStorage().getCharacterByName(name); if (invited != null) { - if(invited.getLevel() < 10 && (!ServerConstants.USE_PARTY_FOR_STARTERS || player.getLevel() >= 10)) { //min requirement is level 10 + if(invited.getLevel() < 10 && (!YamlConfig.config.server.USE_PARTY_FOR_STARTERS || player.getLevel() >= 10)) { //min requirement is level 10 c.announce(MaplePacketCreator.serverNotice(5, "The player you have invited does not meet the requirements.")); return; } - if(ServerConstants.USE_PARTY_FOR_STARTERS && invited.getLevel() >= 10 && player.getLevel() < 10) { //trying to invite high level + if(YamlConfig.config.server.USE_PARTY_FOR_STARTERS && invited.getLevel() >= 10 && player.getLevel() < 10) { //trying to invite high level c.announce(MaplePacketCreator.serverNotice(5, "The player you have invited does not meet the requirements.")); return; } diff --git a/src/net/server/channel/handlers/PetAutoPotHandler.java b/src/net/server/channel/handlers/PetAutoPotHandler.java index 21f447f9f0..1e20797d52 100644 --- a/src/net/server/channel/handlers/PetAutoPotHandler.java +++ b/src/net/server/channel/handlers/PetAutoPotHandler.java @@ -4,167 +4,54 @@ Matthias Butz Jan Christian Meyer - Copyleft (L) 2016 - 2019 RonanLana (HeavenMS) - This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation version 3 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU Affero General Public License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package net.server.channel.handlers; -import client.MapleClient; import client.MapleCharacter; -import client.inventory.Item; -import client.inventory.MapleInventory; -import client.inventory.MapleInventoryType; +import client.MapleClient; +import client.processor.action.PetAutopotProcessor; import net.AbstractMaplePacketHandler; -import client.inventory.manipulator.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MapleStatEffect; -import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; -import constants.ServerConstants; -import java.util.List; -/** - * - * @author Ronan - multi-pot consumption feature - */ public final class PetAutoPotHandler extends AbstractMaplePacketHandler { - short slot; - int itemId; - Item toUse; - List toUseList; - - boolean hasHpGain, hasMpGain; - int maxHp, maxMp, curHp, curMp; - double incHp, incMp; @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - if (!c.getPlayer().isAlive()) { - c.announce(MaplePacketCreator.enableActions()); - return; - } - slea.readByte(); slea.readLong(); slea.readInt(); - slot = slea.readShort(); - itemId = slea.readInt(); + short slot = slea.readShort(); + int itemId = slea.readInt(); MapleCharacter chr = c.getPlayer(); - MapleInventory useInv = chr.getInventory(MapleInventoryType.USE); - - int useCount = 0, qtyCount = 0; - MapleStatEffect stat = null; - - useInv.lockInventory(); - try { - toUse = useInv.getItem(slot); - - if (toUse != null) { - if (toUse.getItemId() != itemId) { - c.announce(MaplePacketCreator.enableActions()); - return; - } - - toUseList = null; - - // from now on, toUse becomes the "cursor" for the current pot being used - if (toUse.getQuantity() <= 0) { - if (!cursorOnNextAvailablePot(chr)) { - c.announce(MaplePacketCreator.enableActions()); - return; - } - } - - stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId()); - hasHpGain = stat.getHp() > 0 || stat.getHpRate() > 0.0; - hasMpGain = stat.getMp() > 0 || stat.getMpRate() > 0.0; - - maxHp = chr.getCurrentMaxHp(); - maxMp = chr.getCurrentMaxMp(); - - curHp = chr.getHp(); - curMp = chr.getMp(); - - incHp = stat.getHp(); - if(incHp <= 0 && hasHpGain) incHp = Math.ceil(maxHp * stat.getHpRate()); - - incMp = stat.getMp(); - if(incMp <= 0 && hasMpGain) incMp = Math.ceil(maxMp * stat.getMpRate()); - - if (ServerConstants.USE_COMPULSORY_AUTOPOT) { - if (hasHpGain) { - qtyCount = (int) Math.ceil(((ServerConstants.PET_AUTOHP_RATIO * maxHp) - curHp) / incHp); - } - - if (hasMpGain) { - qtyCount = Math.max(qtyCount, (int) Math.ceil(((ServerConstants.PET_AUTOMP_RATIO * maxMp) - curMp) / incMp)); - } - } else { - qtyCount = 1; // non-compulsory autopot concept thanks to marcuswoon - } - - while (true) { - short qtyToUse = (short) Math.min(qtyCount, toUse.getQuantity()); - MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, qtyToUse, false); - - curHp += (incHp * qtyToUse); - curMp += (incMp * qtyToUse); - - useCount += qtyToUse; - qtyCount -= qtyToUse; - - if(toUse.getQuantity() == 0 && qtyCount > 0) { - // depleted out the current slot, fetch for more - - if(!cursorOnNextAvailablePot(chr)) { - break; // no more pots available - } - } else { - break; // gracefully finished it's job, quit the loop - } - } - } - } finally { - useInv.unlockInventory(); + MapleStatEffect stat = MapleItemInformationProvider.getInstance().getItemEffect(itemId); + if (stat.getHp() > 0 || stat.getHpRate() > 0.0) { + float estimatedHp = ((float) chr.getHp()) / chr.getMaxHp(); + chr.setAutopotHpAlert(estimatedHp + 0.05f); } - for (int i = 0; i < useCount; i++) { - stat.applyTo(chr); + if (stat.getMp() > 0 || stat.getMpRate() > 0.0) { + float estimatedMp = ((float) chr.getMp()) / chr.getMaxMp(); + chr.setAutopotMpAlert(estimatedMp + 0.05f); } - chr.announce(MaplePacketCreator.enableActions()); + PetAutopotProcessor.runAutopotAction(c, slot, itemId); } - private boolean cursorOnNextAvailablePot(MapleCharacter chr) { - if(toUseList == null) { - toUseList = chr.getInventory(MapleInventoryType.USE).linkedListById(itemId); - } - - toUse = null; - while(!toUseList.isEmpty()) { - Item it = toUseList.remove(0); - - if(it.getQuantity() > 0) { - toUse = it; - slot = it.getPosition(); - - return true; - } - } - - return false; - } } diff --git a/src/net/server/channel/handlers/PetChatHandler.java b/src/net/server/channel/handlers/PetChatHandler.java index 448583b5f6..b18db64dbb 100644 --- a/src/net/server/channel/handlers/PetChatHandler.java +++ b/src/net/server/channel/handlers/PetChatHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.autoban.AutobanFactory; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.FilePrinter; import tools.LogHelper; @@ -50,7 +50,7 @@ public final class PetChatHandler extends AbstractMaplePacketHandler { return; } c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.petChat(c.getPlayer().getId(), pet, act, text), true); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Pet", text); } } diff --git a/src/net/server/channel/handlers/PetExcludeItemsHandler.java b/src/net/server/channel/handlers/PetExcludeItemsHandler.java index 4b1842c51a..b0619ac4fe 100644 --- a/src/net/server/channel/handlers/PetExcludeItemsHandler.java +++ b/src/net/server/channel/handlers/PetExcludeItemsHandler.java @@ -33,6 +33,8 @@ import tools.data.input.SeekableLittleEndianAccessor; * @author Ronan */ public final class PetExcludeItemsHandler extends AbstractMaplePacketHandler { + + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { final int petId = slea.readInt(); slea.skip(4); diff --git a/src/net/server/channel/handlers/PetLootHandler.java b/src/net/server/channel/handlers/PetLootHandler.java index 26e7609ba1..8b794344f2 100644 --- a/src/net/server/channel/handlers/PetLootHandler.java +++ b/src/net/server/channel/handlers/PetLootHandler.java @@ -31,7 +31,6 @@ import server.maps.MapleMapItem; import server.maps.MapleMapObject; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; -import constants.ServerConstants; /** * @author TheRamon @@ -41,10 +40,6 @@ public final class PetLootHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - if(currentServerTime() - chr.getPetLootCd() < ServerConstants.PET_LOOT_UPON_ATTACK) { - c.announce(MaplePacketCreator.enableActions()); - return; - } int petIndex = chr.getPetIndex(slea.readInt()); MaplePet pet = chr.getPet(petIndex); diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java index f2c895e8ef..a066a7fbb2 100644 --- a/src/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java @@ -29,16 +29,18 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import server.MapleItemInformationProvider; +import server.maps.MaplePortal; import server.MapleTrade; -import constants.GameConstants; +import constants.game.GameConstants; import server.maps.FieldLimit; import server.maps.MapleHiredMerchant; import server.maps.MapleMapObject; +import server.maps.MapleMapObjectType; import server.maps.MapleMiniGame; import server.maps.MapleMiniGame.MiniGameType; import server.maps.MaplePlayerShop; @@ -49,6 +51,7 @@ import tools.data.input.SeekableLittleEndianAccessor; import java.awt.Point; import java.sql.SQLException; +import java.util.Arrays; /** * @@ -237,13 +240,9 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { return; } - try { - Point cpos = chr.getPosition(); - if (chr.getMap().findClosestWarpPortal(cpos).getPosition().distance(cpos) < 120.0) { - chr.getClient().announce(MaplePacketCreator.getMiniRoomError(10)); - return; - } - } catch (NullPointerException npe) {} + if (!canPlaceStore(chr)) { + return; + } String desc = slea.readMapleAsciiString(); slea.skip(3); @@ -362,11 +361,15 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.hiredMerchantOwnerMaintenanceLeave()); } + + if (!canPlaceStore(chr)) { // thanks Ari for noticing player shops overlapping on opening time + return; + } MaplePlayerShop shop = chr.getPlayerShop(); MapleHiredMerchant merchant = chr.getHiredMerchant(); if (shop != null && shop.isOwner(chr)) { - if(ServerConstants.USE_ERASE_PERMIT_ON_OPENSHOP) { + if(YamlConfig.config.server.USE_ERASE_PERMIT_ON_OPENSHOP) { try { MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, shop.getItemId(), 1, true, false); } catch(RuntimeException re) {} // fella does not have a player shop permit... @@ -639,7 +642,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr)); - if (ServerConstants.USE_ENFORCE_MERCHANT_SAVE) { + if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) { chr.saveCharToDB(false); } @@ -797,4 +800,37 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { return false; } + + private static boolean canPlaceStore(MapleCharacter chr) { + try { + for (MapleMapObject mmo : chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT, MapleMapObjectType.PLAYER))) { + if (mmo instanceof MapleCharacter) { + MapleCharacter mc = (MapleCharacter) mmo; + if (mc.getId() == chr.getId()) { + continue; + } + + MaplePlayerShop shop = mc.getPlayerShop(); + if (shop != null && shop.isOwner(mc)) { + chr.announce(MaplePacketCreator.getMiniRoomError(13)); + return false; + } + } else { + chr.announce(MaplePacketCreator.getMiniRoomError(13)); + return false; + } + } + + Point cpos = chr.getPosition(); + MaplePortal portal = chr.getMap().findClosestTeleportPortal(cpos); + if (portal != null && portal.getPosition().distance(cpos) < 120.0) { + chr.announce(MaplePacketCreator.getMiniRoomError(10)); + return false; + } + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } } diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index 223149d23e..137e48b2b9 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import net.server.PlayerBuffValueHolder; import net.server.Server; @@ -40,6 +41,7 @@ import net.server.world.MaplePartyCharacter; import net.server.world.PartyOperation; import net.server.world.World; import tools.DatabaseConnection; +import tools.FilePrinter; import tools.MaplePacketCreator; import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; @@ -50,23 +52,24 @@ import client.MapleCharacter; import client.MapleClient; import client.MapleDisease; import client.MapleFamily; +import client.MapleFamilyEntry; import client.MapleKeyBinding; +import client.MapleMount; import client.SkillFactory; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; -import constants.GameConstants; -import constants.ScriptableNPCConstants; -import constants.ServerConstants; +import constants.game.GameConstants; +import constants.game.ScriptableNPCConstants; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Map; import java.util.Set; -import net.server.coordinator.MapleEventRecallCoordinator; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.world.MapleEventRecallCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.session.IoSession; import server.life.MobSkill; import scripting.event.EventInstanceManager; @@ -123,40 +126,46 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid); - boolean newcomer = false; - IoSession session = c.getSession(); + String remoteHwid; if (player == null) { - if (!server.validateCharacteridInTransition(session, cid)) { - c.disconnect(true, false); - return; - } - remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session); if (remoteHwid == null) { c.disconnect(true, false); return; } - + } else { + remoteHwid = player.getClient().getHWID(); + } + + int hwidLen = remoteHwid.length(); + session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); + session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); + c.setHWID(remoteHwid); + + if (!server.validateCharacteridInTransition(c, cid)) { + c.disconnect(true, false); + return; + } + + boolean newcomer = false; + if (player == null) { try { player = MapleCharacter.loadCharFromDB(cid, c, true); newcomer = true; } catch (SQLException e) { e.printStackTrace(); } - } else { - remoteHwid = player.getClient().getHWID(); + + if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs + c.disconnect(true, false); + return; + } } - - if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs - c.disconnect(true, false); - return; - } - c.setPlayer(player); c.setAccID(player.getAccountID()); - + boolean allowLogin = true; /* is this check really necessary? @@ -203,15 +212,11 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } if (!newcomer) { + c.setLanguage(player.getClient().getLanguage()); c.setCharacterSlots((byte) player.getClient().getCharacterSlots()); player.newClient(c); } - int hwidLen = remoteHwid.length(); - session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); - c.setHWID(remoteHwid); - cserv.addPlayer(player); wserv.addPlayer(player); player.setEnteredChannelWorld(); @@ -229,7 +234,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.getCharInfo(player)); if (!player.isHidden()) { - if(player.isGM() && ServerConstants.USE_AUTOHIDE_GM) { + if(player.isGM() && YamlConfig.config.server.USE_AUTOHIDE_GM) { player.toggleHide(true); } } @@ -259,12 +264,23 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.loadFamily(player)); if (player.getFamilyId() > 0) { MapleFamily f = wserv.getFamily(player.getFamilyId()); - if (f == null) { - f = new MapleFamily(player.getId()); - wserv.addFamily(player.getFamilyId(), f); + if(f != null) { + MapleFamilyEntry familyEntry = f.getEntryByID(player.getId()); + if(familyEntry != null) { + familyEntry.setCharacter(player); + player.setFamilyEntry(familyEntry); + + c.announce(MaplePacketCreator.getFamilyInfo(familyEntry)); + familyEntry.announceToSenior(MaplePacketCreator.sendFamilyLoginNotice(player.getName(), true), true); + } else { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + "'s family doesn't have an entry for them. (" + f.getID() + ")"); + } + } else { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + " has an invalid family ID. (" + player.getFamilyId() + ")"); + c.announce(MaplePacketCreator.getFamilyInfo(null)); } - player.setFamily(f); - c.announce(MaplePacketCreator.getFamilyInfo(f.getMember(player.getId()))); + } else { + c.announce(MaplePacketCreator.getFamilyInfo(null)); } if (player.getGuildId() > 0) { MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player); @@ -339,8 +355,14 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { if (newcomer) { for(MaplePet pet : player.getPets()) { - if(pet != null) + if(pet != null) { wserv.registerPetHunger(player, player.getPetIndex(pet)); + } + } + + MapleMount mount = player.getMount(); // thanks Ari for noticing a scenario where Silver Mane quest couldn't be started + if (mount.getItemId() != 0) { + player.announce(MaplePacketCreator.updateMount(player.getId(), mount, false)); } player.reloadQuestExpirations(); @@ -359,15 +381,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { final List> debuff = Collections.singletonList(new Pair<>(e.getKey(), Integer.valueOf(e.getValue().getRight().getX()))); c.announce(MaplePacketCreator.giveDebuff(debuff, e.getValue().getRight())); } - - player.announceDiseases(); } } else { if(player.isRidingBattleship()) { player.announceBattleshipHp(); } } - + player.buffExpireTask(); player.diseaseExpireTask(); player.skillCooldownTask(); @@ -383,7 +403,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask(); player.resetPlayerRates(); - if(ServerConstants.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates(); + if(YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates(); player.setWorldRates(); player.updateCouponRates(); @@ -406,9 +426,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } } - if (ServerConstants.USE_NPCS_SCRIPTABLE) { + if (YamlConfig.config.server.USE_NPCS_SCRIPTABLE) { c.announce(MaplePacketCreator.setNPCScriptable(ScriptableNPCConstants.SCRIPTABLE_NPCS)); } + + if(newcomer) player.setLoginTime(System.currentTimeMillis()); + } catch(Exception e) { + e.printStackTrace(); } finally { c.releaseClient(); } @@ -424,22 +448,22 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { ResultSet rs = null; try { con = DatabaseConnection.getConnection(); - ps = con.prepareStatement("SELECT Mesos FROM dueypackages WHERE ReceiverId = ? and Checked = 1"); + ps = con.prepareStatement("SELECT Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC"); ps.setInt(1, player.getId()); rs = ps.executeQuery(); if (rs.next()) { try { Connection con2 = DatabaseConnection.getConnection(); - pss = con2.prepareStatement("UPDATE dueypackages SET Checked = 0 where ReceiverId = ?"); + pss = con2.prepareStatement("UPDATE dueypackages SET Checked = 0 WHERE ReceiverId = ?"); pss.setInt(1, player.getId()); pss.executeUpdate(); pss.close(); con2.close(); + + c.announce(MaplePacketCreator.sendDueyParcelNotification(rs.getInt("Type") == 1)); } catch (SQLException e) { e.printStackTrace(); } - - c.announce(MaplePacketCreator.sendDueyNotification(false)); } } catch (SQLException e) { e.printStackTrace(); diff --git a/src/net/server/channel/handlers/PlayerMapTransitionHandler.java b/src/net/server/channel/handlers/PlayerMapTransitionHandler.java index 0ba757010f..fa7938a9b2 100644 --- a/src/net/server/channel/handlers/PlayerMapTransitionHandler.java +++ b/src/net/server/channel/handlers/PlayerMapTransitionHandler.java @@ -26,6 +26,8 @@ import client.MapleClient; import java.util.Collections; import java.util.List; import net.AbstractMaplePacketHandler; +import server.life.MapleMonster; +import server.maps.MapleMapObject; import tools.MaplePacketCreator; import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; @@ -48,5 +50,23 @@ public final class PlayerMapTransitionHandler extends AbstractMaplePacketHandler final List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.HOMING_BEACON, 0)); chr.announce(MaplePacketCreator.giveBuff(1, beaconid, stat)); } + + if (!chr.isHidden()) { // thanks Lame for noticing hidden characters controlling mobs + for (MapleMapObject mo : chr.getMap().getMonsters()) { // thanks BHB, IxianMace, Jefe for noticing several issues regarding mob statuses (such as freeze) + MapleMonster m = (MapleMonster) mo; + if (m.getSpawnEffect() == 0 || m.getHp() < m.getMaxHp()) { // avoid effect-spawning mobs + if (m.getController() == chr) { + c.announce(MaplePacketCreator.stopControllingMonster(m.getObjectId())); + m.sendDestroyData(c); + m.aggroRedirectController(); + } else { + m.sendDestroyData(c); + } + + m.aggroSwitchController(chr, false); + m.sendSpawnData(c); + } + } + } } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/QuestActionHandler.java b/src/net/server/channel/handlers/QuestActionHandler.java index 3f21f13dfe..a35b2a0239 100644 --- a/src/net/server/channel/handlers/QuestActionHandler.java +++ b/src/net/server/channel/handlers/QuestActionHandler.java @@ -38,13 +38,19 @@ public final class QuestActionHandler extends AbstractMaplePacketHandler { // isNpcNearby thanks to GabrielSin private static boolean isNpcNearby(SeekableLittleEndianAccessor slea, MapleCharacter player, MapleQuest quest, int npcId) { - Point playerP = null; + Point playerP; + Point pos = player.getPosition(); if(slea.available() >= 4) { playerP = new Point(slea.readShort(), slea.readShort()); + if (playerP.distance(pos) > 1000) { // thanks Darter (YungMoozi) for reporting unchecked player position + playerP = pos; + } + } else { + playerP = pos; } - if (playerP != null && !quest.isAutoStart() && !quest.isAutoComplete()) { + if (!quest.isAutoStart() && !quest.isAutoComplete()) { MapleNPC npc = player.getMap().getNPCById(npcId); if(npc == null) { return false; @@ -66,7 +72,12 @@ public final class QuestActionHandler extends AbstractMaplePacketHandler { short questid = slea.readShort(); MapleCharacter player = c.getPlayer(); MapleQuest quest = MapleQuest.getInstance(questid); - if (action == 1) { //Start Quest + + if (action == 0) { // Restore lost item, Credits Darter ( Rajan ) + slea.readInt(); + int itemid = slea.readInt(); + quest.restoreLostItem(player, itemid); + } else if (action == 1) { //Start Quest int npc = slea.readInt(); if(!isNpcNearby(slea, player, quest, npc)) { return; diff --git a/src/net/server/channel/handlers/RPSActionHandler.java b/src/net/server/channel/handlers/RPSActionHandler.java new file mode 100644 index 0000000000..f3eea78053 --- /dev/null +++ b/src/net/server/channel/handlers/RPSActionHandler.java @@ -0,0 +1,71 @@ +package net.server.channel.handlers; + +import client.MapleCharacter; +import client.MapleClient; +import server.minigame.MapleRockPaperScissor; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; + +/** + * @Author Arnah + * @Website http://Vertisy.ca/ + * @since Aug 15, 2016 + */ +public final class RPSActionHandler extends AbstractMaplePacketHandler{ + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c){ + MapleCharacter chr = c.getPlayer(); + MapleRockPaperScissor rps = chr.getRPS(); + + if (c.tryacquireClient()) { + try { + if(slea.available() == 0 || !chr.getMap().containsNPC(9000019)){ + if(rps != null){ + rps.dispose(c); + } + return; + } + final byte mode = slea.readByte(); + switch (mode){ + case 0: // start game + case 5: // retry + if(rps != null){ + rps.reward(c); + } + if(chr.getMeso() >= 1000){ + chr.setRPS(new MapleRockPaperScissor(c, mode)); + }else{ + c.announce(MaplePacketCreator.rpsMesoError(-1)); + } + break; + case 1: // answer + if(rps == null || !rps.answer(c, slea.readByte())){ + c.announce(MaplePacketCreator.rpsMode((byte) 0x0D));// 13 + } + break; + case 2: // time over + if(rps == null || !rps.timeOut(c)){ + c.announce(MaplePacketCreator.rpsMode((byte) 0x0D)); + } + break; + case 3: // continue + if(rps == null || !rps.nextRound(c)){ + c.announce(MaplePacketCreator.rpsMode((byte) 0x0D)); + } + break; + case 4: // leave + if(rps != null){ + rps.dispose(c); + }else{ + c.announce(MaplePacketCreator.rpsMode((byte) 0x0D)); + } + break; + } + } finally { + c.releaseClient(); + } + } + } +} diff --git a/src/net/server/channel/handlers/RaiseIncExpHandler.java b/src/net/server/channel/handlers/RaiseIncExpHandler.java index 68261b42c0..89c3dd3c2a 100644 --- a/src/net/server/channel/handlers/RaiseIncExpHandler.java +++ b/src/net/server/channel/handlers/RaiseIncExpHandler.java @@ -2,13 +2,17 @@ package net.server.channel.handlers; import java.util.Map; +import client.MapleCharacter; import client.MapleClient; +import client.MapleQuestStatus; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MapleItemInformationProvider.QuestConsItem; +import server.quest.MapleQuest; +import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** @@ -22,7 +26,7 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { byte inventorytype = slea.readByte();//nItemIT short slot = slea.readShort();//nSlotPosition - int itemid = slea.readInt();//nItemID + int itemid = slea.readInt();//nItemID if (c.tryacquireClient()) { try { @@ -32,15 +36,22 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler { return; } - int questid = consItem.questid; + int infoNumber = consItem.questid; Map consumables = consItem.items; - + + MapleCharacter chr = c.getPlayer(); + MapleQuest quest = MapleQuest.getInstanceFromInfoNumber(infoNumber); + if (!chr.getQuest(quest).getStatus().equals(MapleQuestStatus.Status.STARTED)) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + int consId; - MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.getByType(inventorytype)); + MapleInventory inv = chr.getInventory(MapleInventoryType.getByType(inventorytype)); inv.lockInventory(); try { consId = inv.getItem(slot).getItemId(); - if (!consumables.containsKey(consId) || !c.getPlayer().haveItem(consId)) { + if (!consumables.containsKey(consId) || !chr.haveItem(consId)) { return; } @@ -48,9 +59,12 @@ public class RaiseIncExpHandler extends AbstractMaplePacketHandler { } finally { inv.unlockInventory(); } - - int nextValue = Math.min(consumables.get(consId) + Integer.parseInt(c.getPlayer().getQuestInfo(questid)), consItem.exp * consItem.grade); - c.getPlayer().updateQuestInfo(questid, "" + nextValue); + + int questid = quest.getId(); + int nextValue = Math.min(consumables.get(consId) + c.getAbstractPlayerInteraction().getQuestProgressInt(questid, infoNumber), consItem.exp * consItem.grade); + c.getAbstractPlayerInteraction().setQuestProgress(questid, infoNumber, nextValue); + + c.announce(MaplePacketCreator.enableActions()); } finally { c.releaseClient(); } diff --git a/src/net/server/channel/handlers/RaiseUIStateHandler.java b/src/net/server/channel/handlers/RaiseUIStateHandler.java index d5add73e1e..287de88347 100644 --- a/src/net/server/channel/handlers/RaiseUIStateHandler.java +++ b/src/net/server/channel/handlers/RaiseUIStateHandler.java @@ -1,10 +1,12 @@ package net.server.channel.handlers; +import client.MapleCharacter.DelayedQuestUpdate; +import client.MapleCharacter; import client.MapleClient; import client.MapleQuestStatus; import net.AbstractMaplePacketHandler; +import scripting.quest.QuestScriptManager; import server.quest.MapleQuest; -import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** @@ -15,19 +17,21 @@ public class RaiseUIStateHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - int questid = slea.readShort(); + int infoNumber = slea.readShort(); if (c.tryacquireClient()) { try { - MapleQuest quest = MapleQuest.getInstance(questid); - MapleQuestStatus mqs = c.getPlayer().getQuest(quest); + MapleCharacter chr = c.getPlayer(); + MapleQuest quest = MapleQuest.getInstanceFromInfoNumber(infoNumber); + MapleQuestStatus mqs = chr.getQuest(quest); + + QuestScriptManager.getInstance().raiseOpen(c, (short) infoNumber, mqs.getNpc()); + if (mqs.getStatus() == MapleQuestStatus.Status.NOT_STARTED) { - quest.forceStart(c.getPlayer(), 22000); - c.getPlayer().updateQuestInfo(quest.getId(), "0"); + quest.forceStart(chr, 22000); + c.getAbstractPlayerInteraction().setQuestProgress(quest.getId(), infoNumber, 0); } else if (mqs.getStatus() == MapleQuestStatus.Status.STARTED) { - c.announce(MaplePacketCreator.updateQuest(mqs, false)); - } else { - //c.announce(MaplePacketCreator.updateQuestInfo(mqs.getQuestID(), 22000, "0")); + chr.announceUpdateQuest(DelayedQuestUpdate.UPDATE, mqs, mqs.getInfoNumber() > 0); } } finally { c.releaseClient(); diff --git a/src/net/server/channel/handlers/RangedAttackHandler.java b/src/net/server/channel/handlers/RangedAttackHandler.java index b432d75e1e..f2d33047f4 100644 --- a/src/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/net/server/channel/handlers/RangedAttackHandler.java @@ -21,12 +21,6 @@ along with this program. If not, see . */ package net.server.channel.handlers; -import client.inventory.manipulator.MapleInventoryManipulator; -import server.MapleItemInformationProvider; -import server.MapleStatEffect; -import tools.MaplePacketCreator; -import tools.Randomizer; -import tools.data.input.SeekableLittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; @@ -36,8 +30,9 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.MapleWeaponType; -import constants.ItemConstants; -import constants.ServerConstants; +import client.inventory.manipulator.MapleInventoryManipulator; +import config.YamlConfig; +import constants.inventory.ItemConstants; import constants.skills.Aran; import constants.skills.Buccaneer; import constants.skills.NightLord; @@ -45,13 +40,18 @@ import constants.skills.NightWalker; import constants.skills.Shadower; import constants.skills.ThunderBreaker; import constants.skills.WindArcher; +import server.MapleItemInformationProvider; +import server.MapleStatEffect; +import tools.MaplePacketCreator; +import tools.Randomizer; +import tools.data.input.SeekableLittleEndianAccessor; + public final class RangedAttackHandler extends AbstractDealDamageHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - //chr.setPetLootCd(currentServerTime()); /*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8); if(timeElapsed < 300) { @@ -70,7 +70,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { - chr.setDojoEnergy(chr.getDojoEnergy() + ServerConstants.DOJO_ENERGY_ATK); + chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/RemoteGachaponHandler.java b/src/net/server/channel/handlers/RemoteGachaponHandler.java index c7b9382857..e06eb3704f 100644 --- a/src/net/server/channel/handlers/RemoteGachaponHandler.java +++ b/src/net/server/channel/handlers/RemoteGachaponHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.autoban.AutobanFactory; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import scripting.npc.NPCScriptManager; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/RingActionHandler.java b/src/net/server/channel/handlers/RingActionHandler.java index 35e81c7919..bbca1ca702 100644 --- a/src/net/server/channel/handlers/RingActionHandler.java +++ b/src/net/server/channel/handlers/RingActionHandler.java @@ -33,7 +33,7 @@ import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; -import client.processor.DueyProcessor; +import client.processor.npc.DueyProcessor; import net.AbstractMaplePacketHandler; import net.server.world.World; import net.server.channel.Channel; diff --git a/src/net/server/channel/handlers/ScriptedItemHandler.java b/src/net/server/channel/handlers/ScriptedItemHandler.java index 1c95735b17..73678c9025 100644 --- a/src/net/server/channel/handlers/ScriptedItemHandler.java +++ b/src/net/server/channel/handlers/ScriptedItemHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.inventory.Item; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import scripting.item.ItemScriptManager; import server.MapleItemInformationProvider; diff --git a/src/net/server/channel/handlers/ScrollHandler.java b/src/net/server/channel/handlers/ScrollHandler.java index 654463ec36..d485609288 100644 --- a/src/net/server/channel/handlers/ScrollHandler.java +++ b/src/net/server/channel/handlers/ScrollHandler.java @@ -31,7 +31,7 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.ModifyInventory; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -78,17 +78,17 @@ public final class ScrollHandler extends AbstractMaplePacketHandler { if (ItemConstants.isCleanSlate(scroll.getItemId())) { Map eqStats = ii.getEquipStats(toScroll.getItemId()); // clean slate issue found thanks to Masterrulax if (eqStats == null || eqStats.get("tuc") == 0) { - c.announce(MaplePacketCreator.getInventoryFull()); + announceCannotScroll(c, legendarySpirit); return; } } else if (!ItemConstants.isModifierScroll(scroll.getItemId()) && ((Equip) toScroll).getUpgradeSlots() < 1) { - c.announce(MaplePacketCreator.getInventoryFull()); + announceCannotScroll(c, legendarySpirit); // thanks onechord for noticing zero upgrade slots freezing Legendary Scroll UI return; } List scrollReqs = ii.getScrollReqs(scroll.getItemId()); if (scrollReqs.size() > 0 && !scrollReqs.contains(toScroll.getItemId())) { - c.announce(MaplePacketCreator.getInventoryFull()); + announceCannotScroll(c, legendarySpirit); return; } if (whiteScroll) { @@ -100,11 +100,13 @@ public final class ScrollHandler extends AbstractMaplePacketHandler { if (!ItemConstants.isChaosScroll(scroll.getItemId()) && !ItemConstants.isCleanSlate(scroll.getItemId())) { if (!canScroll(scroll.getItemId(), toScroll.getItemId())) { + announceCannotScroll(c, legendarySpirit); return; } } if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) { + announceCannotScroll(c, legendarySpirit); return; } @@ -119,11 +121,13 @@ public final class ScrollHandler extends AbstractMaplePacketHandler { useInventory.lockInventory(); try { if (scroll.getQuantity() < 1) { + announceCannotScroll(c, legendarySpirit); return; } if (whiteScroll && !ItemConstants.isCleanSlate(scroll.getItemId())) { if (wscroll.getQuantity() < 1) { + announceCannotScroll(c, legendarySpirit); return; } @@ -171,7 +175,7 @@ public final class ScrollHandler extends AbstractMaplePacketHandler { mods.add(new ModifyInventory(0, scrolled)); } c.announce(MaplePacketCreator.modifyInventory(true, mods)); - chr.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit)); + chr.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit, whiteScroll)); if (dst < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) { chr.equipChanged(); } @@ -180,6 +184,14 @@ public final class ScrollHandler extends AbstractMaplePacketHandler { } } } + + private static void announceCannotScroll(MapleClient c, boolean legendarySpirit) { + if (legendarySpirit) { + c.announce(MaplePacketCreator.getScrollEffect(c.getPlayer().getId(), Equip.ScrollResult.FAIL, false, false)); + } else { + c.announce(MaplePacketCreator.getInventoryFull()); + } + } private static boolean canScroll(int scrollid, int itemid) { int sid = scrollid / 100; diff --git a/src/net/server/channel/handlers/SkillMacroHandler.java b/src/net/server/channel/handlers/SkillMacroHandler.java index de88635f34..148d93a986 100644 --- a/src/net/server/channel/handlers/SkillMacroHandler.java +++ b/src/net/server/channel/handlers/SkillMacroHandler.java @@ -27,6 +27,8 @@ import tools.data.input.SeekableLittleEndianAccessor; import net.AbstractMaplePacketHandler; public final class SkillMacroHandler extends AbstractMaplePacketHandler { + + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int num = slea.readByte(); for (int i = 0; i < num; i++) { diff --git a/src/net/server/channel/handlers/SpawnPetHandler.java b/src/net/server/channel/handlers/SpawnPetHandler.java index 6ebcff6dc5..be1a203ad7 100644 --- a/src/net/server/channel/handlers/SpawnPetHandler.java +++ b/src/net/server/channel/handlers/SpawnPetHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.SpawnPetProcessor; +import client.processor.action.SpawnPetProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/SpecialMoveHandler.java b/src/net/server/channel/handlers/SpecialMoveHandler.java index 46e0020c8b..cf988013d8 100644 --- a/src/net/server/channel/handlers/SpecialMoveHandler.java +++ b/src/net/server/channel/handlers/SpecialMoveHandler.java @@ -23,6 +23,7 @@ package net.server.channel.handlers; import java.awt.Point; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import server.MapleStatEffect; import server.life.MapleMonster; @@ -32,7 +33,6 @@ import client.MapleCharacter; import client.MapleClient; import client.Skill; import client.SkillFactory; -import constants.ServerConstants; import constants.skills.Brawler; import constants.skills.Corsair; import constants.skills.DarkKnight; @@ -81,7 +81,7 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler { return; } else if (skillid != Corsair.BATTLE_SHIP) { int cooldownTime = effect.getCooldown(); - if(MapleStatEffect.isHerosWill(skillid) && ServerConstants.USE_FAST_REUSE_HERO_WILL) { + if(MapleStatEffect.isHerosWill(skillid) && YamlConfig.config.server.USE_FAST_REUSE_HERO_WILL) { cooldownTime /= 60; } @@ -101,7 +101,8 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler { monster.aggroClearDamages(); monster.aggroMonsterDamage(chr, 1); - // thanks onechord for pointing out Magnet disconnecting the caster (issue would actually happen upon failing to catch mob) + // thanks onechord for pointing out Magnet crashing the caster (issue would actually happen upon failing to catch mob) + // thanks Conrad for noticing Magnet crashing when trying to pull bosses and fixed mobs monster.aggroSwitchController(chr, true); } } diff --git a/src/net/server/channel/handlers/SpouseChatHandler.java b/src/net/server/channel/handlers/SpouseChatHandler.java index 09ec66a389..46d4bff57f 100644 --- a/src/net/server/channel/handlers/SpouseChatHandler.java +++ b/src/net/server/channel/handlers/SpouseChatHandler.java @@ -23,7 +23,7 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import tools.LogHelper; import tools.MaplePacketCreator; @@ -41,7 +41,7 @@ public final class SpouseChatHandler extends AbstractMaplePacketHandler { if (spouse != null) { spouse.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true)); c.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true)); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Spouse", msg); } } else { diff --git a/src/net/server/channel/handlers/StorageHandler.java b/src/net/server/channel/handlers/StorageHandler.java index 6f353a5b4b..7289db93ba 100644 --- a/src/net/server/channel/handlers/StorageHandler.java +++ b/src/net/server/channel/handlers/StorageHandler.java @@ -22,7 +22,7 @@ package net.server.channel.handlers; import client.MapleClient; -import client.processor.StorageProcessor; +import client.processor.npc.StorageProcessor; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/channel/handlers/SummonDamageHandler.java b/src/net/server/channel/handlers/SummonDamageHandler.java index ef67108f08..18722edf21 100644 --- a/src/net/server/channel/handlers/SummonDamageHandler.java +++ b/src/net/server/channel/handlers/SummonDamageHandler.java @@ -25,21 +25,30 @@ import client.MapleCharacter; import client.MapleClient; import client.Skill; import client.SkillFactory; +import client.autoban.AutobanFactory; +import client.inventory.Item; +import client.inventory.MapleInventoryType; +import client.inventory.MapleWeaponType; import client.status.MonsterStatusEffect; +import constants.skills.Outlaw; import java.util.ArrayList; import java.util.List; +import server.MapleItemInformationProvider; import server.MapleStatEffect; import server.life.MapleMonster; +import server.life.MapleMonsterInformationProvider; import server.maps.MapleSummon; +import tools.FilePrinter; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; public final class SummonDamageHandler extends AbstractDealDamageHandler { + public final class SummonAttackEntry { private int monsterOid; private int damage; - + public SummonAttackEntry(int monsterOid, int damage) { this.monsterOid = monsterOid; this.damage = damage; @@ -52,6 +61,7 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { public int getDamage() { return damage; } + } @Override @@ -84,13 +94,24 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { allDamage.add(new SummonAttackEntry(monsterOid, damage)); } player.getMap().broadcastMessage(player, MaplePacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition()); + if (player.getMap().isOwnershipRestricted(player)) { return; } + + boolean magic = summonEffect.getWatk() == 0; + int maxDmg = calcMaxDamage(summonEffect, player, magic); // thanks Darter (YungMoozi) for reporting unchecked max dmg for (SummonAttackEntry attackEntry : allDamage) { int damage = attackEntry.getDamage(); MapleMonster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid()); if (target != null) { + if (damage > maxDmg) { + AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit."); + + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " used a summon of skillid " + summon.getSkill() + " to attack " + MapleMonsterInformationProvider.getInstance().getMobNameFromId(target.getId()) + " with damage " + damage + " (max: " + maxDmg + ")"); + damage = maxDmg; + } + if (damage > 0 && summonEffect.getMonsterStati().size() > 0) { if (summonEffect.makeChanceResult()) { target.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000); @@ -99,5 +120,33 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler { player.getMap().damageMonster(player, target, damage); } } + + if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss + player.cancelEffect(summonEffect, false, -1); + } } -} \ No newline at end of file + + private static int calcMaxDamage(MapleStatEffect summonEffect, MapleCharacter player, boolean magic) { + double maxDamage; + + if (magic) { + int matk = Math.max(player.getTotalMagic(), 14); + maxDamage = player.calculateMaxBaseMagicDamage(matk) * (0.05 * summonEffect.getMatk()); + } else { + int watk = Math.max(player.getTotalWatk(), 14); + Item weapon_item = player.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11); + + int maxBaseDmg; // thanks Conrad, Atoot for detecting some summons legitimately hitting over the calculated limit + if (weapon_item != null) { + maxBaseDmg = player.calculateMaxBaseDamage(watk, MapleItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId())); + } else { + maxBaseDmg = player.calculateMaxBaseDamage(watk, MapleWeaponType.SWORD1H); + } + + float summonDmgMod = (maxBaseDmg >= 438) ? 0.054f : 0.077f; + maxDamage = maxBaseDmg * (summonDmgMod * summonEffect.getWatk()); + } + + return (int) maxDamage; + } +} diff --git a/src/net/server/channel/handlers/TakeDamageHandler.java b/src/net/server/channel/handlers/TakeDamageHandler.java index 63bb21195b..ee1c30db75 100644 --- a/src/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/net/server/channel/handlers/TakeDamageHandler.java @@ -31,9 +31,9 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; import constants.skills.Aran; import java.awt.Point; @@ -44,7 +44,6 @@ import java.util.List; import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; import server.MapleStatEffect; -import server.life.MapleLifeFactory; import server.life.MapleLifeFactory.loseItem; import server.life.MapleMonster; import server.life.MobAttackInfo; @@ -88,16 +87,6 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { } } - if (monsteridfrom == 9300166 && attacker == null) { - if (c.tryacquireClient()) { - try { - attacker = MapleLifeFactory.getMonster(monsteridfrom); - } finally { - c.releaseClient(); - } - } - } - if (attacker != null) { if (attacker.isBuffed(MonsterStatus.NEUTRALISE)) { return; @@ -149,7 +138,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { map.removeMapObject(attacker); } } - } else { + } else if (damagefrom != 0 || !map.removeSelfDestructive(oid)) { // thanks inhyuk for noticing self-destruct damage not being handled properly return; } } catch(ClassCastException e) { @@ -196,21 +185,23 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { } } } + if (damage == -1) { fake = 4020002 + (chr.getJob().getId() / 10 - 40) * 100000; } + if (damage > 0) { + chr.getAutobanManager().resetMisses(); + } else { + chr.getAutobanManager().addMiss(); + } + //in dojo player cannot use pot, so deadly attacks should be turned off as well - if(is_deadly && chr.getMap().isDojoMap() && !ServerConstants.USE_DEADLY_DOJO) { + if(is_deadly && chr.getMap().isDojoMap() && !YamlConfig.config.server.USE_DEADLY_DOJO) { damage = 0; mpattack = 0; } - if (damage == 0) { - chr.getAutobanManager().addMiss(); - } else { - chr.getAutobanManager().resetMisses(); - } if (damage > 0 && !chr.isHidden()) { if (attacker != null) { if (damagefrom == -1) { @@ -222,7 +213,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { map.broadcastMessage(chr, MaplePacketCreator.damageMonster(oid, bouncedamage), false, true); attacker.aggroMonsterDamage(chr, bouncedamage); } - MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.COMBO_BARRIER); + MapleStatEffect bPressure = chr.getBuffEffect(MapleBuffStat.BODY_PRESSURE); // thanks Atoot for noticing an issue on Body Pressure neutralise if (bPressure != null) { Skill skill = SkillFactory.getSkill(Aran.BODY_PRESSURE); if (!attacker.alreadyBuffedStats().contains(MonsterStatus.NEUTRALISE)) { @@ -249,6 +240,12 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { if (achilles != 0 && achilles1 != null) { damage *= (achilles1.getEffect(achilles).getX() / 1000.0); } + + Skill highDef = SkillFactory.getSkill(Aran.HIGH_DEFENSE); + int hdLevel = chr.getSkillLevel(highDef); + if (highDef != null && hdLevel > 0) { + damage *= Math.ceil(highDef.getEffect(hdLevel).getX() / 1000.0); + } } Integer mesoguard = chr.getBuffedValue(MapleBuffStat.MESOGUARD); if (chr.getBuffedValue(MapleBuffStat.MAGIC_GUARD) != null && mpattack == 0) { @@ -285,7 +282,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { map.broadcastGMMessage(chr, MaplePacketCreator.damagePlayer(damagefrom, monsteridfrom, chr.getId(), damage, fake, direction, is_pgmr, pgmr, is_pg, oid, pos_x, pos_y), false); } if (GameConstants.isDojo(map.getId())) { - chr.setDojoEnergy(chr.getDojoEnergy() + ServerConstants.DOJO_ENERGY_DMG); + chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_DMG); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/TransferNameHandler.java b/src/net/server/channel/handlers/TransferNameHandler.java index ef52ba8fbd..17f16e4e2e 100644 --- a/src/net/server/channel/handlers/TransferNameHandler.java +++ b/src/net/server/channel/handlers/TransferNameHandler.java @@ -20,14 +20,25 @@ package net.server.channel.handlers; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Calendar; +import java.sql.Connection; + +import client.MapleCharacter; import client.MapleClient; +import config.YamlConfig; import net.AbstractMaplePacketHandler; +import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** * * @author Ronan + * @author Ubaware */ public final class TransferNameHandler extends AbstractMaplePacketHandler { @@ -40,7 +51,37 @@ public final class TransferNameHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.enableActions()); return; } - - c.announce(MaplePacketCreator.sendNameTransferRules(4)); + if(!YamlConfig.config.server.ALLOW_CASHSHOP_NAME_CHANGE) { + c.announce(MaplePacketCreator.sendNameTransferRules(4)); + return; + } + MapleCharacter chr = c.getPlayer(); + if(chr.getLevel() < 10) { + c.announce(MaplePacketCreator.sendNameTransferRules(4)); + return; + } else if(c.getTempBanCalendar() != null && c.getTempBanCalendar().getTimeInMillis() + (30*24*60*60*1000) < Calendar.getInstance().getTimeInMillis()) { + c.announce(MaplePacketCreator.sendNameTransferRules(2)); + return; + } + //sql queries + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM namechanges WHERE characterid=?")) { //double check, just in case + ps.setInt(1, chr.getId()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + Timestamp completedTimestamp = rs.getTimestamp("completionTime"); + if(completedTimestamp == null) { //has pending name request + c.announce(MaplePacketCreator.sendNameTransferRules(1)); + return; + } else if(completedTimestamp.getTime() + YamlConfig.config.server.NAME_CHANGE_COOLDOWN > System.currentTimeMillis()) { + c.announce(MaplePacketCreator.sendNameTransferRules(3)); + return; + }; + } + } catch(SQLException e) { + e.printStackTrace(); + return; + } + c.announce(MaplePacketCreator.sendNameTransferRules(0)); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/TransferNameResultHandler.java b/src/net/server/channel/handlers/TransferNameResultHandler.java index 3cef139438..aca218c523 100644 --- a/src/net/server/channel/handlers/TransferNameResultHandler.java +++ b/src/net/server/channel/handlers/TransferNameResultHandler.java @@ -35,6 +35,6 @@ public final class TransferNameResultHandler extends AbstractMaplePacketHandler @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { String name = slea.readMapleAsciiString(); - c.announce(MaplePacketCreator.sendNameTransferCheck(MapleCharacter.canCreateChar(name))); + c.announce(MaplePacketCreator.sendNameTransferCheck(name, MapleCharacter.canCreateChar(name))); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/TransferWorldHandler.java b/src/net/server/channel/handlers/TransferWorldHandler.java index eb2c8e1af4..83a27d8bce 100644 --- a/src/net/server/channel/handlers/TransferWorldHandler.java +++ b/src/net/server/channel/handlers/TransferWorldHandler.java @@ -20,14 +20,25 @@ package net.server.channel.handlers; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; + +import client.MapleCharacter; import client.MapleClient; +import config.YamlConfig; import net.AbstractMaplePacketHandler; +import net.server.Server; +import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** * * @author Ronan + * @author Ubaware */ public final class TransferWorldHandler extends AbstractMaplePacketHandler { @@ -40,7 +51,34 @@ public final class TransferWorldHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.enableActions()); return; } - - c.announce(MaplePacketCreator.sendWorldTransferRules(9)); + MapleCharacter chr = c.getPlayer(); + if(!YamlConfig.config.server.ALLOW_CASHSHOP_WORLD_TRANSFER || Server.getInstance().getWorldsSize() <= 1) { + c.announce(MaplePacketCreator.sendWorldTransferRules(9, c)); + return; + } + int worldTransferError = chr.checkWorldTransferEligibility(); + if(worldTransferError != 0) { + c.announce(MaplePacketCreator.sendWorldTransferRules(worldTransferError, c)); + return; + } + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT completionTime FROM worldtransfers WHERE characterid=?")) { + ps.setInt(1, chr.getId()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + Timestamp completedTimestamp = rs.getTimestamp("completionTime"); + if(completedTimestamp == null) { //has pending world transfer + c.announce(MaplePacketCreator.sendWorldTransferRules(6, c)); + return; + } else if(completedTimestamp.getTime() + YamlConfig.config.server.WORLD_TRANSFER_COOLDOWN > System.currentTimeMillis()) { + c.announce(MaplePacketCreator.sendWorldTransferRules(7, c)); + return; + }; + } + } catch(SQLException e) { + e.printStackTrace(); + return; + } + c.announce(MaplePacketCreator.sendWorldTransferRules(0, c)); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java index 9b03ecf0ef..0f3a7ae2ad 100644 --- a/src/net/server/channel/handlers/UseCashItemHandler.java +++ b/src/net/server/channel/handlers/UseCashItemHandler.java @@ -25,6 +25,7 @@ import client.MapleCharacter; import client.MapleClient; import client.Skill; import client.SkillFactory; +import client.SkillMacro; import client.creator.veteran.*; import client.inventory.Equip; import client.inventory.Equip.ScrollResult; @@ -35,11 +36,12 @@ import client.inventory.MaplePet; import client.inventory.ModifyInventory; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import client.processor.AssignAPProcessor; -import client.processor.DueyProcessor; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import client.processor.stat.AssignAPProcessor; +import client.processor.stat.AssignSPProcessor; +import client.processor.npc.DueyProcessor; +import config.YamlConfig; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; import java.sql.SQLException; import java.util.ArrayList; @@ -156,6 +158,10 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { if (itemId > 5050000) { int SPTo = slea.readInt(); + if (!AssignSPProcessor.canSPAssign(c, SPTo)) { // exploit found thanks to Arnah + return; + } + int SPFrom = slea.readInt(); Skill skillSPTo = SkillFactory.getSkill(SPTo); Skill skillSPFrom = SkillFactory.getSkill(SPFrom); @@ -164,6 +170,33 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { if ((curLevel < skillSPTo.getMaxLevel()) && curLevelSPFrom > 0) { player.changeSkillLevel(skillSPFrom, (byte) (curLevelSPFrom - 1), player.getMasterLevel(skillSPFrom), -1); player.changeSkillLevel(skillSPTo, (byte) (curLevel + 1), player.getMasterLevel(skillSPTo), -1); + + // update macros, thanks to Arnah + if((curLevelSPFrom - 1) == 0){ + boolean updated = false; + for(SkillMacro macro : player.getMacros()){ + if(macro == null) continue; + + boolean update = false;// cleaner? + if(macro.getSkill1() == SPFrom){ + update = true; + macro.setSkill1(0); + } + if(macro.getSkill2() == SPFrom){ + update = true; + macro.setSkill2(0); + } + if(macro.getSkill3() == SPFrom){ + update = true; + macro.setSkill3(0); + } + if(update){ + updated = true; + player.updateMacros(macro.getPosition(), macro); + } + } + if(updated) player.sendMacros(); + } } } else { int APTo = slea.readInt(); @@ -189,7 +222,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { if (eq == null) { //Check if the type is EQUIPMENT? return; } - byte flag = eq.getFlag(); + short flag = eq.getFlag(); flag |= ItemConstants.LOCK; if (eq.getExpiration() > -1) { return; //No perma items pls @@ -294,11 +327,9 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { if (item == null) //hack { return; - } else if (item.isUntradeable() || ii.isUnmerchable(item.getItemId())) { - player.dropMessage(1, "You cannot trade this item."); - c.announce(MaplePacketCreator.enableActions()); - return; } + + // thanks Conrad for noticing that untradeable items should be allowed in megas } Server.getInstance().broadcastMessage(c.getWorld(), MaplePacketCreator.itemMegaphone(msg, whisper, c.getChannel(), item)); break; @@ -370,7 +401,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } else if (itemType == 523) { int itemid = slea.readInt(); - if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) c.getWorldServer().addOwlItemSearch(itemid); + if(!YamlConfig.config.server.USE_ENFORCE_ITEM_SUGGESTION) c.getWorldServer().addOwlItemSearch(itemid); player.setOwlSearch(itemid); List> hmsAvailable = c.getWorldServer().getAvailableItemBundles(itemid); if(!hmsAvailable.isEmpty()) remove(c, position, itemId); @@ -398,7 +429,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { ii.getItemEffect(itemId).applyTo(player); remove(c, position, itemId); } else if (itemType == 533) { - DueyProcessor.dueySendTalk(c); + DueyProcessor.dueySendTalk(c, true); } else if (itemType == 537) { if (GameConstants.isFreeMarketRoom(player.getMapId())) { player.dropMessage(5, "You cannot use the chalkboard here."); @@ -425,6 +456,16 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } }, 1000 * 10); remove(c, position, itemId); + } else if (itemType == 540) { + slea.readByte(); + slea.readInt(); + if(itemId == 5400000) { //name change + c.announce(MaplePacketCreator.showNameChangeCancel(player.cancelPendingNameChange())); + } else if(itemId == 5401000) { //world transfer + c.announce(MaplePacketCreator.showWorldTransferCancel(player.cancelPendingWorldTranfer())); + } + remove(c, position, itemId); + c.announce(MaplePacketCreator.enableActions()); } else if (itemType == 543) { if(itemId == 5432000 && !c.gainCharacterSlot()) { player.dropMessage(1, "You have already used up all 12 extra character slots."); @@ -569,7 +610,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { client.announce(MaplePacketCreator.modifyInventory(true, mods)); ScrollResult scrollResult = scrolled.getLevel() > curlevel ? ScrollResult.SUCCESS : ScrollResult.FAIL; - player.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(player.getId(), scrollResult, false)); + player.getMap().broadcastMessage(MaplePacketCreator.getScrollEffect(player.getId(), scrollResult, false, false)); if (eSlot < 0 && (scrollResult == ScrollResult.SUCCESS)) { player.equipChanged(); } @@ -594,11 +635,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { position = it.getPosition(); } } + + MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.CASH, position, (short) 1, true, false); } finally { cashInv.unlockInventory(); } - - MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.CASH, position, (short) 1, true, false); } private static boolean getIncubatedItem(MapleClient c, int id) { diff --git a/src/net/server/channel/handlers/UseCatchItemHandler.java b/src/net/server/channel/handlers/UseCatchItemHandler.java index 97e55b0ff3..ef3ee4b444 100644 --- a/src/net/server/channel/handlers/UseCatchItemHandler.java +++ b/src/net/server/channel/handlers/UseCatchItemHandler.java @@ -25,7 +25,7 @@ import client.MapleCharacter; import client.MapleClient; import client.inventory.MapleInventoryType; import client.autoban.AutobanManager; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import net.server.Server; import client.inventory.manipulator.MapleInventoryManipulator; diff --git a/src/net/server/channel/handlers/UseChairHandler.java b/src/net/server/channel/handlers/UseChairHandler.java index 2b3989d3e7..857fcdb704 100644 --- a/src/net/server/channel/handlers/UseChairHandler.java +++ b/src/net/server/channel/handlers/UseChairHandler.java @@ -23,6 +23,7 @@ package net.server.channel.handlers; import client.MapleClient; import client.inventory.MapleInventoryType; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; @@ -30,10 +31,18 @@ public final class UseChairHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int itemId = slea.readInt(); - if (c.getPlayer().getInventory(MapleInventoryType.SETUP).findById(itemId) == null) { + + // thanks Darter (YungMoozi) for reporting unchecked chair item + if (!ItemConstants.isChair(itemId) || c.getPlayer().getInventory(MapleInventoryType.SETUP).findById(itemId) == null) { return; } - c.getPlayer().sitChair(itemId); + if (c.tryacquireClient()) { + try { + c.getPlayer().sitChair(itemId); + } finally { + c.releaseClient(); + } + } } } diff --git a/src/net/server/channel/handlers/UseItemHandler.java b/src/net/server/channel/handlers/UseItemHandler.java index 54c03dd1ed..61f04cfec5 100644 --- a/src/net/server/channel/handlers/UseItemHandler.java +++ b/src/net/server/channel/handlers/UseItemHandler.java @@ -26,8 +26,8 @@ import client.MapleClient; import client.MapleDisease; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; import server.MapleItemInformationProvider; @@ -53,7 +53,7 @@ public final class UseItemHandler extends AbstractMaplePacketHandler { int itemId = slea.readInt(); Item toUse = chr.getInventory(MapleInventoryType.USE).getItem(slot); if (toUse != null && toUse.getQuantity() > 0 && toUse.getItemId() == itemId) { - if (itemId == 2022178 || itemId == 2050004) { + if (itemId == 2050004) { chr.dispelDebuffs(); remove(c, slot); return; @@ -77,7 +77,7 @@ public final class UseItemHandler extends AbstractMaplePacketHandler { long banTime = currentServerTime(); if (ii.getItemEffect(toUse.getItemId()).applyTo(chr)) { - if(ServerConstants.USE_BANISHABLE_TOWN_SCROLL) { + if(YamlConfig.config.server.USE_BANISHABLE_TOWN_SCROLL) { chr.setBanishPlayerData(banMap, banSp, banTime); } diff --git a/src/net/server/channel/handlers/UseMountFoodHandler.java b/src/net/server/channel/handlers/UseMountFoodHandler.java index a626d5ec1c..3325baa280 100644 --- a/src/net/server/channel/handlers/UseMountFoodHandler.java +++ b/src/net/server/channel/handlers/UseMountFoodHandler.java @@ -27,11 +27,10 @@ import client.MapleMount; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; -import constants.ExpTable; +import constants.game.ExpTable; import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; import tools.MaplePacketCreator; -import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; /** diff --git a/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java b/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java index 5a76dffe32..10268306cd 100644 --- a/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java +++ b/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java @@ -30,7 +30,7 @@ import java.util.LinkedList; import java.util.List; import java.util.PriorityQueue; -import constants.GameConstants; +import constants.game.GameConstants; /** * @author Ronan diff --git a/src/net/server/channel/handlers/WeddingHandler.java b/src/net/server/channel/handlers/WeddingHandler.java index c0526d614c..677f119786 100644 --- a/src/net/server/channel/handlers/WeddingHandler.java +++ b/src/net/server/channel/handlers/WeddingHandler.java @@ -14,8 +14,8 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import net.AbstractMaplePacketHandler; import server.MapleMarriage; import tools.MaplePacketCreator; @@ -50,7 +50,7 @@ public final class WeddingHandler extends AbstractMaplePacketHandler { String groomWishlistProp = "giftedItem" + (groomWishlist ? "G" : "B") + chr.getId(); int giftCount = marriage.getIntProperty(groomWishlistProp); - if (giftCount < ServerConstants.WEDDING_GIFT_LIMIT) { + if (giftCount < YamlConfig.config.server.WEDDING_GIFT_LIMIT) { int cid = marriage.getIntProperty(groomWishlist ? "groomId" : "brideId"); if (chr.getId() != cid) { // cannot gift yourself MapleCharacter spouse = marriage.getPlayerById(cid); @@ -58,20 +58,18 @@ public final class WeddingHandler extends AbstractMaplePacketHandler { MapleInventoryType type = ItemConstants.getInventoryType(itemid); MapleInventory chrInv = chr.getInventory(type); + Item newItem = null; chrInv.lockInventory(); try { Item item = chrInv.getItem((byte) slot); if (item != null) { if (!item.isUntradeable()) { if (itemid == item.getItemId() && quantity <= item.getQuantity()) { - Item newItem = item.copy(); + newItem = item.copy(); marriage.addGiftItem(groomWishlist, newItem); MapleInventoryManipulator.removeFromSlot(c, type, slot, quantity, false, false); - - if (ServerConstants.USE_ENFORCE_MERCHANT_SAVE) chr.saveCharToDB(false); - marriage.saveGiftItemsToDb(c, groomWishlist, cid); - + MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem); marriage.setIntProperty(groomWishlistProp, giftCount + 1); @@ -84,6 +82,11 @@ public final class WeddingHandler extends AbstractMaplePacketHandler { } finally { chrInv.unlockInventory(); } + + if (newItem != null) { + if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) chr.saveCharToDB(false); + marriage.saveGiftItemsToDb(c, groomWishlist, cid); + } } else { c.announce(Wedding.OnWeddingGiftResult((byte) 0xE, marriage.getWishlistItems(groomWishlist), null)); } diff --git a/src/net/server/channel/handlers/WhisperHandler.java b/src/net/server/channel/handlers/WhisperHandler.java index 6126ae5958..816bf60d5f 100644 --- a/src/net/server/channel/handlers/WhisperHandler.java +++ b/src/net/server/channel/handlers/WhisperHandler.java @@ -25,7 +25,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import constants.ServerConstants; +import config.YamlConfig; import net.AbstractMaplePacketHandler; import net.server.world.World; import tools.DatabaseConnection; @@ -62,7 +62,7 @@ public final class WhisperHandler extends AbstractMaplePacketHandler { } if (player != null) { player.getClient().announce(MaplePacketCreator.getWhisper(c.getPlayer().getName(), c.getChannel(), text)); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Whisper To " + player.getName(), text); } if(player.isHidden() && player.gmLevel() >= c.getPlayer().gmLevel()) { @@ -74,7 +74,7 @@ public final class WhisperHandler extends AbstractMaplePacketHandler { World world = c.getWorldServer(); if (world.isConnected(recipient)) { world.whisper(c.getPlayer().getName(), recipient, c.getChannel(), text); - if (ServerConstants.USE_ENABLE_CHAT_LOG) { + if (YamlConfig.config.server.USE_ENABLE_CHAT_LOG) { LogHelper.logChat(c, "Whisper To " + recipient, text); } player = world.getPlayerStorage().getCharacterByName(recipient); diff --git a/src/net/server/channel/worker/FaceExpressionScheduler.java b/src/net/server/channel/worker/FaceExpressionScheduler.java deleted file mode 100644 index b826609c01..0000000000 --- a/src/net/server/channel/worker/FaceExpressionScheduler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.server.channel.worker; - -import java.util.Collections; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; - -/** - * - * @author Ronan - */ -public class FaceExpressionScheduler extends BaseScheduler { - public FaceExpressionScheduler(final MonitoredReentrantLock channelFaceLock) { - super(MonitoredLockType.CHANNEL_FACESCHDL, Collections.singletonList(channelFaceLock)); - } - - public void registerFaceExpression(Integer characterId, Runnable runAction) { - registerEntry(characterId, runAction, 5000); - } - - public void unregisterFaceExpression(Integer characterId) { - interruptEntry(characterId); - } -} diff --git a/src/net/server/channel/worker/MobAnimationScheduler.java b/src/net/server/channel/worker/MobAnimationScheduler.java deleted file mode 100644 index 45676a0eb4..0000000000 --- a/src/net/server/channel/worker/MobAnimationScheduler.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.server.channel.worker; - -import net.server.audit.locks.MonitoredLockType; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import net.server.audit.LockCollector; -import net.server.audit.locks.MonitoredReentrantLock; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; - -/** - * - * @author Ronan - */ -public class MobAnimationScheduler extends BaseScheduler { - Set onAnimationMobs = new HashSet<>(1000); - private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_MOBANIMAT, true); - - private static Runnable r = new Runnable() { - @Override - public void run() {} // do nothing - }; - - public MobAnimationScheduler() { - super(MonitoredLockType.CHANNEL_MOBACTION); - - super.addListener(new SchedulerListener() { - @Override - public void removedScheduledEntries(List toRemove, boolean update) { - animationLock.lock(); - try { - for(Object hashObj : toRemove) { - Integer mobHash = (Integer) hashObj; - onAnimationMobs.remove(mobHash); - } - } finally { - animationLock.unlock(); - } - } - }); - } - - public boolean registerAnimationMode(Integer mobHash, long animationTime) { - animationLock.lock(); - try { - if(onAnimationMobs.contains(mobHash)) { - return false; - } - - registerEntry(mobHash, r, animationTime); - onAnimationMobs.add(mobHash); - return true; - } finally { - animationLock.unlock(); - } - } - - @Override - public void dispose() { - disposeLocks(); - super.dispose(); - } - - private void disposeLocks() { - LockCollector.getInstance().registerDisposeAction(new Runnable() { - @Override - public void run() { - emptyLocks(); - } - }); - } - - private void emptyLocks() { - animationLock = animationLock.dispose(); - } -} diff --git a/src/net/server/channel/worker/MobMistScheduler.java b/src/net/server/channel/worker/MobMistScheduler.java deleted file mode 100644 index 050157457e..0000000000 --- a/src/net/server/channel/worker/MobMistScheduler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.server.channel.worker; - -import net.server.audit.locks.MonitoredLockType; - -/** - * - * @author Ronan - */ -public class MobMistScheduler extends BaseScheduler { - public MobMistScheduler() { - super(MonitoredLockType.CHANNEL_MOBMIST); - } - - public void registerMistCancelAction(Runnable runAction, long delay) { - registerEntry(runAction, runAction, delay); - } -} diff --git a/src/net/server/channel/worker/MobStatusScheduler.java b/src/net/server/channel/worker/MobStatusScheduler.java deleted file mode 100644 index 2d05e53d0a..0000000000 --- a/src/net/server/channel/worker/MobStatusScheduler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2019 RonanLana - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.server.channel.worker; - -import client.status.MonsterStatusEffect; -import constants.ServerConstants; -import java.util.HashMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import net.server.audit.LockCollector; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; - -/** - * - * @author Ronan - */ -public class MobStatusScheduler extends BaseScheduler { - private Map registeredMobStatusOvertime = new HashMap<>(); - private MonitoredReentrantLock overtimeStatusLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_OVTSTATUS, true); - - private class MobStatusOvertimeEntry { - private int procCount; - private int procLimit; - private Runnable r; - - protected MobStatusOvertimeEntry(int delay, Runnable run) { - procCount = 0; - procLimit = (int)Math.ceil((float) delay / ServerConstants.MOB_STATUS_MONITOR_PROC); - r = run; - } - - protected void update(List toRun) { - procCount++; - if(procCount >= procLimit) { - procCount = 0; - toRun.add(r); - } - } - } - - public MobStatusScheduler() { - super(MonitoredLockType.CHANNEL_MOBSTATUS); - - super.addListener(new SchedulerListener() { - @Override - public void removedScheduledEntries(List toRemove, boolean update) { - List toRun = new ArrayList<>(); - - overtimeStatusLock.lock(); - try { - for(Object mseo : toRemove) { - MonsterStatusEffect mse = (MonsterStatusEffect) mseo; - registeredMobStatusOvertime.remove(mse); - } - - if(update) { - // it's probably ok to use one thread for both management & overtime actions - List mdoeList = new ArrayList<>(registeredMobStatusOvertime.values()); - for(MobStatusOvertimeEntry mdoe : mdoeList) { - mdoe.update(toRun); - } - } - } finally { - overtimeStatusLock.unlock(); - } - - for(Runnable r : toRun) { - r.run(); - } - } - }); - } - - public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) { - if(overtimeStatus != null) { - MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus); - - overtimeStatusLock.lock(); - try { - registeredMobStatusOvertime.put(mse, mdoe); - } finally { - overtimeStatusLock.unlock(); - } - } - - registerEntry(mse, cancelStatus, duration); - } - - public void interruptMobStatus(MonsterStatusEffect mse) { - interruptEntry(mse); - } - - @Override - public void dispose() { - disposeLocks(); - super.dispose(); - } - - private void disposeLocks() { - LockCollector.getInstance().registerDisposeAction(new Runnable() { - @Override - public void run() { - emptyLocks(); - } - }); - } - - private void emptyLocks() { - overtimeStatusLock = overtimeStatusLock.dispose(); - } -} diff --git a/src/net/server/coordinator/LoginStorage.java b/src/net/server/coordinator/login/LoginStorage.java similarity index 79% rename from src/net/server/coordinator/LoginStorage.java rename to src/net/server/coordinator/login/LoginStorage.java index dff9d5a85d..6c2bf3dafe 100644 --- a/src/net/server/coordinator/LoginStorage.java +++ b/src/net/server/coordinator/login/LoginStorage.java @@ -17,9 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.login; -import constants.ServerConstants; +import config.YamlConfig; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -37,22 +37,21 @@ public class LoginStorage { private ConcurrentHashMap> loginHistory = new ConcurrentHashMap<>(); public boolean registerLogin(int accountId) { - List accHist = loginHistory.putIfAbsent(accountId, new LinkedList()); - if (accHist != null) { - synchronized (accHist) { - if (accHist.size() > ServerConstants.MAX_ACCOUNT_LOGIN_ATTEMPT) { - long blockExpiration = Server.getInstance().getCurrentTime() + ServerConstants.LOGIN_ATTEMPT_DURATION; - Collections.fill(accHist, blockExpiration); - - return false; - } - } - } else { - accHist = loginHistory.get(accountId); + List accHist = loginHistory.get(accountId); + if (accHist == null) { + accHist = new LinkedList(); + loginHistory.put(accountId, accHist); } synchronized (accHist) { - accHist.add(Server.getInstance().getCurrentTime() + ServerConstants.LOGIN_ATTEMPT_DURATION); + if (accHist.size() > YamlConfig.config.server.MAX_ACCOUNT_LOGIN_ATTEMPT) { + long blockExpiration = Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION; + Collections.fill(accHist, blockExpiration); + + return false; + } + + accHist.add(Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION); return true; } } diff --git a/src/net/server/coordinator/MapleLoginBypassCoordinator.java b/src/net/server/coordinator/login/MapleLoginBypassCoordinator.java similarity index 95% rename from src/net/server/coordinator/MapleLoginBypassCoordinator.java rename to src/net/server/coordinator/login/MapleLoginBypassCoordinator.java index a66f3ce271..30c111ab3d 100644 --- a/src/net/server/coordinator/MapleLoginBypassCoordinator.java +++ b/src/net/server/coordinator/login/MapleLoginBypassCoordinator.java @@ -17,9 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.login; -import constants.ServerConstants; +import config.YamlConfig; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -59,7 +59,7 @@ public class MapleLoginBypassCoordinator { } public void registerLoginBypassEntry(String nibbleHwid, int accId, boolean pic) { - long expireTime = (pic ? ServerConstants.BYPASS_PIC_EXPIRATION : ServerConstants.BYPASS_PIN_EXPIRATION); + long expireTime = (pic ? YamlConfig.config.server.BYPASS_PIC_EXPIRATION : YamlConfig.config.server.BYPASS_PIN_EXPIRATION); if (expireTime > 0) { Pair entry = new Pair<>(nibbleHwid, accId); expireTime = Server.getInstance().getCurrentTime() + expireTime * 60 * 1000; diff --git a/src/net/server/coordinator/MapleMatchCheckerCoordinator.java b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java similarity index 87% rename from src/net/server/coordinator/MapleMatchCheckerCoordinator.java rename to src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java index 5d4984c00c..8f5abda2ac 100644 --- a/src/net/server/coordinator/MapleMatchCheckerCoordinator.java +++ b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java @@ -17,12 +17,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.matchchecker; import client.MapleCharacter; import net.server.PlayerStorage; import net.server.Server; -import net.server.coordinator.matchchecker.AbstractMatchCheckerListener; import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType; import net.server.world.World; import java.util.Collections; @@ -260,19 +259,19 @@ public class MapleMatchCheckerCoordinator { } } - private void createMatchConfirmationInternal(MatchCheckerType matchType, int world, int leaderCid, AbstractMatchCheckerListener leaderListener, Set players, String message) { + private MapleMatchCheckingElement createMatchConfirmationInternal(MatchCheckerType matchType, int world, int leaderCid, AbstractMatchCheckerListener leaderListener, Set players, String message) { MapleMatchCheckingElement mmce = new MapleMatchCheckingElement(matchType, leaderCid, world, leaderListener, players, message); for (Integer cid : players) { matchEntries.put(cid, mmce); } - mmce.dispatchMatchCreated(); - acceptMatchElement(mmce, leaderCid); + return mmce; } public boolean createMatchConfirmation(MatchCheckerType matchType, int world, int leaderCid, Set players, String message) { + MapleMatchCheckingElement mmce = null; try { semaphorePool.acquire(); try { @@ -280,8 +279,7 @@ public class MapleMatchCheckerCoordinator { try { if (isMatchingAvailable(players)) { AbstractMatchCheckerListener leaderListener = matchType.getListener(); - createMatchConfirmationInternal(matchType, world, leaderCid, leaderListener, players, message); - return true; + mmce = createMatchConfirmationInternal(matchType, world, leaderCid, leaderListener, players, message); } else { reenablePlayerMatching(players); } @@ -296,11 +294,16 @@ public class MapleMatchCheckerCoordinator { ie.printStackTrace(); } - return false; + if (mmce != null) { + mmce.dispatchMatchCreated(); + return true; + } else { + return false; + } } private void disposeMatchElement(MapleMatchCheckingElement mmce) { - Set matchPlayers = mmce.getAcceptedMatchPlayers(); + Set matchPlayers = mmce.getMatchPlayers(); // thanks Ai for noticing players getting match-stuck on certain cases while (!poolMatchPlayers(matchPlayers)) { try { Thread.sleep(1000); @@ -320,16 +323,12 @@ public class MapleMatchCheckerCoordinator { if (mmce.acceptEntry(cid)) { unpoolMatchPlayer(cid); disposeMatchElement(mmce); - - mmce.dispatchMatchResult(true); } } private void denyMatchElement(MapleMatchCheckingElement mmce, int cid) { unpoolMatchPlayer(cid); disposeMatchElement(mmce); - - mmce.dispatchMatchResult(false); } private void dismissMatchElement(MapleMatchCheckingElement mmce, int cid) { @@ -337,35 +336,32 @@ public class MapleMatchCheckerCoordinator { unpoolMatchPlayer(cid); disposeMatchElement(mmce); - - mmce.dispatchMatchDismissed(); } public boolean answerMatchConfirmation(int cid, boolean accept) { + MapleMatchCheckingElement mmce = null; try { semaphorePool.acquire(); try { while (matchEntries.containsKey(cid)) { if (poolMatchPlayer(cid)) { try { - MapleMatchCheckingElement mmce = matchEntries.get(cid); + mmce = matchEntries.get(cid); if (mmce != null) { synchronized (mmce) { if (!mmce.isMatchActive()) { // thanks Alex (CanIGetaPR) for noticing that exploiters could stall on match checking matchEntries.remove(cid); - return false; - } - - if (accept) { - acceptMatchElement(mmce, cid); + mmce = null; } else { - denyMatchElement(mmce, cid); - matchEntries.remove(cid); + if (accept) { + acceptMatchElement(mmce, cid); + } else { + denyMatchElement(mmce, cid); + matchEntries.remove(cid); + } } } - - return true; } } finally { unpoolMatchPlayer(cid); @@ -379,26 +375,30 @@ public class MapleMatchCheckerCoordinator { ie.printStackTrace(); } + if (mmce != null) { + mmce.dispatchMatchResult(accept); + } + return false; } public boolean dismissMatchConfirmation(int cid) { + MapleMatchCheckingElement mmce = null; try { semaphorePool.acquire(); try { while (matchEntries.containsKey(cid)) { if (poolMatchPlayer(cid)) { try { - MapleMatchCheckingElement mmce = matchEntries.get(cid); + mmce = matchEntries.get(cid); if (mmce != null) { synchronized (mmce) { if (!mmce.isMatchActive()) { - return false; + mmce = null; + } else { + dismissMatchElement(mmce, cid); } - - dismissMatchElement(mmce, cid); - return true; } } } finally { @@ -413,7 +413,12 @@ public class MapleMatchCheckerCoordinator { ie.printStackTrace(); } - return false; + if (mmce != null) { + mmce.dispatchMatchDismissed(); + return true; + } else { + return false; + } } } diff --git a/src/net/server/coordinator/matchchecker/listener/MatchCheckerCPQChallenge.java b/src/net/server/coordinator/matchchecker/listener/MatchCheckerCPQChallenge.java index 0fbe0af099..0069f02735 100644 --- a/src/net/server/coordinator/matchchecker/listener/MatchCheckerCPQChallenge.java +++ b/src/net/server/coordinator/matchchecker/listener/MatchCheckerCPQChallenge.java @@ -20,7 +20,7 @@ package net.server.coordinator.matchchecker.listener; import client.MapleCharacter; -import constants.LanguageConstants; +import constants.string.LanguageConstants; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -71,7 +71,9 @@ public class MatchCheckerCPQChallenge implements MatchCheckerListenerRecipe { List chrMembers = new LinkedList<>(); for (MaplePartyCharacter mpc : chr.getParty().getMembers()) { - chrMembers.add(mpc); + if (mpc.isOnline()) { + chrMembers.add(mpc); + } } if (message.contentEquals("cpq1")) { diff --git a/src/net/server/coordinator/matchchecker/listener/MatchCheckerGuildCreation.java b/src/net/server/coordinator/matchchecker/listener/MatchCheckerGuildCreation.java index f1e674be8f..6c42b38dbb 100644 --- a/src/net/server/coordinator/matchchecker/listener/MatchCheckerGuildCreation.java +++ b/src/net/server/coordinator/matchchecker/listener/MatchCheckerGuildCreation.java @@ -20,8 +20,8 @@ package net.server.coordinator.matchchecker.listener; import client.MapleCharacter; -import constants.GameConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.GameConstants; import net.server.coordinator.matchchecker.AbstractMatchCheckerListener; import net.server.coordinator.matchchecker.MatchCheckerListenerRecipe; import net.server.guild.MapleGuild; @@ -103,8 +103,8 @@ public class MatchCheckerGuildCreation implements MatchCheckerListenerRecipe { return; } } - if (leader.getMeso() < ServerConstants.CREATE_GUILD_COST) { - leader.dropMessage(1, "You do not have " + GameConstants.numberWithCommas(ServerConstants.CREATE_GUILD_COST) + " mesos to create a Guild."); + if (leader.getMeso() < YamlConfig.config.server.CREATE_GUILD_COST) { + leader.dropMessage(1, "You do not have " + GameConstants.numberWithCommas(YamlConfig.config.server.CREATE_GUILD_COST) + " mesos to create a Guild."); broadcastGuildCreationDismiss(matchPlayers); return; } @@ -115,7 +115,7 @@ public class MatchCheckerGuildCreation implements MatchCheckerListenerRecipe { broadcastGuildCreationDismiss(matchPlayers); return; } - leader.gainMeso(-ServerConstants.CREATE_GUILD_COST, true, false, true); + leader.gainMeso(-YamlConfig.config.server.CREATE_GUILD_COST, true, false, true); leader.getMGC().setGuildId(gid); MapleGuild guild = Server.getInstance().getGuild(leader.getGuildId(), leader.getWorld(), leader); // initialize guild structure @@ -159,7 +159,7 @@ public class MatchCheckerGuildCreation implements MatchCheckerListenerRecipe { } if (chr.isLoggedinWorld()) { - chr.announce(MaplePacketCreator.genericGuildMessage((byte)0x24)); + chr.announce(MaplePacketCreator.genericGuildMessage((byte)0x26)); } } } @@ -189,6 +189,7 @@ public class MatchCheckerGuildCreation implements MatchCheckerListenerRecipe { if (chr.isLoggedinWorld()) { chr.message(msg); + chr.announce(MaplePacketCreator.genericGuildMessage((byte)0x26)); } } } diff --git a/src/net/server/coordinator/MaplePartySearchCoordinator.java b/src/net/server/coordinator/partysearch/MaplePartySearchCoordinator.java similarity index 92% rename from src/net/server/coordinator/MaplePartySearchCoordinator.java rename to src/net/server/coordinator/partysearch/MaplePartySearchCoordinator.java index 28bbd78918..fd0679f74e 100644 --- a/src/net/server/coordinator/MaplePartySearchCoordinator.java +++ b/src/net/server/coordinator/partysearch/MaplePartySearchCoordinator.java @@ -17,16 +17,14 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.partysearch; import client.MapleCharacter; import client.MapleJob; -import constants.ServerConstants; +import config.YamlConfig; import java.io.File; import net.server.world.MapleParty; -import net.server.coordinator.MapleInviteCoordinator.InviteType; -import net.server.coordinator.partysearch.PartySearchEchelon; -import net.server.coordinator.partysearch.PartySearchStorage; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; import tools.MaplePacketCreator; import tools.Pair; @@ -38,11 +36,13 @@ import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; +import net.server.coordinator.world.MapleInviteCoordinator; import provider.MapleData; import provider.MapleDataProviderFactory; import provider.MapleDataTool; @@ -57,9 +57,9 @@ public class MaplePartySearchCoordinator { private Map upcomers = new HashMap<>(); private List leaderQueue = new LinkedList<>(); - private final ReentrantReadWriteLock leaderQueueLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_QUEUE, true); - private final ReadLock leaderQueueRLock = leaderQueueLock.readLock(); - private final WriteLock leaderQueueWLock = leaderQueueLock.writeLock(); + private final MonitoredReentrantReadWriteLock leaderQueueLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_QUEUE, true); + private final MonitoredReadLock leaderQueueRLock = MonitoredReadLockFactory.createLock(leaderQueueLock); + private final MonitoredWriteLock leaderQueueWLock = MonitoredWriteLockFactory.createLock(leaderQueueLock); private Map searchLeaders = new HashMap<>(); private Map searchSettings = new HashMap<>(); @@ -370,7 +370,7 @@ public class MaplePartySearchCoordinator { } else { LeaderSearchMetadata settings = searchSettings.get(leader.getId()); if (settings != null) { - if (settings.reentryCount < ServerConstants.PARTY_SEARCH_REENTRY_LIMIT) { + if (settings.reentryCount < YamlConfig.config.server.PARTY_SEARCH_REENTRY_LIMIT) { settings.reentryCount += 1; recalledLeaders.add(leader); } else { @@ -413,7 +413,7 @@ public class MaplePartySearchCoordinator { if (leader.isLoggedinWorld()) { if (settings != null) { recycledLeaders.add(new Pair<>(leader, settings)); - if (ServerConstants.USE_DEBUG && leader.isGM()) leader.dropMessage(5, "Your Party Search token session is now on waiting queue for up to 7 minutes, to get it working right away please stop your Party Search and retry again later."); + if (YamlConfig.config.server.USE_DEBUG && leader.isGM()) leader.dropMessage(5, "Your Party Search token session is now on waiting queue for up to 7 minutes, to get it working right away please stop your Party Search and retry again later."); } else { leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later."); } diff --git a/src/net/server/coordinator/partysearch/PartySearchCharacter.java b/src/net/server/coordinator/partysearch/PartySearchCharacter.java index c8ed1c05fa..6d95e43007 100644 --- a/src/net/server/coordinator/partysearch/PartySearchCharacter.java +++ b/src/net/server/coordinator/partysearch/PartySearchCharacter.java @@ -20,7 +20,6 @@ package net.server.coordinator.partysearch; import client.MapleCharacter; -import net.server.coordinator.MaplePartySearchCoordinator; import java.lang.ref.WeakReference; diff --git a/src/net/server/coordinator/partysearch/PartySearchEchelon.java b/src/net/server/coordinator/partysearch/PartySearchEchelon.java index 39c5f2410d..fca40326ad 100644 --- a/src/net/server/coordinator/partysearch/PartySearchEchelon.java +++ b/src/net/server/coordinator/partysearch/PartySearchEchelon.java @@ -25,12 +25,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import java.lang.ref.WeakReference; @@ -40,9 +40,9 @@ import java.lang.ref.WeakReference; */ public class PartySearchEchelon { - private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_ECHELON, true); - private final ReadLock psRLock = psLock.readLock(); - private final WriteLock psWLock = psLock.writeLock(); + private final MonitoredReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_ECHELON, true); + private final MonitoredReadLock psRLock = MonitoredReadLockFactory.createLock(psLock); + private final MonitoredWriteLock psWLock = MonitoredWriteLockFactory.createLock(psLock); private Map> echelon = new HashMap<>(20); diff --git a/src/net/server/coordinator/partysearch/PartySearchStorage.java b/src/net/server/coordinator/partysearch/PartySearchStorage.java index 4127af9b5f..059ff89d15 100644 --- a/src/net/server/coordinator/partysearch/PartySearchStorage.java +++ b/src/net/server/coordinator/partysearch/PartySearchStorage.java @@ -28,12 +28,14 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; + import tools.IntervalBuilder; /** @@ -45,9 +47,9 @@ public class PartySearchStorage { private List storage = new ArrayList<>(20); private IntervalBuilder emptyIntervals = new IntervalBuilder(); - private final ReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true); - private final ReadLock psRLock = psLock.readLock(); - private final WriteLock psWLock = psLock.writeLock(); + private final MonitoredReentrantReadWriteLock psLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_PARTY_SEARCH_STORAGE, true); + private final MonitoredReadLock psRLock = MonitoredReadLockFactory.createLock(psLock); + private final MonitoredWriteLock psWLock = MonitoredWriteLockFactory.createLock(psLock); public List getStorageList() { psRLock.lock(); diff --git a/src/net/server/coordinator/MapleSessionCoordinator.java b/src/net/server/coordinator/session/MapleSessionCoordinator.java similarity index 94% rename from src/net/server/coordinator/MapleSessionCoordinator.java rename to src/net/server/coordinator/session/MapleSessionCoordinator.java index d64c9f57f4..9ebd6238c8 100644 --- a/src/net/server/coordinator/MapleSessionCoordinator.java +++ b/src/net/server/coordinator/session/MapleSessionCoordinator.java @@ -17,11 +17,12 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.session; +import net.server.coordinator.login.LoginStorage; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import net.server.Server; import net.server.audit.locks.MonitoredLockType; @@ -163,8 +164,8 @@ public class MapleSessionCoordinator { hwidCount++; } } - - if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) { + + if (hwidCount < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { registerAccessAccount(con, remoteHwid, accountId); return true; } @@ -174,15 +175,15 @@ public class MapleSessionCoordinator { } catch (SQLException ex) { ex.printStackTrace(); } - + return false; } - + private static boolean attemptAccessAccount(String nibbleHwid, int accountId, boolean routineCheck) { try { Connection con = DatabaseConnection.getConnection(); int hwidCount = 0; - + try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM hwidaccounts WHERE accountid = ?")) { ps.setInt(1, accountId); try (ResultSet rs = ps.executeQuery()) { @@ -191,19 +192,19 @@ public class MapleSessionCoordinator { if (rsHwid.endsWith(nibbleHwid)) { if (!routineCheck) { // better update HWID relevance as soon as the login is authenticated - + int loginRelevance = rs.getInt("relevance"); updateAccessAccount(con, rsHwid, accountId, loginRelevance); } - + return true; } - + hwidCount++; } } - - if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) { + + if (hwidCount < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { return true; } } finally { @@ -212,25 +213,35 @@ public class MapleSessionCoordinator { } catch (SQLException ex) { ex.printStackTrace(); } - + return false; } - + private Lock getCoodinatorLock(String remoteHost) { return poolLock.get(Math.abs(remoteHost.hashCode()) % 100); } - + public static String getSessionRemoteAddress(IoSession session) { return (String) session.getAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); } + public static String getSessionRemoteHost(IoSession session) { + String nibbleHwid = (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); + + if (nibbleHwid != null) { + return getSessionRemoteAddress(session) + "-" + nibbleHwid; + } else { + return getSessionRemoteAddress(session); + } + } + private static MapleClient getSessionClient(IoSession session) { return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); } - + public void updateOnlineSession(IoSession session) { MapleClient client = getSessionClient(session); - + if (client != null) { int accountId = client.getAccID(); MapleClient ingameClient = onlineClients.get(accountId); @@ -241,13 +252,13 @@ public class MapleSessionCoordinator { onlineClients.put(accountId, client); } } - + public boolean canStartLoginSession(IoSession session) { - if (!ServerConstants.DETERRED_MULTICLIENT) return true; - - String remoteHost = getSessionRemoteAddress(session); + if (!YamlConfig.config.server.DETERRED_MULTICLIENT) return true; + + String remoteHost = getSessionRemoteHost(session); Lock lock = getCoodinatorLock(remoteHost); - + try { int tries = 0; while (true) { @@ -256,19 +267,19 @@ public class MapleSessionCoordinator { if (pooledRemoteHosts.contains(remoteHost)) { return false; } - + pooledRemoteHosts.add(remoteHost); } finally { lock.unlock(); } - + break; } else { if(tries == 2) { return true; } tries++; - + Thread.sleep(1777); } } @@ -276,7 +287,7 @@ public class MapleSessionCoordinator { e.printStackTrace(); return true; } - + try { String knownHwid = cachedHostHwids.get(remoteHost); if (knownHwid != null) { @@ -284,15 +295,15 @@ public class MapleSessionCoordinator { return false; } } - + if (loginRemoteHosts.containsKey(remoteHost)) { return false; } - + Set lrh = new HashSet<>(2); lrh.add(session); loginRemoteHosts.put(remoteHost, lrh); - + return true; } finally { lock.lock(); @@ -303,9 +314,11 @@ public class MapleSessionCoordinator { } } } - + public void closeLoginSession(IoSession session) { - String remoteHost = getSessionRemoteAddress(session); + String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); + String remoteHost = getSessionRemoteHost(session); + Set lrh = loginRemoteHosts.get(remoteHost); if (lrh != null) { lrh.remove(session); @@ -314,14 +327,13 @@ public class MapleSessionCoordinator { } } - String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); if (nibbleHwid != null) { onlineRemoteHwids.remove(nibbleHwid); - + MapleClient client = getSessionClient(session); if (client != null) { MapleClient loggedClient = onlineClients.get(client.getAccID()); - + // do not remove an online game session here, only login session if (loggedClient != null && loggedClient.getSessionId() == client.getSessionId()) { onlineClients.remove(client.getAccID()); @@ -329,16 +341,16 @@ public class MapleSessionCoordinator { } } } - + public AntiMulticlientResult attemptLoginSession(IoSession session, String nibbleHwid, int accountId, boolean routineCheck) { - if (!ServerConstants.DETERRED_MULTICLIENT) { + if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); return AntiMulticlientResult.SUCCESS; } - - String remoteHost = getSessionRemoteAddress(session); + + String remoteHost = getSessionRemoteHost(session); Lock lock = getCoodinatorLock(remoteHost); - + try { int tries = 0; while (true) { @@ -347,19 +359,19 @@ public class MapleSessionCoordinator { if (pooledRemoteHosts.contains(remoteHost)) { return AntiMulticlientResult.REMOTE_PROCESSING; } - + pooledRemoteHosts.add(remoteHost); } finally { lock.unlock(); } - + break; } else { if(tries == 2) { return AntiMulticlientResult.COORDINATOR_ERROR; } tries++; - + Thread.sleep(1777); } } @@ -367,12 +379,12 @@ public class MapleSessionCoordinator { e.printStackTrace(); return AntiMulticlientResult.COORDINATOR_ERROR; } - + try { if (!loginStorage.registerLogin(accountId)) { return AntiMulticlientResult.MANY_ACCOUNT_ATTEMPTS; } - + if (!routineCheck) { if (onlineRemoteHwids.contains(nibbleHwid)) { return AntiMulticlientResult.REMOTE_LOGGEDIN; @@ -381,7 +393,7 @@ public class MapleSessionCoordinator { if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } - + session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); onlineRemoteHwids.add(nibbleHwid); } else { @@ -389,7 +401,7 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } } - + return AntiMulticlientResult.SUCCESS; } finally { lock.lock(); @@ -400,11 +412,12 @@ public class MapleSessionCoordinator { } } } - + public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) { - String remoteHost = getSessionRemoteAddress(session); - if (!ServerConstants.DETERRED_MULTICLIENT) { + String remoteHost = getSessionRemoteHost(session); + if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { associateRemoteHostHwid(remoteHost, remoteHwid); + associateRemoteHostHwid(getSessionRemoteAddress(session), remoteHwid); // no HWID information on the loggedin newcomer session... return AntiMulticlientResult.SUCCESS; } @@ -450,6 +463,7 @@ public class MapleSessionCoordinator { // updated session CLIENT_HWID attribute will be set when the player log in the game onlineRemoteHwids.add(remoteHwid); associateRemoteHostHwid(remoteHost, remoteHwid); + associateRemoteHostHwid(getSessionRemoteAddress(session), remoteHwid); associateHwidAccountIfAbsent(remoteHwid, accountId); return AntiMulticlientResult.SUCCESS; @@ -485,7 +499,7 @@ public class MapleSessionCoordinator { } MapleClient client = new MapleClient(null, null, session); - Integer cid = Server.getInstance().freeCharacteridInTransition(session); + Integer cid = Server.getInstance().freeCharacteridInTransition(client); if (cid != null) { try { client.setAccID(MapleCharacter.loadCharFromDB(cid, client, false).getAccountID()); @@ -534,7 +548,7 @@ public class MapleSessionCoordinator { } public String getGameSessionHwid(IoSession session) { - String remoteHost = getSessionRemoteAddress(session); + String remoteHost = getSessionRemoteHost(session); return cachedHostHwids.get(remoteHost); } diff --git a/src/net/server/coordinator/MapleEventRecallCoordinator.java b/src/net/server/coordinator/world/MapleEventRecallCoordinator.java similarity index 94% rename from src/net/server/coordinator/MapleEventRecallCoordinator.java rename to src/net/server/coordinator/world/MapleEventRecallCoordinator.java index 302d243011..2016677c07 100644 --- a/src/net/server/coordinator/MapleEventRecallCoordinator.java +++ b/src/net/server/coordinator/world/MapleEventRecallCoordinator.java @@ -17,9 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.world; -import constants.ServerConstants; +import config.YamlConfig; import scripting.event.EventInstanceManager; import java.util.LinkedList; import java.util.List; @@ -50,7 +50,7 @@ public class MapleEventRecallCoordinator { } public void storeEventInstance(int characterId, EventInstanceManager eim) { - if (ServerConstants.USE_ENABLE_RECALL_EVENT && isRecallableEvent(eim)) { + if (YamlConfig.config.server.USE_ENABLE_RECALL_EVENT && isRecallableEvent(eim)) { eventHistory.put(characterId, eim); } } diff --git a/src/net/server/coordinator/MapleInviteCoordinator.java b/src/net/server/coordinator/world/MapleInviteCoordinator.java similarity index 76% rename from src/net/server/coordinator/MapleInviteCoordinator.java rename to src/net/server/coordinator/world/MapleInviteCoordinator.java index c4f2fb7c8b..05a75324b5 100644 --- a/src/net/server/coordinator/MapleInviteCoordinator.java +++ b/src/net/server/coordinator/world/MapleInviteCoordinator.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.world; import client.MapleCharacter; import tools.Pair; @@ -42,7 +42,8 @@ public class MapleInviteCoordinator { public enum InviteType { //BUDDY, (not needed) - //FAMILY, (not implemented) + FAMILY, + FAMILY_SUMMON, MESSENGER, TRADE, PARTY, @@ -52,11 +53,13 @@ public class MapleInviteCoordinator { final ConcurrentHashMap invites; final ConcurrentHashMap inviteFrom; final ConcurrentHashMap inviteTimeouts; + final ConcurrentHashMap inviteParams; private InviteType() { invites = new ConcurrentHashMap<>(); inviteTimeouts = new ConcurrentHashMap<>(); inviteFrom = new ConcurrentHashMap<>(); + inviteParams = new ConcurrentHashMap<>(); } private Map getRequestsTable() { @@ -67,15 +70,15 @@ public class MapleInviteCoordinator { return inviteTimeouts; } - private MapleCharacter removeRequest(Integer target) { + private Pair removeRequest(Integer target) { invites.remove(target); MapleCharacter from = inviteFrom.remove(target); inviteTimeouts.remove(target); - return from; + return new Pair<>(from, inviteParams.remove(target)); } - private boolean addRequest(MapleCharacter from, Object referenceFrom, int targetCid) { + private boolean addRequest(MapleCharacter from, Object referenceFrom, int targetCid, Object[] params) { Object v = invites.putIfAbsent(targetCid, referenceFrom); if (v != null) { // there was already an entry return false; @@ -83,7 +86,7 @@ public class MapleInviteCoordinator { inviteFrom.put(targetCid, from); inviteTimeouts.put(targetCid, 0); - + inviteParams.put(targetCid, params); return true; } @@ -93,29 +96,31 @@ public class MapleInviteCoordinator { } // note: referenceFrom is a specific value that represents the "common association" created between the sender/recver parties - public static boolean createInvite(InviteType type, MapleCharacter from, Object referenceFrom, int targetCid) { - return type.addRequest(from, referenceFrom, targetCid); + public static boolean createInvite(InviteType type, MapleCharacter from, Object referenceFrom, int targetCid, Object... params) { + return type.addRequest(from, referenceFrom, targetCid, params); } public static boolean hasInvite(InviteType type, int targetCid) { return type.hasRequest(targetCid); } - public static Pair answerInvite(InviteType type, int targetCid, Object referenceFrom, boolean answer) { + public static MapleInviteResult answerInvite(InviteType type, int targetCid, Object referenceFrom, boolean answer) { Map table = type.getRequestsTable(); MapleCharacter from = null; InviteResult result = InviteResult.NOT_FOUND; + Pair inviteInfo = null; Object reference = table.get(targetCid); if (referenceFrom.equals(reference)) { - from = type.removeRequest(targetCid); + inviteInfo = type.removeRequest(targetCid); + from = inviteInfo.getLeft(); if (from != null && !from.isLoggedinWorld()) from = null; result = answer ? InviteResult.ACCEPTED : InviteResult.DENIED; } - return new Pair<>(result, from); + return new MapleInviteResult(result, from, inviteInfo != null ? inviteInfo.getRight() : new Object[0]); } public static void removeInvite(InviteType type, int targetCid) { @@ -146,4 +151,17 @@ public class MapleInviteCoordinator { } } } + + public static class MapleInviteResult { + + public final InviteResult result; + public final MapleCharacter from; + public final Object[] params; + + private MapleInviteResult(InviteResult result, MapleCharacter from, Object[] params) { + this.result = result; + this.from = from; + this.params = params; + } + } } diff --git a/src/net/server/coordinator/MapleMonsterAggroCoordinator.java b/src/net/server/coordinator/world/MapleMonsterAggroCoordinator.java similarity index 95% rename from src/net/server/coordinator/MapleMonsterAggroCoordinator.java rename to src/net/server/coordinator/world/MapleMonsterAggroCoordinator.java index 64452f58c5..bbc8437090 100644 --- a/src/net/server/coordinator/MapleMonsterAggroCoordinator.java +++ b/src/net/server/coordinator/world/MapleMonsterAggroCoordinator.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.coordinator; +package net.server.coordinator.world; import java.util.ArrayList; import java.util.Collections; @@ -30,7 +30,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import constants.ServerConstants; +import config.YamlConfig; import client.MapleCharacter; import java.util.concurrent.ScheduledFuture; import net.server.Server; @@ -101,19 +101,19 @@ public class MapleMonsterAggroCoordinator { runAggroUpdate(1); runSortLeadingCharactersAggro(); } - }, ServerConstants.MOB_STATUS_AGGRO_INTERVAL, ServerConstants.MOB_STATUS_AGGRO_INTERVAL); + }, YamlConfig.config.server.MOB_STATUS_AGGRO_INTERVAL, YamlConfig.config.server.MOB_STATUS_AGGRO_INTERVAL); } finally { idleLock.unlock(); } - int timeDelta = (int) Math.ceil((Server.getInstance().getCurrentTime() - lastStopTime) / ServerConstants.MOB_STATUS_AGGRO_INTERVAL); + int timeDelta = (int) Math.ceil((Server.getInstance().getCurrentTime() - lastStopTime) / YamlConfig.config.server.MOB_STATUS_AGGRO_INTERVAL); if (timeDelta > 0) { runAggroUpdate(timeDelta); } } private static void updateEntryExpiration(PlayerAggroEntry pae) { - pae.toNextUpdate = (int) Math.ceil((120000L / ServerConstants.MOB_STATUS_AGGRO_INTERVAL) / Math.pow(2, pae.expireStreak + pae.currentDamageInstances)); + pae.toNextUpdate = (int) Math.ceil((120000L / YamlConfig.config.server.MOB_STATUS_AGGRO_INTERVAL) / Math.pow(2, pae.expireStreak + pae.currentDamageInstances)); } private static void insertEntryDamage(PlayerAggroEntry pae, int damage) { @@ -318,7 +318,7 @@ public class MapleMonsterAggroCoordinator { if (chr != null) { if (player.getId() == pae.cid) { return true; - } else if (pae.updateStreak < ServerConstants.MOB_STATUS_AGGRO_PERSISTENCE && chr.isAlive()) { // verifies currently leading players activity + } else if (pae.updateStreak < YamlConfig.config.server.MOB_STATUS_AGGRO_PERSISTENCE && chr.isAlive()) { // verifies currently leading players activity return false; } } diff --git a/src/net/server/guild/MapleAlliance.java b/src/net/server/guild/MapleAlliance.java index 947b6c62e0..56a44597a4 100644 --- a/src/net/server/guild/MapleAlliance.java +++ b/src/net/server/guild/MapleAlliance.java @@ -31,14 +31,13 @@ import java.util.List; import client.MapleCharacter; import client.MapleClient; import net.server.Server; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; import tools.DatabaseConnection; import tools.MaplePacketCreator; -import tools.Pair; /** * @@ -92,8 +91,13 @@ public class MapleAlliance { List mcl = new LinkedList<>(); for(MaplePartyCharacter mpc: party.getMembers()) { - if(mpc.getPlayer().getGuildRank() == 1 && mpc.getPlayer().getMapId() == party.getLeader().getPlayer().getMapId()) - mcl.add(mpc.getPlayer()); + MapleCharacter chr = mpc.getPlayer(); + if (chr != null) { + MapleCharacter lchr = party.getLeader().getPlayer(); + if (chr.getGuildRank() == 1 && lchr != null && chr.getMapId() == lchr.getMapId()) { + mcl.add(chr); + } + } } if(!mcl.isEmpty() && !mcl.get(0).isPartyLeader()) { @@ -494,11 +498,11 @@ public class MapleAlliance { } public static boolean answerInvitation(int targetId, String targetGuildName, int allianceId, boolean answer) { - Pair res = MapleInviteCoordinator.answerInvite(InviteType.ALLIANCE, targetId, allianceId, answer); + MapleInviteResult res = MapleInviteCoordinator.answerInvite(InviteType.ALLIANCE, targetId, allianceId, answer); String msg; - MapleCharacter sender = res.getRight(); - switch (res.getLeft()) { + MapleCharacter sender = res.from; + switch (res.result) { case ACCEPTED: return true; diff --git a/src/net/server/guild/MapleGuild.java b/src/net/server/guild/MapleGuild.java index f544f6e55c..a682cda6d7 100644 --- a/src/net/server/guild/MapleGuild.java +++ b/src/net/server/guild/MapleGuild.java @@ -23,13 +23,14 @@ package net.server.guild; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Comparator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -45,12 +46,11 @@ import net.server.Server; import net.server.channel.Channel; import tools.DatabaseConnection; import tools.MaplePacketCreator; -import tools.Pair; import net.server.audit.locks.MonitoredLockType; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteType; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleMatchCheckerCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; +import net.server.coordinator.matchchecker.MapleMatchCheckerCoordinator; public class MapleGuild { @@ -308,6 +308,18 @@ public class MapleGuild { } } + public void broadcastInfoChanged() { + PlayerStorage ps = Server.getInstance().getWorld(world).getPlayerStorage(); + + for (MapleGuildCharacter mgc : getMembers()) { + MapleCharacter chr = ps.getCharacterById(mgc.getId()); + if (chr == null || !chr.isLoggedinWorld()) continue; + + byte[] packet = MaplePacketCreator.showGuildInfo(chr); + chr.announce(packet); + } + } + public void broadcast(final byte[] packet) { broadcast(packet, -1, BCOp.NONE); } @@ -577,7 +589,7 @@ public class MapleGuild { public void setGuildNotice(String notice) { this.notice = notice; - writeToDB(false); + this.writeToDB(false); membersLock.lock(); try { @@ -727,11 +739,11 @@ public class MapleGuild { } public static boolean answerInvitation(int targetId, String targetName, int guildId, boolean answer) { - Pair res = MapleInviteCoordinator.answerInvite(InviteType.GUILD, targetId, guildId, answer); + MapleInviteResult res = MapleInviteCoordinator.answerInvite(InviteType.GUILD, targetId, guildId, answer); MapleGuildResponse mgr; - MapleCharacter sender = res.getRight(); - switch (res.getLeft()) { + MapleCharacter sender = res.from; + switch (res.result) { case ACCEPTED: return true; @@ -826,10 +838,10 @@ public class MapleGuild { } public static int getIncreaseGuildCost(int size) { - int cost = ServerConstants.EXPAND_GUILD_BASE_COST + Math.max(0, (size - 15) / 5) * ServerConstants.EXPAND_GUILD_TIER_COST; + int cost = YamlConfig.config.server.EXPAND_GUILD_BASE_COST + Math.max(0, (size - 15) / 5) * YamlConfig.config.server.EXPAND_GUILD_TIER_COST; if (size > 30) { - return Math.min(ServerConstants.EXPAND_GUILD_MAX_COST, Math.max(cost, 5000000)); + return Math.min(YamlConfig.config.server.EXPAND_GUILD_MAX_COST, Math.max(cost, 5000000)); } else { return cost; } diff --git a/src/net/server/guild/MapleGuildCharacter.java b/src/net/server/guild/MapleGuildCharacter.java index 41e12ca42b..aff55becba 100644 --- a/src/net/server/guild/MapleGuildCharacter.java +++ b/src/net/server/guild/MapleGuildCharacter.java @@ -35,22 +35,22 @@ public class MapleGuildCharacter { private boolean online; private String name; - public MapleGuildCharacter(MapleCharacter c) { - this.character = c; - this.name = c.getName(); - this.level = c.getLevel(); - this.id = c.getId(); - this.channel = c.getClient().getChannel(); - this.world = c.getWorld(); - this.jobid = c.getJob().getId(); - this.guildrank = c.getGuildRank(); - this.guildid = c.getGuildId(); + public MapleGuildCharacter(MapleCharacter chr) { + this.character = chr; + this.name = chr.getName(); + this.level = chr.getLevel(); + this.id = chr.getId(); + this.channel = chr.getClient().getChannel(); + this.world = chr.getWorld(); + this.jobid = chr.getJob().getId(); + this.guildrank = chr.getGuildRank(); + this.guildid = chr.getGuildId(); this.online = true; - this.allianceRank = c.getAllianceRank(); + this.allianceRank = chr.getAllianceRank(); } - public MapleGuildCharacter(MapleCharacter c, int _id, int _lv, String _name, int _channel, int _world, int _job, int _rank, int _gid, boolean _on, int _allianceRank) { - this.character = c; + public MapleGuildCharacter(MapleCharacter chr, int _id, int _lv, String _name, int _channel, int _world, int _job, int _rank, int _gid, boolean _on, int _allianceRank) { + this.character = chr; this.level = _lv; this.id = _id; this.name = _name; diff --git a/src/net/server/handlers/login/AfterLoginHandler.java b/src/net/server/handlers/login/AfterLoginHandler.java index 5b2536e16c..f3acb93bf0 100644 --- a/src/net/server/handlers/login/AfterLoginHandler.java +++ b/src/net/server/handlers/login/AfterLoginHandler.java @@ -23,7 +23,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/handlers/login/CharSelectedHandler.java b/src/net/server/handlers/login/CharSelectedHandler.java index b5e7ba5d48..e378ad1b6c 100644 --- a/src/net/server/handlers/login/CharSelectedHandler.java +++ b/src/net/server/handlers/login/CharSelectedHandler.java @@ -26,8 +26,8 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; @@ -101,8 +101,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/net/server/handlers/login/CharSelectedWithPicHandler.java index 31ac500d9f..26f89ccd21 100644 --- a/src/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -5,8 +5,8 @@ import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; @@ -84,8 +84,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/handlers/login/CharlistRequestHandler.java b/src/net/server/handlers/login/CharlistRequestHandler.java index 72f29c06ca..66c290d3b7 100644 --- a/src/net/server/handlers/login/CharlistRequestHandler.java +++ b/src/net/server/handlers/login/CharlistRequestHandler.java @@ -24,6 +24,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.channel.Channel; import net.server.world.World; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -42,7 +43,8 @@ public final class CharlistRequestHandler extends AbstractMaplePacketHandler { } int channel = slea.readByte() + 1; - if(wserv.getChannel(channel) == null) { + Channel ch = wserv.getChannel(channel); + if(ch == null) { c.announce(MaplePacketCreator.getServerStatus(2)); return; } diff --git a/src/net/server/handlers/login/DeleteCharHandler.java b/src/net/server/handlers/login/DeleteCharHandler.java index 5220c9ea3a..ef066d107d 100644 --- a/src/net/server/handlers/login/DeleteCharHandler.java +++ b/src/net/server/handlers/login/DeleteCharHandler.java @@ -21,8 +21,16 @@ */ package net.server.handlers.login; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + import client.MapleClient; +import client.MapleFamily; import net.AbstractMaplePacketHandler; +import net.server.Server; +import tools.DatabaseConnection; import tools.FilePrinter; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -34,11 +42,45 @@ public final class DeleteCharHandler extends AbstractMaplePacketHandler { String pic = slea.readMapleAsciiString(); int cid = slea.readInt(); if (c.checkPic(pic)) { + //check for family, guild leader, pending marriage, world transfer + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT `world`, `guildid`, `guildrank`, `familyId` FROM characters WHERE id = ?"); + PreparedStatement ps2 = con.prepareStatement("SELECT COUNT(*) as rowcount FROM worldtransfers WHERE `characterid` = ? AND completionTime IS NULL")) { + ps.setInt(1, cid); + ResultSet rs = ps.executeQuery(); + if(!rs.next()) throw new SQLException("Character record does not exist."); + int world = rs.getInt("world"); + int guildId = rs.getInt("guildid"); + int guildRank = rs.getInt("guildrank"); + int familyId = rs.getInt("familyId"); + if(guildId != 0 && guildRank <= 1) { + c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x16)); + return; + } else if(familyId != -1) { + MapleFamily family = Server.getInstance().getWorld(world).getFamily(familyId); + if(family != null && family.getTotalMembers() > 1) { + c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x1D)); + return; + } + } + rs.close(); + ps2.setInt(1, cid); + rs = ps2.executeQuery(); + rs.next(); + if(rs.getInt("rowcount") > 0) { + c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x1A)); + return; + } + } catch(SQLException e) { + e.printStackTrace(); + c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x09)); + return; + } if(c.deleteCharacter(cid, c.getAccID())) { FilePrinter.print(FilePrinter.DELETED_CHAR + c.getAccountName() + ".txt", c.getAccountName() + " deleted CID: " + cid); c.announce(MaplePacketCreator.deleteCharResponse(cid, 0)); } else { - c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x14)); + c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x09)); } } else { c.announce(MaplePacketCreator.deleteCharResponse(cid, 0x14)); diff --git a/src/net/server/handlers/login/LoginPasswordHandler.java b/src/net/server/handlers/login/LoginPasswordHandler.java index 2a718656f7..45beacc73c 100644 --- a/src/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/net/server/handlers/login/LoginPasswordHandler.java @@ -26,7 +26,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Calendar; -import constants.ServerConstants; +import config.YamlConfig; import net.MaplePacketHandler; import net.server.Server; import tools.BCrypt; @@ -40,7 +40,7 @@ import java.sql.Statement; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.session.IoSession; public final class LoginPasswordHandler implements MaplePacketHandler { @@ -64,14 +64,14 @@ public final class LoginPasswordHandler implements MaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { String remoteHost = getRemoteIp(c.getSession()); if (!remoteHost.contentEquals("null")) { - if (ServerConstants.USE_IP_VALIDATION) { // thanks Alex (Alex-0000) for suggesting IP validation as a server flag + if (YamlConfig.config.server.USE_IP_VALIDATION) { // thanks Alex (CanIGetaPR) for suggesting IP validation as a server flag if (remoteHost.startsWith("127.")) { - if (!ServerConstants.LOCALSERVER) { // thanks Mills for noting HOST can also have a field named "localhost" + if (!YamlConfig.config.server.LOCALSERVER) { // thanks Mills for noting HOST can also have a field named "localhost" c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as localhost if it's not a local server return; } } else { - if (ServerConstants.LOCALSERVER) { + if (YamlConfig.config.server.LOCALSERVER) { c.announce(MaplePacketCreator.getLoginFailed(13)); // cannot login as non-localhost if it's a local server return; } @@ -93,12 +93,12 @@ public final class LoginPasswordHandler implements MaplePacketHandler { Connection con = null; PreparedStatement ps = null; - if (ServerConstants.AUTOMATIC_REGISTER && loginok == 5) { + if (YamlConfig.config.server.AUTOMATIC_REGISTER && loginok == 5) { try { con = DatabaseConnection.getConnection(); ps = con.prepareStatement("INSERT INTO accounts (name, password, birthday, tempban) VALUES (?, ?, ?, ?);", Statement.RETURN_GENERATED_KEYS); //Jayd: Added birthday, tempban ps.setString(1, login); - ps.setString(2, ServerConstants.BCRYPT_MIGRATION ? BCrypt.hashpw(pwd, BCrypt.gensalt(12)) : hashpwSHA512(pwd)); + ps.setString(2, YamlConfig.config.server.BCRYPT_MIGRATION ? BCrypt.hashpw(pwd, BCrypt.gensalt(12)) : hashpwSHA512(pwd)); ps.setString(3, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (birthday) ps.setString(4, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (tempban) ps.executeUpdate(); @@ -116,7 +116,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { } } - if (ServerConstants.BCRYPT_MIGRATION && (loginok <= -10)) { // -10 means migration to bcrypt, -23 means TOS wasn't accepted + if (YamlConfig.config.server.BCRYPT_MIGRATION && (loginok <= -10)) { // -10 means migration to bcrypt, -23 means TOS wasn't accepted try { con = DatabaseConnection.getConnection(); ps = con.prepareStatement("UPDATE accounts SET password = ? WHERE name = ?;"); @@ -135,7 +135,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { c.announce(MaplePacketCreator.getLoginFailed(3)); return; } - Calendar tempban = c.getTempBanCalendar(); + Calendar tempban = c.getTempBanCalendarFromDB(); if (tempban != null) { if (tempban.getTimeInMillis() > Calendar.getInstance().getTimeInMillis()) { c.announce(MaplePacketCreator.getTempBan(tempban.getTimeInMillis(), c.getGReason())); @@ -150,6 +150,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { return; } if (c.finishLogin() == 0) { + c.checkChar(c.getAccID()); login(c); } else { c.announce(MaplePacketCreator.getLoginFailed(7)); diff --git a/src/net/server/handlers/login/RegisterPicHandler.java b/src/net/server/handlers/login/RegisterPicHandler.java index ed76cee00d..4de8d673fe 100644 --- a/src/net/server/handlers/login/RegisterPicHandler.java +++ b/src/net/server/handlers/login/RegisterPicHandler.java @@ -9,8 +9,8 @@ import net.server.world.World; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleClient; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import org.apache.mina.core.session.IoSession; public final class RegisterPicHandler extends AbstractMaplePacketHandler { @@ -86,8 +86,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/handlers/login/RegisterPinHandler.java b/src/net/server/handlers/login/RegisterPinHandler.java index 4d325996bc..65a3e667a9 100644 --- a/src/net/server/handlers/login/RegisterPinHandler.java +++ b/src/net/server/handlers/login/RegisterPinHandler.java @@ -23,7 +23,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/handlers/login/ServerlistRequestHandler.java b/src/net/server/handlers/login/ServerlistRequestHandler.java index ba29947a4c..9391bafac4 100644 --- a/src/net/server/handlers/login/ServerlistRequestHandler.java +++ b/src/net/server/handlers/login/ServerlistRequestHandler.java @@ -22,7 +22,7 @@ package net.server.handlers.login; import client.MapleClient; -import constants.GameConstants; +import constants.game.GameConstants; import java.util.List; import net.AbstractMaplePacketHandler; import net.server.Server; diff --git a/src/net/server/handlers/login/SetGenderHandler.java b/src/net/server/handlers/login/SetGenderHandler.java index adaed709ac..3cf57342f8 100644 --- a/src/net/server/handlers/login/SetGenderHandler.java +++ b/src/net/server/handlers/login/SetGenderHandler.java @@ -25,7 +25,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; diff --git a/src/net/server/handlers/login/ViewAllCharHandler.java b/src/net/server/handlers/login/ViewAllCharHandler.java index fa04f5ab72..edf3a5f7dc 100644 --- a/src/net/server/handlers/login/ViewAllCharHandler.java +++ b/src/net/server/handlers/login/ViewAllCharHandler.java @@ -23,7 +23,7 @@ package net.server.handlers.login; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import java.util.List; import net.AbstractMaplePacketHandler; import net.server.Server; @@ -64,7 +64,7 @@ public final class ViewAllCharHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.showAllCharacter(charsSize, unk)); for (Pair> wchars : worldChars) { - c.announce(MaplePacketCreator.showAllCharacterInfo(wchars.getLeft(), wchars.getRight(), ServerConstants.ENABLE_PIC && !c.canBypassPic())); + c.announce(MaplePacketCreator.showAllCharacterInfo(wchars.getLeft(), wchars.getRight(), YamlConfig.config.server.ENABLE_PIC && !c.canBypassPic())); } } catch (Exception e) { e.printStackTrace(); diff --git a/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index b308b8ad3c..a4a9f5235b 100644 --- a/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -5,8 +5,8 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; @@ -89,8 +89,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/net/server/handlers/login/ViewAllCharSelectedHandler.java index 66dae5c4ad..e2f458b4bb 100644 --- a/src/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -26,8 +26,8 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; @@ -112,8 +112,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index 21cb544776..aeb6e3208b 100644 --- a/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -10,8 +10,8 @@ import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleClient; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import org.apache.mina.core.session.IoSession; public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandler { @@ -90,8 +90,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle } server.unregisterLoginState(c); - c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - server.setCharacteridInTransition(session, charId); + c.setCharacterOnSessionTransitionState(charId); try { c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId)); diff --git a/src/net/server/channel/worker/BaseScheduler.java b/src/net/server/services/BaseScheduler.java similarity index 96% rename from src/net/server/channel/worker/BaseScheduler.java rename to src/net/server/services/BaseScheduler.java index c598ad2b61..b27203465f 100644 --- a/src/net/server/channel/worker/BaseScheduler.java +++ b/src/net/server/services/BaseScheduler.java @@ -17,9 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.channel.worker; +package net.server.services; -import constants.ServerConstants; +import config.YamlConfig; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -101,7 +101,7 @@ public abstract class BaseScheduler { if(registeredEntries.isEmpty()) { idleProcs++; - if(idleProcs >= ServerConstants.MOB_STATUS_MONITOR_LIFE) { + if(idleProcs >= YamlConfig.config.server.MOB_STATUS_MONITOR_LIFE) { if(schedulerTask != null) { schedulerTask.cancel(false); schedulerTask = null; @@ -147,7 +147,7 @@ public abstract class BaseScheduler { try { idleProcs = 0; if(schedulerTask == null) { - schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); + schedulerTask = TimerManager.getInstance().register(monitorTask, YamlConfig.config.server.MOB_STATUS_MONITOR_PROC, YamlConfig.config.server.MOB_STATUS_MONITOR_PROC); } registeredEntries.put(key, new Pair<>(removalAction, Server.getInstance().getCurrentTime() + duration)); diff --git a/src/net/server/services/BaseService.java b/src/net/server/services/BaseService.java new file mode 100644 index 0000000000..4a6a95da62 --- /dev/null +++ b/src/net/server/services/BaseService.java @@ -0,0 +1,37 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services; + +import config.YamlConfig; + +/** + * + * @author Ronan + */ +public abstract class BaseService { + + protected static int getChannelSchedulerIndex(int mapid) { + int section = 1000000000 / YamlConfig.config.server.CHANNEL_LOCKS; + return mapid / section; + } + + public abstract void dispose(); + +} diff --git a/src/net/server/channel/worker/SchedulerListener.java b/src/net/server/services/SchedulerListener.java similarity index 96% rename from src/net/server/channel/worker/SchedulerListener.java rename to src/net/server/services/SchedulerListener.java index 389420aa6f..975de08b19 100644 --- a/src/net/server/channel/worker/SchedulerListener.java +++ b/src/net/server/services/SchedulerListener.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.channel.worker; +package net.server.services; import java.util.List; diff --git a/src/client/command/RegisteredCommand.java b/src/net/server/services/Service.java similarity index 63% rename from src/client/command/RegisteredCommand.java rename to src/net/server/services/Service.java index 0a37b8ddc6..b9798d4753 100644 --- a/src/client/command/RegisteredCommand.java +++ b/src/net/server/services/Service.java @@ -17,27 +17,34 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +package net.server.services; -/* - @Author: Arthur L - Refactored command content into modules -*/ -package client.command; - -class RegisteredCommand { - - private final Class commandClass; - private final int rank; - - RegisteredCommand(Class commandClass, int rank){ - this.commandClass = commandClass; - this.rank = rank; +/** + * + * @author Ronan + */ +public class Service { + + private Class cls; + private BaseService service; + + public Service(Class s) { + try { + cls = s; + service = (BaseService) cls.getConstructor().newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } } - - public Class getCommandClass() { - return commandClass; + + public T getService() { + return cls.cast(service); } - - public int getRank() { - return rank; + + public void dispose() { + service.dispose(); + service = null; } + } + diff --git a/src/net/server/services/ServiceType.java b/src/net/server/services/ServiceType.java new file mode 100644 index 0000000000..7d43fcdeb6 --- /dev/null +++ b/src/net/server/services/ServiceType.java @@ -0,0 +1,30 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services; + +/** + * + * @author Ronan + */ +public interface ServiceType > { + public abstract Service createService(); + public int ordinal(); + public T[] enumValues(); +} diff --git a/src/net/server/channel/worker/OverallScheduler.java b/src/net/server/services/ServicesManager.java similarity index 54% rename from src/net/server/channel/worker/OverallScheduler.java rename to src/net/server/services/ServicesManager.java index c052f024e5..a083a9b610 100644 --- a/src/net/server/channel/worker/OverallScheduler.java +++ b/src/net/server/services/ServicesManager.java @@ -1,5 +1,5 @@ /* - This file is part of the HeavenMS MapleStory Server + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based Copyleft (L) 2016 - 2019 RonanLana This program is free software: you can redistribute it and/or modify @@ -17,24 +17,34 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.channel.worker; - -import net.server.audit.locks.MonitoredLockType; +package net.server.services; /** * * @author Ronan */ -public class OverallScheduler extends BaseScheduler { - public OverallScheduler() { - super(MonitoredLockType.CHANNEL_OVERALL); +public class ServicesManager { + + private Service[] services; + + public ServicesManager(ServiceType serviceBundle) { + Enum[] serviceTypes = serviceBundle.enumValues(); + + services = new Service[serviceTypes.length]; + for (Enum type : serviceTypes) { + services[type.ordinal()] = ((ServiceType) type).createService(); + } } - public void registerDelayedAction(Runnable runAction, long delay) { - registerEntry(runAction, runAction, delay); + public Service getAccess(ServiceType s) { + return services[s.ordinal()]; } - public void forceRunDelayedAction(Runnable runAction) { - interruptEntry(runAction); + public void shutdown() { + for (int i = 0; i < services.length; i++) { + services[i].dispose(); + } + services = null; } + } diff --git a/src/net/server/services/task/channel/EventService.java b/src/net/server/services/task/channel/EventService.java new file mode 100644 index 0000000000..bb781c917e --- /dev/null +++ b/src/net/server/services/task/channel/EventService.java @@ -0,0 +1,67 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import config.YamlConfig; +import net.server.audit.locks.MonitoredLockType; +import net.server.services.BaseScheduler; + +/** + * + * @author Ronan + */ +public class EventService extends BaseService { + + private EventScheduler eventSchedulers[] = new EventScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public EventService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + eventSchedulers[i] = new EventScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(eventSchedulers[i] != null) { + eventSchedulers[i].dispose(); + eventSchedulers[i] = null; + } + } + } + + public void registerEventAction(int mapid, Runnable runAction, long delay) { + eventSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay); + } + + private class EventScheduler extends BaseScheduler { + + public EventScheduler() { + super(MonitoredLockType.CHANNEL_EVENTS); + } + + public void registerDelayedAction(Runnable runAction, long delay) { + registerEntry(runAction, runAction, delay); + } + + } + +} diff --git a/src/net/server/services/task/channel/FaceExpressionService.java b/src/net/server/services/task/channel/FaceExpressionService.java new file mode 100644 index 0000000000..52b5d343f1 --- /dev/null +++ b/src/net/server/services/task/channel/FaceExpressionService.java @@ -0,0 +1,130 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import client.MapleCharacter; +import config.YamlConfig; +import java.util.Collections; +import net.server.audit.LockCollector; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.services.BaseScheduler; +import server.maps.MapleMap; +import tools.MaplePacketCreator; + +/** + * + * @author Ronan + */ +public class FaceExpressionService extends BaseService { + + private FaceExpressionScheduler faceExpressionSchedulers[] = new FaceExpressionScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[YamlConfig.config.server.CHANNEL_LOCKS]; + + public FaceExpressionService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true); + faceExpressionSchedulers[i] = new FaceExpressionScheduler(faceLock[i]); + } + } + + private void emptyLocks() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + faceLock[i] = faceLock[i].dispose(); + } + } + + private void disposeLocks() { + LockCollector.getInstance().registerDisposeAction(new Runnable() { + @Override + public void run() { + emptyLocks(); + } + }); + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(faceExpressionSchedulers[i] != null) { + faceExpressionSchedulers[i].dispose(); + faceExpressionSchedulers[i] = null; + } + } + + disposeLocks(); + } + + public void registerFaceExpression(final MapleMap map, final MapleCharacter chr, int emote) { + int lockid = getChannelSchedulerIndex(map.getId()); + + Runnable cancelAction = new Runnable() { + @Override + public void run() { + if(chr.isLoggedinWorld()) { + map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, 0), false); + } + } + }; + + faceLock[lockid].lock(); + try { + if(!chr.isLoggedinWorld()) { + return; + } + + faceExpressionSchedulers[lockid].registerFaceExpression(chr.getId(), cancelAction); + } finally { + faceLock[lockid].unlock(); + } + + map.broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, emote), false); + } + + public void unregisterFaceExpression(int mapid, MapleCharacter chr) { + int lockid = getChannelSchedulerIndex(mapid); + + faceLock[lockid].lock(); + try { + faceExpressionSchedulers[lockid].unregisterFaceExpression(chr.getId()); + } finally { + faceLock[lockid].unlock(); + } + } + + private class FaceExpressionScheduler extends BaseScheduler { + + public FaceExpressionScheduler(final MonitoredReentrantLock channelFaceLock) { + super(MonitoredLockType.CHANNEL_FACESCHDL, Collections.singletonList(channelFaceLock)); + } + + public void registerFaceExpression(Integer characterId, Runnable runAction) { + registerEntry(characterId, runAction, 5000); + } + + public void unregisterFaceExpression(Integer characterId) { + interruptEntry(characterId); + } + + } + +} diff --git a/src/net/server/services/task/channel/MobAnimationService.java b/src/net/server/services/task/channel/MobAnimationService.java new file mode 100644 index 0000000000..46d7234c9e --- /dev/null +++ b/src/net/server/services/task/channel/MobAnimationService.java @@ -0,0 +1,127 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import config.YamlConfig; +import net.server.audit.locks.MonitoredLockType; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import net.server.audit.LockCollector; +import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.services.BaseScheduler; +import net.server.services.SchedulerListener; + +/** + * + * @author Ronan + */ +public class MobAnimationService extends BaseService { + + private MobAnimationScheduler mobAnimationSchedulers[] = new MobAnimationScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public MobAnimationService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + mobAnimationSchedulers[i] = new MobAnimationScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(mobAnimationSchedulers[i] != null) { + mobAnimationSchedulers[i].dispose(); + mobAnimationSchedulers[i] = null; + } + } + } + + public boolean registerMobOnAnimationEffect(int mapid, int mobHash, long delay) { + return mobAnimationSchedulers[getChannelSchedulerIndex(mapid)].registerAnimationMode(mobHash, delay); + } + + private static Runnable r = new Runnable() { + @Override + public void run() {} // do nothing + }; + + private class MobAnimationScheduler extends BaseScheduler { + Set onAnimationMobs = new HashSet<>(1000); + private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_MOBANIMAT, true); + + public MobAnimationScheduler() { + super(MonitoredLockType.CHANNEL_MOBACTION); + + super.addListener(new SchedulerListener() { + @Override + public void removedScheduledEntries(List toRemove, boolean update) { + animationLock.lock(); + try { + for(Object hashObj : toRemove) { + Integer mobHash = (Integer) hashObj; + onAnimationMobs.remove(mobHash); + } + } finally { + animationLock.unlock(); + } + } + }); + } + + public boolean registerAnimationMode(Integer mobHash, long animationTime) { + animationLock.lock(); + try { + if(onAnimationMobs.contains(mobHash)) { + return false; + } + + registerEntry(mobHash, r, animationTime); + onAnimationMobs.add(mobHash); + return true; + } finally { + animationLock.unlock(); + } + } + + @Override + public void dispose() { + disposeLocks(); + super.dispose(); + } + + private void disposeLocks() { + LockCollector.getInstance().registerDisposeAction(new Runnable() { + @Override + public void run() { + emptyLocks(); + } + }); + } + + private void emptyLocks() { + animationLock = animationLock.dispose(); + } + + } + +} diff --git a/src/net/server/services/task/channel/MobClearSkillService.java b/src/net/server/services/task/channel/MobClearSkillService.java new file mode 100644 index 0000000000..c5bb1c3e75 --- /dev/null +++ b/src/net/server/services/task/channel/MobClearSkillService.java @@ -0,0 +1,67 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import config.YamlConfig; +import net.server.audit.locks.MonitoredLockType; +import net.server.services.BaseScheduler; + +/** + * + * @author Ronan + */ +public class MobClearSkillService extends BaseService { + + private MobClearSkillScheduler mobClearSkillSchedulers[] = new MobClearSkillScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public MobClearSkillService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + mobClearSkillSchedulers[i] = new MobClearSkillScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(mobClearSkillSchedulers[i] != null) { + mobClearSkillSchedulers[i].dispose(); + mobClearSkillSchedulers[i] = null; + } + } + } + + public void registerMobClearSkillAction(int mapid, Runnable runAction, long delay) { + mobClearSkillSchedulers[getChannelSchedulerIndex(mapid)].registerClearSkillAction(runAction, delay); + } + + private class MobClearSkillScheduler extends BaseScheduler { + + public MobClearSkillScheduler() { + super(MonitoredLockType.CHANNEL_MOBSKILL); + } + + public void registerClearSkillAction(Runnable runAction, long delay) { + registerEntry(runAction, runAction, delay); + } + + } + +} diff --git a/src/net/server/services/task/channel/MobMistService.java b/src/net/server/services/task/channel/MobMistService.java new file mode 100644 index 0000000000..d666880a84 --- /dev/null +++ b/src/net/server/services/task/channel/MobMistService.java @@ -0,0 +1,67 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import config.YamlConfig; +import net.server.audit.locks.MonitoredLockType; +import net.server.services.BaseScheduler; + +/** + * + * @author Ronan + */ +public class MobMistService extends BaseService { + + private MobMistScheduler mobMistSchedulers[] = new MobMistScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public MobMistService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + mobMistSchedulers[i] = new MobMistScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(mobMistSchedulers[i] != null) { + mobMistSchedulers[i].dispose(); + mobMistSchedulers[i] = null; + } + } + } + + public void registerMobMistCancelAction(int mapid, Runnable runAction, long delay) { + mobMistSchedulers[getChannelSchedulerIndex(mapid)].registerMistCancelAction(runAction, delay); + } + + private class MobMistScheduler extends BaseScheduler { + + public MobMistScheduler() { + super(MonitoredLockType.CHANNEL_MOBMIST); + } + + public void registerMistCancelAction(Runnable runAction, long delay) { + registerEntry(runAction, runAction, delay); + } + + } + +} diff --git a/src/net/server/services/task/channel/MobStatusService.java b/src/net/server/services/task/channel/MobStatusService.java new file mode 100644 index 0000000000..62faa56735 --- /dev/null +++ b/src/net/server/services/task/channel/MobStatusService.java @@ -0,0 +1,170 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import client.status.MonsterStatusEffect; +import config.YamlConfig; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import net.server.audit.LockCollector; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.services.BaseScheduler; +import net.server.services.SchedulerListener; + +/** + * + * @author Ronan + */ +public class MobStatusService extends BaseService { + + private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public MobStatusService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + mobStatusSchedulers[i] = new MobStatusScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(mobStatusSchedulers[i] != null) { + mobStatusSchedulers[i].dispose(); + mobStatusSchedulers[i] = null; + } + } + } + + public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) { + registerMobStatus(mapid, mse, cancelAction, duration, null, -1); + } + + public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) { + mobStatusSchedulers[getChannelSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay); + } + + public void interruptMobStatus(int mapid, MonsterStatusEffect mse) { + mobStatusSchedulers[getChannelSchedulerIndex(mapid)].interruptMobStatus(mse); + } + + private class MobStatusScheduler extends BaseScheduler { + + private Map registeredMobStatusOvertime = new HashMap<>(); + private MonitoredReentrantLock overtimeStatusLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_OVTSTATUS, true); + + private class MobStatusOvertimeEntry { + private int procCount; + private int procLimit; + private Runnable r; + + protected MobStatusOvertimeEntry(int delay, Runnable run) { + procCount = 0; + procLimit = (int)Math.ceil((float) delay / YamlConfig.config.server.MOB_STATUS_MONITOR_PROC); + r = run; + } + + protected void update(List toRun) { + procCount++; + if(procCount >= procLimit) { + procCount = 0; + toRun.add(r); + } + } + } + + public MobStatusScheduler() { + super(MonitoredLockType.CHANNEL_MOBSTATUS); + + super.addListener(new SchedulerListener() { + @Override + public void removedScheduledEntries(List toRemove, boolean update) { + List toRun = new ArrayList<>(); + + overtimeStatusLock.lock(); + try { + for(Object mseo : toRemove) { + MonsterStatusEffect mse = (MonsterStatusEffect) mseo; + registeredMobStatusOvertime.remove(mse); + } + + if(update) { + // it's probably ok to use one thread for both management & overtime actions + List mdoeList = new ArrayList<>(registeredMobStatusOvertime.values()); + for(MobStatusOvertimeEntry mdoe : mdoeList) { + mdoe.update(toRun); + } + } + } finally { + overtimeStatusLock.unlock(); + } + + for(Runnable r : toRun) { + r.run(); + } + } + }); + } + + public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) { + if(overtimeStatus != null) { + MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus); + + overtimeStatusLock.lock(); + try { + registeredMobStatusOvertime.put(mse, mdoe); + } finally { + overtimeStatusLock.unlock(); + } + } + + registerEntry(mse, cancelStatus, duration); + } + + public void interruptMobStatus(MonsterStatusEffect mse) { + interruptEntry(mse); + } + + @Override + public void dispose() { + disposeLocks(); + super.dispose(); + } + + private void disposeLocks() { + LockCollector.getInstance().registerDisposeAction(new Runnable() { + @Override + public void run() { + emptyLocks(); + } + }); + } + + private void emptyLocks() { + overtimeStatusLock = overtimeStatusLock.dispose(); + } + + } + +} diff --git a/src/net/server/services/task/channel/OverallService.java b/src/net/server/services/task/channel/OverallService.java new file mode 100644 index 0000000000..74303d3b2f --- /dev/null +++ b/src/net/server/services/task/channel/OverallService.java @@ -0,0 +1,76 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.channel; + +import net.server.services.BaseService; +import config.YamlConfig; +import net.server.audit.locks.MonitoredLockType; +import net.server.services.BaseScheduler; + +/** + * + * @author Ronan + */ +public class OverallService extends BaseService { // thanks Alex for suggesting a refactor over the several channel schedulers unnecessarily populating the Channel class + + private OverallScheduler channelSchedulers[] = new OverallScheduler[YamlConfig.config.server.CHANNEL_LOCKS]; + + public OverallService() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + channelSchedulers[i] = new OverallScheduler(); + } + } + + @Override + public void dispose() { + for(int i = 0; i < YamlConfig.config.server.CHANNEL_LOCKS; i++) { + if(channelSchedulers[i] != null) { + channelSchedulers[i].dispose(); + channelSchedulers[i] = null; + } + } + } + + public void registerOverallAction(int mapid, Runnable runAction, long delay) { + channelSchedulers[getChannelSchedulerIndex(mapid)].registerDelayedAction(runAction, delay); + } + + public void forceRunOverallAction(int mapid, Runnable runAction) { + channelSchedulers[getChannelSchedulerIndex(mapid)].forceRunDelayedAction(runAction); + } + + + public class OverallScheduler extends BaseScheduler { + + public OverallScheduler() { + super(MonitoredLockType.CHANNEL_OVERALL); + } + + public void registerDelayedAction(Runnable runAction, long delay) { + registerEntry(runAction, runAction, delay); + } + + public void forceRunDelayedAction(Runnable runAction) { + interruptEntry(runAction); + } + + } + +} diff --git a/src/net/server/services/task/world/CharacterSaveService.java b/src/net/server/services/task/world/CharacterSaveService.java new file mode 100644 index 0000000000..a1e32fc36b --- /dev/null +++ b/src/net/server/services/task/world/CharacterSaveService.java @@ -0,0 +1,62 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.task.world; + +import net.server.audit.locks.MonitoredLockType; +import net.server.services.BaseScheduler; +import net.server.services.BaseService; + +/** + * + * @author Ronan + */ +public class CharacterSaveService extends BaseService { + + CharacterSaveScheduler chrSaveScheduler = new CharacterSaveScheduler(); + + @Override + public void dispose() { + if(chrSaveScheduler != null) { + chrSaveScheduler.dispose(); + chrSaveScheduler = null; + } + } + + public void registerSaveCharacter(int characterId, Runnable runAction) { + chrSaveScheduler.registerSaveCharacter(characterId, runAction); + } + + private class CharacterSaveScheduler extends BaseScheduler { + + public CharacterSaveScheduler() { + super(MonitoredLockType.WORLD_SAVECHARS); + } + + public void registerSaveCharacter(Integer characterId, Runnable runAction) { + registerEntry(characterId, runAction, 0); + } + + public void unregisterSaveCharacter(Integer characterId) { + interruptEntry(characterId); + } + + } + +} diff --git a/src/net/server/services/type/ChannelServices.java b/src/net/server/services/type/ChannelServices.java new file mode 100644 index 0000000000..d878edf66c --- /dev/null +++ b/src/net/server/services/type/ChannelServices.java @@ -0,0 +1,63 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.type; + +import net.server.services.ServiceType; +import net.server.services.task.channel.EventService; +import net.server.services.task.channel.FaceExpressionService; +import net.server.services.task.channel.MobAnimationService; +import net.server.services.task.channel.MobClearSkillService; +import net.server.services.task.channel.MobMistService; +import net.server.services.task.channel.MobStatusService; +import net.server.services.task.channel.OverallService; +import net.server.services.BaseService; +import net.server.services.Service; + +/** + * + * @author Ronan + */ +public enum ChannelServices implements ServiceType { + + MOB_STATUS(MobStatusService.class), + MOB_ANIMATION(MobAnimationService.class), + MOB_CLEAR_SKILL(MobClearSkillService.class), + MOB_MIST(MobMistService.class), + FACE_EXPRESSION(FaceExpressionService.class), + EVENT(EventService.class), + OVERALL(OverallService.class); + + private Class s; + + private ChannelServices(Class service) { + s = service; + } + + @Override + public Service createService() { + return new Service(s); + } + + @Override + public ChannelServices[] enumValues() { + return ChannelServices.values(); + } + +} diff --git a/src/net/server/services/type/WorldServices.java b/src/net/server/services/type/WorldServices.java new file mode 100644 index 0000000000..033ea3fe38 --- /dev/null +++ b/src/net/server/services/type/WorldServices.java @@ -0,0 +1,51 @@ +/* + This file is part of the HeavenMS MapleStory Server, commands OdinMS-based + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.services.type; + +import net.server.services.ServiceType; +import net.server.services.BaseService; +import net.server.services.Service; +import net.server.services.task.world.CharacterSaveService; + +/** + * + * @author Ronan + */ +public enum WorldServices implements ServiceType { + + SAVE_CHARACTER(CharacterSaveService.class); + + private Class s; + + private WorldServices(Class service) { + s = service; + } + + @Override + public Service createService() { + return new Service(s); + } + + @Override + public WorldServices[] enumValues() { + return WorldServices.values(); + } + +} diff --git a/src/net/server/worker/BaseWorker.java b/src/net/server/task/BaseTask.java similarity index 89% rename from src/net/server/worker/BaseWorker.java rename to src/net/server/task/BaseTask.java index 0c23e3eb79..73b1af0864 100644 --- a/src/net/server/worker/BaseWorker.java +++ b/src/net/server/task/BaseTask.java @@ -17,20 +17,20 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public abstract class BaseWorker implements Runnable { +public abstract class BaseTask implements Runnable { protected World wserv; @Override public void run() {} - public BaseWorker(World world) { + public BaseTask(World world) { wserv = world; } } diff --git a/src/net/server/worker/FredrickWorker.java b/src/net/server/task/BossLogTask.java similarity index 84% rename from src/net/server/worker/FredrickWorker.java rename to src/net/server/task/BossLogTask.java index 87acade713..5c5ce14b05 100644 --- a/src/net/server/worker/FredrickWorker.java +++ b/src/net/server/task/BossLogTask.java @@ -17,17 +17,17 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; -import client.processor.FredrickProcessor; +import server.expeditions.MapleExpeditionBossLog; /** * @author Ronan */ -public class FredrickWorker implements Runnable { +public class BossLogTask implements Runnable { @Override public void run() { - FredrickProcessor.runFredrickSchedule(); + MapleExpeditionBossLog.resetBossLogTable(); } } diff --git a/src/net/server/worker/CharacterAutosaverWorker.java b/src/net/server/task/CharacterAutosaverTask.java similarity index 80% rename from src/net/server/worker/CharacterAutosaverWorker.java rename to src/net/server/task/CharacterAutosaverTask.java index dd3369a364..4c44ef2abb 100644 --- a/src/net/server/worker/CharacterAutosaverWorker.java +++ b/src/net/server/task/CharacterAutosaverTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; +import config.YamlConfig; import net.server.world.World; import client.MapleCharacter; -import constants.ServerConstants; import net.server.PlayerStorage; /** * @author Ronan */ -public class CharacterAutosaverWorker extends BaseWorker implements Runnable { +public class CharacterAutosaverTask extends BaseTask implements Runnable { // thanks Alex (Alex09) for noticing these runnable classes are tasks, "workers" runs them @Override public void run() { - if(!ServerConstants.USE_AUTOSAVE) return; + if(!YamlConfig.config.server.USE_AUTOSAVE) return; PlayerStorage ps = wserv.getPlayerStorage(); for(MapleCharacter chr: ps.getAllCharacters()) { @@ -41,7 +41,7 @@ public class CharacterAutosaverWorker extends BaseWorker implements Runnable { } } - public CharacterAutosaverWorker(World world) { + public CharacterAutosaverTask(World world) { super(world); } } diff --git a/src/net/server/worker/CharacterDiseaseWorker.java b/src/net/server/task/CharacterDiseaseTask.java similarity index 93% rename from src/net/server/worker/CharacterDiseaseWorker.java rename to src/net/server/task/CharacterDiseaseTask.java index a05259c215..9f25bc392c 100644 --- a/src/net/server/worker/CharacterDiseaseWorker.java +++ b/src/net/server/task/CharacterDiseaseTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.Server; @@ -25,7 +25,7 @@ import net.server.Server; * @author Ronan * @info Thread responsible for announcing other players diseases when one enters into a map */ -public class CharacterDiseaseWorker implements Runnable { +public class CharacterDiseaseTask implements Runnable { @Override public void run() { Server serv = Server.getInstance(); diff --git a/src/net/server/worker/CouponWorker.java b/src/net/server/task/CouponTask.java similarity index 94% rename from src/net/server/worker/CouponWorker.java rename to src/net/server/task/CouponTask.java index 22b9607015..4436b3c78d 100644 --- a/src/net/server/worker/CouponWorker.java +++ b/src/net/server/task/CouponTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import java.sql.SQLException; import net.server.Server; @@ -27,7 +27,7 @@ import tools.FilePrinter; * @author Ronan * @info Thread responsible for maintaining coupons EXP & DROP effects active */ -public class CouponWorker implements Runnable { +public class CouponTask implements Runnable { @Override public void run() { try { diff --git a/src/net/server/task/DueyFredrickTask.java b/src/net/server/task/DueyFredrickTask.java new file mode 100644 index 0000000000..17a356134a --- /dev/null +++ b/src/net/server/task/DueyFredrickTask.java @@ -0,0 +1,35 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.task; + +import client.processor.npc.DueyProcessor; +import client.processor.npc.FredrickProcessor; + +/** + * @author Ronan + */ +public class DueyFredrickTask implements Runnable { + + @Override + public void run() { + FredrickProcessor.runFredrickSchedule(); + DueyProcessor.runDueyExpireSchedule(); + } +} diff --git a/src/net/server/worker/EventRecallCoordinatorWorker.java b/src/net/server/task/EventRecallCoordinatorTask.java similarity index 87% rename from src/net/server/worker/EventRecallCoordinatorWorker.java rename to src/net/server/task/EventRecallCoordinatorTask.java index 33e6211d45..585c87d75f 100644 --- a/src/net/server/worker/EventRecallCoordinatorWorker.java +++ b/src/net/server/task/EventRecallCoordinatorTask.java @@ -17,15 +17,15 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; -import net.server.coordinator.MapleEventRecallCoordinator; +import net.server.coordinator.world.MapleEventRecallCoordinator; /** * * @author Ronan */ -public class EventRecallCoordinatorWorker implements Runnable { +public class EventRecallCoordinatorTask implements Runnable { @Override public void run() { diff --git a/src/net/server/task/FamilyDailyResetTask.java b/src/net/server/task/FamilyDailyResetTask.java new file mode 100644 index 0000000000..6e18c6212b --- /dev/null +++ b/src/net/server/task/FamilyDailyResetTask.java @@ -0,0 +1,56 @@ +package net.server.task; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Calendar; + +import client.MapleFamily; +import net.server.world.World; +import tools.DatabaseConnection; +import tools.FilePrinter; + +public class FamilyDailyResetTask implements Runnable { + + private final World world; + + public FamilyDailyResetTask(World world) { + this.world = world; + } + + @Override + public void run() { + resetEntitlementUsage(world); + for(MapleFamily family : world.getFamilies()) { + family.resetDailyReps(); + } + } + + public static void resetEntitlementUsage(World world) { + Calendar resetTime = Calendar.getInstance(); + resetTime.add(Calendar.MINUTE, 1); // to make sure that we're in the "next day", since this is called at midnight + resetTime.set(Calendar.HOUR_OF_DAY, 0); + resetTime.set(Calendar.MINUTE, 0); + resetTime.set(Calendar.SECOND, 0); + resetTime.set(Calendar.MILLISECOND, 0); + try(Connection con = DatabaseConnection.getConnection()) { + try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET todaysrep = 0, reptosenior = 0 WHERE lastresettime <= ?")) { + ps.setLong(1, resetTime.getTimeInMillis()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not reset daily rep for families. On " + Calendar.getInstance().getTime()); + e.printStackTrace(); + } + try(PreparedStatement ps = con.prepareStatement("DELETE FROM family_entitlement WHERE timestamp <= ?")) { + ps.setLong(1, resetTime.getTimeInMillis()); + ps.executeUpdate(); + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not do daily reset for family entitlements. On " + Calendar.getInstance().getTime()); + e.printStackTrace(); + } + } catch(SQLException e) { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB."); + e.printStackTrace(); + } + } +} diff --git a/src/net/server/worker/FishingWorker.java b/src/net/server/task/FishingTask.java similarity index 88% rename from src/net/server/worker/FishingWorker.java rename to src/net/server/task/FishingTask.java index 9094add897..8d45625424 100644 --- a/src/net/server/worker/FishingWorker.java +++ b/src/net/server/task/FishingTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class FishingWorker extends BaseWorker implements Runnable { +public class FishingTask extends BaseTask implements Runnable { @Override public void run() { wserv.runCheckFishingSchedule(); } - public FishingWorker(World world) { + public FishingTask(World world) { super(world); } } diff --git a/src/net/server/worker/HiredMerchantWorker.java b/src/net/server/task/HiredMerchantTask.java similarity index 87% rename from src/net/server/worker/HiredMerchantWorker.java rename to src/net/server/task/HiredMerchantTask.java index a42f82372e..41f624bf75 100644 --- a/src/net/server/worker/HiredMerchantWorker.java +++ b/src/net/server/task/HiredMerchantTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class HiredMerchantWorker extends BaseWorker implements Runnable { +public class HiredMerchantTask extends BaseTask implements Runnable { @Override public void run() { wserv.runHiredMerchantSchedule(); } - public HiredMerchantWorker(World world) { + public HiredMerchantTask(World world) { super(world); } } diff --git a/src/net/server/worker/InvitationWorker.java b/src/net/server/task/InvitationTask.java similarity index 88% rename from src/net/server/worker/InvitationWorker.java rename to src/net/server/task/InvitationTask.java index 8e0189495d..d722c92d7b 100644 --- a/src/net/server/worker/InvitationWorker.java +++ b/src/net/server/task/InvitationTask.java @@ -17,14 +17,14 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; -import net.server.coordinator.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator; /** * @author Ronan */ -public class InvitationWorker implements Runnable { +public class InvitationTask implements Runnable { @Override public void run() { diff --git a/src/net/server/worker/LoginCoordinatorWorker.java b/src/net/server/task/LoginCoordinatorTask.java similarity index 87% rename from src/net/server/worker/LoginCoordinatorWorker.java rename to src/net/server/task/LoginCoordinatorTask.java index 6da6e990b9..bf9109a40b 100644 --- a/src/net/server/worker/LoginCoordinatorWorker.java +++ b/src/net/server/task/LoginCoordinatorTask.java @@ -17,15 +17,15 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; -import net.server.coordinator.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; /** * * @author Ronan */ -public class LoginCoordinatorWorker implements Runnable { +public class LoginCoordinatorTask implements Runnable { @Override public void run() { diff --git a/src/net/server/worker/LoginStorageWorker.java b/src/net/server/task/LoginStorageTask.java similarity index 84% rename from src/net/server/worker/LoginStorageWorker.java rename to src/net/server/task/LoginStorageTask.java index 28751bf8b2..a7b99eaa84 100644 --- a/src/net/server/worker/LoginStorageWorker.java +++ b/src/net/server/task/LoginStorageTask.java @@ -17,16 +17,16 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; -import net.server.coordinator.MapleSessionCoordinator; -import net.server.coordinator.MapleLoginBypassCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.login.MapleLoginBypassCoordinator; /** * * @author Ronan */ -public class LoginStorageWorker implements Runnable { +public class LoginStorageTask implements Runnable { @Override public void run() { diff --git a/src/net/server/worker/MapOwnershipWorker.java b/src/net/server/task/MapOwnershipTask.java similarity index 88% rename from src/net/server/worker/MapOwnershipWorker.java rename to src/net/server/task/MapOwnershipTask.java index 743cc2f361..8b0db7cc45 100644 --- a/src/net/server/worker/MapOwnershipWorker.java +++ b/src/net/server/task/MapOwnershipTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; import net.server.channel.Channel; @@ -25,7 +25,7 @@ import net.server.channel.Channel; /** * @author Ronan */ -public class MapOwnershipWorker extends BaseWorker implements Runnable { +public class MapOwnershipTask extends BaseTask implements Runnable { @Override public void run() { @@ -34,7 +34,7 @@ public class MapOwnershipWorker extends BaseWorker implements Runnable { } } - public MapOwnershipWorker(World world) { + public MapOwnershipTask(World world) { super(world); } } diff --git a/src/net/server/worker/MountTirednessWorker.java b/src/net/server/task/MountTirednessTask.java similarity index 87% rename from src/net/server/worker/MountTirednessWorker.java rename to src/net/server/task/MountTirednessTask.java index 17f2c87a5d..9bc7de66a3 100644 --- a/src/net/server/worker/MountTirednessWorker.java +++ b/src/net/server/task/MountTirednessTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class MountTirednessWorker extends BaseWorker implements Runnable { +public class MountTirednessTask extends BaseTask implements Runnable { @Override public void run() { wserv.runMountSchedule(); } - public MountTirednessWorker(World world) { + public MountTirednessTask(World world) { super(world); } } diff --git a/src/net/server/worker/PartySearchWorker.java b/src/net/server/task/PartySearchTask.java similarity index 88% rename from src/net/server/worker/PartySearchWorker.java rename to src/net/server/task/PartySearchTask.java index 0002898c47..64784ea2dd 100644 --- a/src/net/server/worker/PartySearchWorker.java +++ b/src/net/server/task/PartySearchTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; @@ -25,14 +25,14 @@ import net.server.world.World; * * @author Ronan */ -public class PartySearchWorker extends BaseWorker implements Runnable { +public class PartySearchTask extends BaseTask implements Runnable { @Override public void run() { wserv.runPartySearchUpdateSchedule(); } - public PartySearchWorker(World world) { + public PartySearchTask(World world) { super(world); } } diff --git a/src/net/server/worker/PetFullnessWorker.java b/src/net/server/task/PetFullnessTask.java similarity index 88% rename from src/net/server/worker/PetFullnessWorker.java rename to src/net/server/task/PetFullnessTask.java index 4135735967..9652201f0c 100644 --- a/src/net/server/worker/PetFullnessWorker.java +++ b/src/net/server/task/PetFullnessTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class PetFullnessWorker extends BaseWorker implements Runnable { +public class PetFullnessTask extends BaseTask implements Runnable { @Override public void run() { wserv.runPetSchedule(); } - public PetFullnessWorker(World world) { + public PetFullnessTask(World world) { super(world); } } diff --git a/src/net/server/worker/RankingCommandWorker.java b/src/net/server/task/RankingCommandTask.java similarity index 92% rename from src/net/server/worker/RankingCommandWorker.java rename to src/net/server/task/RankingCommandTask.java index 27c7985d2c..b5f95fdb52 100644 --- a/src/net/server/worker/RankingCommandWorker.java +++ b/src/net/server/task/RankingCommandTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.Server; @@ -25,7 +25,7 @@ import net.server.Server; * * @author Ronan */ -public class RankingCommandWorker implements Runnable { +public class RankingCommandTask implements Runnable { @Override public void run() { diff --git a/src/net/server/worker/RankingLoginWorker.java b/src/net/server/task/RankingLoginTask.java similarity index 95% rename from src/net/server/worker/RankingLoginWorker.java rename to src/net/server/task/RankingLoginTask.java index 32cc51afd7..c56a9ea4db 100644 --- a/src/net/server/worker/RankingLoginWorker.java +++ b/src/net/server/task/RankingLoginTask.java @@ -19,15 +19,16 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import client.MapleJob; +import config.YamlConfig; import tools.DatabaseConnection; -import constants.ServerConstants; +import constants.net.ServerConstants; import net.server.Server; /** @@ -35,7 +36,7 @@ import net.server.Server; * @author Quit * @author Ronan */ -public class RankingLoginWorker implements Runnable { +public class RankingLoginTask implements Runnable { private Connection con; private long lastUpdate = System.currentTimeMillis(); @@ -85,7 +86,7 @@ public class RankingLoginWorker implements Runnable { con = DatabaseConnection.getConnection(); con.setAutoCommit(false); - if(ServerConstants.USE_REFRESH_RANK_MOVE == true) { + if(YamlConfig.config.server.USE_REFRESH_RANK_MOVE == true) { resetMoveRank(true); resetMoveRank(false); } diff --git a/src/net/server/worker/ReleaseLockWorker.java b/src/net/server/task/ReleaseLockTask.java similarity index 93% rename from src/net/server/worker/ReleaseLockWorker.java rename to src/net/server/task/ReleaseLockTask.java index 4571966926..7a54ec264c 100644 --- a/src/net/server/worker/ReleaseLockWorker.java +++ b/src/net/server/task/ReleaseLockTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.audit.LockCollector; @@ -25,7 +25,7 @@ import net.server.audit.LockCollector; * @author Ronan * @info Thread responsible for expiring locks signalized for dispose. */ -public class ReleaseLockWorker implements Runnable { +public class ReleaseLockTask implements Runnable { @Override public void run() { LockCollector.getInstance().runLockCollector(); diff --git a/src/net/server/task/RespawnTask.java b/src/net/server/task/RespawnTask.java new file mode 100644 index 0000000000..0a3c74078d --- /dev/null +++ b/src/net/server/task/RespawnTask.java @@ -0,0 +1,27 @@ +package net.server.task; + +import net.server.PlayerStorage; +import net.server.Server; +import net.server.channel.Channel; +import server.maps.MapleMapManager; + +/** + * @author Resinate + */ +public class RespawnTask implements Runnable { + + @Override + public void run() { + for (Channel ch : Server.getInstance().getAllChannels()) { + PlayerStorage ps = ch.getPlayerStorage(); + if (ps != null) { + if (!ps.getAllCharacters().isEmpty()) { + MapleMapManager mapManager = ch.getMapFactory(); + if (mapManager != null) { + mapManager.updateMaps(); + } + } + } + } + } +} diff --git a/src/net/server/worker/ServerMessageWorker.java b/src/net/server/task/ServerMessageTask.java similarity index 89% rename from src/net/server/worker/ServerMessageWorker.java rename to src/net/server/task/ServerMessageTask.java index 6f24941b25..06982ba19a 100644 --- a/src/net/server/worker/ServerMessageWorker.java +++ b/src/net/server/task/ServerMessageTask.java @@ -17,14 +17,14 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class ServerMessageWorker extends BaseWorker implements Runnable { +public class ServerMessageTask extends BaseTask implements Runnable { @Override public void run() { @@ -34,7 +34,7 @@ public class ServerMessageWorker extends BaseWorker implements Runnable { wserv.runDisabledServerMessagesSchedule(); } - public ServerMessageWorker(World world) { + public ServerMessageTask(World world) { super(world); } } diff --git a/src/net/server/worker/TimedMapObjectWorker.java b/src/net/server/task/TimedMapObjectTask.java similarity index 87% rename from src/net/server/worker/TimedMapObjectWorker.java rename to src/net/server/task/TimedMapObjectTask.java index 231094b8c4..7c5f9f75e6 100644 --- a/src/net/server/worker/TimedMapObjectWorker.java +++ b/src/net/server/task/TimedMapObjectTask.java @@ -17,21 +17,21 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import net.server.world.World; /** * @author Ronan */ -public class TimedMapObjectWorker extends BaseWorker implements Runnable { +public class TimedMapObjectTask extends BaseTask implements Runnable { @Override public void run() { wserv.runTimedMapObjectSchedule(); } - public TimedMapObjectWorker(World world) { + public TimedMapObjectTask(World world) { super(world); } } diff --git a/src/net/server/task/TimeoutTask.java b/src/net/server/task/TimeoutTask.java new file mode 100644 index 0000000000..1ecdfc5d35 --- /dev/null +++ b/src/net/server/task/TimeoutTask.java @@ -0,0 +1,30 @@ +package net.server.task; + +import client.MapleCharacter; +import config.YamlConfig; +import net.server.world.World; +import tools.FilePrinter; + +import java.util.Collection; + +/** + * + * @author Shavit + */ +public class TimeoutTask extends BaseTask implements Runnable { + @Override + public void run() { + long time = System.currentTimeMillis(); + Collection chars = wserv.getPlayerStorage().getAllCharacters(); + for(MapleCharacter chr : chars) { + if(time - chr.getClient().getLastPacket() > YamlConfig.config.server.TIMEOUT_DURATION) { + FilePrinter.print(FilePrinter.DCS + chr.getClient().getAccountName(), chr.getName() + " auto-disconnected due to inactivity."); + chr.getClient().disconnect(true, chr.getCashShop().isOpened()); + } + } + } + + public TimeoutTask(World world) { + super(world); + } +} diff --git a/src/net/server/worker/WeddingReservationWorker.java b/src/net/server/task/WeddingReservationTask.java similarity index 92% rename from src/net/server/worker/WeddingReservationWorker.java rename to src/net/server/task/WeddingReservationTask.java index 8ee90890c4..b7a9b0bcf0 100644 --- a/src/net/server/worker/WeddingReservationWorker.java +++ b/src/net/server/task/WeddingReservationTask.java @@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.worker; +package net.server.task; import java.util.Set; import net.server.world.World; @@ -27,7 +27,7 @@ import tools.Pair; /** * @author Ronan */ -public class WeddingReservationWorker extends BaseWorker implements Runnable { +public class WeddingReservationTask extends BaseTask implements Runnable { @Override public void run() { @@ -50,7 +50,7 @@ public class WeddingReservationWorker extends BaseWorker implements Runnable { } } - public WeddingReservationWorker(World world) { + public WeddingReservationTask(World world) { super(world); } } diff --git a/src/net/server/world/MapleParty.java b/src/net/server/world/MapleParty.java index 728cfbe4a4..7d0d40191b 100644 --- a/src/net/server/world/MapleParty.java +++ b/src/net/server/world/MapleParty.java @@ -23,7 +23,7 @@ package net.server.world; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; +import config.YamlConfig; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; @@ -36,7 +36,7 @@ import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.MapleMatchCheckerCoordinator; +import net.server.coordinator.matchchecker.MapleMatchCheckerCoordinator; import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType; import scripting.event.EventInstanceManager; import server.maps.MapleDoor; @@ -130,7 +130,7 @@ public class MapleParty { public Collection getMembers() { lock.lock(); try { - return Collections.unmodifiableList(members); + return new LinkedList<>(members); } finally { lock.unlock(); } @@ -139,7 +139,24 @@ public class MapleParty { public List getPartyMembers() { lock.lock(); try { - return Collections.unmodifiableList(members); + return new LinkedList<>(members); + } finally { + lock.unlock(); + } + } + + public List getPartyMembersOnline() { + lock.lock(); + try { + List ret = new LinkedList<>(); + + for (MaplePartyCharacter mpc : members) { + if (mpc.isOnline()) { + ret.add(mpc); + } + } + + return ret; } finally { lock.unlock(); } @@ -327,7 +344,7 @@ public class MapleParty { public static boolean createParty(MapleCharacter player, boolean silentCheck) { MapleParty party = player.getParty(); if (party == null) { - if (player.getLevel() < 10 && !ServerConstants.USE_PARTY_FOR_STARTERS) { + if (player.getLevel() < 10 && !YamlConfig.config.server.USE_PARTY_FOR_STARTERS) { player.announce(MaplePacketCreator.partyStatusMessage(10)); return false; } else if (player.getAriantColiseum() != null) { @@ -339,7 +356,7 @@ public class MapleParty { party = player.getWorldServer().createParty(partyplayer); player.setParty(party); player.setMPC(partyplayer); - player.getMap().addPartyMember(player); + player.getMap().addPartyMember(player, party.getId()); player.silentPartyUpdate(); player.updatePartySearchAvailability(false); @@ -366,7 +383,7 @@ public class MapleParty { if (party != null) { if (party.getMembers().size() < 6) { MaplePartyCharacter partyplayer = new MaplePartyCharacter(player); - player.getMap().addPartyMember(player); + player.getMap().addPartyMember(player, party.getId()); world.updateParty(party.getId(), PartyOperation.JOIN, partyplayer); player.receivePartyMemberHP(); @@ -416,7 +433,7 @@ public class MapleParty { } else { MapleMap map = player.getMap(); if (map != null) { - map.removePartyMember(player); + map.removePartyMember(player, party.getId()); } MonsterCarnival mcpq = player.getMonsterCarnival(); @@ -452,10 +469,10 @@ public class MapleParty { if (expelled != null) { MapleCharacter emc = expelled.getPlayer(); if(emc != null) { - List partyMembers = emc.getPartyMembers(); + List partyMembers = emc.getPartyMembersOnline(); MapleMap map = emc.getMap(); - if(map != null) map.removePartyMember(emc); + if(map != null) map.removePartyMember(emc, party.getId()); MonsterCarnival mcpq = player.getMonsterCarnival(); if (mcpq != null) { diff --git a/src/net/server/world/MaplePartyCharacter.java b/src/net/server/world/MaplePartyCharacter.java index 2d0f677925..cf08d9568e 100644 --- a/src/net/server/world/MaplePartyCharacter.java +++ b/src/net/server/world/MaplePartyCharacter.java @@ -82,6 +82,9 @@ public class MaplePartyCharacter { public void setOnline(boolean online) { this.online = online; + if (!online) { + this.character = null; // thanks Feras for noticing offline party members retaining whole character object unnecessarily + } } public int getMapId() { diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 58c8046963..a4a7c6ca7d 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -27,10 +27,10 @@ import client.BuddyList.BuddyOperation; import client.BuddylistEntry; import client.MapleCharacter; import client.MapleFamily; -import constants.GameConstants; -import constants.ServerConstants; -import java.sql.Connection; +import config.YamlConfig; +import constants.game.GameConstants; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -47,10 +47,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.Set; import java.util.HashSet; @@ -59,43 +57,55 @@ import java.util.WeakHashMap; import java.util.concurrent.ScheduledFuture; import scripting.event.EventInstanceManager; +import server.MapleStorage; import server.TimerManager; +import server.maps.AbstractMapleMapObject; import server.maps.MapleHiredMerchant; import server.maps.MapleMap; +import server.maps.MapleMiniDungeon; +import server.maps.MapleMiniDungeonInfo; import server.maps.MaplePlayerShop; import server.maps.MaplePlayerShopItem; -import server.maps.AbstractMapleMapObject; -import net.server.worker.CharacterAutosaverWorker; -import net.server.worker.FishingWorker; -import net.server.worker.HiredMerchantWorker; -import net.server.worker.MapOwnershipWorker; -import net.server.worker.MountTirednessWorker; -import net.server.worker.PartySearchWorker; -import net.server.worker.PetFullnessWorker; -import net.server.worker.ServerMessageWorker; -import net.server.worker.TimedMapObjectWorker; -import net.server.worker.WeddingReservationWorker; import net.server.PlayerStorage; import net.server.Server; import net.server.audit.LockCollector; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; +import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.server.channel.Channel; import net.server.channel.CharacterIdChannelPair; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.matchchecker.MapleMatchCheckerCoordinator; +import net.server.coordinator.partysearch.MaplePartySearchCoordinator; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; import net.server.guild.MapleGuildSummary; +import net.server.services.BaseService; +import net.server.services.ServicesManager; +import net.server.services.type.WorldServices; +import net.server.task.CharacterAutosaverTask; +import net.server.task.FamilyDailyResetTask; +import net.server.task.FishingTask; +import net.server.task.HiredMerchantTask; +import net.server.task.MapOwnershipTask; +import net.server.task.MountTirednessTask; +import net.server.task.PartySearchTask; +import net.server.task.PetFullnessTask; +import net.server.task.ServerMessageTask; +import net.server.task.TimedMapObjectTask; +import net.server.task.TimeoutTask; +import net.server.task.WeddingReservationTask; import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; import tools.packets.Fishing; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; -import net.server.audit.locks.MonitoredReentrantReadWriteLock; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; -import net.server.coordinator.MapleMatchCheckerCoordinator; -import net.server.coordinator.MaplePartySearchCoordinator; /** * @@ -116,19 +126,21 @@ public class World { private Map> relationshipCouples = new HashMap<>(); private Map gsStore = new HashMap<>(); private PlayerStorage players = new PlayerStorage(); + private ServicesManager services = new ServicesManager(WorldServices.SAVE_CHARACTER); private MapleMatchCheckerCoordinator matchChecker = new MapleMatchCheckerCoordinator(); private MaplePartySearchCoordinator partySearch = new MaplePartySearchCoordinator(); - private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true); - private ReadLock chnRLock = chnLock.readLock(); - private WriteLock chnWLock = chnLock.writeLock(); + private final MonitoredReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true); + private MonitoredReadLock chnRLock = MonitoredReadLockFactory.createLock(chnLock); + private MonitoredWriteLock chnWLock = MonitoredWriteLockFactory.createLock(chnLock); private Map> accountChars = new HashMap<>(); + private Map accountStorages = new HashMap<>(); private MonitoredReentrantLock accountCharsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_CHARS, true); private Set queuedGuilds = new HashSet<>(); private Map, Pair>> queuedMarriages = new HashMap<>(); - private Map> marriageGuests = new HashMap<>(); + private Map> marriageGuests = new ConcurrentHashMap<>(); private Map partyChars = new HashMap<>(); private Map parties = new HashMap<>(); @@ -137,9 +149,9 @@ public class World { private Map owlSearched = new LinkedHashMap<>(); private List> cashItemBought = new ArrayList<>(9); - private final ReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true); - private ReadLock suggestRLock = suggestLock.readLock(); - private WriteLock suggestWLock = suggestLock.writeLock(); + private final MonitoredReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true); + private MonitoredReadLock suggestRLock = MonitoredReadLockFactory.createLock(suggestLock); + private MonitoredWriteLock suggestWLock = MonitoredWriteLockFactory.createLock(suggestLock); private Map disabledServerMessages = new HashMap<>(); // reuse owl lock private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES); @@ -174,6 +186,7 @@ public class World { private ScheduledFuture mapOwnershipSchedule; private ScheduledFuture fishingSchedule; private ScheduledFuture partySearchSchedule; + private ScheduledFuture timeoutSchedule; public World(int world, int flag, String eventmsg, int exprate, int droprate, int bossdroprate, int mesorate, int questrate, int travelrate, int fishingrate) { this.id = world; @@ -197,17 +210,23 @@ public class World { } TimerManager tman = TimerManager.getInstance(); - petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000); - srvMessagesSchedule = tman.register(new ServerMessageWorker(this), 10 * 1000, 10 * 1000); - mountsSchedule = tman.register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000); - merchantSchedule = tman.register(new HiredMerchantWorker(this), 10 * 60 * 1000, 10 * 60 * 1000); - timedMapObjectsSchedule = tman.register(new TimedMapObjectWorker(this), 60 * 1000, 60 * 1000); - charactersSchedule = tman.register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000); - marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000); - mapOwnershipSchedule = tman.register(new MapOwnershipWorker(this), 20 * 1000, 20 * 1000); - fishingSchedule = tman.register(new FishingWorker(this), 10 * 1000, 10 * 1000); - partySearchSchedule = tman.register(new PartySearchWorker(this), 10 * 1000, 10 * 1000); + petsSchedule = tman.register(new PetFullnessTask(this), 60 * 1000, 60 * 1000); + srvMessagesSchedule = tman.register(new ServerMessageTask(this), 10 * 1000, 10 * 1000); + mountsSchedule = tman.register(new MountTirednessTask(this), 60 * 1000, 60 * 1000); + merchantSchedule = tman.register(new HiredMerchantTask(this), 10 * 60 * 1000, 10 * 60 * 1000); + timedMapObjectsSchedule = tman.register(new TimedMapObjectTask(this), 60 * 1000, 60 * 1000); + charactersSchedule = tman.register(new CharacterAutosaverTask(this), 60 * 60 * 1000, 60 * 60 * 1000); + marriagesSchedule = tman.register(new WeddingReservationTask(this), YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000, YamlConfig.config.server.WEDDING_RESERVATION_INTERVAL * 60 * 1000); + mapOwnershipSchedule = tman.register(new MapOwnershipTask(this), 20 * 1000, 20 * 1000); + fishingSchedule = tman.register(new FishingTask(this), 10 * 1000, 10 * 1000); + partySearchSchedule = tman.register(new PartySearchTask(this), 10 * 1000, 10 * 1000); + timeoutSchedule = tman.register(new TimeoutTask(this), 10 * 1000, 10 * 1000); + if(YamlConfig.config.server.USE_FAMILY_SYSTEM) { + long timeLeft = Server.getTimeLeftForNextDay(); + FamilyDailyResetTask.resetEntitlementUsage(this); + tman.register(new FamilyDailyResetTask(this), 24 * 60 * 60 * 1000, timeLeft); + } } public int getChannelsSize() { @@ -431,6 +450,41 @@ public class World { } } + public void clearAccountCharacterView(Integer accountId) { + accountCharsLock.lock(); + try { + SortedMap accChars = accountChars.remove(accountId); + if (accChars != null) { + accChars.clear(); + } + } finally { + accountCharsLock.unlock(); + } + } + + public void registerAccountStorage(Integer accountId) { + MapleStorage storage = MapleStorage.loadOrCreateFromDB(accountId, this.id); + accountCharsLock.lock(); + try { + accountStorages.put(accountId, storage); + } finally { + accountCharsLock.unlock(); + } + } + + public void unregisterAccountStorage(Integer accountId) { + accountCharsLock.lock(); + try { + accountStorages.remove(accountId); + } finally { + accountCharsLock.unlock(); + } + } + + public MapleStorage getAccountStorage(Integer accountId) { + return accountStorages.get(accountId); + } + private static List>> getSortedAccountCharacterView(Map> map) { List>> list = new ArrayList<>(map.size()); for(Entry> e : map.entrySet()) { @@ -503,7 +557,7 @@ public class World { public MaplePartySearchCoordinator getPartySearchCoordinator() { return partySearch; } - + public void addPlayer(MapleCharacter chr) { players.addPlayer(chr); } @@ -537,6 +591,12 @@ public class World { } } } + + public void removeFamily(int id) { + synchronized (families) { + families.remove(id); + } + } public MapleFamily getFamily(int id) { synchronized (families) { @@ -546,6 +606,12 @@ public class World { return null; } } + + public Collection getFamilies() { + synchronized(families) { + return Collections.unmodifiableCollection((Collection) families.values()); + } + } public MapleGuild getGuild(MapleGuildCharacter mgc) { if(mgc == null) return null; @@ -563,7 +629,7 @@ public class World { } public int getWorldCapacityStatus() { - int worldCap = getChannelsSize() * ServerConstants.CHANNEL_LOAD; + int worldCap = getChannelsSize() * YamlConfig.config.server.CHANNEL_LOAD; int num = players.getSize(); int status; @@ -668,14 +734,14 @@ public class World { } public void sendPacket(List targetIds, final byte[] packet, int exception) { - MapleCharacter c; + MapleCharacter chr; for (int i : targetIds) { if (i == exception) { continue; } - c = getPlayerStorage().getCharacterById(i); - if (c != null) { - c.getClient().announce(packet); + chr = getPlayerStorage().getCharacterById(i); + if (chr != null) { + chr.getClient().announce(packet); } } } @@ -718,7 +784,7 @@ public class World { return new Pair<>(type, guests); } - public synchronized boolean addMarriageGuest(int marriageId, int playerId) { + public boolean addMarriageGuest(int marriageId, int playerId) { Set guests = marriageGuests.get(marriageId); if(guests != null) { if(guests.contains(playerId)) return false; @@ -888,7 +954,7 @@ public class World { chr.setParty(party); chr.setMPC(partychar); } - chr.getClient().announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target)); + chr.announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target)); } } switch (operation) { @@ -896,7 +962,7 @@ public class World { case EXPEL: MapleCharacter chr = getPlayerStorage().getCharacterById(target.getId()); if (chr != null) { - chr.getClient().announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target)); + chr.announce(MaplePacketCreator.updateParty(chr.getClient().getChannel(), party, operation, target)); chr.setParty(null); chr.setMPC(null); } @@ -927,12 +993,25 @@ public class World { break; case CHANGE_LEADER: MapleCharacter mc = party.getLeader().getPlayer(); - EventInstanceManager eim = mc.getEventInstance(); - - if(eim != null && eim.isEventLeader(mc)) { - eim.changedLeader(target.getPlayer()); + if (mc != null) { + EventInstanceManager eim = mc.getEventInstance(); + + if(eim != null && eim.isEventLeader(mc)) { + eim.changedLeader(target); + } else { + int oldLeaderMapid = mc.getMapId(); + + if (MapleMiniDungeonInfo.isDungeonMap(oldLeaderMapid)) { + if (oldLeaderMapid != target.getMapId()) { + MapleMiniDungeon mmd = mc.getClient().getChannelServer().getMiniDungeon(oldLeaderMapid); + if(mmd != null) { + mmd.close(); + } + } + } + } + party.setLeader(target); } - party.setLeader(target); break; default: System.out.println("Unhandled updateParty operation " + operation.name()); @@ -1093,7 +1172,7 @@ public class World { if (isConnected(sender)) { MapleCharacter senderChr = getPlayerStorage().getCharacterByName(sender); if (senderChr != null && senderChr.getMessenger() != null) { - if (MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), senderChr.getMessenger().getId(), false).getLeft() == InviteResult.DENIED) { + if (MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), senderChr.getMessenger().getId(), false).result == InviteResult.DENIED) { senderChr.getClient().announce(MaplePacketCreator.messengerNote(player.getName(), 5, 0)); } } @@ -1246,7 +1325,7 @@ public class World { } public List> getOwlSearchedItems() { - if(ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) { + if(YamlConfig.config.server.USE_ENFORCE_ITEM_SUGGESTION) { return new ArrayList<>(0); } @@ -1281,7 +1360,7 @@ public class World { } private List>> getBoughtCashItems() { - if (ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) { + if (YamlConfig.config.server.USE_ENFORCE_ITEM_SUGGESTION) { List>> boughtCounts = new ArrayList<>(9); // thanks GabrielSin for pointing out an issue here @@ -1371,7 +1450,7 @@ public class World { } public void registerPetHunger(MapleCharacter chr, byte petSlot) { - if(chr.isGM() && ServerConstants.GM_PETS_NEVER_HUNGRY || ServerConstants.PETS_NEVER_HUNGRY) { + if(chr.isGM() && YamlConfig.config.server.GM_PETS_NEVER_HUNGRY || YamlConfig.config.server.PETS_NEVER_HUNGRY) { return; } @@ -1380,8 +1459,8 @@ public class World { activePetsLock.lock(); try { int initProc; - if(Server.getInstance().getCurrentTime() - petUpdate > 55000) initProc = ServerConstants.PET_EXHAUST_COUNT - 2; - else initProc = ServerConstants.PET_EXHAUST_COUNT - 1; + if(Server.getInstance().getCurrentTime() - petUpdate > 55000) initProc = YamlConfig.config.server.PET_EXHAUST_COUNT - 2; + else initProc = YamlConfig.config.server.PET_EXHAUST_COUNT - 1; activePets.put(key, initProc); } finally { @@ -1416,7 +1495,7 @@ public class World { if(chr == null || !chr.isLoggedinWorld()) continue; Integer dpVal = dp.getValue() + 1; - if(dpVal == ServerConstants.PET_EXHAUST_COUNT) { + if(dpVal == YamlConfig.config.server.PET_EXHAUST_COUNT) { chr.runFullnessSchedule(dp.getKey() % 4); dpVal = 0; } @@ -1431,7 +1510,7 @@ public class World { } public void registerMountHunger(MapleCharacter chr) { - if(chr.isGM() && ServerConstants.GM_PETS_NEVER_HUNGRY || ServerConstants.PETS_NEVER_HUNGRY) { + if(chr.isGM() && YamlConfig.config.server.GM_PETS_NEVER_HUNGRY || YamlConfig.config.server.PETS_NEVER_HUNGRY) { return; } @@ -1439,8 +1518,8 @@ public class World { activeMountsLock.lock(); try { int initProc; - if(Server.getInstance().getCurrentTime() - mountUpdate > 45000) initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 2; - else initProc = ServerConstants.MOUNT_EXHAUST_COUNT - 1; + if(Server.getInstance().getCurrentTime() - mountUpdate > 45000) initProc = YamlConfig.config.server.MOUNT_EXHAUST_COUNT - 2; + else initProc = YamlConfig.config.server.MOUNT_EXHAUST_COUNT - 1; activeMounts.put(key, initProc); } finally { @@ -1474,7 +1553,7 @@ public class World { if(chr == null || !chr.isLoggedinWorld()) continue; int dpVal = dp.getValue() + 1; - if(dpVal == ServerConstants.MOUNT_EXHAUST_COUNT) { + if(dpVal == YamlConfig.config.server.MOUNT_EXHAUST_COUNT) { if (!chr.runTirednessSchedule()) { continue; } @@ -1989,6 +2068,14 @@ public class World { partySearch.runPartySearch(); } + public BaseService getServiceAccess(WorldServices sv) { + return services.getAccess(sv).getService(); + } + + private void closeWorldServices() { + services.shutdown(); + } + private void clearWorldData() { List pList; partyLock.lock(); @@ -2002,6 +2089,7 @@ public class World { p.disposeLocks(); } + closeWorldServices(); disposeLocks(); } @@ -2080,6 +2168,11 @@ public class World { partySearchSchedule = null; } + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + players.disconnectAll(); players = null; diff --git a/src/provider/MapleDataTool.java b/src/provider/MapleDataTool.java index f06412de25..51b92419e1 100644 --- a/src/provider/MapleDataTool.java +++ b/src/provider/MapleDataTool.java @@ -107,7 +107,12 @@ public class MapleDataTool { } else if (data.getType() == MapleDataType.STRING) { return Integer.parseInt(getString(data)); } else { - return ((Integer) data.getData()).intValue(); + Object numData = data.getData(); + if (numData instanceof Integer) { + return (Integer) numData; + } else { + return (Short) numData; + } } } diff --git a/src/provider/wz/XMLDomMapleData.java b/src/provider/wz/XMLDomMapleData.java index 13606f3e07..b24ec2726a 100644 --- a/src/provider/wz/XMLDomMapleData.java +++ b/src/provider/wz/XMLDomMapleData.java @@ -21,7 +21,7 @@ */ package provider.wz; -import constants.GameConstants; +import constants.game.GameConstants; import java.awt.Point; import java.io.File; import java.io.FileInputStream; diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java index 2fe19c8f1f..01d3b869de 100644 --- a/src/scripting/AbstractPlayerInteraction.java +++ b/src/scripting/AbstractPlayerInteraction.java @@ -30,6 +30,7 @@ import java.util.LinkedList; import java.util.List; import client.Skill; +import config.YamlConfig; import net.server.Server; import net.server.channel.Channel; import net.server.guild.MapleGuild; @@ -53,7 +54,9 @@ import server.partyquest.Pyramid; import server.quest.MapleQuest; import tools.MaplePacketCreator; import client.MapleCharacter; +import client.MapleCharacter.DelayedQuestUpdate; import client.MapleClient; +import client.MapleJob; import client.MapleQuestStatus; import client.SkillFactory; import client.inventory.Equip; @@ -64,10 +67,11 @@ import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import client.inventory.ModifyInventory; import client.inventory.manipulator.MapleInventoryManipulator; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import server.MapleMarriage; +import server.expeditions.MapleExpeditionBossLog; import server.life.MapleNPC; import tools.Pair; @@ -91,11 +95,23 @@ public class AbstractPlayerInteraction { return c.getPlayer(); } + public int getJobId() { + return getPlayer().getJob().getId(); + } + + public MapleJob getJob(){ + return getPlayer().getJob(); + } + + public int getLevel() { + return getPlayer().getLevel(); + } + public MapleMap getMap() { return c.getPlayer().getMap(); } - public static int getHourOfDay() { + public int getHourOfDay() { return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); } @@ -103,7 +119,7 @@ public class AbstractPlayerInteraction { return getMarketPortalId(getWarpMap(mapId)); } - private static int getMarketPortalId(MapleMap map) { + private int getMarketPortalId(MapleMap map) { return (map.findMarketPortal() != null) ? map.findMarketPortal().getId() : map.getRandomPlayerSpawnpoint().getId(); } @@ -137,28 +153,15 @@ public class AbstractPlayerInteraction { } public void warpParty(int id, int portalId, int fromMinId, int fromMaxId) { - for (MapleCharacter mc : getPartyMembers()) { - if(mc.getMapId() >= fromMinId && mc.getMapId() <= fromMaxId) { - mc.changeMap(id, portalId); + for (MapleCharacter mc : this.getPlayer().getPartyMembersOnline()) { + if (mc.isLoggedinWorld()) { + if(mc.getMapId() >= fromMinId && mc.getMapId() <= fromMaxId) { + mc.changeMap(id, portalId); + } } } } - public List getPartyMembers() { - if (getPlayer().getParty() == null) { - return null; - } - List chars = new LinkedList<>(); - for (Channel channel : Server.getInstance().getChannelsFromWorld(getPlayer().getWorld())) { - for (MapleCharacter chr : channel.getPartyMembers(getPlayer().getParty())) { - if (chr != null) { - chars.add(chr); - } - } - } - return chars; - } - public MapleMap getWarpMap(int map) { return getPlayer().getWarpMap(map); } @@ -235,25 +238,43 @@ public class AbstractPlayerInteraction { return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity)); } - private static List convertToIntegerArray(List list) { - List intList = new LinkedList<>(); - for(Double d: list) { - intList.add(d.intValue()); + private List convertToIntegerArray(List list) { + List intList = new ArrayList<>(); // JAVA 7 Rhino script engine. Thanks Bruno, felipepm10 for noticing a typecast issue here. + + if (ServerConstants.JAVA_8) { + for (Object d: list) { + intList.add(((Integer) d).intValue()); + } + } else { + for (Object d: list) { + intList.add(((Double) d).intValue()); + } } return intList; } - public boolean canHoldAll(List itemids) { - List quantity = new LinkedList<>(); - for (int i = 0; i < itemids.size(); i++) { - quantity.add(1.0); + public boolean canHoldAll(List itemids) { + List quantity = new LinkedList<>(); + + if (ServerConstants.JAVA_8) { + Integer intOne = 1; + + for (int i = 0; i < itemids.size(); i++) { + quantity.add(intOne); + } + } else { + Double doubleOne = 1.0; + + for (int i = 0; i < itemids.size(); i++) { + quantity.add(doubleOne); + } } - + return canHoldAll(itemids, quantity); } - public boolean canHoldAll(List itemids, List quantity) { + public boolean canHoldAll(List itemids, List quantity) { return canHoldAll(convertToIntegerArray(itemids), convertToIntegerArray(quantity), true); } @@ -269,7 +290,7 @@ public class AbstractPlayerInteraction { return MapleInventory.checkSpots(c.getPlayer(), addedItems, false); } - private static List> prepareProofInventoryItems(List> items) { + private List> prepareProofInventoryItems(List> items) { List> addedItems = new LinkedList<>(); for(Pair p : items) { Item it = new Item(p.getLeft(), (short) 0, p.getRight().shortValue()); @@ -279,7 +300,7 @@ public class AbstractPlayerInteraction { return addedItems; } - private static List>> prepareInventoryItemList(List itemids, List quantity) { + private List>> prepareInventoryItemList(List itemids, List quantity) { int size = Math.min(itemids.size(), quantity.size()); List>> invList = new ArrayList<>(6); @@ -357,28 +378,7 @@ public class AbstractPlayerInteraction { NPCScriptManager.getInstance().start(c, npcid, script, null); } - public void updateQuest(int questid, int data) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid)); - updateQuest(questid, status.getAnyProgressKey(), data); - } - - public void updateQuest(int questid, String data) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid)); - updateQuest(questid, status.getAnyProgressKey(), data); - } - - public void updateQuest(int questid, int pid, int data) { - updateQuest(questid, pid, String.valueOf(data)); - } - - public void updateQuest(int questid, int pid, String data) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(questid)); - status.setStatus(MapleQuestStatus.Status.STARTED); - status.setProgress(pid, data);//override old if exists - c.getPlayer().updateQuest(status); - } - - public int getQuestStatus(int id) { + public int getQuestStatus(int id) { return c.getPlayer().getQuest(MapleQuest.getInstance(id)).getStatus().getId(); } @@ -386,75 +386,93 @@ public class AbstractPlayerInteraction { return c.getPlayer().getQuest(MapleQuest.getInstance(id)).getStatus(); } - public boolean isQuestCompleted(int quest) { + public boolean isQuestCompleted(int id) { try { - return getQuestStat(quest) == MapleQuestStatus.Status.COMPLETED; + return getQuestStat(id) == MapleQuestStatus.Status.COMPLETED; } catch (NullPointerException e) { e.printStackTrace(); return false; } } - public boolean isQuestActive(int quest) { - return isQuestStarted(quest); + public boolean isQuestActive(int id) { + return isQuestStarted(id); } - public boolean isQuestStarted(int quest) { + public boolean isQuestStarted(int id) { try { - return getQuestStat(quest) == MapleQuestStatus.Status.STARTED; + return getQuestStat(id) == MapleQuestStatus.Status.STARTED; } catch (NullPointerException e) { e.printStackTrace(); return false; } } - public void setQuestProgress(int qid, int progress) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid)); - status.setProgress(status.getAnyProgressKey(), String.valueOf(progress)); + public void setQuestProgress(int id, String progress) { + setQuestProgress(id, 0, progress); } - public void setQuestProgress(int qid, int pid, int progress) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid)); - status.setProgress(pid, String.valueOf(progress)); - } - - public void setStringQuestProgress(int qid, int pid, String progress) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid)); - status.setProgress(pid, progress); + public void setQuestProgress(int id, int progress) { + setQuestProgress(id, 0, "" + progress); } - public int getQuestProgress(int qid) { - MapleQuestStatus status = c.getPlayer().getQuest(MapleQuest.getInstance(qid)); - String progress = status.getProgress(status.getAnyProgressKey()); - - if (progress.isEmpty()) { + public void setQuestProgress(int id, int infoNumber, int progress) { + setQuestProgress(id, infoNumber, "" + progress); + } + + public void setQuestProgress(int id, int infoNumber, String progress) { + c.getPlayer().setQuestProgress(id, infoNumber, progress); + } + + public String getQuestProgress(int id) { + return getQuestProgress(id, 0); + } + + public String getQuestProgress(int id, int infoNumber) { + MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id)); + + if (qs.getInfoNumber() == infoNumber && infoNumber > 0) { + qs = getPlayer().getQuest(MapleQuest.getInstance(infoNumber)); + infoNumber = 0; + } + + if (qs != null) { + return qs.getProgress(infoNumber); + } else { + return ""; + } + } + + public int getQuestProgressInt(int id) { + try { + return Integer.valueOf(getQuestProgress(id)); + } catch (NumberFormatException nfe) { return 0; } - return Integer.parseInt(progress); } - public int getQuestProgress(int qid, int pid) { - if (getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid).isEmpty()) { - return 0; + public int getQuestProgressInt(int id, int infoNumber) { + try { + return Integer.valueOf(getQuestProgress(id, infoNumber)); + } catch (NumberFormatException nfe) { + return 0; } - return Integer.parseInt(getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid)); - } + } - public String getStringQuestProgress(int qid, int pid) { - if (getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid).isEmpty()) { - return ""; + public void resetAllQuestProgress(int id) { + MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id)); + if (qs != null) { + qs.resetAllProgress(); + getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); } - return getPlayer().getQuest(MapleQuest.getInstance(qid)).getProgress(pid); } - public void resetAllQuestProgress(int qid) { - getPlayer().getQuest(MapleQuest.getInstance(qid)).resetAllProgress(); - getClient().announce(MaplePacketCreator.updateQuest(getPlayer().getQuest(MapleQuest.getInstance(qid)), false)); - } - - public void resetQuestProgress(int qid, int pid) { - getPlayer().getQuest(MapleQuest.getInstance(qid)).resetProgress(pid); - getClient().announce(MaplePacketCreator.updateQuest(getPlayer().getQuest(MapleQuest.getInstance(qid)), false)); + public void resetQuestProgress(int id, int infoNumber) { + MapleQuestStatus qs = getPlayer().getQuest(MapleQuest.getInstance(id)); + if (qs != null) { + qs.resetProgress(infoNumber); + getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, false); + } } public boolean forceStartQuest(int id) { @@ -489,26 +507,26 @@ public class AbstractPlayerInteraction { return completeQuest(id, 9010000); } - public boolean startQuest(short id, int npcId) { - return startQuest((int) id, npcId); + public boolean startQuest(short id, int npc) { + return startQuest((int) id, npc); } - public boolean completeQuest(short id, int npcId) { - return completeQuest((int) id, npcId); + public boolean completeQuest(short id, int npc) { + return completeQuest((int) id, npc); } - public boolean startQuest(int id, int npcId) { + public boolean startQuest(int id, int npc) { try { - return MapleQuest.getInstance(id).forceStart(getPlayer(), npcId); + return MapleQuest.getInstance(id).forceStart(getPlayer(), npc); } catch (NullPointerException ex) { ex.printStackTrace(); return false; } } - public boolean completeQuest(int id, int npcId) { + public boolean completeQuest(int id, int npc) { try { - return MapleQuest.getInstance(id).forceComplete(getPlayer(), npcId); + return MapleQuest.getInstance(id).forceComplete(getPlayer(), npc); } catch (NullPointerException ex) { ex.printStackTrace(); return false; @@ -620,9 +638,9 @@ public class AbstractPlayerInteraction { it.setUpgradeSlots(3); } - if(ServerConstants.USE_ENHANCED_CRAFTING == true && c.getPlayer().getCS() == true) { + if(YamlConfig.config.server.USE_ENHANCED_CRAFTING == true && c.getPlayer().getCS() == true) { Equip eqp = (Equip)item; - if(!(c.getPlayer().isGM() && ServerConstants.USE_PERFECT_GM_SCROLL)) { + if(!(c.getPlayer().isGM() && YamlConfig.config.server.USE_PERFECT_GM_SCROLL)) { eqp.setUpgradeSlots((byte)(eqp.getUpgradeSlots() + 1)); } item = MapleItemInformationProvider.getInstance().scrollEquipWithId(item, 2049100, true, 2049100, c.getPlayer().isGM()); @@ -674,6 +692,10 @@ public class AbstractPlayerInteraction { public void message(String message) { getPlayer().message(message); } + + public void dropMessage(int type, String message) { + getPlayer().dropMessage(type, message); + } public void mapMessage(int type, String message) { getPlayer().getMap().broadcastMessage(MaplePacketCreator.serverNotice(type, message)); @@ -784,9 +806,14 @@ public class AbstractPlayerInteraction { removeAll(id); return; } - for (MaplePartyCharacter chr : getParty().getMembers()) { - if (chr != null && chr.isOnline() && chr.getPlayer().getClient() != null){ - removeAll(id, chr.getPlayer().getClient()); + for (MaplePartyCharacter mpc : getParty().getMembers()) { + if (mpc == null || !mpc.isOnline()) { + continue; + } + + MapleCharacter chr = mpc.getPlayer(); + if (chr != null && chr.getClient() != null){ + removeAll(id, chr.getClient()); } } } @@ -817,9 +844,14 @@ public class AbstractPlayerInteraction { if(instance) { for(MaplePartyCharacter member: party.getMembers()) { - if(member == null || !member.isOnline() || member.getPlayer().getEventInstance() == null){ + if(member == null || !member.isOnline()){ size--; - } + } else { + MapleCharacter chr = member.getPlayer(); + if(chr != null && chr.getEventInstance() == null) { + size--; + } + } } } @@ -829,14 +861,17 @@ public class AbstractPlayerInteraction { continue; } MapleCharacter player = member.getPlayer(); + if(player == null) { + continue; + } if(instance && player.getEventInstance() == null){ continue; // They aren't in the instance, don't give EXP. } int base = PartyQuest.getExp(PQ, player.getLevel()); int exp = base * bonus / 100; player.gainExp(exp, true, true); - if(ServerConstants.PQ_BONUS_EXP_RATE > 0 && System.currentTimeMillis() <= ServerConstants.EVENT_END_TIMESTAMP) { - player.gainExp((int) (exp * ServerConstants.PQ_BONUS_EXP_RATE), true, true); + if(YamlConfig.config.server.PQ_BONUS_EXP_RATE > 0 && System.currentTimeMillis() <= YamlConfig.config.server.EVENT_END_TIMESTAMP) { + player.gainExp((int) (exp * YamlConfig.config.server.PQ_BONUS_EXP_RATE), true, true); } } } @@ -947,7 +982,7 @@ public class AbstractPlayerInteraction { c.announce(MaplePacketCreator.modifyInventory(false, Collections.singletonList(new ModifyInventory(0, newItem)))); } - public static void spawnNpc(int npcId, Point pos, MapleMap map) { + public void spawnNpc(int npcId, Point pos, MapleMap map) { MapleNPC npc = MapleLifeFactory.getNPC(npcId); if (npc != null) { npc.setPosition(pos); @@ -966,11 +1001,11 @@ public class AbstractPlayerInteraction { getPlayer().getMap().spawnMonster(monster); } - public static MapleMonster getMonsterLifeFactory(int mid) { + public MapleMonster getMonsterLifeFactory(int mid) { return MapleLifeFactory.getMonster(mid); } - public static MobSkill getMobSkill(int skill, int level) { + public MobSkill getMobSkill(int skill, int level) { return MobSkillFactory.getMobSkill(skill, level); } @@ -1067,18 +1102,29 @@ public class AbstractPlayerInteraction { return (Pyramid) getPlayer().getPartyQuest(); } - public boolean createExpedition(MapleExpeditionType type) { + public int createExpedition(MapleExpeditionType type) { return createExpedition(type, false, 0, 0); } - public boolean createExpedition(MapleExpeditionType type, boolean silent, int minPlayers, int maxPlayers) { - MapleExpedition exped = new MapleExpedition(getPlayer(), type, silent, minPlayers, maxPlayers); - return getPlayer().getClient().getChannelServer().addExpedition(exped); + public int createExpedition(MapleExpeditionType type, boolean silent, int minPlayers, int maxPlayers) { + MapleCharacter player = getPlayer(); + MapleExpedition exped = new MapleExpedition(player, type, silent, minPlayers, maxPlayers); + + int channel = player.getMap().getChannelServer().getId(); + if (!MapleExpeditionBossLog.attemptBoss(player.getId(), channel, exped, false)) { // thanks Conrad for noticing missing expeditions entry limit + return 1; + } + + if (exped.addChannelExpedition(player.getClient().getChannelServer())) { + return 0; + } else { + return -1; + } } public void endExpedition(MapleExpedition exped) { exped.dispose(true); - getPlayer().getClient().getChannelServer().removeExpedition(exped); + exped.removeChannelExpedition(getPlayer().getClient().getChannelServer()); } public MapleExpedition getExpedition(MapleExpeditionType type) { @@ -1128,7 +1174,7 @@ public class AbstractPlayerInteraction { } public boolean canGetFirstJob(int jobType) { - if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP) { + if (YamlConfig.config.server.USE_AUTOASSIGN_STARTERS_AP) { return true; } @@ -1153,7 +1199,7 @@ public class AbstractPlayerInteraction { } } - public static String getFirstJobStatRequirement(int jobType) { + public String getFirstJobStatRequirement(int jobType) { switch(jobType) { case 1: return "STR " + 35; diff --git a/src/scripting/AbstractScriptManager.java b/src/scripting/AbstractScriptManager.java index 2ec8782b11..ec9ad7f043 100644 --- a/src/scripting/AbstractScriptManager.java +++ b/src/scripting/AbstractScriptManager.java @@ -27,12 +27,10 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; +import javax.script.*; -import constants.ServerConstants; +import constants.net.ServerConstants; +import jdk.nashorn.api.scripting.NashornScriptEngine; import tools.FilePrinter; /** @@ -40,45 +38,40 @@ import tools.FilePrinter; * @author Matze */ public abstract class AbstractScriptManager { - - protected ScriptEngine engine; - private ScriptEngineManager sem; + private ScriptEngineFactory sef; protected AbstractScriptManager() { - sem = new ScriptEngineManager(); + sef = new ScriptEngineManager().getEngineByName("javascript").getFactory(); } - protected Invocable getInvocable(String path, MapleClient c) { + protected NashornScriptEngine getScriptEngine(String path) { path = "scripts/" + path; - engine = null; - if (c != null) { - try { - engine = c.getScriptEngine(path); - } catch (NullPointerException npe) { - c = null; // player disconnected - } + File scriptFile = new File(path); + if (!scriptFile.exists()) { + return null; } - if (engine == null) { - File scriptFile = new File(path); - if (!scriptFile.exists()) { - return null; - } - engine = sem.getEngineByName("javascript"); - if (c != null) { - c.setScriptEngine(path, engine); - } - try (FileReader fr = new FileReader(scriptFile)) { - if (ServerConstants.JAVA_8){ - engine.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); - } - engine.eval(fr); - } catch (final ScriptException | IOException t) { - FilePrinter.printError(FilePrinter.INVOCABLE + path.substring(12, path.length()), t, path); - return null; + NashornScriptEngine engine = (NashornScriptEngine) sef.getScriptEngine(); + try (FileReader fr = new FileReader(scriptFile)) { + if (ServerConstants.JAVA_8){ + engine.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); } + engine.eval(fr); + } catch (final ScriptException | IOException t) { + FilePrinter.printError(FilePrinter.INVOCABLE + path.substring(12), t, path); + return null; } - return (Invocable) engine; + return engine; + } + + protected NashornScriptEngine getScriptEngine(String path, MapleClient c) { + NashornScriptEngine engine = c.getScriptEngine("scripts/" + path); + if (engine == null) { + engine = getScriptEngine(path); + c.setScriptEngine(path, engine); + } + + return engine; } protected void resetContext(String path, MapleClient c) { diff --git a/src/scripting/event/EventInstanceManager.java b/src/scripting/event/EventInstanceManager.java index c710dc1f71..87c67d40d4 100644 --- a/src/scripting/event/EventInstanceManager.java +++ b/src/scripting/event/EventInstanceManager.java @@ -21,7 +21,6 @@ */ package scripting.event; -import tools.Pair; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -32,14 +31,20 @@ import java.util.Set; import java.util.Iterator; import java.util.Properties; import javax.script.ScriptException; + +import config.YamlConfig; import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; -import server.MaplePortal; +import server.maps.MaplePortal; import server.TimerManager; import server.MapleStatEffect; import server.expeditions.MapleExpedition; @@ -50,23 +55,21 @@ import server.maps.MapleReactor; import client.MapleCharacter; import client.SkillFactory; import client.Skill; -import constants.ItemConstants; -import constants.ServerConstants; +import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import java.awt.Point; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.logging.Level; import java.util.logging.Logger; -import net.server.coordinator.MapleEventRecallCoordinator; +import net.server.coordinator.world.MapleEventRecallCoordinator; import scripting.AbstractPlayerInteraction; -import scripting.event.worker.EventScriptScheduler; +import scripting.event.scheduler.EventScriptScheduler; import server.MapleItemInformationProvider; import server.ThreadManager; import server.life.MapleLifeFactory; import server.life.MapleNPC; import tools.MaplePacketCreator; +import tools.Pair; /** * @@ -89,9 +92,9 @@ public class EventInstanceManager { private MapleExpedition expedition = null; private List mapIds = new LinkedList<>(); - private final ReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredLockType.EIM, true); - private ReadLock rL = lock.readLock(); - private WriteLock wL = lock.writeLock(); + private final MonitoredReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredLockType.EIM, true); + private MonitoredReadLock rL = MonitoredReadLockFactory.createLock(lock); + private MonitoredWriteLock wL = MonitoredWriteLockFactory.createLock(lock); private MonitoredReentrantLock pL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_PARTY, true); private MonitoredReentrantLock sL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_SCRIPT, true); @@ -102,9 +105,9 @@ public class EventInstanceManager { private boolean eventStarted = false; // multi-leveled PQ rewards! - private Map> collectionSet = new HashMap<>(ServerConstants.MAX_EVENT_LEVELS); - private Map> collectionQty = new HashMap<>(ServerConstants.MAX_EVENT_LEVELS); - private Map collectionExp = new HashMap<>(ServerConstants.MAX_EVENT_LEVELS); + private Map> collectionSet = new HashMap<>(YamlConfig.config.server.MAX_EVENT_LEVELS); + private Map> collectionQty = new HashMap<>(YamlConfig.config.server.MAX_EVENT_LEVELS); + private Map collectionExp = new HashMap<>(YamlConfig.config.server.MAX_EVENT_LEVELS); // Exp/Meso rewards by CLEAR on a stage private List onMapClearExp = new ArrayList<>(); @@ -366,9 +369,13 @@ public class EventInstanceManager { } public void registerParty(MapleParty party, MapleMap map) { - for (MaplePartyCharacter pc : party.getEligibleMembers()) { - MapleCharacter c = map.getCharacterById(pc.getId()); - registerPlayer(c); + for (MaplePartyCharacter mpc : party.getEligibleMembers()) { + if (mpc.isOnline()) { // thanks resinate + MapleCharacter chr = map.getCharacterById(mpc.getId()); + if (chr != null) { + registerPlayer(chr); + } + } } } @@ -468,7 +475,7 @@ public class EventInstanceManager { } catch (ScriptException | NoSuchMethodException ex) {} // optional } - public synchronized void changedLeader(final MapleCharacter ldr) { + public synchronized void changedLeader(final MaplePartyCharacter ldr) { try { invokeScriptFunction("changedLeader", EventInstanceManager.this, ldr); } catch (ScriptException | NoSuchMethodException ex) { @@ -518,6 +525,18 @@ public class EventInstanceManager { invokeScriptFunction("friendlyKilled", mob, EventInstanceManager.this, hasKiller); } catch (ScriptException | NoSuchMethodException ex) {} //optional } + + public void friendlyDamaged(final MapleMonster mob) { + try { + invokeScriptFunction("friendlyDamaged", EventInstanceManager.this, mob); + } catch (ScriptException | NoSuchMethodException ex) {} // optional + } + + public void friendlyItemDrop(final MapleMonster mob) { + try { + invokeScriptFunction("friendlyItemDrop", EventInstanceManager.this, mob); + } catch (ScriptException | NoSuchMethodException ex) {} // optional + } public void playerKilled(final MapleCharacter chr) { ThreadManager.getInstance().newTask(new Runnable() { @@ -880,7 +899,7 @@ public class EventInstanceManager { } } - public void dispatchUpdateQuestMobCount(int mobid, int mapid) { + public void dispatchRaiseQuestMobCount(int mobid, int mapid) { Map mapChars = getInstanceMap(mapid).getMapPlayers(); if(!mapChars.isEmpty()) { List eventMembers = getPlayers(); @@ -889,7 +908,7 @@ public class EventInstanceManager { MapleCharacter chr = mapChars.get(evChr.getId()); if(chr != null && chr.isLoggedinWorld()) { - chr.updateQuestMobCount(mobid); + chr.raiseQuestMobCount(mobid); } } } @@ -899,24 +918,28 @@ public class EventInstanceManager { return(MapleLifeFactory.getMonster(mid)); } - private List convertToIntegerArray(List list) { - List intList; - if(ServerConstants.JAVA_8) - intList=new ArrayList (new ArrayList(java.util.Arrays.asList(list.toArray()))); - else - { - intList = new ArrayList<>(); - for(Double d: list) intList.add(d.intValue()); + private List convertToIntegerArray(List list) { + List intList = new ArrayList<>(); + + if (ServerConstants.JAVA_8) { + for (Object d: list) { + intList.add(((Integer) d).intValue()); + } + } else { + for (Object d: list) { + intList.add(((Double) d).intValue()); + } } - return intList; + + return intList; } - public void setEventClearStageExp(List gain) { + public void setEventClearStageExp(List gain) { onMapClearExp.clear(); onMapClearExp.addAll(convertToIntegerArray(gain)); } - public void setEventClearStageMeso(List gain) { + public void setEventClearStageMeso(List gain) { onMapClearMeso.clear(); onMapClearMeso.addAll(convertToIntegerArray(gain)); } @@ -947,7 +970,7 @@ public class EventInstanceManager { } } - public final void setExclusiveItems(List items) { + public final void setExclusiveItems(List items) { List exclusive = convertToIntegerArray(items); wL.lock(); @@ -960,22 +983,22 @@ public class EventInstanceManager { } } - public final void setEventRewards(List rwds, List qtys, int expGiven) { + public final void setEventRewards(List rwds, List qtys, int expGiven) { setEventRewards(1, rwds, qtys, expGiven); } - public final void setEventRewards(List rwds, List qtys) { + public final void setEventRewards(List rwds, List qtys) { setEventRewards(1, rwds, qtys); } - public final void setEventRewards(int eventLevel, List rwds, List qtys) { + public final void setEventRewards(int eventLevel, List rwds, List qtys) { setEventRewards(eventLevel, rwds, qtys, 0); } - public final void setEventRewards(int eventLevel, List rwds, List qtys, int expGiven) { + public final void setEventRewards(int eventLevel, List rwds, List qtys, int expGiven) { // fixed EXP will be rewarded at the same time the random item is given - if(eventLevel <= 0 || eventLevel > ServerConstants.MAX_EVENT_LEVELS) return; + if(eventLevel <= 0 || eventLevel > YamlConfig.config.server.MAX_EVENT_LEVELS) return; eventLevel--; //event level starts from 1 List rewardIds = convertToIntegerArray(rwds); @@ -1062,7 +1085,7 @@ public class EventInstanceManager { sL.lock(); try { - em.getChannelServer().removeExpedition(expedition); + expedition.removeChannelExpedition(em.getChannelServer()); } finally { sL.unlock(); } @@ -1085,7 +1108,7 @@ public class EventInstanceManager { eventCleared = true; for (MapleCharacter chr : getPlayers()) { - chr.awardQuestPoint(ServerConstants.QUEST_POINT_PER_EVENT_CLEAR); + chr.awardQuestPoint(YamlConfig.config.server.QUEST_POINT_PER_EVENT_CLEAR); } sL.lock(); @@ -1127,8 +1150,8 @@ public class EventInstanceManager { if(eventCleared) { if(leavingEventMap && getPlayerCount() <= 1) return true; } else { - if(leavingEventMap && getLeaderId() == quitter.getId()) return true; - if(getPlayerCount() <= (ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : minPlayers)) return true; + // thanks Conrad for noticing expeditions don't need to have neither the leader nor meet the minimum requirement inside the event + if(getPlayerCount() <= 1) return true; } return false; diff --git a/src/scripting/event/EventManager.java b/src/scripting/event/EventManager.java index ba91944e1f..d9661a6239 100644 --- a/src/scripting/event/EventManager.java +++ b/src/scripting/event/EventManager.java @@ -21,6 +21,9 @@ */ package scripting.event; +import config.YamlConfig; +import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.api.scripting.ScriptUtils; import tools.exceptions.EventInstanceInProgressException; import java.util.Collection; import java.util.HashMap; @@ -29,11 +32,10 @@ import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; -import javax.script.Invocable; import javax.script.ScriptException; -import constants.ServerConstants; -import constants.GameConstants; +import constants.net.ServerConstants; +import constants.game.GameConstants; import client.MapleCharacter; import net.server.Server; import net.server.world.World; @@ -41,7 +43,7 @@ import net.server.channel.Channel; import net.server.guild.MapleGuild; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; -import scripting.event.worker.EventScriptScheduler; +import scripting.event.scheduler.EventScriptScheduler; import server.MapleMarriage; import server.expeditions.MapleExpedition; import server.maps.MapleMap; @@ -57,6 +59,7 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import jdk.nashorn.api.scripting.NashornScriptEngine; import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; @@ -70,7 +73,7 @@ import server.ThreadManager; * @author Ronan */ public class EventManager { - private Invocable iv; + private NashornScriptEngine iv; private Channel cserv; private World wserv; private Server server; @@ -93,7 +96,7 @@ public class EventManager { private static final int maxLobbys = 8; // an event manager holds up to this amount of concurrent lobbys - public EventManager(Channel cserv, Invocable iv, String name) { + public EventManager(Channel cserv, NashornScriptEngine iv, String name) { this.server = Server.getInstance(); this.iv = iv; this.cserv = cserv; @@ -165,23 +168,31 @@ public class EventManager { startLock = startLock.dispose(); } - private static List convertToIntegerArray(List list) { + private List convertToIntegerArray(List list) { List intList = new ArrayList<>(); - for(Double d: list) intList.add(d.intValue()); - + + if (ServerConstants.JAVA_8) { + for (Object d: list) { + intList.add(((Integer) d).intValue()); + } + } else { + for (Object d: list) { + intList.add(((Double) d).intValue()); + } + } + return intList; } - public static long getLobbyDelay() { - return ServerConstants.EVENT_LOBBY_DELAY; + public long getLobbyDelay() { + return YamlConfig.config.server.EVENT_LOBBY_DELAY; } private List getLobbyRange() { try { if (!ServerConstants.JAVA_8) { - return convertToIntegerArray((List)iv.invokeFunction("setLobbyRange", (Object) null)); + return convertToIntegerArray((List)iv.invokeFunction("setLobbyRange", (Object) null)); } else { // java 8 support here thanks to MedicOP - /* ScriptObjectMirror object = (ScriptObjectMirror) iv.invokeFunction("setLobbyRange", (Object) null); int[] to = object.to(int[].class); List list = new ArrayList<>(); @@ -189,9 +200,7 @@ public class EventManager { list.add(i); } return list; - */ - - throw new NoSuchMethodException(); + } } catch (ScriptException | NoSuchMethodException ex) { // they didn't define a lobby range List defaultRange = new ArrayList<>(); @@ -248,7 +257,7 @@ public class EventManager { return cserv; } - public Invocable getIv() { + public NashornScriptEngine getIv() { return iv; } @@ -304,7 +313,7 @@ public class EventManager { instances.remove(name); } } - }, ServerConstants.EVENT_LOBBY_DELAY * 1000); + }, YamlConfig.config.server.EVENT_LOBBY_DELAY * 1000); } public void setProperty(String key, String value) { @@ -339,6 +348,12 @@ public class EventManager { private boolean startLobbyInstance(int lobbyId) { lobbyLock.lock(); try { + if (lobbyId < 0) { + lobbyId = 0; + } else if (lobbyId >= maxLobbys) { + lobbyId = maxLobbys - 1; + } + if(!openedLobbys.get(lobbyId)) { openedLobbys.set(lobbyId, true); return true; @@ -745,18 +760,17 @@ public class EventManager { return(new ArrayList<>()); } try { - Object p = iv.invokeFunction("getEligibleParty", party.getPartyMembers()); + Object p = iv.invokeFunction("getEligibleParty", party.getPartyMembersOnline()); if(p != null) { List lmpc; - /*if(ServerConstants.JAVA_8) { + if(ServerConstants.JAVA_8) { lmpc = new ArrayList<>(((Map)(ScriptUtils.convert(p, Map.class))).values()); } else { lmpc = new ArrayList<>((List) p); - }*/ - - lmpc = new ArrayList<>((List) p); + } + party.setEligibleMembers(lmpc); return lmpc; } @@ -826,7 +840,7 @@ public class EventManager { public boolean isQueueFull() { synchronized(queuedGuilds) { - return queuedGuilds.size() >= ServerConstants.EVENT_MAX_GUILD_QUEUE; + return queuedGuilds.size() >= YamlConfig.config.server.EVENT_MAX_GUILD_QUEUE; } } @@ -911,7 +925,7 @@ public class EventManager { } private void fillEimQueue() { - ThreadManager.getInstance().newTask(new EventManagerWorker()); //call new thread to fill up readied instances queue + ThreadManager.getInstance().newTask(new EventManagerTask()); //call new thread to fill up readied instances queue } private EventInstanceManager getReadyInstance() { @@ -960,7 +974,7 @@ public class EventManager { instantiateQueuedInstance(); // keep filling the queue until reach threshold. } - private class EventManagerWorker implements Runnable { + private class EventManagerTask implements Runnable { @Override public void run() { diff --git a/src/scripting/event/EventScheduledFuture.java b/src/scripting/event/EventScheduledFuture.java index 54c847874f..8d0be3bbb4 100644 --- a/src/scripting/event/EventScheduledFuture.java +++ b/src/scripting/event/EventScheduledFuture.java @@ -19,7 +19,7 @@ */ package scripting.event; -import scripting.event.worker.EventScriptScheduler; +import scripting.event.scheduler.EventScriptScheduler; /** * diff --git a/src/scripting/event/EventScriptManager.java b/src/scripting/event/EventScriptManager.java index 2d7d16d905..1e6afe628b 100644 --- a/src/scripting/event/EventScriptManager.java +++ b/src/scripting/event/EventScriptManager.java @@ -21,14 +21,15 @@ */ package scripting.event; -import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import javax.script.Invocable; -import javax.script.ScriptEngine; +import jdk.nashorn.api.scripting.NashornScriptEngine; import net.server.channel.Channel; import scripting.AbstractScriptManager; @@ -41,54 +42,67 @@ public class EventScriptManager extends AbstractScriptManager { private class EventEntry { - public EventEntry(Invocable iv, EventManager em) { + public EventEntry(NashornScriptEngine iv, EventManager em) { this.iv = iv; this.em = em; } - public Invocable iv; + public NashornScriptEngine iv; public EventManager em; } - private Map events = new LinkedHashMap<>(); - + + private static EventEntry fallback; + private Map events = new ConcurrentHashMap<>(); + private boolean active = false; + public EventScriptManager(Channel cserv, String[] scripts) { super(); for (String script : scripts) { if (!script.equals("")) { - Invocable iv = getInvocable("event/" + script + ".js", null); + NashornScriptEngine iv = getScriptEngine("event/" + script + ".js"); events.put(script, new EventEntry(iv, new EventManager(cserv, iv, script))); } } + + init(); + fallback = events.remove("0_EXAMPLE"); } public EventManager getEventManager(String event) { EventEntry entry = events.get(event); if (entry == null) { - return null; + return fallback.em; } return entry.em; } - - public void init() { + + public boolean isActive() { + return active; + } + + public final void init() { for (EventEntry entry : events.values()) { try { - ((ScriptEngine) entry.iv).put("em", entry.em); + entry.iv.put("em", entry.em); entry.iv.invokeFunction("init", (Object) null); } catch (Exception ex) { Logger.getLogger(EventScriptManager.class.getName()).log(Level.SEVERE, null, ex); System.out.println("Error on script: " + entry.em.getName()); } } + + active = events.size() > 1; // bootup loads only 1 script } private void reloadScripts() { - if (events.isEmpty()) { + Set> eventEntries = new HashSet<>(events.entrySet()); + if (eventEntries.isEmpty()) { return; } - Channel cserv = events.values().iterator().next().em.getChannelServer(); - for (Entry entry : events.entrySet()) { + Channel cserv = eventEntries.iterator().next().getValue().em.getChannelServer(); + for (Entry entry : eventEntries) { String script = entry.getKey(); - Invocable iv = getInvocable("event/" + script + ".js", null); + NashornScriptEngine iv = getScriptEngine("event/" + script + ".js"); events.put(script, new EventEntry(iv, new EventManager(cserv, iv, script))); } } @@ -100,8 +114,23 @@ public class EventScriptManager extends AbstractScriptManager { } public void cancel() { + active = false; for (EventEntry entry : events.values()) { entry.em.cancel(); } } + + public void dispose() { + if (events.isEmpty()) { + return; + } + + Set eventEntries = new HashSet<>(events.values()); + events.clear(); + + active = false; + for (EventEntry entry : eventEntries) { + entry.em.cancel(); + } + } } diff --git a/src/scripting/event/worker/EventScriptScheduler.java b/src/scripting/event/scheduler/EventScriptScheduler.java similarity index 95% rename from src/scripting/event/worker/EventScriptScheduler.java rename to src/scripting/event/scheduler/EventScriptScheduler.java index e94951d113..3e8805f2ac 100644 --- a/src/scripting/event/worker/EventScriptScheduler.java +++ b/src/scripting/event/scheduler/EventScriptScheduler.java @@ -17,9 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package scripting.event.worker; +package scripting.event.scheduler; -import constants.ServerConstants; +import config.YamlConfig; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -66,7 +66,7 @@ public class EventScriptScheduler { if (registeredEntries.isEmpty()) { idleProcs++; - if (idleProcs >= ServerConstants.MOB_STATUS_MONITOR_LIFE) { + if (idleProcs >= YamlConfig.config.server.MOB_STATUS_MONITOR_LIFE) { if (schedulerTask != null) { schedulerTask.cancel(false); schedulerTask = null; @@ -118,7 +118,7 @@ public class EventScriptScheduler { return; } - schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); + schedulerTask = TimerManager.getInstance().register(monitorTask, YamlConfig.config.server.MOB_STATUS_MONITOR_PROC, YamlConfig.config.server.MOB_STATUS_MONITOR_PROC); } registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration); diff --git a/src/scripting/map/MapScriptManager.java b/src/scripting/map/MapScriptManager.java index 04acbecc39..976b37521e 100644 --- a/src/scripting/map/MapScriptManager.java +++ b/src/scripting/map/MapScriptManager.java @@ -21,23 +21,19 @@ along with this program. If not, see . */ package scripting.map; +import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; -import javax.script.Compilable; -import javax.script.Invocable; -import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import jdk.nashorn.api.scripting.NashornScriptEngine; +import scripting.AbstractScriptManager; import tools.FilePrinter; -public class MapScriptManager { +public class MapScriptManager extends AbstractScriptManager { private static MapScriptManager instance = new MapScriptManager(); @@ -45,7 +41,7 @@ public class MapScriptManager { return instance; } - private Map scripts = new HashMap<>(); + private Map scripts = new HashMap<>(); private ScriptEngineFactory sef; private MapScriptManager() { @@ -57,53 +53,42 @@ public class MapScriptManager { scripts.clear(); } - public boolean scriptExists(String scriptName, boolean firstUser) { - File scriptFile = new File("scripts/map/" + (firstUser ? "onFirstUserEnter/" : "onUserEnter/") + scriptName + ".js"); - return scriptFile.exists(); - } - - public void runMapScript(MapleClient c, String scriptName, boolean firstUser) { - if (scripts.containsKey(scriptName)) { + public boolean runMapScript(MapleClient c, String mapScriptPath, boolean firstUser) { + if (firstUser) { + MapleCharacter chr = c.getPlayer(); + int mapid = chr.getMapId(); + if (chr.hasEntered(mapScriptPath, mapid)) { + return false; + } else { + chr.enteredScript(mapScriptPath, mapid); + } + } + + NashornScriptEngine iv = scripts.get(mapScriptPath); + if (iv != null) { try { - scripts.get(scriptName).invokeFunction("start", new MapScriptMethods(c)); + iv.invokeFunction("start", new MapScriptMethods(c)); + return true; } catch (final ScriptException | NoSuchMethodException e) { e.printStackTrace(); } - return; } - String type = firstUser ? "onFirstUserEnter/" : "onUserEnter/"; - - File scriptFile = new File("scripts/map/" + type + scriptName + ".js"); - if (!scriptExists(scriptName, firstUser)) { - return; - } - FileReader fr = null; - ScriptEngine se = sef.getScriptEngine(); + try { - fr = new FileReader(scriptFile); - - // java 8 support here thanks to Arufonsu - if (ServerConstants.JAVA_8){ - se.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); + iv = getScriptEngine("map/" + mapScriptPath + ".js"); + if (iv == null) { + return false; } - ((Compilable) se).compile(fr).eval(); - - final Invocable script = ((Invocable) se); - scripts.put(scriptName, script); - script.invokeFunction("start", new MapScriptMethods(c)); + scripts.put(mapScriptPath, iv); + iv.invokeFunction("start", new MapScriptMethods(c)); + return true; } catch (final UndeclaredThrowableException | ScriptException ute) { - FilePrinter.printError(FilePrinter.MAP_SCRIPT + type + scriptName + ".txt", ute); + FilePrinter.printError(FilePrinter.MAP_SCRIPT + mapScriptPath + ".txt", ute); } catch (final Exception e) { - FilePrinter.printError(FilePrinter.MAP_SCRIPT + type + scriptName + ".txt", e); - } finally { - if (fr != null) { - try { - fr.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + FilePrinter.printError(FilePrinter.MAP_SCRIPT + mapScriptPath + ".txt", e); } + + return false; } } \ No newline at end of file diff --git a/src/scripting/map/MapScriptMethods.java b/src/scripting/map/MapScriptMethods.java index d9faa6cb34..cc94de2edd 100644 --- a/src/scripting/map/MapScriptMethods.java +++ b/src/scripting/map/MapScriptMethods.java @@ -21,6 +21,7 @@ along with this program. If not, see . */ package scripting.map; +import client.MapleCharacter.DelayedQuestUpdate; import client.MapleClient; import client.MapleQuestStatus; import scripting.AbstractPlayerInteraction; @@ -34,7 +35,35 @@ public class MapScriptMethods extends AbstractPlayerInteraction { public MapScriptMethods(MapleClient c) { super(c); } - + + public void displayCygnusIntro() { + switch (c.getPlayer().getMapId()) { + case 913040100: + lockUI(); + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene0")); + break; + case 913040101: + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene1")); + break; + case 913040102: + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene2")); + break; + case 913040103: + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene3")); + break; + case 913040104: + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene4")); + break; + case 913040105: + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene5")); + break; + case 913040106: + lockUI(); + c.announce(MaplePacketCreator.showIntro("Effect/Direction.img/cygnusJobTutorial/Scene6")); + break; + } + } + public void displayAranIntro() { switch (c.getPlayer().getMapId()) { case 914090010: @@ -93,13 +122,13 @@ public class MapScriptMethods extends AbstractPlayerInteraction { return; } } - MapleQuestStatus q = getPlayer().getQuest(quest); - if (!q.addMedalMap(getPlayer().getMapId())) { + MapleQuestStatus qs = getPlayer().getQuest(quest); + if (!qs.addMedalMap(getPlayer().getMapId())) { return; } - String status = Integer.toString(q.getMedalProgress()); - String infoex = quest.getInfoEx(); - getPlayer().announce(MaplePacketCreator.updateQuest(q, true)); + String status = Integer.toString(qs.getMedalProgress()); + String infoex = qs.getInfoEx(0); + getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); StringBuilder smp = new StringBuilder(); StringBuilder etm = new StringBuilder(); if (status.equals(infoex)) { @@ -122,15 +151,15 @@ public class MapScriptMethods extends AbstractPlayerInteraction { return; } } - MapleQuestStatus q = getPlayer().getQuest(quest); - if (!q.addMedalMap(getPlayer().getMapId())) { + MapleQuestStatus qs = getPlayer().getQuest(quest); + if (!qs.addMedalMap(getPlayer().getMapId())) { return; } - String status = Integer.toString(q.getMedalProgress()); - getPlayer().announce(MaplePacketCreator.updateQuest(q, true)); + String status = Integer.toString(qs.getMedalProgress()); + getPlayer().announceUpdateQuest(DelayedQuestUpdate.UPDATE, qs, true); getPlayer().announce(MaplePacketCreator.earnTitleMessage(status + "/5 Completed")); getPlayer().announce(MaplePacketCreator.earnTitleMessage("The One Who's Touched the Sky title in progress.")); - if (Integer.toString(q.getMedalProgress()).equals(quest.getInfoEx())) { + if (Integer.toString(qs.getMedalProgress()).equals(qs.getInfoEx(0))) { showInfoText("The One Who's Touched the Sky" + rewardstring); getPlayer().announce(MaplePacketCreator.getShowQuestCompletion(quest.getId())); } else { diff --git a/src/scripting/npc/NPCConversationManager.java b/src/scripting/npc/NPCConversationManager.java index 0caf491768..c336c18a4e 100644 --- a/src/scripting/npc/NPCConversationManager.java +++ b/src/scripting/npc/NPCConversationManager.java @@ -24,7 +24,7 @@ package scripting.npc; import java.io.File; import java.sql.SQLException; -import constants.ServerConstants; +import config.YamlConfig; import net.server.Server; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; @@ -35,6 +35,7 @@ import provider.MapleDataProviderFactory; import scripting.AbstractPlayerInteraction; import server.MapleItemInformationProvider; import server.MapleStatEffect; +import server.MapleShop; import server.MapleShopFactory; import server.events.gm.MapleEvent; import server.gachapon.MapleGachapon; @@ -56,9 +57,9 @@ import client.SkillFactory; import client.inventory.Item; import client.inventory.ItemFactory; import client.inventory.MaplePet; -import constants.GameConstants; -import constants.ItemConstants; -import constants.LanguageConstants; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; +import constants.string.LanguageConstants; import net.server.PlayerStorage; import net.server.channel.Channel; import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType; @@ -83,6 +84,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import tools.FilePrinter; /** * @@ -97,9 +99,9 @@ public class NPCConversationManager extends AbstractPlayerInteraction { private boolean itemScript; private List otherParty; - private static Map npcDefaultTalks = new HashMap<>(); + private Map npcDefaultTalks = new HashMap<>(); - private static String getDefaultTalk(int npcid) { + private String getDefaultTalk(int npcid) { String talk = npcDefaultTalks.get(npcid); if (talk == null) { talk = MapleLifeFactory.getNPCDefaultTalk(npcid); @@ -251,14 +253,6 @@ public class NPCConversationManager extends AbstractPlayerInteraction { return this.getText; } - public int getJobId() { - return getPlayer().getJob().getId(); - } - - public MapleJob getJob(){ - return getPlayer().getJob(); - } - @Override public boolean forceStartQuest(int id) { return forceStartQuest(id, npc); @@ -301,10 +295,6 @@ public class NPCConversationManager extends AbstractPlayerInteraction { getPlayer().gainExp(gain, true, true); } - public int getLevel() { - return getPlayer().getLevel(); - } - @Override public void showEffect(String effect) { getPlayer().getMap().broadcastMessage(MaplePacketCreator.environmentChange(effect, 3)); @@ -338,7 +328,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { public boolean canSpawnPlayerNpc(int mapid) { MapleCharacter chr = getPlayer(); - return !ServerConstants.PLAYERNPC_AUTODEPLOY && chr.getLevel() >= chr.getMaxClassLevel() && !chr.isGM() && MaplePlayerNPC.canSpawnPlayerNpc(chr.getName(), mapid); + return !YamlConfig.config.server.PLAYERNPC_AUTODEPLOY && chr.getLevel() >= chr.getMaxClassLevel() && !chr.isGM() && MaplePlayerNPC.canSpawnPlayerNpc(chr.getName(), mapid); } public MaplePlayerNPC getPlayerNPCByScriptid(int scriptId) { @@ -400,7 +390,14 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } public void openShopNPC(int id) { - MapleShopFactory.getInstance().getShop(id).sendShop(c); + MapleShop shop = MapleShopFactory.getInstance().getShop(id); + + if (shop != null) { + shop.sendShop(c); + } else { // check for missing shopids thanks to resinate + FilePrinter.printError(FilePrinter.NPC_UNCODED, "Shop ID: " + id + " is missing from database."); + MapleShopFactory.getInstance().getShop(11000).sendShop(c); + } } public void maxMastery() { @@ -570,7 +567,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { return itemid != baseid && itemExists(baseid) ? baseid : -1; } - private int getEquippedItemid(int itemid) { + private int getEquippedCosmeticid(int itemid) { if (itemid < 30000) { return getPlayer().getFace(); } else { @@ -579,11 +576,11 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } public boolean isCosmeticEquipped(int itemid) { - return getEquippedItemid(itemid) == itemid; + return getEquippedCosmeticid(itemid) == itemid; } public boolean isUsingOldPqNpcStyle() { - return ServerConstants.USE_OLD_GMS_STYLED_PQ_NPCS && this.getPlayer().getParty() != null; + return YamlConfig.config.server.USE_OLD_GMS_STYLED_PQ_NPCS && this.getPlayer().getParty() != null; } public Object[] getAvailableMasteryBooks() { @@ -670,14 +667,13 @@ public class NPCConversationManager extends AbstractPlayerInteraction { try { final MapleMap map, mapExit; Channel cs = c.getChannelServer(); - PlayerStorage ps = cs.getPlayerStorage(); map = cs.getMapFactory().getMap(980000100 + 100 * field); mapExit = cs.getMapFactory().getMap(980000000); for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) { - final MapleCharacter mc; - mc = ps.getCharacterById(mpc.getId()); + final MapleCharacter mc = mpc.getPlayer(); if (mc != null) { + mc.setChallenged(false); mc.changeMap(map, map.getPortal(0)); mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntryLobby))); TimerManager tMan = TimerManager.getInstance(); @@ -706,18 +702,16 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } public void cancelCPQLobby() { - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.clearCpqTimer(); } } } - private void warpoutCPQLobby() { - MapleMap lobbyMap = c.getPlayer().getMap(); - MapleMap out = this.getWarpMap((lobbyMap.getId() > 980030000) ? 980000000 : 980030000); + private void warpoutCPQLobby(MapleMap lobbyMap) { + MapleMap out = lobbyMap.getChannelServer().getMapFactory().getMap((lobbyMap.getId() < 980030000) ? 980000000 : 980030000); for (MapleCharacter mc : lobbyMap.getAllPlayers()) { mc.resetCP(); mc.setTeam(-1); @@ -725,19 +719,60 @@ public class NPCConversationManager extends AbstractPlayerInteraction { mc.changeMap(out, out.getPortal(0)); } } + + private int isCPQParty(MapleMap lobby, MapleParty party) { + int cpqMinLvl, cpqMaxLvl; + + if (lobby.isCPQLobby()) { + cpqMinLvl = 30; + cpqMaxLvl = 50; + } else { + cpqMinLvl = 51; + cpqMaxLvl = 70; + } + + List partyMembers = party.getPartyMembers(); + for (MaplePartyCharacter pchr : partyMembers) { + if (pchr.getLevel() >= cpqMinLvl && pchr.getLevel() <= cpqMaxLvl) { + if (lobby.getCharacterById(pchr.getId()) == null) { + return 1; // party member detected out of area + } + } else { + return 2; // party member doesn't fit requirements + } + } + + return 0; + } + + private int canStartCPQ(MapleMap lobby, MapleParty party, MapleParty challenger) { + int ret = isCPQParty(lobby, party); + if (ret != 0) { + return ret; + } + + ret = isCPQParty(lobby, challenger); + if (ret != 0) { + return -ret; + } + + return 0; + } public void startCPQ(final MapleCharacter challenger, final int field) { try { cancelCPQLobby(); + + final MapleMap lobbyMap = getPlayer().getMap(); if (challenger != null) { if (challenger.getParty() == null) { - throw new RuntimeException("Nao existe oponente!"); + throw new RuntimeException("No opponent found!"); } - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); + for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { - mc.changeMap(getPlayer().getMap(), getPlayer().getMap().getPortal(0)); + mc.changeMap(lobbyMap, lobbyMap.getPortal(0)); TimerManager tMan = TimerManager.getInstance(); tMan.schedule(new Runnable() { @Override @@ -748,7 +783,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } } for (MaplePartyCharacter mpc : getPlayer().getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { TimerManager tMan = TimerManager.getInstance(); tMan.schedule(new Runnable() { @@ -766,21 +801,30 @@ public class NPCConversationManager extends AbstractPlayerInteraction { @Override public void run() { try { - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); for (MaplePartyCharacter mpc : getPlayer().getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); - mc.setMonsterCarnival(null); + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + mc.setMonsterCarnival(null); + } } for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); - mc.setMonsterCarnival(null); + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + mc.setMonsterCarnival(null); + } } } catch (NullPointerException npe) { - warpoutCPQLobby(); + warpoutCPQLobby(lobbyMap); return; } - new MonsterCarnival(getPlayer().getParty(), challenger.getParty(), mapid, true, (field / 100) % 10); + MapleParty lobbyParty = getPlayer().getParty(), challengerParty = challenger.getParty(); + int status = canStartCPQ(lobbyMap, lobbyParty, challengerParty); + if (status == 0) { + new MonsterCarnival(lobbyParty, challengerParty, mapid, true, (field / 100) % 10); + } else { + warpoutCPQLobby(lobbyMap); + } } }, 11000); } catch (Exception e) { @@ -791,15 +835,17 @@ public class NPCConversationManager extends AbstractPlayerInteraction { public void startCPQ2(final MapleCharacter challenger, final int field) { try { cancelCPQLobby(); + + final MapleMap lobbyMap = getPlayer().getMap(); if (challenger != null) { if (challenger.getParty() == null) { - throw new RuntimeException("Não existe oponente!"); + throw new RuntimeException("No opponent found!"); } - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); + for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { - mc.changeMap(getPlayer().getMap(), getPlayer().getMap().getPortal(0)); + mc.changeMap(lobbyMap, lobbyMap.getPortal(0)); mapClock(10); } } @@ -810,21 +856,30 @@ public class NPCConversationManager extends AbstractPlayerInteraction { @Override public void run() { try { - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); for (MaplePartyCharacter mpc : getPlayer().getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); - mc.setMonsterCarnival(null); + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + mc.setMonsterCarnival(null); + } } for (MaplePartyCharacter mpc : challenger.getParty().getMembers()) { - MapleCharacter mc = ps.getCharacterById(mpc.getId()); - mc.setMonsterCarnival(null); + MapleCharacter mc = mpc.getPlayer(); + if (mc != null) { + mc.setMonsterCarnival(null); + } } } catch (NullPointerException npe) { - warpoutCPQLobby(); + warpoutCPQLobby(lobbyMap); return; } - new MonsterCarnival(getPlayer().getParty(), challenger.getParty(), mapid, false, (field / 1000) % 10); + MapleParty lobbyParty = getPlayer().getParty(), challengerParty = challenger.getParty(); + int status = canStartCPQ(lobbyMap, lobbyParty, challengerParty); + if (status == 0) { + new MonsterCarnival(lobbyParty, challengerParty, mapid, false, (field / 1000) % 10); + } else { + warpoutCPQLobby(lobbyMap); + } } }, 10000); } catch (Exception e) { @@ -887,14 +942,13 @@ public class NPCConversationManager extends AbstractPlayerInteraction { try { final MapleMap map, mapExit; Channel cs = c.getChannelServer(); - PlayerStorage ps = c.getChannelServer().getPlayerStorage(); mapExit = cs.getMapFactory().getMap(980030000); map = cs.getMapFactory().getMap(980031000 + 1000 * field); for (MaplePartyCharacter mpc : c.getPlayer().getParty().getMembers()) { - final MapleCharacter mc; - mc = ps.getCharacterById(mpc.getId()); + final MapleCharacter mc = mpc.getPlayer(); if (mc != null) { + mc.setChallenged(false); mc.changeMap(map, map.getPortal(0)); mc.announce(MaplePacketCreator.serverNotice(6, LanguageConstants.getMessage(mc, LanguageConstants.CPQEntryLobby))); TimerManager tMan = TimerManager.getInstance(); diff --git a/src/scripting/npc/NPCScriptManager.java b/src/scripting/npc/NPCScriptManager.java index 910f49bd9b..d310efa2a8 100644 --- a/src/scripting/npc/NPCScriptManager.java +++ b/src/scripting/npc/NPCScriptManager.java @@ -29,8 +29,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.script.Invocable; import javax.script.ScriptException; + +import jdk.nashorn.api.scripting.NashornScriptEngine; import net.server.world.MaplePartyCharacter; import scripting.AbstractScriptManager; @@ -51,12 +52,12 @@ public class NPCScriptManager extends AbstractScriptManager { } private Map cms = new HashMap<>(); - private Map scripts = new HashMap<>(); + private Map scripts = new HashMap<>(); public boolean isNpcScriptAvailable(MapleClient c, String fileName) { - Invocable iv = null; + NashornScriptEngine iv = null; if (fileName != null) { - iv = getInvocable("npc/" + fileName + ".js", c); + iv = getScriptEngine("npc/" + fileName + ".js", c); } return iv != null; @@ -90,24 +91,19 @@ public class NPCScriptManager extends AbstractScriptManager { return; } cms.put(c, cm); - Invocable iv = null; - iv = getInvocable("npc/" + filename + ".js", c); + NashornScriptEngine iv = getScriptEngine("npc/" + filename + ".js", c); if (iv == null) { - c.getPlayer().dropMessage(1, npc + ""); + c.getPlayer().dropMessage(1, "NPC " + npc + " is uncoded."); cm.dispose(); return; } - engine.put("cm", cm); + iv.put("cm", cm); scripts.put(c, iv); try { iv.invokeFunction("start", chrs); } catch (final NoSuchMethodException nsme) { - try { - iv.invokeFunction("start", chrs); - } catch (final NoSuchMethodException nsma) { - nsma.printStackTrace(); - } + nsme.printStackTrace(); } } catch (final UndeclaredThrowableException ute) { @@ -127,25 +123,25 @@ public class NPCScriptManager extends AbstractScriptManager { } if (c.canClickNPC()) { cms.put(c, cm); - Invocable iv = null; + NashornScriptEngine iv = null; if (!itemScript) { if (fileName != null) { - iv = getInvocable("npc/" + fileName + ".js", c); + iv = getScriptEngine("npc/" + fileName + ".js", c); } } else { if (fileName != null) { // thanks MiLin for drafting NPC-based item scripts - iv = getInvocable("item/" + fileName + ".js", c); + iv = getScriptEngine("item/" + fileName + ".js", c); } } if (iv == null) { - iv = getInvocable("npc/" + npc + ".js", c); + iv = getScriptEngine("npc/" + npc + ".js", c); cm.resetItemScript(); } if (iv == null) { dispose(c); return false; } - engine.put(engineName, cm); + iv.put(engineName, cm); scripts.put(c, iv); c.setClickedNPC(); try { @@ -175,7 +171,7 @@ public class NPCScriptManager extends AbstractScriptManager { } public void action(MapleClient c, byte mode, byte type, int selection) { - Invocable iv = scripts.get(c); + NashornScriptEngine iv = scripts.get(c); if (iv != null) { try { c.setClickedNPC(); @@ -202,6 +198,8 @@ public class NPCScriptManager extends AbstractScriptManager { } else { resetContext(scriptFolder + "/" + cm.getNpc() + ".js", c); } + + c.getPlayer().flushDelayedUpdateQuests(); } public void dispose(MapleClient c) { diff --git a/src/scripting/portal/PortalPlayerInteraction.java b/src/scripting/portal/PortalPlayerInteraction.java index 15b770555e..ad23c6703b 100644 --- a/src/scripting/portal/PortalPlayerInteraction.java +++ b/src/scripting/portal/PortalPlayerInteraction.java @@ -27,8 +27,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import scripting.AbstractPlayerInteraction; -import server.MaplePortal; -import server.quest.MapleQuest; +import scripting.map.MapScriptManager; +import server.maps.MaplePortal; import tools.DatabaseConnection; import tools.MaplePacketCreator; @@ -44,6 +44,11 @@ public class PortalPlayerInteraction extends AbstractPlayerInteraction { public MaplePortal getPortal() { return portal; } + + public void runMapScript() { + MapScriptManager msm = MapScriptManager.getInstance(); + msm.runMapScript(c, "onUserEnter/" + portal.getScriptName(), false); + } public boolean hasLevel30Character() { PreparedStatement ps = null; diff --git a/src/scripting/portal/PortalScriptManager.java b/src/scripting/portal/PortalScriptManager.java index fb2717034e..926f5fd52e 100644 --- a/src/scripting/portal/PortalScriptManager.java +++ b/src/scripting/portal/PortalScriptManager.java @@ -22,23 +22,17 @@ along with this program. If not, see . package scripting.portal; import client.MapleClient; -import constants.ServerConstants; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; -import javax.script.Compilable; -import javax.script.Invocable; -import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import server.MaplePortal; +import jdk.nashorn.api.scripting.NashornScriptEngine; +import scripting.AbstractScriptManager; +import server.maps.MaplePortal; import tools.FilePrinter; -public class PortalScriptManager { +public class PortalScriptManager extends AbstractScriptManager { private static PortalScriptManager instance = new PortalScriptManager(); @@ -46,7 +40,7 @@ public class PortalScriptManager { return instance; } - private Map scripts = new HashMap<>(); + private Map scripts = new HashMap<>(); private ScriptEngineFactory sef; private PortalScriptManager() { @@ -54,47 +48,28 @@ public class PortalScriptManager { sef = sem.getEngineByName("javascript").getFactory(); } - private PortalScript getPortalScript(String scriptName) { - if (scripts.containsKey(scriptName)) { - return scripts.get(scriptName); + private NashornScriptEngine getPortalScript(String scriptName) { + String scriptPath = "portal/" + scriptName + ".js"; + NashornScriptEngine iv = scripts.get(scriptPath); + if (iv != null) { + return iv; } - File scriptFile = new File("scripts/portal/" + scriptName + ".js"); - if (!scriptFile.exists()) { - scripts.put(scriptName, null); + + iv = getScriptEngine(scriptPath); + if (iv == null) { return null; } - FileReader fr = null; - ScriptEngine portal = sef.getScriptEngine(); - try { - fr = new FileReader(scriptFile); - - // java 8 support here thanks to Arufonsu - if (ServerConstants.JAVA_8){ - portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); - } - - ((Compilable) portal).compile(fr).eval(); - } catch (ScriptException | IOException | UndeclaredThrowableException e) { - FilePrinter.printError(FilePrinter.PORTAL + scriptName + ".txt", e); - } finally { - if (fr != null) { - try { - fr.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - PortalScript script = ((Invocable) portal).getInterface(PortalScript.class); - scripts.put(scriptName, script); - return script; + + scripts.put(scriptPath, iv); + return iv; } public boolean executePortalScript(MaplePortal portal, MapleClient c) { try { - PortalScript script = getPortalScript(portal.getScriptName()); - if (script != null) { - return script.enter(new PortalPlayerInteraction(c, portal)); + NashornScriptEngine iv = getPortalScript(portal.getScriptName()); + if (iv != null) { + boolean couldWarp = (boolean) iv.invokeFunction("enter", new PortalPlayerInteraction(c, portal)); + return couldWarp; } } catch (UndeclaredThrowableException ute) { FilePrinter.printError(FilePrinter.PORTAL + portal.getScriptName() + ".txt", ute); diff --git a/src/scripting/quest/QuestScriptManager.java b/src/scripting/quest/QuestScriptManager.java index c599a943f2..c67de4b196 100644 --- a/src/scripting/quest/QuestScriptManager.java +++ b/src/scripting/quest/QuestScriptManager.java @@ -25,12 +25,11 @@ import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; -import javax.script.Invocable; - +import jdk.nashorn.api.scripting.NashornScriptEngine; import scripting.AbstractScriptManager; import server.quest.MapleQuest; import tools.FilePrinter; -import constants.GameConstants; +import constants.game.GameConstants; import client.MapleClient; import client.MapleQuestStatus; @@ -47,14 +46,19 @@ public class QuestScriptManager extends AbstractScriptManager { } private Map qms = new HashMap<>(); - private Map scripts = new HashMap<>(); + private Map scripts = new HashMap<>(); + + private NashornScriptEngine getQuestScriptEngine(MapleClient c, short questid) { + NashornScriptEngine iv = getScriptEngine("quest/" + questid + ".js", c); + if (iv == null && GameConstants.isMedalQuest(questid)) { + iv = getScriptEngine("quest/medalQuest.js", c); // start generic medal quest + } + + return iv; + } public void start(MapleClient c, short questid, int npc) { MapleQuest quest = MapleQuest.getInstance(questid); - if (!quest.canStartWithoutRequirements(c.getPlayer())) { - dispose(c); - return; - } try { QuestActionManager qm = new QuestActionManager(c, questid, npc, true); if (qms.containsKey(c)) { @@ -62,19 +66,20 @@ public class QuestScriptManager extends AbstractScriptManager { } if(c.canClickNPC()) { qms.put(c, qm); - Invocable iv = getInvocable("quest/" + questid + ".js", c); - if (iv == null) { - if(GameConstants.isMedalQuest(questid)) { // start generic medal quest - iv = getInvocable("quest/medalQuest.js", c); - } else { - FilePrinter.printError(FilePrinter.QUEST_UNCODED, "START Quest " + questid + " is uncoded."); - } - } - if (iv == null || QuestScriptManager.getInstance() == null) { + + if (!quest.hasScriptRequirement(false)) { // lack of scripted quest checks found thanks to Mali, Resinate qm.dispose(); return; } - engine.put("qm", qm); + + NashornScriptEngine iv = getQuestScriptEngine(c, questid); + if (iv == null) { + FilePrinter.printError(FilePrinter.QUEST_UNCODED, "START Quest " + questid + " is uncoded."); + qm.dispose(); + return; + } + + iv.put("qm", qm); scripts.put(c, iv); c.setClickedNPC(); iv.invokeFunction("start", (byte) 1, (byte) 0, 0); @@ -89,7 +94,7 @@ public class QuestScriptManager extends AbstractScriptManager { } public void start(MapleClient c, byte mode, byte type, int selection) { - Invocable iv = scripts.get(c); + NashornScriptEngine iv = scripts.get(c); if (iv != null) { try { c.setClickedNPC(); @@ -117,17 +122,20 @@ public class QuestScriptManager extends AbstractScriptManager { } if(c.canClickNPC()){ qms.put(c, qm); - Invocable iv = getInvocable("quest/" + questid + ".js", c); - if (iv == null) { - if(GameConstants.isMedalQuest(questid)) { // start generic medal quest - iv = getInvocable("quest/medalQuest.js", c); - } else { - FilePrinter.printError(FilePrinter.QUEST_UNCODED, "END Quest " + questid + " is uncoded."); - qm.dispose(); - return; - } - } - engine.put("qm", qm); + + if (!quest.hasScriptRequirement(true)) { + qm.dispose(); + return; + } + + NashornScriptEngine iv = getQuestScriptEngine(c, questid); + if (iv == null) { + FilePrinter.printError(FilePrinter.QUEST_UNCODED, "END Quest " + questid + " is uncoded."); + qm.dispose(); + return; + } + + iv.put("qm", qm); scripts.put(c, iv); c.setClickedNPC(); iv.invokeFunction("end", (byte) 1, (byte) 0, 0); @@ -142,7 +150,7 @@ public class QuestScriptManager extends AbstractScriptManager { } public void end(MapleClient c, byte mode, byte type, int selection) { - Invocable iv = scripts.get(c); + NashornScriptEngine iv = scripts.get(c); if (iv != null) { try { c.setClickedNPC(); @@ -157,11 +165,42 @@ public class QuestScriptManager extends AbstractScriptManager { } } + public void raiseOpen(MapleClient c, short questid, int npc) { + try { + QuestActionManager qm = new QuestActionManager(c, questid, npc, true); + if (qms.containsKey(c)) { + return; + } + if(c.canClickNPC()) { + qms.put(c, qm); + + NashornScriptEngine iv = getQuestScriptEngine(c, questid); + if (iv == null) { + //FilePrinter.printError(FilePrinter.QUEST_UNCODED, "RAISE Quest " + questid + " is uncoded."); + qm.dispose(); + return; + } + + iv.put("qm", qm); + scripts.put(c, iv); + c.setClickedNPC(); + iv.invokeFunction("raiseOpen"); + } + } catch (final UndeclaredThrowableException ute) { + FilePrinter.printError(FilePrinter.QUEST + questid + ".txt", ute); + dispose(c); + } catch (final Throwable t) { + FilePrinter.printError(FilePrinter.QUEST + getQM(c).getQuest() + ".txt", t); + dispose(c); + } + } + public void dispose(QuestActionManager qm, MapleClient c) { qms.remove(c); scripts.remove(c); c.getPlayer().setNpcCooldown(System.currentTimeMillis()); resetContext("quest/" + qm.getQuest() + ".js", c); + c.getPlayer().flushDelayedUpdateQuests(); } public void dispose(MapleClient c) { diff --git a/src/scripting/reactor/ReactorActionManager.java b/src/scripting/reactor/ReactorActionManager.java index 44237c5630..dd43569083 100644 --- a/src/scripting/reactor/ReactorActionManager.java +++ b/src/scripting/reactor/ReactorActionManager.java @@ -26,16 +26,17 @@ import client.MapleClient; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import java.awt.Point; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.logging.Level; import java.util.logging.Logger; -import javax.script.Invocable; import javax.script.ScriptException; +import jdk.nashorn.api.scripting.NashornScriptEngine; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.event.EventManager; @@ -57,24 +58,70 @@ import tools.MaplePacketCreator; */ public class ReactorActionManager extends AbstractPlayerInteraction { private MapleReactor reactor; - private MapleClient client; - private Invocable iv; + private NashornScriptEngine iv; private ScheduledFuture sprayTask = null; - public ReactorActionManager(MapleClient c, MapleReactor reactor, Invocable iv) { + public ReactorActionManager(MapleClient c, MapleReactor reactor, NashornScriptEngine iv) { super(c); this.reactor = reactor; - this.client = c; this.iv = iv; } public void hitReactor() { - reactor.hitReactor(client); + reactor.hitReactor(c); } public void destroyNpc(int npcId) { reactor.getMap().destroyNPC(npcId); } + + private static void sortDropEntries(List from, List item, List visibleQuest, List otherQuest, MapleCharacter chr) { + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); + + for(ReactorDropEntry mde : from) { + if(!ii.isQuestItem(mde.itemId)) { + item.add(mde); + } else { + if(chr.needQuestItem(mde.questid, mde.itemId)) { + visibleQuest.add(mde); + } else { + otherQuest.add(mde); + } + } + } + } + + private static List assembleReactorDropEntries(MapleCharacter chr, List items) { + final List dropEntry = new ArrayList<>(); + final List visibleQuestEntry = new ArrayList<>(); + final List otherQuestEntry = new ArrayList<>(); + sortDropEntries(items, dropEntry, visibleQuestEntry, otherQuestEntry, chr); + + Collections.shuffle(dropEntry); + Collections.shuffle(visibleQuestEntry); + Collections.shuffle(otherQuestEntry); + + items.clear(); + items.addAll(dropEntry); + items.addAll(visibleQuestEntry); + items.addAll(otherQuestEntry); + + List items1 = new ArrayList<>(items.size()); + List items2 = new ArrayList<>(items.size() / 2); + + for (int i = 0; i < items.size(); i++) { + if (i % 2 == 0) { + items1.add(items.get(i)); + } else { + items2.add(items.get(i)); + } + } + + Collections.reverse(items1); + items1.addAll(items2); + + return items1; + } public void sprayItems() { sprayItems(false, 0, 0, 0, 0); @@ -105,14 +152,14 @@ public class ReactorActionManager extends AbstractPlayerInteraction { } public void dropItems(int posX, int posY, boolean meso, int mesoChance, int minMeso, int maxMeso, int minItems) { - dropItems(false, posX, posY, meso, mesoChance, minMeso, maxMeso, minItems); + dropItems(true, posX, posY, meso, mesoChance, minMeso, maxMeso, minItems); // all reactors actually drop items sequentially... thanks inhyuk for pointing this out! } public void dropItems(boolean delayed, int posX, int posY, boolean meso, int mesoChance, final int minMeso, final int maxMeso, int minItems) { - if(c.getPlayer() == null) return; - - List items = generateDropList(getDropChances(), c.getPlayer().getDropRate(), meso, mesoChance, minItems); + MapleCharacter chr = c.getPlayer(); + if(chr == null) return; + List items = assembleReactorDropEntries(chr, generateDropList(getDropChances(), chr.getDropRate(), meso, mesoChance, minItems)); if(items.size() % 2 == 0) posX -= 12; final Point dropPos = new Point(posX, posY); @@ -127,8 +174,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction { if (d.itemId == 0) { int range = maxMeso - minMeso; int displayDrop = (int) (Math.random() * range) + minMeso; - int mesoDrop = (displayDrop * client.getWorldServer().getMesoRate()); - reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, client.getPlayer(), false, (byte) 2); + int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate()); + reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2); } else { Item drop; @@ -142,10 +189,9 @@ public class ReactorActionManager extends AbstractPlayerInteraction { } } } else { - final MapleCharacter chr = client.getPlayer(); final MapleReactor r = reactor; final List dropItems = items; - final int worldMesoRate = client.getWorldServer().getMesoRate(); + final int worldMesoRate = c.getWorldServer().getMesoRate(); dropPos.x -= (12 * items.size()); @@ -178,7 +224,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction { dropPos.x += 25; } - }, 100); + }, 200); } } @@ -186,38 +232,22 @@ public class ReactorActionManager extends AbstractPlayerInteraction { return ReactorScriptManager.getInstance().getDrops(reactor.getId()); } - private static List generateDropList(List drops, int dropRate, boolean meso, int mesoChance, int minItems) { - MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - + private List generateDropList(List drops, int dropRate, boolean meso, int mesoChance, int minItems) { List items = new ArrayList<>(); - List questItems = new ArrayList<>(); - int numItems = 0; - if (meso && Math.random() < (1 / (double) mesoChance)) { items.add(new ReactorDropEntry(0, mesoChance, -1)); } for(ReactorDropEntry mde : drops) { if (Math.random() < (dropRate / (double) mde.chance)) { - if(!ii.isQuestItem(mde.itemId)) { - items.add(mde); - } else { - questItems.add(mde); - } - - numItems++; + items.add(mde); } } - while (numItems < minItems) { + while (items.size() < minItems) { items.add(new ReactorDropEntry(0, mesoChance, -1)); - numItems++; } - java.util.Collections.shuffle(items); - java.util.Collections.shuffle(questItems); - - items.addAll(questItems); return items; } @@ -226,7 +256,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction { } public void createMapMonitor(int mapId, String portal) { - new MapMonitor(client.getChannelServer().getMapFactory().getMap(mapId), portal); + new MapMonitor(c.getChannelServer().getMapFactory().getMap(mapId), portal); } public void spawnMonster(int id, int qty) { @@ -258,7 +288,7 @@ public class ReactorActionManager extends AbstractPlayerInteraction { } public void hitMonsterWithReactor(int id, int hitsToKill) { // until someone comes with a better solution, why not? - int customTime = ServerConstants.MOB_REACTOR_REFRESH_TIME; + int customTime = YamlConfig.config.server.MOB_REACTOR_REFRESH_TIME; if(customTime > 0) { reactor.setDelay(customTime); } diff --git a/src/scripting/reactor/ReactorScriptManager.java b/src/scripting/reactor/ReactorScriptManager.java index 45e61feed0..7fcb1acacc 100644 --- a/src/scripting/reactor/ReactorScriptManager.java +++ b/src/scripting/reactor/ReactorScriptManager.java @@ -29,8 +29,9 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import javax.script.Invocable; import javax.script.ScriptException; + +import jdk.nashorn.api.scripting.NashornScriptEngine; import scripting.AbstractScriptManager; import server.maps.MapleReactor; import server.maps.ReactorDropEntry; @@ -52,11 +53,11 @@ public class ReactorScriptManager extends AbstractScriptManager { public void onHit(MapleClient c, MapleReactor reactor) { try { - Invocable iv = getInvocable("reactor/" + reactor.getId() + ".js", c); + NashornScriptEngine iv = getScriptEngine("reactor/" + reactor.getId() + ".js", c); if (iv == null) return; ReactorActionManager rm = new ReactorActionManager(c, reactor, iv); - engine.put("rm", rm); + iv.put("rm", rm); iv.invokeFunction("hit"); } catch (final NoSuchMethodException e) {} //do nothing, hit is OPTIONAL @@ -67,11 +68,11 @@ public class ReactorScriptManager extends AbstractScriptManager { public void act(MapleClient c, MapleReactor reactor) { try { - Invocable iv = getInvocable("reactor/" + reactor.getId() + ".js", c); + NashornScriptEngine iv = getScriptEngine("reactor/" + reactor.getId() + ".js", c); if (iv == null) return; ReactorActionManager rm = new ReactorActionManager(c, reactor, iv); - engine.put("rm", rm); + iv.put("rm", rm); iv.invokeFunction("act"); } catch (final ScriptException | NoSuchMethodException | NullPointerException e) { FilePrinter.printError(FilePrinter.REACTOR + reactor.getId() + ".txt", e); @@ -114,13 +115,13 @@ public class ReactorScriptManager extends AbstractScriptManager { touching(c, reactor, false); } - public synchronized void touching(MapleClient c, MapleReactor reactor, boolean touching) { + private void touching(MapleClient c, MapleReactor reactor, boolean touching) { try { - Invocable iv = getInvocable("reactor/" + reactor.getId() + ".js", c); + NashornScriptEngine iv = getScriptEngine("reactor/" + reactor.getId() + ".js", c); if (iv == null) return; ReactorActionManager rm = new ReactorActionManager(c, reactor, iv); - engine.put("rm", rm); + iv.put("rm", rm); if (touching) { iv.invokeFunction("touch"); } else { diff --git a/src/server/CashShop.java b/src/server/CashShop.java index ae07b48907..19b99e9b03 100644 --- a/src/server/CashShop.java +++ b/src/server/CashShop.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.Lock; +import config.YamlConfig; import net.server.Server; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -47,8 +48,8 @@ import client.inventory.Item; import client.inventory.ItemFactory; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; -import constants.ItemConstants; -import constants.ServerConstants; +import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import java.util.Collections; import net.server.audit.locks.MonitoredLockType; @@ -157,7 +158,7 @@ public class CashShop { private static final Map items = new HashMap<>(); private static final Map> packages = new HashMap<>(); private static final List specialcashitems = new ArrayList<>(); - private static final List randomitemids = new ArrayList<>(); + private static final List randomitemsns = new ArrayList<>(); static { MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz")); @@ -184,7 +185,7 @@ public class CashShop { for(Entry e : items.entrySet()) { if(e.getValue().isOnSale()) { - randomitemids.add(e.getKey()); + randomitemsns.add(e.getKey()); } } @@ -212,10 +213,10 @@ public class CashShop { } public static CashItem getRandomCashItem() { - if(randomitemids.isEmpty()) return null; + if(randomitemsns.isEmpty()) return null; - int rnd = (int)(Math.random() * randomitemids.size()); - return items.get(randomitemids.get(rnd)); + int rnd = (int)(Math.random() * randomitemsns.size()); + return items.get(randomitemsns.get(rnd)); } public static CashItem getItem(int sn) { @@ -278,7 +279,7 @@ public class CashShop { this.accountId = accountId; this.characterId = characterId; - if (!ServerConstants.USE_JOINT_CASHSHOP_INVENTORY) { + if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) { if (jobType == 0) { factory = ItemFactory.CASH_EXPLORER; } else if (jobType == 1) { @@ -358,7 +359,7 @@ public class CashShop { public void gainCash(int type, CashItem buyItem, int world) { gainCash(type, -buyItem.getPrice()); - if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) Server.getInstance().getWorld(world).addCashItemBought(buyItem.getSN()); + if(!YamlConfig.config.server.USE_ENFORCE_ITEM_SUGGESTION) Server.getInstance().getWorld(world).addCashItemBought(buyItem.getSN()); } public boolean isOpened() { diff --git a/src/server/DueyPackage.java b/src/server/DueyPackage.java index 0aba4868f8..1b95a0250c 100644 --- a/src/server/DueyPackage.java +++ b/src/server/DueyPackage.java @@ -23,15 +23,14 @@ package server; import client.inventory.Item; import java.util.Calendar; +import java.sql.Timestamp; public class DueyPackage { private String sender = null; private Item item = null; private int mesos = 0; - private String message = ""; - private int day; - private int month; - private int year; + private String message = null; + private Calendar timestamp; private int packageId = 0; public DueyPackage(int pId, Item item) { @@ -76,14 +75,37 @@ public class DueyPackage { } public long sentTimeInMilliseconds() { - Calendar cal = Calendar.getInstance(); - cal.set(year, month, day); - return cal.getTimeInMillis(); + Calendar ts = timestamp; + if (ts != null) { + Calendar cal = Calendar.getInstance(); + cal.setTime(ts.getTime()); + cal.add(Calendar.MONTH, 1); // duey representation is in an array of months. + + return cal.getTimeInMillis(); + } else { + return 0; + } + } + + public boolean isDeliveringTime() { + Calendar ts = timestamp; + if (ts != null) { + return ts.getTimeInMillis() >= System.currentTimeMillis(); + } else { + return false; + } } - public void setSentTime(String sentTime) { - day = Integer.parseInt(sentTime.substring(0, 2)); - month = Integer.parseInt(sentTime.substring(3, 5)); - year = Integer.parseInt(sentTime.substring(6, 10)); + public void setSentTime(Timestamp ts, boolean quick) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(ts.getTime()); + + if (quick) { + if (System.currentTimeMillis() - ts.getTime() < 24 * 60 * 60 * 1000) { // thanks inhyuk for noticing quick delivery packages unavailable to retrieve from the get-go + cal.add(Calendar.DATE, -1); + } + } + + this.timestamp = cal; } } diff --git a/src/server/MakerItemFactory.java b/src/server/MakerItemFactory.java index 99c6eda687..25591b71f9 100644 --- a/src/server/MakerItemFactory.java +++ b/src/server/MakerItemFactory.java @@ -21,8 +21,8 @@ */ package server; -import constants.EquipType; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.EquipType; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -57,19 +57,24 @@ public class MakerItemFactory { return makerEntry; } - public static MakerItemCreateEntry generateLeftoverCrystalEntry(int fromLeftoverid) { - MakerItemCreateEntry ret = new MakerItemCreateEntry(0, 0, 1, 1); + public static MakerItemCreateEntry generateLeftoverCrystalEntry(int fromLeftoverid, int crystalId) { + MakerItemCreateEntry ret = new MakerItemCreateEntry(0, 0, 1); ret.addReqItem(fromLeftoverid, 100); + ret.addGainItem(crystalId, 1); return ret; } - public static MakerItemCreateEntry generateDisassemblyCrystalEntry(int cost, int crystalGain) { // equipment at specific position already taken - MakerItemCreateEntry ret = new MakerItemCreateEntry(cost, 0, 1, crystalGain); + public static MakerItemCreateEntry generateDisassemblyCrystalEntry(int fromEquipid, int cost, List> gains) { // equipment at specific position already taken + MakerItemCreateEntry ret = new MakerItemCreateEntry(cost, 0, 1); + ret.addReqItem(fromEquipid, 1); + for (Pair p : gains) { + ret.addGainItem(p.getLeft(), p.getRight()); + } return ret; } private static double getMakerStimulantFee(int itemid) { - if(ServerConstants.USE_MAKER_FEE_HEURISTICS) { + if(YamlConfig.config.server.USE_MAKER_FEE_HEURISTICS) { EquipType et = EquipType.getEquipTypeById(itemid); int eqpLevel = ii.getEquipLevelReq(itemid); @@ -104,7 +109,7 @@ public class MakerItemFactory { } private static double getMakerReagentFee(int itemid, int reagentLevel) { - if(ServerConstants.USE_MAKER_FEE_HEURISTICS) { + if(YamlConfig.config.server.USE_MAKER_FEE_HEURISTICS) { EquipType et = EquipType.getEquipTypeById(itemid); int eqpLevel = ii.getEquipLevelReq(itemid); @@ -143,33 +148,35 @@ public class MakerItemFactory { private double cost; private int reqCost; private List> reqItems = new ArrayList<>(); // itemId / amount - private int toGive; + private List> gainItems = new ArrayList<>(); // itemId / amount - public MakerItemCreateEntry(int cost, int reqLevel, int reqMakerLevel, int toGive) { + public MakerItemCreateEntry(int cost, int reqLevel, int reqMakerLevel) { this.cost = cost; this.reqLevel = reqLevel; this.reqMakerLevel = reqMakerLevel; - this.toGive = toGive; } public MakerItemCreateEntry(MakerItemCreateEntry mi) { this.cost = mi.cost; this.reqLevel = mi.reqLevel; this.reqMakerLevel = mi.reqMakerLevel; - this.toGive = mi.toGive; for(Pair p : mi.reqItems) { reqItems.add(p); } - } - - public int getRewardAmount() { - return toGive; + + for(Pair p : mi.gainItems) { + gainItems.add(p); + } } public List> getReqItems() { return reqItems; } + + public List> getGainItems() { + return gainItems; + } public int getReqLevel() { return reqLevel; @@ -191,6 +198,10 @@ public class MakerItemFactory { reqItems.add(new Pair<>(itemId, amount)); } + protected void addGainItem(int itemId, int amount) { + gainItems.add(new Pair<>(itemId, amount)); + } + public void trimCost() { reqCost = (int) (cost / 1000); reqCost *= 1000; diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index 6c2084723a..f8d32d7ef1 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -36,6 +36,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.HashSet; +import config.YamlConfig; import net.server.Server; import provider.MapleData; import provider.MapleDataDirectoryEntry; @@ -59,9 +60,9 @@ import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.MapleWeaponType; -import constants.ServerConstants; -import constants.EquipSlot; -import constants.ItemConstants; +import constants.net.ServerConstants; +import constants.inventory.EquipSlot; +import constants.inventory.ItemConstants; import constants.skills.Assassin; import constants.skills.Gunslinger; import constants.skills.NightWalker; @@ -100,12 +101,14 @@ public class MapleItemInformationProvider { protected Map equipLevelInfoCache = new HashMap<>(); protected Map equipLevelReqCache = new HashMap<>(); protected Map equipMaxLevelCache = new HashMap<>(); + protected Map> scrollReqsCache = new HashMap<>(); protected Map wholePriceCache = new HashMap<>(); protected Map unitPriceCache = new HashMap<>(); protected Map projectileWatkCache = new HashMap<>(); protected Map nameCache = new HashMap<>(); protected Map descCache = new HashMap<>(); protected Map msgCache = new HashMap<>(); + protected Map accountItemRestrictionCache = new HashMap<>(); protected Map dropRestrictionCache = new HashMap<>(); protected Map pickupRestrictionCache = new HashMap<>(); protected Map getMesoCache = new HashMap<>(); @@ -592,15 +595,20 @@ public class MapleItemInformationProvider { } public List getScrollReqs(int itemId) { + if (scrollReqsCache.containsKey(itemId)) { + return scrollReqsCache.get(itemId); + } + List ret = new ArrayList<>(); MapleData data = getItemData(itemId); data = data.getChildByPath("req"); - if (data == null) { - return ret; - } - for (MapleData req : data.getChildren()) { - ret.add(MapleDataTool.getInt(req)); + if (data != null) { + for (MapleData req : data.getChildren()) { + ret.add(MapleDataTool.getInt(req)); + } } + + scrollReqsCache.put(itemId, ret); return ret; } @@ -618,7 +626,7 @@ public class MapleItemInformationProvider { } public static boolean rollSuccessChance(double propPercent) { - return Math.random() >= testYourLuck(propPercent / 100.0, ServerConstants.SCROLL_CHANCE_RATE); + return Math.random() >= testYourLuck(propPercent / 100.0, YamlConfig.config.server.SCROLL_CHANCE_ROLLS); } private static short getMaximumShortMaxIfOverflow(int value1, int value2) { @@ -639,72 +647,72 @@ public class MapleItemInformationProvider { if(!option) { if (nEquip.getStr() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setStr(getMaximumShortMaxIfOverflow(nEquip.getStr(), (nEquip.getStr() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setStr(getMaximumShortMaxIfOverflow(nEquip.getStr(), (nEquip.getStr() + chscrollRandomizedStat(range)))); else nEquip.setStr(getMaximumShortMaxIfOverflow(0, (nEquip.getStr() + chscrollRandomizedStat(range)))); } if (nEquip.getDex() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setDex(getMaximumShortMaxIfOverflow(nEquip.getDex(), (nEquip.getDex() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setDex(getMaximumShortMaxIfOverflow(nEquip.getDex(), (nEquip.getDex() + chscrollRandomizedStat(range)))); else nEquip.setDex(getMaximumShortMaxIfOverflow(0, (nEquip.getDex() + chscrollRandomizedStat(range)))); } if (nEquip.getInt() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setInt(getMaximumShortMaxIfOverflow(nEquip.getInt(), (nEquip.getInt() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setInt(getMaximumShortMaxIfOverflow(nEquip.getInt(), (nEquip.getInt() + chscrollRandomizedStat(range)))); else nEquip.setInt(getMaximumShortMaxIfOverflow(0, (nEquip.getInt() + chscrollRandomizedStat(range)))); } if (nEquip.getLuk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setLuk(getMaximumShortMaxIfOverflow(nEquip.getLuk(), (nEquip.getLuk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setLuk(getMaximumShortMaxIfOverflow(nEquip.getLuk(), (nEquip.getLuk() + chscrollRandomizedStat(range)))); else nEquip.setLuk(getMaximumShortMaxIfOverflow(0, (nEquip.getLuk() + chscrollRandomizedStat(range)))); } if (nEquip.getAcc() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setAcc(getMaximumShortMaxIfOverflow(nEquip.getAcc(), (nEquip.getAcc() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setAcc(getMaximumShortMaxIfOverflow(nEquip.getAcc(), (nEquip.getAcc() + chscrollRandomizedStat(range)))); else nEquip.setAcc(getMaximumShortMaxIfOverflow(0, (nEquip.getAcc() + chscrollRandomizedStat(range)))); } if (nEquip.getAvoid() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setAvoid(getMaximumShortMaxIfOverflow(nEquip.getAvoid(), (nEquip.getAvoid() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setAvoid(getMaximumShortMaxIfOverflow(nEquip.getAvoid(), (nEquip.getAvoid() + chscrollRandomizedStat(range)))); else nEquip.setAvoid(getMaximumShortMaxIfOverflow(0, (nEquip.getAvoid() + chscrollRandomizedStat(range)))); } } else { if (nEquip.getWatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setWatk(getMaximumShortMaxIfOverflow(nEquip.getWatk(), (nEquip.getWatk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setWatk(getMaximumShortMaxIfOverflow(nEquip.getWatk(), (nEquip.getWatk() + chscrollRandomizedStat(range)))); else nEquip.setWatk(getMaximumShortMaxIfOverflow(0, (nEquip.getWatk() + chscrollRandomizedStat(range)))); } if (nEquip.getWdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setWdef(getMaximumShortMaxIfOverflow(nEquip.getWdef(), (nEquip.getWdef() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setWdef(getMaximumShortMaxIfOverflow(nEquip.getWdef(), (nEquip.getWdef() + chscrollRandomizedStat(range)))); else nEquip.setWdef(getMaximumShortMaxIfOverflow(0, (nEquip.getWdef() + chscrollRandomizedStat(range)))); } if (nEquip.getMatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMatk(getMaximumShortMaxIfOverflow(nEquip.getMatk(), (nEquip.getMatk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMatk(getMaximumShortMaxIfOverflow(nEquip.getMatk(), (nEquip.getMatk() + chscrollRandomizedStat(range)))); else nEquip.setMatk(getMaximumShortMaxIfOverflow(0, (nEquip.getMatk() + chscrollRandomizedStat(range)))); } if (nEquip.getMdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMdef(getMaximumShortMaxIfOverflow(nEquip.getMdef(), (nEquip.getMdef() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMdef(getMaximumShortMaxIfOverflow(nEquip.getMdef(), (nEquip.getMdef() + chscrollRandomizedStat(range)))); else nEquip.setMdef(getMaximumShortMaxIfOverflow(0, (nEquip.getMdef() + chscrollRandomizedStat(range)))); } if (nEquip.getSpeed() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setSpeed(getMaximumShortMaxIfOverflow(nEquip.getSpeed(), (nEquip.getSpeed() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setSpeed(getMaximumShortMaxIfOverflow(nEquip.getSpeed(), (nEquip.getSpeed() + chscrollRandomizedStat(range)))); else nEquip.setSpeed(getMaximumShortMaxIfOverflow(0, (nEquip.getSpeed() + chscrollRandomizedStat(range)))); } if (nEquip.getJump() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setJump(getMaximumShortMaxIfOverflow(nEquip.getJump(), (nEquip.getJump() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setJump(getMaximumShortMaxIfOverflow(nEquip.getJump(), (nEquip.getJump() + chscrollRandomizedStat(range)))); else nEquip.setJump(getMaximumShortMaxIfOverflow(0, (nEquip.getJump() + chscrollRandomizedStat(range)))); } if (nEquip.getHp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setHp(getMaximumShortMaxIfOverflow(nEquip.getHp(), (nEquip.getHp() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setHp(getMaximumShortMaxIfOverflow(nEquip.getHp(), (nEquip.getHp() + chscrollRandomizedStat(range)))); else nEquip.setHp(getMaximumShortMaxIfOverflow(0, (nEquip.getHp() + chscrollRandomizedStat(range)))); } if (nEquip.getMp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMp(getMaximumShortMaxIfOverflow(nEquip.getMp(), (nEquip.getMp() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMp(getMaximumShortMaxIfOverflow(nEquip.getMp(), (nEquip.getMp() + chscrollRandomizedStat(range)))); else nEquip.setMp(getMaximumShortMaxIfOverflow(0, (nEquip.getMp() + chscrollRandomizedStat(range)))); } } } private void scrollEquipWithChaos(Equip nEquip, int range) { - if(ServerConstants.CHSCROLL_STAT_RATE > 0) { + if(YamlConfig.config.server.CHSCROLL_STAT_RATE > 0) { int temp; short curStr, curDex, curInt, curLuk, curWatk, curWdef, curMatk, curMdef, curAcc, curAvoid, curSpeed, curJump, curHp, curMp; - if(ServerConstants.USE_ENHANCED_CHSCROLL) { + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) { curStr = nEquip.getStr(); curDex = nEquip.getDex(); curInt = nEquip.getInt(); @@ -736,100 +744,100 @@ public class MapleItemInformationProvider { curMp = Short.MIN_VALUE; } - for(int i = 0; i < ServerConstants.CHSCROLL_STAT_RATE; i++) { + for(int i = 0; i < YamlConfig.config.server.CHSCROLL_STAT_RATE; i++) { if (nEquip.getStr() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curStr + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curStr + chscrollRandomizedStat(range); else temp = nEquip.getStr() + chscrollRandomizedStat(range); curStr = getMaximumShortMaxIfOverflow(temp, curStr); } if (nEquip.getDex() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curDex + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curDex + chscrollRandomizedStat(range); else temp = nEquip.getDex() + chscrollRandomizedStat(range); curDex = getMaximumShortMaxIfOverflow(temp, curDex); } if (nEquip.getInt() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curInt + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curInt + chscrollRandomizedStat(range); else temp = nEquip.getInt() + chscrollRandomizedStat(range); curInt = getMaximumShortMaxIfOverflow(temp, curInt); } if (nEquip.getLuk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curLuk + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curLuk + chscrollRandomizedStat(range); else temp = nEquip.getLuk() + chscrollRandomizedStat(range); curLuk = getMaximumShortMaxIfOverflow(temp, curLuk); } if (nEquip.getWatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curWatk + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curWatk + chscrollRandomizedStat(range); else temp = nEquip.getWatk() + chscrollRandomizedStat(range); curWatk = getMaximumShortMaxIfOverflow(temp, curWatk); } if (nEquip.getWdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curWdef + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curWdef + chscrollRandomizedStat(range); else temp = nEquip.getWdef() + chscrollRandomizedStat(range); curWdef = getMaximumShortMaxIfOverflow(temp, curWdef); } if (nEquip.getMatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curMatk + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curMatk + chscrollRandomizedStat(range); else temp = nEquip.getMatk() + chscrollRandomizedStat(range); curMatk = getMaximumShortMaxIfOverflow(temp, curMatk); } if (nEquip.getMdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curMdef + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curMdef + chscrollRandomizedStat(range); else temp = nEquip.getMdef() + chscrollRandomizedStat(range); curMdef = getMaximumShortMaxIfOverflow(temp, curMdef); } if (nEquip.getAcc() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curAcc + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curAcc + chscrollRandomizedStat(range); else temp = nEquip.getAcc() + chscrollRandomizedStat(range); curAcc = getMaximumShortMaxIfOverflow(temp, curAcc); } if (nEquip.getAvoid() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curAvoid + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curAvoid + chscrollRandomizedStat(range); else temp = nEquip.getAvoid() + chscrollRandomizedStat(range); curAvoid = getMaximumShortMaxIfOverflow(temp, curAvoid); } if (nEquip.getSpeed() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curSpeed + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curSpeed + chscrollRandomizedStat(range); else temp = nEquip.getSpeed() + chscrollRandomizedStat(range); curSpeed = getMaximumShortMaxIfOverflow(temp, curSpeed); } if (nEquip.getJump() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curJump + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curJump + chscrollRandomizedStat(range); else temp = nEquip.getJump() + chscrollRandomizedStat(range); curJump = getMaximumShortMaxIfOverflow(temp, curJump); } if (nEquip.getHp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curHp + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curHp + chscrollRandomizedStat(range); else temp = nEquip.getHp() + chscrollRandomizedStat(range); curHp = getMaximumShortMaxIfOverflow(temp, curHp); } if (nEquip.getMp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) temp = curMp + chscrollRandomizedStat(range); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) temp = curMp + chscrollRandomizedStat(range); else temp = nEquip.getMp() + chscrollRandomizedStat(range); curMp = getMaximumShortMaxIfOverflow(temp, curMp); @@ -854,59 +862,59 @@ public class MapleItemInformationProvider { else { if (nEquip.getStr() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setStr(getMaximumShortMaxIfOverflow(nEquip.getStr(), (nEquip.getStr() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setStr(getMaximumShortMaxIfOverflow(nEquip.getStr(), (nEquip.getStr() + chscrollRandomizedStat(range)))); else nEquip.setStr(getMaximumShortMaxIfOverflow(0, (nEquip.getStr() + chscrollRandomizedStat(range)))); } if (nEquip.getDex() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setDex(getMaximumShortMaxIfOverflow(nEquip.getDex(), (nEquip.getDex() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setDex(getMaximumShortMaxIfOverflow(nEquip.getDex(), (nEquip.getDex() + chscrollRandomizedStat(range)))); else nEquip.setDex(getMaximumShortMaxIfOverflow(0, (nEquip.getDex() + chscrollRandomizedStat(range)))); } if (nEquip.getInt() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setInt(getMaximumShortMaxIfOverflow(nEquip.getInt(), (nEquip.getInt() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setInt(getMaximumShortMaxIfOverflow(nEquip.getInt(), (nEquip.getInt() + chscrollRandomizedStat(range)))); else nEquip.setInt(getMaximumShortMaxIfOverflow(0, (nEquip.getInt() + chscrollRandomizedStat(range)))); } if (nEquip.getLuk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setLuk(getMaximumShortMaxIfOverflow(nEquip.getLuk(), (nEquip.getLuk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setLuk(getMaximumShortMaxIfOverflow(nEquip.getLuk(), (nEquip.getLuk() + chscrollRandomizedStat(range)))); else nEquip.setLuk(getMaximumShortMaxIfOverflow(0, (nEquip.getLuk() + chscrollRandomizedStat(range)))); } if (nEquip.getWatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setWatk(getMaximumShortMaxIfOverflow(nEquip.getWatk(), (nEquip.getWatk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setWatk(getMaximumShortMaxIfOverflow(nEquip.getWatk(), (nEquip.getWatk() + chscrollRandomizedStat(range)))); else nEquip.setWatk(getMaximumShortMaxIfOverflow(0, (nEquip.getWatk() + chscrollRandomizedStat(range)))); } if (nEquip.getWdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setWdef(getMaximumShortMaxIfOverflow(nEquip.getWdef(), (nEquip.getWdef() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setWdef(getMaximumShortMaxIfOverflow(nEquip.getWdef(), (nEquip.getWdef() + chscrollRandomizedStat(range)))); else nEquip.setWdef(getMaximumShortMaxIfOverflow(0, (nEquip.getWdef() + chscrollRandomizedStat(range)))); } if (nEquip.getMatk() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMatk(getMaximumShortMaxIfOverflow(nEquip.getMatk(), (nEquip.getMatk() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMatk(getMaximumShortMaxIfOverflow(nEquip.getMatk(), (nEquip.getMatk() + chscrollRandomizedStat(range)))); else nEquip.setMatk(getMaximumShortMaxIfOverflow(0, (nEquip.getMatk() + chscrollRandomizedStat(range)))); } if (nEquip.getMdef() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMdef(getMaximumShortMaxIfOverflow(nEquip.getMdef(), (nEquip.getMdef() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMdef(getMaximumShortMaxIfOverflow(nEquip.getMdef(), (nEquip.getMdef() + chscrollRandomizedStat(range)))); else nEquip.setMdef(getMaximumShortMaxIfOverflow(0, (nEquip.getMdef() + chscrollRandomizedStat(range)))); } if (nEquip.getAcc() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setAcc(getMaximumShortMaxIfOverflow(nEquip.getAcc(), (nEquip.getAcc() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setAcc(getMaximumShortMaxIfOverflow(nEquip.getAcc(), (nEquip.getAcc() + chscrollRandomizedStat(range)))); else nEquip.setAcc(getMaximumShortMaxIfOverflow(0, (nEquip.getAcc() + chscrollRandomizedStat(range)))); } if (nEquip.getAvoid() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setAvoid(getMaximumShortMaxIfOverflow(nEquip.getAvoid(), (nEquip.getAvoid() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setAvoid(getMaximumShortMaxIfOverflow(nEquip.getAvoid(), (nEquip.getAvoid() + chscrollRandomizedStat(range)))); else nEquip.setAvoid(getMaximumShortMaxIfOverflow(0, (nEquip.getAvoid() + chscrollRandomizedStat(range)))); } if (nEquip.getSpeed() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setSpeed(getMaximumShortMaxIfOverflow(nEquip.getSpeed(), (nEquip.getSpeed() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setSpeed(getMaximumShortMaxIfOverflow(nEquip.getSpeed(), (nEquip.getSpeed() + chscrollRandomizedStat(range)))); else nEquip.setSpeed(getMaximumShortMaxIfOverflow(0, (nEquip.getSpeed() + chscrollRandomizedStat(range)))); } if (nEquip.getJump() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setJump(getMaximumShortMaxIfOverflow(nEquip.getJump(), (nEquip.getJump() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setJump(getMaximumShortMaxIfOverflow(nEquip.getJump(), (nEquip.getJump() + chscrollRandomizedStat(range)))); else nEquip.setJump(getMaximumShortMaxIfOverflow(0, (nEquip.getJump() + chscrollRandomizedStat(range)))); } if (nEquip.getHp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setHp(getMaximumShortMaxIfOverflow(nEquip.getHp(), (nEquip.getHp() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setHp(getMaximumShortMaxIfOverflow(nEquip.getHp(), (nEquip.getHp() + chscrollRandomizedStat(range)))); else nEquip.setHp(getMaximumShortMaxIfOverflow(0, (nEquip.getHp() + chscrollRandomizedStat(range)))); } if (nEquip.getMp() > 0) { - if(ServerConstants.USE_ENHANCED_CHSCROLL) nEquip.setMp(getMaximumShortMaxIfOverflow(nEquip.getMp(), (nEquip.getMp() + chscrollRandomizedStat(range)))); + if(YamlConfig.config.server.USE_ENHANCED_CHSCROLL) nEquip.setMp(getMaximumShortMaxIfOverflow(nEquip.getMp(), (nEquip.getMp() + chscrollRandomizedStat(range)))); else nEquip.setMp(getMaximumShortMaxIfOverflow(0, (nEquip.getMp() + chscrollRandomizedStat(range)))); } } @@ -918,11 +926,11 @@ public class MapleItemInformationProvider { */ public boolean canUseCleanSlate(Equip nEquip) { Map eqstats = this.getEquipStats(nEquip.getItemId()); - return ServerConstants.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious()); + return YamlConfig.config.server.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious()); // issue with clean slate found thanks to Masterrulax, vicious added in the check thanks to Crypter (CrypterDEV) } public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) { - boolean assertGM = (isGM && ServerConstants.USE_PERFECT_GM_SCROLL); + boolean assertGM = (isGM && YamlConfig.config.server.USE_PERFECT_GM_SCROLL); if (equip instanceof Equip) { Equip nEquip = (Equip) equip; @@ -969,7 +977,7 @@ public class MapleItemInformationProvider { case 2049100: case 2049101: case 2049102: - scrollEquipWithChaos(nEquip, ServerConstants.CHSCROLL_STAT_RANGE); + scrollEquipWithChaos(nEquip, YamlConfig.config.server.CHSCROLL_STAT_RANGE); break; default: @@ -983,7 +991,7 @@ public class MapleItemInformationProvider { nEquip.setLevel((byte) (nEquip.getLevel() + 1)); } } else { - if (!ServerConstants.USE_PERFECT_SCROLLING && !usingWhiteScroll && !ItemConstants.isCleanSlate(scrollId) && !assertGM && !ItemConstants.isModifierScroll(scrollId)) { + if (!YamlConfig.config.server.USE_PERFECT_SCROLLING && !usingWhiteScroll && !ItemConstants.isCleanSlate(scrollId) && !assertGM && !ItemConstants.isModifierScroll(scrollId)) { nEquip.setUpgradeSlots((byte) (nEquip.getUpgradeSlots() - 1)); } if (Randomizer.nextInt(100) < stats.get("cursed")) { @@ -1050,7 +1058,7 @@ public class MapleItemInformationProvider { return getEquipById(equipId, -1); } - Item getEquipById(int equipId, int ringId) { + private Item getEquipById(int equipId, int ringId) { Equip nEquip; nEquip = new Equip(equipId, (byte) 0, ringId); nEquip.setQuantity((short) 1); @@ -1088,11 +1096,11 @@ public class MapleItemInformationProvider { } else if (stat.getKey().equals("tuc")) { nEquip.setUpgradeSlots((byte) stat.getValue().intValue()); } else if (isUntradeableRestricted(equipId)) { // thanks Hyun & Thora for showing an issue with more than only "Untradeable" items being flagged as such here - byte flag = nEquip.getFlag(); + short flag = nEquip.getFlag(); flag |= ItemConstants.UNTRADEABLE; nEquip.setFlag(flag); } else if (stats.get("fs") > 0) { - byte flag = nEquip.getFlag(); + short flag = nEquip.getFlag(); flag |= ItemConstants.SPIKES; nEquip.setFlag(flag); equipCache.put(equipId, nEquip); @@ -1234,6 +1242,23 @@ public class MapleItemInformationProvider { untradeableCache.put(itemId, bRestricted); return bRestricted; } + + public boolean isAccountRestricted(int itemId) { + if (accountItemRestrictionCache.containsKey(itemId)) { + return accountItemRestrictionCache.get(itemId); + } + + boolean bRestricted = false; + if(itemId != 0) { + MapleData data = getItemData(itemId); + if (data != null) { + bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; + } + } + + accountItemRestrictionCache.put(itemId, bRestricted); + return bRestricted; + } public boolean isLootRestricted(int itemId) { if (dropRestrictionCache.containsKey(itemId)) { @@ -1246,7 +1271,7 @@ public class MapleItemInformationProvider { if (data != null) { bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; if (!bRestricted) { - bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; + bRestricted = isAccountRestricted(itemId); } } } @@ -1592,11 +1617,11 @@ public class MapleItemInformationProvider { } public boolean isUnmerchable(int itemId) { - if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && isCash(itemId)) { + if(YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_CASH && isCash(itemId)) { return true; } - if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(itemId)) { + if (YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(itemId)) { return true; } @@ -1972,7 +1997,8 @@ public class MapleItemInformationProvider { } ps.close(); rs.close(); - makerEntry = new MakerItemCreateEntry(cost, reqLevel, reqMakerLevel, toGive); + makerEntry = new MakerItemCreateEntry(cost, reqLevel, reqMakerLevel); + makerEntry.addGainItem(toCreate, toGive); ps = con.prepareStatement("SELECT req_item, count FROM makerrecipedata WHERE itemid = ?"); ps.setInt(1, toCreate); rs = ps.executeQuery(); @@ -2012,17 +2038,18 @@ public class MapleItemInformationProvider { return -1; } - public int getMakerDisassembledQuantity(Integer itemId) { - int avail = 0; + public List> getMakerDisassembledItems(Integer itemId) { + List> items = new LinkedList<>(); + Connection con; try { con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT count FROM makerrecipedata WHERE itemid = ? AND req_item >= 4260000 AND req_item <= 4260008 ORDER BY count DESC"); + PreparedStatement ps = con.prepareStatement("SELECT req_item, count FROM makerrecipedata WHERE itemid = ? AND req_item >= 4260000 AND req_item < 4270000"); ps.setInt(1, itemId); ResultSet rs = ps.executeQuery(); - if(rs.next()) { - avail = (int) Math.ceil(rs.getInt("count") / 2); // return to the player half of the crystals needed + while (rs.next()) { + items.add(new Pair<>(rs.getInt("req_item"), rs.getInt("count") / 2)); // return to the player half of the crystals needed } rs.close(); @@ -2032,7 +2059,7 @@ public class MapleItemInformationProvider { e.printStackTrace(); } - return avail; + return items; } public int getMakerDisassembledFee(Integer itemId) { @@ -2139,11 +2166,10 @@ public class MapleItemInformationProvider { return questItemConsCache.get(itemId); } MapleData data = getItemData(itemId); + QuestConsItem qcItem = null; MapleData infoData = data.getChildByPath("info"); - MapleData ciData = infoData.getChildByPath("consumeItem"); - QuestConsItem qcItem = null; - if (ciData != null) { + if (infoData.getChildByPath("uiData") != null) { qcItem = new QuestConsItem(); qcItem.exp = MapleDataTool.getInt("exp", infoData); qcItem.grade = MapleDataTool.getInt("grade", infoData); @@ -2151,11 +2177,14 @@ public class MapleItemInformationProvider { qcItem.items = new HashMap<>(2); Map cItems = qcItem.items; - for (MapleData ciItem : ciData.getChildren()) { - int itemid = MapleDataTool.getInt("0", ciItem); - int qty = MapleDataTool.getInt("1", ciItem); - - cItems.put(itemid, qty); + MapleData ciData = infoData.getChildByPath("consumeItem"); + if (ciData != null) { + for (MapleData ciItem : ciData.getChildren()) { + int itemid = MapleDataTool.getInt("0", ciItem); + int qty = MapleDataTool.getInt("1", ciItem); + + cItems.put(itemid, qty); + } } } diff --git a/src/server/MapleMarriage.java b/src/server/MapleMarriage.java index b69c8a615e..1fcae81703 100644 --- a/src/server/MapleMarriage.java +++ b/src/server/MapleMarriage.java @@ -69,18 +69,10 @@ public class MapleMarriage extends EventInstanceManager { } public List getGiftItems(MapleClient c, boolean groom) { - if (c.tryacquireClient()) { - try { - List gifts = getGiftItemsList(groom); - synchronized (gifts) { - return new LinkedList<>(gifts); - } - } finally { - c.releaseClient(); - } + List gifts = getGiftItemsList(groom); + synchronized (gifts) { + return new LinkedList<>(gifts); } - - return new LinkedList<>(); } private List getGiftItemsList(boolean groom) { @@ -130,7 +122,9 @@ public class MapleMarriage extends EventInstanceManager { Connection con = DatabaseConnection.getConnection(); ItemFactory.MARRIAGE_GIFTS.saveItems(new LinkedList>(), chr.getId(), con); con.close(); - } catch (SQLException sqle) {} + } catch (SQLException sqle) { + sqle.printStackTrace(); + } for (Item item : gifts) { MapleInventoryManipulator.addFromDrop(chr.getClient(), item, false); @@ -144,18 +138,13 @@ public class MapleMarriage extends EventInstanceManager { public static List loadGiftItemsFromDb(MapleClient c, int cid) { List items = new LinkedList<>(); - if (c.tryacquireClient()) { - try { - try { - for (Pair it : ItemFactory.MARRIAGE_GIFTS.loadItems(cid, false)) { - items.add(it.getLeft()); - } - } catch (SQLException sqle) { - sqle.printStackTrace(); - } - } finally { - c.releaseClient(); + + try { + for (Pair it : ItemFactory.MARRIAGE_GIFTS.loadItems(cid, false)) { + items.add(it.getLeft()); } + } catch (SQLException sqle) { + sqle.printStackTrace(); } return items; @@ -170,19 +159,13 @@ public class MapleMarriage extends EventInstanceManager { for (Item it : giftItems) { items.add(new Pair<>(it, it.getInventoryType())); } - - if (c.tryacquireClient()) { - try { - try { - Connection con = DatabaseConnection.getConnection(); - ItemFactory.MARRIAGE_GIFTS.saveItems(items, cid, con); - con.close(); - } catch (SQLException sqle) { - sqle.printStackTrace(); - } - } finally { - c.releaseClient(); - } + + try { + Connection con = DatabaseConnection.getConnection(); + ItemFactory.MARRIAGE_GIFTS.saveItems(items, cid, con); + con.close(); + } catch (SQLException sqle) { + sqle.printStackTrace(); } } } diff --git a/src/server/MapleShop.java b/src/server/MapleShop.java index 3364e36ee8..3c75335311 100644 --- a/src/server/MapleShop.java +++ b/src/server/MapleShop.java @@ -26,7 +26,7 @@ import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.inventory.MaplePet; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; diff --git a/src/server/MapleSkillbookInformationProvider.java b/src/server/MapleSkillbookInformationProvider.java index e1246160e5..41bad6890a 100644 --- a/src/server/MapleSkillbookInformationProvider.java +++ b/src/server/MapleSkillbookInformationProvider.java @@ -19,11 +19,8 @@ */ package server; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStreamReader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -38,6 +35,9 @@ import java.util.Scanner; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import provider.MapleData; +import provider.MapleDataProviderFactory; +import provider.MapleDataTool; import tools.DatabaseConnection; /** @@ -45,6 +45,7 @@ import tools.DatabaseConnection; * @author RonanLana */ public class MapleSkillbookInformationProvider { + private final static MapleSkillbookInformationProvider instance = new MapleSkillbookInformationProvider(); public static MapleSkillbookInformationProvider getInstance() { @@ -64,187 +65,40 @@ public class MapleSkillbookInformationProvider { static String driver = "com.mysql.jdbc.Driver"; static String username = "root"; static String password = ""; - - static String wzPath = "wz"; + static String rootDirectory = "."; - static InputStreamReader fileReader = null; - static BufferedReader bufferedReader = null; - - static int initialStringLength = 50; - static int skillbookMinItemid = 2280000; static int skillbookMaxItemid = 2300000; // exclusively - static byte status = 0; - static int questId = -1; - static int isCompleteState = 0; - - static int currentItemid = 0; - static int currentCount = 0; - static { loadSkillbooks(); } - - private static String getName(String token) { - int i, j; - char[] dest; - String d; - - i = token.lastIndexOf("name"); - i = token.indexOf("\"", i) + 1; //lower bound of the string - j = token.indexOf("\"", i); //upper bound - - if(j < i) { //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway - System.out.println("[CRITICAL] Found this '" + token + "'"); - return "0"; - } - - dest = new char[initialStringLength]; - token.getChars(i, j, dest, 0); - - d = new String(dest); - return(d.trim()); - } - private static String getValue(String token) { - int i, j; - char[] dest; - String d; - - i = token.lastIndexOf("value"); - i = token.indexOf("\"", i) + 1; //lower bound of the string - j = token.indexOf("\"", i); //upper bound - - dest = new char[initialStringLength]; - token.getChars(i, j, dest, 0); - - d = new String(dest); - return(d.trim()); - } - - private static void forwardCursor(int st) { - String line = null; - - try { - while(status >= st && (line = bufferedReader.readLine()) != null) { - simpleToken(line); - } - } - catch(Exception e) { - e.printStackTrace(); - } - } - - private static void simpleToken(String token) { - if(token.contains("/imgdir")) { - status -= 1; - } - else if(token.contains("imgdir") && !token.endsWith("/>")) { // '\>' XML node description not being accounted, issue found thanks to Robin Schulz, Alex-0000 (CanIGetaPR) - status += 1; - } - } - - private static void inspectQuestItemList(int st) { - String line = null; - - try { - while(status >= st && (line = bufferedReader.readLine()) != null) { - readItemToken(line); - } - } - catch(Exception e) { - e.printStackTrace(); - } - } - public static boolean isSkillBook(int itemid) { return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid; } - private static void processCurrentItem() { - try { - if(isSkillBook(currentItemid)) { - if(currentCount > 0) { - foundSkillbooks.put(currentItemid, SkillBookEntry.QUEST); - } - } - } catch(Exception e) {} - } - - private static void readItemToken(String token) { - if(token.contains("/imgdir")) { - status -= 1; - - processCurrentItem(); - - currentItemid = 0; - currentCount = 0; - } - else if(token.contains("imgdir") && !token.endsWith("/>")) { - status += 1; - } - else { - String d = getName(token); - - if(d.equals("id")) { - currentItemid = Integer.parseInt(getValue(token)); - } else if(d.equals("count")) { - currentCount = Integer.parseInt(getValue(token)); - } - } - } - - private static void translateActToken(String token) { - String d; - int temp; - - if(token.contains("/imgdir")) { - status -= 1; - } - else if(token.contains("imgdir") && !token.endsWith("/>")) { - if(status == 1) { //getting QuestId - d = getName(token); - questId = Integer.parseInt(d); - } - else if(status == 2) { //start/complete - d = getName(token); - isCompleteState = Integer.parseInt(d); - } - else if(status == 3) { - d = getName(token); - - if(d.contains("item")) { - temp = status; - inspectQuestItemList(temp); - } else { - forwardCursor(status); - } - } - - status += 1; - } - } - private static void fetchSkillbooksFromQuests() { - String line = ""; - int lineNumber = 0; // add line number, thanks to Alex (CanIGetaPR) + MapleData actData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")).getData("Act.img"); - try { - fileReader = new InputStreamReader(new FileInputStream(wzPath + "/Quest.wz/Act.img.xml"), "UTF-8"); - bufferedReader = new BufferedReader(fileReader); - - while((line = bufferedReader.readLine()) != null) { - lineNumber++; - translateActToken(line); + for (MapleData questData : actData.getChildren()) { + for (MapleData questStatusData : questData.getChildren()) { + for (MapleData questNodeData : questStatusData.getChildren()) { + if (questNodeData.getName().contentEquals("item")) { + for (MapleData questItemData : questNodeData.getChildren()) { + int itemid = MapleDataTool.getInt("id", questItemData, 0); + int itemcount = MapleDataTool.getInt("count", questItemData, 0); + + if (isSkillBook(itemid) && itemcount > 0) { + foundSkillbooks.put(itemid, SkillBookEntry.QUEST); + } + } + + break; + } + } } - - bufferedReader.close(); - fileReader.close(); - } catch(IOException ioe) { - System.out.println("Failed to read Quest.wz file. Line " + lineNumber + ": " + line); - ioe.printStackTrace(); } } diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index 9171bd7446..42f3a83f68 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -21,15 +21,16 @@ */ package server; -import client.inventory.manipulator.MapleInventoryManipulator; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.HashMap; import java.util.Map; +import config.YamlConfig; import net.server.Server; import provider.MapleData; import provider.MapleDataTool; @@ -40,6 +41,7 @@ import server.maps.MapleMap; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import server.maps.MapleMist; +import server.maps.MaplePortal; import server.maps.MapleSummon; import server.maps.SummonMovementType; import tools.ArrayMap; @@ -55,10 +57,10 @@ import client.SkillFactory; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; +import client.inventory.manipulator.MapleInventoryManipulator; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.ItemConstants; -import constants.ServerConstants; +import constants.inventory.ItemConstants; import constants.skills.Aran; import constants.skills.Assassin; import constants.skills.Bandit; @@ -260,6 +262,7 @@ public class MapleStatEffect { } if (MapleDataTool.getInt("weakness", source, 0) > 0) { cure.add(MapleDisease.WEAKEN); + cure.add(MapleDisease.SLOW); } if (MapleDataTool.getInt("curse", source, 0) > 0) { cure.add(MapleDisease.CURSE); @@ -443,7 +446,7 @@ public class MapleStatEffect { } else { if (isMapChair(sourceid)) { addBuffStatPairToListIfNotZero(statups, MapleBuffStat.MAP_CHAIR, 1); - } else if ((sourceid == Beginner.NIMBLE_FEET || sourceid == Noblesse.NIMBLE_FEET || sourceid == Evan.NIMBLE_FEET || sourceid == Legend.AGILE_BODY) && ServerConstants.USE_ULTRA_NIMBLE_FEET == true) { + } else if ((sourceid == Beginner.NIMBLE_FEET || sourceid == Noblesse.NIMBLE_FEET || sourceid == Evan.NIMBLE_FEET || sourceid == Legend.AGILE_BODY) && YamlConfig.config.server.USE_ULTRA_NIMBLE_FEET == true) { ret.jump = (short) (ret.speed * 4); ret.speed *= 15; } @@ -464,7 +467,7 @@ public class MapleStatEffect { ret.lt = (Point) ltd.getData(); ret.rb = (Point) source.getChildByPath("rb").getData(); - if (ServerConstants.USE_MAXRANGE_ECHO_OF_HERO && (sourceid == Beginner.ECHO_OF_HERO || sourceid == Noblesse.ECHO_OF_HERO || sourceid == Legend.ECHO_OF_HERO || sourceid == Evan.ECHO_OF_HERO)) { + if (YamlConfig.config.server.USE_MAXRANGE_ECHO_OF_HERO && (sourceid == Beginner.ECHO_OF_HERO || sourceid == Noblesse.ECHO_OF_HERO || sourceid == Legend.ECHO_OF_HERO || sourceid == Evan.ECHO_OF_HERO)) { ret.lt = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE); ret.rb = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); } @@ -472,7 +475,7 @@ public class MapleStatEffect { int x = MapleDataTool.getInt("x", source, 0); - if ((sourceid == Beginner.RECOVERY || sourceid == Noblesse.RECOVERY || sourceid == Evan.RECOVERY || sourceid == Legend.RECOVERY) && ServerConstants.USE_ULTRA_RECOVERY == true) { + if ((sourceid == Beginner.RECOVERY || sourceid == Noblesse.RECOVERY || sourceid == Evan.RECOVERY || sourceid == Legend.RECOVERY) && YamlConfig.config.server.USE_ULTRA_RECOVERY == true) { x *= 10; } ret.x = x; @@ -823,6 +826,7 @@ public class MapleStatEffect { break; case ILMage.SEAL: case FPMage.SEAL: + case BlazeWizard.SEAL: monsterStatus.put(MonsterStatus.SEAL, Integer.valueOf(1)); break; case Hermit.SHADOW_WEB: // shadow web @@ -930,7 +934,7 @@ public class MapleStatEffect { applyto.toggleHide(false); return true; } - + if (primary && isHeal()) { affectedPlayers = applyBuff(applyfrom, useMaxRange); } @@ -1004,21 +1008,26 @@ public class MapleStatEffect { } } if (isShadowClaw()) { - int projectile = 0; MapleInventory use = applyto.getInventory(MapleInventoryType.USE); - for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order... - Item item = use.getItem((short) i); - if (item != null) { - if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) { - projectile = item.getItemId(); - break; + use.lockInventory(); + try { + Item projectile = null; + for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order... + Item item = use.getItem((short) i); + if (item != null) { + if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) { + projectile = item; + break; + } } } - } - if (projectile == 0) { - return false; - } else { - MapleInventoryManipulator.removeById(applyto.getClient(), MapleInventoryType.USE, projectile, 200, false, true); + if (projectile == null) { + return false; + } else { + MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) 200, false, true); + } + } finally { + use.unlockInventory(); } } SummonMovementType summonMovementType = getSummonMovementType(); @@ -1062,7 +1071,7 @@ public class MapleStatEffect { if (isMagicDoor() && !FieldLimit.DOOR.check(applyto.getMap().getFieldLimit())) { // Magic Door int y = applyto.getFh(); if (y == 0) { - y = applyto.getPosition().y; + y = applyto.getMap().getGroundBelow(applyto.getPosition()).y; // thanks Lame for pointing out unusual cases of doors sending players on ground below } Point doorPosition = new Point(applyto.getPosition().x, y); MapleDoor door = new MapleDoor(applyto, doorPosition); @@ -1110,7 +1119,7 @@ public class MapleStatEffect { } } } else { - int amount = opposition.getMembers().size() - 1; + int amount = opposition.getMembers().size(); int randd = (int) Math.floor(Math.random() * amount); MapleCharacter chrApp = applyfrom.getMap().getCharacterById(opposition.getMemberByPos(randd).getId()); if (chrApp != null && chrApp.getMap().isCPQMap()) { @@ -1124,13 +1133,7 @@ public class MapleStatEffect { } } else if (cureDebuffs.size() > 0) { // added by Drago (Dragohe4rt) for (final MapleDisease debuff : cureDebuffs) { - if (applyfrom.getParty() != null) { - for (MaplePartyCharacter chrs : applyfrom.getParty().getPartyMembers()) { - chrs.getPlayer().dispelDebuff(debuff); - } - } else { - applyfrom.dispelDebuff(debuff); - } + applyfrom.dispelDebuff(debuff); } } else if (mobSkill > 0 && mobSkillLevel > 0) { MobSkill ms = MobSkillFactory.getMobSkill(mobSkill, mobSkillLevel); @@ -1151,7 +1154,7 @@ public class MapleStatEffect { private int applyBuff(MapleCharacter applyfrom, boolean useMaxRange) { int affectedc = 1; - + if (isPartyBuff() && (applyfrom.getParty() != null || isGmBuff())) { Rectangle bounds = (!useMaxRange) ? calculateBoundingBox(applyfrom.getPosition(), applyfrom.isFacingLeft()) : new Rectangle(Integer.MIN_VALUE / 2, Integer.MIN_VALUE / 2, Integer.MAX_VALUE, Integer.MAX_VALUE); List affecteds = applyfrom.getMap().getMapObjectsInRect(bounds, Arrays.asList(MapleMapObjectType.PLAYER)); @@ -1170,7 +1173,7 @@ public class MapleStatEffect { } } } - + affectedc += affectedp.size(); // used for heal for (MapleCharacter affected : affectedp) { applyTo(applyfrom, affected, false, null, useMaxRange, affectedc); @@ -1191,6 +1194,8 @@ public class MapleStatEffect { MapleMonster monster = (MapleMonster) mo; if (isDispel()) { monster.debuffMob(skill_.getId()); + } else if (isSeal() && monster.isBoss()) { // thanks IxianMace for noticing seal working on bosses + // do nothing } else { if (makeChanceResult()) { monster.applyStatus(applyfrom, new MonsterStatusEffect(getMonsterStati(), skill_, null, false), isPoison(), getDuration()); @@ -1207,15 +1212,21 @@ public class MapleStatEffect { } private Rectangle calculateBoundingBox(Point posFrom, boolean facingLeft) { - int multiplier = facingLeft ? 1 : -1; - Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y); - Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y); + Point mylt; + Point myrb; + if (facingLeft) { + mylt = new Point(lt.x + posFrom.x, lt.y + posFrom.y); + myrb = new Point(rb.x + posFrom.x, rb.y + posFrom.y); + } else { + myrb = new Point(-lt.x + posFrom.x, rb.y + posFrom.y); // thanks Conrad, April for noticing a disturbance in AoE skill behavior after a hitched refactor here + mylt = new Point(-rb.x + posFrom.x, lt.y + posFrom.y); + } Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y); return bounds; } public int getBuffLocalDuration() { - return !ServerConstants.USE_BUFF_EVERLASTING ? duration : Integer.MAX_VALUE; + return !YamlConfig.config.server.USE_BUFF_EVERLASTING ? duration : Integer.MAX_VALUE; } public void silentApplyBuff(MapleCharacter chr, long localStartTime) { @@ -1262,7 +1273,11 @@ public class MapleStatEffect { long leftDuration = (starttime + localDuration) - Server.getInstance().getCurrentTime(); if (leftDuration > 0) { - target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int) leftDuration, activeStats)); + if (isDash() || isInfusion()) { + target.announce(MaplePacketCreator.givePirateBuff(activeStats, (skill ? sourceid : -sourceid), (int) leftDuration)); + } else { + target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int) leftDuration, activeStats)); + } } } @@ -1270,7 +1285,7 @@ public class MapleStatEffect { if (!isMonsterRiding() && !isCouponBuff() && !isMysticDoor() && !isHyperBody() && !isCombo()) { // last mystic door already dispelled if it has been used before. applyto.cancelEffect(this, true, -1); } - + List> localstatups = statups; int localDuration = getBuffLocalDuration(); int localsourceid = sourceid; @@ -1282,6 +1297,7 @@ public class MapleStatEffect { if (mount != null) { ridingMountId = mount.getItemId(); } + if (sourceid == Corsair.BATTLE_SHIP) { ridingMountId = 1932000; } else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) { @@ -1294,28 +1310,12 @@ public class MapleStatEffect { ridingMountId = 1932005; } else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) { ridingMountId = 1932010; - } else { - if (applyto.getMount() == null) { - applyto.mount(ridingMountId, sourceid); - } - - applyto.getClient().getWorldServer().registerMountHunger(applyto); - } - if (sourceid == Corsair.BATTLE_SHIP) { - givemount = new MapleMount(applyto, 1932000, sourceid); - } else if (sourceid == Beginner.SPACESHIP || sourceid == Noblesse.SPACESHIP) { - givemount = new MapleMount(applyto, 1932000 + applyto.getSkillLevel(sourceid), sourceid); - } else if (sourceid == Beginner.YETI_MOUNT1 || sourceid == Noblesse.YETI_MOUNT1 || sourceid == Legend.YETI_MOUNT1) { - givemount = new MapleMount(applyto, 1932003, sourceid); - } else if (sourceid == Beginner.YETI_MOUNT2 || sourceid == Noblesse.YETI_MOUNT2 || sourceid == Legend.YETI_MOUNT2) { - givemount = new MapleMount(applyto, 1932004, sourceid); - } else if (sourceid == Beginner.WITCH_BROOMSTICK || sourceid == Noblesse.WITCH_BROOMSTICK || sourceid == Legend.WITCH_BROOMSTICK) { - givemount = new MapleMount(applyto, 1932005, sourceid); - } else if (sourceid == Beginner.BALROG_MOUNT || sourceid == Noblesse.BALROG_MOUNT || sourceid == Legend.BALROG_MOUNT) { - givemount = new MapleMount(applyto, 1932010, sourceid); - } else { - givemount = applyto.getMount(); } + + // thanks inhyuk for noticing some skill mounts not acting properly for other players when changing maps + givemount = applyto.mount(ridingMountId, sourceid); + applyto.getClient().getWorldServer().registerMountHunger(applyto); + localDuration = sourceid; localsourceid = ridingMountId; localstatups = Collections.singletonList(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0)); @@ -1334,7 +1334,7 @@ public class MapleStatEffect { if (localstatups.size() > 0) { byte[] buff = null; byte[] mbuff = null; - if (getSummonMovementType() == null && this.isActive(applyto)) { + if (this.isActive(applyto)) { buff = MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), localDuration, localstatups); } if (isDash()) { @@ -1384,18 +1384,16 @@ public class MapleStatEffect { } if (buff != null) { - if (!hasNoIcon()) { //Thanks flav for such a simple release! :) - applyto.announce(buff); - } else { - System.out.println(" NO buff icon for id " + sourceid); - } + //Thanks flav for such a simple release! :) + //Thanks Conrad, Atoot for noticing summons not using buff icon + + applyto.announce(buff); } - + long starttime = Server.getInstance().getCurrentTime(); //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); } @@ -1706,6 +1704,10 @@ public class MapleStatEffect { private boolean isCrash() { return skill && (sourceid == DragonKnight.POWER_CRASH || sourceid == Crusader.ARMOR_CRASH || sourceid == WhiteKnight.MAGIC_CRASH); } + + private boolean isSeal() { + return skill && (sourceid == ILMage.SEAL || sourceid == FPMage.SEAL || sourceid == BlazeWizard.SEAL); + } private boolean isDispel() { return skill && (sourceid == Priest.DISPEL || sourceid == SuperGM.HEAL_PLUS_DISPEL); @@ -1775,10 +1777,10 @@ public class MapleStatEffect { } private int getMorph(MapleCharacter chr) { - if (morphId % 10 == 0) { - return morphId + chr.getGender(); - } - return morphId + 100 * chr.getGender(); + if (morphId == 1000 || morphId == 1001 || morphId == 1003) { // morph skill + return chr.getGender() == 0 ? morphId : morphId + 100; + } + return morphId; } private SummonMovementType getSummonMovementType() { @@ -1814,16 +1816,6 @@ public class MapleStatEffect { return null; } - public boolean hasNoIcon() { - return (sourceid == 3111002 || sourceid == 3211002 || + // puppet, puppet - sourceid == 3211005 || + // golden eagle - sourceid == 2121005 || sourceid == 2221005 || + // elquines, ifrit - sourceid == 2321003 || sourceid == 3121006 || + // bahamut, phoenix - sourceid == 3221005 || sourceid == 3111005 || + // frostprey, silver hawk - sourceid == 2311006 || sourceid == 5220002 || + // summon dragon, wrath of the octopi - sourceid == 5211001 || sourceid == 5211002); // octopus, gaviota - } - public boolean isSkill() { return skill; } diff --git a/src/server/MapleStorage.java b/src/server/MapleStorage.java index 18d8c0edbb..af551e435c 100644 --- a/src/server/MapleStorage.java +++ b/src/server/MapleStorage.java @@ -22,6 +22,7 @@ import client.MapleClient; import client.inventory.Item; import client.inventory.ItemFactory; import client.inventory.MapleInventoryType; +import constants.game.GameConstants; import java.io.File; import java.sql.Connection; import java.sql.PreparedStatement; @@ -44,6 +45,7 @@ import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; import net.server.audit.locks.MonitoredLockType; +import tools.FilePrinter; /** * @@ -58,76 +60,74 @@ public class MapleStorage { private int meso; private byte slots; private Map> typeItems = new HashMap<>(); - private List items; + private List items = new LinkedList<>(); private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.STORAGE, true); private MapleStorage(int id, byte slots, int meso) { this.id = id; this.slots = slots; - this.items = new LinkedList<>(); this.meso = meso; } - private static MapleStorage create(int id, int world) { - try { - Connection con = DatabaseConnection.getConnection(); - try (PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, world, slots, meso) VALUES (?, ?, 4, 0)")) { - ps.setInt(1, id); - ps.setInt(2, world); - ps.executeUpdate(); - } - - con.close(); - } catch (Exception e) { - e.printStackTrace(); + private static MapleStorage create(int id, int world) throws SQLException { + Connection con = DatabaseConnection.getConnection(); + try (PreparedStatement ps = con.prepareStatement("INSERT INTO storages (accountid, world, slots, meso) VALUES (?, ?, 4, 0)")) { + ps.setInt(1, id); + ps.setInt(2, world); + ps.executeUpdate(); } + con.close(); + return loadOrCreateFromDB(id, world); } public static MapleStorage loadOrCreateFromDB(int id, int world) { - MapleStorage ret = null; - int storeId; try { + MapleStorage ret; Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT storageid, slots, meso FROM storages WHERE accountid = ? AND world = ?"); ps.setInt(1, id); ps.setInt(2, world); + ResultSet rs = ps.executeQuery(); - if (!rs.next()) { - rs.close(); - ps.close(); - con.close(); - return create(id, world); - } else { - storeId = rs.getInt("storageid"); - ret = new MapleStorage(storeId, (byte) rs.getInt("slots"), rs.getInt("meso")); - rs.close(); - ps.close(); + if (rs.next()) { + ret = new MapleStorage(rs.getInt("storageid"), (byte) rs.getInt("slots"), rs.getInt("meso")); for (Pair item : ItemFactory.STORAGE.loadItems(ret.id, false)) { ret.items.add(item.getLeft()); } + } else { + ret = create(id, world); } + rs.close(); + ps.close(); con.close(); - } catch (SQLException ex) { - ex.printStackTrace(); + + return ret; + } catch (SQLException ex) { // exceptions leading to deploy null storages found thanks to Jefe + FilePrinter.printError(FilePrinter.STORAGE, ex, "SQL error occurred when trying to load storage for accountid " + id + ", world " + GameConstants.WORLD_NAMES[world]); + throw new RuntimeException(ex); } - return ret; } public byte getSlots() { return slots; } - public synchronized boolean gainSlots(int slots) { - slots += this.slots; + public boolean gainSlots(int slots) { + lock.lock(); + try { + slots += this.slots; + + if (slots <= 48) { + this.slots = (byte) slots; + return true; + } - if (slots <= 48) { - this.slots = (byte) slots; - return true; + return false; + } finally { + lock.unlock(); } - - return false; } public void saveToDB(Connection con) { @@ -160,29 +160,33 @@ public class MapleStorage { } } - public Item takeOut(byte slot) { - Item ret; - + public boolean takeOut(Item item) { lock.lock(); try { - ret = items.remove(slot); + boolean ret = items.remove(item); - MapleInventoryType type = ret.getInventoryType(); + MapleInventoryType type = item.getInventoryType(); typeItems.put(type, new ArrayList<>(filterItems(type))); + + return ret; } finally { lock.unlock(); } - - return ret; } - public void store(Item item) { + public boolean store(Item item) { lock.lock(); try { + if (isFull()) { // thanks Optimist for noticing unrestricted amount of insertions here + return false; + } + items.add(item); MapleInventoryType type = item.getInventoryType(); typeItems.put(type, new ArrayList<>(filterItems(type))); + + return true; } finally { lock.unlock(); } @@ -196,7 +200,7 @@ public class MapleStorage { lock.unlock(); } } - + private List filterItems(MapleInventoryType type) { List storageItems = getItems(); List ret = new LinkedList<>(); @@ -208,7 +212,7 @@ public class MapleStorage { } return ret; } - + public byte getSlot(MapleInventoryType type, byte slot) { lock.lock(); try { @@ -225,7 +229,7 @@ public class MapleStorage { lock.unlock(); } } - + public void sendStorage(MapleClient c, int npcId) { if (c.getPlayer().getLevel() < 15){ c.getPlayer().dropMessage(1, "You may only use the storage once you have reached level 15."); @@ -287,7 +291,7 @@ public class MapleStorage { for (MapleInventoryType type : MapleInventoryType.values()) { typeItems.put(type, new ArrayList<>(items)); } - + c.announce(MaplePacketCreator.arrangeStorage(slots, items)); } finally { lock.unlock(); @@ -353,7 +357,7 @@ public class MapleStorage { lock.unlock(); } } - + public void close() { lock.lock(); try { @@ -362,4 +366,5 @@ public class MapleStorage { lock.unlock(); } } + } \ No newline at end of file diff --git a/src/server/MapleStorageInventory.java b/src/server/MapleStorageInventory.java index 854883d1e3..294d9df698 100644 --- a/src/server/MapleStorageInventory.java +++ b/src/server/MapleStorageInventory.java @@ -25,8 +25,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import client.MapleClient; import client.inventory.Equip; @@ -359,7 +359,7 @@ public class MapleStorageInventory { } int invTypeCriteria = 1; - int sortCriteria = (ServerConstants.USE_ITEM_SORT_BY_NAME == true) ? 2 : 0; + int sortCriteria = (YamlConfig.config.server.USE_ITEM_SORT_BY_NAME == true) ? 2 : 0; PairedQuicksort pq = new PairedQuicksort(itemarray, sortCriteria, invTypeCriteria); inventory.clear(); diff --git a/src/server/MapleTrade.java b/src/server/MapleTrade.java index 911cb0caec..5c2c00e3b8 100644 --- a/src/server/MapleTrade.java +++ b/src/server/MapleTrade.java @@ -26,6 +26,7 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import config.YamlConfig; import tools.LogHelper; import tools.MaplePacketCreator; import client.MapleCharacter; @@ -34,11 +35,12 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.GameConstants; -import constants.ServerConstants; -import net.server.coordinator.MapleInviteCoordinator; -import net.server.coordinator.MapleInviteCoordinator.InviteResult; -import net.server.coordinator.MapleInviteCoordinator.InviteType; +import constants.game.GameConstants; +import constants.net.ServerConstants; +import net.server.coordinator.world.MapleInviteCoordinator; +import net.server.coordinator.world.MapleInviteCoordinator.InviteResult; +import net.server.coordinator.world.MapleInviteCoordinator.InviteType; +import net.server.coordinator.world.MapleInviteCoordinator.MapleInviteResult; import tools.Pair; /** @@ -78,8 +80,8 @@ public class MapleTrade { private byte number; private boolean fullTrade = false; - public MapleTrade(byte number, MapleCharacter c) { - chr = c; + public MapleTrade(byte number, MapleCharacter chr) { + this.chr = chr; this.number = number; } @@ -113,7 +115,7 @@ public class MapleTrade { private void completeTrade() { byte result; - boolean show = ServerConstants.USE_DEBUG; + boolean show = YamlConfig.config.server.USE_DEBUG; items.clear(); meso = 0; @@ -146,7 +148,7 @@ public class MapleTrade { } private void cancel(byte result) { - boolean show = ServerConstants.USE_DEBUG; + boolean show = YamlConfig.config.server.USE_DEBUG; for (Item item : items) { MapleInventoryManipulator.addFromDrop(chr.getClient(), item, show); @@ -289,8 +291,8 @@ public class MapleTrade { } } - public static void completeTrade(MapleCharacter c) { - MapleTrade local = c.getTrade(); + public static void completeTrade(MapleCharacter chr) { + MapleTrade local = chr.getTrade(); MapleTrade partner = local.getPartner(); if (local.checkCompleteHandshake()) { local.fetchExchangedItems(); @@ -298,12 +300,12 @@ public class MapleTrade { if (!local.fitsMeso()) { cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL); - c.message("There is not enough meso inventory space to complete the trade."); + chr.message("There is not enough meso inventory space to complete the trade."); partner.getChr().message("Partner does not have enough meso inventory space to complete the trade."); return; } else if (!partner.fitsMeso()) { cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL); - c.message("Partner does not have enough meso inventory space to complete the trade."); + chr.message("Partner does not have enough meso inventory space to complete the trade."); partner.getChr().message("There is not enough meso inventory space to complete the trade."); return; } @@ -311,7 +313,7 @@ public class MapleTrade { if (!local.fitsInInventory()) { if (local.fitsUniquesInInventory()) { cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL); - c.message("There is not enough inventory space to complete the trade."); + chr.message("There is not enough inventory space to complete the trade."); partner.getChr().message("Partner does not have enough inventory space to complete the trade."); } else { cancelTrade(local.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT); @@ -321,11 +323,11 @@ public class MapleTrade { } else if (!partner.fitsInInventory()) { if (partner.fitsUniquesInInventory()) { cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL); - c.message("Partner does not have enough inventory space to complete the trade."); + chr.message("Partner does not have enough inventory space to complete the trade."); partner.getChr().message("There is not enough inventory space to complete the trade."); } else { cancelTrade(partner.getChr(), TradeResult.UNSUCCESSFUL_UNIQUE_ITEM_LIMIT); - c.message("Partner cannot hold more than one one-of-a-kind item at a time."); + chr.message("Partner cannot hold more than one one-of-a-kind item at a time."); } return; } @@ -353,7 +355,7 @@ public class MapleTrade { partner.completeTrade(); partner.getChr().setTrade(null); - c.setTrade(null); + chr.setTrade(null); } } @@ -422,9 +424,9 @@ public class MapleTrade { trade.cancelHandshake(result.getValue()); } - public static void startTrade(MapleCharacter c) { - if (c.getTrade() == null) { - c.setTrade(new MapleTrade((byte) 0, c)); + public static void startTrade(MapleCharacter chr) { + if (chr.getTrade() == null) { + chr.setTrade(new MapleTrade((byte) 0, chr)); } } @@ -476,9 +478,9 @@ public class MapleTrade { } public static void visitTrade(MapleCharacter c1, MapleCharacter c2) { - Pair inviteRes = MapleInviteCoordinator.answerInvite(InviteType.TRADE, c1.getId(), c2.getId(), true); + MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.TRADE, c1.getId(), c2.getId(), true); - InviteResult res = inviteRes.getLeft(); + InviteResult res = inviteRes.result; if (res == InviteResult.ACCEPTED) { if (c1.getTrade() != null && c1.getTrade().getPartner() == c2.getTrade() && c2.getTrade() != null && c2.getTrade().getPartner() == c1.getTrade()) { c2.getClient().announce(MaplePacketCreator.getTradePartnerAdd(c1)); @@ -494,13 +496,13 @@ public class MapleTrade { } } - public static void declineTrade(MapleCharacter c) { - MapleTrade trade = c.getTrade(); + public static void declineTrade(MapleCharacter chr) { + MapleTrade trade = chr.getTrade(); if (trade != null) { if (trade.getPartner() != null) { MapleCharacter other = trade.getPartner().getChr(); - if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, c.getId(), other.getId(), false).getLeft() == InviteResult.DENIED) { - other.message(c.getName() + " has declined your trade request."); + if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, chr.getId(), other.getId(), false).result == InviteResult.DENIED) { + other.message(chr.getName() + " has declined your trade request."); } other.getTrade().cancel(TradeResult.PARTNER_CANCEL.getValue()); @@ -508,7 +510,7 @@ public class MapleTrade { } trade.cancel(TradeResult.NO_RESPONSE.getValue()); - c.setTrade(null); + chr.setTrade(null); } } diff --git a/src/server/events/RescueGaga.java b/src/server/events/RescueGaga.java index b9dd84b4ab..bd19544754 100644 --- a/src/server/events/RescueGaga.java +++ b/src/server/events/RescueGaga.java @@ -13,57 +13,46 @@ import client.SkillFactory; * @author kevintjuh93 */ public class RescueGaga extends MapleEvents { - private byte fallen; - private int completed; + + private int completed; + + public RescueGaga(int completed) { + super(); + this.completed = completed; + } - public RescueGaga(int completed) { - super(); - this.completed = completed; - this.fallen = 0; - } + public int getCompleted() { + return completed; + } - public int fallAndGet() { - fallen++; - if (fallen > 3) { - fallen = 0; - return 4; - } - return fallen; - } + public void complete() { + completed++; + } - public byte getFallen() { - return fallen; - } - - public int getCompleted() { - return completed; - } - - public void complete() { - completed++; + @Override + public int getInfo() { + return getCompleted(); + } + + public void giveSkill(MapleCharacter chr) { + int skillid = 0; + switch (chr.getJobType()) { + case 0: + skillid = 1013; + break; + case 1: + case 2: + skillid = 10001014; } - public int getInfo() { - return getCompleted(); - } - - public void giveSkill(MapleCharacter chr) { - int skillid = 0; - switch (chr.getJobType()) { - case 0: - skillid = 1013; - break; - case 1: - case 2: - skillid = 10001014; - } - long expiration = (System.currentTimeMillis() + (long) (3600 * 24 * 20 * 1000));//20 days - if (completed < 20) { - chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 1, 1, expiration); - chr.changeSkillLevel(SkillFactory.getSkill(skillid + 1), (byte) 1, 1, expiration); - chr.changeSkillLevel(SkillFactory.getSkill(skillid + 2), (byte) 1, 1, expiration); - } else { - chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 2, 2, chr.getSkillExpiration(skillid)); - } + long expiration = (System.currentTimeMillis() + 3600 * 24 * 20 * 1000);//20 days + if (completed < 20) { + chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 1, 1, expiration); + chr.changeSkillLevel(SkillFactory.getSkill(skillid + 1), (byte) 1, 1, expiration); + chr.changeSkillLevel(SkillFactory.getSkill(skillid + 2), (byte) 1, 1, expiration); + } else { + chr.changeSkillLevel(SkillFactory.getSkill(skillid), (byte) 2, 2, chr.getSkillExpiration(skillid)); } + } + } diff --git a/src/server/expeditions/MapleExpedition.java b/src/server/expeditions/MapleExpedition.java index 5a3c0dab03..17335dd105 100644 --- a/src/server/expeditions/MapleExpedition.java +++ b/src/server/expeditions/MapleExpedition.java @@ -46,6 +46,7 @@ import java.util.Properties; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.channel.Channel; /** * @@ -108,7 +109,6 @@ public class MapleExpedition { minSize = (minPlayers != 0) ? minPlayers : type.getMinSize(); maxSize = (maxPlayers != 0) ? maxPlayers : type.getMaxSize(); bossLogs = new CopyOnWriteArrayList<>(); - beginRegistration(); } public int getMinSize() { @@ -119,7 +119,7 @@ public class MapleExpedition { return maxSize; } - private void beginRegistration() { + public void beginRegistration() { registering = true; leader.announce(MaplePacketCreator.getClock(type.getRegistrationTime() * 60)); if (!silent) { @@ -137,7 +137,7 @@ public class MapleExpedition { @Override public void run() { if (registering){ - startMap.getChannelServer().removeExpedition(exped); + exped.removeChannelExpedition(startMap.getChannelServer()); if (!silent) startMap.broadcastMessage(MaplePacketCreator.serverNotice(6, "[Expedition] The time limit has been reached. Expedition has been disbanded.")); dispose(false); @@ -163,6 +163,7 @@ public class MapleExpedition { public void start(){ finishRegistration(); + registerExpeditionAttempt(); broadcastExped(MaplePacketCreator.removeClock()); if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] The expedition has started! Good luck, brave heroes!")); startTime = System.currentTimeMillis(); @@ -180,6 +181,11 @@ public class MapleExpedition { return "Sorry, this expedition is full!"; } + int channel = this.getRecruitingMap().getChannelServer().getId(); + if (!MapleExpeditionBossLog.attemptBoss(player.getId(), channel, this, false)) { // thanks Conrad, Cato for noticing some expeditions have entry limit + return "Sorry, you've already reached the quota of attempts for this expedition! Try again another day..."; + } + members.put(player.getId(), player.getName()); player.announce(MaplePacketCreator.getClock((int)(startTime - System.currentTimeMillis()) / 1000)); if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!")); @@ -202,13 +208,21 @@ public class MapleExpedition { if (!silent) broadcastExped(MaplePacketCreator.serverNotice(6, "[Expedition] " + player.getName() + " has joined the expedition!")); return 0; //"You have registered for the expedition successfully!"; } + + private void registerExpeditionAttempt(){ + int channel = this.getRecruitingMap().getChannelServer().getId(); + for (MapleCharacter chr : getActiveMembers()){ + MapleExpeditionBossLog.attemptBoss(chr.getId(), channel, this, true); + } + } + private void broadcastExped(byte[] packet){ for (MapleCharacter chr : getActiveMembers()){ chr.announce(packet); } } - + public boolean removeMember(MapleCharacter chr) { if(members.remove(chr.getId()) != null) { chr.announce(MaplePacketCreator.removeClock()); @@ -359,6 +373,14 @@ public class MapleExpedition { chr.changeMap(warpTo, toSp); } } + + public final boolean addChannelExpedition(Channel ch) { + return ch.addExpedition(this); + } + + public final void removeChannelExpedition(Channel ch) { + ch.removeExpedition(this); + } public MapleCharacter getLeader(){ return leader; diff --git a/src/server/expeditions/MapleExpeditionBossLog.java b/src/server/expeditions/MapleExpeditionBossLog.java new file mode 100644 index 0000000000..87c91bf828 --- /dev/null +++ b/src/server/expeditions/MapleExpeditionBossLog.java @@ -0,0 +1,208 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package server.expeditions; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.LinkedList; +import java.util.List; + +import config.YamlConfig; +import tools.DatabaseConnection; +import tools.Pair; + +/** + * + * @author Conrad + * @author Ronan + */ +public class MapleExpeditionBossLog { + + public enum BossLogEntry { + ZAKUM(2, 1, false), + HORNTAIL(2, 1, false), + PINKBEAN(1, 1, false), + SCARGA(1, 1, false), + PAPULATUS(2, 1, false); + + private int entries; + private int timeLength; + private int minChannel, maxChannel; + private boolean week; + + private BossLogEntry(int entries, int timeLength, boolean week) { + this(entries, 0, Integer.MAX_VALUE, timeLength, week); + } + + private BossLogEntry(int entries, int minChannel, int maxChannel, int timeLength, boolean week) { + this.entries = entries; + this.minChannel = minChannel; + this.maxChannel = maxChannel; + this.timeLength = timeLength; + this.week = week; + } + + private static List> getBossLogResetTimestamps(Calendar timeNow, boolean week) { + List> resetTimestamps = new LinkedList<>(); + + Timestamp ts = new Timestamp(timeNow.getTime().getTime()); // reset all table entries actually, thanks Conrad + for (BossLogEntry b : BossLogEntry.values()) { + if (b.week == week) { + resetTimestamps.add(new Pair<>(ts, b)); + } + } + + return resetTimestamps; + } + + private static BossLogEntry getBossEntryByName(String name) { + for (BossLogEntry b : BossLogEntry.values()) { + if (name.contentEquals(b.name())) { + return b; + } + } + + return null; + } + + } + + public static void resetBossLogTable() { + /* + Boss logs resets 12am, weekly thursday 12AM - thanks Smitty Werbenjagermanjensen (superadlez) - https://www.reddit.com/r/Maplestory/comments/61tiup/about_reset_time/ + */ + + Calendar thursday = Calendar.getInstance(); + thursday.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY); + thursday.set(Calendar.HOUR, 0); + thursday.set(Calendar.MINUTE, 0); + thursday.set(Calendar.SECOND, 0); + + Calendar now = Calendar.getInstance(); + + long weekLength = 7 * 24 * 60 * 60 * 1000; + long halfDayLength = 12 * 60 * 60 * 1000; + + long deltaTime = now.getTime().getTime() - thursday.getTime().getTime(); // 2x time: get Date into millis + deltaTime += halfDayLength; + deltaTime %= weekLength; + deltaTime -= halfDayLength; + + if (deltaTime < halfDayLength) { + MapleExpeditionBossLog.resetBossLogTable(true, thursday); + } + + now.set(Calendar.HOUR, 0); + now.set(Calendar.MINUTE, 0); + now.set(Calendar.SECOND, 0); + + MapleExpeditionBossLog.resetBossLogTable(false, now); + } + + private static void resetBossLogTable(boolean week, Calendar c) { + List> resetTimestamps = BossLogEntry.getBossLogResetTimestamps(c, week); + + try { + Connection con = DatabaseConnection.getConnection(); + + for (Pair p : resetTimestamps) { + PreparedStatement ps = con.prepareStatement("DELETE FROM " + getBossLogTable(week) + " WHERE attempttime <= ? AND bosstype LIKE ?"); + ps.setTimestamp(1, p.getLeft()); + ps.setString(2, p.getRight().name()); + ps.executeUpdate(); + ps.close(); + } + + con.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private static String getBossLogTable(boolean week) { + return week ? "bosslog_weekly" : "bosslog_daily"; + } + + private static int countPlayerEntries(int cid, BossLogEntry boss) { + int ret_count = 0; + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps; + ps = con.prepareStatement("SELECT COUNT(*) FROM " + getBossLogTable(boss.week) + " WHERE characterid = ? AND bosstype LIKE ?"); + ps.setInt(1, cid); + ps.setString(2, boss.name()); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + ret_count = rs.getInt(1); + } else { + ret_count = -1; + } + rs.close(); + ps.close(); + con.close(); + return ret_count; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + private static void insertPlayerEntry(int cid, BossLogEntry boss) { + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("INSERT INTO " + getBossLogTable(boss.week) + " (characterid, bosstype) VALUES (?,?)"); + ps.setInt(1, cid); + ps.setString(2, boss.name()); + ps.executeUpdate(); + ps.close(); + con.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static boolean attemptBoss(int cid, int channel, MapleExpedition exped, boolean log) { + if (!YamlConfig.config.server.USE_ENABLE_DAILY_EXPEDITIONS) { + return true; + } + + BossLogEntry boss = BossLogEntry.getBossEntryByName(exped.getType().name()); + if (boss == null) { + return true; + } + + if (channel < boss.minChannel || channel > boss.maxChannel) { + return false; + } + + if (countPlayerEntries(cid, boss) >= boss.entries) { + return false; + } + + if (log) { + insertPlayerEntry(cid, boss); + } + return true; + } +} diff --git a/src/server/expeditions/MapleExpeditionType.java b/src/server/expeditions/MapleExpeditionType.java index c94007f176..ca2cefd457 100644 --- a/src/server/expeditions/MapleExpeditionType.java +++ b/src/server/expeditions/MapleExpeditionType.java @@ -22,7 +22,7 @@ package server.expeditions; -import constants.ServerConstants; +import config.YamlConfig; /** * @@ -60,7 +60,7 @@ public enum MapleExpeditionType { } public int getMinSize() { - return !ServerConstants.USE_ENABLE_SOLO_EXPEDITIONS ? minSize : 1; + return !YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? minSize : 1; } public int getMaxSize() { diff --git a/src/server/life/ChangeableStats.java b/src/server/life/ChangeableStats.java index 61b3de01af..876cc7b553 100644 --- a/src/server/life/ChangeableStats.java +++ b/src/server/life/ChangeableStats.java @@ -17,7 +17,7 @@ along with this program. If not, see . */ package server.life; -import constants.GameConstants; +import constants.game.GameConstants; public class ChangeableStats extends OverrideMonsterStats { diff --git a/src/server/life/MapleLifeFactory.java b/src/server/life/MapleLifeFactory.java index b691581503..4d74054ee4 100644 --- a/src/server/life/MapleLifeFactory.java +++ b/src/server/life/MapleLifeFactory.java @@ -25,9 +25,11 @@ import java.awt.Point; import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import provider.MapleData; import provider.MapleDataProvider; import provider.MapleDataProviderFactory; @@ -43,7 +45,19 @@ public class MapleLifeFactory { private static MapleData mobStringData = stringDataWZ.getData("Mob.img"); private static MapleData npcStringData = stringDataWZ.getData("Npc.img"); private static Map monsterStats = new HashMap<>(); + private static Set hpbarBosses = getHpBarBosses(); + private static Set getHpBarBosses() { + Set ret = new HashSet<>(); + + MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/UI.wz")); + for (MapleData bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) { + ret.add(Integer.valueOf(bossData.getName())); + } + + return ret; + } + public static AbstractLoadedMapleLife getLife(int id, String type) { if (type.equalsIgnoreCase("n")) { return getNPC(id); @@ -88,21 +102,19 @@ public class MapleLifeFactory { MapleData monsterInfoData = monsterData.getChildByPath("info"); List attackInfos = new LinkedList<>(); - MapleMonsterStats stats; + MapleMonsterStats stats = new MapleMonsterStats(); int linkMid = MapleDataTool.getIntConvert("link", monsterInfoData, 0); - if (linkMid == 0) { - stats = new MapleMonsterStats(); - } else { + if (linkMid != 0) { Pair> linkStats = getMonsterStats(linkMid); if (linkStats == null) { return null; } - stats = linkStats.getLeft(); + // thanks resinate for noticing non-propagable infos such as revives getting retrieved attackInfos.addAll(linkStats.getRight()); } - + stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData)); stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, stats.isFriendly() ? 1 : 0) == 1); stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData)); @@ -149,10 +161,12 @@ public class MapleLifeFactory { } stats.setFirstAttack(firstAttack > 0); stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000); - - stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0)); - stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0)); - + + // thanks yuxaij, Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements + boolean hpbarBoss = stats.isBoss() && hpbarBosses.contains(mid); + stats.setTagColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0) : 0); + stats.setTagBgColor(hpbarBoss ? MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0) : 0); + for (MapleData idata : monsterData) { if (!idata.getName().equals("info")) { int delay = 0; diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index f3ed231f70..bef2d83a8d 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -24,12 +24,13 @@ package server.life; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; +import client.MapleFamilyEntry; import client.MapleJob; import client.Skill; import client.SkillFactory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.ServerConstants; +import config.YamlConfig; import constants.skills.Crusader; import constants.skills.FPMage; import constants.skills.Hermit; @@ -52,6 +53,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import net.server.audit.locks.MonitoredReentrantLock; @@ -70,7 +72,12 @@ import tools.Randomizer; import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.MapleMonsterAggroCoordinator; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.MobAnimationService; +import net.server.services.task.channel.MobClearSkillService; +import net.server.services.task.channel.MobStatusService; +import net.server.services.task.channel.OverallService; +import net.server.coordinator.world.MapleMonsterAggroCoordinator; import server.MapleStatEffect; import server.loot.MapleLootManager; import server.maps.MapleSummon; @@ -96,10 +103,12 @@ public class MapleMonster extends AbstractLoadedMapleLife { private Set usedAttacks = new HashSet<>(); private Set calledMobOids = null; private WeakReference callerMob = new WeakReference<>(null); - private List stolenItems = new ArrayList<>(); + private List stolenItems = new ArrayList<>(5); private int team; private int parentMobOid = 0; + private int spawnEffect = 0; private final HashMap takenDamage = new HashMap<>(); + private ScheduledFuture monsterItemDrop = null; private Runnable removeAfterAction = null; private boolean availablePuppetUpdate = true; @@ -135,6 +144,14 @@ public class MapleMonster extends AbstractLoadedMapleLife { maxHpPlusHeal.set(hp.get()); } + + public void setSpawnEffect(int effect) { + spawnEffect = effect; + } + + public int getSpawnEffect() { + return spawnEffect; + } public void disableDrops() { this.dropsDisabled = true; @@ -321,7 +338,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { } if(animationTime > 0) { - return map.getChannelServer().registerMobOnAnimationEffect(map.getId(), this.hashCode(), animationTime); + MobAnimationService service = (MobAnimationService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_ANIMATION); + return service.registerMobOnAnimationEffect(map.getId(), this.hashCode(), animationTime); } else { return true; } @@ -415,7 +433,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { */ if (damage > 0) { - this.applyDamage(attacker, damage, stayAlive); + this.applyDamage(attacker, damage, stayAlive, false); if (!this.isAlive()) { // monster just died lastHit = true; } @@ -433,17 +451,20 @@ public class MapleMonster extends AbstractLoadedMapleLife { * @param damage * @param stayAlive */ - private void applyDamage(MapleCharacter from, int damage, boolean stayAlive) { + private void applyDamage(MapleCharacter from, int damage, boolean stayAlive, boolean fake) { Integer trueDamage = applyAndGetHpDamage(damage, stayAlive); if (trueDamage == null) { return; } - if (ServerConstants.USE_DEBUG) { + if (YamlConfig.config.server.USE_DEBUG) { from.dropMessage(5, "Hitted MOB " + this.getId() + ", OID " + this.getObjectId()); } - dispatchMonsterDamaged(from, trueDamage); - + + if (!fake) { + dispatchMonsterDamaged(from, trueDamage); + } + if (!takenDamage.containsKey(from.getId())) { takenDamage.put(from.getId(), new AtomicLong(trueDamage)); } else { @@ -453,6 +474,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { broadcastMobHpBar(from); } + public void applyFakeDamage(MapleCharacter from, int damage, boolean stayAlive) { + applyDamage(from, damage, stayAlive, true); + } + public void heal(int hp, int mp) { Integer hpHealed = applyAndGetHpDamage(-hp, false); if (hpHealed == null) { @@ -505,19 +530,20 @@ public class MapleMonster extends AbstractLoadedMapleLife { return avgExpReward + Math.sqrt(varExpReward); } - private void distributePlayerExperience(MapleCharacter chr, float exp, float partyBonusMod, int totalPartyLevel, boolean highestPartyDamager, boolean whiteExpGain) { - float playerExp = (ServerConstants.EXP_SPLIT_COMMON_MOD * chr.getLevel()) / totalPartyLevel; - if (highestPartyDamager) playerExp += ServerConstants.EXP_SPLIT_MVP_MOD; + private void distributePlayerExperience(MapleCharacter chr, float exp, float partyBonusMod, int totalPartyLevel, boolean highestPartyDamager, boolean whiteExpGain, boolean hasPartySharers) { + float playerExp = (YamlConfig.config.server.EXP_SPLIT_COMMON_MOD * chr.getLevel()) / totalPartyLevel; + if (highestPartyDamager) playerExp += YamlConfig.config.server.EXP_SPLIT_MVP_MOD; playerExp *= exp; float bonusExp = partyBonusMod * playerExp; - this.giveExpToCharacter(chr, playerExp, bonusExp, whiteExpGain); + this.giveExpToCharacter(chr, playerExp, bonusExp, whiteExpGain, hasPartySharers); + giveFamilyRep(chr.getFamilyEntry()); } private void distributePartyExperience(Map partyParticipation, float expPerDmg, Set underleveled, Map personalRatio, double sdevRatio) { IntervalBuilder leechInterval = new IntervalBuilder(); - leechInterval.addInterval(this.getLevel() - ServerConstants.EXP_SPLIT_LEVEL_INTERVAL, this.getLevel() + ServerConstants.EXP_SPLIT_LEVEL_INTERVAL); + leechInterval.addInterval(this.getLevel() - YamlConfig.config.server.EXP_SPLIT_LEVEL_INTERVAL, this.getLevel() + YamlConfig.config.server.EXP_SPLIT_LEVEL_INTERVAL); long maxDamage = 0, partyDamage = 0; MapleCharacter participationMvp = null; @@ -532,31 +558,40 @@ public class MapleMonster extends AbstractLoadedMapleLife { // thanks Thora for pointing out leech level limitation int chrLevel = e.getKey().getLevel(); - leechInterval.addInterval(chrLevel - ServerConstants.EXP_SPLIT_LEECH_INTERVAL, chrLevel + ServerConstants.EXP_SPLIT_LEECH_INTERVAL); + leechInterval.addInterval(chrLevel - YamlConfig.config.server.EXP_SPLIT_LEECH_INTERVAL, chrLevel + YamlConfig.config.server.EXP_SPLIT_LEECH_INTERVAL); } List expMembers = new LinkedList<>(); int totalPartyLevel = 0; // thanks G h o s t, Alfred, Vcoc, BHB for poiting out a bug in detecting party members after membership transactions in a party took place - for (MapleCharacter member : partyParticipation.keySet().iterator().next().getPartyMembersOnSameMap()) { - if (!leechInterval.inInterval(member.getLevel())) { - underleveled.add(member); - continue; + if (YamlConfig.config.server.USE_ENFORCE_MOB_LEVEL_RANGE) { + for (MapleCharacter member : partyParticipation.keySet().iterator().next().getPartyMembersOnSameMap()) { + if (!leechInterval.inInterval(member.getLevel())) { + underleveled.add(member); + continue; + } + + totalPartyLevel += member.getLevel(); + expMembers.add(member); + } + } else { // thanks Ari for noticing unused server flag after EXP system overhaul + for (MapleCharacter member : partyParticipation.keySet().iterator().next().getPartyMembersOnSameMap()) { + totalPartyLevel += member.getLevel(); + expMembers.add(member); } - - totalPartyLevel += member.getLevel(); - expMembers.add(member); } int membersSize = expMembers.size(); float participationExp = partyDamage * expPerDmg; // thanks Crypter for reporting an insufficiency on party exp bonuses - float partyBonusMod = (membersSize > 1) ? 0.05f * membersSize : 0.0f; + boolean hasPartySharers = membersSize > 1; + float partyBonusMod = hasPartySharers ? 0.05f * membersSize : 0.0f; for (MapleCharacter mc : expMembers) { - distributePlayerExperience(mc, participationExp, partyBonusMod, totalPartyLevel, mc == participationMvp, isWhiteExpGain(mc, personalRatio, sdevRatio)); + distributePlayerExperience(mc, participationExp, partyBonusMod, totalPartyLevel, mc == participationMvp, isWhiteExpGain(mc, personalRatio, sdevRatio), hasPartySharers); + giveFamilyRep(mc.getFamilyEntry()); } } @@ -629,7 +664,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { float exp = chrParticipation.getValue() * expPerDmg; MapleCharacter chr = chrParticipation.getKey(); - distributePlayerExperience(chr, exp, 0.0f, chr.getLevel(), true, isWhiteExpGain(chr, personalRatio, sdevRatio)); + distributePlayerExperience(chr, exp, 0.0f, chr.getLevel(), true, isWhiteExpGain(chr, personalRatio, sdevRatio), false); } for (Map partyParticipation : partyExpDist.values()) { @@ -650,13 +685,17 @@ public class MapleMonster extends AbstractLoadedMapleLife { } - private float getStatusExpMultiplier(MapleCharacter attacker) { + private float getStatusExpMultiplier(MapleCharacter attacker, boolean hasPartySharers) { float multiplier = 1.0f; // thanks Prophecy & Aika for finding out Holy Symbol not being applied on party bonuses Integer holySymbol = attacker.getBuffedValue(MapleBuffStat.HOLY_SYMBOL); if (holySymbol != null) { - multiplier *= (1.0 + (holySymbol.doubleValue() / 100.0)); + if (YamlConfig.config.server.USE_FULL_HOLY_SYMBOL) { // thanks Mordred, xinyifly, AyumiLove, andy33 for noticing HS hands out 20% of its potential on less than 3 players + multiplier *= (1.0 + (holySymbol.doubleValue() / 100.0)); + } else { + multiplier *= (1.0 + (holySymbol.doubleValue() / (hasPartySharers ? 100.0 : 500.0))); + } } statiLock.lock(); @@ -679,13 +718,13 @@ public class MapleMonster extends AbstractLoadedMapleLife { exp = Integer.MIN_VALUE; } - return (int) exp; + return (int) Math.round(exp); // operations on float point are not point-precise... thanks IxianMace for noticing -1 EXP gains } - private void giveExpToCharacter(MapleCharacter attacker, Float personalExp, Float partyExp, boolean white) { + private void giveExpToCharacter(MapleCharacter attacker, Float personalExp, Float partyExp, boolean white, boolean hasPartySharers) { if (attacker.isAlive()) { if (personalExp != null) { - personalExp *= getStatusExpMultiplier(attacker); + personalExp *= getStatusExpMultiplier(attacker, hasPartySharers); personalExp *= attacker.getExpRate(); } else { personalExp = 0.0f; @@ -699,9 +738,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { int _personalExp = expValueToInteger(personalExp); // assuming no negative xp here if (partyExp != null) { - partyExp *= getStatusExpMultiplier(attacker); + partyExp *= getStatusExpMultiplier(attacker, hasPartySharers); partyExp *= attacker.getExpRate(); - partyExp *= ServerConstants.PARTY_BONUS_EXP_RATE; + partyExp *= YamlConfig.config.server.PARTY_BONUS_EXP_RATE; } else { partyExp = 0.0f; } @@ -710,11 +749,15 @@ public class MapleMonster extends AbstractLoadedMapleLife { attacker.gainExp(_personalExp, _partyExp, true, false, white); attacker.increaseEquipExp(_personalExp); - attacker.updateQuestMobCount(getId()); + attacker.raiseQuestMobCount(getId()); } } public List retrieveRelevantDrops() { + if (this.getStats().isFriendly()) { // thanks Conrad for noticing friendly mobs not spawning loots after a recent update + return MapleMonsterInformationProvider.getInstance().retrieveEffectiveDrop(this.getId()); + } + Map pchars = map.getMapAllPlayers(); List lootChars = new LinkedList<>(); @@ -727,7 +770,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { return MapleLootManager.retrieveRelevantDrops(this.getId(), lootChars); } - + public MapleCharacter killBy(final MapleCharacter killer) { distributeExperience(killer != null ? killer.getId() : 0); @@ -744,21 +787,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (toSpawn.contains(timeMob.getLeft())) { reviveMap.broadcastMessage(MaplePacketCreator.serverNotice(6, timeMob.getRight())); } - - if (timeMob.getLeft() == 9300338 && (reviveMap.getId() >= 922240100 && reviveMap.getId() <= 922240119)) { - if (!reviveMap.containsNPC(9001108)) { - MapleNPC npc = MapleLifeFactory.getNPC(9001108); - npc.setPosition(new Point(172, 9)); - npc.setCy(9); - npc.setRx0(172 + 50); - npc.setRx1(172 - 50); - npc.setFh(27); - reviveMap.addMapObject(npc); - reviveMap.broadcastMessage(MaplePacketCreator.spawnNPC(npc)); - } else { - reviveMap.toggleHiddenNPC(9001108); - } - } } if(toSpawn.size() > 0) { @@ -796,7 +824,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { if(htKilled) { reviveMap.killMonster(ht, killer, true); - ht.broadcastMobHpBar(killer); } } @@ -822,7 +849,36 @@ public class MapleMonster extends AbstractLoadedMapleLife { return looter != null ? looter : killer; } - private void dispatchUpdateQuestMobCount() { + public void dropFromFriendlyMonster(long delay) { + final MapleMonster m = this; + monsterItemDrop = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + if (!m.isAlive()) { + if (monsterItemDrop != null) { + monsterItemDrop.cancel(false); + } + + return; + } + + MapleMap map = m.getMap(); + List chrList = map.getAllPlayers(); + if (!chrList.isEmpty()) { + MapleCharacter chr = (MapleCharacter) chrList.get(0); + + EventInstanceManager eim = map.getEventInstance(); + if (eim != null) { + eim.friendlyItemDrop(m); + } + + map.dropFromFriendlyMonster(chr, m); + } + } + }, delay, delay); + } + + private void dispatchRaiseQuestMobCount() { Set attackerChrids = takenDamage.keySet(); if(!attackerChrids.isEmpty()) { Map mapChars = map.getMapPlayers(); @@ -833,7 +889,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { MapleCharacter chr = mapChars.get(chrid); if(chr != null && chr.isLoggedinWorld()) { - chr.updateQuestMobCount(mobid); + chr.raiseQuestMobCount(mobid); } } } @@ -855,7 +911,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { private synchronized void processMonsterKilled(boolean hasKiller) { if(!hasKiller) { // players won't gain EXP from a mob that has no killer, but a quest count they should - dispatchUpdateQuestMobCount(); + dispatchRaiseQuestMobCount(); } this.aggroClearDamages(); @@ -910,6 +966,14 @@ public class MapleMonster extends AbstractLoadedMapleLife { listener.monsterHealed(trueHeal); } } + + private void giveFamilyRep(MapleFamilyEntry entry) { + if(entry != null) { + int repGain = isBoss() ? YamlConfig.config.server.FAMILY_REP_PER_BOSS_KILL : YamlConfig.config.server.FAMILY_REP_PER_KILL; + if(getMaxHp() <= 1) repGain = 0; //don't count trash mobs + entry.giveReputationToSenior(repGain, true); + } + } public int getHighestDamagerId() { int curId = 0; @@ -976,16 +1040,20 @@ public class MapleMonster extends AbstractLoadedMapleLife { return isBoss() && getTagColor() > 0; } + public void broadcastMonsterStatus() { + Collection mseList = this.getStati().values(); + for (MapleCharacter chr : map.getAllPlayers()) { + announceMonsterStatusInternal(chr.getClient(), mseList); + } + } + public void announceMonsterStatus(MapleClient client) { - statiLock.lock(); - try { - if (stati.size() > 0) { - for (final MonsterStatusEffect mse : this.stati.values()) { - client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); - } - } - } finally { - statiLock.unlock(); + announceMonsterStatusInternal(client, this.getStati().values()); + } + + public void announceMonsterStatusInternal(MapleClient client, Collection mseList) { + for (MonsterStatusEffect mse : mseList) { + client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); } } @@ -1010,6 +1078,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { @Override public void sendDestroyData(MapleClient client) { client.announce(MaplePacketCreator.killMonster(getObjectId(), false)); + client.announce(MaplePacketCreator.killMonster(getObjectId(), true)); } @Override @@ -1137,7 +1206,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (oldEffect != null) { oldEffect.removeActiveStatus(stat); if (oldEffect.getStati().isEmpty()) { - ch.interruptMobStatus(mapid, oldEffect); + MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS); + service.interruptMobStatus(mapid, oldEffect); } } } @@ -1243,7 +1313,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { statiLock.unlock(); } - ch.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay); + MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS); + service.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay); return true; } @@ -1294,7 +1365,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { statiLock.unlock(); } - map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration); + MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS); + service.registerMobStatus(map.getId(), effect, cancelTask, duration); } public void refreshMobPosition() { @@ -1305,7 +1377,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { aggroRemoveController(); setPosition(newPoint); - map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement())); + map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement(), getIdleMovementDataLength())); map.moveMonster(this, this.getPosition()); aggroUpdateController(); @@ -1341,7 +1413,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { int i = (skillid == Crusader.ARMOR_CRASH ? 1 : (skillid == WhiteKnight.MAGIC_CRASH ? 2 : 0)); debuffMobStat(statups[i]); - if(ServerConstants.USE_ANTI_IMMUNITY_CRASH) { + if(YamlConfig.config.server.USE_ANTI_IMMUNITY_CRASH) { if (skillid == Crusader.ARMOR_CRASH) { if(!isBuffed(MonsterStatus.WEAPON_REFLECT)) { debuffMobStat(MonsterStatus.WEAPON_IMMUNITY); @@ -1492,7 +1564,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { } }; - mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, cooltime); + MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL); + service.registerMobClearSkillAction(mmap.getId(), r, cooltime); } private void clearSkill(int skillId, int level) { @@ -1559,8 +1632,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { mons.clearAttack(attackPos); } }; - - mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, cooltime); + + MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL); + service.registerMobClearSkillAction(mmap.getId(), r, cooltime); } finally { monsterLock.unlock(); } @@ -1607,7 +1681,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { public void run() { int curHp = hp.get(); if(curHp <= 1) { - map.getChannelServer().interruptMobStatus(map.getId(), status); + MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS); + service.interruptMobStatus(map.getId(), status); return; } @@ -1615,13 +1690,14 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (damage >= curHp) { damage = curHp - 1; if (type == 1 || type == 2) { - map.getChannelServer().interruptMobStatus(map.getId(), status); + MobStatusService service = (MobStatusService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_STATUS); + service.interruptMobStatus(map.getId(), status); } } if (damage > 0) { lockMonster(); try { - applyDamage(chr, damage, true); + applyDamage(chr, damage, true, false); } finally { unlockMonster(); } @@ -1671,7 +1747,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { } }; - mmap.getChannelServer().registerMobClearSkillAction(mmap.getId(), r, milli); + MobClearSkillService service = (MobClearSkillService) mmap.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL); + service.registerMobClearSkillAction(mmap.getId(), r, milli); } } finally { monsterLock.unlock(); @@ -1706,7 +1783,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { public Map getStati() { statiLock.lock(); try { - return Collections.unmodifiableMap(stati); + return new HashMap<>(stati); } finally { statiLock.unlock(); } @@ -2046,7 +2123,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { MapleCharacter chrController = this.getActiveController(); if (chrController == null) { - this.aggroSwitchController(player, true); + this.aggroSwitchController(player, true); } else if (chrController.getId() == player.getId()) { this.setControllerHasAggro(true); } @@ -2153,7 +2230,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { }; // had to schedule this since mob wouldn't stick to puppet aggro who knows why - this.getMap().getChannelServer().registerOverallAction(this.getMap().getId(), r, ServerConstants.UPDATE_INTERVAL); + OverallService service = (OverallService) this.getMap().getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(this.getMap().getId(), r, YamlConfig.config.server.UPDATE_INTERVAL); } /** @@ -2184,6 +2262,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { } public void dispose() { + if (monsterItemDrop != null) { + monsterItemDrop.cancel(false); + } + this.getMap().dismissRemoveAfter(this); disposeLocks(); } diff --git a/src/server/life/MapleMonsterInformationProvider.java b/src/server/life/MapleMonsterInformationProvider.java index f4d44ea319..66e0d385b6 100644 --- a/src/server/life/MapleMonsterInformationProvider.java +++ b/src/server/life/MapleMonsterInformationProvider.java @@ -20,8 +20,8 @@ */ package server.life; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.inventory.ItemConstants; import java.io.File; import java.sql.Connection; import java.sql.PreparedStatement; @@ -138,7 +138,7 @@ public class MapleMonsterInformationProvider { // this reads the drop entries searching for multi-equip, properly processing them List list = retrieveDrop(monsterId); - if (hasNoMultiEquipDrops.contains(monsterId) || !ServerConstants.USE_MULTIPLE_SAME_EQUIP_DROP) { + if (hasNoMultiEquipDrops.contains(monsterId) || !YamlConfig.config.server.USE_MULTIPLE_SAME_EQUIP_DROP) { return list; } @@ -152,7 +152,7 @@ public class MapleMonsterInformationProvider { int rnd = Randomizer.rand(mde.Minimum, mde.Maximum); for (int i = 0; i < rnd - 1; i++) { - extra.add(mde); // this passes copies of the equips' MDE with min/max quantity > 1, but idc it'll be unused anyways + extra.add(mde); // this passes copies of the equips' MDE with min/max quantity > 1, but idc on equips they are unused anyways } } } diff --git a/src/server/life/MaplePlayerNPC.java b/src/server/life/MaplePlayerNPC.java index f956a04864..e49da183a2 100644 --- a/src/server/life/MaplePlayerNPC.java +++ b/src/server/life/MaplePlayerNPC.java @@ -21,6 +21,7 @@ */ package server.life; +import config.YamlConfig; import server.life.positioner.MaplePlayerNPCPositioner; import server.life.positioner.MaplePlayerNPCPodium; import java.awt.Point; @@ -42,8 +43,8 @@ import client.MapleCharacter; import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.GameConstants; -import constants.ServerConstants; +import constants.game.GameConstants; +import constants.net.ServerConstants; import net.server.Server; import net.server.channel.Channel; import net.server.world.World; @@ -360,11 +361,15 @@ public class MaplePlayerNPC extends AbstractMapleMapObject { int j = 0; for(int i = branchSid; i < nextBranchSid; i++) { if(!usedScriptIds.contains(i)) { - availables.add(i); - j++; - - if(j == 20) { - break; + if (MaplePlayerNPCFactory.isExistentScriptid(i)) { // thanks Ark, Zein, geno, Ariel, JrCl0wn for noticing client crashes due to use of missing scriptids + availables.add(i); + j++; + + if(j == 20) { + break; + } + } else { + break; // after this point no more scriptids expected... } } } @@ -426,7 +431,7 @@ public class MaplePlayerNPC extends AbstractMapleMapObject { } } - if(ServerConstants.USE_DEBUG) System.out.println("GOT SID " + scriptId + " POS " + pos); + if(YamlConfig.config.server.USE_DEBUG) System.out.println("GOT SID " + scriptId + " POS " + pos); int worldId = chr.getWorld(); int jobId = (chr.getJob().getId() / 100) * 100; diff --git a/src/server/life/MaplePlayerNPCFactory.java b/src/server/life/MaplePlayerNPCFactory.java index 854fa9890b..bd32f773b6 100644 --- a/src/server/life/MaplePlayerNPCFactory.java +++ b/src/server/life/MaplePlayerNPCFactory.java @@ -19,6 +19,7 @@ */ package server.life; +import constants.net.ServerConstants; import java.io.File; import java.util.HashMap; import java.util.Map; @@ -36,9 +37,15 @@ import provider.MapleDataTool; */ public class MaplePlayerNPCFactory { + private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz")); + private static final Map> dnpcMaps = new HashMap<>(); private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough + public static boolean isExistentScriptid(int scriptid) { + return npcData.getData(scriptid + ".img") != null; + } + public static void loadDeveloperRoomMetadata(MapleDataProvider npc) { MapleData thisData = npc.getData("9977777.img"); if(thisData != null) { @@ -60,7 +67,7 @@ public class MaplePlayerNPCFactory { } public static void loadFactoryMetadata() { - MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz")); + MapleDataProvider npc = npcData; loadDeveloperRoomMetadata(npc); MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz")); diff --git a/src/server/life/MobSkill.java b/src/server/life/MobSkill.java index 55c6e6436d..a5cd921fd5 100644 --- a/src/server/life/MobSkill.java +++ b/src/server/life/MobSkill.java @@ -29,9 +29,11 @@ import java.util.List; import client.MapleCharacter; import client.MapleDisease; import client.status.MonsterStatus; -import constants.GameConstants; +import constants.game.GameConstants; import java.util.LinkedList; import java.util.Map; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.OverallService; import tools.Randomizer; import server.maps.MapleMap; import server.maps.MapleMapObject; @@ -115,7 +117,8 @@ public class MobSkill { } }; - monster.getMap().getChannelServer().registerOverallAction(monster.getMap().getId(), toRun, animationTime); + OverallService service = (OverallService) monster.getMap().getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(monster.getMap().getId(), toRun, animationTime); } public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill, List banishPlayers) { @@ -331,7 +334,7 @@ public class MobSkill { if (lt != null && rb != null && skill) { int i = 0; for (MapleCharacter character : getPlayersInRange(monster, player)) { - if (!character.isActiveBuffedValue(2321005)) { // holy shield + if (!character.hasActiveBuff(2321005)) { // holy shield if (disease.equals(MapleDisease.SEDUCE)) { if (i < 10) { character.giveDebuff(MapleDisease.SEDUCE, this); diff --git a/src/server/life/MobSkillFactory.java b/src/server/life/MobSkillFactory.java index d6f9a416e6..584b73d760 100644 --- a/src/server/life/MobSkillFactory.java +++ b/src/server/life/MobSkillFactory.java @@ -27,11 +27,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import provider.MapleData; import provider.MapleDataProvider; import provider.MapleDataProviderFactory; @@ -46,9 +47,9 @@ public class MobSkillFactory { private static Map mobSkills = new HashMap(); private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz")); private static MapleData skillRoot = dataSource.getData("MobSkill.img"); - private final static ReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY); - private final static ReadLock rL = dataLock.readLock(); - private final static WriteLock wL = dataLock.writeLock(); + private final static MonitoredReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY); + private final static MonitoredReadLock rL = MonitoredReadLockFactory.createLock(dataLock); + private final static MonitoredWriteLock wL = MonitoredWriteLockFactory.createLock(dataLock); public static MobSkill getMobSkill(final int skillId, final int level) { final String key = skillId + "" + level; diff --git a/src/server/life/positioner/MaplePlayerNPCPodium.java b/src/server/life/positioner/MaplePlayerNPCPodium.java index a8b29a71d5..46bce9a406 100644 --- a/src/server/life/positioner/MaplePlayerNPCPodium.java +++ b/src/server/life/positioner/MaplePlayerNPCPodium.java @@ -19,7 +19,7 @@ */ package server.life.positioner; -import constants.ServerConstants; +import config.YamlConfig; import java.awt.Point; import java.util.ArrayList; import java.util.Arrays; @@ -85,7 +85,7 @@ public class MaplePlayerNPCPodium { private static Point reorganizePlayerNpcs(MapleMap map, int newStep, List mmoList) { if(!mmoList.isEmpty()) { - if(ServerConstants.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep); + if(YamlConfig.config.server.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep); List playerNpcs = new ArrayList<>(mmoList.size()); for(MapleMapObject mmo : mmoList) { @@ -135,7 +135,7 @@ public class MaplePlayerNPCPodium { int podiumStep = podiumData % (1 << 5), podiumCount = (podiumData / (1 << 5)); if(podiumCount >= 3 * podiumStep) { - if(podiumStep >= ServerConstants.PLAYERNPC_AREA_STEPS) return null; + if(podiumStep >= YamlConfig.config.server.PLAYERNPC_AREA_STEPS) return null; List mmoList = map.getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER_NPC)); map.getWorldServer().setPlayerNpcMapPodiumData(map.getId(), encodePodiumData(podiumStep + 1, podiumCount + 1)); diff --git a/src/server/life/positioner/MaplePlayerNPCPositioner.java b/src/server/life/positioner/MaplePlayerNPCPositioner.java index 6053240add..be57d3f139 100644 --- a/src/server/life/positioner/MaplePlayerNPCPositioner.java +++ b/src/server/life/positioner/MaplePlayerNPCPositioner.java @@ -19,7 +19,7 @@ */ package server.life.positioner; -import constants.ServerConstants; +import config.YamlConfig; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; @@ -59,18 +59,18 @@ public class MaplePlayerNPCPositioner { } private static int calcDx(int newStep) { - return ServerConstants.PLAYERNPC_AREA_X / (newStep + 1); + return YamlConfig.config.server.PLAYERNPC_AREA_X / (newStep + 1); } private static int calcDy(int newStep) { - return (ServerConstants.PLAYERNPC_AREA_Y / 2) + (ServerConstants.PLAYERNPC_AREA_Y / (1 << (newStep + 1))); + return (YamlConfig.config.server.PLAYERNPC_AREA_Y / 2) + (YamlConfig.config.server.PLAYERNPC_AREA_Y / (1 << (newStep + 1))); } private static List rearrangePlayerNpcPositions(MapleMap map, int newStep, int pnpcsSize) { Rectangle mapArea = map.getMapArea(); - int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y; - int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; + int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y; + int outx = mapArea.x + mapArea.width - YamlConfig.config.server.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; int cx = calcDx(newStep), cy = calcDy(newStep); List otherPlayerNpcs = new LinkedList<>(); @@ -101,8 +101,8 @@ public class MaplePlayerNPCPositioner { private static Point rearrangePlayerNpcs(MapleMap map, int newStep, List pnpcs) { Rectangle mapArea = map.getMapArea(); - int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y; - int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; + int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y; + int outx = mapArea.x + mapArea.width - YamlConfig.config.server.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; int cx = calcDx(newStep), cy = calcDy(newStep); List otherPlayerNpcs = new LinkedList<>(); @@ -138,7 +138,7 @@ public class MaplePlayerNPCPositioner { private static Point reorganizePlayerNpcs(MapleMap map, int newStep, List mmoList) { if(!mmoList.isEmpty()) { - if(ServerConstants.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep); + if(YamlConfig.config.server.USE_DEBUG) System.out.println("Reorganizing pnpc map, step " + newStep); List playerNpcs = new ArrayList<>(mmoList.size()); for(MapleMapObject mmo : mmoList) { @@ -189,12 +189,12 @@ public class MaplePlayerNPCPositioner { int cx = calcDx(initStep), cy = calcDy(initStep); Rectangle mapArea = map.getMapArea(); - int outx = mapArea.x + mapArea.width - ServerConstants.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; + int outx = mapArea.x + mapArea.width - YamlConfig.config.server.PLAYERNPC_INITIAL_X, outy = mapArea.y + mapArea.height; boolean reorganize = false; int i = initStep; - while(i < ServerConstants.PLAYERNPC_AREA_STEPS) { - int leftPx = mapArea.x + ServerConstants.PLAYERNPC_INITIAL_X, px, py = mapArea.y + ServerConstants.PLAYERNPC_INITIAL_Y; + while(i < YamlConfig.config.server.PLAYERNPC_AREA_STEPS) { + int leftPx = mapArea.x + YamlConfig.config.server.PLAYERNPC_INITIAL_X, px, py = mapArea.y + YamlConfig.config.server.PLAYERNPC_INITIAL_Y; while(py < outy) { px = leftPx; @@ -207,7 +207,7 @@ public class MaplePlayerNPCPositioner { map.getWorldServer().setPlayerNpcMapStep(map.getId(), i); } - if(reorganize && ServerConstants.PLAYERNPC_ORGANIZE_AREA) { + if(reorganize && YamlConfig.config.server.PLAYERNPC_ORGANIZE_AREA) { return reorganizePlayerNpcs(map, i, mmoList); } @@ -226,13 +226,13 @@ public class MaplePlayerNPCPositioner { cx = calcDx(i); cy = calcDy(i); - if(ServerConstants.PLAYERNPC_ORGANIZE_AREA) { + if(YamlConfig.config.server.PLAYERNPC_ORGANIZE_AREA) { otherPlayerNpcs = rearrangePlayerNpcPositions(map, i, mmoList.size()); } } if(i > initStep) { - map.getWorldServer().setPlayerNpcMapStep(map.getId(), ServerConstants.PLAYERNPC_AREA_STEPS - 1); + map.getWorldServer().setPlayerNpcMapStep(map.getId(), YamlConfig.config.server.PLAYERNPC_AREA_STEPS - 1); } return null; } diff --git a/src/server/loot/MapleLootManager.java b/src/server/loot/MapleLootManager.java index d988869823..f1a9485347 100644 --- a/src/server/loot/MapleLootManager.java +++ b/src/server/loot/MapleLootManager.java @@ -56,14 +56,12 @@ public class MapleLootManager { qItemAmount = qCompleteAmount; } - if (qItemAmount <= 0) { - continue; - } + // thanks kvmba for noticing quest items with no required amount failing to be detected as such int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount); if (qItemStatus == 2) { continue; - } /*else if (restricted && qItemStatus == 1) { + } /*else if (restricted && qItemStatus == 1) { // one-of-a-kind loots should be available everytime, thanks onechord for noticing continue; }*/ } /*else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) { // thanks Conrad, Legalize for noticing eligible loots not being available to drop for non-killer parties diff --git a/src/server/maps/AbstractAnimatedMapleMapObject.java b/src/server/maps/AbstractAnimatedMapleMapObject.java index 8c54788a73..e7b1f9d4c1 100644 --- a/src/server/maps/AbstractAnimatedMapleMapObject.java +++ b/src/server/maps/AbstractAnimatedMapleMapObject.java @@ -21,14 +21,32 @@ */ package server.maps; -import java.awt.Point; -import java.util.Collections; -import java.util.List; -import server.movement.AbsoluteLifeMovement; -import server.movement.LifeMovementFragment; +import java.util.Arrays; + +import tools.data.input.ByteArrayByteStream; +import tools.data.input.GenericSeekableLittleEndianAccessor; +import tools.data.input.SeekableLittleEndianAccessor; +import tools.data.output.MaplePacketLittleEndianWriter; public abstract class AbstractAnimatedMapleMapObject extends AbstractMapleMapObject implements AnimatedMapleMapObject { - private int stance; + + static { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter((int) getIdleMovementDataLength()); + mplew.write(1); //movement command count + mplew.write(0); + mplew.writeShort(-1); //x + mplew.writeShort(-1); //y + mplew.writeShort(0); //xwobble + mplew.writeShort(0); //ywobble + mplew.writeShort(0); //fh + mplew.write(-1); //stance + mplew.writeShort(0); //duration + idleMovementPacketData = mplew.getPacket(); + } + + private static final byte[] idleMovementPacketData; + + private int stance; @Override public int getStance() { @@ -45,11 +63,20 @@ public abstract class AbstractAnimatedMapleMapObject extends AbstractMapleMapObj return Math.abs(stance) % 2 == 1; } - public List getIdleMovement() { - AbsoluteLifeMovement alm = new AbsoluteLifeMovement(0, getPosition(), 0, getStance()); - alm.setPixelsPerSecond(new Point(0, 0)); - - List moveUpdate = Collections.singletonList((LifeMovementFragment) alm); - return moveUpdate; + public SeekableLittleEndianAccessor getIdleMovement() { + byte[] movementData = Arrays.copyOf(idleMovementPacketData, idleMovementPacketData.length); + //seems wasteful to create a whole packet writer when only a few values are changed + int x = getPosition().x; + int y = getPosition().y; + movementData[2] = (byte) (x & 0xFF); //x + movementData[3] = (byte) (x >> 8 & 0xFF); + movementData[4] = (byte) (y & 0xFF); //y + movementData[5] = (byte) (y >> 8 & 0xFF); + movementData[12] = (byte) (getStance() & 0xFF); + return new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(movementData)); + } + + public static long getIdleMovementDataLength() { + return 15; } } diff --git a/src/server/maps/AbstractMapleMapObject.java b/src/server/maps/AbstractMapleMapObject.java index 398c373698..24499433e6 100644 --- a/src/server/maps/AbstractMapleMapObject.java +++ b/src/server/maps/AbstractMapleMapObject.java @@ -37,8 +37,7 @@ public abstract class AbstractMapleMapObject implements MapleMapObject { @Override public void setPosition(Point position) { - this.position.x = position.x; - this.position.y = position.y; + this.position.move(position.x, position.y); } @Override diff --git a/src/server/maps/FieldLimit.java b/src/server/maps/FieldLimit.java index 0fe31e9a50..04abc34c35 100644 --- a/src/server/maps/FieldLimit.java +++ b/src/server/maps/FieldLimit.java @@ -42,15 +42,15 @@ public enum FieldLimit { //CASH_WEATHER_CONSUME_LIMIT(0x4000), //NO_PET(0x8000), // Ariant colosseum-related? //ANTI_MACRO_LIMIT(0x10000), // No notes - CANNOTJUMPDOWN(0x20000); + CANNOTJUMPDOWN(0x20000), //SUMMON_NPC_LIMIT(0x40000); // Seems to .. disable Rush if 0x2 is set //......... EVEN MORE LIMITS ............ //SUMMON_NPC_LIMIT(0x40000), - //NO_EXP_DECREASE(0x80000), + NO_EXP_DECREASE(0x80000), //NO_DAMAGE_ON_FALLING(0x100000), //PARCEL_OPEN_LIMIT(0x200000), - //DROP_LIMIT(0x400000), + DROP_LIMIT(0x400000); //ROCKETBOOSTER_LIMIT(0x800000) //lol we don't even have mechanics <3 private long i; diff --git a/src/server/maps/MapMonitor.java b/src/server/maps/MapMonitor.java index 7dc08243f7..0243aa5784 100644 --- a/src/server/maps/MapMonitor.java +++ b/src/server/maps/MapMonitor.java @@ -22,7 +22,6 @@ package server.maps; import java.util.concurrent.ScheduledFuture; -import server.MaplePortal; import server.TimerManager; public class MapMonitor { diff --git a/src/server/maps/MapleDoor.java b/src/server/maps/MapleDoor.java index abbb4c79e7..89dd65b7ba 100644 --- a/src/server/maps/MapleDoor.java +++ b/src/server/maps/MapleDoor.java @@ -23,11 +23,13 @@ package server.maps; import java.awt.Point; import java.util.Collection; + +import config.YamlConfig; import tools.Pair; -import server.MaplePortal; import client.MapleCharacter; -import constants.ServerConstants; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.OverallService; /** * @@ -51,7 +53,7 @@ public class MapleDoor { this.target = owner.getMap(); if(target.canDeployDoor(targetPosition)) { - if(ServerConstants.USE_ENFORCE_MDOOR_POSITION) { + if(YamlConfig.config.server.USE_ENFORCE_MDOOR_POSITION) { posStatus = target.getDoorPositionStatus(targetPosition); } @@ -103,10 +105,12 @@ public class MapleDoor { for (MapleCharacter chr : targetChars) { areaDoor.sendDestroyData(chr.getClient()); + chr.removeVisibleMapObject(areaDoor); } for (MapleCharacter chr : townChars) { townDoor.sendDestroyData(chr.getClient()); + chr.removeVisibleMapObject(townDoor); } owner.removePartyDoor(false); @@ -116,6 +120,7 @@ public class MapleDoor { MapleDoor door = chr.getMainTownDoor(); if (door != null) { townDoor.sendSpawnData(chr.getClient()); + chr.addVisibleMapObject(townDoor); } } } @@ -127,7 +132,9 @@ public class MapleDoor { long effectTimeLeft = 3000 - destroyDoor.getElapsedDeployTime(); // portal deployment effect duration if (effectTimeLeft > 0) { MapleMap town = destroyDoor.getTown(); - town.getChannelServer().registerOverallAction(town.getId(), new Runnable() { + + OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(town.getId(), new Runnable() { @Override public void run() { destroyDoor.broadcastRemoveDoor(owner); // thanks BHB88 for noticing doors crashing players when instantly cancelling buff diff --git a/src/server/maps/MapleDoorObject.java b/src/server/maps/MapleDoorObject.java index 10bf1dd40f..834bebd4d0 100644 --- a/src/server/maps/MapleDoorObject.java +++ b/src/server/maps/MapleDoorObject.java @@ -20,11 +20,14 @@ package server.maps; import java.awt.Point; -import java.util.concurrent.locks.ReentrantReadWriteLock; import client.MapleCharacter; import client.MapleClient; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.server.world.MapleParty; import tools.MaplePacketCreator; @@ -41,9 +44,9 @@ public class MapleDoorObject extends AbstractMapleMapObject { private int linkedPortalId; private Point linkedPos; - private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true); - private ReentrantReadWriteLock.ReadLock rlock = locks.readLock(); - private ReentrantReadWriteLock.WriteLock wlock = locks.writeLock(); + private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true); + private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks); + private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks); public MapleDoorObject(int owner, MapleMap destination, MapleMap origin, int townPortalId, Point targetPosition, Point toPosition) { super(); @@ -87,6 +90,8 @@ public class MapleDoorObject extends AbstractMapleMapObject { public void warp(final MapleCharacter chr) { MapleParty party = chr.getParty(); if (chr.getId() == ownerId || (party != null && party.getMemberById(ownerId) != null)) { + chr.announce(MaplePacketCreator.playPortalSound()); + if(!inTown() && party == null) { chr.changeMap(to, getLinkedPortalId()); } else { diff --git a/src/server/maps/MapleFootholdTree.java b/src/server/maps/MapleFootholdTree.java index 5d897e681d..ef077fdeb7 100644 --- a/src/server/maps/MapleFootholdTree.java +++ b/src/server/maps/MapleFootholdTree.java @@ -169,25 +169,27 @@ public class MapleFootholdTree { } Collections.sort(xMatches); for (MapleFoothold fh : xMatches) { - if (!fh.isWall() && fh.getY1() != fh.getY2()) { - int calcY; - double s1 = Math.abs(fh.getY2() - fh.getY1()); - double s2 = Math.abs(fh.getX2() - fh.getX1()); - double s4 = Math.abs(p.x - fh.getX1()); - double alpha = Math.atan(s2 / s1); - double beta = Math.atan(s1 / s2); - double s5 = Math.cos(alpha) * (s4 / Math.cos(beta)); - if (fh.getY2() < fh.getY1()) { - calcY = fh.getY1() - (int) s5; + if (!fh.isWall()) { + if (fh.getY1() != fh.getY2()) { + int calcY; + double s1 = Math.abs(fh.getY2() - fh.getY1()); + double s2 = Math.abs(fh.getX2() - fh.getX1()); + double s4 = Math.abs(p.x - fh.getX1()); + double alpha = Math.atan(s2 / s1); + double beta = Math.atan(s1 / s2); + double s5 = Math.cos(alpha) * (s4 / Math.cos(beta)); + if (fh.getY2() < fh.getY1()) { + calcY = fh.getY1() - (int) s5; + } else { + calcY = fh.getY1() + (int) s5; + } + if (calcY >= p.y) { + return fh; + } } else { - calcY = fh.getY1() + (int) s5; - } - if (calcY >= p.y) { - return fh; - } - } else if (!fh.isWall()) { - if (fh.getY1() >= p.y) { - return fh; + if (fh.getY1() >= p.y) { + return fh; + } } } } diff --git a/src/server/maps/MapleGenericPortal.java b/src/server/maps/MapleGenericPortal.java index be8b169c87..38d4301808 100644 --- a/src/server/maps/MapleGenericPortal.java +++ b/src/server/maps/MapleGenericPortal.java @@ -23,10 +23,9 @@ package server.maps; import client.MapleClient; import client.MapleCharacter; -import constants.GameConstants; +import constants.game.GameConstants; import java.awt.Point; import scripting.portal.PortalScriptManager; -import server.MaplePortal; import tools.MaplePacketCreator; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index f4f40a6b2d..a1870dd6ae 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -29,9 +29,9 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import client.processor.FredrickProcessor; +import client.processor.npc.FredrickProcessor; import com.mysql.jdbc.Statement; -import constants.ServerConstants; +import config.YamlConfig; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -232,7 +232,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { chr.announce(MaplePacketCreator.updateHiredMerchant(this, chr)); } - if (ServerConstants.USE_ENFORCE_MERCHANT_SAVE) { + if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) { chr.saveCharToDB(false); } } @@ -287,7 +287,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { pItem.setDoesExist(false); } - if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc + if(YamlConfig.config.server.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc announceItemSold(newItem, price, getQuantityLeft(pItem.getItem().getItemId())); } @@ -448,7 +448,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { e.printStackTrace(); } - if (ServerConstants.USE_ENFORCE_MERCHANT_SAVE) { + if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) { c.getPlayer().saveCharToDB(false); } diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 44bd9d6d16..e865e243dc 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -31,9 +31,9 @@ import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; @@ -55,20 +55,25 @@ import java.util.Random; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import java.lang.ref.WeakReference; import net.server.Server; -import net.server.coordinator.MapleMonsterAggroCoordinator; +import net.server.coordinator.world.MapleMonsterAggroCoordinator; import net.server.channel.Channel; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.FaceExpressionService; +import net.server.services.task.channel.MobMistService; +import net.server.services.task.channel.OverallService; +import net.server.world.MapleParty; import net.server.world.World; import scripting.map.MapScriptManager; import server.MapleItemInformationProvider; -import server.MaplePortal; import server.MapleStatEffect; import server.TimerManager; import server.events.gm.MapleCoconut; @@ -101,6 +106,7 @@ public class MapleMap { private static final Map> dropBoundsCache = new HashMap<>(100); private Map mapobjects = new LinkedHashMap<>(); + private Set selfDestructives = new LinkedHashSet<>(); private Collection monsterSpawn = Collections.synchronizedList(new LinkedList()); private Collection allMonsterSpawn = Collections.synchronizedList(new LinkedList()); private AtomicInteger spawnedMonstersOnMap = new AtomicInteger(0); @@ -122,6 +128,7 @@ public class MapleMap { private AtomicInteger runningOid = new AtomicInteger(1000000001); private int returnMapId; private int channel, world; + private int seats; private byte monsterRate; private boolean clock; private boolean boat; @@ -132,8 +139,10 @@ public class MapleMap { private MapleMapEffect mapEffect = null; private boolean everlast = false; private int forcedReturnMap = 999999999; - private long timeLimit; + private int timeLimit; + private long mapTimer; private int decHP = 0; + private float recovery = 1.0f; private int protectItem = 0; private boolean town; private MapleOxQuiz ox; @@ -145,7 +154,6 @@ public class MapleMap { private int fieldLimit = 0; private int mobCapacity = -1; private MapleMonsterAggroCoordinator aggroMonitor = null; // aggroMonitor activity in sync with itemMonitor - private ScheduledFuture mapMonitor = null; private ScheduledFuture itemMonitor = null; private ScheduledFuture expireItemsTask = null; private ScheduledFuture mobSpawnLootTask = null; @@ -157,9 +165,6 @@ public class MapleMap { private MapleCharacter mapOwner = null; private long mapOwnerLastActivityTime = Long.MAX_VALUE; - // HPQ - private int riceCakes = 0; - private int bunnyDamage = 0; // events private boolean eventstarted = false, isMuted = false; private MapleSnowball snowball0 = null; @@ -174,10 +179,10 @@ public class MapleMap { private int timeExpand; //locks - private ReadLock chrRLock; - private WriteLock chrWLock; - private ReadLock objectRLock; - private WriteLock objectWLock; + private MonitoredReadLock chrRLock; + private MonitoredWriteLock chrWLock; + private MonitoredReadLock objectRLock; + private MonitoredWriteLock objectWLock; private Lock lootLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_LOOT, true); @@ -193,13 +198,13 @@ public class MapleMap { if (this.monsterRate == 0) { this.monsterRate = 1; } - final ReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_CHRS, true); - chrRLock = chrLock.readLock(); - chrWLock = chrLock.writeLock(); + final MonitoredReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_CHRS, true); + chrRLock = MonitoredReadLockFactory.createLock(chrLock); + chrWLock = MonitoredWriteLockFactory.createLock(chrLock); - final ReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_OBJS, true); - objectRLock = objectLock.readLock(); - objectWLock = objectLock.writeLock(); + final MonitoredReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_OBJS, true); + objectRLock = MonitoredReadLockFactory.createLock(objectLock); + objectWLock = MonitoredWriteLockFactory.createLock(objectLock); aggroMonitor = new MapleMonsterAggroCoordinator(); } @@ -252,7 +257,7 @@ public class MapleMap { private static double getRangedDistance() { - return(ServerConstants.USE_MAXRANGE ? Double.POSITIVE_INFINITY : 722500); + return(YamlConfig.config.server.USE_MAXRANGE ? Double.POSITIVE_INFINITY : 722500); } public List getMapObjectsInRect(Rectangle box, List types) { @@ -307,7 +312,7 @@ public class MapleMap { this.forcedReturnMap = map; } - public long getTimeLimit() { + public int getTimeLimit() { return timeLimit; } @@ -316,7 +321,7 @@ public class MapleMap { } public int getTimeLeft() { - return (int) ((timeLimit - System.currentTimeMillis()) / 1000); + return (int) ((mapTimer - System.currentTimeMillis()) / 1000); } public void setReactorState() { @@ -388,9 +393,10 @@ public class MapleMap { } public void addMapObject(MapleMapObject mapobject) { + int curOID = getUsableOID(); + objectWLock.lock(); try { - int curOID = getUsableOID(); mapobject.setObjectId(curOID); this.mapobjects.put(curOID, mapobject); } finally { @@ -398,17 +404,27 @@ public class MapleMap { } } + public void addSelfDestructive(MapleMonster mob) { + if (mob.getStats().selfDestruction() != null) { + this.selfDestructives.add(mob.getObjectId()); + } + } + + public boolean removeSelfDestructive(int mapobjectid) { + return this.selfDestructives.remove(mapobjectid); + } + private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery) { spawnAndAddRangedMapObject(mapobject, packetbakery, null); } private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery, SpawnCondition condition) { List inRangeCharacters = new LinkedList<>(); + int curOID = getUsableOID(); chrRLock.lock(); objectWLock.lock(); try { - int curOID = getUsableOID(); mapobject.setObjectId(curOID); this.mapobjects.put(curOID, mapobject); for (MapleCharacter chr : characters) { @@ -556,7 +572,7 @@ public class MapleMap { initial.x = xLimits.right; } - Point ret = calcPointBelow(new Point(initial.x, initial.y - 85)); + Point ret = calcPointBelow(new Point(initial.x, initial.y - 85)); // actual drop ranges: default - 120, explosive - 360 if (ret == null) { ret = bsearchDropPos(initial, fallback); } @@ -742,7 +758,11 @@ public class MapleMap { final List dropEntry = new ArrayList<>(); final List visibleQuestEntry = new ArrayList<>(); final List otherQuestEntry = new ArrayList<>(); - sortDropEntries(ServerConstants.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr); + sortDropEntries(YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr); + + if (dropEntry.isEmpty() && visibleQuestEntry.isEmpty()) { // thanks resinate + return; + } registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); } @@ -776,7 +796,7 @@ public class MapleMap { expireItemsTask.cancel(false); expireItemsTask = null; - if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + if(YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { mobSpawnLootTask.cancel(false); mobSpawnLootTask = null; } @@ -836,16 +856,16 @@ public class MapleMap { cleanItemMonitor(); } } - }, ServerConstants.ITEM_MONITOR_TIME, ServerConstants.ITEM_MONITOR_TIME); + }, YamlConfig.config.server.ITEM_MONITOR_TIME, YamlConfig.config.server.ITEM_MONITOR_TIME); expireItemsTask = TimerManager.getInstance().register(new Runnable() { @Override public void run() { makeDisappearExpiredItemDrops(); } - }, ServerConstants.ITEM_EXPIRE_CHECK, ServerConstants.ITEM_EXPIRE_CHECK); + }, YamlConfig.config.server.ITEM_EXPIRE_CHECK, YamlConfig.config.server.ITEM_EXPIRE_CHECK); - if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + if(YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { lootLock.lock(); try { mobLootEntries.clear(); @@ -887,15 +907,16 @@ public class MapleMap { return droppedItemCount.get(); } - private synchronized void instantiateItemDrop(MapleMapItem mdrop) { - if(droppedItemCount.get() >= ServerConstants.ITEM_LIMIT_ON_MAP) { + private void instantiateItemDrop(MapleMapItem mdrop) { + if(droppedItemCount.get() >= YamlConfig.config.server.ITEM_LIMIT_ON_MAP) { MapleMapObject mapobj; do { + mapobj = null; + objectWLock.lock(); try { - mapobj = registeredDrops.remove(0).get(); - while(mapobj == null) { + while (mapobj == null) { if (registeredDrops.isEmpty()) { break; } @@ -919,7 +940,7 @@ public class MapleMap { } private void registerItemDrop(MapleMapItem mdrop) { - droppedItems.put(mdrop, !everlast ? Server.getInstance().getCurrentTime() + ServerConstants.ITEM_EXPIRE_TIME : Long.MAX_VALUE); + droppedItems.put(mdrop, !everlast ? Server.getInstance().getCurrentTime() + YamlConfig.config.server.ITEM_EXPIRE_TIME : Long.MAX_VALUE); } private void unregisterItemDrop(MapleMapItem mdrop) { @@ -964,7 +985,7 @@ public class MapleMap { private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, List dropEntry, List visibleQuestEntry, List otherQuestEntry, List globalEntry, MapleCharacter chr, MapleMonster mob) { MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); - if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + if(YamlConfig.config.server.USE_SPAWN_LOOT_ON_ANIMATION) { int animationTime = mob.getAnimationTime("die1"); lootLock.lock(); @@ -1318,6 +1339,18 @@ public class MapleMap { return count; } + public int countBosses() { + int count = 0; + + for(MapleMonster mob: getAllMonsters()) { + if (mob.isBoss()) { + count++; + } + } + + return count; + } + public boolean damageMonster(final MapleCharacter chr, final MapleMonster monster, final int damage) { if (monster.getId() == 8800000) { for (MapleMapObject object : chr.getMap().getMapObjects()) { @@ -1331,9 +1364,11 @@ public class MapleMap { } if (monster.isAlive()) { boolean killed = monster.damage(chr, damage, false); - if (monster.getStats().selfDestruction() != null && monster.getStats().selfDestruction().getHp() > -1) {// should work ;p - if (monster.getHp() <= monster.getStats().selfDestruction().getHp()) { - killMonster(monster, chr, true, monster.getStats().selfDestruction().getAction()); + + selfDestruction selfDestr = monster.getStats().selfDestruction(); + if (selfDestr != null && selfDestr.getHp() > -1) {// should work ;p + if (monster.getHp() <= selfDestr.getHp()) { + killMonster(monster, chr, true, selfDestr.getAction()); return true; } } @@ -1371,6 +1406,9 @@ public class MapleMap { spawnedMonstersOnMap.decrementAndGet(); removeMapObject(monster); monster.disposeMapObject(); + if (monster.hasBossHPBar()) { // thanks resinate for noticing boss HPbar not clearing after mob defeat in certain scenarios + broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); + } return true; } finally { @@ -1395,81 +1433,84 @@ public class MapleMap { } } else { if (removeKilledMonsterObject(monster)) { - if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) { - AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher."); - } - - /*if (chr.getQuest(MapleQuest.getInstance(29400)).getStatus().equals(MapleQuestStatus.Status.STARTED)) { - if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) { - //FIX MEDAL SHET - } else if (monster.getStats().getLevel() >= chr.getLevel()) { - } - }*/ - - if (monster.getCP() > 0 && chr.getMap().isCPQMap()) { - chr.gainCP(monster.getCP()); - } - - int buff = monster.getBuffToGive(); - if (buff > -1) { - MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance(); - for (MapleMapObject mmo : this.getPlayers()) { - MapleCharacter character = (MapleCharacter) mmo; - if (character.isAlive()) { - MapleStatEffect statEffect = mii.getItemEffect(buff); - character.getClient().announce(MaplePacketCreator.showOwnBuffEffect(buff, 1)); - broadcastMessage(character, MaplePacketCreator.showBuffeffect(character.getId(), buff, 1), false); - statEffect.applyTo(character); - } + try { + if (monster.getStats().getLevel() >= chr.getLevel() + 30 && !chr.isGM()) { + AutobanFactory.GENERAL.alert(chr, " for killing a " + monster.getName() + " which is over 30 levels higher."); } - } - - if (monster.getId() >= 8800003 && monster.getId() <= 8800010) { - boolean makeZakReal = true; - Collection objects = getMapObjects(); - for (MapleMapObject object : objects) { - MapleMonster mons = getMonsterByOid(object.getObjectId()); - if (mons != null) { - if (mons.getId() >= 8800003 && mons.getId() <= 8800010) { - makeZakReal = false; - break; + + /*if (chr.getQuest(MapleQuest.getInstance(29400)).getStatus().equals(MapleQuestStatus.Status.STARTED)) { + if (chr.getLevel() >= 120 && monster.getStats().getLevel() >= 120) { + //FIX MEDAL SHET + } else if (monster.getStats().getLevel() >= chr.getLevel()) { + } + }*/ + + if (monster.getCP() > 0 && chr.getMap().isCPQMap()) { + chr.gainCP(monster.getCP()); + } + + int buff = monster.getBuffToGive(); + if (buff > -1) { + MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance(); + for (MapleMapObject mmo : this.getPlayers()) { + MapleCharacter character = (MapleCharacter) mmo; + if (character.isAlive()) { + MapleStatEffect statEffect = mii.getItemEffect(buff); + character.getClient().announce(MaplePacketCreator.showOwnBuffEffect(buff, 1)); + broadcastMessage(character, MaplePacketCreator.showBuffeffect(character.getId(), buff, 1), false); + statEffect.applyTo(character); } } } - if (makeZakReal) { - MapleMap map = chr.getMap(); + if (monster.getId() >= 8800003 && monster.getId() <= 8800010) { + boolean makeZakReal = true; + Collection objects = getMapObjects(); for (MapleMapObject object : objects) { - MapleMonster mons = map.getMonsterByOid(object.getObjectId()); + MapleMonster mons = getMonsterByOid(object.getObjectId()); if (mons != null) { - if (mons.getId() == 8800000) { - makeMonsterReal(mons); - mons.aggroUpdateController(); + if (mons.getId() >= 8800003 && mons.getId() <= 8800010) { + makeZakReal = false; break; } } } - } - } + if (makeZakReal) { + MapleMap map = chr.getMap(); - MapleCharacter dropOwner = monster.killBy(chr); - if (withDrops && !monster.dropsDisabled()) { - if (dropOwner == null) { - dropOwner = chr; - } - dropFromMonster(dropOwner, monster, false); - } - - if (monster.hasBossHPBar()) { - for(MapleCharacter mc : this.getAllPlayers()) { - if(mc.getTargetHpBarHash() == monster.hashCode()) { - mc.resetPlayerAggro(); + for (MapleMapObject object : objects) { + MapleMonster mons = map.getMonsterByOid(object.getObjectId()); + if (mons != null) { + if (mons.getId() == 8800000) { + makeMonsterReal(mons); + break; + } + } + } } } - } + + MapleCharacter dropOwner = monster.killBy(chr); + if (withDrops && !monster.dropsDisabled()) { + if (dropOwner == null) { + dropOwner = chr; + } + dropFromMonster(dropOwner, monster, false); + } - monster.dispatchMonsterKilled(true); - broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); + if (monster.hasBossHPBar()) { + for(MapleCharacter mc : this.getAllPlayers()) { + if(mc.getTargetHpBarHash() == monster.hashCode()) { + mc.resetPlayerAggro(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { // thanks resinate for pointing out a memory leak possibly from an exception thrown + monster.dispatchMonsterKilled(true); + broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition()); + } } } } @@ -1565,18 +1606,11 @@ public class MapleMap { public void destroyReactor(int oid) { final MapleReactor reactor = getReactorByOid(oid); - broadcastMessage(MaplePacketCreator.destroyReactor(reactor)); - reactor.cancelReactorTimeout(); - reactor.setAlive(false); - removeMapObject(reactor); - if (reactor.getDelay() > 0) { - registerMapSchedule(new Runnable() { - @Override - public void run() { - respawnReactor(reactor); - } - }, reactor.getDelay()); + if (reactor != null) { + if (reactor.destroy()) { + removeMapObject(reactor); + } } } @@ -1600,9 +1634,14 @@ public class MapleMap { public final void resetReactors(List list) { for (MapleReactor r : list) { + if (r.forceDelayedRespawn()) { // thanks Conrad for suggesting reactor with delay respawning immediately + continue; + } + r.lockReactor(); try { r.resetReactorActions(0); + r.setAlive(true); broadcastMessage(MaplePacketCreator.triggerReactor(r, 0)); } finally { r.unlockReactor(); @@ -1731,7 +1770,7 @@ public class MapleMap { public void destroyNPC(int npcid) { // assumption: there's at most one of the same NPC in a map. List npcs = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.NPC)); - + chrRLock.lock(); objectWLock.lock(); try { @@ -1850,35 +1889,8 @@ public class MapleMap { spawnMonster(mob); } - public void addBunnyHit() { - bunnyDamage++; - if (bunnyDamage > 5) { - broadcastMessage(MaplePacketCreator.serverNotice(6, "The Moon Bunny is feeling sick. Please protect it so it can make delicious rice cakes.")); - bunnyDamage = 0; - } - } - private void monsterItemDrop(final MapleMonster m, long delay) { - final ScheduledFuture monsterItemDrop = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - List chrList = MapleMap.this.getPlayers(); - - if (m.isAlive() && !chrList.isEmpty()) { - MapleCharacter chr = (MapleCharacter) chrList.get(0); - - if (m.getId() == 9300061) { - MapleMap.this.riceCakes++; - MapleMap.this.broadcastMessage(MaplePacketCreator.serverNotice(6, "The Moon Bunny made rice cake number " + (MapleMap.this.riceCakes) + ".")); - } - - dropFromFriendlyMonster(chr, m); - } - } - }, delay, delay); - if (!m.isAlive()) { - monsterItemDrop.cancel(true); - } + m.dropFromFriendlyMonster(delay); } public void spawnFakeMonsterOnGroundBelow(MapleMonster mob, Point pos) { @@ -1907,18 +1919,15 @@ public class MapleMap { spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() { @Override public void sendPackets(MapleClient c) { - c.announce(MaplePacketCreator.spawnMonster(monster, false)); } }); monster.aggroUpdateController(); - - if (monster.hasBossHPBar()) { - broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); - } + updateBossSpawn(monster); spawnedMonstersOnMap.incrementAndGet(); + addSelfDestructive(monster); applyRemoveAfter(monster); } @@ -1954,7 +1963,8 @@ public class MapleMap { public void dismissRemoveAfter(final MapleMonster monster) { Runnable removeAfterAction = monster.popRemoveAfterAction(); if (removeAfterAction != null) { - this.getChannelServer().forceRunOverallAction(mapid, removeAfterAction); + OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.forceRunOverallAction(mapid, removeAfterAction); } } @@ -2016,6 +2026,7 @@ public class MapleMap { }, null); monster.aggroUpdateController(); + updateBossSpawn(monster); if ((monster.getTeam() == 1 || monster.getTeam() == 0) && (isCPQMap() || isCPQMap2())) { List teamS = null; @@ -2032,10 +2043,6 @@ public class MapleMap { } } } - - if (monster.hasBossHPBar()) { - broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); - } if (monster.getDropPeriodTime() > 0) { //9300102 - Watchhog, 9300061 - Moon Bunny (HPQ), 9300093 - Tylus if (monster.getId() == 9300102) { @@ -2052,6 +2059,7 @@ public class MapleMap { } spawnedMonstersOnMap.incrementAndGet(); + addSelfDestructive(monster); applyRemoveAfter(monster); // thanks LightRyuzaki for pointing issues with spawned CWKPQ mobs not applying this } @@ -2074,6 +2082,8 @@ public class MapleMap { spos.y--; monster.setPosition(spos); + monster.setSpawnEffect(effect); + spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() { @Override public void sendPackets(MapleClient c) { @@ -2082,12 +2092,10 @@ public class MapleMap { }); monster.aggroUpdateController(); - - if (monster.hasBossHPBar()) { - broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); - } + updateBossSpawn(monster); spawnedMonstersOnMap.incrementAndGet(); + addSelfDestructive(monster); applyRemoveAfter(monster); } @@ -2102,13 +2110,15 @@ public class MapleMap { }); spawnedMonstersOnMap.incrementAndGet(); + addSelfDestructive(monster); } public void makeMonsterReal(final MapleMonster monster) { monster.setFake(false); broadcastMessage(MaplePacketCreator.makeMonsterReal(monster)); - + monster.broadcastMonsterStatus(); monster.aggroUpdateController(); + updateBossSpawn(monster); } public void spawnReactor(final MapleReactor reactor) { @@ -2119,26 +2129,17 @@ public class MapleMap { c.announce(reactor.makeSpawnData()); } }); - - } - - private void respawnReactor(final MapleReactor reactor) { - reactor.lockReactor(); - try { - reactor.resetReactorActions(0); - reactor.setAlive(true); - } finally { - reactor.unlockReactor(); - } - - spawnReactor(reactor); } public void spawnDoor(final MapleDoorObject door) { spawnAndAddRangedMapObject(door, new DelayedPacketCreation() { @Override public void sendPackets(MapleClient c) { - door.sendSpawnData(c, false); + MapleCharacter chr = c.getPlayer(); + if (chr != null) { + door.sendSpawnData(c, false); + chr.addVisibleMapObject(door); + } } }, new SpawnCondition() { @Override @@ -2219,7 +2220,8 @@ public class MapleMap { } }; - this.getChannelServer().registerMobMistCancelAction(mapid, mistSchedule, duration); + MobMistService service = (MobMistService) this.getChannelServer().getServiceAccess(ChannelServices.MOB_MIST); + service.registerMobMistCancelAction(mapid, mistSchedule, duration); } public void spawnKite(final MapleKite kite) { @@ -2234,7 +2236,7 @@ public class MapleMap { } }; - getWorldServer().registerTimedMapObject(expireKite, ServerConstants.KITE_EXPIRE_TIME); + getWorldServer().registerTimedMapObject(expireKite, YamlConfig.config.server.KITE_EXPIRE_TIME); } public final void spawnItemDrop(final MapleMapObject dropper, final MapleCharacter owner, final Item item, Point pos, final boolean ffaDrop, final boolean playerDrop) { @@ -2242,6 +2244,11 @@ public class MapleMap { } public final void spawnItemDrop(final MapleMapObject dropper, final MapleCharacter owner, final Item item, Point pos, final byte dropType, final boolean playerDrop) { + if (FieldLimit.DROP_LIMIT.check(this.getFieldLimit())) { // thanks Conrad for noticing some maps shouldn't have loots available + this.disappearingItemDrop(dropper, owner, item, pos); + return; + } + final Point droppos = calcDropPos(pos, pos); final MapleMapItem mdrop = new MapleMapItem(item, droppos, dropper, owner, owner.getClient(), dropType, playerDrop); mdrop.setDropTime(Server.getInstance().getCurrentTime()); @@ -2313,7 +2320,8 @@ public class MapleMap { } private void registerMapSchedule(Runnable r, long delay) { - this.getChannelServer().registerOverallAction(mapid, r, delay); + OverallService service = (OverallService) this.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(mapid, r, delay); } private void activateItemReactors(final MapleMapItem drop, final MapleClient c) { @@ -2406,8 +2414,7 @@ public class MapleMap { return null; } - private void addPartyMemberInternal(MapleCharacter chr) { - int partyid = chr.getPartyId(); + private void addPartyMemberInternal(MapleCharacter chr, int partyid) { if (partyid == -1) { return; } @@ -2423,8 +2430,7 @@ public class MapleMap { } } - private void removePartyMemberInternal(MapleCharacter chr) { - int partyid = chr.getPartyId(); + private void removePartyMemberInternal(MapleCharacter chr, int partyid) { if (partyid == -1) { return; } @@ -2439,19 +2445,19 @@ public class MapleMap { } } - public void addPartyMember(MapleCharacter chr) { + public void addPartyMember(MapleCharacter chr, int partyid) { chrWLock.lock(); try { - addPartyMemberInternal(chr); + addPartyMemberInternal(chr, partyid); } finally { chrWLock.unlock(); } } - public void removePartyMember(MapleCharacter chr) { + public void removePartyMember(MapleCharacter chr, int partyid) { chrWLock.lock(); try { - removePartyMemberInternal(chr); + removePartyMemberInternal(chr, partyid); } finally { chrWLock.unlock(); } @@ -2468,12 +2474,15 @@ public class MapleMap { public void addPlayer(final MapleCharacter chr) { int chrSize; + MapleParty party = chr.getParty(); chrWLock.lock(); try { characters.add(chr); chrSize = characters.size(); - addPartyMemberInternal(chr); + if (party != null && party.getMemberById(chr.getId()) != null) { + addPartyMemberInternal(chr, party.getId()); + } itemMonitorTimeout = 1; } finally { chrWLock.unlock(); @@ -2482,22 +2491,23 @@ public class MapleMap { chr.setMapId(mapid); chr.updateActiveEffects(); + MapScriptManager msm = MapScriptManager.getInstance(); if (chrSize == 1) { if(!hasItemMonitor()) { startItemMonitor(); aggroMonitor.startAggroCoordinator(); } - if (onFirstUserEnter.length() != 0 && !chr.hasEntered(onFirstUserEnter, mapid) && MapScriptManager.getInstance().scriptExists(onFirstUserEnter, true)) { - chr.enteredScript(onFirstUserEnter, mapid); - MapScriptManager.getInstance().runMapScript(chr.getClient(), onFirstUserEnter, true); + if (onFirstUserEnter.length() != 0) { + msm.runMapScript(chr.getClient(), "onFirstUserEnter/" + onFirstUserEnter, true); } } if (onUserEnter.length() != 0) { if (onUserEnter.equals("cygnusTest") && (mapid < 913040000 || mapid > 913040006)) { chr.saveLocation("INTRO"); } - MapScriptManager.getInstance().runMapScript(chr.getClient(), onUserEnter, false); + + msm.runMapScript(chr.getClient(), "onUserEnter/" + onUserEnter, false); } if (FieldLimit.CANNOTUSEMOUNTS.check(fieldLimit) && chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) { chr.cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING); @@ -2595,6 +2605,7 @@ public class MapleMap { break; } } + chr.commitExcludedItems(); // thanks OishiiKawaiiDesu for noticing pet item ignore registry erasing upon changing maps if (chr.getMonsterCarnival() != null) { chr.getClient().announce(MaplePacketCreator.getClock(chr.getMonsterCarnival().getTimeLeftSeconds())); @@ -2727,12 +2738,12 @@ public class MapleMap { return portal != null ? portal : getPortal(0); } - public MaplePortal findClosestWarpPortal(Point from) { + public MaplePortal findClosestTeleportPortal(Point from) { MaplePortal closest = null; double shortestDistance = Double.POSITIVE_INFINITY; for (MaplePortal portal : portals.values()) { double distance = portal.getPosition().distanceSq(from); - if (portal.getType() == MaplePortal.MAP_PORTAL && distance < shortestDistance && portal.getTargetMapId() == 999999999) { + if (portal.getType() == MaplePortal.TELEPORT_PORTAL && distance < shortestDistance && portal.getTargetMapId() != 999999999) { closest = portal; shortestDistance = distance; } @@ -2797,12 +2808,17 @@ public class MapleMap { public void removePlayer(MapleCharacter chr) { Channel cserv = chr.getClient().getChannelServer(); - cserv.unregisterFaceExpression(mapid, chr); + FaceExpressionService service = (FaceExpressionService) this.getChannelServer().getServiceAccess(ChannelServices.FACE_EXPRESSION); + service.unregisterFaceExpression(mapid, chr); chr.unregisterChairBuff(); + MapleParty party = chr.getParty(); chrWLock.lock(); try { - removePartyMemberInternal(chr); + if (party != null && party.getMemberById(chr.getId()) != null) { + removePartyMemberInternal(chr, party.getId()); + } + characters.remove(chr); } finally { chrWLock.unlock(); @@ -2915,6 +2931,21 @@ public class MapleMap { } } + private void updateBossSpawn(MapleMonster monster) { + if (monster.hasBossHPBar()) { + broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); + } + if (monster.isBoss()) { + if (unclaimOwnership() != null) { + String mobName = MapleMonsterInformationProvider.getInstance().getMobNameFromId(monster.getId()); + if (mobName != null) { + mobName = mobName.trim(); + this.dropMessage(5, "This lawn has been taken siege by " + mobName + "'s forces and will be kept hold until their defeat."); + } + } + } + } + public void broadcastBossHpMessage(MapleMonster mm, int bossHash, final byte[] packet) { broadcastBossHpMessage(mm, bossHash, null, packet, Double.POSITIVE_INFINITY, null); } @@ -3036,8 +3067,8 @@ public class MapleMap { } } - private void sendObjectPlacement(MapleClient mapleClient) { - MapleCharacter chr = mapleClient.getPlayer(); + private void sendObjectPlacement(MapleClient c) { + MapleCharacter chr = c.getPlayer(); Collection objects; objectRLock.lock(); @@ -3049,7 +3080,7 @@ public class MapleMap { for (MapleMapObject o : objects) { if (isNonRangedType(o.getType())) { - o.sendSpawnData(mapleClient); + o.sendSpawnData(c); } else if (o.getType() == MapleMapObjectType.MONSTER) { ((MapleMonster) o).aggroUpdateController(); } else if (o.getType() == MapleMapObjectType.SUMMON) { @@ -3270,9 +3301,9 @@ public class MapleMap { public MapleCharacter getCharacterById(int id) { chrRLock.lock(); try { - for (MapleCharacter c : this.characters) { - if (c.getId() == id) { - return c; + for (MapleCharacter chr : this.characters) { + if (chr.getId() == id) { + return chr; } } } finally { @@ -3430,8 +3461,8 @@ public class MapleMap { public void broadcastNightEffect() { chrRLock.lock(); try { - for (MapleCharacter c : characters) { - sendNightEffect(c); + for (MapleCharacter chr : this.characters) { + sendNightEffect(chr); } } finally { chrRLock.unlock(); @@ -3441,9 +3472,9 @@ public class MapleMap { public MapleCharacter getCharacterByName(String name) { chrRLock.lock(); try { - for (MapleCharacter c : this.characters) { - if (c.getName().toLowerCase().equals(name.toLowerCase())) { - return c; + for (MapleCharacter chr : this.characters) { + if (chr.getName().toLowerCase().equals(name.toLowerCase())) { + return chr; } } } finally { @@ -3534,7 +3565,7 @@ public class MapleMap { @Override public void run() { - reactor.lockReactor(); + reactor.hitLockReactor(); try { if(reactor.getReactorType() == 100) { if (reactor.getShouldCollect() == true && mapitem != null && mapitem == getMapObject(mapitem.getObjectId())) { @@ -3557,12 +3588,14 @@ public class MapleMap { if (reactor.getDelay() > 0) { MapleMap reactorMap = reactor.getMap(); - reactorMap.getChannelServer().registerOverallAction(reactorMap.getId(), new Runnable() { + OverallService service = (OverallService) reactorMap.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(reactorMap.getId(), new Runnable() { @Override public void run() { reactor.lockReactor(); try { reactor.resetReactorActions(0); + reactor.setAlive(true); broadcastMessage(MaplePacketCreator.triggerReactor(reactor, 0)); } finally { reactor.unlockReactor(); @@ -3576,7 +3609,7 @@ public class MapleMap { } } } finally { - reactor.unlockReactor(); + reactor.hitUnlockReactor(); } } } @@ -3688,7 +3721,7 @@ public class MapleMap { System.out.println("----------------------------------"); */ - if(ServerConstants.USE_ENABLE_FULL_RESPAWN) { + if(YamlConfig.config.server.USE_ENABLE_FULL_RESPAWN) { return (monsterSpawn.size() - spawnedMonstersOnMap.get()); } @@ -3796,6 +3829,14 @@ public class MapleMap { public void setHPDecProtect(int delta) { this.protectItem = delta; } + + public float getRecovery() { + return recovery; + } + + public void setRecovery(float recRate) { + recovery = recRate; + } private int hasBoat() { return !boat ? 0 : (docked ? 1 : 2); @@ -3812,6 +3853,14 @@ public class MapleMap { public boolean getDocked() { return this.docked; } + + public void setSeats(int seats) { + this.seats = seats; + } + + public int getSeats() { + return seats; + } public void broadcastGMMessage(MapleCharacter source, final byte[] packet, boolean repeatToSource) { broadcastGMMessage(repeatToSource ? null : source, packet, Double.POSITIVE_INFINITY, source.getPosition()); @@ -3904,35 +3953,7 @@ public class MapleMap { this.broadcastMessage(MaplePacketCreator.removeItemFromMap(i.getObjectId(), 0, 0)); } } - - public void addMapTimer(int time) { - timeLimit = System.currentTimeMillis() + (time * 1000); - broadcastMessage(MaplePacketCreator.getClock(time)); - mapMonitor = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - if (timeLimit != 0 && timeLimit < System.currentTimeMillis()) { - warpEveryone(getForcedReturnId()); - } - - if (getCharacters().isEmpty()) { - resetReactors(); - killAllMonsters(); - clearDrops(); - timeLimit = 0; - if (mapid >= 922240100 && mapid <= 922240119) { - toggleHiddenNPC(9001108); - } - - if (mapMonitor != null) { - mapMonitor.cancel(true); - mapMonitor = null; - } - } - } - }, 1000); - } - + public void setFieldLimit(int fieldLimit) { this.fieldLimit = fieldLimit; } @@ -3941,10 +3962,6 @@ public class MapleMap { return fieldLimit; } - public void resetRiceCakes() { - this.riceCakes = 0; - } - public void allowSummonState(boolean b) { MapleMap.this.allowSummons = b; } @@ -4079,7 +4096,7 @@ public class MapleMap { return this.mapid >= 109010000 && this.mapid < 109050000 || this.mapid > 109050001 && this.mapid <= 109090000; } - public void timeMob(int id, String msg) { + public void setTimeMob(int id, String msg) { timeMob = new Pair<>(id, msg); } @@ -4206,8 +4223,8 @@ public class MapleMap { @Override public void monsterDamaged(MapleCharacter from, int trueDmg) { - ht.applyAndGetHpDamage(trueDmg, true); - ht.broadcastMobHpBar(from); + // thanks Halcyon for noticing HT not dropping loots due to propagated damage not registering attacker + ht.applyFakeDamage(from, trueDmg, true); } @Override @@ -4222,7 +4239,9 @@ public class MapleMap { public boolean claimOwnership(MapleCharacter chr) { if (mapOwner == null) { - mapOwner = chr; + this.mapOwner = chr; + chr.setOwnedMap(this); + mapOwnerLastActivityTime = Server.getInstance().getCurrentTime(); getChannelServer().registerOwnedMap(this); @@ -4232,9 +4251,16 @@ public class MapleMap { } } + public MapleCharacter unclaimOwnership() { + MapleCharacter lastOwner = this.mapOwner; + return unclaimOwnership(lastOwner) ? lastOwner : null; + } + public boolean unclaimOwnership(MapleCharacter chr) { - if (mapOwner == chr) { - mapOwner = null; + if (chr != null && mapOwner == chr) { + this.mapOwner = null; + chr.setOwnedMap(null); + mapOwnerLastActivityTime = Long.MAX_VALUE; getChannelServer().unregisterOwnedMap(this); @@ -4266,7 +4292,7 @@ public class MapleMap { public void checkMapOwnerActivity() { long timeNow = Server.getInstance().getCurrentTime(); if (timeNow - mapOwnerLastActivityTime > 60000) { - if (unclaimOwnership(mapOwner)) { + if (unclaimOwnership() != null) { this.dropMessage(5, "This lawn is now free real estate."); } } @@ -4533,11 +4559,6 @@ public class MapleMap { portals.clear(); mapEffect = null; - if(mapMonitor != null) { - mapMonitor.cancel(false); - mapMonitor = null; - } - chrWLock.lock(); try { aggroMonitor.dispose(); diff --git a/src/server/maps/MapleMapFactory.java b/src/server/maps/MapleMapFactory.java index e3c33db3fa..dd483131d2 100644 --- a/src/server/maps/MapleMapFactory.java +++ b/src/server/maps/MapleMapFactory.java @@ -31,12 +31,10 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import provider.MapleData; import provider.MapleDataProvider; import provider.MapleDataProviderFactory; import provider.MapleDataTool; -import server.PortalFactory; import server.life.AbstractLoadedMapleLife; import server.life.MapleLifeFactory; import server.life.MapleMonster; @@ -49,8 +47,6 @@ import tools.StringUtil; public class MapleMapFactory { - private static Map mapRecoveryRateCache = new HashMap<>(); - private static MapleData nameData; private static MapleDataProvider mapSource; @@ -58,7 +54,7 @@ public class MapleMapFactory { nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img"); mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz")); } - + private static void loadLifeFromWz(MapleMap map, MapleData mapData) { for (MapleData life : mapData.getChildByPath("life")) { life.getName(); @@ -166,14 +162,13 @@ public class MapleMapFactory { map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0)); map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000)); - PortalFactory portalFactory = new PortalFactory(); + MaplePortalFactory portalFactory = new MaplePortalFactory(); for (MapleData portal : mapData.getChildByPath("portal")) { map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal)); } MapleData timeMob = infoData.getChildByPath("timeMob"); if (timeMob != null) { - map.timeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")), - MapleDataTool.getString(timeMob.getChildByPath("message"))); + map.setTimeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")), MapleDataTool.getString(timeMob.getChildByPath("message"))); } int bounds[] = new int[4]; @@ -243,6 +238,10 @@ public class MapleMapFactory { map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1))); } } + if (mapData.getChildByPath("seat") != null) { + int seats = mapData.getChildByPath("seat").getChildren().size(); + map.setSeats(seats); + } if (event == null) { try { Connection con = DatabaseConnection.getConnection(); @@ -275,10 +274,10 @@ public class MapleMapFactory { MapleData mcData = mapData.getChildByPath("monsterCarnival"); if (mcData != null) { map.setDeathCP(MapleDataTool.getIntConvert("deathCP", mcData, 0)); - map.setMaxMobs(MapleDataTool.getIntConvert("mobGenMax", mcData, 0)); + map.setMaxMobs(MapleDataTool.getIntConvert("mobGenMax", mcData, 20)); // thanks Atoot for noticing CPQ1 bf. 3 & 4 not accepting spawns due to undefined limits, Lame for noticing a need to cap mob spawns even on such undefined limits map.setTimeDefault(MapleDataTool.getIntConvert("timeDefault", mcData, 0)); map.setTimeExpand(MapleDataTool.getIntConvert("timeExpand", mcData, 0)); - map.setMaxReactors(MapleDataTool.getIntConvert("guardianGenMax", mcData, 0)); + map.setMaxReactors(MapleDataTool.getIntConvert("guardianGenMax", mcData, 16)); MapleData guardianGenData = mcData.getChildByPath("guardianGenPos"); for (MapleData node : guardianGenData.getChildren()) { GuardianSpawnPoint pt = new GuardianSpawnPoint(new Point(MapleDataTool.getIntConvert("x", node), MapleDataTool.getIntConvert("y", node))); @@ -310,9 +309,10 @@ public class MapleMapFactory { } } } + try { - map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), "")); - map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), "")); + map.setMapName(loadPlaceName(mapid)); + map.setStreetName(loadStreetName(mapid)); } catch (Exception e) { if (mapid / 1000 != 1020) { // explorer job introduction scenes e.printStackTrace(); @@ -333,11 +333,10 @@ public class MapleMapFactory { map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1)); map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0)); map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs? - + MapleData recData = infoData.getChildByPath("recovery"); if (recData != null) { - float recoveryRate = MapleDataTool.getFloat(recData); - mapRecoveryRateCache.put(mapid, recoveryRate); + map.setRecovery(MapleDataTool.getFloat(recData)); } HashMap backTypes = new HashMap<>(); @@ -435,9 +434,21 @@ public class MapleMapFactory { builder.append("/").append(mapid); return builder.toString(); } - - public static float getMapRecoveryRate(int mapid) { - Float recRate = mapRecoveryRateCache.get(mapid); - return recRate != null ? recRate : 1.0f; + + public static String loadPlaceName(int mapid) throws Exception { + try { + return MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""); + } catch (Exception e) { + return ""; + } } + + public static String loadStreetName(int mapid) throws Exception { + try { + return MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""); + } catch (Exception e) { + return ""; + } + } + } diff --git a/src/server/maps/MapleMapManager.java b/src/server/maps/MapleMapManager.java index 0943c1cec6..11d1df4216 100644 --- a/src/server/maps/MapleMapManager.java +++ b/src/server/maps/MapleMapManager.java @@ -19,17 +19,15 @@ */ package server.maps; -import constants.ServerConstants; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; import scripting.event.EventInstanceManager; -import server.TimerManager; public class MapleMapManager { @@ -38,26 +36,17 @@ public class MapleMapManager { private Map maps = new HashMap<>(); - private ScheduledFuture updateTask; - - private ReadLock mapsRLock; - private WriteLock mapsWLock; + private MonitoredReadLock mapsRLock; + private MonitoredWriteLock mapsWLock; public MapleMapManager(EventInstanceManager eim, int world, int channel) { this.world = world; this.channel = channel; this.event = eim; - ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER); - this.mapsRLock = rrwl.readLock(); - this.mapsWLock = rrwl.writeLock(); - - updateTask = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - updateMaps(); - } - }, ServerConstants.RESPAWN_INTERVAL); + MonitoredReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER); + this.mapsRLock = MonitoredReadLockFactory.createLock(rrwl); + this.mapsWLock = MonitoredWriteLockFactory.createLock(rrwl); } public MapleMap resetMap(int mapid) { @@ -136,7 +125,7 @@ public class MapleMapManager { } } - private void updateMaps() { + public void updateMaps() { for (MapleMap map : getMaps().values()) { map.respawn(); map.mobMpRecovery(); @@ -144,11 +133,6 @@ public class MapleMapManager { } public void dispose() { - if (updateTask != null) { - updateTask.cancel(false); - updateTask = null; - } - for (MapleMap map : getMaps().values()) { map.dispose(); } @@ -156,7 +140,4 @@ public class MapleMapManager { this.event = null; } - public static float getMapRecoveryRate(int mapid) { - return MapleMapFactory.getMapRecoveryRate(mapid); - } } diff --git a/src/server/maps/MapleMapPortal.java b/src/server/maps/MapleMapPortal.java index 5e73f96258..b06f99b489 100644 --- a/src/server/maps/MapleMapPortal.java +++ b/src/server/maps/MapleMapPortal.java @@ -21,8 +21,6 @@ */ package server.maps; -import server.MaplePortal; - public class MapleMapPortal extends MapleGenericPortal { public MapleMapPortal() { super(MaplePortal.MAP_PORTAL); diff --git a/src/server/maps/MapleMiniDungeon.java b/src/server/maps/MapleMiniDungeon.java index 271eceefb8..125c2a8319 100644 --- a/src/server/maps/MapleMiniDungeon.java +++ b/src/server/maps/MapleMiniDungeon.java @@ -42,26 +42,14 @@ public class MapleMiniDungeon { int baseMap; long expireTime; - public MapleMiniDungeon(int base, int durationMin) { + public MapleMiniDungeon(int base, long timeLimit) { baseMap = base; - expireTime = durationMin * 60 * 1000; + expireTime = timeLimit * 1000; timeoutTask = TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { - lock.lock(); - try { - List lchr = new ArrayList<>(players); - - for(MapleCharacter chr : lchr) { - chr.changeMap(baseMap); - } - - dispose(); - timeoutTask = null; - } finally { - lock.unlock(); - } + close(); } }, expireTime); @@ -95,8 +83,28 @@ public class MapleMiniDungeon { dispose(); return false; } - - return true; + } finally { + lock.unlock(); + } + + if (chr.isPartyLeader()) { // thanks Conrad for noticing party is not sent out of the MD as soon as leader leaves it + close(); + } + + return true; + } + + public void close() { + lock.lock(); + try { + List lchr = new ArrayList<>(players); + + for(MapleCharacter chr : lchr) { + chr.changeMap(baseMap); + } + + dispose(); + timeoutTask = null; } finally { lock.unlock(); } diff --git a/src/server/maps/MapleMiniGame.java b/src/server/maps/MapleMiniGame.java index b0fd6c843b..30b7066fe2 100644 --- a/src/server/maps/MapleMiniGame.java +++ b/src/server/maps/MapleMiniGame.java @@ -89,8 +89,8 @@ public class MapleMiniGame extends AbstractMapleMapObject { return visitor == null; } - public boolean isOwner(MapleCharacter c) { - return owner.equals(c); + public boolean isOwner(MapleCharacter chr) { + return owner.equals(chr); } public void addVisitor(MapleCharacter challenger) { diff --git a/src/server/maps/MaplePlayerShop.java b/src/server/maps/MaplePlayerShop.java index ed3ba67bc7..c895ae998a 100644 --- a/src/server/maps/MaplePlayerShop.java +++ b/src/server/maps/MaplePlayerShop.java @@ -120,8 +120,8 @@ public class MaplePlayerShop extends AbstractMapleMapObject { } } - public boolean isOwner(MapleCharacter c) { - return owner.equals(c); + public boolean isOwner(MapleCharacter chr) { + return owner.equals(chr); } private void addVisitor(MapleCharacter visitor) { diff --git a/src/server/MaplePortal.java b/src/server/maps/MaplePortal.java similarity index 96% rename from src/server/MaplePortal.java rename to src/server/maps/MaplePortal.java index 3e0c02c096..1cc9af4e41 100644 --- a/src/server/MaplePortal.java +++ b/src/server/maps/MaplePortal.java @@ -19,12 +19,13 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package server; +package server.maps; import java.awt.Point; import client.MapleClient; public interface MaplePortal { + public final int TELEPORT_PORTAL = 1; public final int MAP_PORTAL = 2; public final int DOOR_PORTAL = 6; public static boolean OPEN = true; diff --git a/src/server/PortalFactory.java b/src/server/maps/MaplePortalFactory.java similarity index 96% rename from src/server/PortalFactory.java rename to src/server/maps/MaplePortalFactory.java index fdb59939bd..816a1f09a3 100644 --- a/src/server/PortalFactory.java +++ b/src/server/maps/MaplePortalFactory.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 server; +package server.maps; import java.awt.Point; @@ -28,10 +28,10 @@ import provider.MapleDataTool; import server.maps.MapleGenericPortal; import server.maps.MapleMapPortal; -public class PortalFactory { +public class MaplePortalFactory { private int nextDoorPortal; - public PortalFactory() { + public MaplePortalFactory() { nextDoorPortal = 0x80; } diff --git a/src/server/maps/MapleReactor.java b/src/server/maps/MapleReactor.java index 38f2128d7e..0202127f7e 100644 --- a/src/server/maps/MapleReactor.java +++ b/src/server/maps/MapleReactor.java @@ -22,8 +22,7 @@ package server.maps; import client.MapleClient; -import client.status.MonsterStatus; -import constants.ServerConstants; +import config.YamlConfig; import java.awt.Rectangle; import java.util.List; @@ -37,6 +36,8 @@ import server.TimerManager; import tools.MaplePacketCreator; import tools.Pair; import net.server.audit.locks.MonitoredLockType; +import net.server.services.type.ChannelServices; +import net.server.services.task.channel.OverallService; import server.partyquest.GuardianSpawnPoint; /** @@ -57,6 +58,7 @@ public class MapleReactor extends AbstractMapleMapObject { private boolean shouldCollect; private boolean attackHit; private ScheduledFuture timeoutTask = null; + private Runnable delayedRespawnRun = null; private GuardianSpawnPoint guardian = null; private byte facingDirection = 0; private Lock reactorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR, true); @@ -84,6 +86,16 @@ public class MapleReactor extends AbstractMapleMapObject { public void unlockReactor() { reactorLock.unlock(); } + + public void hitLockReactor() { + hitLock.lock(); + reactorLock.lock(); + } + + public void hitUnlockReactor() { + reactorLock.unlock(); + hitLock.unlock(); + } public void setState(byte state) { this.state = state; @@ -165,7 +177,9 @@ public class MapleReactor extends AbstractMapleMapObject { @Override public void sendSpawnData(MapleClient client) { - client.announce(makeSpawnData()); + if (this.isAlive()) { + client.announce(makeSpawnData()); + } } public final byte[] makeSpawnData() { @@ -253,7 +267,7 @@ public class MapleReactor extends AbstractMapleMapObject { cancelReactorTimeout(); attackHit = wHit; - if (ServerConstants.USE_DEBUG == true) { + if (YamlConfig.config.server.USE_DEBUG == true) { c.getPlayer().dropMessage(5, "Hitted REACTOR " + this.getId() + " with POS " + charPos + " , STANCE " + stance + " , SkillID " + skillid + " , STATE " + stats.getType(state) + " STATESIZE " + stats.getStateSize(state)); } ReactorScriptManager.getInstance().onHit(c, this); @@ -318,6 +332,74 @@ public class MapleReactor extends AbstractMapleMapObject { e.printStackTrace(); } } + + public boolean destroy() { + if (reactorLock.tryLock()) { + try { + boolean alive = this.isAlive(); + if (alive) { + this.setAlive(false); + this.cancelReactorTimeout(); + + if (this.getDelay() > 0) { + this.delayedRespawn(); + } + } else if (this.inDelayedRespawn()) { + return false; + } else { + return true; // reactor neither alive nor in delayed respawn, remove map object allowed + } + } finally { + reactorLock.unlock(); + } + } + + map.broadcastMessage(MaplePacketCreator.destroyReactor(this)); + return false; + } + + private void respawn() { + this.lockReactor(); + try { + this.resetReactorActions(0); + this.setAlive(true); + } finally { + this.unlockReactor(); + } + + map.broadcastMessage(this.makeSpawnData()); + } + + public void delayedRespawn() { + Runnable r = new Runnable() { + @Override + public void run() { + delayedRespawnRun = null; + respawn(); + } + }; + + delayedRespawnRun = r; + + OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.registerOverallAction(map.getId(), r, this.getDelay()); + } + + public boolean forceDelayedRespawn() { + Runnable r = delayedRespawnRun; + + if (r != null) { + OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL); + service.forceRunOverallAction(map.getId(), r); + return true; + } else { + return false; + } + } + + public boolean inDelayedRespawn() { + return delayedRespawnRun != null; + } public Rectangle getArea() { return new Rectangle(getPosition().x + stats.getTL().x, getPosition().y + stats.getTL().y, stats.getBR().x - stats.getTL().x, stats.getBR().y - stats.getTL().y); diff --git a/src/server/minigame/MapleRockPaperScissor.java b/src/server/minigame/MapleRockPaperScissor.java new file mode 100644 index 0000000000..16832abcc0 --- /dev/null +++ b/src/server/minigame/MapleRockPaperScissor.java @@ -0,0 +1,84 @@ +package server.minigame; + +import client.MapleClient; +import client.inventory.Item; +import client.inventory.manipulator.MapleInventoryManipulator; +import tools.MaplePacketCreator; +import tools.Randomizer; + +/** + * @Author Arnah + * @Website http://Vertisy.ca/ + * @since Aug 15, 2016 + */ +public class MapleRockPaperScissor{ + + private int round = 0; + private boolean ableAnswer = true; + private boolean win = false; + + public MapleRockPaperScissor(final MapleClient c, final byte mode){ + c.announce(MaplePacketCreator.rpsMode((byte) (9 + mode))); + if(mode == 0){ + c.getPlayer().gainMeso(-1000, true, true, true); + } + } + + public final boolean answer(final MapleClient c, final int answer){ + if(ableAnswer && !win && answer >= 0 && answer <= 2){ + final int response = Randomizer.nextInt(3); + if(response == answer){ + c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) round)); + // dont do anything. they can still answer once a draw + }else if((answer == 0 && response == 2) || (answer == 1 && response == 0) || (answer == 2 && response == 1)){ // they win + c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) (round + 1))); + ableAnswer = false; + win = true; + }else{ // they lose + c.announce(MaplePacketCreator.rpsSelection((byte) response, (byte) -1)); + ableAnswer = false; + } + return true; + } + reward(c); + return false; + } + + public final boolean timeOut(final MapleClient c){ + if(ableAnswer && !win){ + ableAnswer = false; + c.announce(MaplePacketCreator.rpsMode((byte) 0x0A)); + return true; + } + reward(c); + return false; + } + + public final boolean nextRound(final MapleClient c){ + if(win){ + round++; + if(round < 10){ + win = false; + ableAnswer = true; + c.announce(MaplePacketCreator.rpsMode((byte) 0x0C)); + return true; + } else { + round = 10; + } + } + reward(c); + return false; + } + + public final void reward(final MapleClient c){ + if(win){ + MapleInventoryManipulator.addFromDrop(c, new Item(4031332 + round, (short) 0, (short) 1), true); + } + c.getPlayer().setRPS(null); + } + + public final void dispose(final MapleClient c){ + reward(c); + c.announce(MaplePacketCreator.rpsMode((byte) 0x0D)); + } +} diff --git a/src/server/partyquest/AriantColiseum.java b/src/server/partyquest/AriantColiseum.java index 5adfacd6a3..0addb32380 100644 --- a/src/server/partyquest/AriantColiseum.java +++ b/src/server/partyquest/AriantColiseum.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; import client.MapleCharacter; -import constants.GameConstants; +import constants.game.GameConstants; import server.TimerManager; import server.expeditions.MapleExpedition; import server.expeditions.MapleExpeditionType; @@ -272,7 +272,7 @@ public class AriantColiseum { } private void enterKingsRoom() { - map.getChannelServer().removeExpedition(exped); + exped.removeChannelExpedition(map.getChannelServer()); cancelAriantSchedules(); for (MapleCharacter chr : map.getAllPlayers()) { diff --git a/src/server/partyquest/MapleCarnivalFactory.java b/src/server/partyquest/MapleCarnivalFactory.java index 1ee329aae3..c8a3f6e67c 100644 --- a/src/server/partyquest/MapleCarnivalFactory.java +++ b/src/server/partyquest/MapleCarnivalFactory.java @@ -59,7 +59,7 @@ public class MapleCarnivalFactory { if (multi) { return skills.get(multiTargetedSkills.get((int) (Math.random() * multiTargetedSkills.size()))); } else { - return skills.get(multiTargetedSkills.get((int) (Math.random() * multiTargetedSkills.size()))); + return skills.get(singleTargetedSkills.get((int) (Math.random() * singleTargetedSkills.size()))); } } diff --git a/src/server/partyquest/MonsterCarnival.java b/src/server/partyquest/MonsterCarnival.java index 3363fcea8f..2d68579d43 100644 --- a/src/server/partyquest/MonsterCarnival.java +++ b/src/server/partyquest/MonsterCarnival.java @@ -2,8 +2,8 @@ package server.partyquest; import java.util.concurrent.ScheduledFuture; import client.MapleCharacter; -import constants.LanguageConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.string.LanguageConstants; import net.server.Server; import net.server.channel.Channel; import net.server.world.MapleParty; @@ -28,7 +28,7 @@ public class MonsterCarnival { private ScheduledFuture timer, effectTimer, respawnTask; private long startTime = 0; private int summonsR = 0, summonsB = 0, room = 0; - private MapleCharacter leader1, leader2, Grupo1, Grupo2; + private MapleCharacter leader1, leader2, team1, team2; private int redCP, blueCP, redTotalCP, blueTotalCP, redTimeupCP, blueTimeupCP; private boolean cpq1; @@ -50,7 +50,7 @@ public class MonsterCarnival { bluePortal = 1; } for (MaplePartyCharacter mpc : p1.getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.setMonsterCarnival(this); mc.setTeam(0); @@ -60,11 +60,11 @@ public class MonsterCarnival { if (p1.getLeader().getId() == mc.getId()) { leader1 = mc; } - Grupo1 = mc; + team1 = mc; } } for (MaplePartyCharacter mpc : p2.getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.setMonsterCarnival(this); mc.setTeam(1); @@ -74,15 +74,21 @@ public class MonsterCarnival { if (p2.getLeader().getId() == mc.getId()) { leader2 = mc; } - Grupo2 = mc; + team2 = mc; } } - if (Grupo1 == null || Grupo2 == null) { - for (MaplePartyCharacter mpc : p2.getMembers()) { - mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError)); + if (team1 == null || team2 == null) { + for (MaplePartyCharacter mpc : p1.getMembers()) { + MapleCharacter chr = mpc.getPlayer(); + if (chr != null) { + chr.dropMessage(5, LanguageConstants.getMessage(chr, LanguageConstants.CPQError)); + } } for (MaplePartyCharacter mpc : p2.getMembers()) { - mpc.getPlayer().dropMessage(5, LanguageConstants.getMessage(mpc.getPlayer(), LanguageConstants.CPQError)); + MapleCharacter chr = mpc.getPlayer(); + if (chr != null) { + chr.dropMessage(5, LanguageConstants.getMessage(chr, LanguageConstants.CPQError)); + } } return; } @@ -94,7 +100,7 @@ public class MonsterCarnival { public void run() { timeUp(); } - }, (map.getTimeDefault() - 10) * 1000); + }, map.getTimeDefault() * 1000); // thanks Atoot for noticing an irregular "event extended" issue here effectTimer = TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { @@ -106,7 +112,7 @@ public class MonsterCarnival { public void run() { respawn(); } - }, ServerConstants.RESPAWN_INTERVAL); + }, YamlConfig.config.server.RESPAWN_INTERVAL); cs.initMonsterCarnival(cpq1, room); } catch (Exception e) { @@ -207,7 +213,7 @@ public class MonsterCarnival { out = cs.getMapFactory().getMap(980000010); } for (MaplePartyCharacter mpc : leader1.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.resetCP(); mc.setTeam(-1); @@ -218,7 +224,7 @@ public class MonsterCarnival { } } for (MaplePartyCharacter mpc : leader2.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.resetCP(); mc.setTeam(-1); @@ -263,7 +269,7 @@ public class MonsterCarnival { Channel cs = map.getChannelServer(); if (winningTeam == 0) { for (MaplePartyCharacter mpc : leader1.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.gainFestivalPoints(this.redTotalCP); mc.setMonsterCarnival(null); @@ -277,7 +283,7 @@ public class MonsterCarnival { } } for (MaplePartyCharacter mpc : leader2.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.gainFestivalPoints(this.blueTotalCP); mc.setMonsterCarnival(null); @@ -292,7 +298,7 @@ public class MonsterCarnival { } } else if (winningTeam == 1) { for (MaplePartyCharacter mpc : leader2.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.gainFestivalPoints(this.blueTotalCP); mc.setMonsterCarnival(null); @@ -306,7 +312,7 @@ public class MonsterCarnival { } } for (MaplePartyCharacter mpc : leader1.getParty().getMembers()) { - MapleCharacter mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { mc.gainFestivalPoints(this.redTotalCP); mc.setMonsterCarnival(null); @@ -387,12 +393,10 @@ public class MonsterCarnival { if (chnl != chnl1) { throw new RuntimeException("Os lideres estao em canais diferentes."); } - - Channel cs = map.getChannelServer(); + map.killAllMonsters(); for (MaplePartyCharacter mpc : leader1.getParty().getMembers()) { - MapleCharacter mc; - mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { if (redWin) { mc.getClient().announce(MaplePacketCreator.showEffect("quest/carnival/win")); @@ -406,8 +410,7 @@ public class MonsterCarnival { } } for (MaplePartyCharacter mpc : leader2.getParty().getMembers()) { - MapleCharacter mc; - mc = cs.getPlayerStorage().getCharacterById(mpc.getId()); + MapleCharacter mc = mpc.getPlayer(); if (mc != null) { if (!redWin) { mc.getClient().announce(MaplePacketCreator.showEffect("quest/carnival/win")); diff --git a/src/server/quest/MapleQuest.java b/src/server/quest/MapleQuest.java index 48b1131c4a..73f08f5ed5 100644 --- a/src/server/quest/MapleQuest.java +++ b/src/server/quest/MapleQuest.java @@ -30,8 +30,10 @@ import java.util.Map; import client.MapleCharacter; import client.MapleQuestStatus; import client.MapleQuestStatus.Status; -import constants.ServerConstants; +import config.YamlConfig; +import java.util.Collection; import java.util.EnumMap; +import java.util.Map.Entry; import java.util.Set; import provider.MapleData; import provider.MapleDataProvider; @@ -50,6 +52,7 @@ import tools.StringUtil; public class MapleQuest { private static Map quests = new HashMap<>(); + private static Map infoNumberQuests = new HashMap<>(); private static Map medals = new HashMap<>(); private static final Set exploitableQuests = new HashSet<>(); @@ -60,9 +63,8 @@ public class MapleQuest { exploitableQuests.add((short) 21752); } - protected short infoNumber, id; + protected short id; protected int timeLimit, timeLimit2; - protected String infoex; protected Map startReqs = new EnumMap<>(MapleQuestRequirementType.class); protected Map completeReqs = new EnumMap<>(MapleQuestRequirementType.class); protected Map startActs = new EnumMap<>(MapleQuestActionType.class); @@ -73,9 +75,9 @@ public class MapleQuest { private boolean repeatable = false; private String name = "", parent = ""; private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Quest.wz")); - private static MapleData questInfo; - private static MapleData questAct; - private static MapleData questReq; + private final static MapleData questInfo = questData.getData("QuestInfo.img"); + private final static MapleData questAct = questData.getData("Act.img"); + private final static MapleData questReq = questData.getData("Check.img"); private MapleQuest(int id) { this.id = (short) id; @@ -110,8 +112,6 @@ public class MapleQuest { MapleQuestRequirementType type = MapleQuestRequirementType.getByWZName(startReq.getName()); if (type.equals(MapleQuestRequirementType.INTERVAL)) { repeatable = true; - } else if (type.equals(MapleQuestRequirementType.INFO_NUMBER)) { - infoNumber = (short) MapleDataTool.getInt(startReq, 0); } else if (type.equals(MapleQuestRequirementType.MOB)) { for (MapleData mob : startReq.getChildren()) { relevantMobs.add(MapleDataTool.getInt(mob.getChildByPath("id"))); @@ -119,8 +119,9 @@ public class MapleQuest { } MapleQuestRequirement req = this.getRequirement(type, startReq); - if(req == null) - continue; + if (req == null) { + continue; + } startReqs.put(type, req); } @@ -130,14 +131,13 @@ public class MapleQuest { if (completeReqData != null) { for (MapleData completeReq : completeReqData.getChildren()) { MapleQuestRequirementType type = MapleQuestRequirementType.getByWZName(completeReq.getName()); - MapleQuestRequirement req = this.getRequirement(type, completeReq); - if(req == null) - continue; + MapleQuestRequirement req = this.getRequirement(type, completeReq); + if (req == null) { + continue; + } - if (type.equals(MapleQuestRequirementType.INFO_NUMBER)) { - infoNumber = (short) MapleDataTool.getInt(completeReq, 0); - } else if (type.equals(MapleQuestRequirementType.MOB)) { + if (type.equals(MapleQuestRequirementType.MOB)) { for (MapleData mob : completeReq.getChildren()) { relevantMobs.add(MapleDataTool.getInt(mob.getChildByPath("id"))); } @@ -174,7 +174,7 @@ public class MapleQuest { } } } - + public boolean isAutoComplete() { return autoPreComplete || autoComplete; } @@ -186,182 +186,198 @@ public class MapleQuest { public static MapleQuest getInstance(int id) { MapleQuest ret = quests.get(id); if (ret == null) { - questInfo = questData.getData("QuestInfo.img"); - questReq = questData.getData("Check.img"); - questAct = questData.getData("Act.img"); - ret = new MapleQuest(id); quests.put(id, ret); } return ret; } - private String getIntervalTimeLeft(MapleCharacter c, IntervalRequirement r) { - StringBuilder str = new StringBuilder(); - - long futureTime = c.getQuest(MapleQuest.getInstance(getId())).getCompletionTime() + r.getInterval(); - long leftTime = futureTime - System.currentTimeMillis(); - - byte mode = 0; - if(leftTime / (60*1000) > 0) { - mode++; //counts minutes - - if(leftTime / (60*60*1000) > 0) - mode++; //counts hours + public static MapleQuest getInstanceFromInfoNumber(int infoNumber) { + Integer id = infoNumberQuests.get(infoNumber); + if (id == null) { + id = infoNumber; } - switch(mode) { - case 2: - int hours = (int) ((leftTime / (1000*60*60))); - str.append(hours + " hours, "); - - case 1: - int minutes = (int) ((leftTime / (1000*60)) % 60); - str.append(minutes + " minutes, "); - - default: - int seconds = (int) (leftTime / 1000) % 60 ; - str.append(seconds + " seconds"); - } - - return str.toString(); + return getInstance(id); } public boolean isSameDayRepeatable() { if(!repeatable) return false; IntervalRequirement ir = (IntervalRequirement) startReqs.get(MapleQuestRequirementType.INTERVAL); - return ir.getInterval() < ServerConstants.QUEST_POINT_REPEATABLE_INTERVAL * 60 * 60 * 1000; + return ir.getInterval() < YamlConfig.config.server.QUEST_POINT_REPEATABLE_INTERVAL * 60 * 60 * 1000; } - public boolean canStartWithoutRequirements(MapleCharacter c) { - MapleQuestStatus mqs = c.getQuest(this); - return !(mqs.getStatus() != Status.NOT_STARTED && !(mqs.getStatus() == Status.COMPLETED && repeatable)); + public boolean canStartQuestByStatus(MapleCharacter chr) { + MapleQuestStatus mqs = chr.getQuest(this); + return !(!mqs.getStatus().equals(Status.NOT_STARTED) && !(mqs.getStatus().equals(Status.COMPLETED) && repeatable)); } - public boolean canStart(MapleCharacter c, int npcid) { - if (!canStartWithoutRequirements(c)) { + public boolean canQuestByInfoProgress(MapleCharacter chr) { + MapleQuestStatus mqs = chr.getQuest(this); + List ix = mqs.getInfoEx(); + if (!ix.isEmpty()) { + short questid = mqs.getQuestID(); + short infoNumber = mqs.getInfoNumber(); + if (infoNumber <= 0) { + infoNumber = questid; // on default infoNumber mimics questid + } + + int ixSize = ix.size(); + for (int i = 0; i < ixSize; i++) { + String progress = chr.getAbstractPlayerInteraction().getQuestProgress(infoNumber, i); + String ixProgress = ix.get(i); + + if (!progress.contentEquals(ixProgress)) { + return false; + } + } + } + + return true; + } + + public boolean canStart(MapleCharacter chr, int npcid) { + if (!canStartQuestByStatus(chr)) { return false; } + for (MapleQuestRequirement r : startReqs.values()) { - if (!r.check(c, npcid)) { - if(r.getType().getType() == MapleQuestRequirementType.INTERVAL.getType()) { - c.message("This quest will become available again in approximately " + getIntervalTimeLeft(c, (IntervalRequirement)r) + "."); - } + if (!r.check(chr, npcid)) { return false; } } - return true; - } - - public boolean canComplete(MapleCharacter c, Integer npcid) { - if (!c.getQuest(this).getStatus().equals(Status.STARTED)) { + + if (!canQuestByInfoProgress(chr)) { return false; } - for (MapleQuestRequirement r : completeReqs.values()) { - if (r == null) { - return false; - } else if(!r.check(c, npcid)) { - if(r.getType() == MapleQuestRequirementType.MESO) { // TODO: find a way to tell the client about the new MESO requirement type. - c.dropMessage(5, "You don't have enough mesos to complete this quest."); - } - return false; - } - } + return true; } - public void start(MapleCharacter c, int npc) { - if (autoStart || canStart(c, npc)) { - for (MapleQuestAction a : startActs.values()) { - if (!a.check(c, null)) { // would null be good ? - return; - } - a.run(c, null); - } - forceStart(c, npc); + public boolean canComplete(MapleCharacter chr, Integer npcid) { + MapleQuestStatus mqs = chr.getQuest(this); + if (!mqs.getStatus().equals(Status.STARTED)) { + return false; } + + for (MapleQuestRequirement r : completeReqs.values()) { + if (!r.check(chr, npcid)) { + return false; + } + } + + if (!canQuestByInfoProgress(chr)) { + return false; + } + + return true; } - public void complete(MapleCharacter c, int npc) { - complete(c, npc, null); - } - - public void complete(MapleCharacter c, int npc, Integer selection) { - if (autoPreComplete || canComplete(c, npc)) { - for (MapleQuestAction a : completeActs.values()) { - if (!a.check(c, selection)) { + public void start(MapleCharacter chr, int npc) { + if (autoStart || canStart(chr, npc)) { + Collection acts = startActs.values(); + for (MapleQuestAction a : acts) { + if (!a.check(chr, null)) { // would null be good ? return; } } + for (MapleQuestAction a : acts) { + a.run(chr, null); + } + forceStart(chr, npc); + } + } - forceComplete(c, npc); - for (MapleQuestAction a : completeActs.values()) { - a.run(c, selection); + public void complete(MapleCharacter chr, int npc) { + complete(chr, npc, null); + } + + public void complete(MapleCharacter chr, int npc, Integer selection) { + if (autoPreComplete || canComplete(chr, npc)) { + Collection acts = completeActs.values(); + for (MapleQuestAction a : acts) { + if (!a.check(chr, selection)) { + return; + } + } + forceComplete(chr, npc); + for (MapleQuestAction a : acts) { + a.run(chr, selection); + } + if (!this.hasNextQuestAction()) { + chr.announceUpdateQuest(MapleCharacter.DelayedQuestUpdate.INFO, chr.getQuest(this)); } } } - public void reset(MapleCharacter c) { - c.updateQuest(new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED)); + public void reset(MapleCharacter chr) { + MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED); + chr.updateQuestStatus(newStatus); } - public void forfeit(MapleCharacter c) { - if (!c.getQuest(this).getStatus().equals(Status.STARTED)) { - return; + public boolean forfeit(MapleCharacter chr) { + if (!chr.getQuest(this).getStatus().equals(Status.STARTED)) { + return false; } if (timeLimit > 0) { - c.announce(MaplePacketCreator.removeQuestTimeLimit(id)); + chr.announce(MaplePacketCreator.removeQuestTimeLimit(id)); } MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.NOT_STARTED); - newStatus.setForfeited(c.getQuest(this).getForfeited() + 1); - c.updateQuest(newStatus); + newStatus.setForfeited(chr.getQuest(this).getForfeited() + 1); + chr.updateQuestStatus(newStatus); + return true; } - public boolean forceStart(MapleCharacter c, int npc) { + public boolean forceStart(MapleCharacter chr, int npc) { MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.STARTED, npc); - newStatus.setForfeited(c.getQuest(this).getForfeited()); - newStatus.setCompleted(c.getQuest(this).getCompleted()); - - if (timeLimit > 0) { - newStatus.setExpirationTime(System.currentTimeMillis() + (timeLimit * 1000)); - c.questTimeLimit(this, timeLimit); - } - if (timeLimit2 > 0) { - newStatus.setExpirationTime(System.currentTimeMillis() + timeLimit2); - c.questTimeLimit2(this, newStatus.getExpirationTime()); + + MapleQuestStatus oldStatus = chr.getQuest(this.getId()); + for (Entry e : oldStatus.getProgress().entrySet()) { + newStatus.setProgress(e.getKey(), e.getValue()); } - c.updateQuest(newStatus); - - if(id / 100 == 35 && ServerConstants.TOT_MOB_QUEST_REQUIREMENT > 0) { - int setProg = 999 - Math.min(999, ServerConstants.TOT_MOB_QUEST_REQUIREMENT); + if(id / 100 == 35 && YamlConfig.config.server.TOT_MOB_QUEST_REQUIREMENT > 0) { + int setProg = 999 - Math.min(999, YamlConfig.config.server.TOT_MOB_QUEST_REQUIREMENT); for(Integer pid : newStatus.getProgress().keySet()) { if(pid >= 8200000 && pid <= 8200012) { String pr = StringUtil.getLeftPaddedStr(Integer.toString(setProg), '0', 3); newStatus.setProgress(pid, pr); - c.announce(MaplePacketCreator.updateQuest(newStatus, false)); } } } + newStatus.setForfeited(chr.getQuest(this).getForfeited()); + newStatus.setCompleted(chr.getQuest(this).getCompleted()); + + if (timeLimit > 0) { + newStatus.setExpirationTime(System.currentTimeMillis() + (timeLimit * 1000)); + chr.questTimeLimit(this, timeLimit); + } + if (timeLimit2 > 0) { + newStatus.setExpirationTime(System.currentTimeMillis() + timeLimit2); + chr.questTimeLimit2(this, newStatus.getExpirationTime()); + } + + chr.updateQuestStatus(newStatus); + return true; } - public boolean forceComplete(MapleCharacter c, int npc) { + public boolean forceComplete(MapleCharacter chr, int npc) { if (timeLimit > 0) { - c.announce(MaplePacketCreator.removeQuestTimeLimit(id)); + chr.announce(MaplePacketCreator.removeQuestTimeLimit(id)); } MapleQuestStatus newStatus = new MapleQuestStatus(this, MapleQuestStatus.Status.COMPLETED, npc); - newStatus.setForfeited(c.getQuest(this).getForfeited()); - newStatus.setCompleted(c.getQuest(this).getCompleted()); + newStatus.setForfeited(chr.getQuest(this).getForfeited()); + newStatus.setCompleted(chr.getQuest(this).getCompleted()); newStatus.setCompletionTime(System.currentTimeMillis()); - c.updateQuest(newStatus); + chr.updateQuestStatus(newStatus); - c.announce(MaplePacketCreator.showSpecialEffect(9)); // Quest completion - c.getMap().broadcastMessage(c, MaplePacketCreator.showForeignEffect(c.getId(), 9), false); //use 9 instead of 12 for both + chr.announce(MaplePacketCreator.showSpecialEffect(9)); // Quest completion + chr.getMap().broadcastMessage(chr, MaplePacketCreator.showForeignEffect(chr.getId(), 9), false); //use 9 instead of 12 for both return true; } @@ -376,49 +392,66 @@ public class MapleQuest { public int getStartItemAmountNeeded(int itemid) { MapleQuestRequirement req = startReqs.get(MapleQuestRequirementType.ITEM); if(req == null) - return 0; + return Integer.MIN_VALUE; ItemRequirement ireq = (ItemRequirement) req; - return ireq.getItemAmountNeeded(itemid); + return ireq.getItemAmountNeeded(itemid, false); } public int getCompleteItemAmountNeeded(int itemid) { MapleQuestRequirement req = completeReqs.get(MapleQuestRequirementType.ITEM); if(req == null) - return 0; + return Integer.MAX_VALUE; ItemRequirement ireq = (ItemRequirement) req; - return ireq.getItemAmountNeeded(itemid); + return ireq.getItemAmountNeeded(itemid, true); } public int getMobAmountNeeded(int mid) { MapleQuestRequirement req = completeReqs.get(MapleQuestRequirementType.MOB); - if(req == null) - return 0; - - MobRequirement mreq = (MobRequirement) req; - - return mreq.getRequiredMobCount(mid); + if(req == null) + return 0; + + MobRequirement mreq = (MobRequirement) req; + + return mreq.getRequiredMobCount(mid); } - public short getInfoNumber() { - return infoNumber; + public short getInfoNumber(Status qs) { + boolean checkEnd = qs.equals(Status.STARTED); + Map reqs = !checkEnd ? startReqs : completeReqs; + + MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_NUMBER); + if (req != null) { + InfoNumberRequirement inReq = (InfoNumberRequirement) req; + return inReq.getInfoNumber(); + } else { + return 0; + } } - public String getInfoEx() { - MapleQuestRequirement req = startReqs.get(MapleQuestRequirementType.INFO_EX); - String ret = ""; - if(req != null) { - InfoExRequirement ireq = (InfoExRequirement) req; - ret = ireq.getFirstInfo(); - } else { // Check complete requirements. - req = completeReqs.get(MapleQuestRequirementType.INFO_EX); - if(req != null) { - InfoExRequirement ireq = (InfoExRequirement) req; - ret = ireq.getFirstInfo(); - } - } - return ret; + public String getInfoEx(Status qs, int index) { + boolean checkEnd = qs.equals(Status.STARTED); + Map reqs = !checkEnd ? startReqs : completeReqs; + try { + MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_EX); + InfoExRequirement ixReq = (InfoExRequirement) req; + return ixReq.getInfo().get(index); + } catch (Exception e) { + return ""; + } + } + + public List getInfoEx(Status qs) { + boolean checkEnd = qs.equals(Status.STARTED); + Map reqs = !checkEnd ? startReqs : completeReqs; + try { + MapleQuestRequirement req = reqs.get(MapleQuestRequirementType.INFO_EX); + InfoExRequirement ixReq = (InfoExRequirement) req; + return ixReq.getInfo(); + } catch (Exception e) { + return new LinkedList<>(); + } } public int getTimeLimit() { @@ -450,6 +483,9 @@ public class MapleQuest { case FIELD_ENTER: ret = new FieldEnterRequirement(this, data); break; + case INFO_NUMBER: + ret = new InfoNumberRequirement(this, data); + break; case INFO_EX: ret = new InfoExRequirement(this, data); break; @@ -493,10 +529,11 @@ public class MapleQuest { ret = new BuffExceptRequirement(this, data); break; case SCRIPT: + ret = new ScriptRequirement(this, data); + break; case NORMAL_AUTO_START: case START: case END: - case INFO_NUMBER: break; default: //FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Requirement Type: " + type.toString() + " QuestID: " + this.getId()); @@ -541,6 +578,9 @@ public class MapleQuest { case PETSPEED: ret = new PetSpeedAction(this, data); break; + case INFO: + ret = new InfoAction(this, data); + break; default: //FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId()); break; @@ -548,8 +588,15 @@ public class MapleQuest { return ret; } - public static boolean isExploitableQuest(short questid) { - return exploitableQuests.contains(questid); + public boolean restoreLostItem(MapleCharacter chr, int itemid) { + if (chr.getQuest(this).getStatus().equals(MapleQuestStatus.Status.STARTED)) { + ItemAction itemAct = (ItemAction) startActs.get(MapleQuestActionType.ITEM); + if (itemAct != null) { + return itemAct.restoreLostItem(chr, itemid); + } + } + + return false; } public int getMedalRequirement() { @@ -557,9 +604,8 @@ public class MapleQuest { return medalid != null ? medalid : -1; } - public int getNpcRequirement(boolean complete) { - Map reqs = !complete ? startReqs : completeReqs; - + public int getNpcRequirement(boolean checkEnd) { + Map reqs = !checkEnd ? startReqs : completeReqs; MapleQuestRequirement mqr = reqs.get(MapleQuestRequirementType.NPC); if (mqr != null) { return ((NpcRequirement) mqr).get(); @@ -568,6 +614,24 @@ public class MapleQuest { } } + public boolean hasScriptRequirement(boolean checkEnd) { + Map reqs = !checkEnd ? startReqs : completeReqs; + MapleQuestRequirement mqr = reqs.get(MapleQuestRequirementType.SCRIPT); + + if (mqr != null) { + return ((ScriptRequirement) mqr).get(); + } else { + return false; + } + } + + public boolean hasNextQuestAction() { + Map acts = completeActs; + MapleQuestAction mqa = acts.get(MapleQuestActionType.NEXTQUEST); + + return mqa != null; + } + public String getName() { return name; } @@ -576,6 +640,10 @@ public class MapleQuest { return parent; } + public static boolean isExploitableQuest(short questid) { + return exploitableQuests.contains(questid); + } + public static List getMatchedQuests(String search) { List ret = new LinkedList<>(); @@ -590,15 +658,24 @@ public class MapleQuest { } public static void loadAllQuest() { - questInfo = questData.getData("QuestInfo.img"); - questReq = questData.getData("Check.img"); - questAct = questData.getData("Act.img"); - try { for(MapleData quest : questInfo.getChildren()) { int questID = Integer.parseInt(quest.getName()); - quests.put(questID, new MapleQuest(questID)); + MapleQuest q = new MapleQuest(questID); + quests.put(questID, q); + + int infoNumber; + + infoNumber = q.getInfoNumber(Status.STARTED); + if (infoNumber > 0) { + infoNumberQuests.put(infoNumber, questID); + } + + infoNumber = q.getInfoNumber(Status.COMPLETED); + if (infoNumber > 0) { + infoNumberQuests.put(infoNumber, questID); + } } } catch (Exception ex) { ex.printStackTrace(); diff --git a/src/server/quest/MapleQuestActionType.java b/src/server/quest/MapleQuestActionType.java index 06721b45bd..f009d0ff21 100644 --- a/src/server/quest/MapleQuestActionType.java +++ b/src/server/quest/MapleQuestActionType.java @@ -26,7 +26,7 @@ package server.quest; * @author Matze */ public enum MapleQuestActionType { - UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), ZERO(16); + UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), PETSPEED(15), INFO(16), ZERO(16); final byte type; private MapleQuestActionType(int type) { @@ -64,6 +64,8 @@ public enum MapleQuestActionType { return PETTAMENESS; } else if (name.equals("petspeed")) { return PETSPEED; + } else if (name.equals("info")) { + return INFO; } else if (name.equals("0")) { return ZERO; } else { diff --git a/src/server/quest/actions/ExpAction.java b/src/server/quest/actions/ExpAction.java index 2ad65b3928..a617e91422 100644 --- a/src/server/quest/actions/ExpAction.java +++ b/src/server/quest/actions/ExpAction.java @@ -22,7 +22,7 @@ package server.quest.actions; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; import provider.MapleData; import provider.MapleDataTool; import server.quest.MapleQuest; @@ -52,7 +52,7 @@ public class ExpAction extends MapleQuestAction { } public static void runAction(MapleCharacter chr, int gain) { - if (!ServerConstants.USE_QUEST_RATE) { + if (!YamlConfig.config.server.USE_QUEST_RATE) { chr.gainExp(gain * chr.getExpRate(), true, true); } else { chr.gainExp(gain * chr.getQuestExpRate(), true, true); diff --git a/src/server/quest/actions/FameAction.java b/src/server/quest/actions/FameAction.java index aee2b04298..e37b88a1be 100644 --- a/src/server/quest/actions/FameAction.java +++ b/src/server/quest/actions/FameAction.java @@ -22,12 +22,10 @@ package server.quest.actions; import client.MapleCharacter; -import client.MapleStat; import provider.MapleData; import provider.MapleDataTool; import server.quest.MapleQuest; import server.quest.MapleQuestActionType; -import tools.MaplePacketCreator; /** * diff --git a/scripts/quest/3345.js b/src/server/quest/actions/InfoAction.java similarity index 51% rename from scripts/quest/3345.js rename to src/server/quest/actions/InfoAction.java index a4350cdf98..068e570221 100644 --- a/scripts/quest/3345.js +++ b/src/server/quest/actions/InfoAction.java @@ -17,34 +17,39 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +package server.quest.actions; -var status = -1; +import client.MapleCharacter; +import client.MapleQuestStatus; +import provider.MapleData; +import provider.MapleDataTool; +import server.quest.MapleQuest; +import server.quest.MapleQuestActionType; -function end(mode, type, selection) { - if (mode == -1) { - qm.dispose(); - } else { - if(mode == 0 && type > 0) { - qm.dispose(); - return; +/** + * + * @author Ronan + */ +public class InfoAction extends MapleQuestAction { + + private String info; + private int questID; + + public InfoAction(MapleQuest quest, MapleData data) { + super(MapleQuestActionType.INFO, quest); + questID = quest.getId(); + processData(data); + } + + @Override + public void processData(MapleData data) { + info = MapleDataTool.getString(data, ""); + } + + + @Override + public void run(MapleCharacter chr, Integer extSelection) { + chr.getAbstractPlayerInteraction().setQuestProgress(questID, info); } - if (mode == 1) - status++; - else - status--; - - if (status == 0) { - if(qm.getQuestProgress(3345, 0) == 4) { - qm.sendNext("So, you have succeeded. With this, Magatia's upfront demise has been averted, well done brave adventurer!"); - qm.forceCompleteQuest(); - - qm.gainExp(20000); - } else { - qm.sendNext("Did you not seal the #rmagic circle beneath Magatia#k yet? It is a matter of great importance, please haste yourself."); - } - - qm.dispose(); - } - } -} \ No newline at end of file +} diff --git a/src/server/quest/actions/ItemAction.java b/src/server/quest/actions/ItemAction.java index 1491515a98..15b69c64fc 100644 --- a/src/server/quest/actions/ItemAction.java +++ b/src/server/quest/actions/ItemAction.java @@ -24,9 +24,8 @@ package server.quest.actions; import client.MapleCharacter; import client.MapleClient; import client.inventory.Item; -import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -35,8 +34,10 @@ import java.util.List; import provider.MapleData; import provider.MapleDataTool; import client.inventory.manipulator.MapleInventoryManipulator; +import server.MapleItemInformationProvider; import server.quest.MapleQuest; import server.quest.MapleQuestActionType; +import tools.FilePrinter; import tools.MaplePacketCreator; import tools.Pair; import tools.Randomizer; @@ -107,6 +108,7 @@ public class ItemAction extends MapleQuestAction { if (!canGetItem(iEntry, chr)) { continue; } + if(iEntry.getProp() != null) { if(iEntry.getProp() == -1) { if(extSelection != extNum++) @@ -179,11 +181,11 @@ public class ItemAction extends MapleQuestAction { } } else { - if(item.getCount() > 0) { - // Make sure they can hold the item. - Item toItem = new Item(item.getId(), (short) 0, (short) item.getCount()); - gainList.add(new Pair<>(toItem, type)); - } else { + // Make sure they can hold the item. + Item toItem = new Item(item.getId(), (short) 0, (short) item.getCount()); + gainList.add(new Pair<>(toItem, type)); + + if(item.getCount() < 0) { // Make sure they actually have the item. int quantity = item.getCount() * -1; @@ -192,7 +194,7 @@ public class ItemAction extends MapleQuestAction { if(type.equals(MapleInventoryType.EQUIP) && chr.getInventory(MapleInventoryType.EQUIPPED).countById(item.getId()) > quantity) continue; - chr.dropMessage(1, "Please check if you have enough items in your inventory."); + announceInventoryLimit(Collections.singletonList(item.getId()), chr); return false; } else { int idx = type.getType() - 1; // more slots available from the given items! @@ -214,7 +216,7 @@ public class ItemAction extends MapleQuestAction { result = MapleInventoryManipulator.checkSpaceProgressively(c, it.getLeft().getItemId(), it.getLeft().getQuantity(), "", rndUsed.get(idx), false); if(result % 2 == 0) { - chr.dropMessage(1, "Please check if you have enough space in your inventory."); + announceInventoryLimit(Collections.singletonList(it.getLeft().getItemId()), chr); return false; } @@ -227,12 +229,50 @@ public class ItemAction extends MapleQuestAction { gainList.add(selected); } - if (!MapleInventory.checkSpots(chr, gainList, allSlotUsed, false)) { - chr.dropMessage(1, "Please check if you have enough space in your inventory."); + if (!canHold(chr, gainList)) { + List gainItemids = new LinkedList<>(); + for (Pair it : gainList) { + gainItemids.add(it.getLeft().getItemId()); + } + + announceInventoryLimit(gainItemids, chr); return false; } return true; } + + private void announceInventoryLimit(List itemids, MapleCharacter chr) { + for (Integer id : itemids) { + if (MapleItemInformationProvider.getInstance().isPickupRestricted(id) && chr.haveItemWithId(id, true)) { + chr.dropMessage(1, "Please check if you already have a similar one-of-a-kind item in your inventory."); + return; + } + } + + chr.dropMessage(1, "Please check if you have enough space in your inventory."); + } + + private boolean canHold(MapleCharacter chr, List> gainList) { + List toAddItemids = new LinkedList<>(); + List toAddQuantity = new LinkedList<>(); + List toRemoveItemids = new LinkedList<>(); + List toRemoveQuantity = new LinkedList<>(); + + for (Pair item : gainList) { + Item it = item.getLeft(); + + if (it.getQuantity() > 0) { + toAddItemids.add(it.getItemId()); + toAddQuantity.add((int) it.getQuantity()); + } else { + toRemoveItemids.add(it.getItemId()); + toRemoveQuantity.add(-1 * ((int) it.getQuantity())); + } + } + + // thanks onechord for noticing quests unnecessarily giving out "full inventory" from quests that also takes items from players + return chr.getAbstractPlayerInteraction().canHoldAllAfterRemoving(toAddItemids, toAddQuantity, toRemoveItemids, toRemoveQuantity); + } private boolean canGetItem(ItemData item, MapleCharacter chr) { if (item.getGender() != 2 && item.getGender() != chr.getGender()) { @@ -250,8 +290,34 @@ public class ItemAction extends MapleQuestAction { } return jobFound; } - return true; - } + + return true; + } + + public boolean restoreLostItem(MapleCharacter chr, int itemid) { + if (!MapleItemInformationProvider.getInstance().isQuestItem(itemid)) { + return false; + } + + // thanks danielktran (MapleHeroesD) + for (ItemData item : items) { + if (item.getId() == itemid) { + int missingQty = item.getCount() - chr.countItem(itemid); + if (missingQty > 0) { + if (!chr.canHold(itemid, missingQty)) { + chr.dropMessage(1, "Please check if you have enough space in your inventory."); + return false; + } + + MapleInventoryManipulator.addById(chr.getClient(), item.getId(), (short) missingQty); + FilePrinter.print(FilePrinter.QUEST_RESTORE_ITEM, chr + " obtained " + itemid + " qty. " + missingQty + " from quest " + questID); + } + return true; + } + } + + return false; + } private class ItemData { private final int map, id, count, job, gender; diff --git a/src/server/quest/actions/MapleQuestAction.java b/src/server/quest/actions/MapleQuestAction.java index c5d70b6545..7dfd57f2bd 100644 --- a/src/server/quest/actions/MapleQuestAction.java +++ b/src/server/quest/actions/MapleQuestAction.java @@ -19,7 +19,6 @@ package server.quest.actions; import client.MapleCharacter; -import client.MapleQuestStatus; import provider.MapleData; import server.quest.MapleQuest; import server.quest.MapleQuestActionType; @@ -43,10 +42,8 @@ public abstract class MapleQuestAction { public abstract void run(MapleCharacter chr, Integer extSelection); public abstract void processData(MapleData data); - public boolean check(MapleCharacter chr, Integer extSelection) { - MapleQuestStatus status = chr.getQuest(MapleQuest.getInstance(questID)); - return !(status.getStatus() == MapleQuestStatus.Status.NOT_STARTED && status.getForfeited() > 0); + return true; } public MapleQuestActionType getType() { diff --git a/src/server/quest/actions/MesoAction.java b/src/server/quest/actions/MesoAction.java index 8f84d6d299..df8e5d8af3 100644 --- a/src/server/quest/actions/MesoAction.java +++ b/src/server/quest/actions/MesoAction.java @@ -22,7 +22,7 @@ package server.quest.actions; import client.MapleCharacter; -import constants.ServerConstants; +import config.YamlConfig; import provider.MapleData; import provider.MapleDataTool; import server.quest.MapleQuest; @@ -56,7 +56,7 @@ public class MesoAction extends MapleQuestAction { if (gain < 0) { chr.gainMeso(gain, true, false, true); } else { - if (!ServerConstants.USE_QUEST_RATE) { + if (!YamlConfig.config.server.USE_QUEST_RATE) { chr.gainMeso(gain * chr.getMesoRate(), true, false, true); } else { chr.gainMeso(gain * chr.getQuestMesoRate(), true, false, true); diff --git a/src/server/quest/actions/PetSkillAction.java b/src/server/quest/actions/PetSkillAction.java index b99fdb1ea1..2e058e7f9f 100644 --- a/src/server/quest/actions/PetSkillAction.java +++ b/src/server/quest/actions/PetSkillAction.java @@ -23,7 +23,7 @@ package server.quest.actions; import client.MapleCharacter; import client.MapleQuestStatus; -import constants.ItemConstants; +import constants.inventory.ItemConstants; import provider.MapleData; import provider.MapleDataTool; import server.quest.MapleQuest; diff --git a/src/server/quest/actions/QuestAction.java b/src/server/quest/actions/QuestAction.java index f132b362d4..3d86b771c8 100644 --- a/src/server/quest/actions/QuestAction.java +++ b/src/server/quest/actions/QuestAction.java @@ -58,7 +58,7 @@ public class QuestAction extends MapleQuestAction { public void run(MapleCharacter chr, Integer extSelection) { for(Integer questID : quests.keySet()) { int stat = quests.get(questID); - chr.updateQuest(new MapleQuestStatus(MapleQuest.getInstance(questID), MapleQuestStatus.Status.getById(stat))); + chr.updateQuestStatus(new MapleQuestStatus(MapleQuest.getInstance(questID), MapleQuestStatus.Status.getById(stat))); } } } diff --git a/src/server/quest/requirements/InfoExRequirement.java b/src/server/quest/requirements/InfoExRequirement.java index 1740f918b7..ec39f0383b 100644 --- a/src/server/quest/requirements/InfoExRequirement.java +++ b/src/server/quest/requirements/InfoExRequirement.java @@ -22,7 +22,6 @@ package server.quest.requirements; import client.MapleCharacter; -import client.MapleQuestStatus; import java.util.ArrayList; import java.util.List; import provider.MapleData; @@ -41,14 +40,14 @@ public class InfoExRequirement extends MapleQuestRequirement { public InfoExRequirement(MapleQuest quest, MapleData data) { super(MapleQuestRequirementType.INFO_EX); - processData(data); questID = quest.getId(); + processData(data); } @Override public void processData(MapleData data) { // Because we have to... - for(MapleData infoEx : data.getChildren()) { + for(MapleData infoEx : data.getChildren()) { MapleData value = infoEx.getChildByPath("value"); infoExpected.add(MapleDataTool.getString(value, "")); } @@ -57,15 +56,10 @@ public class InfoExRequirement extends MapleQuestRequirement { @Override public boolean check(MapleCharacter chr, Integer npcid) { - MapleQuestStatus status = chr.getQuest(MapleQuest.getInstance(questID)); - return infoExpected.contains(status.getInfo()); + return true; } public List getInfo() { return infoExpected; - } - - public String getFirstInfo() { - return !infoExpected.isEmpty() ? infoExpected.get(0) : ""; - } + } } diff --git a/src/server/quest/requirements/InfoNumberRequirement.java b/src/server/quest/requirements/InfoNumberRequirement.java new file mode 100644 index 0000000000..080176c0cf --- /dev/null +++ b/src/server/quest/requirements/InfoNumberRequirement.java @@ -0,0 +1,57 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package server.quest.requirements; + +import client.MapleCharacter; +import provider.MapleData; +import provider.MapleDataTool; +import server.quest.MapleQuest; +import server.quest.MapleQuestRequirementType; + +/** + * + * @author Ronan + */ +public class InfoNumberRequirement extends MapleQuestRequirement { + + private short infoNumber; + private int questID; + + public InfoNumberRequirement(MapleQuest quest, MapleData data) { + super(MapleQuestRequirementType.INFO_NUMBER); + questID = quest.getId(); + processData(data); + } + + @Override + public void processData(MapleData data) { + infoNumber = (short) MapleDataTool.getIntConvert(data, 0); + } + + + @Override + public boolean check(MapleCharacter chr, Integer npcid) { + return true; + } + + public short getInfoNumber() { + return infoNumber; + } +} diff --git a/src/server/quest/requirements/IntervalRequirement.java b/src/server/quest/requirements/IntervalRequirement.java index 76d0645a25..e1e127a1ce 100644 --- a/src/server/quest/requirements/IntervalRequirement.java +++ b/src/server/quest/requirements/IntervalRequirement.java @@ -38,8 +38,8 @@ public class IntervalRequirement extends MapleQuestRequirement { public IntervalRequirement(MapleQuest quest, MapleData data) { super(MapleQuestRequirementType.INTERVAL); - processData(data); questID = quest.getId(); + processData(data); } public int getInterval() { @@ -51,11 +51,47 @@ public class IntervalRequirement extends MapleQuestRequirement { interval = MapleDataTool.getInt(data) * 60 * 1000; } + private static String getIntervalTimeLeft(MapleCharacter chr, IntervalRequirement r) { + StringBuilder str = new StringBuilder(); + + long futureTime = chr.getQuest(MapleQuest.getInstance(r.questID)).getCompletionTime() + r.getInterval(); + long leftTime = futureTime - System.currentTimeMillis(); + + byte mode = 0; + if(leftTime / (60*1000) > 0) { + mode++; //counts minutes + + if(leftTime / (60*60*1000) > 0) + mode++; //counts hours + } + + switch(mode) { + case 2: + int hours = (int) ((leftTime / (1000*60*60))); + str.append(hours + " hours, "); + + case 1: + int minutes = (int) ((leftTime / (1000*60)) % 60); + str.append(minutes + " minutes, "); + + default: + int seconds = (int) (leftTime / 1000) % 60 ; + str.append(seconds + " seconds"); + } + + return str.toString(); + } @Override public boolean check(MapleCharacter chr, Integer npcid) { boolean check = !chr.getQuest(MapleQuest.getInstance(questID)).getStatus().equals(MapleQuestStatus.Status.COMPLETED); boolean check2 = chr.getQuest(MapleQuest.getInstance(questID)).getCompletionTime() <= System.currentTimeMillis() - interval; - return check || check2; + + if (check || check2) { + return true; + } else { + chr.message("This quest will become available again in approximately " + getIntervalTimeLeft(chr, this) + "."); + return false; + } } } diff --git a/src/server/quest/requirements/ItemRequirement.java b/src/server/quest/requirements/ItemRequirement.java index 7481a2056a..67eb679c28 100644 --- a/src/server/quest/requirements/ItemRequirement.java +++ b/src/server/quest/requirements/ItemRequirement.java @@ -32,7 +32,7 @@ import server.quest.MapleQuestRequirementType; import client.MapleCharacter; import client.inventory.Item; import client.inventory.MapleInventoryType; -import constants.ItemConstants; +import constants.inventory.ItemConstants; /** * @@ -96,11 +96,12 @@ public class ItemRequirement extends MapleQuestRequirement { return true; } - public int getItemAmountNeeded(int itemid) { - if(items.containsKey(itemid)) { - return items.get(itemid); - } - - return 0; + public int getItemAmountNeeded(int itemid, boolean complete) { + Integer amount = items.get(itemid); + if (amount != null) { + return amount; + } else { + return complete ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } } } diff --git a/src/server/quest/requirements/MesoRequirement.java b/src/server/quest/requirements/MesoRequirement.java index 986458c785..54c683d207 100644 --- a/src/server/quest/requirements/MesoRequirement.java +++ b/src/server/quest/requirements/MesoRequirement.java @@ -45,6 +45,11 @@ public class MesoRequirement extends MapleQuestRequirement { @Override public boolean check(MapleCharacter chr, Integer npcid) { - return chr.getMeso() >= meso; + if (chr.getMeso() >= meso) { + return true; + } else { + chr.dropMessage(5, "You don't have enough mesos to complete this quest."); + return false; + } } } diff --git a/src/server/quest/requirements/MobRequirement.java b/src/server/quest/requirements/MobRequirement.java index 5e2a8cd3b8..8b13440a4e 100644 --- a/src/server/quest/requirements/MobRequirement.java +++ b/src/server/quest/requirements/MobRequirement.java @@ -42,8 +42,8 @@ public class MobRequirement extends MapleQuestRequirement { public MobRequirement(MapleQuest quest, MapleData data) { super(MapleQuestRequirementType.MOB); - processData(data); questID = quest.getId(); + processData(data); } /** diff --git a/src/server/quest/requirements/QuestRequirement.java b/src/server/quest/requirements/QuestRequirement.java index bb142fc7d7..47bc8457db 100644 --- a/src/server/quest/requirements/QuestRequirement.java +++ b/src/server/quest/requirements/QuestRequirement.java @@ -58,12 +58,12 @@ public class QuestRequirement extends MapleQuestRequirement { public boolean check(MapleCharacter chr, Integer npcid) { for(Integer questID : quests.keySet()) { int stateReq = quests.get(questID); - MapleQuestStatus q = chr.getQuest(MapleQuest.getInstance(questID)); + MapleQuestStatus qs = chr.getQuest(MapleQuest.getInstance(questID)); - if(q == null && MapleQuestStatus.Status.getById(stateReq).equals(MapleQuestStatus.Status.NOT_STARTED)) + if(qs == null && MapleQuestStatus.Status.getById(stateReq).equals(MapleQuestStatus.Status.NOT_STARTED)) continue; - if(q == null || !q.getStatus().equals(MapleQuestStatus.Status.getById(stateReq))) { + if(qs == null || !qs.getStatus().equals(MapleQuestStatus.Status.getById(stateReq))) { return false; } diff --git a/src/server/quest/requirements/ScriptRequirement.java b/src/server/quest/requirements/ScriptRequirement.java new file mode 100644 index 0000000000..52ec7e0d46 --- /dev/null +++ b/src/server/quest/requirements/ScriptRequirement.java @@ -0,0 +1,53 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package server.quest.requirements; + +import client.MapleCharacter; +import provider.MapleData; +import provider.MapleDataTool; +import server.quest.MapleQuest; +import server.quest.MapleQuestRequirementType; + +/** + * + * @author Ronan + */ +public class ScriptRequirement extends MapleQuestRequirement { + private boolean reqScript; + + public ScriptRequirement(MapleQuest quest, MapleData data) { + super(MapleQuestRequirementType.BUFF); + processData(data); + } + + @Override + public void processData(MapleData data) { + reqScript = !MapleDataTool.getString(data, "").isEmpty(); + } + + @Override + public boolean check(MapleCharacter chr, Integer npcid) { + return true; + } + + public boolean get() { + return reqScript; + } +} diff --git a/src/tools/DatabaseConnection.java b/src/tools/DatabaseConnection.java index 2e6753e293..e408be455f 100644 --- a/src/tools/DatabaseConnection.java +++ b/src/tools/DatabaseConnection.java @@ -8,7 +8,7 @@ import java.sql.SQLException; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import constants.ServerConstants; +import config.YamlConfig; /** * @author Frz (Big Daddy) @@ -30,7 +30,7 @@ public class DatabaseConnection { int denies = 0; while(true) { // There is no way it can pass with a null out of here? try { - return DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS); + return DriverManager.getConnection(YamlConfig.config.server.DB_URL, YamlConfig.config.server.DB_USER, YamlConfig.config.server.DB_PASS); } catch (SQLException sqle) { denies++; @@ -45,7 +45,7 @@ public class DatabaseConnection { private static int getNumberOfAccounts() { try { - Connection con = DriverManager.getConnection(ServerConstants.DB_URL, ServerConstants.DB_USER, ServerConstants.DB_PASS); + Connection con = DriverManager.getConnection(YamlConfig.config.server.DB_URL, YamlConfig.config.server.DB_USER, YamlConfig.config.server.DB_PASS); try (PreparedStatement ps = con.prepareStatement("SELECT count(*) FROM accounts")) { try (ResultSet rs = ps.executeQuery()) { rs.next(); @@ -69,14 +69,14 @@ public class DatabaseConnection { ds = null; - if(ServerConstants.DB_CONNECTION_POOL) { + if(YamlConfig.config.server.DB_CONNECTION_POOL) { // Connection Pool on database ftw! HikariConfig config = new HikariConfig(); - config.setJdbcUrl(ServerConstants.DB_URL); + config.setJdbcUrl(YamlConfig.config.server.DB_URL); - config.setUsername(ServerConstants.DB_USER); - config.setPassword(ServerConstants.DB_PASS); + config.setUsername(YamlConfig.config.server.DB_USER); + config.setPassword(YamlConfig.config.server.DB_PASS); // Make sure pool size is comfortable for the worst case scenario. // Under 100 accounts? Make it 10. Over 10000 accounts? Make it 30. diff --git a/src/tools/FilePrinter.java b/src/tools/FilePrinter.java index 72ac10b490..d21987a62d 100644 --- a/src/tools/FilePrinter.java +++ b/src/tools/FilePrinter.java @@ -21,6 +21,7 @@ public class FilePrinter { LOG_LEAF = "interactions/MapleLeaves.txt", LOG_GACHAPON = "interactions/Gachapon.txt", LOG_CHAT = "interactions/ChatLog.txt", + QUEST_RESTORE_ITEM = "game/QuestItemRestore.txt", EXCEPTION_CAUGHT = "game/ExceptionCaught.txt", CLIENT_START = "game/ClientStartError.txt", MAPLE_MAP = "game/MapleMap.txt", @@ -42,6 +43,7 @@ public class FilePrinter { MOB_MOVEMENT = "game/MobMovement.txt", MAP_SCRIPT = "game/mapscript/", DIRECTION = "game/directions/", + GUILD_CHAR_ERROR = "guilds/GuildCharError.txt", SAVE_CHAR = "players/SaveToDB.txt", INSERT_CHAR = "players/InsertCharacter.txt", LOAD_CHAR = "players/LoadCharFromDB.txt", @@ -49,6 +51,7 @@ public class FilePrinter { DELETED_CHAR = "players/deletedchars/", UNHANDLED_EVENT = "game/DoesNotExist.txt", SESSION = "players/Sessions.txt", + DCS = "game/disconnections/", EXPLOITS = "game/exploits/", STORAGE = "game/storage/", PACKET_LOGS = "game/packetlogs/", @@ -58,6 +61,9 @@ public class FilePrinter { QUEST_UNCODED = "game/quests/UncodedQuests.txt", AUTOSAVING_CHARACTER = "players/SaveCharAuto.txt", SAVING_CHARACTER = "players/SaveChar.txt", + CHANGE_CHARACTER_NAME = "players/NameChange.txt", + WORLD_TRANSFER = "players/WorldTransfer.txt", + FAMILY_ERROR = "players/FamilyErrors.txt", USED_COMMANDS = "commands/UsedCommands.txt", DEADLOCK_ERROR = "deadlocks/Deadlocks.txt", DEADLOCK_STACK = "deadlocks/Path.txt", diff --git a/src/tools/HexTool.java b/src/tools/HexTool.java index d1853edfcc..e2f1b23d9a 100644 --- a/src/tools/HexTool.java +++ b/src/tools/HexTool.java @@ -21,7 +21,7 @@ */ package tools; -import constants.CharsetConstants; +import constants.string.CharsetConstants; import java.io.ByteArrayOutputStream; public class HexTool { diff --git a/src/tools/IntervalBuilder.java b/src/tools/IntervalBuilder.java index 67c2bf5cbd..0e87949faa 100644 --- a/src/tools/IntervalBuilder.java +++ b/src/tools/IntervalBuilder.java @@ -22,11 +22,12 @@ package tools; import java.awt.geom.Line2D; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.factory.MonitoredReadLockFactory; +import net.server.audit.locks.factory.MonitoredWriteLockFactory; /** * @@ -36,13 +37,13 @@ public class IntervalBuilder { private List intervalLimits = new ArrayList<>(); - protected ReadLock intervalRlock; - protected WriteLock intervalWlock; + protected MonitoredReadLock intervalRlock; + protected MonitoredWriteLock intervalWlock; public IntervalBuilder() { - ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.INTERVAL, true); - intervalRlock = locks.readLock(); - intervalWlock = locks.writeLock(); + MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.INTERVAL, true); + intervalRlock = MonitoredReadLockFactory.createLock(locks); + intervalWlock = MonitoredWriteLockFactory.createLock(locks); } private void refitOverlappedIntervals(int st, int en, int newFrom, int newTo) { diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 09dcf395a0..20d776223b 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -35,54 +35,13 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import net.opcodes.SendOpcode; -import net.server.PlayerCoolDownValueHolder; -import net.server.Server; -import net.server.channel.Channel; -import net.server.channel.handlers.PlayerInteractionHandler; -import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry; -import net.server.guild.MapleAlliance; -import net.server.guild.MapleGuild; -import net.server.guild.MapleGuildCharacter; -import net.server.guild.MapleGuildSummary; -import net.server.world.MapleParty; -import net.server.world.MaplePartyCharacter; -import net.server.world.PartyOperation; -import server.CashShop.CashItem; -import server.CashShop.CashItemFactory; -import server.CashShop.SpecialCashItem; -import server.DueyPackage; -import server.MTSItemInfo; -import server.MapleItemInformationProvider; -import server.MapleShopItem; -import server.MapleTrade; -import server.events.gm.MapleSnowball; -import server.life.MapleMonster; -import server.life.MapleNPC; -import server.life.MobSkill; -import server.maps.MapleHiredMerchant; -import server.maps.MapleDoor; -import server.maps.MapleDoorObject; -import server.maps.MapleDragon; -import server.maps.MapleMap; -import server.maps.MapleMapItem; -import server.maps.MapleMist; -import server.maps.MapleMiniGame; -import server.maps.MapleMiniGame.MiniGameResult; -import server.maps.MaplePlayerShop; -import server.maps.MaplePlayerShopItem; -import server.maps.MapleReactor; -import server.maps.MapleSummon; -import server.life.MaplePlayerNPC; -import server.movement.LifeMovementFragment; -import tools.data.output.LittleEndianWriter; -import tools.data.output.MaplePacketLittleEndianWriter; import client.BuddylistEntry; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleCharacter.SkillEntry; import client.MapleClient; import client.MapleDisease; +import client.MapleFamilyEntitlement; import client.MapleFamilyEntry; import client.MapleKeyBinding; import client.MapleMount; @@ -103,14 +62,60 @@ import client.inventory.ModifyInventory; import client.newyear.NewYearCardRecord; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; -import constants.ExpTable; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.ExpTable; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import constants.skills.Buccaneer; import constants.skills.Corsair; import constants.skills.ThunderBreaker; +import net.opcodes.SendOpcode; +import net.server.PlayerCoolDownValueHolder; +import net.server.Server; +import net.server.channel.Channel; +import net.server.channel.handlers.PlayerInteractionHandler; +import net.server.channel.handlers.SummonDamageHandler.SummonAttackEntry; +import net.server.guild.MapleAlliance; +import net.server.guild.MapleGuild; +import net.server.guild.MapleGuildCharacter; +import net.server.guild.MapleGuildSummary; +import net.server.world.MapleParty; +import net.server.world.MaplePartyCharacter; +import net.server.world.PartyOperation; +import net.server.world.World; +import server.CashShop.CashItem; +import server.CashShop.CashItemFactory; +import server.CashShop.SpecialCashItem; +import server.DueyPackage; +import server.MTSItemInfo; +import server.MapleItemInformationProvider; +import server.MapleShopItem; +import server.MapleTrade; +import server.events.gm.MapleSnowball; +import server.life.MapleMonster; +import server.life.MapleNPC; +import server.life.MaplePlayerNPC; +import server.life.MobSkill; import server.maps.AbstractMapleMapObject; +import server.maps.MapleDoor; +import server.maps.MapleDoorObject; +import server.maps.MapleDragon; +import server.maps.MapleHiredMerchant; +import server.maps.MapleMap; +import server.maps.MapleMapItem; +import server.maps.MapleMiniGame; +import server.maps.MapleMiniGame.MiniGameResult; +import server.maps.MapleMist; +import server.maps.MaplePlayerShop; +import server.maps.MaplePlayerShopItem; +import server.maps.MapleReactor; +import server.maps.MapleSummon; +import server.movement.LifeMovementFragment; +import tools.data.input.SeekableLittleEndianAccessor; +import tools.data.output.LittleEndianWriter; +import tools.data.output.MaplePacketLittleEndianWriter; +import java.util.TimeZone; /** * @@ -119,20 +124,23 @@ import server.maps.AbstractMapleMapObject; public class MaplePacketCreator { public static final List> EMPTY_STATUPDATE = Collections.emptyList(); - private final static long FT_UT_OFFSET = 116444628000000000L; + private final static long FT_UT_OFFSET = 116444736010800000L + (10000L * TimeZone.getDefault().getOffset(System.currentTimeMillis())); // normalize with timezone offset suggested by Ari private final static long DEFAULT_TIME = 150842304000000000L;//00 80 05 BB 46 E6 17 02 public final static long ZERO_TIME = 94354848000000000L;//00 40 E0 FD 3B 37 4F 01 private final static long PERMANENT = 150841440000000000L; // 00 C0 9B 90 7D E5 17 02 - private static long getTime(long realTimestamp) { - if (realTimestamp == -1) { - return DEFAULT_TIME;//high number ll - } else if (realTimestamp == -2) { - return ZERO_TIME; - } else if (realTimestamp == -3) { - return PERMANENT; + private static long getTime(long utcTimestamp) { + if (utcTimestamp < 0 && utcTimestamp >= -3) { + if (utcTimestamp == -1) { + return DEFAULT_TIME; //high number ll + } else if (utcTimestamp == -2) { + return ZERO_TIME; + } else { + return PERMANENT; + } } - return realTimestamp * 10000 + FT_UT_OFFSET; + + return utcTimestamp * 10000 + FT_UT_OFFSET; } public static byte[] showHpHealed(int cid, int amount) { @@ -332,7 +340,7 @@ public class MaplePacketCreator { if (!viewall) { mplew.write(0); } - if (chr.isGM()) { + if (chr.isGM() || chr.isGmJob()) { // thanks Egg Daddy (Ubaware), resinate for noticing GM jobs crashing on non-GM players account mplew.write(0); return; } @@ -344,23 +352,34 @@ public class MaplePacketCreator { } private static void addQuestInfo(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr) { - mplew.writeShort(chr.getStartedQuestsSize()); - for (MapleQuestStatus q : chr.getStartedQuests()) { - mplew.writeShort(q.getQuest().getId()); - mplew.writeMapleAsciiString(q.getQuestData()); - if (q.getQuest().getInfoNumber() > 0) { - mplew.writeShort(q.getQuest().getInfoNumber()); - mplew.writeMapleAsciiString(q.getQuestData()); + List started = chr.getStartedQuests(); + int startedSize = 0; + for (MapleQuestStatus qs : started) { + if (qs.getInfoNumber() > 0) { + startedSize++; + } + startedSize++; + } + mplew.writeShort(startedSize); + for (MapleQuestStatus qs : started) { + mplew.writeShort(qs.getQuest().getId()); + mplew.writeMapleAsciiString(qs.getProgressData()); + + short infoNumber = qs.getInfoNumber(); + if (infoNumber > 0) { + MapleQuestStatus iqs = chr.getQuest(infoNumber); + mplew.writeShort(infoNumber); + mplew.writeMapleAsciiString(iqs.getProgressData()); } } List completed = chr.getCompletedQuests(); mplew.writeShort(completed.size()); - for (MapleQuestStatus q : completed) { - mplew.writeShort(q.getQuest().getId()); - mplew.writeLong(getTime(q.getCompletionTime())); + for (MapleQuestStatus qs : completed) { + mplew.writeShort(qs.getQuest().getId()); + mplew.writeLong(getTime(qs.getCompletionTime())); } } - + private static void addExpirationTime(final MaplePacketLittleEndianWriter mplew, long time) { mplew.writeLong(getTime(time)); // offset expiration time issue found thanks to Thora } @@ -479,8 +498,7 @@ public class MaplePacketCreator { equipped.add((Item) item); } } - Collections.sort(equipped); - for (Item item : equipped) { + for (Item item : equipped) { // equipped doesn't actually need sorting, thanks Pllsz addItemInfo(mplew, item); } mplew.writeShort(0); // start of equip cash @@ -696,7 +714,8 @@ public class MaplePacketCreator { */ public static byte[] getAuthSuccess(MapleClient c) { Server.getInstance().loadAccountCharacters(c); // locks the login session until data is recovered from the cache or the DB. - + Server.getInstance().loadAccountStorages(c); + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.LOGIN_STATUS.getValue()); mplew.writeInt(0); @@ -705,8 +724,8 @@ public class MaplePacketCreator { mplew.write(c.getGender()); boolean canFly = Server.getInstance().canFly(c.getAccID()); - mplew.writeBool((ServerConstants.USE_ENFORCE_ADMIN_ACCOUNT || canFly) ? c.getGMLevel() > 1 : false); // thanks Steve(kaito1410) for pointing the GM account boolean here - mplew.write(((ServerConstants.USE_ENFORCE_ADMIN_ACCOUNT || canFly) && c.getGMLevel() > 1) ? 0x80 : 0); // Admin Byte. 0x80,0x40,0x20.. Rubbish. + mplew.writeBool((YamlConfig.config.server.USE_ENFORCE_ADMIN_ACCOUNT || canFly) ? c.getGMLevel() > 1 : false); // thanks Steve(kaito1410) for pointing the GM account boolean here + mplew.write(((YamlConfig.config.server.USE_ENFORCE_ADMIN_ACCOUNT || canFly) && c.getGMLevel() > 1) ? 0x80 : 0); // Admin Byte. 0x80,0x40,0x20.. Rubbish. mplew.write(0); // Country Code. mplew.writeMapleAsciiString(c.getAccountName()); @@ -718,8 +737,8 @@ public class MaplePacketCreator { mplew.writeInt(1); // 1: Remove the "Select the world you want to play in" - mplew.write(ServerConstants.ENABLE_PIN && !c.canBypassPin() ? 0 : 1); // 0 = Pin-System Enabled, 1 = Disabled - mplew.write(ServerConstants.ENABLE_PIC && !c.canBypassPic() ? (c.getPic() == null || c.getPic().equals("") ? 0 : 1) : 2); // 0 = Register PIC, 1 = Ask for PIC, 2 = Disabled + mplew.write(YamlConfig.config.server.ENABLE_PIN && !c.canBypassPin() ? 0 : 1); // 0 = Pin-System Enabled, 1 = Disabled + mplew.write(YamlConfig.config.server.ENABLE_PIC && !c.canBypassPic() ? (c.getPic() == null || c.getPic().equals("") ? 0 : 1) : 2); // 0 = Register PIC, 1 = Ask for PIC, 2 = Disabled return mplew.getPacket(); } @@ -905,8 +924,8 @@ public class MaplePacketCreator { addCharEntry(mplew, chr, false); } - mplew.write(ServerConstants.ENABLE_PIC && !c.canBypassPic() ? (c.getPic() == null || c.getPic().equals("") ? 0 : 1) : 2); - mplew.writeInt(ServerConstants.COLLECTIVE_CHARSLOT ? chars.size() + c.getAvailableCharacterSlots() : c.getCharacterSlots()); + mplew.write(YamlConfig.config.server.ENABLE_PIC && !c.canBypassPic() ? (c.getPic() == null || c.getPic().equals("") ? 0 : 1) : 2); + mplew.writeInt(YamlConfig.config.server.COLLECTIVE_CHARSLOT ? chars.size() + c.getAvailableCharacterSlots() : c.getCharacterSlots()); return mplew.getPacket(); } @@ -1025,7 +1044,7 @@ public class MaplePacketCreator { for (Pair statupdate : mystats) { if (statupdate.getLeft().getValue() >= 1) { if (statupdate.getLeft().getValue() == 0x1) { - mplew.writeShort(statupdate.getRight().shortValue()); + mplew.write(statupdate.getRight().byteValue()); } else if (statupdate.getLeft().getValue() <= 0x4) { mplew.writeInt(statupdate.getRight()); } else if (statupdate.getLeft().getValue() < 0x20) { @@ -1038,6 +1057,8 @@ public class MaplePacketCreator { } } else if (statupdate.getLeft().getValue() < 0xFFFF) { mplew.writeShort(statupdate.getRight().shortValue()); + } else if (statupdate.getLeft().getValue() == 0x20000) { + mplew.writeShort(statupdate.getRight().shortValue()); } else { mplew.writeInt(statupdate.getRight().intValue()); } @@ -1065,7 +1086,6 @@ public class MaplePacketCreator { mplew.writeShort(chr.getHp()); mplew.writeBool(false); mplew.writeLong(getTime(Server.getInstance().getCurrentTime())); - mplew.skip(18); return mplew.getPacket(); } @@ -1082,7 +1102,6 @@ public class MaplePacketCreator { mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy) mplew.writeInt(spawnPosition.y); mplew.writeLong(getTime(Server.getInstance().getCurrentTime())); - mplew.skip(18); return mplew.getPacket(); } @@ -1194,7 +1213,7 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] removeKite(int objectid, int animationType) { // thanks to Arnah + public static byte[] removeKite(int objectid, int animationType) { // thanks to Arnah (Vertisy) MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.REMOVE_KITE.getValue()); mplew.write(animationType); // 0 is 10/10, 1 just vanishes @@ -1807,6 +1826,11 @@ public class MaplePacketCreator { } public static byte[] dropItemFromMapObject(MapleCharacter player, MapleMapItem drop, Point dropfrom, Point dropto, byte mod) { + int dropType = drop.getDropType(); + if (drop.hasClientsideOwnership(player) && dropType < 3) { + dropType = 2; + } + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.DROP_ITEM_FROM_MAPOBJECT.getValue()); mplew.write(mod); @@ -1814,7 +1838,7 @@ public class MaplePacketCreator { mplew.writeBool(drop.getMeso() > 0); // 1 mesos, 0 item, 2 and above all item meso bag, mplew.writeInt(drop.getItemId()); // drop object ID mplew.writeInt(drop.getClientsideOwnerId()); // owner charid/partyid :) - mplew.write(drop.hasClientsideOwnership(player) ? 2 : drop.getDropType()); // 0 = timeout for non-owner, 1 = timeout for non-owner's party, 2 = FFA, 3 = explosive/FFA + mplew.write(dropType); // 0 = timeout for non-owner, 1 = timeout for non-owner's party, 2 = FFA, 3 = explosive/FFA mplew.writePos(dropto); mplew.writeInt(drop.getDropper().getObjectId()); // dropper oid, found thanks to Li Jixue @@ -1906,17 +1930,12 @@ public class MaplePacketCreator { Integer bv = chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING); if (bv != null) { - if(bv.equals(Corsair.BATTLE_SHIP)) { - mplew.writeInt(1932000); - mplew.writeInt(Corsair.BATTLE_SHIP); + MapleMount mount = chr.getMount(); + if (mount != null) { + mplew.writeInt(mount.getItemId()); + mplew.writeInt(mount.getSkillId()); } else { - final Item mount = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18); - if(mount != null) { - mplew.writeInt(mount.getItemId()); - mplew.writeInt(1004); - } else { - mplew.writeLong(0); - } + mplew.writeLong(0); } } else { mplew.writeLong(0); @@ -2040,7 +2059,8 @@ public class MaplePacketCreator { addRingLook(mplew, chr, false); // friendship addMarriageRingLook(target, mplew, chr); encodeNewYearCardInfo(mplew, chr); // new year seems to crash sometimes... - mplew.skip(2); + mplew.write(0); + mplew.write(0); mplew.write(chr.getTeam());//only needed in specific fields return mplew.getPacket(); } @@ -2269,33 +2289,41 @@ public class MaplePacketCreator { return mplew.getPacket(); } + private static void rebroadcastMovementList(LittleEndianWriter lew, SeekableLittleEndianAccessor slea, long movementDataLength) { + //movement command length is sent by client, probably not a big issue? (could be calculated on server) + //if multiple write/reads are slow, could use a (cached?) byte[] buffer + for(long i = 0; i < movementDataLength; i++) { + lew.write(slea.readByte()); + } + } + private static void serializeMovementList(LittleEndianWriter lew, List moves) { - lew.write(moves.size()); - for (LifeMovementFragment move : moves) { - move.serialize(lew); - } + lew.write(moves.size()); + for(LifeMovementFragment move : moves) { + move.serialize(lew); + } } - public static byte[] movePlayer(int cid, List moves) { + public static byte[] movePlayer(int cid, SeekableLittleEndianAccessor movementSlea, long movementDataLength) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.MOVE_PLAYER.getValue()); mplew.writeInt(cid); mplew.writeInt(0); - serializeMovementList(mplew, moves); + rebroadcastMovementList(mplew, movementSlea, movementDataLength); return mplew.getPacket(); } - public static byte[] moveSummon(int cid, int oid, Point startPos, List moves) { + public static byte[] moveSummon(int cid, int oid, Point startPos, SeekableLittleEndianAccessor movementSlea, long movementDataLength) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.MOVE_SUMMON.getValue()); mplew.writeInt(cid); mplew.writeInt(oid); mplew.writePos(startPos); - serializeMovementList(mplew, moves); + rebroadcastMovementList(mplew, movementSlea, movementDataLength); return mplew.getPacket(); } - public static byte[] moveMonster(int oid, boolean skillPossible, int skill, int skillId, int skillLevel, int pOption, Point startPos, List moves) { + public static byte[] moveMonster(int oid, boolean skillPossible, int skill, int skillId, int skillLevel, int pOption, Point startPos, SeekableLittleEndianAccessor movementSlea, long movementDataLength) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.MOVE_MONSTER.getValue()); mplew.writeInt(oid); @@ -2306,7 +2334,7 @@ public class MaplePacketCreator { mplew.write(skillLevel); mplew.writeShort(pOption); mplew.writePos(startPos); - serializeMovementList(mplew, moves); + rebroadcastMovementList(mplew, movementSlea, movementDataLength); return mplew.getPacket(); } @@ -2511,25 +2539,14 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getScrollEffect(int chr, ScrollResult scrollSuccess, boolean legendarySpirit) { + public static byte[] getScrollEffect(int chr, ScrollResult scrollSuccess, boolean legendarySpirit, boolean whiteScroll) { // thanks to Rien dev team final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.SHOW_SCROLL_EFFECT.getValue()); mplew.writeInt(chr); - switch (scrollSuccess) { - case SUCCESS: - mplew.writeShort(1); - mplew.writeShort(legendarySpirit ? 1 : 0); - break; - case FAIL: - mplew.writeShort(0); - mplew.writeShort(legendarySpirit ? 1 : 0); - break; - case CURSE: - mplew.write(0); - mplew.write(1); - mplew.writeShort(legendarySpirit ? 1 : 0); - break; - } + mplew.writeBool(scrollSuccess == ScrollResult.SUCCESS); + mplew.writeBool(scrollSuccess == ScrollResult.CURSE); + mplew.writeBool(legendarySpirit); + mplew.writeBool(whiteScroll); return mplew.getPacket(); } @@ -2579,6 +2596,13 @@ public class MaplePacketCreator { return mplew.getPacket(); } + public static byte[] updateWitchTowerScore(int score) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.WITCH_TOWER_SCORE_UPDATE.getValue()); + mplew.write(score); + return mplew.getPacket(); + } + public static byte[] silentRemoveItemFromMap(int oid) { return removeItemFromMap(oid, 1, 0); } @@ -2707,7 +2731,17 @@ public class MaplePacketCreator { } /** - * state 0 = del ok state 12 = invalid bday state 14 = incorrect pic + * State : + * 0x00 = success + * 0x06 = Trouble logging into the game? + * 0x09 = Unknown error + * 0x0A = Could not be processed due to too many connection requests to the server. + * 0x12 = invalid bday + * 0x14 = incorrect pic + * 0x16 = Cannot delete a guild master. + * 0x18 = Cannot delete a character with a pending wedding. + * 0x1A = Cannot delete a character with a pending world transfer. + * 0x1D = Cannot delete a character that has a family. * * @param cid * @param state @@ -2815,9 +2849,9 @@ public class MaplePacketCreator { } ArrayList medalQuests = new ArrayList<>(); List completed = chr.getCompletedQuests(); - for (MapleQuestStatus q : completed) { - if (q.getQuest().getId() >= 29000) { // && q.getQuest().getId() <= 29923 - medalQuests.add(q.getQuest().getId()); + for (MapleQuestStatus qs : completed) { + if (qs.getQuest().getId() >= 29000) { // && q.getQuest().getId() <= 29923 + medalQuests.add(qs.getQuest().getId()); } } @@ -2965,18 +2999,20 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] updateQuest(MapleQuestStatus q, boolean infoUpdate) { + public static byte[] updateQuest(MapleCharacter chr, MapleQuestStatus qs, boolean infoUpdate) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.SHOW_STATUS_INFO.getValue()); mplew.write(1); - mplew.writeShort(infoUpdate ? q.getQuest().getInfoNumber() : q.getQuest().getId()); if (infoUpdate) { + MapleQuestStatus iqs = chr.getQuest(qs.getInfoNumber()); + mplew.writeShort(iqs.getQuestID()); mplew.write(1); + mplew.writeMapleAsciiString(iqs.getProgressData()); } else { - mplew.write(q.getStatus().getId()); + mplew.writeShort(qs.getQuest().getId()); + mplew.write(qs.getStatus().getId()); + mplew.writeMapleAsciiString(qs.getProgressData()); } - - mplew.writeMapleAsciiString(q.getQuestData()); mplew.skip(5); return mplew.getPacket(); } @@ -3170,23 +3206,23 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getPlayerShopChat(MapleCharacter c, String chat, boolean owner) { + public static byte[] getPlayerShopChat(MapleCharacter chr, String chat, boolean owner) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.CHAT.getCode()); mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode()); mplew.write(owner ? 0 : 1); - mplew.writeMapleAsciiString(c.getName() + " : " + chat); + mplew.writeMapleAsciiString(chr.getName() + " : " + chat); return mplew.getPacket(); } - public static byte[] getPlayerShopNewVisitor(MapleCharacter c, int slot) { + public static byte[] getPlayerShopNewVisitor(MapleCharacter chr, int slot) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.VISIT.getCode()); mplew.write(slot); - addCharLook(mplew, c, false); - mplew.writeMapleAsciiString(c.getName()); + addCharLook(mplew, chr, false); + mplew.writeMapleAsciiString(chr.getName()); return mplew.getPacket(); } @@ -3200,22 +3236,22 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getTradePartnerAdd(MapleCharacter c) { + public static byte[] getTradePartnerAdd(MapleCharacter chr) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.VISIT.getCode()); mplew.write(1); - addCharLook(mplew, c, false); - mplew.writeMapleAsciiString(c.getName()); + addCharLook(mplew, chr, false); + mplew.writeMapleAsciiString(chr.getName()); return mplew.getPacket(); } - public static byte[] tradeInvite(MapleCharacter c) { + public static byte[] tradeInvite(MapleCharacter chr) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.INVITE.getCode()); mplew.write(3); - mplew.writeMapleAsciiString(c.getName()); + mplew.writeMapleAsciiString(chr.getName()); mplew.write(new byte[]{(byte) 0xB7, (byte) 0x50, 0, 0}); return mplew.getPacket(); } @@ -3441,6 +3477,44 @@ public class MaplePacketCreator { mplew.writeInt(0); return mplew.getPacket(); } + + // thanks NPC Quiz packets thanks to Eric + public static byte[] OnAskQuiz(int nSpeakerTypeID, int nSpeakerTemplateID, int nResCode, String sTitle, String sProblemText, String sHintText, int nMinInput, int nMaxInput, int tRemainInitialQuiz) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.NPC_TALK.getValue()); + mplew.write(nSpeakerTypeID); + mplew.writeInt(nSpeakerTemplateID); + mplew.write(0x6); + mplew.write(0); + mplew.write(nResCode); + if (nResCode == 0x0) {//fail has no bytes <3 + mplew.writeMapleAsciiString(sTitle); + mplew.writeMapleAsciiString(sProblemText); + mplew.writeMapleAsciiString(sHintText); + mplew.writeShort(nMinInput); + mplew.writeShort(nMaxInput); + mplew.writeInt(tRemainInitialQuiz); + } + return mplew.getPacket(); + } + + public static byte[] OnAskSpeedQuiz(int nSpeakerTypeID, int nSpeakerTemplateID, int nResCode, int nType, int dwAnswer, int nCorrect, int nRemain, int tRemainInitialQuiz) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.NPC_TALK.getValue()); + mplew.write(nSpeakerTypeID); + mplew.writeInt(nSpeakerTemplateID); + mplew.write(0x7); + mplew.write(0); + mplew.write(nResCode); + if (nResCode == 0x0) {//fail has no bytes <3 + mplew.writeInt(nType); + mplew.writeInt(dwAnswer); + mplew.writeInt(nCorrect); + mplew.writeInt(nRemain); + mplew.writeInt(tRemainInitialQuiz); + } + return mplew.getPacket(); + } public static byte[] showBuffeffect(int cid, int skillid, int effectid) { return showBuffeffect(cid, skillid, effectid, (byte) 3); @@ -4246,7 +4320,7 @@ public class MaplePacketCreator { public static byte[] cancelChair(int id) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CANCEL_CHAIR.getValue()); - if (id == -1) { + if (id < 0) { mplew.write(0); } else { mplew.write(1); @@ -4377,15 +4451,15 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] showGuildInfo(MapleCharacter c) { + public static byte[] showGuildInfo(MapleCharacter chr) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.GUILD_OPERATION.getValue()); mplew.write(0x1A); //signature for showing guild info - if (c == null) { //show empty guild (used for leaving, expelled) + if (chr == null) { //show empty guild (used for leaving, expelled) mplew.write(0); return mplew.getPacket(); } - MapleGuild g = c.getClient().getWorldServer().getGuild(c.getMGC()); + MapleGuild g = chr.getClient().getWorldServer().getGuild(chr.getMGC()); if (g == null) { //failed to read from DB - don't show a guild mplew.write(0); return mplew.getPacket(); @@ -5230,17 +5304,17 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getMiniGameNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) { + public static byte[] getMiniGameNewVisitor(MapleMiniGame minigame, MapleCharacter chr, int slot) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.VISIT.getCode()); mplew.write(slot); - addCharLook(mplew, c, false); - mplew.writeMapleAsciiString(c.getName()); + addCharLook(mplew, chr, false); + mplew.writeMapleAsciiString(chr.getName()); mplew.writeInt(1); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, true)); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, true)); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, true)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.WIN, true)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.TIE, true)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.LOSS, true)); mplew.writeInt(minigame.getVisitorScore()); return mplew.getPacket(); } @@ -5385,17 +5459,17 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getMatchCardNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) { + public static byte[] getMatchCardNewVisitor(MapleMiniGame minigame, MapleCharacter chr, int slot) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.VISIT.getCode()); mplew.write(slot); - addCharLook(mplew, c, false); - mplew.writeMapleAsciiString(c.getName()); + addCharLook(mplew, chr, false); + mplew.writeMapleAsciiString(chr.getName()); mplew.writeInt(1); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, false)); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, false)); - mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, false)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.WIN, false)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.TIE, false)); + mplew.writeInt(chr.getMiniGamePoints(MiniGameResult.LOSS, false)); mplew.writeInt(minigame.getVisitorScore()); return mplew.getPacket(); } @@ -5414,6 +5488,41 @@ public class MaplePacketCreator { } return mplew.getPacket(); } + + // RPS_GAME packets thanks to Arnah (Vertisy) + public static byte[] openRPSNPC() { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.RPS_GAME.getValue()); + mplew.write(8);// open npc + mplew.writeInt(9000019); + return mplew.getPacket(); + } + + public static byte[] rpsMesoError(int mesos) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.RPS_GAME.getValue()); + mplew.write(0x06); + if (mesos != -1) { + mplew.writeInt(mesos); + } + return mplew.getPacket(); + } + + public static byte[] rpsSelection(byte selection, byte answer) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.RPS_GAME.getValue()); + mplew.write(0x0B);// 11l + mplew.write(selection); + mplew.write(answer); + return mplew.getPacket(); + } + + public static byte[] rpsMode(byte mode) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.RPS_GAME.getValue()); + mplew.write(mode); + return mplew.getPacket(); + } public static byte[] fredrickMessage(byte operation) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -5462,19 +5571,19 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] addOmokBox(MapleCharacter c, int ammount, int type) { + public static byte[] addOmokBox(MapleCharacter chr, int ammount, int type) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue()); - mplew.writeInt(c.getId()); - addAnnounceBox(mplew, c.getMiniGame(), ammount, type); + mplew.writeInt(chr.getId()); + addAnnounceBox(mplew, chr.getMiniGame(), ammount, type); return mplew.getPacket(); } - public static byte[] addMatchCardBox(MapleCharacter c, int ammount, int type) { + public static byte[] addMatchCardBox(MapleCharacter chr, int ammount, int type) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue()); - mplew.writeInt(c.getId()); - addAnnounceBox(mplew, c.getMiniGame(), ammount, type); + mplew.writeInt(chr.getId()); + addAnnounceBox(mplew, chr.getMiniGame(), ammount, type); return mplew.getPacket(); } @@ -5486,23 +5595,23 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] getPlayerShopChat(MapleCharacter c, String chat, byte slot) { + public static byte[] getPlayerShopChat(MapleCharacter chr, String chat, byte slot) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.CHAT.getCode()); mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode()); mplew.write(slot); - mplew.writeMapleAsciiString(c.getName() + " : " + chat); + mplew.writeMapleAsciiString(chr.getName() + " : " + chat); return mplew.getPacket(); } - public static byte[] getTradeChat(MapleCharacter c, String chat, boolean owner) { + public static byte[] getTradeChat(MapleCharacter chr, String chat, boolean owner) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue()); mplew.write(PlayerInteractionHandler.Action.CHAT.getCode()); mplew.write(PlayerInteractionHandler.Action.CHAT_THING.getCode()); mplew.write(owner ? 0 : 1); - mplew.writeMapleAsciiString(c.getName() + " : " + chat); + mplew.writeMapleAsciiString(chr.getName() + " : " + chat); return mplew.getPacket(); } @@ -6005,18 +6114,33 @@ public class MaplePacketCreator { 8: must quit family, 9: unknown error */ - public static byte[] sendWorldTransferRules(int error) { + public static byte[] sendWorldTransferRules(int error, MapleClient c) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CASHSHOP_CHECK_TRANSFER_WORLD_POSSIBLE_RESULT.getValue()); - mplew.writeInt(0); - mplew.write(0); + mplew.writeInt(0); //ignored mplew.write(error); mplew.writeInt(0); - + mplew.writeBool(error == 0); //0 = ?, otherwise list servers + if(error == 0) { + List worlds = Server.getInstance().getWorlds(); + mplew.writeInt(worlds.size()); + for(World world : worlds) { + mplew.writeMapleAsciiString(GameConstants.WORLD_NAMES[world.getId()]); + } + } return mplew.getPacket(); } - /* 1: name change already submitted + public static byte[] showWorldTransferSuccess(Item item, int accountId) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); + mplew.write(0xA0); + addCashItemInformation(mplew, item, accountId); + return mplew.getPacket(); + } + + /* 0: no error, send rules + 1: name change already submitted 2: name change within a month 3: recently banned 4: unknown error @@ -6031,15 +6155,46 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] sendNameTransferCheck(boolean canUseName) { + /* 0: Name available + * >0: Name is in use + * <0: Unknown error + */ + + public static byte[] sendNameTransferCheck(String availableName, boolean canUseName) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CASHSHOP_CHECK_NAME_CHANGE.getValue()); - mplew.writeShort(0); + //Send provided name back to client to add to temporary cache of checked & accepted names + mplew.writeMapleAsciiString(availableName); mplew.writeBool(!canUseName); - return mplew.getPacket(); } + public static byte[] showNameChangeSuccess(Item item, int accountId) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); + mplew.write(0x9E); + addCashItemInformation(mplew, item, accountId); + return mplew.getPacket(); + } + + public static byte[] showNameChangeCancel(boolean success) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CANCEL_NAME_CHANGE_RESULT.getValue()); + mplew.writeBool(success); + if(!success) mplew.write(0); + //mplew.writeMapleAsciiString("Custom message."); //only if ^ != 0 + return mplew.getPacket(); + } + + public static byte[] showWorldTransferCancel(boolean success) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CANCEL_TRANSFER_WORLD_RESULT.getValue()); + mplew.writeBool(success); + if(!success) mplew.write(0); + //mplew.writeMapleAsciiString("Custom message."); //only if ^ != 0 + return mplew.getPacket(); + } + public static byte[] showMTSCash(MapleCharacter p) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.MTS_OPERATION2.getValue()); @@ -6138,16 +6293,23 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] showCouponRedeemedItem(int itemid) { + public static byte[] showCouponRedeemedItems(int accountId, int maplePoints, int mesos, List cashItems, List> items) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); - mplew.writeShort(0x49); //v72 - mplew.writeInt(0); - mplew.writeInt(1); - mplew.writeShort(1); - mplew.writeShort(0x1A); - mplew.writeInt(itemid); - mplew.writeInt(0); + mplew.write(0x59); + mplew.write((byte)cashItems.size()); + for(Item item : cashItems) { + addCashItemInformation(mplew, item, accountId); + } + mplew.writeInt(maplePoints); + mplew.writeInt(items.size()); + for(Pair itemPair : items) { + int quantity = itemPair.getLeft(); + mplew.writeShort((short) quantity); //quantity (0 = 1 for cash items) + mplew.writeShort(0x1F); //0 = ?, >=0x20 = ?, <0x20 = ? (does nothing?) + mplew.writeInt(itemPair.getRight()); + } + mplew.writeInt(mesos); return mplew.getPacket(); } @@ -6259,18 +6421,16 @@ public class MaplePacketCreator { } public static byte[] loadFamily(MapleCharacter player) { - String[] title = {"Family Reunion", "Summon Family", "My Drop Rate 1.5x (15 min)", "My EXP 1.5x (15 min)", "Family Bonding (30 min)", "My Drop Rate 2x (15 min)", "My EXP 2x (15 min)", "My Drop Rate 2x (30 min)", "My EXP 2x (30 min)", "My Party Drop Rate 2x (30 min)", "My Party EXP 2x (30 min)"}; - String[] description = {"[Target] Me\n[Effect] Teleport directly to the Family member of your choice.", "[Target] 1 Family member\n[Effect] Summon a Family member of choice to the map you're in.", "[Target] Me\n[Time] 15 min.\n[Effect] Monster drop rate will be increased #c1.5x#.\n* If the Drop Rate event is in progress, this will be nullified.", "[Target] Me\n[Time] 15 min.\n[Effect] EXP earned from hunting will be increased #c1.5x#.\n* If the EXP event is in progress, this will be nullified.", "[Target] At least 6 Family members online that are below me in the Pedigree\n[Time] 30 min.\n[Effect] Monster drop rate and EXP earned will be increased #c2x#. \n* If the EXP event is in progress, this will be nullified.", "[Target] Me\n[Time] 15 min.\n[Effect] Monster drop rate will be increased #c2x#.\n* If the Drop Rate event is in progress, this will be nullified.", "[Target] Me\n[Time] 15 min.\n[Effect] EXP earned from hunting will be increased #c2x#.\n* If the EXP event is in progress, this will be nullified.", "[Target] Me\n[Time] 30 min.\n[Effect] Monster drop rate will be increased #c2x#.\n* If the Drop Rate event is in progress, this will be nullified.", "[Target] Me\n[Time] 30 min.\n[Effect] EXP earned from hunting will be increased #c2x#. \n* If the EXP event is in progress, this will be nullified.", "[Target] My party\n[Time] 30 min.\n[Effect] Monster drop rate will be increased #c2x#.\n* If the Drop Rate event is in progress, this will be nullified.", "[Target] My party\n[Time] 30 min.\n[Effect] EXP earned from hunting will be increased #c2x#.\n* If the EXP event is in progress, this will be nullified."}; - int[] repCost = {3, 5, 7, 8, 10, 12, 15, 20, 25, 40, 50}; final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.FAMILY_PRIVILEGE_LIST.getValue()); - mplew.writeInt(11); - for (int i = 0; i < 11; i++) { - mplew.write(i > 4 ? (i % 2) + 1 : i); - mplew.writeInt(repCost[i] * 100); - mplew.writeInt(1); - mplew.writeMapleAsciiString(title[i]); - mplew.writeMapleAsciiString(description[i]); + mplew.writeInt(MapleFamilyEntitlement.values().length); + for (int i = 0; i < MapleFamilyEntitlement.values().length; i++) { + MapleFamilyEntitlement entitlement = MapleFamilyEntitlement.values()[i]; + mplew.write(i <= 1 ? 1 : 2); //type + mplew.writeInt(entitlement.getRepCost()); + mplew.writeInt(entitlement.getUsageLimit()); + mplew.writeMapleAsciiString(entitlement.getName()); + mplew.writeMapleAsciiString(entitlement.getDescription()); } return mplew.getPacket(); } @@ -6279,6 +6439,9 @@ public class MaplePacketCreator { * Family Result Message * * Possible values for type:
+ * 64: You cannot add this character as a junior. + * 65: The name could not be found or is not online. + * 66: You belong to the same family. * 67: You do not belong to the same family.
* 69: The character you wish to add as\r\na Junior must be in the same * map.
@@ -6316,27 +6479,121 @@ public class MaplePacketCreator { } public static byte[] getFamilyInfo(MapleFamilyEntry f) { + if(f == null) return getEmptyFamilyInfo(); final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.FAMILY_INFO_RESULT.getValue()); mplew.writeInt(f.getReputation()); // cur rep left mplew.writeInt(f.getTotalReputation()); // tot rep left mplew.writeInt(f.getTodaysRep()); // todays rep - mplew.writeShort(f.getJuniors()); // juniors added - mplew.writeShort(f.getTotalJuniors()); // juniors allowed + mplew.writeShort(f.getJuniorCount()); // juniors added + mplew.writeShort(2); // juniors allowed mplew.writeShort(0); //Unknown - mplew.writeInt(f.getId()); // id? - mplew.writeMapleAsciiString(f.getFamilyName()); + mplew.writeInt(f.getFamily().getLeader().getChrId()); // Leader ID (Allows setting message) + mplew.writeMapleAsciiString(f.getFamily().getName()); + mplew.writeMapleAsciiString(f.getFamily().getMessage()); //family message + mplew.writeInt(MapleFamilyEntitlement.values().length); //Entitlement info count + for(MapleFamilyEntitlement entitlement : MapleFamilyEntitlement.values()) { + mplew.writeInt(entitlement.ordinal()); //ID + mplew.writeInt(f.isEntitlementUsed(entitlement) ? 1 : 0); //Used count + } + return mplew.getPacket(); + } + + private static byte[] getEmptyFamilyInfo() { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_INFO_RESULT.getValue()); + mplew.writeInt(0); // cur rep left + mplew.writeInt(0); // tot rep left + mplew.writeInt(0); // todays rep + mplew.writeShort(0); // juniors added + mplew.writeShort(2); // juniors allowed + mplew.writeShort(0); //Unknown + mplew.writeInt(0); // Leader ID (Allows setting message) + mplew.writeMapleAsciiString(""); + mplew.writeMapleAsciiString(""); //family message mplew.writeInt(0); - mplew.writeShort(0); return mplew.getPacket(); } - public static byte[] showPedigree(int chrid, Map members) { + public static byte[] showPedigree(MapleFamilyEntry entry) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.FAMILY_CHART_RESULT.getValue()); - //Hmmm xD + mplew.writeInt(entry.getChrId()); //ID of viewed player's pedigree, can't be leader? + List superJuniors = new ArrayList(4); + boolean hasOtherJunior = false; + int entryCount = 2; //2 guaranteed, leader and self + entryCount += Math.min(2, entry.getTotalSeniors()); + //needed since MaplePacketLittleEndianWriter doesn't have any seek functionality + if(entry.getSenior() != null) { + if(entry.getSenior().getJuniorCount() == 2) { + entryCount++; + hasOtherJunior = true; + } + } + for(MapleFamilyEntry junior : entry.getJuniors()) { + if(junior == null) continue; + entryCount++; + for(MapleFamilyEntry superJunior : junior.getJuniors()) { + if(superJunior == null) continue; + entryCount++; + superJuniors.add(superJunior); + } + } + //write entries + boolean missingEntries = entryCount == 2; //pedigree requires at least 3 entries to show leader, might only have 2 if leader's juniors leave + if(missingEntries) entryCount++; + mplew.writeInt(entryCount); //player count + addPedigreeEntry(mplew, entry.getFamily().getLeader()); + if(entry.getSenior() != null) { + if(entry.getSenior().getSenior() != null) addPedigreeEntry(mplew, entry.getSenior().getSenior()); + addPedigreeEntry(mplew, entry.getSenior()); + } + addPedigreeEntry(mplew, entry); + if(hasOtherJunior) { //must be sent after own entry + MapleFamilyEntry otherJunior = entry.getSenior().getOtherJunior(entry); + if(otherJunior != null) addPedigreeEntry(mplew, otherJunior); + } + if(missingEntries) addPedigreeEntry(mplew, entry); + for(MapleFamilyEntry junior : entry.getJuniors()) { + if(junior == null) continue; + addPedigreeEntry(mplew, junior); + for(MapleFamilyEntry superJunior : junior.getJuniors()) { + if(superJunior != null) addPedigreeEntry(mplew, superJunior); + } + } + mplew.writeInt(2 + superJuniors.size()); //member info count + // 0 = total seniors, -1 = total members, otherwise junior count of ID + mplew.writeInt(-1); + mplew.writeInt(entry.getFamily().getTotalMembers()); + mplew.writeInt(0); + mplew.writeInt(entry.getTotalSeniors()); //client subtracts provided seniors + for(MapleFamilyEntry superJunior : superJuniors) { + mplew.writeInt(superJunior.getChrId()); + mplew.writeInt(superJunior.getTotalJuniors()); + } + mplew.writeInt(0); //another loop count (entitlements used) + //mplew.writeInt(1); //entitlement index + //mplew.writeInt(2); //times used + mplew.writeShort(entry.getJuniorCount() >= 2 ? 0 : 2); //0 disables Add button (only if viewing own pedigree) return mplew.getPacket(); } + + private static void addPedigreeEntry(MaplePacketLittleEndianWriter mplew, MapleFamilyEntry entry) { + MapleCharacter chr = entry.getChr(); + boolean isOnline = chr != null; + mplew.writeInt(entry.getChrId()); //ID + mplew.writeInt(entry.getSenior() != null ? entry.getSenior().getChrId() : 0); //parent ID + mplew.writeShort(entry.getJob().getId()); //job id + mplew.write(entry.getLevel()); //level + mplew.writeBool(isOnline); //isOnline + mplew.writeInt(entry.getReputation()); //current rep + mplew.writeInt(entry.getTotalReputation()); //total rep + mplew.writeInt(entry.getRepsToSenior()); //reps recorded to senior + mplew.writeInt(entry.getTodaysRep()); + mplew.writeInt(isOnline ? ((chr.isAwayFromWorld() || chr.getCashShop().isOpened()) ? -1 : chr.getClient().getChannel() - 1) : 0); + mplew.writeInt(isOnline ? (int) (chr.getLoggedInTime() / 60000) : 0); //time online in minutes + mplew.writeMapleAsciiString(entry.getName()); //name + } public static byte[] updateAreaInfo(int area, String info) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -6678,6 +6935,72 @@ public class MaplePacketCreator { mplew.writeShort(quest); return mplew.getPacket(); } + + // MAKER_RESULT packets thanks to Arnah (Vertisy) + public static byte[] makerResult(boolean success, int itemMade, int itemCount, int mesos, List> itemsLost, int catalystID, List INCBuffGems) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.MAKER_RESULT.getValue()); + mplew.writeInt(success ? 0 : 1); // 0 = success, 1 = fail + mplew.writeInt(1); // 1 or 2 doesn't matter, same methods + mplew.writeBool(!success); + if (success) { + mplew.writeInt(itemMade); + mplew.writeInt(itemCount); + } + mplew.writeInt(itemsLost.size()); // Loop + for (Pair item : itemsLost) { + mplew.writeInt(item.getLeft()); + mplew.writeInt(item.getRight()); + } + mplew.writeInt(INCBuffGems.size()); + for (Integer gem : INCBuffGems) { + mplew.writeInt(gem); + } + if (catalystID != -1) { + mplew.write(1); // stimulator + mplew.writeInt(catalystID); + } else { + mplew.write(0); + } + + mplew.writeInt(mesos); + return mplew.getPacket(); + } + + public static byte[] makerResultCrystal(int itemIdGained, int itemIdLost) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.MAKER_RESULT.getValue()); + mplew.writeInt(0); // Always successful! + mplew.writeInt(3); // Monster Crystal + mplew.writeInt(itemIdGained); + mplew.writeInt(itemIdLost); + return mplew.getPacket(); + } + + public static byte[] makerResultDesynth(int itemId, int mesos, List> itemsGained) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.MAKER_RESULT.getValue()); + mplew.writeInt(0); // Always successful! + mplew.writeInt(4); // Mode Desynth + mplew.writeInt(itemId); // Item desynthed + mplew.writeInt(itemsGained.size()); // Loop of items gained, (int, int) + for (Pair item : itemsGained) { + mplew.writeInt(item.getLeft()); + mplew.writeInt(item.getRight()); + } + mplew.writeInt(mesos); // Mesos spent. + return mplew.getPacket(); + } + + public static byte[] makerEnableActions() { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.MAKER_RESULT.getValue()); + mplew.writeInt(0); // Always successful! + mplew.writeInt(0); // Monster Crystal + mplew.writeInt(0); + mplew.writeInt(0); + return mplew.getPacket(); + } public static byte[] getMultiMegaphone(String[] messages, int channel, boolean showEar) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -6752,6 +7075,46 @@ public class MaplePacketCreator { mplew.writeMapleAsciiString(inviter); return mplew.getPacket(); } + + public static byte[] sendFamilySummonRequest(String familyName, String from) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_SUMMON_REQUEST.getValue()); + mplew.writeMapleAsciiString(from); + mplew.writeMapleAsciiString(familyName); + return mplew.getPacket(); + } + + public static byte[] sendFamilyLoginNotice(String name, boolean loggedIn) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_NOTIFY_LOGIN_OR_LOGOUT.getValue()); + mplew.writeBool(loggedIn); + mplew.writeMapleAsciiString(name); + return mplew.getPacket(); + } + + public static byte[] sendFamilyJoinResponse(boolean accepted, String added) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_JOIN_REQUEST_RESULT.getValue()); + mplew.write(accepted ? 1 : 0); + mplew.writeMapleAsciiString(added); + return mplew.getPacket(); + } + + public static byte[] getSeniorMessage(String name) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_JOIN_ACCEPTED.getValue()); + mplew.writeMapleAsciiString(name); + mplew.writeInt(0); + return mplew.getPacket(); + } + + public static byte[] sendGainRep(int gain, String from) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FAMILY_REP_GAIN.getValue()); + mplew.writeInt(gain); + mplew.writeMapleAsciiString(from); + return mplew.getPacket(); + } public static byte[] showBoughtCashPackage(List cashPackage, int accountId) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -6787,14 +7150,14 @@ public class MaplePacketCreator { public static byte[] onCashItemGachaponOpenFailed(){ MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CASHSHOP_CASH_ITEM_GACHAPON_RESULT.getValue()); - mplew.write(189); + mplew.write(0xE4); return mplew.getPacket(); } public static byte[] onCashGachaponOpenSuccess(int accountid, long sn, int remainingBoxes, Item item, int itemid, int nSelectedItemCount, boolean bJackpot){ MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CASHSHOP_CASH_ITEM_GACHAPON_RESULT.getValue()); - mplew.write(190); + mplew.write(0xE5); // subopcode thanks to Ubaware mplew.writeLong(sn);// sn of the box used mplew.writeInt(remainingBoxes); addCashItemInformation(mplew, item, accountid); @@ -6993,30 +7356,6 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] sendFamilyJoinResponse(boolean accepted, String added) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.FAMILY_JOIN_REQUEST_RESULT.getValue()); - mplew.write(accepted ? 1 : 0); - mplew.writeMapleAsciiString(added); - return mplew.getPacket(); - } - - public static byte[] getSeniorMessage(String name) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.FAMILY_JOIN_ACCEPTED.getValue()); - mplew.writeMapleAsciiString(name); - mplew.writeInt(0); - return mplew.getPacket(); - } - - public static byte[] sendGainRep(int gain, int mode) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.FAMILY_FAMOUS_POINT_INC_RESULT.getValue()); - mplew.writeInt(gain); - mplew.writeShort(0); - return mplew.getPacket(); - } - public static byte[] removeItemFromDuey(boolean remove, int Package) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PARCEL.getValue()); @@ -7025,13 +7364,21 @@ public class MaplePacketCreator { mplew.write(remove ? 3 : 4); return mplew.getPacket(); } + + public static byte[] sendDueyParcelReceived(String from, boolean quick) { // thanks inhyuk + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.PARCEL.getValue()); + mplew.write(0x19); + mplew.writeMapleAsciiString(from); + mplew.writeBool(quick); + return mplew.getPacket(); + } - public static byte[] sendDueyNotification(boolean quickDelivery) { + public static byte[] sendDueyParcelNotification(boolean quick) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PARCEL.getValue()); mplew.write(0x1B); - mplew.writeBool(quickDelivery); // 0 : package received, 1 : quick delivery package - + mplew.writeBool(quick); // 0 : package received, 1 : quick delivery package return mplew.getPacket(); } @@ -7039,7 +7386,7 @@ public class MaplePacketCreator { return sendDuey(operation, null); } - public static byte[] sendDuey(byte operation, List packages) { + public static byte[] sendDuey(int operation, List packages) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PARCEL.getValue()); mplew.write(operation); @@ -7057,15 +7404,15 @@ public class MaplePacketCreator { mplew.writeLong(getTime(dp.sentTimeInMilliseconds())); String msg = dp.getMessage(); - if (!msg.isEmpty()) { - mplew.writeInt(1); - mplew.writeAsciiString(msg); - for (int i = msg.length(); i < 200; i++) { - mplew.write(0); - } + if (msg != null) { + mplew.writeInt(1); + mplew.writeAsciiString(msg); + for (int i = msg.length(); i < 200; i++) { + mplew.write(0); + } } else { - mplew.writeInt(0); - mplew.skip(200); + mplew.writeInt(0); + mplew.skip(200); } mplew.write(0); @@ -7261,20 +7608,12 @@ public class MaplePacketCreator { return builder.toString(); } - public static byte[] MobDamageMobFriendly(MapleMonster mob, int damage) { + public static byte[] MobDamageMobFriendly(MapleMonster mob, int damage, int remainingHp) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.DAMAGE_MONSTER.getValue()); mplew.writeInt(mob.getObjectId()); mplew.write(1); // direction ? mplew.writeInt(damage); - - mob.applyAndGetHpDamage(damage, false); - int remainingHp = mob.getHp(); - if(remainingHp <= 0) { - remainingHp = 0; - mob.getMap().removeMapObject(mob); - } - mplew.writeInt(remainingHp); mplew.writeInt(mob.getMaxHp()); return mplew.getPacket(); @@ -7549,8 +7888,20 @@ public class MaplePacketCreator { return mplew.getPacket(); } + public static byte[] showBoughtCashRing(Item ring, String recipient, int accountId) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); + mplew.write(0x87); + addCashItemInformation(mplew, ring, accountId); + mplew.writeMapleAsciiString(recipient); + mplew.writeInt(ring.getItemId()); + mplew.writeShort(1); //quantity + return mplew.getPacket(); + } + /* * 00 = Due to an unknown error, failed + * A3 = Request timed out. Please try again. * A4 = Due to an unknown error, failed + warpout * A5 = You don't have enough cash. * A6 = long as shet msg @@ -7567,6 +7918,7 @@ public class MaplePacketCreator { * B2 = Expired Coupon * B3 = Coupon has been used already * B4 = Nexon internet cafes? lolfk + * B8 = Due to gender restrictions, the coupon cannot be used. * BB = inv full * BC = long as shet "(not?) available to purchase by a use at the premium" msg * BD = invalid gift recipient @@ -7579,6 +7931,8 @@ public class MaplePacketCreator { * C4 = check birthday code * C7 = only available to users buying cash item, whatever msg too long * C8 = already applied for this + * CD = You have reached the daily purchase limit for the cash shop. + * D0 = coupon account limit reached * D2 = coupon system currently unavailable * D3 = item can only be used 15 days after registration * D4 = not enough gift tokens @@ -7692,6 +8046,23 @@ public class MaplePacketCreator { return mplew.getPacket(); } + + public static byte[] deleteCashItem(Item item) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); + mplew.write(0x6C); + mplew.writeLong(item.getCashId()); + return mplew.getPacket(); + } + + public static byte[] refundCashItem(Item item, int maplePoints) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue()); + mplew.write(0x85); + mplew.writeLong(item.getCashId()); + mplew.writeInt(maplePoints); + return mplew.getPacket(); + } public static byte[] putIntoCashInventory(Item item, int accountId) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -7868,7 +8239,7 @@ public class MaplePacketCreator { mplew.writeInt(exp); return mplew.getPacket(); } - + public static byte[] spawnDragon(MapleDragon dragon) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.SPAWN_DRAGON.getValue()); @@ -7883,12 +8254,12 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] moveDragon(MapleDragon dragon, Point startPos, List res) { + public static byte[] moveDragon(MapleDragon dragon, Point startPos, SeekableLittleEndianAccessor movementSlea, long movementDataLength) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.MOVE_DRAGON.getValue()); mplew.writeInt(dragon.getOwner().getId()); mplew.writePos(startPos); - serializeMovementList(mplew, res); + rebroadcastMovementList(mplew, movementSlea, movementDataLength); return mplew.getPacket(); } @@ -7940,4 +8311,74 @@ public class MaplePacketCreator { return mplew.getPacket(); } -} \ No newline at end of file + private static byte[] MassacreResult(byte nRank,int nIncExp) { + //CField_MassacreResult__OnMassacreResult @ 0x005617C5 + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.PYRAMID_SCORE.getValue()); //MASSACRERESULT | 0x009E + mplew.write(nRank); //(0 - S) (1 - A) (2 - B) (3 - C) (4 - D) ( Else - Crash ) + mplew.writeInt(nIncExp); + return mplew.getPacket(); + } + + private static byte[] GuildBoss_HealerMove(short nY) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.GUILD_BOSS_HEALER_MOVE.getValue()); + mplew.writeShort(nY); //New Y Position + return mplew.getPacket(); + } + + + private static byte[] GuildBoss_PulleyStateChange(byte nState) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.GUILD_BOSS_PULLEY_STATE_CHANGE.getValue()); + mplew.write(nState); + return mplew.getPacket(); + } + + private static byte[] Tournament__Tournament(byte nState, byte nSubState) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.TOURNAMENT.getValue()); + mplew.write(nState); + mplew.write(nSubState); + return mplew.getPacket(); + } + + private static byte[] Tournament__MatchTable(byte nState, byte nSubState) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.TOURNAMENT_MATCH_TABLE.getValue()); //Prompts CMatchTableDlg Modal + return mplew.getPacket(); + } + + private static byte[] Tournament__SetPrize(byte bSetPrize, byte bHasPrize,int nItemID1,int nItemID2) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.TOURNAMENT_SET_PRIZE.getValue()); + + //0 = "You have failed the set the prize. Please check the item number again." + //1 = "You have successfully set the prize." + mplew.write(bSetPrize); + + mplew.write(bHasPrize); + + if(bHasPrize != 0) + { + mplew.writeInt(nItemID1); + mplew.writeInt(nItemID2); + } + + return mplew.getPacket(); + } + + private static byte[] Tournament__UEW(byte nState) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.TOURNAMENT_UEW.getValue()); + + //Is this a bitflag o.o ? + //2 = "You have reached the finals by default." + //4 = "You have reached the semifinals by default." + //8 or 16 = "You have reached the round of %n by default." | Encodes nState as %n ?! + mplew.write(nState); + + return mplew.getPacket(); + } + +} diff --git a/src/tools/data/output/GenericLittleEndianWriter.java b/src/tools/data/output/GenericLittleEndianWriter.java index 91779e4d57..7dd866c4f8 100644 --- a/src/tools/data/output/GenericLittleEndianWriter.java +++ b/src/tools/data/output/GenericLittleEndianWriter.java @@ -23,7 +23,7 @@ package tools.data.output; import java.awt.Point; import java.nio.charset.Charset; -import constants.CharsetConstants.MapleLanguageType; +import constants.string.CharsetConstants.MapleLanguageType; /** * Provides a generic writer of a little-endian sequence of bytes. diff --git a/src/tools/exceptions/EmptyMovementException.java b/src/tools/exceptions/EmptyMovementException.java new file mode 100644 index 0000000000..4e45381c71 --- /dev/null +++ b/src/tools/exceptions/EmptyMovementException.java @@ -0,0 +1,35 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package tools.exceptions; + +import tools.data.input.LittleEndianAccessor; + + +/** + * + * @author Ronan + */ +public class EmptyMovementException extends Exception { + + public EmptyMovementException(LittleEndianAccessor lea) { + super("Empty movement: " + lea); + } + +} diff --git a/src/tools/packets/Fishing.java b/src/tools/packets/Fishing.java index 980b17d49d..469e56e6c6 100644 --- a/src/tools/packets/Fishing.java +++ b/src/tools/packets/Fishing.java @@ -20,9 +20,10 @@ package tools.packets; import client.MapleCharacter; -import constants.GameConstants; -import constants.ItemConstants; -import constants.ServerConstants; +import config.YamlConfig; +import constants.game.GameConstants; +import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import server.MapleItemInformationProvider; import tools.MaplePacketCreator; @@ -56,7 +57,7 @@ public class Fishing { private static boolean hitFishingTime(MapleCharacter chr, int baitLevel, double yearLikelihood, double timeLikelihood) { double baitLikelihood = 0.0002 * chr.getWorldServer().getFishingRate() * baitLevel; // can improve 10.0 at "max level 50000" on rate 1x - if (ServerConstants.USE_DEBUG) { + if (YamlConfig.config.server.USE_DEBUG) { chr.dropMessage(5, "----- FISHING RESULT -----"); chr.dropMessage(5, "Likelihoods - Year: " + yearLikelihood + " Time: " + timeLikelihood + " Meso: " + baitLikelihood); chr.dropMessage(5, "Score rolls - Year: " + (0.23 * yearLikelihood) + " Time: " + (0.77 * timeLikelihood) + " Meso: " + baitLikelihood); diff --git a/tools/MapleInvalidItemWithNoNameFetcher/src/maplenoitemnamefetcher/MapleNoItemNameFetcher.java b/tools/MapleInvalidItemWithNoNameFetcher/src/maplenoitemnamefetcher/MapleNoItemNameFetcher.java index 19f860cf35..b47047b256 100644 --- a/tools/MapleInvalidItemWithNoNameFetcher/src/maplenoitemnamefetcher/MapleNoItemNameFetcher.java +++ b/tools/MapleInvalidItemWithNoNameFetcher/src/maplenoitemnamefetcher/MapleNoItemNameFetcher.java @@ -445,7 +445,7 @@ public class MapleNoItemNameFetcher { private static String getMissingEquipDesc(int itemid) { String s = descContentCache.get(itemid); - if (s == null) { + if (s == null && itemid >= 2000000) { // thanks Halcyon for noticing "missing info" on equips s = "MISSING INFO " + itemid; } diff --git a/tools/MapleMapFieldLimitChecker/build.xml b/tools/MapleMapFieldLimitChecker/build.xml new file mode 100644 index 0000000000..2da76eb63d --- /dev/null +++ b/tools/MapleMapFieldLimitChecker/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleMapFieldLimitChecker. + + + diff --git a/tools/MapleMapFieldLimitChecker/lib/Report.txt b/tools/MapleMapFieldLimitChecker/lib/Report.txt new file mode 100644 index 0000000000..2d4b275314 --- /dev/null +++ b/tools/MapleMapFieldLimitChecker/lib/Report.txt @@ -0,0 +1,149 @@ + # Report File autogenerated from the MapleEmptyItemWzChecker feature by Ronan Lana. + # Generated data takes into account several data info from the server-side WZ.xmls. + +String.wz NAMES with no Item.wz node, 130 entries: + 20816 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 20817 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 21817 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 21820 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Face\ + 1002655 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1002657 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1002658 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003028 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003029 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003030 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1003043 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Cap\ + 1022096 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1042180 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Coat\ + 1052226 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Longcoat\ + 1060115 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1060138 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1061125 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1061160 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1062036 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1062037 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Pants\ + 1072248 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072249 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072418 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1072425 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Shoes\ + 1080002 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082217 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082221 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1082261 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Glove\ + 1142152 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1142155 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Accessory\ + 1302032 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1302069 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1322030 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1322034 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1332058 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1382013 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1452047 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1462020 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1462042 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1472057 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 1702113 ../../wz/String.wz/Eqp.img.xml -> Eqp.img\Eqp\Weapon\ + 2002012 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2002013 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2002014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2012004 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022034 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022036 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022046 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2022114 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2070014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2083000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2084000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101016 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101017 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101018 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101019 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101022 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2101058 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2210023 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2210024 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240004 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240005 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240006 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240007 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240008 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240009 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240010 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240011 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240012 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240013 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240014 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2240015 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2290109 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 2390000 ../../wz/String.wz/Consume.img.xml -> Consume.img\ + 3010044 ../../wz/String.wz/Ins.img.xml -> Ins.img\ + 3994016 ../../wz/String.wz/Ins.img.xml -> Ins.img\ + 4000275 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4001150 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031294 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031627 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031628 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031629 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031630 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031631 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031632 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031633 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031634 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031635 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031636 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031637 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031638 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031639 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031640 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031641 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031642 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031643 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031644 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031645 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031646 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031647 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031648 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031795 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4031867 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 4032526 ../../wz/String.wz/Etc.img.xml -> Etc.img\Etc\ + 5000040 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5000043 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5000046 ../../wz/String.wz/Pet.img.xml -> Pet.img\ + 5201000 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5201001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210000 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210002 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210003 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210004 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5210005 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211001 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211002 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211003 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5211047 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5240016 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5240019 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251004 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251005 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5251006 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360009 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360010 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360011 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360012 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360013 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + 5360014 ../../wz/String.wz/Cash.img.xml -> Cash.img\ + +Item.wz ITEMS with no String.wz node, 12 entries: + 1942000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942000.img.xml -> NOT FOUND + 1942001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942001.img.xml -> NOT FOUND + 1942002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01942002.img.xml -> NOT FOUND + 1952000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952000.img.xml -> NOT FOUND + 1952001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952001.img.xml -> NOT FOUND + 1952002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01952002.img.xml -> NOT FOUND + 1962000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962000.img.xml -> NOT FOUND + 1962001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962001.img.xml -> NOT FOUND + 1962002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01962002.img.xml -> NOT FOUND + 1972000 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972000.img.xml -> NOT FOUND + 1972001 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972001.img.xml -> NOT FOUND + 1972002 C:\Nexon\HeavenMS\wz\Character.wz\Dragon\01972002.img.xml -> NOT FOUND + diff --git a/tools/MapleMapFieldLimitChecker/manifest.mf b/tools/MapleMapFieldLimitChecker/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleMapFieldLimitChecker/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleMapFieldLimitChecker/src/maplemapfieldlimitchecker/MapleMapFieldLimitChecker.java b/tools/MapleMapFieldLimitChecker/src/maplemapfieldlimitchecker/MapleMapFieldLimitChecker.java new file mode 100644 index 0000000000..657ca2ebc4 --- /dev/null +++ b/tools/MapleMapFieldLimitChecker/src/maplemapfieldlimitchecker/MapleMapFieldLimitChecker.java @@ -0,0 +1,197 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package maplemapfieldlimitchecker; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + * @author RonanLana + * + This application seeks from the XMLs all mapid entries that holds the specified + fieldLimit. + */ +public class MapleMapFieldLimitChecker { + + static String newFile = "lib/Report.txt"; + static String outputWzPath = "lib"; + static PrintWriter printWriter = null; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static String wzPath = "../../wz"; + static int initialStringLength = 50; + static int itemFileNameSize = 13; + + static int fieldLimit = 0x400000; + + static byte status = 0; + static int mapid = 0; + + private static String getName(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("name"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static String getValue(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("value"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static void forwardCursor(int st) { + String line = null; + + try { + while(status >= st && (line = bufferedReader.readLine()) != null) { + simpleToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void simpleToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } + } + + private static void listFiles(String directoryName, ArrayList files) { + File directory = new File(directoryName); + + // get all the files from a directory + File[] fList = directory.listFiles(); + for (File file : fList) { + if (file.isFile()) { + files.add(file); + } else if (file.isDirectory()) { + listFiles(file.getAbsolutePath(), files); + } + } + } + + private static int getMapIdFromFilename(String name) { + try { + return Integer.valueOf(name.substring(0, name.indexOf('.'))); + } catch(Exception e) { + return -1; + } + } + + private static void translateToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + + if (status == 2) { + String d = getName(token); + if (!d.contentEquals("info")) { + forwardCursor(status); + } + } + } + else { + if (status == 2) { + String d = getName(token); + + if (d.contentEquals("fieldLimit")) { + int value = Integer.valueOf(getValue(token)); + if ((value & fieldLimit) == fieldLimit) { + System.out.println(mapid + " " + value); + } + } + } + } + } + + private static void inspectMapEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + translateToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void loadMapWz() throws IOException { + System.out.println("Reading Map.wz ..."); + ArrayList files = new ArrayList<>(); + listFiles(wzPath + "/Map.wz/Map", files); + + for(File f : files) { + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + mapid = getMapIdFromFilename(f.getName()); + inspectMapEntry(); + + bufferedReader.close(); + fileReader.close(); + } + } + + public static void main(String[] args) { + try { + loadMapWz(); + System.out.println("Done!"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + +} diff --git a/tools/MapleSkillMakerFetcher/lib/MakerData.sql b/tools/MapleSkillMakerFetcher/lib/MakerData.sql index 5100226037..72806f71b6 100644 --- a/tools/MapleSkillMakerFetcher/lib/MakerData.sql +++ b/tools/MapleSkillMakerFetcher/lib/MakerData.sql @@ -62,6 +62,8 @@ INSERT IGNORE INTO `makercreatedata` (`id`, `itemid`, `req_level`, `req_maker_le (0, 4032312, 70, 1, 0, 0, 0, 0, 1, 0), (0, 2041058, 50, 1, 55000, 0, 1122013, 0, 1, 0), (0, 2040727, 50, 1, 55000, 0, 1122013, 0, 1, 0), + (0, 4260007, 105, 3, 2200000, 4001126, 0, 0, 5, 0), + (0, 4260008, 105, 3, 5500000, 4001126, 0, 0, 10, 0), (1, 1002028, 45, 1, 55000, 0, 0, 4130018, 1, 1), (1, 1002085, 45, 1, 50000, 0, 0, 4130018, 1, 1), (1, 1002086, 45, 1, 41000, 0, 0, 4130018, 1, 1), @@ -945,6 +947,10 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (2041058, 4000299, 10), (2040727, 4000159, 50), (2040727, 4000299, 10), + (4260007, 4260006, 100), + (4260007, 4001126, 5), + (4260008, 4260007, 50), + (4260008, 4001126, 5), (1002028, 4007001, 5), (1002028, 4260000, 5), (1002085, 4007002, 5), @@ -1790,7 +1796,7 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (1372016, 4011002, 3), (1372016, 4260003, 26), (1382008, 4011002, 3), - (1382008, 4260003, 26), + (1382008, 4260004, 26), (1372009, 4011002, 4), (1372009, 4260004, 28), (1382035, 4011002, 4), @@ -2432,7 +2438,7 @@ INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES (1072194, 4260004, 9), (1072195, 4007006, 9), (1072195, 4260004, 9), - (1072213, 4007001, 10), + (1072213, 4007003, 10), (1072213, 4260005, 10), (1072214, 4007002, 10), (1072214, 4260005, 10), diff --git a/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql b/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql deleted file mode 100644 index 41b45f9522..0000000000 --- a/tools/MapleSkillbookChanceFetcher/lib/meso_drop_data.sql +++ /dev/null @@ -1,447 +0,0 @@ - # SQL File autogenerated from the MapleMesoFetcher feature by Ronan Lana. - # Generated data takes into account mob stats such as level and boss for the meso ranges. - # Only mobs with 4 or more items with no meso entry on the DB it was compiled are presented here. - - INSERT IGNORE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES -(100122, 0, 35, 52, 0, 400000), -(100123, 0, 38, 55, 0, 400000), -(100124, 0, 40, 59, 0, 400000), -(100130, 0, 31, 45, 0, 400000), -(100131, 0, 33, 49, 0, 400000), -(100132, 0, 35, 52, 0, 400000), -(100133, 0, 38, 55, 0, 400000), -(100134, 0, 40, 59, 0, 400000), -(1110130, 0, 49, 72, 0, 400000), -(1140130, 0, 56, 83, 0, 400000), -(2100100, 0, 58, 85, 0, 400000), -(2100101, 0, 60, 88, 0, 400000), -(2100106, 0, 66, 97, 0, 400000), -(2100107, 0, 73, 108, 0, 400000), -(2100108, 0, 78, 115, 0, 400000), -(2110300, 0, 66, 97, 0, 400000), -(2110301, 0, 78, 115, 0, 400000), -(2230105, 0, 64, 94, 0, 400000), -(2230107, 0, 66, 97, 0, 400000), -(2230110, 0, 64, 94, 0, 400000), -(2230111, 0, 66, 97, 0, 400000), -(2230131, 0, 66, 97, 0, 400000), -(3000005, 0, 81, 119, 0, 400000), -(3100101, 0, 86, 127, 0, 400000), -(3100102, 0, 81, 119, 0, 400000), -(3110301, 0, 86, 127, 0, 400000), -(3110302, 0, 95, 140, 0, 400000), -(3110303, 0, 105, 155, 0, 400000), -(3220000, 0, 285, 1400, 0, 400000), -(3220001, 0, 315, 1550, 0, 400000), -(3300000, 0, 81, 119, 0, 400000), -(3300001, 0, 81, 119, 0, 400000), -(3300002, 0, 83, 123, 0, 400000), -(3300003, 0, 86, 127, 0, 400000), -(3300004, 0, 89, 131, 0, 400000), -(3300005, 0, 95, 140, 0, 400000), -(3300006, 0, 95, 140, 0, 400000), -(3300007, 0, 95, 140, 0, 400000), -(3300008, 0, 315, 1550, 0, 400000), -(4110300, 0, 120, 177, 0, 400000), -(4110301, 0, 132, 195, 0, 400000), -(4130103, 0, 423, 2080, 0, 400000), -(4220000, 0, 396, 1950, 0, 400000), -(4230122, 0, 120, 177, 0, 400000), -(4230125, 0, 128, 189, 0, 400000), -(4230400, 0, 132, 195, 0, 400000), -(4230502, 0, 124, 182, 0, 400000), -(4230503, 0, 132, 195, 0, 400000), -(4230504, 0, 132, 195, 0, 400000), -(4230600, 0, 112, 165, 0, 400000), -(4240000, 0, 151, 222, 0, 400000), -(4250000, 0, 120, 177, 0, 400000), -(4250001, 0, 137, 201, 0, 400000), -(5110300, 0, 156, 230, 0, 400000), -(5120500, 0, 190, 280, 0, 400000), -(5120501, 0, 172, 254, 0, 400000), -(5120502, 0, 178, 262, 0, 400000), -(5120505, 0, 203, 299, 0, 400000), -(5120506, 0, 184, 271, 0, 400000), -(5130105, 0, 190, 280, 0, 400000), -(5130108, 0, 196, 289, 0, 400000), -(5220000, 0, 552, 2710, 0, 400000), -(5220002, 0, 468, 2300, 0, 400000), -(5220003, 0, 630, 3090, 0, 400000), -(5220004, 0, 468, 2300, 0, 400000), -(5250000, 0, 184, 271, 0, 400000), -(5250001, 0, 161, 237, 0, 400000), -(5250002, 0, 196, 289, 0, 400000), -(6110300, 0, 255, 377, 0, 400000), -(6130102, 0, 217, 320, 0, 400000), -(6130103, 0, 217, 320, 0, 400000), -(6130203, 0, 217, 320, 0, 400000), -(6130207, 0, 231, 341, 0, 400000), -(6130209, 0, 264, 389, 0, 400000), -(6220000, 0, 765, 3770, 0, 400000), -(6220001, 0, 765, 3770, 0, 400000), -(6230100, 0, 231, 341, 0, 400000), -(6230401, 0, 239, 353, 0, 400000), -(6300005, 0, 765, 3770, 0, 400000), -(6400006, 0, 384, 1890, 0, 400000), -(6400008, 0, 552, 2710, 0, 400000), -(6400009, 0, 552, 2710, 0, 400000), -(7110300, 0, 355, 524, 0, 400000), -(7110301, 0, 332, 490, 0, 400000), -(7120103, 0, 301, 444, 0, 400000), -(7120104, 0, 311, 459, 0, 400000), -(7120106, 0, 355, 524, 0, 400000), -(7120107, 0, 355, 524, 0, 400000), -(7120108, 0, 379, 559, 0, 400000), -(7120109, 0, 405, 597, 0, 400000), -(7130000, 0, 332, 490, 0, 400000), -(7130002, 0, 321, 474, 0, 400000), -(7130003, 0, 367, 541, 0, 400000), -(7130004, 0, 391, 578, 0, 400000), -(7130102, 0, 391, 578, 0, 400000), -(7130103, 0, 332, 490, 0, 400000), -(7130400, 0, 903, 4440, 0, 400000), -(7130401, 0, 903, 4440, 0, 400000), -(7130402, 0, 903, 4440, 0, 400000), -(7130601, 0, 367, 541, 0, 400000), -(7220000, 0, 933, 4590, 0, 400000), -(7220001, 0, 903, 4440, 0, 400000), -(7220002, 0, 1137, 5590, 0, 400000), -(8110300, 0, 418, 617, 0, 400000), -(8120102, 0, 446, 659, 0, 400000), -(8120103, 0, 477, 704, 0, 400000), -(8120104, 0, 509, 752, 0, 400000), -(8120105, 0, 544, 803, 0, 400000), -(8120106, 0, 562, 830, 0, 400000), -(8120107, 0, 562, 830, 0, 400000), -(8140000, 0, 418, 617, 0, 400000), -(8140100, 0, 446, 659, 0, 400000), -(8140511, 0, 581, 871, 0, 400000), -(8140512, 0, 608, 908, 0, 400000), -(8140600, 0, 594, 889, 0, 400000), -(8140702, 0, 637, 946, 0, 400000), -(8140703, 0, 666, 986, 0, 400000), -(8141300, 0, 622, 927, 0, 400000), -(8150000, 0, 2142, 10490, 0, 400000), -(8150100, 0, 714, 1049, 0, 400000), -(8150101, 0, 747, 1093, 0, 400000), -(8150200, 0, 714, 1049, 0, 400000), -(8150201, 0, 800, 1162, 0, 400000), -(8150300, 0, 666, 986, 0, 400000), -(8150301, 0, 730, 1070, 0, 400000), -(8150302, 0, 764, 1115, 0, 400000), -(8190001, 0, 800, 1162, 0, 400000), -(8220003, 0, 3381, 15830, 0, 400000), -(8220005, 0, 4350, 19860, 0, 400000), -(8220006, 0, 5466, 24400, 0, 400000), -(8220007, 0, 1704, 8530, 0, 400000), -(8220009, 0, 1479, 7280, 0, 400000), -(8830000, 0, 2400, 11620, 0, 400000), -(9001009, 0, 1254, 6170, 0, 400000), -(9001011, 0, 95, 140, 0, 400000), -(9200016, 0, 81, 119, 0, 400000), -(9200019, 0, 203, 299, 0, 400000), -(9300011, 0, 109, 160, 0, 400000), -(9300058, 0, 38, 55, 0, 400000), -(9300059, 0, 42, 61, 0, 400000), -(9300060, 0, 120, 177, 0, 400000), -(9300078, 0, 800, 1162, 0, 400000), -(9300080, 0, 282, 416, 0, 400000), -(9300096, 0, 544, 803, 0, 400000), -(9300105, 0, 717, 3530, 0, 400000), -(9300106, 0, 846, 4160, 0, 400000), -(9300127, 0, 81, 119, 0, 400000), -(9300129, 0, 81, 119, 0, 400000), -(9300131, 0, 109, 160, 0, 400000), -(9300132, 0, 81, 119, 0, 400000), -(9300133, 0, 81, 119, 0, 400000), -(9300134, 0, 81, 119, 0, 400000), -(9300136, 0, 243, 1190, 0, 400000), -(9300139, 0, 1296, 6380, 0, 400000), -(9300155, 0, 38, 55, 0, 400000), -(9300160, 0, 396, 1950, 0, 400000), -(9300161, 0, 396, 1950, 0, 400000), -(9300163, 0, 1127, 1583, 0, 400000), -(9300164, 0, 1127, 1583, 0, 400000), -(9300165, 0, 1127, 1583, 0, 400000), -(9300182, 0, 1479, 7280, 0, 400000), -(9300184, 0, 174, 850, 0, 400000), -(9300185, 0, 285, 1400, 0, 400000), -(9300186, 0, 315, 1550, 0, 400000), -(9300187, 0, 336, 1650, 0, 400000), -(9300188, 0, 468, 2300, 0, 400000), -(9300189, 0, 468, 2300, 0, 400000), -(9300190, 0, 552, 2710, 0, 400000), -(9300191, 0, 651, 3200, 0, 400000), -(9300192, 0, 570, 2800, 0, 400000), -(9300193, 0, 630, 3090, 0, 400000), -(9300194, 0, 765, 3770, 0, 400000), -(9300195, 0, 765, 3770, 0, 400000), -(9300196, 0, 765, 3770, 0, 400000), -(9300197, 0, 765, 3770, 0, 400000), -(9300198, 0, 651, 3200, 0, 400000), -(9300199, 0, 903, 4440, 0, 400000), -(9300200, 0, 933, 4590, 0, 400000), -(9300201, 0, 1479, 7280, 0, 400000), -(9300202, 0, 1137, 5590, 0, 400000), -(9300203, 0, 1254, 6170, 0, 400000), -(9300204, 0, 1383, 6810, 0, 400000), -(9300205, 0, 1296, 6380, 0, 400000), -(9300206, 0, 1479, 7280, 0, 400000), -(9300207, 0, 1479, 7280, 0, 400000), -(9300208, 0, 1704, 8530, 0, 400000), -(9300209, 0, 1704, 8530, 0, 400000), -(9300210, 0, 2142, 10490, 0, 400000), -(9300211, 0, 2400, 11620, 0, 400000), -(9300212, 0, 2400, 11620, 0, 400000), -(9300213, 0, 3381, 15830, 0, 400000), -(9300214, 0, 3792, 17550, 0, 400000), -(9300215, 0, 6714, 29370, 0, 400000), -(9300217, 0, 32, 47, 0, 400000), -(9300218, 0, 34, 50, 0, 400000), -(9300219, 0, 34, 50, 0, 400000), -(9300220, 0, 52, 77, 0, 400000), -(9300221, 0, 68, 101, 0, 400000), -(9300222, 0, 75, 111, 0, 400000), -(9300223, 0, 36, 54, 0, 400000), -(9300224, 0, 102, 150, 0, 400000), -(9300225, 0, 102, 150, 0, 400000), -(9300226, 0, 112, 165, 0, 400000), -(9300227, 0, 102, 150, 0, 400000), -(9300228, 0, 146, 215, 0, 400000), -(9300229, 0, 39, 57, 0, 400000), -(9300230, 0, 116, 171, 0, 400000), -(9300231, 0, 137, 201, 0, 400000), -(9300232, 0, 92, 136, 0, 400000), -(9300233, 0, 112, 165, 0, 400000), -(9300234, 0, 86, 127, 0, 400000), -(9300235, 0, 167, 245, 0, 400000), -(9300236, 0, 190, 280, 0, 400000), -(9300237, 0, 210, 309, 0, 400000), -(9300238, 0, 66, 97, 0, 400000), -(9300239, 0, 120, 177, 0, 400000), -(9300240, 0, 132, 195, 0, 400000), -(9300241, 0, 282, 416, 0, 400000), -(9300242, 0, 301, 444, 0, 400000), -(9300243, 0, 190, 280, 0, 400000), -(9300244, 0, 190, 280, 0, 400000), -(9300245, 0, 217, 320, 0, 400000), -(9300246, 0, 231, 341, 0, 400000), -(9300247, 0, 255, 377, 0, 400000), -(9300248, 0, 264, 389, 0, 400000), -(9300249, 0, 301, 444, 0, 400000), -(9300250, 0, 355, 524, 0, 400000), -(9300251, 0, 332, 490, 0, 400000), -(9300252, 0, 132, 195, 0, 400000), -(9300253, 0, 156, 230, 0, 400000), -(9300254, 0, 332, 490, 0, 400000), -(9300255, 0, 141, 208, 0, 400000), -(9300256, 0, 217, 320, 0, 400000), -(9300257, 0, 217, 320, 0, 400000), -(9300258, 0, 255, 377, 0, 400000), -(9300259, 0, 58, 85, 0, 400000), -(9300260, 0, 418, 617, 0, 400000), -(9300261, 0, 544, 803, 0, 400000), -(9300262, 0, 544, 803, 0, 400000), -(9300263, 0, 544, 803, 0, 400000), -(9300264, 0, 764, 1115, 0, 400000), -(9300265, 0, 730, 1070, 0, 400000), -(9300266, 0, 933, 4590, 0, 400000), -(9300267, 0, 1254, 6170, 0, 400000), -(9300268, 0, 933, 4590, 0, 400000), -(9300269, 0, 174, 850, 0, 400000), -(9300270, 0, 418, 617, 0, 400000), -(9300274, 0, 39, 57, 0, 400000), -(9300289, 0, 1704, 8530, 0, 400000), -(9300294, 0, 2142, 10490, 0, 400000), -(9300315, 0, 483, 2370, 0, 400000), -(9300316, 0, 516, 2540, 0, 400000), -(9300317, 0, 552, 2710, 0, 400000), -(9300318, 0, 588, 2890, 0, 400000), -(9300319, 0, 630, 3090, 0, 400000), -(9300320, 0, 672, 3300, 0, 400000), -(9300321, 0, 717, 3530, 0, 400000), -(9300322, 0, 765, 3770, 0, 400000), -(9300332, 0, 112, 165, 0, 400000), -(9300334, 0, 151, 222, 0, 400000), -(9300335, 0, 116, 171, 0, 400000), -(9300336, 0, 137, 201, 0, 400000), -(9300337, 0, 137, 201, 0, 400000), -(9300367, 0, 126, 610, 0, 400000), -(9300368, 0, 174, 850, 0, 400000), -(9300369, 0, 243, 1190, 0, 400000), -(9300370, 0, 336, 1650, 0, 400000), -(9300371, 0, 468, 2300, 0, 400000), -(9300372, 0, 651, 3200, 0, 400000), -(9300373, 0, 903, 4440, 0, 400000), -(9300374, 0, 1254, 6170, 0, 400000), -(9300375, 0, 1704, 8530, 0, 400000), -(9300376, 0, 1704, 8530, 0, 400000), -(9300377, 0, 2691, 12890, 0, 400000), -(9303000, 0, 42, 61, 0, 400000), -(9303001, 0, 42, 61, 0, 400000), -(9303003, 0, 42, 61, 0, 400000), -(9303004, 0, 42, 61, 0, 400000), -(9303005, 0, 71, 104, 0, 400000), -(9303006, 0, 71, 104, 0, 400000), -(9303007, 0, 71, 104, 0, 400000), -(9303008, 0, 71, 104, 0, 400000), -(9303009, 0, 161, 237, 0, 400000), -(9303010, 0, 161, 237, 0, 400000), -(9303011, 0, 161, 237, 0, 400000), -(9303013, 0, 432, 638, 0, 400000), -(9303014, 0, 432, 638, 0, 400000), -(9303016, 0, 432, 638, 0, 400000), -(9400009, 0, 7014, 8225, 0, 400000), -(9400012, 0, 217, 320, 0, 400000), -(9400120, 0, 1911, 9460, 0, 400000), -(9400122, 0, 1911, 9460, 0, 400000), -(9400200, 0, 184, 271, 0, 400000), -(9400203, 0, 112, 165, 0, 400000), -(9400205, 0, 1704, 8530, 0, 400000), -(9400238, 0, 81, 119, 0, 400000), -(9400239, 0, 66, 97, 0, 400000), -(9400241, 0, 38, 55, 0, 400000), -(9400242, 0, 42, 61, 0, 400000), -(9400243, 0, 184, 271, 0, 400000), -(9400244, 0, 210, 309, 0, 400000), -(9400245, 0, 66, 97, 0, 400000), -(9400246, 0, 62, 91, 0, 400000), -(9400247, 0, 81, 119, 0, 400000), -(9400248, 0, 66, 97, 0, 400000), -(9400500, 0, 93, 450, 0, 400000), -(9400501, 0, 35, 52, 0, 400000), -(9400502, 0, 105, 520, 0, 400000), -(9400503, 0, 105, 520, 0, 400000), -(9400504, 0, 31, 45, 0, 400000), -(9400538, 0, 56, 83, 0, 400000), -(9400539, 0, 60, 88, 0, 400000), -(9400540, 0, 68, 101, 0, 400000), -(9400541, 0, 68, 101, 0, 400000), -(9400542, 0, 98, 145, 0, 400000), -(9400543, 0, 116, 171, 0, 400000), -(9400544, 0, 156, 230, 0, 400000), -(9400546, 0, 128, 189, 0, 400000), -(9400547, 0, 73, 108, 0, 400000), -(9400548, 0, 81, 119, 0, 400000), -(9400550, 0, 73, 108, 0, 400000), -(9400556, 0, 60, 88, 0, 400000), -(9400558, 0, 81, 119, 0, 400000), -(9400560, 0, 156, 230, 0, 400000), -(9400561, 0, 217, 320, 0, 400000), -(9400562, 0, 217, 320, 0, 400000), -(9400563, 0, 112, 165, 0, 400000), -(9400565, 0, 60, 88, 0, 400000), -(9400570, 0, 49, 72, 0, 400000), -(9400571, 0, 468, 2300, 0, 400000), -(9400573, 0, 112, 165, 0, 400000), -(9400574, 0, 714, 1049, 0, 400000), -(9400576, 0, 301, 444, 0, 400000), -(9400578, 0, 568, 853, 0, 400000), -(9400579, 0, 714, 1049, 0, 400000), -(9400580, 0, 637, 946, 0, 400000), -(9400581, 0, 418, 617, 0, 400000), -(9400582, 0, 1127, 1583, 0, 400000), -(9400609, 0, 204, 1010, 0, 400000), -(9400612, 0, 204, 1010, 0, 400000), -(9400633, 0, 258, 1270, 0, 400000), -(9400644, 0, 42, 61, 0, 400000), -(9410014, 0, 493, 728, 0, 400000), -(9410015, 0, 1479, 7280, 0, 400000), -(9420507, 0, 146, 215, 0, 400000), -(9420527, 0, 132, 195, 0, 400000), -(9420528, 0, 141, 208, 0, 400000), -(9420529, 0, 167, 245, 0, 400000), -(9420530, 0, 190, 280, 0, 400000), -(9420531, 0, 210, 309, 0, 400000), -(9420532, 0, 210, 309, 0, 400000), -(9420533, 0, 224, 330, 0, 400000), -(9420534, 0, 255, 377, 0, 400000), -(9420535, 0, 282, 416, 0, 400000), -(9420536, 0, 321, 474, 0, 400000), -(9420537, 0, 355, 524, 0, 400000), -(9420538, 0, 446, 659, 0, 400000), -(9420539, 0, 526, 777, 0, 400000), -(9420545, 0, 210, 309, 0, 400000), -(9420550, 0, 210, 309, 0, 400000), -(9500101, 0, 38, 55, 0, 400000), -(9500102, 0, 39, 57, 0, 400000), -(9500103, 0, 49, 72, 0, 400000), -(9500104, 0, 44, 65, 0, 400000), -(9500105, 0, 49, 72, 0, 400000), -(9500106, 0, 62, 91, 0, 400000), -(9500107, 0, 81, 119, 0, 400000), -(9500108, 0, 86, 127, 0, 400000), -(9500109, 0, 86, 127, 0, 400000), -(9500110, 0, 95, 140, 0, 400000), -(9500111, 0, 95, 140, 0, 400000), -(9500112, 0, 95, 140, 0, 400000), -(9500113, 0, 98, 145, 0, 400000), -(9500115, 0, 102, 150, 0, 400000), -(9500116, 0, 112, 165, 0, 400000), -(9500117, 0, 98, 145, 0, 400000), -(9500118, 0, 109, 160, 0, 400000), -(9500119, 0, 109, 160, 0, 400000), -(9500120, 0, 132, 195, 0, 400000), -(9500121, 0, 146, 215, 0, 400000), -(9500122, 0, 151, 222, 0, 400000), -(9500123, 0, 210, 309, 0, 400000), -(9500124, 0, 651, 3200, 0, 400000), -(9500125, 0, 217, 320, 0, 400000), -(9500126, 0, 247, 365, 0, 400000), -(9500127, 0, 273, 402, 0, 400000), -(9500128, 0, 282, 416, 0, 400000), -(9500129, 0, 301, 444, 0, 400000), -(9500130, 0, 903, 4440, 0, 400000), -(9500131, 0, 332, 490, 0, 400000), -(9500132, 0, 355, 524, 0, 400000), -(9500134, 0, 418, 617, 0, 400000), -(9500135, 0, 493, 728, 0, 400000), -(9500136, 0, 682, 1006, 0, 400000), -(9500137, 0, 637, 946, 0, 400000), -(9500138, 0, 568, 853, 0, 400000), -(9500139, 0, 1254, 6170, 0, 400000), -(9500140, 0, 2142, 10490, 0, 400000), -(9500156, 0, 146, 215, 0, 400000), -(9500157, 0, 95, 140, 0, 400000), -(9500158, 0, 903, 4440, 0, 400000), -(9500159, 0, 903, 4440, 0, 400000), -(9500160, 0, 903, 4440, 0, 400000), -(9500161, 0, 418, 617, 0, 400000), -(9500162, 0, 418, 617, 0, 400000), -(9500163, 0, 461, 681, 0, 400000), -(9500164, 0, 544, 803, 0, 400000), -(9500165, 0, 544, 803, 0, 400000), -(9500166, 0, 544, 803, 0, 400000), -(9500178, 0, 112, 165, 0, 400000), -(9500180, 0, 1704, 8530, 0, 400000), -(9500181, 0, 1704, 8530, 0, 400000), -(9500306, 0, 174, 850, 0, 400000), -(9500307, 0, 285, 1400, 0, 400000), -(9500308, 0, 468, 2300, 0, 400000), -(9500309, 0, 552, 2710, 0, 400000), -(9500310, 0, 630, 3090, 0, 400000), -(9500311, 0, 765, 3770, 0, 400000), -(9500312, 0, 903, 4440, 0, 400000), -(9500313, 0, 933, 4590, 0, 400000), -(9500314, 0, 1137, 5590, 0, 400000), -(9500317, 0, 126, 610, 0, 400000), -(9500318, 0, 336, 1650, 0, 400000), -(9500319, 0, 903, 4440, 0, 400000), -(9500321, 0, 42, 61, 0, 400000), -(9500326, 0, 396, 1950, 0, 400000), -(9500327, 0, 243, 1190, 0, 400000), -(9500328, 0, 285, 1400, 0, 400000), -(9500331, 0, 552, 2710, 0, 400000), -(9500332, 0, 396, 1950, 0, 400000), -(9500333, 0, 468, 2300, 0, 400000), -(9500334, 0, 552, 2710, 0, 400000), -(9500335, 0, 468, 2300, 0, 400000), -(9500366, 0, 49, 72, 0, 400000), -(9500367, 0, 49, 72, 0, 400000), -(9500368, 0, 49, 72, 0, 400000), -(9500369, 0, 49, 72, 0, 400000), -(9500370, 0, 49, 72, 0, 400000), -(9500371, 0, 49, 72, 0, 400000), -(9500372, 0, 49, 72, 0, 400000); - - DELETE FROM drop_data WHERE dropperid >= 9300184 AND dropperid <= 9300215 AND itemid = 0; diff --git a/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql b/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql index 8dc97ded65..01a6890faf 100644 --- a/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql +++ b/tools/MapleSkillbookChanceFetcher/lib/skillbook_drop_data.sql @@ -3,525 +3,525 @@ REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES (851000, 2290132, 1, 1, 0, 3861), -(7090000, 2290087, 1, 1, 0, 40000), -(8090000, 2290045, 1, 1, 0, 40000), -(8140103, 2290044, 1, 1, 0, 2000), -(8140511, 2290009, 1, 1, 0, 2000), -(8140511, 2290050, 1, 1, 0, 2000), -(8140511, 2290083, 1, 1, 0, 2000), -(8140511, 2290134, 1, 1, 0, 6000), -(8140512, 2290013, 1, 1, 0, 2000), -(8140512, 2290067, 1, 1, 0, 2000), -(8140512, 2290082, 1, 1, 0, 2000), -(8140512, 2290097, 1, 1, 0, 2000), -(8140512, 2290116, 1, 1, 0, 2000), -(8140512, 2290131, 1, 1, 0, 6000), -(8140600, 2290132, 1, 1, 0, 6000), -(8140700, 2290106, 1, 1, 0, 2000), -(8140700, 2290126, 1, 1, 0, 6000), -(8140701, 2290122, 1, 1, 0, 2000), -(8140702, 2290112, 1, 1, 0, 2000), -(8140703, 2290088, 1, 1, 0, 2000), -(8140703, 2290099, 1, 1, 0, 2000), -(8141000, 2290082, 1, 1, 0, 2000), -(8141000, 2290097, 1, 1, 0, 2000), -(8141100, 2280005, 1, 1, 0, 2000), -(8141300, 2290098, 1, 1, 0, 2000), -(8142100, 2290032, 1, 1, 0, 2000), -(8142100, 2290082, 1, 1, 0, 2000), -(8142100, 2290114, 1, 1, 0, 2000), -(8143000, 2280004, 1, 1, 0, 2000), -(8150000, 2280013, 1, 1, 0, 120000), -(8150000, 2290070, 1, 1, 0, 40000), -(8150000, 2290091, 1, 1, 0, 40000), -(8150100, 2290042, 1, 1, 0, 2000), -(8150100, 2290053, 1, 1, 0, 2000), -(8150100, 2290073, 1, 1, 0, 2000), -(8150100, 2290102, 1, 1, 0, 2000), -(8150100, 2290118, 1, 1, 0, 2000), -(8150101, 2290017, 1, 1, 0, 2000), -(8150101, 2290021, 1, 1, 0, 2000), -(8150101, 2290035, 1, 1, 0, 2000), -(8150101, 2290042, 1, 1, 0, 2000), -(8150101, 2290052, 1, 1, 0, 2000), -(8150101, 2290102, 1, 1, 0, 2000), -(8150200, 2290024, 1, 1, 0, 2000), -(8150200, 2290100, 1, 1, 0, 2000), -(8150200, 2290135, 1, 1, 0, 6000), -(8150201, 2290004, 1, 1, 0, 2000), -(8150201, 2290006, 1, 1, 0, 2000), -(8150201, 2290024, 1, 1, 0, 2000), -(8150201, 2290036, 1, 1, 0, 2000), -(8150201, 2290056, 1, 1, 0, 2000), -(8150201, 2290072, 1, 1, 0, 2000), -(8150201, 2290078, 1, 1, 0, 2000), -(8150201, 2290117, 1, 1, 0, 2000), -(8150300, 2290003, 1, 1, 0, 2000), -(8150300, 2290033, 1, 1, 0, 2000), -(8150300, 2290111, 1, 1, 0, 2000), -(8150300, 2290120, 1, 1, 0, 2000), -(8150300, 2290127, 1, 1, 0, 6000), -(8150301, 2290023, 1, 1, 0, 2000), -(8150301, 2290029, 1, 1, 0, 2000), -(8150301, 2290101, 1, 1, 0, 2000), -(8150301, 2290107, 1, 1, 0, 2000), -(8150302, 2290010, 1, 1, 0, 2000), -(8150302, 2290019, 1, 1, 0, 2000), -(8150302, 2290026, 1, 1, 0, 2000), -(8150302, 2290076, 1, 1, 0, 2000), -(8150302, 2290085, 1, 1, 0, 2000), -(8150302, 2290096, 1, 1, 0, 2000), -(8150302, 2290113, 1, 1, 0, 2000), -(8150302, 2290119, 1, 1, 0, 2000), -(8150302, 2290128, 1, 1, 0, 6000), -(8160000, 2290017, 1, 1, 0, 2000), -(8160000, 2290045, 1, 1, 0, 2000), -(8160000, 2290065, 1, 1, 0, 2000), -(8160000, 2290067, 1, 1, 0, 2000), -(8160000, 2290081, 1, 1, 0, 2000), -(8170000, 2290012, 1, 1, 0, 2000), -(8170000, 2290086, 1, 1, 0, 2000), -(8170000, 2290087, 1, 1, 0, 2000), -(8170000, 2290134, 1, 1, 0, 6000), -(8180000, 2290002, 1, 1, 0, 40000), -(8180000, 2290003, 1, 1, 0, 40000), -(8180000, 2290014, 1, 1, 0, 40000), -(8180000, 2290015, 1, 1, 0, 40000), -(8180000, 2290030, 1, 1, 0, 40000), -(8180000, 2290035, 1, 1, 0, 40000), -(8180000, 2290036, 1, 1, 0, 40000), -(8180000, 2290063, 1, 1, 0, 40000), -(8180000, 2290080, 1, 1, 0, 40000), -(8180000, 2290098, 1, 1, 0, 40000), -(8180000, 2290101, 1, 1, 0, 40000), -(8180000, 2290117, 1, 1, 0, 40000), -(8180000, 2290130, 1, 1, 0, 120000), -(8180001, 2290018, 1, 1, 0, 40000), -(8180001, 2290019, 1, 1, 0, 40000), -(8180001, 2290032, 1, 1, 0, 40000), -(8180001, 2290042, 1, 1, 0, 40000), -(8180001, 2290058, 1, 1, 0, 40000), -(8180001, 2290059, 1, 1, 0, 40000), -(8180001, 2290068, 1, 1, 0, 40000), -(8180001, 2290069, 1, 1, 0, 40000), -(8180001, 2290072, 1, 1, 0, 40000), -(8180001, 2290092, 1, 1, 0, 40000), -(8180001, 2290099, 1, 1, 0, 40000), -(8180001, 2290100, 1, 1, 0, 40000), -(8180001, 2290102, 1, 1, 0, 40000), -(8180001, 2290119, 1, 1, 0, 40000), -(8180001, 2290128, 1, 1, 0, 120000), -(8190000, 2280016, 1, 1, 0, 6000), -(8190000, 2290030, 1, 1, 0, 2000), -(8190000, 2290044, 1, 1, 0, 2000), -(8190000, 2290054, 1, 1, 0, 2000), -(8190000, 2290066, 1, 1, 0, 2000), -(8190000, 2290075, 1, 1, 0, 2000), -(8190000, 2290092, 1, 1, 0, 2000), -(8190000, 2290103, 1, 1, 0, 2000), -(8190002, 2290000, 1, 1, 0, 2000), -(8190002, 2290008, 1, 1, 0, 2000), -(8190002, 2290018, 1, 1, 0, 2000), -(8190002, 2290038, 1, 1, 0, 2000), -(8190002, 2290060, 1, 1, 0, 2000), -(8190002, 2290080, 1, 1, 0, 2000), -(8190002, 2290124, 1, 1, 0, 2000), -(8190003, 2280013, 1, 1, 0, 6000), -(8190003, 2290007, 1, 1, 0, 2000), -(8190003, 2290012, 1, 1, 0, 2000), -(8190003, 2290014, 1, 1, 0, 2000), -(8190003, 2290033, 1, 1, 0, 2000), -(8190003, 2290045, 1, 1, 0, 2000), -(8190003, 2290050, 1, 1, 0, 2000), -(8190003, 2290055, 1, 1, 0, 2000), -(8190003, 2290062, 1, 1, 0, 2000), -(8190003, 2290063, 1, 1, 0, 2000), -(8190003, 2290070, 1, 1, 0, 2000), -(8190003, 2290086, 1, 1, 0, 2000), -(8190003, 2290108, 1, 1, 0, 2000), -(8190003, 2290133, 1, 1, 0, 6000), -(8190004, 2290002, 1, 1, 0, 2000), -(8190004, 2290009, 1, 1, 0, 2000), -(8190004, 2290021, 1, 1, 0, 2000), -(8190004, 2290034, 1, 1, 0, 2000), -(8190004, 2290041, 1, 1, 0, 2000), -(8190004, 2290052, 1, 1, 0, 2000), -(8190004, 2290053, 1, 1, 0, 2000), -(8190004, 2290058, 1, 1, 0, 2000), -(8190004, 2290068, 1, 1, 0, 2000), -(8190004, 2290071, 1, 1, 0, 2000), -(8190004, 2290073, 1, 1, 0, 2000), -(8190004, 2290090, 1, 1, 0, 2000), -(8190004, 2290112, 1, 1, 0, 2000), -(8190004, 2290121, 1, 1, 0, 2000), -(8190004, 2290130, 1, 1, 0, 6000), -(8190005, 2290000, 1, 1, 0, 2000), -(8190005, 2290008, 1, 1, 0, 2000), -(8190005, 2290018, 1, 1, 0, 2000), -(8190005, 2290038, 1, 1, 0, 2000), -(8190005, 2290060, 1, 1, 0, 2000), -(8190005, 2290080, 1, 1, 0, 2000), -(8190005, 2290124, 1, 1, 0, 2000), -(8200000, 2290005, 1, 1, 0, 2000), -(8200000, 2290011, 1, 1, 0, 2000), -(8200000, 2290114, 1, 1, 0, 2000), -(8200001, 2280015, 1, 1, 0, 6000), -(8200001, 2290050, 1, 1, 0, 2000), -(8200001, 2290059, 1, 1, 0, 2000), -(8200001, 2290065, 1, 1, 0, 2000), -(8200001, 2290129, 1, 1, 0, 6000), -(8200002, 2290062, 1, 1, 0, 2000), -(8200002, 2290066, 1, 1, 0, 2000), -(8200002, 2290070, 1, 1, 0, 2000), -(8200002, 2290131, 1, 1, 0, 6000), -(8200002, 2290139, 1, 1, 0, 6000), -(8200003, 2290012, 1, 1, 0, 2000), -(8200003, 2290056, 1, 1, 0, 2000), -(8200003, 2290071, 1, 1, 0, 2000), -(8200003, 2290101, 1, 1, 0, 2000), -(8200003, 2290136, 1, 1, 0, 6000), -(8200004, 2280016, 1, 1, 0, 6000), -(8200004, 2290069, 1, 1, 0, 2000), -(8200004, 2290072, 1, 1, 0, 2000), -(8200004, 2290073, 1, 1, 0, 2000), -(8200004, 2290127, 1, 1, 0, 6000), -(8200004, 2290134, 1, 1, 0, 6000), -(8200005, 2280014, 1, 1, 0, 6000), -(8200005, 2290078, 1, 1, 0, 2000), -(8200005, 2290079, 1, 1, 0, 2000), -(8200005, 2290095, 1, 1, 0, 2000), -(8200006, 2290003, 1, 1, 0, 2000), -(8200006, 2290064, 1, 1, 0, 2000), -(8200006, 2290076, 1, 1, 0, 2000), -(8200006, 2290077, 1, 1, 0, 2000), -(8200006, 2290129, 1, 1, 0, 6000), -(8200006, 2290138, 1, 1, 0, 6000), -(8200007, 2290006, 1, 1, 0, 2000), -(8200007, 2290007, 1, 1, 0, 2000), -(8200007, 2290011, 1, 1, 0, 2000), -(8200007, 2290016, 1, 1, 0, 2000), -(8200007, 2290125, 1, 1, 0, 2000), -(8200007, 2290136, 1, 1, 0, 6000), -(8200008, 2290006, 1, 1, 0, 2000), -(8200008, 2290051, 1, 1, 0, 2000), -(8200008, 2290121, 1, 1, 0, 2000), -(8200008, 2290122, 1, 1, 0, 2000), -(8200008, 2290133, 1, 1, 0, 6000), -(8200009, 2290013, 1, 1, 0, 2000), -(8200009, 2290016, 1, 1, 0, 2000), -(8200009, 2290031, 1, 1, 0, 2000), -(8200009, 2290039, 1, 1, 0, 2000), -(8200010, 2290026, 1, 1, 0, 2000), -(8200010, 2290059, 1, 1, 0, 2000), -(8200010, 2290088, 1, 1, 0, 2000), -(8200010, 2290089, 1, 1, 0, 2000), -(8200010, 2290127, 1, 1, 0, 6000), -(8200011, 2290001, 1, 1, 0, 3000), -(8200011, 2290040, 1, 1, 0, 3000), -(8200011, 2290046, 1, 1, 0, 3000), -(8200011, 2290048, 1, 1, 0, 3000), -(8200011, 2290049, 1, 1, 0, 3000), -(8200011, 2290114, 1, 1, 0, 3000), -(8200011, 2290137, 1, 1, 0, 9000), -(8200012, 2290041, 1, 1, 0, 3000), -(8200012, 2290092, 1, 1, 0, 3000), -(8200012, 2290093, 1, 1, 0, 3000), -(8200012, 2290115, 1, 1, 0, 3000), -(8200012, 2290137, 1, 1, 0, 9000), -(8200012, 2290139, 1, 1, 0, 9000), -(8220002, 2290020, 1, 1, 0, 40000), -(8220002, 2290081, 1, 1, 0, 40000), -(8220002, 2290085, 1, 1, 0, 40000), -(8220002, 2290133, 1, 1, 0, 120000), -(8220003, 2290006, 1, 1, 0, 40000), -(8220003, 2290030, 1, 1, 0, 40000), -(8220003, 2290031, 1, 1, 0, 40000), -(8220003, 2290032, 1, 1, 0, 40000), -(8220003, 2290033, 1, 1, 0, 40000), -(8220003, 2290060, 1, 1, 0, 40000), -(8220003, 2290061, 1, 1, 0, 40000), -(8220003, 2290076, 1, 1, 0, 40000), -(8220003, 2290077, 1, 1, 0, 40000), -(8220003, 2290104, 1, 1, 0, 40000), -(8220003, 2290105, 1, 1, 0, 40000), -(8220003, 2290117, 1, 1, 0, 40000), -(8220003, 2290118, 1, 1, 0, 40000), -(8220004, 2290018, 1, 1, 0, 40000), -(8220004, 2290019, 1, 1, 0, 40000), -(8220004, 2290024, 1, 1, 0, 40000), -(8220004, 2290025, 1, 1, 0, 40000), -(8220004, 2290058, 1, 1, 0, 40000), -(8220004, 2290059, 1, 1, 0, 40000), -(8220004, 2290076, 1, 1, 0, 40000), -(8220004, 2290077, 1, 1, 0, 40000), -(8220004, 2290106, 1, 1, 0, 40000), -(8220004, 2290127, 1, 1, 0, 120000), -(8220004, 2290134, 1, 1, 0, 120000), -(8220005, 2290002, 1, 1, 0, 60000), -(8220005, 2290003, 1, 1, 0, 60000), -(8220005, 2290036, 1, 1, 0, 60000), -(8220005, 2290037, 1, 1, 0, 60000), -(8220005, 2290055, 1, 1, 0, 60000), -(8220005, 2290080, 1, 1, 0, 60000), -(8220005, 2290099, 1, 1, 0, 60000), -(8220005, 2290131, 1, 1, 0, 180000), -(8220005, 2290136, 1, 1, 0, 180000), -(8220006, 2290012, 1, 1, 0, 80000), -(8220006, 2290013, 1, 1, 0, 80000), -(8220006, 2290042, 1, 1, 0, 80000), -(8220006, 2290043, 1, 1, 0, 80000), -(8220006, 2290060, 1, 1, 0, 80000), -(8220006, 2290061, 1, 1, 0, 80000), -(8220006, 2290090, 1, 1, 0, 80000), -(8220006, 2290119, 1, 1, 0, 80000), -(8220006, 2290120, 1, 1, 0, 80000), -(8220006, 2290135, 1, 1, 0, 240000), -(8220006, 2290138, 1, 1, 0, 240000), -(8220007, 2290035, 1, 1, 0, 40000), -(8220007, 2290091, 1, 1, 0, 40000), -(8220007, 2290108, 1, 1, 0, 40000), -(8220009, 2290031, 1, 1, 0, 40000), -(8220009, 2290129, 1, 1, 0, 120000), -(8220015, 2280004, 1, 1, 0, 40000), -(8220015, 2280005, 1, 1, 0, 40000), -(8220015, 2280006, 1, 1, 0, 40000), -(8500002, 2280007, 1, 1, 0, 60000), -(8500002, 2280008, 1, 1, 0, 60000), -(8500002, 2280009, 1, 1, 0, 60000), -(8500002, 2280010, 1, 1, 0, 60000), -(8500002, 2290006, 1, 1, 0, 60000), -(8500002, 2290010, 1, 1, 0, 60000), -(8500002, 2290011, 1, 1, 0, 60000), -(8500002, 2290013, 1, 1, 0, 60000), -(8500002, 2290028, 1, 1, 0, 60000), -(8500002, 2290037, 1, 1, 0, 60000), -(8500002, 2290043, 1, 1, 0, 60000), -(8500002, 2290051, 1, 1, 0, 60000), -(8500002, 2290056, 1, 1, 0, 60000), -(8500002, 2290061, 1, 1, 0, 60000), -(8500002, 2290066, 1, 1, 0, 60000), -(8500002, 2290071, 1, 1, 0, 60000), -(8500002, 2290078, 1, 1, 0, 60000), -(8500002, 2290089, 1, 1, 0, 60000), -(8500002, 2290091, 1, 1, 0, 60000), -(8500002, 2290104, 1, 1, 0, 60000), -(8500002, 2290107, 1, 1, 0, 60000), -(8500002, 2290121, 1, 1, 0, 60000), -(8500002, 2290123, 1, 1, 0, 60000), -(8500002, 2290126, 1, 1, 0, 180000), -(8500002, 2290129, 1, 1, 0, 180000), -(8510000, 2280007, 1, 1, 0, 40000), -(8510000, 2280008, 1, 1, 0, 40000), -(8510000, 2280009, 1, 1, 0, 40000), -(8510000, 2280010, 1, 1, 0, 40000), -(8510000, 2290000, 1, 1, 0, 40000), -(8510000, 2290001, 1, 1, 0, 40000), -(8510000, 2290004, 1, 1, 0, 40000), -(8510000, 2290005, 1, 1, 0, 40000), -(8510000, 2290024, 1, 1, 0, 40000), -(8510000, 2290025, 1, 1, 0, 40000), -(8510000, 2290026, 1, 1, 0, 40000), -(8510000, 2290027, 1, 1, 0, 40000), -(8510000, 2290052, 1, 1, 0, 40000), -(8510000, 2290053, 1, 1, 0, 40000), -(8510000, 2290054, 1, 1, 0, 40000), -(8510000, 2290055, 1, 1, 0, 40000), -(8510000, 2290076, 1, 1, 0, 40000), -(8510000, 2290077, 1, 1, 0, 40000), -(8510000, 2290082, 1, 1, 0, 40000), -(8510000, 2290083, 1, 1, 0, 40000), -(8510000, 2290097, 1, 1, 0, 40000), -(8510000, 2290099, 1, 1, 0, 40000), -(8510000, 2290106, 1, 1, 0, 40000), -(8510000, 2290108, 1, 1, 0, 40000), -(8510000, 2290112, 1, 1, 0, 40000), -(8510000, 2290114, 1, 1, 0, 40000), -(8510000, 2290122, 1, 1, 0, 40000), -(8510000, 2290124, 1, 1, 0, 40000), -(8510000, 2290132, 1, 1, 0, 120000), -(8520000, 2280007, 1, 1, 0, 40000), -(8520000, 2280008, 1, 1, 0, 40000), -(8520000, 2280009, 1, 1, 0, 40000), -(8520000, 2280010, 1, 1, 0, 40000), -(8520000, 2290000, 1, 1, 0, 40000), -(8520000, 2290001, 1, 1, 0, 40000), -(8520000, 2290004, 1, 1, 0, 40000), -(8520000, 2290005, 1, 1, 0, 40000), -(8520000, 2290024, 1, 1, 0, 40000), -(8520000, 2290025, 1, 1, 0, 40000), -(8520000, 2290026, 1, 1, 0, 40000), -(8520000, 2290027, 1, 1, 0, 40000), -(8520000, 2290052, 1, 1, 0, 40000), -(8520000, 2290053, 1, 1, 0, 40000), -(8520000, 2290054, 1, 1, 0, 40000), -(8520000, 2290055, 1, 1, 0, 40000), -(8520000, 2290076, 1, 1, 0, 40000), -(8520000, 2290077, 1, 1, 0, 40000), -(8520000, 2290082, 1, 1, 0, 40000), -(8520000, 2290083, 1, 1, 0, 40000), -(8520000, 2290097, 1, 1, 0, 40000), -(8520000, 2290099, 1, 1, 0, 40000), -(8520000, 2290106, 1, 1, 0, 40000), -(8520000, 2290108, 1, 1, 0, 40000), -(8520000, 2290112, 1, 1, 0, 40000), -(8520000, 2290114, 1, 1, 0, 40000), -(8520000, 2290122, 1, 1, 0, 40000), -(8520000, 2290124, 1, 1, 0, 40000), -(8520000, 2290132, 1, 1, 0, 120000), -(8800002, 2280007, 1, 1, 0, 80000), -(8800002, 2280008, 1, 1, 0, 80000), -(8800002, 2280009, 1, 1, 0, 80000), -(8800002, 2280010, 1, 1, 0, 80000), -(8800002, 2280013, 1, 1, 0, 240000), -(8800002, 2280014, 1, 1, 0, 240000), -(8800002, 2280015, 1, 1, 0, 240000), -(8800002, 2280016, 1, 1, 0, 240000), -(8800002, 2290006, 1, 1, 0, 80000), -(8800002, 2290007, 1, 1, 0, 80000), -(8800002, 2290016, 1, 1, 0, 80000), -(8800002, 2290020, 1, 1, 0, 80000), -(8800002, 2290022, 1, 1, 0, 80000), -(8800002, 2290024, 1, 1, 0, 80000), -(8800002, 2290028, 1, 1, 0, 80000), -(8800002, 2290029, 1, 1, 0, 80000), -(8800002, 2290040, 1, 1, 0, 80000), -(8800002, 2290046, 1, 1, 0, 80000), -(8800002, 2290048, 1, 1, 0, 80000), -(8800002, 2290056, 1, 1, 0, 80000), -(8800002, 2290057, 1, 1, 0, 80000), -(8800002, 2290058, 1, 1, 0, 80000), -(8800002, 2290064, 1, 1, 0, 80000), -(8800002, 2290067, 1, 1, 0, 80000), -(8800002, 2290074, 1, 1, 0, 80000), -(8800002, 2290079, 1, 1, 0, 80000), -(8800002, 2290084, 1, 1, 0, 80000), -(8800002, 2290094, 1, 1, 0, 80000), -(8800002, 2290110, 1, 1, 0, 80000), -(8800002, 2290115, 1, 1, 0, 80000), -(8810018, 2290017, 1, 1, 0, 100000), -(8810018, 2290021, 1, 1, 0, 100000), -(8810018, 2290023, 1, 1, 0, 100000), -(8810018, 2290041, 1, 1, 0, 100000), -(8810018, 2290047, 1, 1, 0, 100000), -(8810018, 2290049, 1, 1, 0, 100000), -(8810018, 2290065, 1, 1, 0, 100000), -(8810018, 2290075, 1, 1, 0, 100000), -(8810018, 2290085, 1, 1, 0, 100000), -(8810018, 2290095, 1, 1, 0, 100000), -(8810018, 2290096, 1, 1, 0, 100000), -(8810018, 2290111, 1, 1, 0, 100000), -(8810018, 2290116, 1, 1, 0, 100000), -(8810018, 2290125, 1, 1, 0, 100000), -(8810018, 2290133, 1, 1, 0, 300000), -(8810018, 2290137, 1, 1, 0, 300000), -(8810018, 2290139, 1, 1, 0, 300000), -(8820000, 2290010, 1, 1, 0, 120000), -(8820000, 2290022, 1, 1, 0, 120000), -(8820000, 2290040, 1, 1, 0, 120000), -(8820000, 2290046, 1, 1, 0, 120000), -(8820000, 2290048, 1, 1, 0, 120000), -(8820000, 2290052, 1, 1, 0, 120000), -(8820000, 2290084, 1, 1, 0, 120000), -(8820000, 2290090, 1, 1, 0, 120000), -(8820000, 2290106, 1, 1, 0, 120000), -(8820000, 2290119, 1, 1, 0, 120000), -(8820001, 2290010, 1, 1, 0, 120000), -(8820001, 2290022, 1, 1, 0, 120000), -(8820001, 2290040, 1, 1, 0, 120000), -(8820001, 2290046, 1, 1, 0, 120000), -(8820001, 2290048, 1, 1, 0, 120000), -(8820001, 2290052, 1, 1, 0, 120000), -(8820001, 2290084, 1, 1, 0, 120000), -(8820001, 2290090, 1, 1, 0, 120000), -(8820001, 2290106, 1, 1, 0, 120000), -(8820001, 2290119, 1, 1, 0, 120000), -(9300028, 2280015, 1, 1, 0, 120000), -(9300028, 2290026, 1, 1, 0, 40000), -(9300028, 2290064, 1, 1, 0, 40000), -(9300028, 2290075, 1, 1, 0, 40000), -(9300028, 2290093, 1, 1, 0, 40000), -(9300028, 2290111, 1, 1, 0, 40000), -(9300094, 2280004, 1, 1, 0, 40000), -(9300094, 2280005, 1, 1, 0, 40000), -(9300094, 2280006, 1, 1, 0, 40000), -(9300095, 2280004, 1, 1, 0, 2000), -(9300095, 2280005, 1, 1, 0, 2000), -(9300095, 2280006, 1, 1, 0, 2000), -(9303016, 2290006, 1, 1, 0, 2000), -(9303016, 2290030, 1, 1, 0, 2000), -(9303016, 2290032, 1, 1, 0, 2000), -(9303016, 2290060, 1, 1, 0, 2000), -(9303016, 2290076, 1, 1, 0, 2000), -(9303016, 2290104, 1, 1, 0, 2000), -(9303016, 2290117, 1, 1, 0, 2000), -(9400014, 2290053, 1, 1, 0, 40000), -(9400014, 2290087, 1, 1, 0, 40000), -(9400014, 2290112, 1, 1, 0, 40000), -(9400014, 2290122, 1, 1, 0, 40000), -(9400120, 2290045, 1, 1, 0, 40000), -(9400121, 2280014, 1, 1, 0, 180000), -(9400121, 2290081, 1, 1, 0, 60000), -(9400121, 2290087, 1, 1, 0, 60000), -(9400121, 2290101, 1, 1, 0, 60000), -(9400121, 2290103, 1, 1, 0, 60000), -(9400122, 2290007, 1, 1, 0, 40000), -(9400122, 2290062, 1, 1, 0, 40000), -(9400122, 2290116, 1, 1, 0, 40000), -(9400300, 2290045, 1, 1, 0, 120000), -(9400300, 2290055, 1, 1, 0, 120000), -(9400300, 2290063, 1, 1, 0, 120000), -(9400300, 2290079, 1, 1, 0, 120000), -(9400300, 2290081, 1, 1, 0, 120000), -(9400300, 2290096, 1, 1, 0, 120000), -(9400514, 2290023, 1, 1, 0, 40000), -(9400514, 2290057, 1, 1, 0, 40000), -(9400514, 2290088, 1, 1, 0, 40000), -(9400514, 2290095, 1, 1, 0, 40000), -(9400514, 2290115, 1, 1, 0, 40000), -(9400514, 2290139, 1, 1, 0, 120000), -(9400549, 2290001, 1, 1, 0, 40000), -(9400549, 2290020, 1, 1, 0, 40000), -(9400549, 2290045, 1, 1, 0, 40000), -(9400549, 2290057, 1, 1, 0, 40000), -(9400549, 2290086, 1, 1, 0, 40000), -(9400575, 2290009, 1, 1, 0, 40000), -(9400575, 2290051, 1, 1, 0, 40000), -(9400575, 2290081, 1, 1, 0, 40000), -(9400575, 2290087, 1, 1, 0, 40000), -(9400575, 2290107, 1, 1, 0, 40000), -(9400575, 2290123, 1, 1, 0, 40000), -(9400580, 2290004, 1, 1, 0, 2000), -(9400580, 2290024, 1, 1, 0, 2000), -(9400580, 2290083, 1, 1, 0, 2000), -(9400580, 2290087, 1, 1, 0, 2000), -(9400580, 2290103, 1, 1, 0, 2000), -(9400580, 2290121, 1, 1, 0, 2000), -(9400582, 2290005, 1, 1, 0, 2000), -(9400582, 2290010, 1, 1, 0, 2000), -(9400582, 2290029, 1, 1, 0, 2000), -(9400582, 2290047, 1, 1, 0, 2000), -(9400582, 2290049, 1, 1, 0, 2000), -(9400582, 2290074, 1, 1, 0, 2000), -(9400582, 2290079, 1, 1, 0, 2000), -(9400582, 2290081, 1, 1, 0, 2000), -(9400582, 2290135, 1, 1, 0, 6000), -(9400590, 2290088, 1, 1, 0, 60000), -(9400590, 2290125, 1, 1, 0, 60000), -(9400590, 2290135, 1, 1, 0, 180000), -(9400591, 2290039, 1, 1, 0, 60000), -(9400591, 2290074, 1, 1, 0, 60000), -(9400591, 2290113, 1, 1, 0, 60000), -(9400592, 2290047, 1, 1, 0, 60000), -(9400592, 2290123, 1, 1, 0, 60000), -(9400592, 2290131, 1, 1, 0, 180000), -(9400593, 2290069, 1, 1, 0, 60000), -(9400593, 2290093, 1, 1, 0, 60000), -(9400593, 2290138, 1, 1, 0, 180000), -(9420513, 2290039, 1, 1, 0, 40000), -(9420513, 2290100, 1, 1, 0, 40000), -(9420513, 2290108, 1, 1, 0, 40000), -(9420513, 2290118, 1, 1, 0, 40000), -(9420513, 2290138, 1, 1, 0, 120000), +(7090000, 2290087, 1, 1, 0, 10000), +(8090000, 2290045, 1, 1, 0, 10000), +(8140103, 2290044, 1, 1, 0, 500), +(8140511, 2290009, 1, 1, 0, 500), +(8140511, 2290050, 1, 1, 0, 500), +(8140511, 2290083, 1, 1, 0, 500), +(8140511, 2290134, 1, 1, 0, 1500), +(8140512, 2290013, 1, 1, 0, 500), +(8140512, 2290067, 1, 1, 0, 500), +(8140512, 2290082, 1, 1, 0, 500), +(8140512, 2290097, 1, 1, 0, 500), +(8140512, 2290116, 1, 1, 0, 500), +(8140512, 2290131, 1, 1, 0, 1500), +(8140600, 2290132, 1, 1, 0, 1500), +(8140700, 2290106, 1, 1, 0, 500), +(8140700, 2290126, 1, 1, 0, 1500), +(8140701, 2290122, 1, 1, 0, 500), +(8140702, 2290112, 1, 1, 0, 500), +(8140703, 2290088, 1, 1, 0, 500), +(8140703, 2290099, 1, 1, 0, 500), +(8141000, 2290082, 1, 1, 0, 500), +(8141000, 2290097, 1, 1, 0, 500), +(8141100, 2280005, 1, 1, 0, 500), +(8141300, 2290098, 1, 1, 0, 500), +(8142100, 2290032, 1, 1, 0, 500), +(8142100, 2290082, 1, 1, 0, 500), +(8142100, 2290114, 1, 1, 0, 500), +(8143000, 2280004, 1, 1, 0, 500), +(8150000, 2280013, 1, 1, 0, 30000), +(8150000, 2290070, 1, 1, 0, 10000), +(8150000, 2290091, 1, 1, 0, 10000), +(8150100, 2290042, 1, 1, 0, 500), +(8150100, 2290053, 1, 1, 0, 500), +(8150100, 2290073, 1, 1, 0, 500), +(8150100, 2290102, 1, 1, 0, 500), +(8150100, 2290118, 1, 1, 0, 500), +(8150101, 2290017, 1, 1, 0, 500), +(8150101, 2290021, 1, 1, 0, 500), +(8150101, 2290035, 1, 1, 0, 500), +(8150101, 2290042, 1, 1, 0, 500), +(8150101, 2290052, 1, 1, 0, 500), +(8150101, 2290102, 1, 1, 0, 500), +(8150200, 2290024, 1, 1, 0, 500), +(8150200, 2290100, 1, 1, 0, 500), +(8150200, 2290135, 1, 1, 0, 1500), +(8150201, 2290004, 1, 1, 0, 500), +(8150201, 2290006, 1, 1, 0, 500), +(8150201, 2290024, 1, 1, 0, 500), +(8150201, 2290036, 1, 1, 0, 500), +(8150201, 2290056, 1, 1, 0, 500), +(8150201, 2290072, 1, 1, 0, 500), +(8150201, 2290078, 1, 1, 0, 500), +(8150201, 2290117, 1, 1, 0, 500), +(8150300, 2290003, 1, 1, 0, 500), +(8150300, 2290033, 1, 1, 0, 500), +(8150300, 2290111, 1, 1, 0, 500), +(8150300, 2290120, 1, 1, 0, 500), +(8150300, 2290127, 1, 1, 0, 1500), +(8150301, 2290023, 1, 1, 0, 500), +(8150301, 2290029, 1, 1, 0, 500), +(8150301, 2290101, 1, 1, 0, 500), +(8150301, 2290107, 1, 1, 0, 500), +(8150302, 2290010, 1, 1, 0, 500), +(8150302, 2290019, 1, 1, 0, 500), +(8150302, 2290026, 1, 1, 0, 500), +(8150302, 2290076, 1, 1, 0, 500), +(8150302, 2290085, 1, 1, 0, 500), +(8150302, 2290096, 1, 1, 0, 500), +(8150302, 2290113, 1, 1, 0, 500), +(8150302, 2290119, 1, 1, 0, 500), +(8150302, 2290128, 1, 1, 0, 1500), +(8160000, 2290017, 1, 1, 0, 500), +(8160000, 2290045, 1, 1, 0, 500), +(8160000, 2290065, 1, 1, 0, 500), +(8160000, 2290067, 1, 1, 0, 500), +(8160000, 2290081, 1, 1, 0, 500), +(8170000, 2290012, 1, 1, 0, 500), +(8170000, 2290086, 1, 1, 0, 500), +(8170000, 2290087, 1, 1, 0, 500), +(8170000, 2290134, 1, 1, 0, 1500), +(8180000, 2290002, 1, 1, 0, 10000), +(8180000, 2290003, 1, 1, 0, 10000), +(8180000, 2290014, 1, 1, 0, 10000), +(8180000, 2290015, 1, 1, 0, 10000), +(8180000, 2290030, 1, 1, 0, 10000), +(8180000, 2290035, 1, 1, 0, 10000), +(8180000, 2290036, 1, 1, 0, 10000), +(8180000, 2290063, 1, 1, 0, 10000), +(8180000, 2290080, 1, 1, 0, 10000), +(8180000, 2290098, 1, 1, 0, 10000), +(8180000, 2290101, 1, 1, 0, 10000), +(8180000, 2290117, 1, 1, 0, 10000), +(8180000, 2290130, 1, 1, 0, 30000), +(8180001, 2290018, 1, 1, 0, 10000), +(8180001, 2290019, 1, 1, 0, 10000), +(8180001, 2290032, 1, 1, 0, 10000), +(8180001, 2290042, 1, 1, 0, 10000), +(8180001, 2290058, 1, 1, 0, 10000), +(8180001, 2290059, 1, 1, 0, 10000), +(8180001, 2290068, 1, 1, 0, 10000), +(8180001, 2290069, 1, 1, 0, 10000), +(8180001, 2290072, 1, 1, 0, 10000), +(8180001, 2290092, 1, 1, 0, 10000), +(8180001, 2290099, 1, 1, 0, 10000), +(8180001, 2290100, 1, 1, 0, 10000), +(8180001, 2290102, 1, 1, 0, 10000), +(8180001, 2290119, 1, 1, 0, 10000), +(8180001, 2290128, 1, 1, 0, 30000), +(8190000, 2280016, 1, 1, 0, 1500), +(8190000, 2290030, 1, 1, 0, 500), +(8190000, 2290044, 1, 1, 0, 500), +(8190000, 2290054, 1, 1, 0, 500), +(8190000, 2290066, 1, 1, 0, 500), +(8190000, 2290075, 1, 1, 0, 500), +(8190000, 2290092, 1, 1, 0, 500), +(8190000, 2290103, 1, 1, 0, 500), +(8190002, 2290000, 1, 1, 0, 500), +(8190002, 2290008, 1, 1, 0, 500), +(8190002, 2290018, 1, 1, 0, 500), +(8190002, 2290038, 1, 1, 0, 500), +(8190002, 2290060, 1, 1, 0, 500), +(8190002, 2290080, 1, 1, 0, 500), +(8190002, 2290124, 1, 1, 0, 500), +(8190003, 2280013, 1, 1, 0, 1500), +(8190003, 2290007, 1, 1, 0, 500), +(8190003, 2290012, 1, 1, 0, 500), +(8190003, 2290014, 1, 1, 0, 500), +(8190003, 2290033, 1, 1, 0, 500), +(8190003, 2290045, 1, 1, 0, 500), +(8190003, 2290050, 1, 1, 0, 500), +(8190003, 2290055, 1, 1, 0, 500), +(8190003, 2290062, 1, 1, 0, 500), +(8190003, 2290063, 1, 1, 0, 500), +(8190003, 2290070, 1, 1, 0, 500), +(8190003, 2290086, 1, 1, 0, 500), +(8190003, 2290108, 1, 1, 0, 500), +(8190003, 2290133, 1, 1, 0, 1500), +(8190004, 2290002, 1, 1, 0, 500), +(8190004, 2290009, 1, 1, 0, 500), +(8190004, 2290021, 1, 1, 0, 500), +(8190004, 2290034, 1, 1, 0, 500), +(8190004, 2290041, 1, 1, 0, 500), +(8190004, 2290052, 1, 1, 0, 500), +(8190004, 2290053, 1, 1, 0, 500), +(8190004, 2290058, 1, 1, 0, 500), +(8190004, 2290068, 1, 1, 0, 500), +(8190004, 2290071, 1, 1, 0, 500), +(8190004, 2290073, 1, 1, 0, 500), +(8190004, 2290090, 1, 1, 0, 500), +(8190004, 2290112, 1, 1, 0, 500), +(8190004, 2290121, 1, 1, 0, 500), +(8190004, 2290130, 1, 1, 0, 1500), +(8190005, 2290000, 1, 1, 0, 500), +(8190005, 2290008, 1, 1, 0, 500), +(8190005, 2290018, 1, 1, 0, 500), +(8190005, 2290038, 1, 1, 0, 500), +(8190005, 2290060, 1, 1, 0, 500), +(8190005, 2290080, 1, 1, 0, 500), +(8190005, 2290124, 1, 1, 0, 500), +(8200000, 2290005, 1, 1, 0, 500), +(8200000, 2290011, 1, 1, 0, 500), +(8200000, 2290114, 1, 1, 0, 500), +(8200001, 2280015, 1, 1, 0, 1500), +(8200001, 2290050, 1, 1, 0, 500), +(8200001, 2290059, 1, 1, 0, 500), +(8200001, 2290065, 1, 1, 0, 500), +(8200001, 2290129, 1, 1, 0, 1500), +(8200002, 2290062, 1, 1, 0, 500), +(8200002, 2290066, 1, 1, 0, 500), +(8200002, 2290070, 1, 1, 0, 500), +(8200002, 2290131, 1, 1, 0, 1500), +(8200002, 2290139, 1, 1, 0, 1500), +(8200003, 2290012, 1, 1, 0, 500), +(8200003, 2290056, 1, 1, 0, 500), +(8200003, 2290071, 1, 1, 0, 500), +(8200003, 2290101, 1, 1, 0, 500), +(8200003, 2290136, 1, 1, 0, 1500), +(8200004, 2280016, 1, 1, 0, 1500), +(8200004, 2290069, 1, 1, 0, 500), +(8200004, 2290072, 1, 1, 0, 500), +(8200004, 2290073, 1, 1, 0, 500), +(8200004, 2290127, 1, 1, 0, 1500), +(8200004, 2290134, 1, 1, 0, 1500), +(8200005, 2280014, 1, 1, 0, 1500), +(8200005, 2290078, 1, 1, 0, 500), +(8200005, 2290079, 1, 1, 0, 500), +(8200005, 2290095, 1, 1, 0, 500), +(8200006, 2290003, 1, 1, 0, 500), +(8200006, 2290064, 1, 1, 0, 500), +(8200006, 2290076, 1, 1, 0, 500), +(8200006, 2290077, 1, 1, 0, 500), +(8200006, 2290129, 1, 1, 0, 1500), +(8200006, 2290138, 1, 1, 0, 1500), +(8200007, 2290006, 1, 1, 0, 500), +(8200007, 2290007, 1, 1, 0, 500), +(8200007, 2290011, 1, 1, 0, 500), +(8200007, 2290016, 1, 1, 0, 500), +(8200007, 2290125, 1, 1, 0, 500), +(8200007, 2290136, 1, 1, 0, 1500), +(8200008, 2290006, 1, 1, 0, 500), +(8200008, 2290051, 1, 1, 0, 500), +(8200008, 2290121, 1, 1, 0, 500), +(8200008, 2290122, 1, 1, 0, 500), +(8200008, 2290133, 1, 1, 0, 1500), +(8200009, 2290013, 1, 1, 0, 500), +(8200009, 2290016, 1, 1, 0, 500), +(8200009, 2290031, 1, 1, 0, 500), +(8200009, 2290039, 1, 1, 0, 500), +(8200010, 2290026, 1, 1, 0, 500), +(8200010, 2290059, 1, 1, 0, 500), +(8200010, 2290088, 1, 1, 0, 500), +(8200010, 2290089, 1, 1, 0, 500), +(8200010, 2290127, 1, 1, 0, 1500), +(8200011, 2290001, 1, 1, 0, 750), +(8200011, 2290040, 1, 1, 0, 750), +(8200011, 2290046, 1, 1, 0, 750), +(8200011, 2290048, 1, 1, 0, 750), +(8200011, 2290049, 1, 1, 0, 750), +(8200011, 2290114, 1, 1, 0, 750), +(8200011, 2290137, 1, 1, 0, 2250), +(8200012, 2290041, 1, 1, 0, 750), +(8200012, 2290092, 1, 1, 0, 750), +(8200012, 2290093, 1, 1, 0, 750), +(8200012, 2290115, 1, 1, 0, 750), +(8200012, 2290137, 1, 1, 0, 2250), +(8200012, 2290139, 1, 1, 0, 2250), +(8220002, 2290020, 1, 1, 0, 10000), +(8220002, 2290081, 1, 1, 0, 10000), +(8220002, 2290085, 1, 1, 0, 10000), +(8220002, 2290133, 1, 1, 0, 30000), +(8220003, 2290006, 1, 1, 0, 10000), +(8220003, 2290030, 1, 1, 0, 10000), +(8220003, 2290031, 1, 1, 0, 10000), +(8220003, 2290032, 1, 1, 0, 10000), +(8220003, 2290033, 1, 1, 0, 10000), +(8220003, 2290060, 1, 1, 0, 10000), +(8220003, 2290061, 1, 1, 0, 10000), +(8220003, 2290076, 1, 1, 0, 10000), +(8220003, 2290077, 1, 1, 0, 10000), +(8220003, 2290104, 1, 1, 0, 10000), +(8220003, 2290105, 1, 1, 0, 10000), +(8220003, 2290117, 1, 1, 0, 10000), +(8220003, 2290118, 1, 1, 0, 10000), +(8220004, 2290018, 1, 1, 0, 10000), +(8220004, 2290019, 1, 1, 0, 10000), +(8220004, 2290024, 1, 1, 0, 10000), +(8220004, 2290025, 1, 1, 0, 10000), +(8220004, 2290058, 1, 1, 0, 10000), +(8220004, 2290059, 1, 1, 0, 10000), +(8220004, 2290076, 1, 1, 0, 10000), +(8220004, 2290077, 1, 1, 0, 10000), +(8220004, 2290106, 1, 1, 0, 10000), +(8220004, 2290127, 1, 1, 0, 30000), +(8220004, 2290134, 1, 1, 0, 30000), +(8220005, 2290002, 1, 1, 0, 15000), +(8220005, 2290003, 1, 1, 0, 15000), +(8220005, 2290036, 1, 1, 0, 15000), +(8220005, 2290037, 1, 1, 0, 15000), +(8220005, 2290055, 1, 1, 0, 15000), +(8220005, 2290080, 1, 1, 0, 15000), +(8220005, 2290099, 1, 1, 0, 15000), +(8220005, 2290131, 1, 1, 0, 45000), +(8220005, 2290136, 1, 1, 0, 45000), +(8220006, 2290012, 1, 1, 0, 20000), +(8220006, 2290013, 1, 1, 0, 20000), +(8220006, 2290042, 1, 1, 0, 20000), +(8220006, 2290043, 1, 1, 0, 20000), +(8220006, 2290060, 1, 1, 0, 20000), +(8220006, 2290061, 1, 1, 0, 20000), +(8220006, 2290090, 1, 1, 0, 20000), +(8220006, 2290119, 1, 1, 0, 20000), +(8220006, 2290120, 1, 1, 0, 20000), +(8220006, 2290135, 1, 1, 0, 60000), +(8220006, 2290138, 1, 1, 0, 60000), +(8220007, 2290035, 1, 1, 0, 10000), +(8220007, 2290091, 1, 1, 0, 10000), +(8220007, 2290108, 1, 1, 0, 10000), +(8220009, 2290031, 1, 1, 0, 10000), +(8220009, 2290129, 1, 1, 0, 30000), +(8220015, 2280004, 1, 1, 0, 10000), +(8220015, 2280005, 1, 1, 0, 10000), +(8220015, 2280006, 1, 1, 0, 10000), +(8500002, 2280007, 1, 1, 0, 15000), +(8500002, 2280008, 1, 1, 0, 15000), +(8500002, 2280009, 1, 1, 0, 15000), +(8500002, 2280010, 1, 1, 0, 15000), +(8500002, 2290006, 1, 1, 0, 15000), +(8500002, 2290010, 1, 1, 0, 15000), +(8500002, 2290011, 1, 1, 0, 15000), +(8500002, 2290013, 1, 1, 0, 15000), +(8500002, 2290028, 1, 1, 0, 15000), +(8500002, 2290037, 1, 1, 0, 15000), +(8500002, 2290043, 1, 1, 0, 15000), +(8500002, 2290051, 1, 1, 0, 15000), +(8500002, 2290056, 1, 1, 0, 15000), +(8500002, 2290061, 1, 1, 0, 15000), +(8500002, 2290066, 1, 1, 0, 15000), +(8500002, 2290071, 1, 1, 0, 15000), +(8500002, 2290078, 1, 1, 0, 15000), +(8500002, 2290089, 1, 1, 0, 15000), +(8500002, 2290091, 1, 1, 0, 15000), +(8500002, 2290104, 1, 1, 0, 15000), +(8500002, 2290107, 1, 1, 0, 15000), +(8500002, 2290121, 1, 1, 0, 15000), +(8500002, 2290123, 1, 1, 0, 15000), +(8500002, 2290126, 1, 1, 0, 45000), +(8500002, 2290129, 1, 1, 0, 45000), +(8510000, 2280007, 1, 1, 0, 10000), +(8510000, 2280008, 1, 1, 0, 10000), +(8510000, 2280009, 1, 1, 0, 10000), +(8510000, 2280010, 1, 1, 0, 10000), +(8510000, 2290000, 1, 1, 0, 10000), +(8510000, 2290001, 1, 1, 0, 10000), +(8510000, 2290004, 1, 1, 0, 10000), +(8510000, 2290005, 1, 1, 0, 10000), +(8510000, 2290024, 1, 1, 0, 10000), +(8510000, 2290025, 1, 1, 0, 10000), +(8510000, 2290026, 1, 1, 0, 10000), +(8510000, 2290027, 1, 1, 0, 10000), +(8510000, 2290052, 1, 1, 0, 10000), +(8510000, 2290053, 1, 1, 0, 10000), +(8510000, 2290054, 1, 1, 0, 10000), +(8510000, 2290055, 1, 1, 0, 10000), +(8510000, 2290076, 1, 1, 0, 10000), +(8510000, 2290077, 1, 1, 0, 10000), +(8510000, 2290082, 1, 1, 0, 10000), +(8510000, 2290083, 1, 1, 0, 10000), +(8510000, 2290097, 1, 1, 0, 10000), +(8510000, 2290099, 1, 1, 0, 10000), +(8510000, 2290106, 1, 1, 0, 10000), +(8510000, 2290108, 1, 1, 0, 10000), +(8510000, 2290112, 1, 1, 0, 10000), +(8510000, 2290114, 1, 1, 0, 10000), +(8510000, 2290122, 1, 1, 0, 10000), +(8510000, 2290124, 1, 1, 0, 10000), +(8510000, 2290132, 1, 1, 0, 30000), +(8520000, 2280007, 1, 1, 0, 10000), +(8520000, 2280008, 1, 1, 0, 10000), +(8520000, 2280009, 1, 1, 0, 10000), +(8520000, 2280010, 1, 1, 0, 10000), +(8520000, 2290000, 1, 1, 0, 10000), +(8520000, 2290001, 1, 1, 0, 10000), +(8520000, 2290004, 1, 1, 0, 10000), +(8520000, 2290005, 1, 1, 0, 10000), +(8520000, 2290024, 1, 1, 0, 10000), +(8520000, 2290025, 1, 1, 0, 10000), +(8520000, 2290026, 1, 1, 0, 10000), +(8520000, 2290027, 1, 1, 0, 10000), +(8520000, 2290052, 1, 1, 0, 10000), +(8520000, 2290053, 1, 1, 0, 10000), +(8520000, 2290054, 1, 1, 0, 10000), +(8520000, 2290055, 1, 1, 0, 10000), +(8520000, 2290076, 1, 1, 0, 10000), +(8520000, 2290077, 1, 1, 0, 10000), +(8520000, 2290082, 1, 1, 0, 10000), +(8520000, 2290083, 1, 1, 0, 10000), +(8520000, 2290097, 1, 1, 0, 10000), +(8520000, 2290099, 1, 1, 0, 10000), +(8520000, 2290106, 1, 1, 0, 10000), +(8520000, 2290108, 1, 1, 0, 10000), +(8520000, 2290112, 1, 1, 0, 10000), +(8520000, 2290114, 1, 1, 0, 10000), +(8520000, 2290122, 1, 1, 0, 10000), +(8520000, 2290124, 1, 1, 0, 10000), +(8520000, 2290132, 1, 1, 0, 30000), +(8800002, 2280007, 1, 1, 0, 20000), +(8800002, 2280008, 1, 1, 0, 20000), +(8800002, 2280009, 1, 1, 0, 20000), +(8800002, 2280010, 1, 1, 0, 20000), +(8800002, 2280013, 1, 1, 0, 60000), +(8800002, 2280014, 1, 1, 0, 60000), +(8800002, 2280015, 1, 1, 0, 60000), +(8800002, 2280016, 1, 1, 0, 60000), +(8800002, 2290006, 1, 1, 0, 20000), +(8800002, 2290007, 1, 1, 0, 20000), +(8800002, 2290016, 1, 1, 0, 20000), +(8800002, 2290020, 1, 1, 0, 20000), +(8800002, 2290022, 1, 1, 0, 20000), +(8800002, 2290024, 1, 1, 0, 20000), +(8800002, 2290028, 1, 1, 0, 20000), +(8800002, 2290029, 1, 1, 0, 20000), +(8800002, 2290040, 1, 1, 0, 20000), +(8800002, 2290046, 1, 1, 0, 20000), +(8800002, 2290048, 1, 1, 0, 20000), +(8800002, 2290056, 1, 1, 0, 20000), +(8800002, 2290057, 1, 1, 0, 20000), +(8800002, 2290058, 1, 1, 0, 20000), +(8800002, 2290064, 1, 1, 0, 20000), +(8800002, 2290067, 1, 1, 0, 20000), +(8800002, 2290074, 1, 1, 0, 20000), +(8800002, 2290079, 1, 1, 0, 20000), +(8800002, 2290084, 1, 1, 0, 20000), +(8800002, 2290094, 1, 1, 0, 20000), +(8800002, 2290110, 1, 1, 0, 20000), +(8800002, 2290115, 1, 1, 0, 20000), +(8810018, 2290017, 1, 1, 0, 25000), +(8810018, 2290021, 1, 1, 0, 25000), +(8810018, 2290023, 1, 1, 0, 25000), +(8810018, 2290041, 1, 1, 0, 25000), +(8810018, 2290047, 1, 1, 0, 25000), +(8810018, 2290049, 1, 1, 0, 25000), +(8810018, 2290065, 1, 1, 0, 25000), +(8810018, 2290075, 1, 1, 0, 25000), +(8810018, 2290085, 1, 1, 0, 25000), +(8810018, 2290095, 1, 1, 0, 25000), +(8810018, 2290096, 1, 1, 0, 25000), +(8810018, 2290111, 1, 1, 0, 25000), +(8810018, 2290116, 1, 1, 0, 25000), +(8810018, 2290125, 1, 1, 0, 25000), +(8810018, 2290133, 1, 1, 0, 75000), +(8810018, 2290137, 1, 1, 0, 75000), +(8810018, 2290139, 1, 1, 0, 75000), +(8820000, 2290010, 1, 1, 0, 30000), +(8820000, 2290022, 1, 1, 0, 30000), +(8820000, 2290040, 1, 1, 0, 30000), +(8820000, 2290046, 1, 1, 0, 30000), +(8820000, 2290048, 1, 1, 0, 30000), +(8820000, 2290052, 1, 1, 0, 30000), +(8820000, 2290084, 1, 1, 0, 30000), +(8820000, 2290090, 1, 1, 0, 30000), +(8820000, 2290106, 1, 1, 0, 30000), +(8820000, 2290119, 1, 1, 0, 30000), +(8820001, 2290010, 1, 1, 0, 30000), +(8820001, 2290022, 1, 1, 0, 30000), +(8820001, 2290040, 1, 1, 0, 30000), +(8820001, 2290046, 1, 1, 0, 30000), +(8820001, 2290048, 1, 1, 0, 30000), +(8820001, 2290052, 1, 1, 0, 30000), +(8820001, 2290084, 1, 1, 0, 30000), +(8820001, 2290090, 1, 1, 0, 30000), +(8820001, 2290106, 1, 1, 0, 30000), +(8820001, 2290119, 1, 1, 0, 30000), +(9300028, 2280015, 1, 1, 0, 30000), +(9300028, 2290026, 1, 1, 0, 10000), +(9300028, 2290064, 1, 1, 0, 10000), +(9300028, 2290075, 1, 1, 0, 10000), +(9300028, 2290093, 1, 1, 0, 10000), +(9300028, 2290111, 1, 1, 0, 10000), +(9300094, 2280004, 1, 1, 0, 10000), +(9300094, 2280005, 1, 1, 0, 10000), +(9300094, 2280006, 1, 1, 0, 10000), +(9300095, 2280004, 1, 1, 0, 500), +(9300095, 2280005, 1, 1, 0, 500), +(9300095, 2280006, 1, 1, 0, 500), +(9303016, 2290006, 1, 1, 0, 500), +(9303016, 2290030, 1, 1, 0, 500), +(9303016, 2290032, 1, 1, 0, 500), +(9303016, 2290060, 1, 1, 0, 500), +(9303016, 2290076, 1, 1, 0, 500), +(9303016, 2290104, 1, 1, 0, 500), +(9303016, 2290117, 1, 1, 0, 500), +(9400014, 2290053, 1, 1, 0, 10000), +(9400014, 2290087, 1, 1, 0, 10000), +(9400014, 2290112, 1, 1, 0, 10000), +(9400014, 2290122, 1, 1, 0, 10000), +(9400120, 2290045, 1, 1, 0, 10000), +(9400121, 2280014, 1, 1, 0, 45000), +(9400121, 2290081, 1, 1, 0, 15000), +(9400121, 2290087, 1, 1, 0, 15000), +(9400121, 2290101, 1, 1, 0, 15000), +(9400121, 2290103, 1, 1, 0, 15000), +(9400122, 2290007, 1, 1, 0, 10000), +(9400122, 2290062, 1, 1, 0, 10000), +(9400122, 2290116, 1, 1, 0, 10000), +(9400300, 2290045, 1, 1, 0, 30000), +(9400300, 2290055, 1, 1, 0, 30000), +(9400300, 2290063, 1, 1, 0, 30000), +(9400300, 2290079, 1, 1, 0, 30000), +(9400300, 2290081, 1, 1, 0, 30000), +(9400300, 2290096, 1, 1, 0, 30000), +(9400514, 2290023, 1, 1, 0, 10000), +(9400514, 2290057, 1, 1, 0, 10000), +(9400514, 2290088, 1, 1, 0, 10000), +(9400514, 2290095, 1, 1, 0, 10000), +(9400514, 2290115, 1, 1, 0, 10000), +(9400514, 2290139, 1, 1, 0, 30000), +(9400549, 2290001, 1, 1, 0, 10000), +(9400549, 2290020, 1, 1, 0, 10000), +(9400549, 2290045, 1, 1, 0, 10000), +(9400549, 2290057, 1, 1, 0, 10000), +(9400549, 2290086, 1, 1, 0, 10000), +(9400575, 2290009, 1, 1, 0, 10000), +(9400575, 2290051, 1, 1, 0, 10000), +(9400575, 2290081, 1, 1, 0, 10000), +(9400575, 2290087, 1, 1, 0, 10000), +(9400575, 2290107, 1, 1, 0, 10000), +(9400575, 2290123, 1, 1, 0, 10000), +(9400580, 2290004, 1, 1, 0, 500), +(9400580, 2290024, 1, 1, 0, 500), +(9400580, 2290083, 1, 1, 0, 500), +(9400580, 2290087, 1, 1, 0, 500), +(9400580, 2290103, 1, 1, 0, 500), +(9400580, 2290121, 1, 1, 0, 500), +(9400582, 2290005, 1, 1, 0, 500), +(9400582, 2290010, 1, 1, 0, 500), +(9400582, 2290029, 1, 1, 0, 500), +(9400582, 2290047, 1, 1, 0, 500), +(9400582, 2290049, 1, 1, 0, 500), +(9400582, 2290074, 1, 1, 0, 500), +(9400582, 2290079, 1, 1, 0, 500), +(9400582, 2290081, 1, 1, 0, 500), +(9400582, 2290135, 1, 1, 0, 1500), +(9400590, 2290088, 1, 1, 0, 15000), +(9400590, 2290125, 1, 1, 0, 15000), +(9400590, 2290135, 1, 1, 0, 45000), +(9400591, 2290039, 1, 1, 0, 15000), +(9400591, 2290074, 1, 1, 0, 15000), +(9400591, 2290113, 1, 1, 0, 15000), +(9400592, 2290047, 1, 1, 0, 15000), +(9400592, 2290123, 1, 1, 0, 15000), +(9400592, 2290131, 1, 1, 0, 45000), +(9400593, 2290069, 1, 1, 0, 15000), +(9400593, 2290093, 1, 1, 0, 15000), +(9400593, 2290138, 1, 1, 0, 45000), +(9420513, 2290039, 1, 1, 0, 10000), +(9420513, 2290100, 1, 1, 0, 10000), +(9420513, 2290108, 1, 1, 0, 10000), +(9420513, 2290118, 1, 1, 0, 10000), +(9420513, 2290138, 1, 1, 0, 30000), (9420514, 2290099, 1, 1, 0, 1287), (9420517, 2290000, 1, 1, 0, 1287), (9420517, 2290008, 1, 1, 0, 1287), @@ -545,75 +545,75 @@ (9420522, 2290089, 1, 1, 0, 1287), (9420522, 2290091, 1, 1, 0, 1287), (9420522, 2290107, 1, 1, 0, 1287), -(9420540, 2280006, 1, 1, 0, 2000), -(9420540, 2290119, 1, 1, 0, 2000), -(9420540, 2290120, 1, 1, 0, 2000), -(9420544, 2280007, 1, 1, 0, 80000), -(9420544, 2280008, 1, 1, 0, 80000), -(9420544, 2280009, 1, 1, 0, 80000), -(9420544, 2280010, 1, 1, 0, 80000), -(9420544, 2290002, 1, 1, 0, 80000), -(9420544, 2290015, 1, 1, 0, 80000), -(9420544, 2290022, 1, 1, 0, 80000), -(9420544, 2290027, 1, 1, 0, 80000), -(9420544, 2290034, 1, 1, 0, 80000), -(9420544, 2290052, 1, 1, 0, 80000), -(9420544, 2290054, 1, 1, 0, 80000), -(9420544, 2290089, 1, 1, 0, 80000), -(9420544, 2290094, 1, 1, 0, 80000), -(9420544, 2290098, 1, 1, 0, 80000), -(9420544, 2290105, 1, 1, 0, 80000), -(9420544, 2290110, 1, 1, 0, 80000), -(9420544, 2290119, 1, 1, 0, 80000), -(9420549, 2280007, 1, 1, 0, 80000), -(9420549, 2280008, 1, 1, 0, 80000), -(9420549, 2280009, 1, 1, 0, 80000), -(9420549, 2280010, 1, 1, 0, 80000), -(9420549, 2290002, 1, 1, 0, 80000), -(9420549, 2290015, 1, 1, 0, 80000), -(9420549, 2290022, 1, 1, 0, 80000), -(9420549, 2290027, 1, 1, 0, 80000), -(9420549, 2290034, 1, 1, 0, 80000), -(9420549, 2290052, 1, 1, 0, 80000), -(9420549, 2290054, 1, 1, 0, 80000), -(9420549, 2290089, 1, 1, 0, 80000), -(9420549, 2290094, 1, 1, 0, 80000), -(9420549, 2290098, 1, 1, 0, 80000), -(9420549, 2290105, 1, 1, 0, 80000), -(9420549, 2290110, 1, 1, 0, 80000), -(9420549, 2290119, 1, 1, 0, 80000), -(9500166, 2290044, 1, 1, 0, 2000), -(9500173, 2290018, 1, 1, 0, 40000), -(9500173, 2290019, 1, 1, 0, 40000), -(9500173, 2290032, 1, 1, 0, 40000), -(9500173, 2290042, 1, 1, 0, 40000), -(9500173, 2290058, 1, 1, 0, 40000), -(9500173, 2290068, 1, 1, 0, 40000), -(9500173, 2290072, 1, 1, 0, 40000), -(9500173, 2290092, 1, 1, 0, 40000), -(9500173, 2290099, 1, 1, 0, 40000), -(9500173, 2290102, 1, 1, 0, 40000), -(9500173, 2290119, 1, 1, 0, 40000), -(9500173, 2290128, 1, 1, 0, 120000), -(9500174, 2290002, 1, 1, 0, 40000), -(9500174, 2290014, 1, 1, 0, 40000), -(9500174, 2290030, 1, 1, 0, 40000), -(9500174, 2290080, 1, 1, 0, 40000), -(9500174, 2290130, 1, 1, 0, 120000), -(9500180, 2290010, 1, 1, 0, 40000), -(9500180, 2290028, 1, 1, 0, 40000), -(9500180, 2290126, 1, 1, 0, 120000), -(9500181, 2290010, 1, 1, 0, 40000), -(9500181, 2290028, 1, 1, 0, 40000), -(9500181, 2290126, 1, 1, 0, 120000), -(9500331, 2290010, 1, 1, 0, 40000), -(9500331, 2290028, 1, 1, 0, 40000), -(9500331, 2290126, 1, 1, 0, 120000), -(9500332, 2290132, 1, 1, 0, 120000), -(9500333, 2290006, 1, 1, 0, 40000), -(9500333, 2290030, 1, 1, 0, 40000), -(9500333, 2290032, 1, 1, 0, 40000), -(9500333, 2290060, 1, 1, 0, 40000), -(9500333, 2290076, 1, 1, 0, 40000), -(9500333, 2290104, 1, 1, 0, 40000), -(9500333, 2290117, 1, 1, 0, 40000), +(9420540, 2280006, 1, 1, 0, 500), +(9420540, 2290119, 1, 1, 0, 500), +(9420540, 2290120, 1, 1, 0, 500), +(9420544, 2280007, 1, 1, 0, 20000), +(9420544, 2280008, 1, 1, 0, 20000), +(9420544, 2280009, 1, 1, 0, 20000), +(9420544, 2280010, 1, 1, 0, 20000), +(9420544, 2290002, 1, 1, 0, 20000), +(9420544, 2290015, 1, 1, 0, 20000), +(9420544, 2290022, 1, 1, 0, 20000), +(9420544, 2290027, 1, 1, 0, 20000), +(9420544, 2290034, 1, 1, 0, 20000), +(9420544, 2290052, 1, 1, 0, 20000), +(9420544, 2290054, 1, 1, 0, 20000), +(9420544, 2290089, 1, 1, 0, 20000), +(9420544, 2290094, 1, 1, 0, 20000), +(9420544, 2290098, 1, 1, 0, 20000), +(9420544, 2290105, 1, 1, 0, 20000), +(9420544, 2290110, 1, 1, 0, 20000), +(9420544, 2290119, 1, 1, 0, 20000), +(9420549, 2280007, 1, 1, 0, 20000), +(9420549, 2280008, 1, 1, 0, 20000), +(9420549, 2280009, 1, 1, 0, 20000), +(9420549, 2280010, 1, 1, 0, 20000), +(9420549, 2290002, 1, 1, 0, 20000), +(9420549, 2290015, 1, 1, 0, 20000), +(9420549, 2290022, 1, 1, 0, 20000), +(9420549, 2290027, 1, 1, 0, 20000), +(9420549, 2290034, 1, 1, 0, 20000), +(9420549, 2290052, 1, 1, 0, 20000), +(9420549, 2290054, 1, 1, 0, 20000), +(9420549, 2290089, 1, 1, 0, 20000), +(9420549, 2290094, 1, 1, 0, 20000), +(9420549, 2290098, 1, 1, 0, 20000), +(9420549, 2290105, 1, 1, 0, 20000), +(9420549, 2290110, 1, 1, 0, 20000), +(9420549, 2290119, 1, 1, 0, 20000), +(9500166, 2290044, 1, 1, 0, 500), +(9500173, 2290018, 1, 1, 0, 10000), +(9500173, 2290019, 1, 1, 0, 10000), +(9500173, 2290032, 1, 1, 0, 10000), +(9500173, 2290042, 1, 1, 0, 10000), +(9500173, 2290058, 1, 1, 0, 10000), +(9500173, 2290068, 1, 1, 0, 10000), +(9500173, 2290072, 1, 1, 0, 10000), +(9500173, 2290092, 1, 1, 0, 10000), +(9500173, 2290099, 1, 1, 0, 10000), +(9500173, 2290102, 1, 1, 0, 10000), +(9500173, 2290119, 1, 1, 0, 10000), +(9500173, 2290128, 1, 1, 0, 30000), +(9500174, 2290002, 1, 1, 0, 10000), +(9500174, 2290014, 1, 1, 0, 10000), +(9500174, 2290030, 1, 1, 0, 10000), +(9500174, 2290080, 1, 1, 0, 10000), +(9500174, 2290130, 1, 1, 0, 30000), +(9500180, 2290010, 1, 1, 0, 10000), +(9500180, 2290028, 1, 1, 0, 10000), +(9500180, 2290126, 1, 1, 0, 30000), +(9500181, 2290010, 1, 1, 0, 10000), +(9500181, 2290028, 1, 1, 0, 10000), +(9500181, 2290126, 1, 1, 0, 30000), +(9500331, 2290010, 1, 1, 0, 10000), +(9500331, 2290028, 1, 1, 0, 10000), +(9500331, 2290126, 1, 1, 0, 30000), +(9500332, 2290132, 1, 1, 0, 30000), +(9500333, 2290006, 1, 1, 0, 10000), +(9500333, 2290030, 1, 1, 0, 10000), +(9500333, 2290032, 1, 1, 0, 10000), +(9500333, 2290060, 1, 1, 0, 10000), +(9500333, 2290076, 1, 1, 0, 10000), +(9500333, 2290104, 1, 1, 0, 10000), +(9500333, 2290117, 1, 1, 0, 10000), diff --git a/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java b/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java index 6ea2ad954a..f4a4b5ae37 100644 --- a/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java +++ b/tools/MapleSkillbookChanceFetcher/src/mapleskillbookchancefetcher/MapleSkillbookChanceFetcher.java @@ -93,7 +93,7 @@ public class MapleSkillbookChanceFetcher { int mobid = rs.getInt("dropperid"); int itemid = rs.getInt("itemid"); - int expectedChance = 1000; + int expectedChance = 250; if (mobStats.get(mobid) != null) { int level = mobStats.get(mobid).getLevel(); diff --git a/tools/ScriptQuestReleaseTracker/hashset.c b/tools/ScriptQuestReleaseTracker/hashset.c new file mode 100644 index 0000000000..71300f69f2 --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/hashset.c @@ -0,0 +1,238 @@ +#include +#include +#include + +//NOTE: should the HASH_MAXITEM or HASH_NUMBUCK value be too small, program will crash by SIG_SEGV +#define HASH_MAXITEM 4000 +#define HASH_NUMBUCK 1340 +#define HASH_HIVALUE 2147483647 //32-BIT integer + +#define HASH_REHTHRE 3.5 +#define HASH_REHRATE 5 + +typedef struct { + int list[HASH_MAXITEM]; + int first; + + unsigned int count; +} HastSetIndex; + +typedef struct { + HastSetIndex **table; + int *list; + + unsigned int threshold; + unsigned int length; + unsigned int count; +} HashSet; + +void hashset_create_table(HashSet *hs) { + hs->table = (HastSetIndex **)malloc(hs->length * sizeof(HastSetIndex *)); + hs->threshold = (unsigned int)(HASH_REHTHRE * hs->length); + + unsigned int i; + for(i = 0; i < hs->length; i++) { + hs->table[i] = (HastSetIndex *)malloc(sizeof(HastSetIndex)); + hs->table[i]->count = 0; + hs->table[i]->first = HASH_HIVALUE; + } +} + +HashSet* hashset_create() { + HashSet *hs = (HashSet *)malloc(sizeof(HashSet)); + hs->count = 0; + hs->length = HASH_NUMBUCK; + hs->list = NULL; + + hashset_create_table(hs); + return(hs); +} + +void hashset_destroy(HashSet *hs) { + if(hs->list != NULL) { + free(hs->list); + } + + unsigned int i; + for(i = 0; i < hs->length; i++) + free(hs->table[i]); + + free(hs->table); + free(hs); +} + +unsigned int hashset_maptable(HashSet *hs, int item) { + return(item % hs->length); +} + +unsigned int hashset_slot(HashSet *hs, int item, unsigned int *bucket) { + *bucket = hashset_maptable(hs, item); + + unsigned int i; + for(i = 0; i < hs->table[*bucket]->count; i++) { + if(hs->table[*bucket]->list[i] == item) + return(i); + } + + return(-1); +} + +short hashset_contains(HashSet *hs, int item, unsigned int *bucket) { + return(hashset_slot(hs, item, bucket) != -1); +} + +short hashset_insertinto(HashSet *hs, int item) { + unsigned int bucket; + + if(!hashset_contains(hs, item, &bucket)) { + if(hs->table[bucket]->first > item) + hs->table[bucket]->first = item; + + hs->table[bucket]->list[hs->table[bucket]->count] = item; + + (hs->count)++; + (hs->table[bucket]->count)++; + if(hs->table[bucket]->count > hs->threshold) return(1); + } + + return(0); +} + +void hashset_rehash(HashSet *hs) { + int *temp = (int *)malloc(hs->count * sizeof(int)); + unsigned int temp_cursor = 0, i, j; + + for(i = 0; i < hs->length; i++) { + for(j = 0; j < hs->table[i]->count; j++) { + temp[temp_cursor] = hs->table[i]->list[j]; + temp_cursor++; + } + } + + for(i = 0; i < hs->length; i++) + free(hs->table[i]); + free(hs->table); + + hs->count = 0; + hs->length *= HASH_REHRATE; + hashset_create_table(hs); + + for(i = 0; i < temp_cursor; i++) + hashset_insertinto(hs, temp[i]); + + free(temp); +} + +void hashset_insert(HashSet *hs, int item) { + if(hashset_insertinto(hs, item)) { + hashset_rehash(hs); + } +} + +int hashset_recalc_first(HashSet *hs, int bucket) { + int i, val = HASH_HIVALUE; + for(i = 0; i < hs->table[bucket]->count; i++) { + if(val > hs->table[bucket]->list[i]) + val = hs->table[bucket]->list[i]; + } + + return(val); +} + +void hashset_remove(HashSet *hs, int item) { + unsigned int bucket; + unsigned int slot = hashset_slot(hs, item, &bucket); + + if(slot != -1) { + (hs->count)--; + (hs->table[bucket]->count)--; + hs->table[bucket]->list[slot] = hs->table[bucket]->list[hs->table[bucket]->count]; + + if(item == hs->table[bucket]->first) + hs->table[bucket]->first = hashset_recalc_first(hs, bucket); + } +} + +short hashset_is_empty(HashSet *hs) { + return(hs->count == 0); +} + +void hashset_make_empty(HashSet *hs) { + unsigned int i; + for(i = 0; i < hs->length; i++) { + hs->table[i]->first = HASH_HIVALUE; + hs->table[i]->count = 0; + } + + hs->count = 0; +} + +int hashset_remove_first(HashSet *hs) { + int i, take = HASH_HIVALUE; + for(i = 0; i < hs->length; i++) { + if(take > hs->table[i]->first) + take = hs->table[i]->first; + } + + hashset_remove(hs, take); + return(take); +} + +void hashset_merge(HashSet *hs1, HashSet *hs2) { + //add values from hs2 to hs1 + + unsigned int i, j; + for(i = 0; i < hs2->length; i++) { + for(j = 0; j < hs2->table[i]->count; j++) { + hashset_insert(hs1, hs2->table[i]->list[j]); + } + } +} + +void hashset_dump(HashSet *hs) { + printf("HASHSET v1.0 -- count: %d, buckets: %d, threshold: %d\n", hs->count, hs->length, hs->threshold); + + unsigned int i, j; + for(i = 0; i < hs->length; i++) { + printf("\n%d -> ", i); + for(j = 0; j < hs->table[i]->count; j++) { + printf("%d ", hs->table[i]->list[j]); + } + printf("$"); + } + printf("\n"); +} + +int* hashset_list(HashSet *hs) { + int *list = hs->list; + if(list != NULL) { + free(list); + } + + list = (int *)malloc(hs->count * sizeof(int)); + + unsigned int i, j, k = 0; + for(i = 0; i < hs->length; i++) { + for(j = 0; j < hs->table[i]->count; j++) { + list[k] = hs->table[i]->list[j]; + k++; + } + } + + return list; +} + +/* + HASHSET: + + HashSet* hashset_create(); + void hashset_destroy(HashSet *hs); + short hashset_contains(HashSet *hs, int item, unsigned int *bucket); + void hashset_insert(HashSet *hs, int item); + void hashset_remove(HashSet *hs, int item); + short hashset_is_empty(HashSet *hs); + void hashset_make_empty(HashSet *hs); + int hashset_remove_first(HashSet *hs); + void hashset_merge(HashSet *hs1, HashSet *hs2); + void hashset_dump(HashSet *hs); +*/ diff --git a/tools/ScriptQuestReleaseTracker/pcre3.dll b/tools/ScriptQuestReleaseTracker/pcre3.dll new file mode 100644 index 0000000000..b5fd2a6378 Binary files /dev/null and b/tools/ScriptQuestReleaseTracker/pcre3.dll differ diff --git a/tools/ScriptQuestReleaseTracker/quest_diff.c b/tools/ScriptQuestReleaseTracker/quest_diff.c new file mode 100644 index 0000000000..6e842924ea --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/quest_diff.c @@ -0,0 +1,85 @@ +#include + +// string hash version by chqrlie - https://stackoverflow.com/questions/20462826/hash-function-for-strings-in-c +unsigned int strhash(const char *word) { + unsigned int hash = 0, c; + + size_t i = 0; + for (i = 0; word[i] != '\0'; i++) { + c = (unsigned char)word[i]; + hash = (hash << 3) + (hash >> (sizeof(hash) * CHAR_BIT - 3)) + c; + } + return hash % UINT_MAX; +} + +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include "strmap.h" +#include "hashset.c" + +void performQuestDiff(ScriptedQuestList *quests_then, ScriptedQuestList *quests_curr) { + char buf[100], bufhash[100]; + HashSet *script_quests = hashset_create(); + + // bookkeep quest-script hash + StrMap *sm = sm_new(2000); + + // insert ongoing scripts + resetScriptedQuestCursor(quests_curr); + while(true) { + ScriptedQuest *method = readScriptedQuest(quests_curr); + if (method == NULL) { + break; + } + + int hash_quest = strhash(method->name); + sprintf(bufhash, "%d", hash_quest); + + sm_put(sm, bufhash, method->name); + hashset_insert(script_quests, hash_quest); + } + + // remove initial scripts + resetScriptedQuestCursor(quests_then); + while(true) { + ScriptedQuest *method = readScriptedQuest(quests_then); + if (method == NULL) { + break; + } + + int hash_quest = strhash(method->name); + hashset_remove(script_quests, hash_quest); + } + + int *list = hashset_list(script_quests); + int i; + for (i = 0; i < script_quests->count; i++) { + int hash_quest = list[i]; + sprintf(bufhash, "%d", hash_quest); + + // dump ongoing script releases + sm_get(sm, bufhash, buf, sizeof(buf)); + printf("%s\n", buf); + } + + sm_delete(sm); + hashset_destroy(script_quests); +} diff --git a/tools/ScriptQuestReleaseTracker/quest_diff.h b/tools/ScriptQuestReleaseTracker/quest_diff.h new file mode 100644 index 0000000000..9911d581cc --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/quest_diff.h @@ -0,0 +1,28 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef QUEST_DIFF_H_ +#define QUEST_DIFF_H_ + +void performQuestDiff(ScriptedQuestList *quests_then, ScriptedQuestList *quests_curr); + +#include "quest_diff.c" + +#endif /* QUEST_DIFF_H_ */ diff --git a/tools/ScriptQuestReleaseTracker/quest_list.c b/tools/ScriptQuestReleaseTracker/quest_list.c new file mode 100644 index 0000000000..758bc64ab5 --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/quest_list.c @@ -0,0 +1,85 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +ScriptedQuest* createScriptedQuest(const char *name) { + ScriptedQuest* method = (ScriptedQuest *)malloc(sizeof(ScriptedQuest)); + method->name = (char *)malloc((strlen(name) + 1) * sizeof(char)); + strcpy(method->name, name); + return method; +} + +void freeScriptedQuest(ScriptedQuest *method) { + free(method->name); + free(method); +} + +ScriptedQuestList createScriptedQuestList() { + ScriptedQuestList list; + list.size = 0; + + ScriptedQuestListItem *item = (ScriptedQuestListItem *)malloc(sizeof(ScriptedQuestListItem)); + item->prox = NULL; + + list.last = item; + list.first = list.last; + + return list; +} + +void insertScriptedQuest(ScriptedQuestList *list, ScriptedQuest *method) { + ScriptedQuestListItem *item = (ScriptedQuestListItem *)malloc(sizeof(ScriptedQuestListItem)); + item->prox = NULL; + + list->last->method = method; + list->last->prox = item; + + list->last = item; + list->size++; +} + +void freeScriptedQuestList(ScriptedQuestList *list) { + ScriptedQuestListItem *aux = list->first; + + list->first = list->last; + list->size = 0; + + while (aux->prox != NULL) { + ScriptedQuestListItem *aux2 = aux; + aux = aux->prox; + + freeScriptedQuest(aux2->method); + free(aux2); + } + free(aux); +} + +void resetScriptedQuestCursor(ScriptedQuestList *list) { + list->cursor = list->first; +} + +ScriptedQuest* readScriptedQuest(ScriptedQuestList *list) { + ScriptedQuestListItem *aux = list->cursor; + if (aux->prox == NULL) { + return NULL; + } + + list->cursor = aux->prox; + return aux->method; +} diff --git a/src/net/server/channel/worker/MobClearSkillScheduler.java b/tools/ScriptQuestReleaseTracker/quest_list.h similarity index 67% rename from src/net/server/channel/worker/MobClearSkillScheduler.java rename to tools/ScriptQuestReleaseTracker/quest_list.h index 5aff13cd3b..4d6b91ef3b 100644 --- a/src/net/server/channel/worker/MobClearSkillScheduler.java +++ b/tools/ScriptQuestReleaseTracker/quest_list.h @@ -17,20 +17,27 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.channel.worker; -import net.server.audit.locks.MonitoredLockType; +#ifndef QUEST_LIST_H_ +#define QUEST_LIST_H_ -/** - * - * @author Ronan - */ -public class MobClearSkillScheduler extends BaseScheduler { - public MobClearSkillScheduler() { - super(MonitoredLockType.CHANNEL_MOBSKILL); - } - - public void registerClearSkillAction(Runnable runAction, long delay) { - registerEntry(runAction, runAction, delay); - } -} +typedef struct { + char *name; +} ScriptedQuest; + +typedef struct ScriptedQuestListItem { + ScriptedQuest *method; + struct ScriptedQuestListItem *prox; +} ScriptedQuestListItem; + +typedef struct { + ScriptedQuestListItem *first; + ScriptedQuestListItem *last; + ScriptedQuestListItem *cursor; + + int size; +} ScriptedQuestList; + +#include "quest_list.c" + +#endif /* QUEST_LIST_H_ */ diff --git a/tools/ScriptQuestReleaseTracker/script_tracker.c b/tools/ScriptQuestReleaseTracker/script_tracker.c new file mode 100644 index 0000000000..4a1bb71fe0 --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/script_tracker.c @@ -0,0 +1,159 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include "strmap.c" +#include "quest_list.h" +#include "quest_diff.h" + +ScriptedQuestList getBestSubstringsFromStringList(char *aStrRegex, ScriptedQuestList *lines, int lines_size) { + ScriptedQuestList ret = createScriptedQuestList(); + + // ------------ an adaptation from Mitch Richling's https://www.mitchr.me/SS/exampleCode/AUPG/pcre_example.c.html ----------- + + int subStrVec[30]; + int subStrVecLength = 30; + const char *pcreErrorStr; + int pcreErrorOffset; + + pcre *reCompiled = pcre_compile(aStrRegex, 0, &pcreErrorStr, &pcreErrorOffset, NULL); + if(reCompiled == NULL) { + printf("ERROR: Could not compile '%s': %s\n", aStrRegex, pcreErrorStr); + return ret; + } + + pcre_extra *pcreExtra = pcre_study(reCompiled, 0, &pcreErrorStr); + if(pcreErrorStr != NULL) { + printf("ERROR: Could not study '%s': %s\n", aStrRegex, pcreErrorStr); + return ret; + } + + int i; + for (i = 0; i < lines_size; i++) { + ScriptedQuestList list = lines[i]; + + resetScriptedQuestCursor(&list); + while(true) { + ScriptedQuest *method = readScriptedQuest(&list); + if (method == NULL) { + break; + } + + char *str = method->name; + int st = 0, en = strlen(str); + while(st < en) { + int pcreExecRet = pcre_exec(reCompiled, pcreExtra, str, en, st, 0, subStrVec, subStrVecLength); + if(pcreExecRet < 0) { + switch(pcreExecRet) { + //case PCRE_ERROR_NOMATCH : printf("String did not match the pattern\n"); break; + case PCRE_ERROR_NULL : printf("Something was null\n"); break; + case PCRE_ERROR_BADOPTION : printf("A bad option was passed\n"); break; + case PCRE_ERROR_BADMAGIC : printf("Magic number bad (compiled re corrupt?)\n"); break; + case PCRE_ERROR_UNKNOWN_NODE : printf("Something kooky in the compiled re\n"); break; + case PCRE_ERROR_NOMEMORY : printf("Ran out of memory\n"); break; + //default : printf("Unknown error\n"); break; + } + + break; + } else { + if(pcreExecRet == 0) { + printf("But too many substrings were found to fit in subStrVec!\n"); + // Set rc to the max number of substring matches possible. + pcreExecRet = 30 / 3; + } + + const char *psubStrMatchStr; + pcre_get_substring(str, subStrVec, pcreExecRet, 1, &(psubStrMatchStr)); + + insertScriptedQuest(&ret, createScriptedQuest(psubStrMatchStr)); + pcre_free_substring(psubStrMatchStr); + + st = subStrVec[1]; + } + } + } + } + + pcre_free(reCompiled); + + if(pcreExtra != NULL) { + pcre_free(pcreExtra); + } + + return ret; +} + +char *getContentFromFile(FILE *f) { + fseek(f, 0, SEEK_END); // implemented by user529758 @ StackOverflow + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + char *string = malloc(fsize + 1); + fread(string, 1, fsize, f); + + string[fsize] = 0; + return string; +} + +ScriptedQuestList readQuestXml(char *file_path) { + ScriptedQuestList *file_content = (ScriptedQuestList *)malloc(sizeof(ScriptedQuestList)); + file_content[0] = createScriptedQuestList(); + + FILE *f = fopen(file_path, "r+t"); + char *content = getContentFromFile(f); + + char *tok = strtok(content, "\n"); + int i = 0; + while (tok != NULL) { + insertScriptedQuest(&(file_content[0]), createScriptedQuest(tok)); + tok = strtok(NULL, "\n"); + i++; + } + + free(content); + fclose(f); + + ScriptedQuestList ret = getBestSubstringsFromStringList("script\" value=\"(.+)\"", file_content, 1); + + freeScriptedQuestList(&file_content[0]); + free(file_content); + + return ret; +} + +void trackScriptQuestReleases() { + ScriptedQuestList quests_then = readQuestXml("Check2.img.xml"); + ScriptedQuestList quests_curr = readQuestXml("Check.img.xml"); + + performQuestDiff(&quests_then, &quests_curr); + + freeScriptedQuestList(&quests_curr); + freeScriptedQuestList(&quests_then); +} + +int main() { + trackScriptQuestReleases(); + return 0; +} diff --git a/tools/ScriptQuestReleaseTracker/strmap.c b/tools/ScriptQuestReleaseTracker/strmap.c new file mode 100644 index 0000000000..6111209abe --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/strmap.c @@ -0,0 +1,515 @@ +/* + * strmap version 2.0.1 + * + * ANSI C hash table for strings. + * + * Version history: + * 1.0.0 - initial release + * 2.0.0 - changed function prefix from strmap to sm to ensure + * ANSI C compatibility + * 2.0.1 - improved documentation + * + * strmap.c + * + * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * strmap is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * strmap 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with strmap. If not, see . + */ +#include "strmap.h" + +typedef struct Pair Pair; + +typedef struct Bucket Bucket; + +struct Pair { + char *key; + char *value; +}; + +struct Bucket { + unsigned int count; + Pair *pairs; +}; + +struct StrMap { + unsigned int count; + Bucket *buckets; +}; + +static Pair * get_pair(Bucket *bucket, const char *key); +static unsigned long hash(const char *str); + +StrMap * sm_new(unsigned int capacity) +{ + StrMap *map; + + map = malloc(sizeof(StrMap)); + if (map == NULL) { + return NULL; + } + map->count = capacity; + map->buckets = malloc(map->count * sizeof(Bucket)); + if (map->buckets == NULL) { + free(map); + return NULL; + } + memset(map->buckets, 0, map->count * sizeof(Bucket)); + return map; +} + +void sm_delete(StrMap *map) +{ + unsigned int i, j, n, m; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return; + } + n = map->count; + bucket = map->buckets; + i = 0; + while (i < n) { + m = bucket->count; + pair = bucket->pairs; + j = 0; + while(j < m) { + free(pair->key); + free(pair->value); + pair++; + j++; + } + free(bucket->pairs); + bucket++; + i++; + } + free(map->buckets); + free(map); +} + +int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf) +{ + unsigned int index; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (key == NULL) { + return 0; + } + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + pair = get_pair(bucket, key); + if (pair == NULL) { + return 0; + } + if (out_buf == NULL && n_out_buf == 0) { + return strlen(pair->value) + 1; + } + if (out_buf == NULL) { + return 0; + } + if (strlen(pair->value) >= n_out_buf) { + return 0; + } + strcpy(out_buf, pair->value); + return 1; +} + +int sm_exists(const StrMap *map, const char *key) +{ + unsigned int index; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (key == NULL) { + return 0; + } + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + pair = get_pair(bucket, key); + if (pair == NULL) { + return 0; + } + return 1; +} + +int sm_put(StrMap *map, const char *key, const char *value) +{ + unsigned int key_len, value_len, index; + Bucket *bucket; + Pair *tmp_pairs, *pair; + char *tmp_value; + char *new_key, *new_value; + + if (map == NULL) { + return 0; + } + if (key == NULL || value == NULL) { + return 0; + } + key_len = strlen(key); + value_len = strlen(value); + /* Get a pointer to the bucket the key string hashes to */ + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + /* Check if we can handle insertion by simply replacing + * an existing value in a key-value pair in the bucket. + */ + if ((pair = get_pair(bucket, key)) != NULL) { + /* The bucket contains a pair that matches the provided key, + * change the value for that pair to the new value. + */ + if (strlen(pair->value) < value_len) { + /* If the new value is larger than the old value, re-allocate + * space for the new larger value. + */ + tmp_value = realloc(pair->value, (value_len + 1) * sizeof(char)); + if (tmp_value == NULL) { + return 0; + } + pair->value = tmp_value; + } + /* Copy the new value into the pair that matches the key */ + strcpy(pair->value, value); + return 1; + } + /* Allocate space for a new key and value */ + new_key = malloc((key_len + 1) * sizeof(char)); + if (new_key == NULL) { + return 0; + } + new_value = malloc((value_len + 1) * sizeof(char)); + if (new_value == NULL) { + free(new_key); + return 0; + } + /* Create a key-value pair */ + if (bucket->count == 0) { + /* The bucket is empty, lazily allocate space for a single + * key-value pair. + */ + bucket->pairs = malloc(sizeof(Pair)); + if (bucket->pairs == NULL) { + free(new_key); + free(new_value); + return 0; + } + bucket->count = 1; + } + else { + /* The bucket wasn't empty but no pair existed that matches the provided + * key, so create a new key-value pair. + */ + tmp_pairs = realloc(bucket->pairs, (bucket->count + 1) * sizeof(Pair)); + if (tmp_pairs == NULL) { + free(new_key); + free(new_value); + return 0; + } + bucket->pairs = tmp_pairs; + bucket->count++; + } + /* Get the last pair in the chain for the bucket */ + pair = &(bucket->pairs[bucket->count - 1]); + pair->key = new_key; + pair->value = new_value; + /* Copy the key and its value into the key-value pair */ + strcpy(pair->key, key); + strcpy(pair->value, value); + return 1; +} + +int sm_get_count(const StrMap *map) +{ + unsigned int i, j, n, m; + unsigned int count; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + bucket = map->buckets; + n = map->count; + i = 0; + count = 0; + while (i < n) { + pair = bucket->pairs; + m = bucket->count; + j = 0; + while (j < m) { + count++; + pair++; + j++; + } + bucket++; + i++; + } + return count; +} + +int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj) +{ + unsigned int i, j, n, m; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (enum_func == NULL) { + return 0; + } + bucket = map->buckets; + n = map->count; + i = 0; + while (i < n) { + pair = bucket->pairs; + m = bucket->count; + j = 0; + while (j < m) { + enum_func(pair->key, pair->value, obj); + pair++; + j++; + } + bucket++; + i++; + } + return 1; +} + +/* + * Returns a pair from the bucket that matches the provided key, + * or null if no such pair exist. + */ +static Pair * get_pair(Bucket *bucket, const char *key) +{ + unsigned int i, n; + Pair *pair; + + n = bucket->count; + if (n == 0) { + return NULL; + } + pair = bucket->pairs; + i = 0; + while (i < n) { + if (pair->key != NULL && pair->value != NULL) { + if (strcmp(pair->key, key) == 0) { + return pair; + } + } + pair++; + i++; + } + return NULL; +} + +/* + * Returns a hash code for the provided string. + */ +static unsigned long hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while (c = *str++) { + hash = ((hash << 5) + hash) + c; + } + return hash; +} + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +*/ \ No newline at end of file diff --git a/tools/ScriptQuestReleaseTracker/strmap.h b/tools/ScriptQuestReleaseTracker/strmap.h new file mode 100644 index 0000000000..b0ff19cbc5 --- /dev/null +++ b/tools/ScriptQuestReleaseTracker/strmap.h @@ -0,0 +1,356 @@ +/* + * strmap version 2.0.1 + * + * ANSI C hash table for strings. + * + * Version history: + * 1.0.0 - initial release + * 2.0.0 - changed function prefix from strmap to sm to ensure + * ANSI C compatibility + * 2.0.1 - improved documentation + * + * strmap.h + * + * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * strmap is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * strmap 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with strmap. If not, see . + */ +#ifndef _STRMAP_H_ +#define _STRMAP_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +typedef struct StrMap StrMap; + +/* + * This callback function is called once per key-value when iterating over + * all keys associated to values. + * + * Parameters: + * + * key: A pointer to a null-terminated C string. The string must not + * be modified by the client. + * + * value: A pointer to a null-terminated C string. The string must + * not be modified by the client. + * + * obj: A pointer to a client-specific object. This parameter may be + * null. + * + * Return value: None. + */ +typedef void(*sm_enum_func)(const char *key, const char *value, const void *obj); + +/* + * Creates a string map. + * + * Parameters: + * + * capacity: The number of top-level slots this string map + * should allocate. This parameter must be > 0. + * + * Return value: A pointer to a string map object, + * or null if a new string map could not be allocated. + */ +StrMap * sm_new(unsigned int capacity); + +/* + * Releases all memory held by a string map object. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * If the supplied string map has been previously released, the + * behaviour of this function is undefined. + * + * Return value: None. + */ +void sm_delete(StrMap *map); + +/* + * Returns the value associated with the supplied key. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out_buf: A pointer to an output buffer which will contain the value, + * if it exists and fits into the buffer. + * + * n_out_buf: The size of the output buffer in bytes. + * + * Return value: If out_buf is set to null and n_out_buf is set to 0 the return + * value will be the number of bytes required to store the value (if it exists) + * and its null-terminator. For all other parameter configurations the return value + * is 1 if an associated value was found and completely copied into the output buffer, + * 0 otherwise. + */ +int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf); + +/* + * Queries the existence of a key. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: 1 if the key exists, 0 otherwise. + */ +int sm_exists(const StrMap *map, const char *key); + +/* + * Associates a value with the supplied key. If the key is already + * associated with a value, the previous value is replaced. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * value: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * Return value: 1 if the association succeeded, 0 otherwise. + */ +int sm_put(StrMap *map, const char *key, const char *value); + +/* + * Returns the number of associations between keys and values. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * Return value: The number of associations between keys and values. + */ +int sm_get_count(const StrMap *map); + +/* + * An enumerator over all associations between keys and values. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * enum_func: A pointer to a callback function that will be + * called by this procedure once for every key associated + * with a value. This parameter cannot be null. + * + * obj: A pointer to a client-specific object. This parameter will be + * passed back to the client's callback function. This parameter can + * be null. + * + * Return value: 1 if enumeration completed, 0 otherwise. + */ +int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +*/ \ No newline at end of file diff --git a/tools/ScriptStaticMethodTracker/method_list.c b/tools/ScriptStaticMethodTracker/method_list.c new file mode 100644 index 0000000000..4fd6a87372 --- /dev/null +++ b/tools/ScriptStaticMethodTracker/method_list.c @@ -0,0 +1,85 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +JavaMethod* createJavaMethod(const char *name) { + JavaMethod* method = (JavaMethod *)malloc(sizeof(JavaMethod)); + method->name = (char *)malloc((strlen(name) + 1) * sizeof(char)); + strcpy(method->name, name); + return method; +} + +void freeJavaMethod(JavaMethod *method) { + free(method->name); + free(method); +} + +JavaMethodList createJavaMethodList() { + JavaMethodList list; + list.size = 0; + + JavaMethodListItem *item = (JavaMethodListItem *)malloc(sizeof(JavaMethodListItem)); + item->prox = NULL; + + list.last = item; + list.first = list.last; + + return list; +} + +void insertJavaMethod(JavaMethodList *list, JavaMethod *method) { + JavaMethodListItem *item = (JavaMethodListItem *)malloc(sizeof(JavaMethodListItem)); + item->prox = NULL; + + list->last->method = method; + list->last->prox = item; + + list->last = item; + list->size++; +} + +void freeJavaMethodList(JavaMethodList *list) { + JavaMethodListItem *aux = list->first; + + list->first = list->last; + list->size = 0; + + while (aux->prox != NULL) { + JavaMethodListItem *aux2 = aux; + aux = aux->prox; + + freeJavaMethod(aux2->method); + free(aux2); + } + free(aux); +} + +void resetJavaMethodCursor(JavaMethodList *list) { + list->cursor = list->first; +} + +JavaMethod* readJavaMethod(JavaMethodList *list) { + JavaMethodListItem *aux = list->cursor; + if (aux->prox == NULL) { + return NULL; + } + + list->cursor = aux->prox; + return aux->method; +} diff --git a/src/net/server/channel/worker/EventScheduler.java b/tools/ScriptStaticMethodTracker/method_list.h similarity index 68% rename from src/net/server/channel/worker/EventScheduler.java rename to tools/ScriptStaticMethodTracker/method_list.h index 554f9c5c22..ae4fabed1d 100644 --- a/src/net/server/channel/worker/EventScheduler.java +++ b/tools/ScriptStaticMethodTracker/method_list.h @@ -17,20 +17,27 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server.channel.worker; -import net.server.audit.locks.MonitoredLockType; +#ifndef METHOD_LIST_H_ +#define METHOD_LIST_H_ -/** - * - * @author Ronan - */ -public class EventScheduler extends BaseScheduler { - public EventScheduler() { - super(MonitoredLockType.CHANNEL_EVENTS); - } - - public void registerDelayedAction(Runnable runAction, long delay) { - registerEntry(runAction, runAction, delay); - } -} +typedef struct { + char *name; +} JavaMethod; + +typedef struct JavaMethodListItem { + JavaMethod *method; + struct JavaMethodListItem *prox; +} JavaMethodListItem; + +typedef struct { + JavaMethodListItem *first; + JavaMethodListItem *last; + JavaMethodListItem *cursor; + + int size; +} JavaMethodList; + +#include "method_list.c" + +#endif /* METHOD_LIST_H_ */ diff --git a/tools/ScriptStaticMethodTracker/method_tracker.c b/tools/ScriptStaticMethodTracker/method_tracker.c new file mode 100644 index 0000000000..5a23d0f23e --- /dev/null +++ b/tools/ScriptStaticMethodTracker/method_tracker.c @@ -0,0 +1,349 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include "method_list.h" +#include "script_path.h" + +JavaMethodList getBestSubstringsFromStringList(char *aStrRegex, JavaMethodList *lines, int lines_size) { + JavaMethodList ret = createJavaMethodList(); + + // ------------ an adaptation from Mitch Richling's https://www.mitchr.me/SS/exampleCode/AUPG/pcre_example.c.html ----------- + + int subStrVec[30]; + int subStrVecLength = 30; + const char *pcreErrorStr; + int pcreErrorOffset; + + pcre *reCompiled = pcre_compile(aStrRegex, 0, &pcreErrorStr, &pcreErrorOffset, NULL); + if(reCompiled == NULL) { + printf("ERROR: Could not compile '%s': %s\n", aStrRegex, pcreErrorStr); + return ret; + } + + pcre_extra *pcreExtra = pcre_study(reCompiled, 0, &pcreErrorStr); + if(pcreErrorStr != NULL) { + printf("ERROR: Could not study '%s': %s\n", aStrRegex, pcreErrorStr); + return ret; + } + + int i; + for (i = 0; i < lines_size; i++) { + JavaMethodList list = lines[i]; + + resetJavaMethodCursor(&list); + while(true) { + JavaMethod *method = readJavaMethod(&list); + if (method == NULL) { + break; + } + + char *str = method->name; + int st = 0, en = strlen(str); + while (st < en) { + int pcreExecRet = pcre_exec(reCompiled, pcreExtra, str, en, st, 0, subStrVec, subStrVecLength); + if(pcreExecRet < 0) { + switch(pcreExecRet) { + //case PCRE_ERROR_NOMATCH : printf("String did not match the pattern\n"); break; + case PCRE_ERROR_NULL : printf("Something was null\n"); break; + case PCRE_ERROR_BADOPTION : printf("A bad option was passed\n"); break; + case PCRE_ERROR_BADMAGIC : printf("Magic number bad (compiled re corrupt?)\n"); break; + case PCRE_ERROR_UNKNOWN_NODE : printf("Something kooky in the compiled re\n"); break; + case PCRE_ERROR_NOMEMORY : printf("Ran out of memory\n"); break; + //default : printf("Unknown error\n"); break; + } + + break; // no more matches found + } else { + if(pcreExecRet == 0) { + printf("But too many substrings were found to fit in subStrVec!\n"); + // Set rc to the max number of substring matches possible. + pcreExecRet = 30 / 3; + } + + const char *psubStrMatchStr; + pcre_get_substring(str, subStrVec, pcreExecRet, 0, &(psubStrMatchStr)); + + insertJavaMethod(&ret, createJavaMethod(psubStrMatchStr)); + pcre_free_substring(psubStrMatchStr); + + st = subStrVec[1]; + } + } + } + } + + pcre_free(reCompiled); + + if(pcreExtra != NULL) { + pcre_free(pcreExtra); + } + + return ret; +} + +char* extractStaticMethodName(const char *method_line) { + char *aStrRegex = "([A-Za-z0-9])+(\\s)*\\("; + + int lines_size = 1; + JavaMethodList *lines = (JavaMethodList *)malloc(lines_size * sizeof(JavaMethodList)); + + int i; + for (i = 0; i < lines_size; i++) { + lines[i] = createJavaMethodList(); + insertJavaMethod(&(lines[i]), createJavaMethod(method_line)); + } + + JavaMethodList subs = getBestSubstringsFromStringList(aStrRegex, lines, lines_size); + + char *ret; + if (subs.size > 0) { + resetJavaMethodCursor(&subs); + JavaMethod *method = readJavaMethod(&subs); + + char *method_scoop = method->name; + int i; + for (i = 0; i < strlen(method_scoop) - 1; i++) { + char ch = method_scoop[i]; + if (ch == '(' || ch == ' ' || ch == '\t') { + break; + } + } + method->name[i] = 0; + + ret = (char *)malloc((strlen(method->name) + 1) * sizeof(char)); + strcpy(ret, method->name); + } else { + ret = NULL; + } + + freeJavaMethodList(&subs); + + for (i = 0; i < lines_size; i++) { + freeJavaMethodList(&(lines[i])); + } + + free(lines); + + return ret; +} + +JavaMethodList getStaticJavaMethodNames(char *aStrRegex, JavaMethodList *lines, int lines_size) { + JavaMethodList subs = getBestSubstringsFromStringList(aStrRegex, lines, lines_size); + JavaMethodList ret = createJavaMethodList(); + + resetJavaMethodCursor(&subs); + while (true) { + JavaMethod *method = readJavaMethod(&subs); + if (method == NULL) { + break; + } + + char *method_name = extractStaticMethodName(method->name); + if (method_name != NULL) { + insertJavaMethod(&ret, createJavaMethod(method_name)); + free(method_name); + } + } + + freeJavaMethodList(&subs); + + return ret; +} + +bool isIgnoreMethod(char *method_name) { + const char * ignoreMethods[] = {"getInstance", "toString", NULL}; + + int i = 0; + while(true) { + const char *ign = ignoreMethods[i]; + if (ign == NULL) { + break; + } + + if (!strcmp(method_name, ign)) { + return true; + } + + i++; + } + + return false; +} + +JavaMethodList trackerFindSourceStaticMethods(JavaMethodList *lines, int lines_size) { + char *aStrRegex = "(public static\\s).*([A-Za-z0-9])+(\\s)*\\(.*\\{"; + JavaMethodList ret = createJavaMethodList(); + + JavaMethodList list = getStaticJavaMethodNames(aStrRegex, lines, lines_size); + resetJavaMethodCursor(&list); + while(true) { + JavaMethod *method = readJavaMethod(&list); + if (method == NULL) { + break; + } + + if (isIgnoreMethod(method->name)) { + continue; + } + + insertJavaMethod(&ret, createJavaMethod(method->name)); + } + + freeJavaMethodList(&list); + + return ret; +} + +char *getContentFromFile(FILE *f) { + fseek(f, 0, SEEK_END); // implemented by user529758 @ StackOverflow + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); /* same as rewind(f); */ + + char *string = malloc(fsize + 1); + fread(string, 1, fsize, f); + + string[fsize] = 0; + return string; +} + +bool locateMethodCall(const char *method_name, char *file_path) { + FILE *f = fopen(file_path, "r+t"); + + char aStrRegex[1000]; + strcpy(aStrRegex, method_name); + strcat(aStrRegex, "(\\s)*\\("); + + JavaMethodList *file_content = (JavaMethodList *)malloc(sizeof(JavaMethodList)); + file_content[0] = createJavaMethodList(); + + char *content = getContentFromFile(f); + + JavaMethod *method = createJavaMethod(content); + insertJavaMethod(&(file_content[0]), method); + free(content); + + JavaMethodList list = getBestSubstringsFromStringList(aStrRegex, file_content, 1); + bool found = (list.size > 0); + + freeJavaMethodList(&(file_content[0])); + free(file_content); + fclose(f); + + return found; +} + +void locateMethodCalls(const char *method_name, char **file_paths, int file_paths_size) { + int i; + for (i = 0; i < file_paths_size; i++) { + char *path = file_paths[i]; + if (locateMethodCall(method_name, path)) { + printf(" %s : \'%s\'\n", path, method_name); + } + } +} + +int trackerLocateScriptsStaticCalls(JavaMethodList method_names) { + ScriptFiles *files = createScriptFiles("../../scripts"); + if (files == NULL) { + printf("ERROR: Could not initialize script files.\n"); + return -1; + } + + resetJavaMethodCursor(&method_names); + while (true) { + JavaMethod *method = readJavaMethod(&method_names); + if (method == NULL) { + break; + } + + locateMethodCalls(method->name, files->file_paths, files->file_paths_size); + } + + freeScriptFiles(files); + return 0; +} + +typedef struct { + JavaMethodList *file_content; + int size; +} SourceFilesContent; + +SourceFilesContent* readSourceFileContents() { + ScriptFiles *srcFilePaths = createScriptFiles("../../src"); + + SourceFilesContent *files = (SourceFilesContent *)malloc(sizeof(SourceFilesContent)); + files->file_content = (JavaMethodList *)malloc(srcFilePaths->file_paths_size * sizeof(JavaMethodList)); + files->size = srcFilePaths->file_paths_size; + + //int max_len = 0; + int i; + for (i = 0; i < srcFilePaths->file_paths_size; i++) { + files->file_content[i] = createJavaMethodList(); + + FILE *f = fopen(srcFilePaths->file_paths[i], "r+t"); + char *content = getContentFromFile(f); + + //int this_len = strlen(content); + //if (max_len < this_len) max_len = this_len; + + fclose(f); + + insertJavaMethod(&(files->file_content[i]), createJavaMethod(content)); + } + + freeScriptFiles(srcFilePaths); + + return files; +} + +void freeSourceFileContents(SourceFilesContent *files) { + int i; + for (i = 0; i < files->size; i++) { + freeJavaMethodList(&(files->file_content[i])); + } + + free(files->file_content); + free(files); +} + +int main() { + printf("Loading source files...\n"); + SourceFilesContent *src_contents = readSourceFileContents(); + + int lines_size = src_contents->size; + JavaMethodList *lines = src_contents->file_content; + + printf("Tracking static methods on source...\n"); + JavaMethodList method_names = trackerFindSourceStaticMethods(lines, lines_size); + printf("Finding static methods calls on scripts...\n"); + trackerLocateScriptsStaticCalls(method_names); + printf("Track complete!\n"); + + freeSourceFileContents(src_contents); + freeJavaMethodList(&method_names); + + return 0; +} diff --git a/tools/ScriptStaticMethodTracker/pcre3.dll b/tools/ScriptStaticMethodTracker/pcre3.dll new file mode 100644 index 0000000000..b5fd2a6378 Binary files /dev/null and b/tools/ScriptStaticMethodTracker/pcre3.dll differ diff --git a/tools/ScriptStaticMethodTracker/script_path.c b/tools/ScriptStaticMethodTracker/script_path.c new file mode 100644 index 0000000000..aab9ddd1b8 --- /dev/null +++ b/tools/ScriptStaticMethodTracker/script_path.c @@ -0,0 +1,72 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +void fetchScriptFilesFromDirectory(ScriptFiles *files, char *root_dir) { + struct dirent *de; + struct stat info; + DIR *dir = opendir(root_dir); + if (dir != NULL) { + de = readdir(dir); // skip . + de = readdir(dir); // skip .. + + while ((de = readdir(dir)) != NULL) { + char file_path[SCRIPT_FILES_MAX_PATH_SIZE]; + strcpy(file_path, root_dir); + strcat(file_path, "/"); + strcat(file_path, de->d_name); + + stat(file_path, &info); + if (S_ISREG(info.st_mode)) { + files->file_paths[files->file_paths_size] = (char *)malloc(SCRIPT_FILES_MAX_PATH_SIZE * sizeof(char)); + strcpy(files->file_paths[files->file_paths_size], file_path); + files->file_paths_size++; + } else { + fetchScriptFilesFromDirectory(files, file_path); + } + } + } +} + +ScriptFiles* createScriptFiles(char *root_dir) { + ScriptFiles *files = (ScriptFiles *)malloc(sizeof(ScriptFiles)); + + files->file_paths = (char **)malloc(SCRIPT_FILES_MAX_COUNT * sizeof(char *)); + files->file_paths_size = 0; + + fetchScriptFilesFromDirectory(files, root_dir); + + return files; +} + +void freeScriptFiles(ScriptFiles *files) { + int i; + for (i = 0; i < files->file_paths_size; i++) { + free(files->file_paths[i]); + } + + free(files->file_paths); + free(files); +} diff --git a/tools/ScriptStaticMethodTracker/script_path.h b/tools/ScriptStaticMethodTracker/script_path.h new file mode 100644 index 0000000000..cd2452a517 --- /dev/null +++ b/tools/ScriptStaticMethodTracker/script_path.h @@ -0,0 +1,34 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2019 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef SCRIPT_PATH_H_ +#define SCRIPT_PATH_H_ + +#define SCRIPT_FILES_MAX_COUNT 70000 +#define SCRIPT_FILES_MAX_PATH_SIZE 40000 + +typedef struct { + char **file_paths; + int file_paths_size; +} ScriptFiles; + +#include "script_path.c" + +#endif /* SCRIPT_PATH_H_ */ diff --git a/world.ini b/world.ini deleted file mode 100644 index 0dda7a5575..0000000000 --- a/world.ini +++ /dev/null @@ -1,153 +0,0 @@ -#Author: Ronan, Vcoc -#World Name: (0 "Scania", 1 "Bera", 2 "Broa", 3 "Windia", 4 "Khaini", 5 "Bellocan", 6 "Mardia", 7 "Kradia", 8 "Yellonde", 9 "Demethos", 10 "Galicia", 11 "El Nido", 12 "Zenith", 13 "Arcenia", 14 "Kastia", 15 "Judis", 16 "Plana", 17 "Kalluna", 18 "Stius", 19 "Croa", 20 "Medere") -#Flag types: (0 = nothing, 1 = event, 2 = new, 3 = hot) -#Recommended to use only up to 15 worlds -gmserver=false -worlds=1 - -#Properties for Scania 0 -flag0=0 -servermessage0=Welcome to Scania! -eventmessage0=Scania! -whyamirecommended0=Welcome to Scania! -channels0=3 - -#Properties for Bera 1 -flag1=0 -servermessage1=Welcome to Bera! -eventmessage1=Bera! -whyamirecommended1=Welcome to Bera! -channels1=3 - -#Properties for Broa 2 -flag2=0 -servermessage2=Welcome to Broa! -eventmessage2=Broa! -whyamirecommended2=Welcome to Broa! -channels2=3 - -#Properties for Windia 3 -flag3=0 -servermessage3=Welcome to Windia! -eventmessage3=Windia! -whyamirecommended3=Welcome to Windia! -channels3=3 - -#Properties for Khaini 4 -flag4=0 -servermessage4=Welcome to Khaini! -eventmessage4=Khaini! -whyamirecommended4=Welcome to Khaini! -channels4=3 - -#Properties for Bellocan 5 -flag5=0 -servermessage5=Welcome to Bellocan! -eventmessage5=Bellocan! -whyamirecommended5=Welcome to Bellocan! -channels5=3 - -#Properties for Mardia 6 -flag6=0 -servermessage6=Welcome to Mardia! -eventmessage6=Mardia! -whyamirecommended6=Welcome to Mardia! -channels6=3 - -#Properties for Kradia 7 -flag7=0 -servermessage7=Welcome to Kradia! -eventmessage7=Kradia! -whyamirecommended7=Welcome to Kradia! -channels7=3 - -#Properties for Yellonde 8 -flag8=0 -servermessage8=Welcome to Yellonde! -eventmessage8=Yellonde! -whyamirecommended8=Welcome to Yellonde! -channels8=3 - -#Properties for Demethos 9 -flag9=0 -servermessage9=Welcome to Demethos! -eventmessage9=Demethos! -whyamirecommended9=Welcome to Demethos! -channels9=3 - -#Properties for Galicia 10 -flag10=0 -servermessage10=Welcome to Galicia! -eventmessage10=Galicia! -whyamirecommended10=Welcome to Galicia! -channels10=3 - -#Properties for El Nido 11 -flag11=0 -servermessage11=Welcome to El Nido! -eventmessage11=El Nido! -whyamirecommended11=Welcome to El Nido! -channels11=3 - -#Properties for Zenith 12 -flag12=0 -servermessage12=Welcome to Zenith! -eventmessage12=Zenith! -whyamirecommended12=Welcome to Zenith! -channels12=3 - -#Properties for Arcenia 13 -flag13=0 -servermessage13=Welcome to Arcenia! -eventmessage13=Arcenia! -whyamirecommended13=Welcome to Arcenia! -channels13=3 - -#Properties for Kastia 14 -flag14=0 -servermessage14=Welcome to Kastia! -eventmessage14=Kastia! -whyamirecommended14=Welcome to Kastia! -channels14=3 - -#Properties for Judis 15 -flag15=0 -servermessage15=Welcome to Judis! -eventmessage15=Judis! -whyamirecommended15=Welcome to Judis! -channels15=3 - -#Properties for Plana 16 -flag16=0 -servermessage16=Welcome to Plana! -eventmessage16=Plana! -whyamirecommended16=Welcome to Plana! -channels16=3 - -#Properties for Kalluna 17 -flag17=0 -servermessage17=Welcome to Kalluna! -eventmessage17=Kalluna! -whyamirecommended17=Welcome to Kalluna! -channels17=3 - -#Properties for Stius 18 -flag18=0 -servermessage18=Welcome to Stius! -eventmessage18=Stius! -whyamirecommended18=Welcome to Stius! -channels18=3 - -#Properties for Croa 19 -flag19=0 -servermessage19=Welcome to Croa! -eventmessage19=Croa! -whyamirecommended19=Welcome to Croa! -channels19=3 - -#Properties for Medere 20 -flag20=0 -servermessage20=Welcome to Medere! -eventmessage20=Medere! -whyamirecommended20=Welcome to Medere! -channels20=3 diff --git a/wz/Character.wz/Longcoat/01050018.img.xml b/wz/Character.wz/Longcoat/01050018.img.xml index 3edf4f7ef0..41f9468146 100644 --- a/wz/Character.wz/Longcoat/01050018.img.xml +++ b/wz/Character.wz/Longcoat/01050018.img.xml @@ -20,7 +20,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/wz/Character.wz/Longcoat/01050100.img.xml b/wz/Character.wz/Longcoat/01050100.img.xml index 27bac3d905..e6aed8455d 100644 --- a/wz/Character.wz/Longcoat/01050100.img.xml +++ b/wz/Character.wz/Longcoat/01050100.img.xml @@ -16,7 +16,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/wz/Character.wz/Longcoat/01050127.img.xml b/wz/Character.wz/Longcoat/01050127.img.xml index 849fcfbe09..54650795ee 100644 --- a/wz/Character.wz/Longcoat/01050127.img.xml +++ b/wz/Character.wz/Longcoat/01050127.img.xml @@ -16,7 +16,6 @@ - @@ -116,6 +115,7 @@ + diff --git a/wz/Character.wz/Longcoat/01051017.img.xml b/wz/Character.wz/Longcoat/01051017.img.xml index d5cfcf06fb..9304c640ca 100644 --- a/wz/Character.wz/Longcoat/01051017.img.xml +++ b/wz/Character.wz/Longcoat/01051017.img.xml @@ -20,7 +20,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/wz/Character.wz/Longcoat/01051098.img.xml b/wz/Character.wz/Longcoat/01051098.img.xml index 7260350896..fa0c2cdfee 100644 --- a/wz/Character.wz/Longcoat/01051098.img.xml +++ b/wz/Character.wz/Longcoat/01051098.img.xml @@ -16,7 +16,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/wz/Character.wz/Longcoat/01051140.img.xml b/wz/Character.wz/Longcoat/01051140.img.xml index d3c52a7054..c5c97730e3 100644 --- a/wz/Character.wz/Longcoat/01051140.img.xml +++ b/wz/Character.wz/Longcoat/01051140.img.xml @@ -16,7 +16,6 @@ - @@ -116,6 +115,7 @@ + diff --git a/wz/Map.wz/Map/Map2/211042400.img.xml b/wz/Map.wz/Map/Map2/211042400.img.xml index 165b3cb66c..ccc3c4ef70 100644 --- a/wz/Map.wz/Map/Map2/211042400.img.xml +++ b/wz/Map.wz/Map/Map2/211042400.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Map.wz/Map/Map9/922240200.img.xml b/wz/Map.wz/Map/Map9/922240200.img.xml index 2a51c2f179..f12a74ee56 100644 --- a/wz/Map.wz/Map/Map9/922240200.img.xml +++ b/wz/Map.wz/Map/Map9/922240200.img.xml @@ -3,7 +3,7 @@ - + diff --git a/wz/Quest.wz/Act.img.xml b/wz/Quest.wz/Act.img.xml index e612835194..647b53de72 100644 --- a/wz/Quest.wz/Act.img.xml +++ b/wz/Quest.wz/Act.img.xml @@ -12775,8 +12775,16 @@ + + + + + + + + @@ -20077,6 +20085,7 @@ + diff --git a/wz/Quest.wz/Check.img.xml b/wz/Quest.wz/Check.img.xml index dda18b6107..25900a598e 100644 --- a/wz/Quest.wz/Check.img.xml +++ b/wz/Quest.wz/Check.img.xml @@ -17944,7 +17944,7 @@ - + @@ -20136,6 +20136,12 @@ + + + + + + @@ -22133,7 +22139,12 @@ - + + + + + + @@ -22309,6 +22320,12 @@ + + + + + + @@ -23461,7 +23478,7 @@ - + @@ -24184,12 +24201,17 @@ - + + + + + + @@ -24238,7 +24260,7 @@ - + @@ -39696,7 +39718,11 @@ - + + + + + @@ -40748,7 +40774,6 @@ - @@ -41038,7 +41063,11 @@ - + + + + + @@ -41728,7 +41757,11 @@ - + + + + + @@ -42046,7 +42079,11 @@ - + + + + + @@ -44000,7 +44037,12 @@ - + + + + + + @@ -48372,7 +48414,11 @@ - + + + + + @@ -48388,7 +48434,11 @@ - + + + + + @@ -48438,7 +48488,11 @@ - + + + + + @@ -50100,6 +50154,12 @@ + + + + + + @@ -50141,8 +50201,6 @@ - - @@ -56926,6 +56984,11 @@ + + + + + @@ -60029,7 +60092,12 @@ - + + + + + + diff --git a/wz/Quest.wz/QuestInfo.img.xml b/wz/Quest.wz/QuestInfo.img.xml index 296d0a4c72..faa9d55646 100644 --- a/wz/Quest.wz/QuestInfo.img.xml +++ b/wz/Quest.wz/QuestInfo.img.xml @@ -4232,7 +4232,7 @@ You can now begin the "Manji's Request" quest. - + @@ -4581,7 +4581,7 @@ You can now begin the "Manji's Request" quest. - + @@ -6467,7 +6467,7 @@ Once there, talk to #b#p1200003##k to board the ship. - + @@ -12719,7 +12719,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + @@ -13511,7 +13511,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + @@ -13520,7 +13520,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + @@ -15338,7 +15338,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + @@ -16163,26 +16163,26 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + - + - - - + + + - - + + @@ -16190,9 +16190,9 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + - + @@ -16200,40 +16200,40 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - - + + - - - + + + - - - + + + - + - - - + + + @@ -16241,7 +16241,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + - + diff --git a/wz/Quest.wz/Say.img.xml b/wz/Quest.wz/Say.img.xml index 5eb5b41717..b5303a4f62 100644 --- a/wz/Quest.wz/Say.img.xml +++ b/wz/Quest.wz/Say.img.xml @@ -8206,7 +8206,7 @@ - + @@ -8249,7 +8249,7 @@ - + @@ -11685,7 +11685,7 @@ - + @@ -11707,7 +11707,7 @@ - + @@ -11729,7 +11729,7 @@ - + @@ -11751,7 +11751,7 @@ - + @@ -11779,14 +11779,14 @@ - + - + - + @@ -15560,7 +15560,7 @@ - + @@ -15571,7 +15571,7 @@ - + @@ -18926,7 +18926,7 @@ - + @@ -22664,10 +22664,10 @@ - + - + @@ -22824,7 +22824,7 @@ - + @@ -30134,6 +30134,23 @@ + + + + + + + + + + + + + + + + + @@ -30745,7 +30762,7 @@ - + diff --git a/wz/Skill.wz/MobSkill.img.xml b/wz/Skill.wz/MobSkill.img.xml index 49011b15d9..fdd8ed5669 100644 --- a/wz/Skill.wz/MobSkill.img.xml +++ b/wz/Skill.wz/MobSkill.img.xml @@ -13724,12 +13724,12 @@ - - - - - - + + + + + + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 39f3c90f2c..e98d748844 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -18,7 +18,7 @@ - + @@ -30,23 +30,23 @@ - + - + - + - + - + @@ -54,11 +54,11 @@ - + - + @@ -90,7 +90,7 @@ - + @@ -118,19 +118,19 @@ - + - + - + - + @@ -138,7 +138,7 @@ - + @@ -150,7 +150,7 @@ - + @@ -190,27 +190,27 @@ - + - + - + - + - + - + @@ -222,7 +222,7 @@ - + @@ -242,19 +242,19 @@ - + - + - + - + @@ -290,35 +290,35 @@ - + - + - + - + - + - + - + - + @@ -346,11 +346,11 @@ - + - + @@ -362,7 +362,7 @@ - + @@ -378,7 +378,7 @@ - + @@ -394,7 +394,7 @@ - + @@ -406,11 +406,11 @@ - + - + @@ -418,23 +418,23 @@ - + - + - + - + - + @@ -446,15 +446,15 @@ - + - + - + @@ -486,19 +486,19 @@ - + - + - + - + @@ -538,7 +538,7 @@ - + @@ -546,15 +546,15 @@ - + - + - + @@ -614,19 +614,19 @@ - + - + - + - + @@ -634,11 +634,11 @@ - + - + @@ -650,7 +650,7 @@ - + @@ -666,23 +666,23 @@ - + - + - + - + - + @@ -722,19 +722,19 @@ - + - + - + - + @@ -754,19 +754,19 @@ - + - + - + - + @@ -782,7 +782,7 @@ - + @@ -794,7 +794,7 @@ - + @@ -802,15 +802,15 @@ - + - + - + @@ -838,7 +838,7 @@ - + @@ -866,31 +866,31 @@ - + - + - + - + - + - + - + @@ -902,7 +902,7 @@ - + @@ -938,7 +938,7 @@ - + @@ -962,7 +962,7 @@ - + @@ -970,11 +970,11 @@ - + - + @@ -1006,15 +1006,15 @@ - + - + - + @@ -1022,19 +1022,19 @@ - + - + - + - + @@ -1050,31 +1050,31 @@ - + - + - + - + - + - + - + @@ -1110,7 +1110,7 @@ - + @@ -1119,33 +1119,33 @@ - + - + - + - + - + - + - + @@ -1161,7 +1161,7 @@ - + @@ -1185,19 +1185,19 @@ - + - + - + - + @@ -1221,31 +1221,31 @@ - + - + - + - + - + - + - + @@ -1265,15 +1265,15 @@ - + - + - + @@ -1293,15 +1293,15 @@ - + - + - + @@ -1309,7 +1309,7 @@ - + @@ -1317,11 +1317,11 @@ - + - + @@ -1337,31 +1337,31 @@ - + - + - + - + - + - + - + @@ -1433,7 +1433,7 @@ - + @@ -1441,35 +1441,35 @@ - + - + - + - + - + - + - + - + @@ -1493,11 +1493,11 @@ - + - + @@ -1513,279 +1513,279 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1797,11 +1797,11 @@ - + - + @@ -1809,7 +1809,7 @@ - + @@ -1817,407 +1817,407 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2249,35 +2249,35 @@ - + - + - + - + - + - + - + - + @@ -2297,203 +2297,203 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2505,351 +2505,351 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2857,275 +2857,275 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3133,167 +3133,167 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3301,203 +3301,203 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3505,47 +3505,47 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -3553,11 +3553,11 @@ - + - + @@ -3573,51 +3573,51 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -3629,7 +3629,7 @@ - + @@ -3641,7 +3641,7 @@ - + @@ -3677,19 +3677,19 @@ - + - + - + - + @@ -3697,7 +3697,7 @@ - + @@ -3705,87 +3705,87 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3793,55 +3793,55 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3849,7 +3849,7 @@ - + @@ -3857,7 +3857,7 @@ - + @@ -3901,7 +3901,7 @@ - + @@ -3921,35 +3921,35 @@ - + - + - + - + - + - + - + - + @@ -3985,7 +3985,7 @@ - + @@ -4001,31 +4001,31 @@ - + - + - + - + - + - + - + @@ -4041,15 +4041,15 @@ - + - + - + @@ -4065,7 +4065,7 @@ - + @@ -4073,39 +4073,39 @@ - + - + - + - + - + - + - + - + - + @@ -4149,7 +4149,7 @@ - + @@ -4205,27 +4205,27 @@ - + - + - + - + - + - + @@ -4233,11 +4233,11 @@ - + - + @@ -4249,59 +4249,59 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4345,7 +4345,7 @@ - + @@ -4376,7 +4376,7 @@ - + @@ -4384,19 +4384,19 @@ - + - + - + - + @@ -4404,7 +4404,7 @@ - + @@ -4436,7 +4436,7 @@ - + @@ -4464,7 +4464,7 @@ - + @@ -4504,15 +4504,15 @@ - + - + - + @@ -4572,7 +4572,7 @@ - + @@ -4584,43 +4584,43 @@ - + - + - + - + - + - + - + - + - + - + @@ -4632,31 +4632,31 @@ - + - + - + - + - + - + - + @@ -4668,7 +4668,7 @@ - + @@ -4680,67 +4680,67 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4953,1027 +4953,1027 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5985,59 +5985,59 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6353,7 +6353,7 @@ - + @@ -6405,7 +6405,7 @@ - + @@ -6433,23 +6433,23 @@ - + - + - + - + - + @@ -6461,71 +6461,71 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6541,7 +6541,7 @@ - + @@ -6605,7 +6605,7 @@ - + @@ -6681,11 +6681,11 @@ - + - + @@ -6733,7 +6733,7 @@ - + @@ -6777,91 +6777,91 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -6869,31 +6869,31 @@ - + - + - + - + - + - + - + @@ -7045,15 +7045,15 @@ - + - + - + @@ -7093,7 +7093,7 @@ - + @@ -7113,255 +7113,255 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7425,7 +7425,7 @@ - + @@ -7521,23 +7521,23 @@ - + - + - + - + - + @@ -7553,11 +7553,11 @@ - + - + @@ -7565,91 +7565,91 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -7661,31 +7661,31 @@ - + - + - + - + - + - + - + @@ -7729,7 +7729,7 @@ - + @@ -7737,7 +7737,7 @@ - + @@ -7781,15 +7781,15 @@ - + - + - + @@ -7909,71 +7909,71 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -8041,59 +8041,59 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -8129,23 +8129,23 @@ - + - + - + - + - + @@ -8197,35 +8197,35 @@ - + - + - + - + - + - + - + - + @@ -8241,7 +8241,7 @@ - + @@ -8293,235 +8293,235 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -8529,11 +8529,11 @@ - + - + @@ -8565,7 +8565,7 @@ - + @@ -8573,503 +8573,503 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/wz/String.wz/Etc.img.xml b/wz/String.wz/Etc.img.xml index 2cc65ee97e..e5221feaf8 100644 --- a/wz/String.wz/Etc.img.xml +++ b/wz/String.wz/Etc.img.xml @@ -1630,7 +1630,7 @@ - + @@ -2178,7 +2178,7 @@ - + @@ -2366,7 +2366,7 @@ - + @@ -2430,15 +2430,15 @@ - + - + - + @@ -4930,19 +4930,19 @@ - + - + - + - + @@ -4950,11 +4950,11 @@ - + - + @@ -7156,7 +7156,7 @@ - +