Compare commits

..

64 Commits

Author SHA1 Message Date
Ponk
380e4284ee Merge pull request #276 from JalenJaceX2/command-list #patch
Create Commands.txt
2024-10-19 13:44:27 +02:00
JalenJaceX2
16ff5a6f54 Create Commands.txt 2024-10-14 02:02:50 -07:00
Ponk
5fabbaf7ab Merge pull request #261 from pimittens/vanillafixes #patch
chaos scroll fix
2024-09-13 21:25:51 +02:00
pimittens
215dc42294 Create RandomizerTest.java 2024-09-10 13:49:16 -07:00
Ponk
314916279a Merge pull request #271 from P0nk/fix/global-drop-concurrency #patch
Fix global drops thread issue
2024-08-28 21:30:39 +02:00
P0nk
dbf1a1bb36 Fix global drops thread issue
This randomly caused global drops to stop working after a while.
Originally reported by Crabo of MapleHorizons.
2024-08-28 21:22:47 +02:00
Ponk
8f2b8dd013 Merge pull request #248 from HarkuLi/fix/vard-plastic-surgery #patch
NPC (Vard): Fix "Plastic Surgery" option
2024-08-28 19:57:04 +02:00
Ponk
8039852aa3 Merge pull request #247 from NoirReverie/master #patch
Fix minimum HP and MP checks on AP reset
2024-08-28 18:30:15 +02:00
Ponk
d3b567953d Merge pull request #270 from P0nk/fix/exploded-meso-delay #patch
Fix staggered exploding mesos
2024-08-27 07:47:31 +02:00
P0nk
5064ee936a Fix staggered exploding mesos
Thanks teto: "the staggering should only go up to 400ms [attack.attackDelay + (index++ % 5) * 100], and the total delay should be capped at 1000ms according to the BMS implementation"
2024-08-27 07:41:24 +02:00
Ponk
86da9b0b29 Merge pull request #262 from P0nk/fix/drop-delay #patch
Proper delay for item drops
2024-08-20 20:38:27 +02:00
P0nk
acac203e42 Scheduler free timing for reactor drops 2024-08-20 20:32:33 +02:00
P0nk
cdd1c8cb61 Proper timing on removing exploded meso
No longer using scheduling on server side but rather
a delay value inherent to the "remove item from map" packet.
2024-08-20 20:13:01 +02:00
P0nk
439753eb6d Fix drop delay for Meso Explosion 2024-08-20 19:44:16 +02:00
P0nk
e30700de66 Fix drop delay from summon attack 2024-08-17 21:27:43 +02:00
P0nk
994d1723b6 Remove unnecessary "spawn loot on animation" feature
No longer needed since item drop timing is now dictated by the client
since two commits back.
2024-08-17 19:14:04 +02:00
P0nk
2d40a89c55 Overload damageMonster for when no delay is needed 2024-08-17 19:13:38 +02:00
P0nk
802cc2b5f5 Use delay from packets for drop timing 2024-08-17 19:13:36 +02:00
P0nk
2ffca90d29 Add attack delay to AttackInfo
Meant to be used in item drop packet for timing handled by the client
rather than scheduling item drop packets on the server.
2024-08-14 07:45:06 +02:00
pimittens
ec90df9c58 chaos scroll fix
The intention of the rand method is to return a pseudorandom uniformly distributed int between lbound and ubound (both inclusive). The previous implementation did not work as intended when the lower bound was negative since java round up instead of down when casting a negative double to an int. Adding the lower bound after the cast rather than before fixes this.

Also changed the CHSCROLL_STAT_RANGE from 6 to 5 to be consistent with gms.
2024-08-06 20:42:23 -07:00
Ponk
8f2c2dc08f Merge pull request #253 from P0nk/cleanup/rebirth #minor
Remove rebirth system
2024-07-19 17:24:18 +02:00
P0nk
cad10c4d5c Remove rebirth system 2024-07-19 17:16:14 +02:00
Ponk
aa3686ed0b Merge pull request #251 from P0nk/cleanup/banish #minor
Remove custom banish scroll feature
2024-07-17 19:47:15 +02:00
P0nk
3850b63cec Add migration for <v0.14.0 to fix shop dc 2024-07-17 19:24:19 +02:00
P0nk
a7df8a4f49 Remove banish scroll from WZ files 2024-07-17 18:50:44 +02:00
P0nk
7071b13e41 Remove banish scroll from shops 2024-07-17 18:39:50 +02:00
P0nk
2324ae7f9e Refactor BanishInfo - make it a record 2024-07-17 18:11:50 +02:00
P0nk
402163c33d Remove "banishable town scroll" feature 2024-07-17 17:45:38 +02:00
P0nk
3356e42e71 Remove "spikes avoid banish" feature 2024-07-16 20:17:21 +02:00
P0nk
205e263255 Specify required Java version 2024-07-16 20:16:26 +02:00
Ponk
aab9823a06 Merge pull request #227 from HarkuLi/feat/fix-ariant-coliseum #patch
Ariant Coliseum: Fix score counting and trade conversation
2024-06-19 17:58:38 +02:00
HarkuLi
0245e7d1af NPC (Vard): Fix "Plastic Surgery" option
Correct typos for variable `fface_v`.
2024-06-19 18:56:14 +08:00
HarkuLi
3f800c4a68 Ignore .DS_Store files 2024-06-19 18:43:26 +08:00
HarkuLi
31e901ab6d Ariant Coliseum: Fix score counting and trade conversation 2024-06-19 18:42:31 +08:00
Noir
94a08d86a0 Min HP / MP needs to check against post-AP-reset value 2024-06-18 21:42:52 -04:00
NoirReverie
bcc7bedbc9 Merge branch 'P0nk:master' into master 2024-06-17 18:53:21 -04:00
Noir
a878a4f3f9 Fix minimum HP and MP checks on AP reset 2024-06-17 18:48:13 -04:00
Ponk
238c01baf4 Merge pull request #245 from P0nk/fix/energy-charge-crash #patch
Fix Energy charge crashing certain other players
2024-06-16 19:13:03 +02:00
P0nk
5a4bdd343c Fix Energy charge crashing certain other players
Crabo in #bug-report (2024-06-10):
"(...) this will crash everyone in the map besides the bucc and 1 character, when a bucc (or TB) charges energy and a character with an ID that's a multiple of 102 is in the same map (and the energy reaches that number so if character ID is 204 it will reach that after 2 hits and DC the whole map besides the bucc and that char with id 204).
 Thanks to others for helping me fix it. Thought I'd report it!"
2024-06-16 19:09:21 +02:00
Ponk
d916502f58 Merge pull request #244 from P0nk/fix/cash-shop-surprise-package #patch
Fix able to get package from Cash shop surprise
2024-06-16 16:15:30 +02:00
P0nk
1791365e0f Fix able to get package from Cash shop surprise
Packages aren't real items and crash the client.
2024-06-16 16:06:29 +02:00
Ponk
de2a86c859 Merge pull request #243 from P0nk/fix/last-cash-shop-surprise #patch
Fix using the last Cash shop surprise not removing the item
2024-06-16 15:12:25 +02:00
P0nk
5aeed01e38 Fix not using the selected Cash shop surprise
If you have multiple, it would always use the first one. Now it uses whichever you select (as expected).
2024-06-16 15:08:48 +02:00
P0nk
6ab1af99da Add tests for CashShopSurpriseHandler 2024-06-16 13:56:54 +02:00
P0nk
c7b2d218ef Fix count not being updated after last Cash shop surprise 2024-06-16 12:59:30 +02:00
P0nk
01ae462b72 Refactor CashShop - add constants for cash types 2024-06-16 12:19:16 +02:00
Ponk
eb603e7ee9 Merge pull request #242 from P0nk/fix/cash-shop-surprise-count #patch
Fix cash shop surprise count not properly updated on usage
2024-06-15 08:35:58 +02:00
P0nk
b67b29def5 Fix cash shop surprise count not properly updated on usage
This reverts the hacky solution made in db82cbcfae
2024-06-15 08:31:24 +02:00
Ponk
d22d9b603b Merge pull request #228 from HarkuLi/feat/fix-npc-gain-meso #patch
NPC: Fix type casting error for `gainMeso()` method
2024-06-14 21:44:10 +02:00
Ponk
16b0a36c86 Merge pull request #238 from channarit1994/master #patch
Surprise Box Implementation
2024-06-14 21:33:08 +02:00
Ponk
313f48d4ce Merge pull request #237 from peamy/master #patch
Check if the amount of damage lines doesn't exceed the max (autoban)
2024-06-14 20:11:39 +02:00
Channarit Sittiparat
db82cbcfae Surprise Box Implementation
- Added showCashInventory after a successful gachapon opening in the CashShopSurpriseHandler.
- Added getItemsSize() condition to check the inventory size before proceeding with the cash shop surprise opening
2024-06-13 20:02:40 +07:00
remsus
eed47a9064 Check if the amount of damage lines doesn't exceed the max (autoban) 2024-06-11 18:44:47 +02:00
Ponk
9945d37df8 Merge pull request #233 from P0nk/fix/ide-settings #patch
Add IntelliJ code style settings (no wildcard imports)
2024-05-22 08:51:47 +02:00
P0nk
4e743128e9 Remove all wildcard imports 2024-05-22 08:33:44 +02:00
P0nk
2ed35db216 Add IntelliJ code style settings (no wildcard imports) 2024-05-22 08:06:46 +02:00
Ponk
df0c7f8b46 Merge pull request #232 from P0nk/cleanup/remove-buyback #minor
Remove custom sounds in Sound.wz, add devtest command
2024-05-21 22:54:59 +02:00
P0nk
9fe6ba4483 Add new wz files zip (reduced size by ~400mb)
Removed: Base.wz, Effect.wz, Morph.wz, Sound.wz, TamingMob.wz
2024-05-21 22:47:47 +02:00
P0nk
dee8651e61 Add DevtestCommand to easily test without restarting server 2024-05-21 21:36:54 +02:00
P0nk
948a9de667 Remove Spirit of Rock die sound 2024-05-21 20:23:34 +02:00
P0nk
d55437ddf3 Remove subway whistle 2024-05-21 19:56:09 +02:00
P0nk
98fd8c13d4 Remove brazil anthem 2024-05-21 19:54:30 +02:00
P0nk
04a92fe0c1 Remove buyback custom feature 2024-05-21 19:44:25 +02:00
HarkuLi
0d684c1400 NPC: Fix type casting error for gainMeso() method
Number type values might be passed into the `gainMeso()` method in js
scripts, and thus it expects a `gainMeso(Double gain)` method in the
`NPCConversationManager` class.
2024-03-02 15:38:22 +08:00
164 changed files with 3241 additions and 1703 deletions

10
.gitignore vendored
View File

@@ -1,5 +1,6 @@
/logs/**
.idea/
.idea/*
!.idea/codeStyles/
*.iml
/target
@@ -16,3 +17,10 @@
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Database
database/docker-db-data
database/docker-pg-db-data
# macOS files
.DS_Store

8
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
</JavaCodeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -71,7 +71,7 @@ You will start by installing the database server and client, and then run some s
You will start by cloning the repository, then configure the database properties and lastly start the server.
#### Prerequisites
* Java 21+ (I recommend [Amazon Corretto](https://aws.amazon.com/corretto))
* Java 21 (I recommend [Amazon Corretto](https://aws.amazon.com/corretto))
* IDE (I recommend [IntelliJ IDEA](https://www.jetbrains.com/idea/))
#### Steps
@@ -107,7 +107,7 @@ You will start by installing the game with the old installer, then overwrite som
1. Download _MapleGlobal-v83-setup.exe_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT). This is the official installer from back then.
2. Install it in a directory of your choice.
3. Delete the following files from the installation directory: _HShield_ (entire directory), _ASPLnchr.exe_, _MapleStory.exe_, and _Patcher.exe_.
4. Download _CosmicWZ-v1-2021.05.10.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT).
4. Download _CosmicWZ-2024-07-17-v0.14.0.zip_ from my [Google Drive](https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT).
5. Unzip it and copy all .wz-files into the installation directory. Replace the existing ones.
6. Download _HeavenMS-localhost-WINDOW.exe_ from [hostr.co](https://hostr.co/amuX5SLeeVZx). This is a client modified to connect to your localhost instead of Nexon's server (along with some fixes and custom changes).
- Your antivirus will likely detect the file as a trojan or similar and automatically delete it. To prevent this from happening, add your _Downloads_ directory and the installation directory as exclusions in your antivirus software. On W11, this is under "Virus & threat protection settings" -> "Add or remove exclusions".

View File

@@ -220,7 +220,6 @@ server:
USE_MTS: false
USE_CPQ: true #Renders the CPQ available or not.
USE_AUTOHIDE_GM: true #When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
USE_BUYBACK_SYSTEM: false #Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button.
USE_FIXED_RATIO_HPMP_UPDATE: false #Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio.
USE_FAMILY_SYSTEM: true
USE_DUEY: true
@@ -250,7 +249,6 @@ server:
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.
@@ -258,10 +256,8 @@ server:
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: false #Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available.
USE_ENABLE_FULL_RESPAWN: false #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: false #Flag to enable/disable map ownership system
USE_FISHING_SYSTEM: false #Flag to enable/disable custom fishing system
USE_NPCS_SCRIPTABLE: true #Flag to enable/disable serverside predefined script NPCs.
@@ -319,7 +315,6 @@ server:
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).
REBIRTH_NPC_ID: 9010021 #ID of the NPC that should be replaced with the rebirth mechanic, if enabled.
#Dangling Items/Locks Configuration
ITEM_EXPIRE_TIME: 180000 # (3 * 60 * 1000) Time before items start disappearing. Recommended to be set up to 3 minutes.
@@ -349,7 +344,7 @@ server:
USE_ENHANCED_CRAFTING: false #Apply chaos scroll on every equip crafted.
SCROLL_CHANCE_ROLLS: 1 #Number of rolls for success on a scroll, set 1 for default.
CHSCROLL_STAT_RATE: 1 #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.
CHSCROLL_STAT_RANGE: 5 #Stat upgrade range (-N, N) on chaos scrolls.
#Beginner Skills Configuration
USE_ULTRA_NIMBLE_FEET: false #Massive speed & jump upgrade.
@@ -396,7 +391,6 @@ server:
USE_EQUIPMNT_LVLUP_SLOTS: false #Equips can upgrade slots at level up.
USE_EQUIPMNT_LVLUP_POWER: false #Enable more powerful stat upgrades at equip level up.
USE_EQUIPMNT_LVLUP_CASH: false #Enable equip leveling up on cash equipments as well.
USE_SPIKES_AVOID_BANISH: false #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: 1 #All equips lvlup at max level of N, set 1 to disable.
@@ -447,14 +441,6 @@ server:
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).

View File

@@ -131,39 +131,6 @@ INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`
(9201101, 2012008, 4200000, 0, 164),
(9201101, 2022251, 3800000, 0, 168);
# adding antibanish scrolls
INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
(1001100, 2030100, 450, 0, 130),
(1011100, 2030100, 450, 0, 142),
(1021100, 2030100, 450, 0, 142),
(1031100, 2030100, 450, 0, 146),
(1051002, 2030100, 450, 0, 142),
(1052116, 2030100, 450, 0, 118),
(1061001, 2030100, 450, 0, 126),
(1061002, 2030100, 450, 0, 130),
(1091002, 2030100, 450, 0, 130),
(1100002, 2030100, 450, 0, 138),
(2012005, 2030100, 450, 0, 126),
(2022001, 2030100, 450, 0, 126),
(2030009, 2030100, 450, 0, 126),
(2040051, 2030100, 450, 0, 102),
(2041006, 2030100, 450, 0, 134),
(2051000, 2030100, 450, 0, 134),
(2060004, 2030100, 450, 0, 134),
(2070001, 2030100, 450, 0, 134),
(2080001, 2030100, 450, 0, 134),
(2090003, 2030100, 450, 0, 126),
(2093002, 2030100, 450, 0, 126),
(2100004, 2030100, 450, 0, 130),
(2110001, 2030100, 450, 0, 130),
(2130000, 2030100, 450, 0, 126),
(9201060, 2030100, 450, 0, 114),
(9270021, 2030100, 450, 0, 118),
(9270022, 2030100, 450, 0, 114), -- Thanks Rednor for finding duplicate item on NPC
(1338, 2030100, 450, 0, 114),
(9270057, 2030100, 450, 0, 4),
(9270065, 2030100, 450, 0, 3);
-- Thanks to Vcoc
-- GMShop: Sacks, GmEquip, Cheese & Onyx, Utils,
-- Arrows, Bullets, Throwings and Capsules,
@@ -301,7 +268,6 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU
(1200002, 2070000, 500, 0, 108),
(1200002, 2061000, 1, 0, 120),
(1200002, 2060000, 1, 0, 124),
(1200002, 2030100, 400, 0, 128),
(1200002, 2030000, 400, 0, 132),
(1200002, 2020028, 3000, 0, 136),
(1200002, 2010004, 310, 0, 140),
@@ -323,7 +289,6 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU
(1301000, 2070000, 500, 0, 108),
(1301000, 2061000, 1, 0, 112),
(1301000, 2060000, 1, 0, 116),
(1301000, 2030100, 400, 0, 120),
(1301000, 2030000, 400, 0, 124),
(1301000, 2022000, 1650, 0, 128),
(1301000, 2022003, 1100, 0, 132),
@@ -348,4 +313,4 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU
(9270019, 1482004, 75000, 0, 100),
(9270020, 1052113, 120000, 0, 92),
(9270020, 1052110, 100000, 0, 96),
(9270020, 1002625, 75000, 0, 100);
(9270020, 1002625, 75000, 0, 100);

View File

@@ -0,0 +1,13 @@
# Migrations
The files contained in this directory are intended to be run manually when transitioning from an earlier version of Cosmic to a more recent one.
Not every version comes with an associated migration script. Only those with breaking changes such as removal of custom assets that would otherwise crash the client.
This is a temporary solution until automatic database migrations are in place.
## How to
Each script is only intended to be run __once__.
When a new migration is available, simply run the SQL script in HeidiSQL (or other SQL client of choice).
If there are multiple new migrations that you haven't run, run them in order starting with the lowest version and ending with the highest version.

View File

@@ -0,0 +1,2 @@
DELETE FROM shopitems
WHERE itemid = 2030100; # Return Scroll - Banished Area (a custom item added in HeavenMS)

View File

@@ -302,10 +302,10 @@ Localhost:
* Removed caps for MATK, WDEF, MDEF, ACC and AVOID.
* Removed "AP excess" popup and "Admin/MWLB" action block, original credits to kevintjuh93.
* Removed "You've gained a level!" popup, original credits to PrinceReborn.
* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS), rendering the buyback option now available for all maps.
* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS).
* Removed a check for players wishing to create/join a party being novices under level 10.
* Set a new high cap for SPEED.
* Removed the AP assign block for novices.
* Removed a block that would show up when trying to apply an attack gem on equipments that aren't weapons.
---------------------------
---------------------------

200
handbook/Commands.txt Normal file
View File

@@ -0,0 +1,200 @@
Common Commands:
@help - Show available commands.
@commands - Show available commands.
@droplimit - Check drop limit of current map.
@time - Show current server time.
@credits - Show credits. These people made the server possible.
@uptime - Show server online time.
@gacha - Show gachapon rewards.
@dispose - Dispose to fix NPC chat.
@changel - Change language settings
@equiplv - Show levels of all equipped items.
@showrates - Show all world/character rates.
@rates - Show your rates.
@online - Show all online players.
@gm- Send a message to the game masters.
@reportbug - Send in a bug report.
@points - Show point total.
@joinevent - Join active event.
@leaveevent-Leave active event.
@ranks - Show player rankings.
@str - Assign AP into STR.
@dex - Assign AP into DEX.
@int - Assign AP into INT.
@luk - Assign AP into LUK.
@enableauth - Enable PIC code by resetting the cooldown.
@toggleexp - Toggle enable/disable all exp gain.
@mylawn - Claim ownership of the current map.
@bosshp - Show HP of bosses on current map.
@mobhp - Show HP of mobs on current map.
Donator Commands:
@whatdropsfrom - Show what items drop from a mob.
@whodrops - Show what drops an item.
@buffme - Activate GM buffs on self.
@goto - Warp to a predefined map.
JrGM Commands:
!recharge - Recharge and refill all USE items.
!whereami - Show info about objects on current map.
!hide - Hide from players.
!unhide - Toggle Hide.
!sp - Set available SP.
!ap - Set available AP.
!empowerme - Activate all useful buffs.
!buffmap - Give GM buffs to the whole map.
!buff - Activate a buff.
!bomb - Bomb a player, dealing damage.
!dc - Disconnect a player.
!cleardrops - Clear drops by player.
!clearslot - Clear all items in an inventory tab.
!clearsavelocs - Clear saved locations for a player.
!warp - Warp to a map.
!warphere - Move a player to your location.
!summon - Move a player to your location.
!warpto - Warp to a player.
!reach - Warp to a player.
!follow - Warp to a player.
!gmshop - Open the GM shop.
!heal - Fully heal your HP/MP.
!item - Spawn an item into your inventory.
!drop - Spawn an item onto the ground.
!level - Set your level.
!levelpro - Set your level, one by one.
!setslot - Set amount of inventory slots in all tabs.
!setstat - Set all primary stats to new value.
!maxstat - Max out all character stats.
!maxskill - Max out all job skills.
!resetskill Set all skill levels to 0.
!search - Search String.wz.
!jail - Move a player to the jail.
!unjail- Free a player from jail.
!job - Change job of a player.
!unbug - Unbug self.
!id - Search in handbook.
!gachalist - Show gachapon rewards.
!loot - Loots all items that belong to you.
!mobskill - Apply a mob skill to all mobs on the map.
Args: <mob skill id> <skill level>
GM Commands:
!debuff - Put a debuff on all nearby players.
!fly - Enable/disable fly feature.
!spawn - Spawn mob(s) on your location.
!mutemap - Toggle mute players in the map.
!checkdmg - Show stats and damage of a player.
!inmap - Show all players in the map.
!reloadevents - Reload all event data.
!reloaddrops - Reload all drop data.
!reloadportals - Reload all portal scripts.
!reloadmap - Reload the map.
!reloadshops - Reload popup shops and NPC shops.
!hpmp - Set HP/MP of a player.
!maxhpmp - Set base HP/MP of a player.
!music - Play a song.
!monitor - Toggle monitored packet logging of a character.
!monitors - Show all characters being monitored for packet logging.
!ignore - Toggle ignore a character from auto-ban alerts.
!ignored - Show all characters being ignored in auto-ban alerts.
!pos - Show current position and foothold.
!togglecoupon - Toggle enable/disable a coupon.
!togglewhitechat - Toggle white GM chat.
!fame - Set new fame value of a player.
!givenx - Give NX to a player.
!givevp - Give vote points to a player.
!givems - Give mesos to a player.
!giverp - Give reward points to a player.
!expeds - Show all ongoing boss expeditions.
!kill - Kill a player.
!seed - Drop all seeds inside Henesys PQ.
!maxenergy - Set dojo energy to max value.
!killall - Kill all mobs in the map.
!notice - Send a blue message to everyone on the server.
!rip - Send a RIP notice.
!openportal - Open a portal on the map.
!closeportal - Close a portal.
!pe - Handle synthesized packets from file, and handle them as if sent from a client.
!startevent - Start an event on current map.
!endevent - Close entry for ongoing event.
!startmapevent - Start a "classic" event on current map.
!stopmapevent - Stop ongoing "classic" event.
!online2 - Show all online players.
!ban - Ban a player.
!unban - Unban a player.
!healmap - Heal all HP/MP of all players in the map.
!healperson - Heal all HP/MP of a player.
!hurt - Nearly kill a player.
!killmap - Kill all players in the map.
!night - Set sky background to black.
!npc - Spawn an NPC on your location.
!face - Change face of a player.
!hair - Change hair of a player.
!startquest - Start a quest.
!completequest - Complete an active quest.
!resetquest - Reset a completed quest.
!timer - Set timer on a player in current map.
!timermap - Set timer on all players in current map.
!timerall Set a server wide timer.
!warpmap - Warp all characters on current map to a new map.
!warparea - Warp all nearby players to a new map.
SuperGM Commands:
!servermessage - Set scrolling server message.
!proitem - Spawn an item with custom stats.
!seteqstat-Set stats of all equips in inventory.
!exprate - Set world exp rate.
!mesorate - Set world meso rate.
!droprate - Set world drop rate.
!bossdroprate - Set world boss drop rate.
!questrate - Set world quest rate.
!travelrate - Set world travel rate.
!fishrate - Set fishing rate.
!itemvac - Loot all drops on the map.
!forcevac-Loot all drops on the map.
!zakum - Spawn Zakum on your location.
!horntail - Spawn Horntail on your location.
!pinkbean - Spawn Pink Bean on your location.
!pap - Spawn Papulatus on your location.
!pianus - Spawn Pianus (R) on your location.
!cake - Spawn Cake boss with specified HP.
!playernpc - Spawn a player NPC of an online player.
!playernpcremove - Remove a "Iv 200" player NPC.
!pnpc - Spawn a permanent NPC on your location.
!pnpcremove - Remove a permanent NPC on the map.
!pmob - Spawn a permanent mob on your location.
!pmobremove - Remove all permanent mobs of the same type on the map.
Developer Commands:
!debug - Show a debug message.
!set - Store value in an array, for testing.
!showpackets - Toggle show received packets in console.
!showmovelife - Toggle show move life in console.
!showsessions - Show online sessions.
!iplist - Show IP of all players.
Admin Commands:
!setgmlevel - Set GM level of a player.
!warpworld - Warp to a different world.
!saveall - Save all characters.
!dcall - Disconnect all players (online or logged in).
!mapplayers - Show all players on the map.
!getacc - Show account name of an online player.
!shutdown - Shut down the server.
!clearquestcache - Clear all quest cache.
!clearquest - Clear cache of a quest.
!supplyratecoupon - Set availability of coupons in Cash Shop.
!spawnallpnpcs - Spawn player NPCs of all existing players.
!eraseallpnpcs - Remove all player NPCs.
!addchannel - Add a new channel to a world.
!addworld - Add a new world.
!removechannel - Remove channel from a world.
!removeworld - Remove a world.
!devtest - Runs devtest.js. Developer utility - test stuff without restarting the server.

View File

@@ -385,7 +385,6 @@
2030016 - Phyllia's Warp Powder - Warp powder made by fairy Phyllia. Teleports you to Magatia when used inside the Nihal desert region.
2030019 - Return Scroll to Nautilus - This scroll enables you to return to the Pirate village, Nautilus. This is a one use item and will disappear after use.
2030020 - Return Scroll to New Leaf City - Use this scroll to venture back to New Leaf City whenever you want!
2030100 - Return Scroll - Banished Area - Returns you to the map where you were last banished. Requires immediate use and have changed neither maps nor channels.
2031000 - Masked Man's Invitation - An invitation from the Masked Man to the Halloween Party at the Haunted Mansion. Double-click to move straight to the mansion.
2031001 - Studio Invitation - An invitation to the studio for the event "For Guild Only".
2040000 - Scroll for Helmet for DEF - Improves the helmet's weapon def.\nSuccess rate:100%, weapon def. +1

12
pom.xml
View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cosmic-maplestory</groupId>
@@ -52,7 +53,8 @@
<!-- Maven plugins -->
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version> <!-- For running unit tests -->
<maven-jar-plugin.version>3.4.1</maven-jar-plugin.version> <!-- Disabled. (for building thin jar) -->
<maven-assembly-plugin.version>3.7.1</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
<maven-assembly-plugin.version>3.7.1
</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
<!-- Dependencies -->
<slf4j-api.version>2.0.13</slf4j-api.version> <!-- Logging facade -->
@@ -180,6 +182,12 @@
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

3
scripts/devtest.js Normal file
View File

@@ -0,0 +1,3 @@
function run(chr) {
chr.message("devtest.js")
}

View File

@@ -36,12 +36,6 @@ function stopEntry() {
}
function takeoff() {
const PacketCreator = Java.type('tools.PacketCreator');
//sound src: https://www.soundjay.com/transportation/metro-door-close-01.mp3
KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
em.setProperty("docked", "false");
KC_Waiting.warpEveryone(Subway_to_NLC.getId());
NLC_Waiting.warpEveryone(Subway_to_KC.getId());
@@ -52,10 +46,6 @@ function arrived() {
Subway_to_KC.warpEveryone(KC_docked.getId(), 0);
Subway_to_NLC.warpEveryone(NLC_docked.getId(), 0);
scheduleNew();
const PacketCreator = Java.type('tools.PacketCreator');
KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
}
function cancelSchedule() {}

View File

@@ -58,8 +58,8 @@ function action(mode, type, selection) {
}
}
if (cm.getChar().getGender() == 1) {
for (var i = 0; i < fface.length; i++) {
pushIfItemExists(facenew, fface[i] + cm.getChar().getFace()
for (var i = 0; i < fface_v.length; i++) {
pushIfItemExists(facenew, fface_v[i] + cm.getChar().getFace()
% 1000 - (cm.getChar().getFace()
% 100));
}

View File

@@ -32,14 +32,14 @@ function action(mode, type, selection) {
} else if (status == 1) {
if (selection == 0) {
apqpoints = cm.getPlayer().getAriantPoints();
if (apqpoints < 100) {
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points.");
cm.dispose();
if (apqpoints >= 100) {
cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!");
} else if (apqpoints + arena.getAriantRewardTier(cm.getPlayer()) >= 100) {
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016#to get them and then re-chat with me!");
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016# to get them and then re-chat with me!");
cm.dispose();
} else {
cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!");
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points.");
cm.dispose();
}
} else if (selection == 1) {
cm.sendOk("The main objective of the Battle Arena is to allow the player to accumulate points so that they can be traded honorably for the highest prize: the #bPalm Tree Beach Chair#k. Collect points during the battles and talk to me when it's time to get the prize. In each battle, the player is given the opportunity to score points based on the amount of jewelry that the player has at the end. But be careful! If your points distance from other players #ris too high#k, this will have been all for nothing and you will earn mere #r1 point#k only.");

View File

@@ -24,7 +24,6 @@
var status;
var anthemSong = "Field/anthem/brazil"; // sound src: https://c7.rbxcdn.com/f91060652a6e9fbfbf92cb1418435448
var ambientSong = "Bgm04/Shinin'Harbor";
var feature_tree = [];
@@ -217,7 +216,6 @@ function writeFeatureTab_Serverpotentials() {
addFeature("Poison damage value visible for other players.");
addFeature("M. book announcer displays info based on demand.");
addFeature("Custom jail system.");
addFeature("Custom buyback system, uses mesos / NX, via MTS.");
addFeature("Custom fishing system having 'seasonal' catch times.");
addFeature("Actual fishing handling w/ F. Net - thanks Dragohe4rt!");
addFeature("Custom map leasing system.");
@@ -260,7 +258,7 @@ function writeFeatureTab_CustomNPCs() {
function writeFeatureTab_Localhostedits() {
addFeature("Removed the 'n' NPC dialog issue.");
addFeature("Removed caps for MATK, WMDEF, ACC and AVOID.");
addFeature("Removed MTS block, buyback available anywhere.");
addFeature("Removed MTS block.");
addFeature("Removed party blocks for novices under level 10.");
addFeature("Set a much more higher cap for SPEED.");
addFeature("Removed AP usage block for novices.");
@@ -308,8 +306,6 @@ function writeAllFeatures() {
}
function start() {
const PacketCreator = Java.type('tools.PacketCreator');
cm.getPlayer().sendPacket(PacketCreator.musicChange(anthemSong));
status = -1;
writeAllFeatures();
action(1, 0, 0);
@@ -369,4 +365,4 @@ function generateSelectionMenu(array) {
menu += "#L" + i + "#" + array[i] + "#l\r\n";
}
return menu;
}
}

View File

@@ -1,79 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
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 <http://www.gnu.org/licenses/>.
*/
/* Rebirth NPC
@author Ronan
@author wejrox
*/
var status;
var jobId = 0;
function start() {
status = -1;
const YamlConfig = Java.type('config.YamlConfig');
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
cm.sendOk("Rebirths aren't enabled on this server, how did you get here?");
cm.dispose();
return;
}
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode === 1) {
status++;
} else {
cm.dispose();
return;
}
if (status === 0) {
cm.sendNext("Come to me when you want to be reborn again. You currently have a total of #r" + cm.getChar().getReborns() + " #krebirths.");
} else if (status === 1) {
cm.sendSimple("What do you want me to do today: \r\n \r\n #L0##bI want to be reborn!#l \r\n #L1##bNothing for now...#k#l");
} else if (status === 2) {
if (selection === 0) {
if (cm.getChar().getLevel() === cm.getChar().getMaxClassLevel()) {
cm.sendSimple("I see... and which path would you like to take? \r\n\r\n #L0##bExplorer (Beginner)#l \r\n #L1##bCygnus Knight (Noblesse)#l \r\n #L2##bAran (Legend)#l");
} else {
cm.sendOk("It looks like your journey has not yet ended... come back when you're level " + cm.getChar().getMaxClassLevel());
cm.dispose();
}
} else if (selection === 1) {
cm.sendOk("See you soon!")
cm.dispose();
}
} else if (status === 3) {
// 0 => beginner, 1000 => noblesse, 2000 => legend
// makes this very easy :-)
jobId = selection * 1000;
var job = "";
if (selection === 0) job = "Beginner";
else if (selection === 1) job = "Noblesse";
else if (selection === 2) job = "Legend";
cm.sendYesNo("Are you sure you want to be reborn as a " + job + "?");
}
else if (status === 4 && type === 1) {
cm.getChar().executeRebornAsId(jobId);
cm.sendOk("You have now been reborn. That's a total of #r" + cm.getChar().getReborns() + "#k rebirths");
cm.dispose();
}
}

View File

@@ -50,8 +50,14 @@ function end(mode, type, selection) {
status++;
}
// TODO: there are 10 different riffs; quest2288/0 through quest2288/9.
// One of the riffs should play randomly upon the death of Spirit of Rock, but there is currently no system in place to achieve that in a reasonable way.
// Spirit of Rock (4300013) spawns an invisible mob on death (Spirit of Rock's Soul, 4300017) which was likely used in some clever way in GMS.
// The map (103040430) has two scripts which could be useful: onFirstUserEnter=Depart_Boss_F_Enter and onUserEnter=Depart_BossEnter
// Currently, the best hypothesis is that one of the map scripts registers some form of "mob spawn" action/script that runs once the invisible mob spawns.
// The script would randomly pick one of the 10 riffs and then register it with all chrs on the map (to later be used by this quest 2293) and play it.
if (status == 0) {
qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choide.\r\n\
qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choice.\r\n\
\t#b#L1# Listen to song No. 1#l \r\n\
\t#L2# Listen to Song No. 2#l \r\n\
\t#L3# Listen to Song No. 3#l \r\n\
@@ -67,7 +73,7 @@ function end(mode, type, selection) {
qm.sendOk("Was it this?");
status = -1;
} else if (selection == 3) {
qm.playSound("quest2293/Die");
qm.playSound("quest2288/6");
qm.sendOk("You heard that?");
status = -1;
} else if (selection == 4) {
@@ -88,4 +94,4 @@ function end(mode, type, selection) {
} else if (status == 3) {
qm.dispose();
}
}
}

View File

@@ -30,7 +30,12 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
public class BuddyList {
public enum BuddyOperation {

View File

@@ -24,8 +24,17 @@ package client;
import client.autoban.AutobanManager;
import client.creator.CharacterFactoryRecipe;
import client.inventory.*;
import client.inventory.Equip;
import client.inventory.Equip.StatUpgrade;
import client.inventory.Inventory;
import client.inventory.InventoryProof;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.ModifyInventory;
import client.inventory.Pet;
import client.inventory.PetDataFactory;
import client.inventory.WeaponType;
import client.inventory.manipulator.CashIdGenerator;
import client.inventory.manipulator.InventoryManipulator;
import client.keybind.KeyBinding;
@@ -40,7 +49,35 @@ import constants.id.ItemId;
import constants.id.MapId;
import constants.id.MobId;
import constants.inventory.ItemConstants;
import constants.skills.*;
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;
import constants.skills.DarkKnight;
import constants.skills.DawnWarrior;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.Hermit;
import constants.skills.Hero;
import constants.skills.ILArchMage;
import constants.skills.Legend;
import constants.skills.Magician;
import constants.skills.Marauder;
import constants.skills.Marksman;
import constants.skills.NightLord;
import constants.skills.Noblesse;
import constants.skills.Paladin;
import constants.skills.Priest;
import constants.skills.Ranger;
import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.ThunderBreaker;
import constants.skills.Warrior;
import net.packet.Packet;
import net.server.PlayerBuffValueHolder;
import net.server.PlayerCoolDownValueHolder;
@@ -52,39 +89,98 @@ import net.server.guild.GuildCharacter;
import net.server.guild.GuildPackets;
import net.server.services.task.world.CharacterSaveService;
import net.server.services.type.WorldServices;
import net.server.world.*;
import net.server.world.Messenger;
import net.server.world.MessengerCharacter;
import net.server.world.Party;
import net.server.world.PartyCharacter;
import net.server.world.PartyOperation;
import net.server.world.World;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scripting.AbstractPlayerInteraction;
import scripting.event.EventInstanceManager;
import scripting.item.ItemScriptManager;
import server.*;
import server.CashShop;
import server.ExpLogger;
import server.ExpLogger.ExpLogRecord;
import server.ItemInformationProvider;
import server.ItemInformationProvider.ScriptedItem;
import server.Marriage;
import server.Shop;
import server.StatEffect;
import server.Storage;
import server.ThreadManager;
import server.TimerManager;
import server.Trade;
import server.events.Events;
import server.events.RescueGaga;
import server.events.gm.Fitness;
import server.events.gm.Ola;
import server.life.*;
import server.maps.*;
import server.life.BanishInfo;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillId;
import server.life.MobSkillType;
import server.life.Monster;
import server.life.PlayerNPC;
import server.maps.AbstractAnimatedMapObject;
import server.maps.Door;
import server.maps.DoorObject;
import server.maps.Dragon;
import server.maps.FieldLimit;
import server.maps.HiredMerchant;
import server.maps.MapEffect;
import server.maps.MapItem;
import server.maps.MapManager;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MapleMap;
import server.maps.MiniGame;
import server.maps.MiniGame.MiniGameResult;
import server.maps.PlayerShop;
import server.maps.PlayerShopItem;
import server.maps.Portal;
import server.maps.SavedLocation;
import server.maps.SavedLocationType;
import server.maps.Summon;
import server.minigame.RockPaperScissor;
import server.partyquest.AriantColiseum;
import server.partyquest.MonsterCarnival;
import server.partyquest.MonsterCarnivalParty;
import server.partyquest.PartyQuest;
import server.quest.Quest;
import tools.*;
import tools.exceptions.NotEnabledException;
import tools.DatabaseConnection;
import tools.LongTool;
import tools.PacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.packets.WeddingPackets;
import java.awt.*;
import java.lang.ref.WeakReference;
import java.sql.*;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -94,7 +190,9 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Character extends AbstractCharacterObject {
private static final Logger log = LoggerFactory.getLogger(Character.class);
@@ -128,7 +226,7 @@ public class Character extends AbstractCharacterObject {
private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1;
private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses;
private int owlSearch;
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, jailExpiration = -1;
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastDeathtime, jailExpiration = -1;
private transient int localstr, localdex, localluk, localint_, localmagic, localwatk;
private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp;
private int localchairrate;
@@ -254,9 +352,6 @@ public class Character extends AbstractCharacterObject {
private int targetHpBarHash = 0;
private long targetHpBarTime = 0;
private long nextWarningTime = 0;
private int banishMap = -1;
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;
@@ -1258,48 +1353,14 @@ public class Character extends AbstractCharacterObject {
}
}
public boolean canRecoverLastBanish() {
return System.currentTimeMillis() - this.banishTime < MINUTES.toMillis(5);
}
public Pair<Integer, Integer> getLastBanishData() {
return new Pair<>(this.banishMap, this.banishSp);
}
public void clearBanishPlayerData() {
this.banishMap = -1;
this.banishSp = -1;
this.banishTime = 0;
}
public void setBanishPlayerData(int banishMap, int banishSp, long banishTime) {
this.banishMap = banishMap;
this.banishSp = banishSp;
this.banishTime = banishTime;
}
public void changeMapBanish(int mapid, String portal, String msg) {
if (YamlConfig.config.server.USE_SPIKES_AVOID_BANISH) {
for (Item it : this.getInventory(InventoryType.EQUIPPED).list()) {
if ((it.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) {
return;
}
}
}
int banMap = this.getMapId();
int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId();
long banTime = System.currentTimeMillis();
if (msg != null) {
dropMessage(5, msg);
public void changeMapBanish(BanishInfo banishInfo) {
if (banishInfo.msg() != null) {
dropMessage(5, banishInfo.msg());
}
MapleMap map_ = getWarpMap(mapid);
Portal portal_ = map_.getPortal(portal);
Portal portal_ = map_.getPortal(banishInfo.portal());
changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint());
setBanishPlayerData(banMap, banSp, banTime);
}
public void changeMap(int map) {
@@ -1682,7 +1743,6 @@ public class Character extends AbstractCharacterObject {
this.mapTransitioning.set(true);
this.unregisterChairBuff();
this.clearBanishPlayerData();
Trade.cancelTrade(this, Trade.TradeResult.UNSUCCESSFUL_ANOTHER_MAP);
this.closePlayerInteractions();
@@ -1970,7 +2030,7 @@ public class Character extends AbstractCharacterObject {
this.getCashShop().gainCash(1, nxGain);
if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) {
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300);
}
this.getMap().pickItemDrop(pickupPacket, mapitem);
@@ -2022,7 +2082,7 @@ public class Character extends AbstractCharacterObject {
this.getCashShop().gainCash(1, nxGain);
if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) {
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300);
}
} else if (applyConsumeOnPickup(mItem.getItemId())) {
} else if (InventoryManipulator.addFromDrop(client, mItem, true)) {
@@ -5965,7 +6025,8 @@ public class Character extends AbstractCharacterObject {
sendPacket(PacketCreator.giveBuff(energybar, 0, stat));
sendPacket(PacketCreator.showOwnBuffEffect(energycharge.getId(), 2));
getMap().broadcastPacket(this, PacketCreator.showBuffEffect(id, energycharge.getId(), 2));
getMap().broadcastPacket(this, PacketCreator.giveForeignBuff(energybar, stat));
getMap().broadcastPacket(this, PacketCreator.giveForeignPirateBuff(id, energycharge.getId(),
ceffect.getDuration(), stat));
}
if (energybar >= 10000 && energybar < 11000) {
energybar = 15000;
@@ -6054,65 +6115,6 @@ public class Character extends AbstractCharacterObject {
}
}
private boolean canBuyback(int fee, boolean usingMesos) {
return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee;
}
private void applyBuybackFee(int fee, boolean usingMesos) {
if (usingMesos) {
this.gainMeso(-fee);
} else {
cashshop.gainCash(1, -fee);
}
}
private long getNextBuybackTime() {
return lastBuyback + MINUTES.toMillis(YamlConfig.config.server.BUYBACK_COOLDOWN_MINUTES);
}
private boolean isBuybackInvincible() {
return Server.getInstance().getCurrentTime() - lastBuyback < 4200;
}
private int getBuybackFee() {
float fee = YamlConfig.config.server.BUYBACK_FEE;
int grade = Math.min(Math.max(level, 30), 120) - 30;
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() + " " + (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 = MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow));
if (timeRemaining < 1) {
s += "Buyback #e#rUNAVAILABLE#k#n";
avail = false;
} else {
s += "Buyback countdown: #e#b" + getTimeRemaining(MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - timeLapsed) + "#k#n";
}
s += "\r\n";
}
if (timeNow < getNextBuybackTime() && avail) {
s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k";
s += "\r\n";
} else {
s += "Buyback #bavailable#k";
}
this.showHint(s);
}
private static String getTimeRemaining(long timeLeft) {
int seconds = (int) Math.floor(timeLeft / SECONDS.toMillis(1)) % 60;
int minutes = (int) Math.floor(timeLeft / MINUTES.toMillis(1)) % 60;
@@ -6120,34 +6122,6 @@ public class Character extends AbstractCharacterObject {
return (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds";
}
public boolean couldBuyback() { // Ronan's buyback system
long timeNow = Server.getInstance().getCurrentTime();
if (timeNow - lastDeathtime > MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES)) {
this.dropMessage(5, "The period of time to decide has expired, therefore you are unable to buyback.");
return false;
}
long nextBuybacktime = getNextBuybackTime();
if (timeNow < nextBuybacktime) {
long timeLeft = nextBuybacktime - timeNow;
this.dropMessage(5, "Next buyback available in " + getTimeRemaining(timeLeft) + ".");
return false;
}
boolean usingMesos = YamlConfig.config.server.USE_BUYBACK_WITH_MESOS;
int fee = getBuybackFee();
if (!canBuyback(fee, usingMesos)) {
this.dropMessage(5, "You don't have " + fee + " " + (usingMesos ? "mesos" : "NX") + " to buyback.");
return false;
}
lastBuyback = timeNow;
applyBuybackFee(fee, usingMesos);
return true;
}
public boolean isBuffFrom(BuffStat stat, Skill skill) {
effLock.lock();
chrLock.lock();
@@ -8911,11 +8885,7 @@ public class Character extends AbstractCharacterObject {
boolean playerDied = false;
if (hp <= 0) {
if (oldHp > hp) {
if (!isBuybackInvincible()) {
playerDied = true;
} else {
hp = 1;
}
playerDied = true;
}
}
@@ -10991,70 +10961,6 @@ public class Character extends AbstractCharacterObject {
}
}
public void setReborns(int value) {
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
yellowMessage("Rebirth system is not enabled!");
throw new NotEnabledException();
}
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE characters SET reborns=? WHERE id=?;")) {
ps.setInt(1, value);
ps.setInt(2, id);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void addReborns() {
setReborns(getReborns() + 1);
}
public int getReborns() {
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
yellowMessage("Rebirth system is not enabled!");
throw new NotEnabledException();
}
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT reborns FROM characters WHERE id=?;")) {
ps.setInt(1, id);
try (ResultSet rs = ps.executeQuery()) {
rs.next();
return rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
}
throw new RuntimeException();
}
public void executeReborn() {
// default to beginner: job id = 0
// this prevents a breaking change
executeRebornAs(Job.BEGINNER);
}
public void executeRebornAsId(int jobId) {
executeRebornAs(Job.getById(jobId));
}
public void executeRebornAs(Job job) {
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
yellowMessage("Rebirth system is not enabled!");
throw new NotEnabledException();
}
if (getLevel() != getMaxClassLevel()) {
return;
}
addReborns();
changeJob(job);
setLevel(0);
levelUp(true);
}
//EVENTS
private byte team = 0;
private Fitness fitness;

View File

@@ -44,7 +44,11 @@ import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult;
import net.server.guild.Guild;
import net.server.guild.GuildCharacter;
import net.server.guild.GuildPackets;
import net.server.world.*;
import net.server.world.MessengerCharacter;
import net.server.world.Party;
import net.server.world.PartyCharacter;
import net.server.world.PartyOperation;
import net.server.world.World;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scripting.AbstractPlayerInteraction;
@@ -73,9 +77,23 @@ import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -1500,7 +1518,6 @@ public class Client extends ChannelInboundHandlerAdapter {
player.getInventory(InventoryType.EQUIPPED).checked(false); //test
player.getMap().removePlayer(player);
player.clearBanishPlayerData();
player.getClient().getChannelServer().removePlayer(player);
player.saveCharToDB();
@@ -1581,4 +1598,4 @@ public class Client extends ChannelInboundHandlerAdapter {
public void setLanguage(int lingua) {
this.lang = lingua;
}
}
}

View File

@@ -28,8 +28,12 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

View File

@@ -24,7 +24,11 @@ package client;
import server.quest.Quest;
import tools.StringUtil;
import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author Matze

View File

@@ -21,8 +21,63 @@
*/
package client;
import constants.skills.*;
import provider.*;
import constants.skills.Aran;
import constants.skills.Archer;
import constants.skills.Assassin;
import constants.skills.Bandit;
import constants.skills.Beginner;
import constants.skills.Bishop;
import constants.skills.BlazeWizard;
import constants.skills.Bowmaster;
import constants.skills.Buccaneer;
import constants.skills.ChiefBandit;
import constants.skills.Cleric;
import constants.skills.Corsair;
import constants.skills.Crossbowman;
import constants.skills.Crusader;
import constants.skills.DarkKnight;
import constants.skills.DawnWarrior;
import constants.skills.DragonKnight;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.FPMage;
import constants.skills.FPWizard;
import constants.skills.Fighter;
import constants.skills.GM;
import constants.skills.Gunslinger;
import constants.skills.Hermit;
import constants.skills.Hero;
import constants.skills.Hunter;
import constants.skills.ILArchMage;
import constants.skills.ILMage;
import constants.skills.ILWizard;
import constants.skills.Legend;
import constants.skills.Magician;
import constants.skills.Marauder;
import constants.skills.Marksman;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Noblesse;
import constants.skills.Page;
import constants.skills.Paladin;
import constants.skills.Pirate;
import constants.skills.Priest;
import constants.skills.Ranger;
import constants.skills.Rogue;
import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Spearman;
import constants.skills.SuperGM;
import constants.skills.ThunderBreaker;
import constants.skills.Warrior;
import constants.skills.WhiteKnight;
import constants.skills.WindArcher;
import provider.Data;
import provider.DataDirectoryEntry;
import provider.DataFileEntry;
import provider.DataProvider;
import provider.DataProviderFactory;
import provider.DataTool;
import provider.wz.WZFiles;
import server.StatEffect;
import server.life.Element;

View File

@@ -24,13 +24,180 @@
package client.command;
import client.Client;
import client.command.commands.gm0.*;
import client.command.commands.gm1.*;
import client.command.commands.gm2.*;
import client.command.commands.gm3.*;
import client.command.commands.gm4.*;
import client.command.commands.gm5.*;
import client.command.commands.gm6.*;
import client.command.commands.gm0.ChangeLanguageCommand;
import client.command.commands.gm0.DisposeCommand;
import client.command.commands.gm0.DropLimitCommand;
import client.command.commands.gm0.EnableAuthCommand;
import client.command.commands.gm0.EquipLvCommand;
import client.command.commands.gm0.GachaCommand;
import client.command.commands.gm0.GmCommand;
import client.command.commands.gm0.HelpCommand;
import client.command.commands.gm0.JoinEventCommand;
import client.command.commands.gm0.LeaveEventCommand;
import client.command.commands.gm0.MapOwnerClaimCommand;
import client.command.commands.gm0.OnlineCommand;
import client.command.commands.gm0.RanksCommand;
import client.command.commands.gm0.RatesCommand;
import client.command.commands.gm0.ReadPointsCommand;
import client.command.commands.gm0.ReportBugCommand;
import client.command.commands.gm0.ShowRatesCommand;
import client.command.commands.gm0.StaffCommand;
import client.command.commands.gm0.StatDexCommand;
import client.command.commands.gm0.StatIntCommand;
import client.command.commands.gm0.StatLukCommand;
import client.command.commands.gm0.StatStrCommand;
import client.command.commands.gm0.TimeCommand;
import client.command.commands.gm0.ToggleExpCommand;
import client.command.commands.gm0.UptimeCommand;
import client.command.commands.gm1.BossHpCommand;
import client.command.commands.gm1.BuffMeCommand;
import client.command.commands.gm1.GotoCommand;
import client.command.commands.gm1.MobHpCommand;
import client.command.commands.gm1.WhatDropsFromCommand;
import client.command.commands.gm1.WhoDropsCommand;
import client.command.commands.gm2.ApCommand;
import client.command.commands.gm2.BombCommand;
import client.command.commands.gm2.BuffCommand;
import client.command.commands.gm2.BuffMapCommand;
import client.command.commands.gm2.ClearDropsCommand;
import client.command.commands.gm2.ClearSavedLocationsCommand;
import client.command.commands.gm2.ClearSlotCommand;
import client.command.commands.gm2.DcCommand;
import client.command.commands.gm2.EmpowerMeCommand;
import client.command.commands.gm2.GachaListCommand;
import client.command.commands.gm2.GmShopCommand;
import client.command.commands.gm2.HealCommand;
import client.command.commands.gm2.HideCommand;
import client.command.commands.gm2.IdCommand;
import client.command.commands.gm2.ItemCommand;
import client.command.commands.gm2.ItemDropCommand;
import client.command.commands.gm2.JailCommand;
import client.command.commands.gm2.JobCommand;
import client.command.commands.gm2.LevelCommand;
import client.command.commands.gm2.LevelProCommand;
import client.command.commands.gm2.LootCommand;
import client.command.commands.gm2.MaxSkillCommand;
import client.command.commands.gm2.MaxStatCommand;
import client.command.commands.gm2.MobSkillCommand;
import client.command.commands.gm2.ReachCommand;
import client.command.commands.gm2.RechargeCommand;
import client.command.commands.gm2.ResetSkillCommand;
import client.command.commands.gm2.SearchCommand;
import client.command.commands.gm2.SetSlotCommand;
import client.command.commands.gm2.SetStatCommand;
import client.command.commands.gm2.SpCommand;
import client.command.commands.gm2.SummonCommand;
import client.command.commands.gm2.UnBugCommand;
import client.command.commands.gm2.UnHideCommand;
import client.command.commands.gm2.UnJailCommand;
import client.command.commands.gm2.WarpAreaCommand;
import client.command.commands.gm2.WarpCommand;
import client.command.commands.gm2.WarpMapCommand;
import client.command.commands.gm2.WhereaMiCommand;
import client.command.commands.gm3.BanCommand;
import client.command.commands.gm3.ChatCommand;
import client.command.commands.gm3.CheckDmgCommand;
import client.command.commands.gm3.ClosePortalCommand;
import client.command.commands.gm3.DebuffCommand;
import client.command.commands.gm3.EndEventCommand;
import client.command.commands.gm3.ExpedsCommand;
import client.command.commands.gm3.FaceCommand;
import client.command.commands.gm3.FameCommand;
import client.command.commands.gm3.FlyCommand;
import client.command.commands.gm3.GiveMesosCommand;
import client.command.commands.gm3.GiveNxCommand;
import client.command.commands.gm3.GiveRpCommand;
import client.command.commands.gm3.GiveVpCommand;
import client.command.commands.gm3.HairCommand;
import client.command.commands.gm3.HealMapCommand;
import client.command.commands.gm3.HealPersonCommand;
import client.command.commands.gm3.HpMpCommand;
import client.command.commands.gm3.HurtCommand;
import client.command.commands.gm3.IgnoreCommand;
import client.command.commands.gm3.IgnoredCommand;
import client.command.commands.gm3.InMapCommand;
import client.command.commands.gm3.KillAllCommand;
import client.command.commands.gm3.KillCommand;
import client.command.commands.gm3.KillMapCommand;
import client.command.commands.gm3.MaxEnergyCommand;
import client.command.commands.gm3.MaxHpMpCommand;
import client.command.commands.gm3.MonitorCommand;
import client.command.commands.gm3.MonitorsCommand;
import client.command.commands.gm3.MusicCommand;
import client.command.commands.gm3.MuteMapCommand;
import client.command.commands.gm3.NightCommand;
import client.command.commands.gm3.NoticeCommand;
import client.command.commands.gm3.NpcCommand;
import client.command.commands.gm3.OnlineTwoCommand;
import client.command.commands.gm3.OpenPortalCommand;
import client.command.commands.gm3.PeCommand;
import client.command.commands.gm3.PosCommand;
import client.command.commands.gm3.QuestCompleteCommand;
import client.command.commands.gm3.QuestResetCommand;
import client.command.commands.gm3.QuestStartCommand;
import client.command.commands.gm3.ReloadDropsCommand;
import client.command.commands.gm3.ReloadEventsCommand;
import client.command.commands.gm3.ReloadMapCommand;
import client.command.commands.gm3.ReloadPortalsCommand;
import client.command.commands.gm3.ReloadShopsCommand;
import client.command.commands.gm3.RipCommand;
import client.command.commands.gm3.SeedCommand;
import client.command.commands.gm3.SpawnCommand;
import client.command.commands.gm3.StartEventCommand;
import client.command.commands.gm3.StartMapEventCommand;
import client.command.commands.gm3.StopMapEventCommand;
import client.command.commands.gm3.TimerAllCommand;
import client.command.commands.gm3.TimerCommand;
import client.command.commands.gm3.TimerMapCommand;
import client.command.commands.gm3.ToggleCouponCommand;
import client.command.commands.gm3.UnBanCommand;
import client.command.commands.gm4.BossDropRateCommand;
import client.command.commands.gm4.CakeCommand;
import client.command.commands.gm4.DropRateCommand;
import client.command.commands.gm4.ExpRateCommand;
import client.command.commands.gm4.FishingRateCommand;
import client.command.commands.gm4.ForceVacCommand;
import client.command.commands.gm4.HorntailCommand;
import client.command.commands.gm4.ItemVacCommand;
import client.command.commands.gm4.MesoRateCommand;
import client.command.commands.gm4.PapCommand;
import client.command.commands.gm4.PianusCommand;
import client.command.commands.gm4.PinkbeanCommand;
import client.command.commands.gm4.PlayerNpcCommand;
import client.command.commands.gm4.PlayerNpcRemoveCommand;
import client.command.commands.gm4.PmobCommand;
import client.command.commands.gm4.PmobRemoveCommand;
import client.command.commands.gm4.PnpcCommand;
import client.command.commands.gm4.PnpcRemoveCommand;
import client.command.commands.gm4.ProItemCommand;
import client.command.commands.gm4.QuestRateCommand;
import client.command.commands.gm4.ServerMessageCommand;
import client.command.commands.gm4.SetEqStatCommand;
import client.command.commands.gm4.TravelRateCommand;
import client.command.commands.gm4.ZakumCommand;
import client.command.commands.gm5.DebugCommand;
import client.command.commands.gm5.IpListCommand;
import client.command.commands.gm5.SetCommand;
import client.command.commands.gm5.ShowMoveLifeCommand;
import client.command.commands.gm5.ShowPacketsCommand;
import client.command.commands.gm5.ShowSessionsCommand;
import client.command.commands.gm6.ClearQuestCacheCommand;
import client.command.commands.gm6.ClearQuestCommand;
import client.command.commands.gm6.DCAllCommand;
import client.command.commands.gm6.DevtestCommand;
import client.command.commands.gm6.EraseAllPNpcsCommand;
import client.command.commands.gm6.GetAccCommand;
import client.command.commands.gm6.MapPlayersCommand;
import client.command.commands.gm6.SaveAllCommand;
import client.command.commands.gm6.ServerAddChannelCommand;
import client.command.commands.gm6.ServerAddWorldCommand;
import client.command.commands.gm6.ServerRemoveChannelCommand;
import client.command.commands.gm6.ServerRemoveWorldCommand;
import client.command.commands.gm6.SetGmLevelCommand;
import client.command.commands.gm6.ShutdownCommand;
import client.command.commands.gm6.SpawnAllPNpcsCommand;
import client.command.commands.gm6.SupplyRateCouponCommand;
import client.command.commands.gm6.WarpWorldCommand;
import constants.id.MapId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -177,7 +344,6 @@ public class CommandsExecutor {
addCommand("droplimit", DropLimitCommand.class);
addCommand("time", TimeCommand.class);
addCommand("credits", StaffCommand.class);
addCommand("buyback", BuyBackCommand.class);
addCommand("uptime", UptimeCommand.class);
addCommand("gacha", GachaCommand.class);
addCommand("dispose", DisposeCommand.class);
@@ -391,6 +557,7 @@ public class CommandsExecutor {
addCommand("addworld", 6, ServerAddWorldCommand.class);
addCommand("removechannel", 6, ServerRemoveChannelCommand.class);
addCommand("removeworld", 6, ServerRemoveWorldCommand.class);
addCommand("devtest", 6, DevtestCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}

View File

@@ -1,48 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/*
@Author: Arthur L - Refactored command content into modules
*/
package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.processor.action.BuybackProcessor;
public class BuyBackCommand extends Command {
{
setDescription("Revive yourself after a death.");
}
@Override
public void execute(Client c, String[] params) {
if (params.length < 1) {
c.getPlayer().yellowMessage("Syntax: @buyback <info|now>");
return;
}
if (params[0].contentEquals("now")) {
BuybackProcessor.processBuyback(c);
} else {
c.getPlayer().showBuybackInfo();
}
}
}

View File

@@ -27,7 +27,10 @@ import client.Client;
import client.command.Command;
import net.server.Server;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public class UptimeCommand extends Command {
{

View File

@@ -28,7 +28,11 @@ import client.Client;
import client.command.Command;
import constants.game.GameConstants;
import constants.id.NpcId;
import server.maps.*;
import server.maps.FieldLimit;
import server.maps.MapFactory;
import server.maps.MapleMap;
import server.maps.MiniDungeonInfo;
import server.maps.Portal;
import java.util.ArrayList;
import java.util.HashMap;

View File

@@ -11,7 +11,11 @@ import tools.exceptions.IdTypeNotSupportedException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;

View File

@@ -24,7 +24,10 @@
package client.command.commands.gm2;
import client.Character;
import client.*;
import client.Client;
import client.Job;
import client.Skill;
import client.SkillFactory;
import client.command.Command;
import provider.Data;
import provider.DataProviderFactory;

View File

@@ -24,7 +24,10 @@
package client.command.commands.gm2;
import client.Character;
import client.*;
import client.Client;
import client.Job;
import client.Skill;
import client.SkillFactory;
import client.command.Command;
import provider.Data;
import provider.DataProviderFactory;

View File

@@ -0,0 +1,40 @@
package client.command.commands.gm6;
import client.Client;
import client.command.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scripting.AbstractScriptManager;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class DevtestCommand extends Command {
{
setDescription("Runs devtest.js. Developer utility - test stuff without restarting the server.");
}
private static final Logger log = LoggerFactory.getLogger(DevtestCommand.class);
private static class DevtestScriptManager extends AbstractScriptManager {
@Override
public ScriptEngine getInvocableScriptEngine(String path) {
return super.getInvocableScriptEngine(path);
}
}
@Override
public void execute(Client client, String[] params) {
DevtestScriptManager scriptManager = new DevtestScriptManager();
ScriptEngine scriptEngine = scriptManager.getInvocableScriptEngine("devtest.js");
try {
Invocable invocable = (Invocable) scriptEngine;
invocable.invokeFunction("run", client.getPlayer());
} catch (ScriptException | NoSuchMethodException e) {
log.info("devtest.js run() threw an exception", e);
}
}
}

View File

@@ -30,7 +30,10 @@ import net.server.Server;
import net.server.world.World;
import server.TimerManager;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public class ShutdownCommand extends Command {
{

View File

@@ -31,7 +31,14 @@ import server.ItemInformationProvider;
import server.ThreadManager;
import tools.Pair;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -652,4 +659,4 @@ public class Inventory implements Iterable<Item> {
public void dispose() {
owner = null;
}
}
}

View File

@@ -23,7 +23,11 @@ package client.inventory;
import tools.DatabaseConnection;
import tools.Pair;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
@@ -418,4 +422,4 @@ public enum ItemFactory {
lock.unlock();
}
}
}
}

View File

@@ -24,7 +24,12 @@ package client.inventory.manipulator;
import client.BuffStat;
import client.Character;
import client.Client;
import client.inventory.*;
import client.inventory.Equip;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ModifyInventory;
import client.inventory.Pet;
import client.newyear.NewYearCardRecord;
import config.YamlConfig;
import constants.id.ItemId;

View File

@@ -1,83 +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 <http://www.gnu.org/licenses/>.
*/
package client.processor.action; // thanks Alex for pointing out some package structures containing broad modules
import client.Character;
import client.Client;
import server.maps.MapleMap;
import tools.PacketCreator;
/**
* @author RonanLana
*/
public class BuybackProcessor {
public static void processBuyback(Client c) {
Character chr = c.getPlayer();
boolean buyback;
c.lockClient();
try {
buyback = !chr.isAlive() && chr.couldBuyback();
} finally {
c.unlockClient();
}
if (buyback) {
String jobString;
switch (chr.getJobStyle()) {
case WARRIOR:
jobString = "warrior";
break;
case MAGICIAN:
jobString = "magician";
break;
case BOWMAN:
jobString = "bowman";
break;
case THIEF:
jobString = "thief";
break;
case BRAWLER:
case GUNSLINGER:
jobString = "pirate";
break;
default:
jobString = "beginner";
}
chr.healHpMp();
chr.purgeDebuffs();
chr.broadcastStance(chr.isFacingLeft() ? 5 : 4);
MapleMap map = chr.getMap();
map.broadcastMessage(PacketCreator.playSound("Buyback/" + jobString));
map.broadcastMessage(PacketCreator.earnTitleMessage(chr.getName() + " just bought back into the game!"));
chr.sendPacket(PacketCreator.showBuybackEffect());
map.broadcastMessage(chr, PacketCreator.showForeignBuybackEffect(chr.getId()), false);
}
}
}

View File

@@ -45,7 +45,12 @@ import tools.DatabaseConnection;
import tools.PacketCreator;
import tools.Pair;
import java.sql.*;
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;

View File

@@ -41,7 +41,11 @@ import tools.DatabaseConnection;
import tools.PacketCreator;
import tools.Pair;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;

View File

@@ -24,13 +24,22 @@
package client.processor.stat;
import client.Character;
import client.*;
import client.Client;
import client.Job;
import client.Skill;
import client.SkillFactory;
import client.Stat;
import client.autoban.AutobanFactory;
import client.inventory.Equip;
import client.inventory.InventoryType;
import client.inventory.Item;
import config.YamlConfig;
import constants.skills.*;
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 net.packet.InPacket;
import tools.PacketCreator;
import tools.Randomizer;
@@ -543,16 +552,14 @@ public class AssignAPProcessor {
return false;
}
int hp = player.getMaxHp();
int level_ = player.getLevel();
if (hp < level_ * 14 + 148) {
int hplose = -takeHp(player.getJob());
if (player.getMaxHp() + hplose < getMinHp(player.getJob(), player.getLevel())) {
player.message("You don't have the minimum HP pool required to swap.");
c.sendPacket(PacketCreator.enableActions());
return false;
}
int curHp = player.getHp();
int hplose = -takeHp(player.getJob());
player.assignHP(hplose, -1);
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
player.updateHp(Math.max(1, curHp + hplose));
@@ -574,29 +581,14 @@ public class AssignAPProcessor {
return false;
}
int mp = player.getMaxMp();
int level = player.getLevel();
Job job = player.getJob();
boolean canWash = true;
if (job.isA(Job.SPEARMAN) && mp < 4 * level + 156) {
canWash = false;
} else if ((job.isA(Job.FIGHTER) || job.isA(Job.ARAN1)) && mp < 4 * level + 56) {
canWash = false;
} else if (job.isA(Job.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
canWash = false;
} else if (mp < level * 14 + 148) {
canWash = false;
}
if (!canWash) {
int mplose = -takeMp(player.getJob());
if (player.getMaxMp() + mplose < getMinMp(player.getJob(), player.getLevel())) {
player.message("You don't have the minimum MP pool required to swap.");
c.sendPacket(PacketCreator.enableActions());
return false;
}
int curMp = player.getMp();
int mplose = -takeMp(job);
player.assignMP(mplose, -1);
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
player.updateMp(Math.max(0, curMp + mplose));
@@ -887,4 +879,109 @@ public class AssignAPProcessor {
return MaxMP;
}
public static int getMinHp(Job job, int level) {
int multiplier = 0;
int offset = 0;
if (job == Job.WARRIOR ||
job.isA(Job.PAGE) ||
job.isA(Job.SPEARMAN) ||
job == Job.DAWNWARRIOR1 ||
job == Job.ARAN1) {
multiplier = 24; offset = 118;
} else if (job.isA(Job.FIGHTER) ||
job.isA(Job.DAWNWARRIOR2) ||
job.isA(Job.ARAN2)) {
multiplier = 24; offset = 418;
} else if (job.isA(Job.MAGICIAN) ||
job.isA(Job.BLAZEWIZARD1)) {
multiplier = 10; offset = 54;
} else if (job == Job.BOWMAN ||
job == Job.THIEF ||
job == Job.WINDARCHER1 ||
job == Job.NIGHTWALKER1) {
multiplier = 20; offset = 58;
} else if (job.isA(Job.HUNTER) ||
job.isA(Job.CROSSBOWMAN) ||
job.isA(Job.ASSASSIN) ||
job.isA(Job.BANDIT) ||
job.isA(Job.WINDARCHER2) ||
job.isA(Job.NIGHTWALKER2)) {
multiplier = 20; offset = 358;
} else if (job == Job.PIRATE ||
job == Job.THUNDERBREAKER1) {
multiplier = 22; offset = 38;
} else if (job.isA(Job.BRAWLER) ||
job.isA(Job.GUNSLINGER) ||
job.isA(Job.THUNDERBREAKER2)) {
multiplier = 22; offset = 338;
} else if (job == Job.BEGINNER ||
job == Job.NOBLESSE) {
multiplier = 12; offset = 38;
}
return (multiplier * level) + offset;
}
public static int getMinMp(Job job, int level) {
int multiplier = 0;
int offset = 0;
if (job == Job.WARRIOR ||
job.isA(Job.FIGHTER) ||
job.isA(Job.DAWNWARRIOR1) ||
job.isA(Job.ARAN1)) {
multiplier = 4; offset = 55;
} else if (job.isA(Job.PAGE) ||
job.isA(Job.SPEARMAN)) {
multiplier = 4; offset = 155;
} else if (job == Job.MAGICIAN ||
job == Job.BLAZEWIZARD1) {
multiplier = 22; offset = -1;
} else if (job.isA(Job.FP_WIZARD) ||
job.isA(Job.IL_WIZARD) ||
job.isA(Job.CLERIC) ||
job.isA(Job.BLAZEWIZARD2)) {
multiplier = 22; offset = 449;
} else if (job == Job.BOWMAN ||
job == Job.THIEF ||
job == Job.WINDARCHER1 ||
job == Job.NIGHTWALKER1) {
multiplier = 14; offset = -15;
} else if (job.isA(Job.HUNTER) ||
job.isA(Job.CROSSBOWMAN) ||
job.isA(Job.ASSASSIN) ||
job.isA(Job.BANDIT) ||
job.isA(Job.WINDARCHER2) ||
job.isA(Job.NIGHTWALKER2)) {
multiplier = 14; offset = 135;
} else if (job == Job.PIRATE ||
job == Job.THUNDERBREAKER1) {
multiplier = 18; offset = -55;
} else if (job.isA(Job.BRAWLER) ||
job.isA(Job.GUNSLINGER) ||
job.isA(Job.THUNDERBREAKER2)) {
multiplier = 18; offset = 95;
} else if (job == Job.BEGINNER ||
job == Job.NOBLESSE) {
multiplier = 10; offset = -5;
}
return (multiplier * level) + offset;
}
}

View File

@@ -68,7 +68,6 @@ public class ServerConfig {
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;
@@ -98,7 +97,6 @@ public class ServerConfig {
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;
@@ -106,10 +104,8 @@ public class ServerConfig {
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;
@@ -167,7 +163,6 @@ public class ServerConfig {
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;
public int REBIRTH_NPC_ID;
//Dangling Items/Locks Configuration
public int ITEM_EXPIRE_TIME;
@@ -244,7 +239,6 @@ public class ServerConfig {
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;
@@ -295,14 +289,6 @@ public class ServerConfig {
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;

View File

@@ -165,7 +165,6 @@ public class ItemId {
public static final int EYEDROP = 2050001;
public static final int TONIC = 2050002;
public static final int HOLY_WATER = 2050003;
public static final int ANTI_BANISH_SCROLL = 2030100;
private static final int DOJO_PARTY_ALL_CURE = 2022433;
private static final int CARNIVAL_PARTY_ALL_CURE = 2022163;
public static final int WHITE_ELIXIR = 2022544;
@@ -312,6 +311,10 @@ public class ItemId {
return itemId == NX_CARD_100 || itemId == NX_CARD_250;
}
public static boolean isCashPackage(int itemId) {
return itemId / 10000 == 910;
}
// Face expression
private static final int FACE_EXPRESSION_MIN = 5160000;
private static final int FACE_EXPRESSION_MAX = 5160014;

View File

@@ -134,11 +134,7 @@ public final class ItemConstants {
}
public static boolean isTownScroll(int itemId) {
return itemId >= 2030000 && itemId < ItemId.ANTI_BANISH_SCROLL;
}
public static boolean isAntibanishScroll(int itemId) {
return itemId == ItemId.ANTI_BANISH_SCROLL;
return itemId >= 2030000;
}
public static boolean isCleanSlate(int scrollId) {

View File

@@ -23,11 +23,174 @@ package net;
import net.netty.LoginServer;
import net.opcodes.RecvOpcode;
import net.server.channel.handlers.*;
import net.server.channel.handlers.AcceptFamilyHandler;
import net.server.channel.handlers.AdminChatHandler;
import net.server.channel.handlers.AdminCommandHandler;
import net.server.channel.handlers.AdminLogHandler;
import net.server.channel.handlers.AllianceOperationHandler;
import net.server.channel.handlers.AranComboHandler;
import net.server.channel.handlers.AutoAggroHandler;
import net.server.channel.handlers.AutoAssignHandler;
import net.server.channel.handlers.BBSOperationHandler;
import net.server.channel.handlers.BeholderHandler;
import net.server.channel.handlers.BuddylistModifyHandler;
import net.server.channel.handlers.CancelBuffHandler;
import net.server.channel.handlers.CancelChairHandler;
import net.server.channel.handlers.CancelDebuffHandler;
import net.server.channel.handlers.CancelItemEffectHandler;
import net.server.channel.handlers.CashOperationHandler;
import net.server.channel.handlers.CashShopSurpriseHandler;
import net.server.channel.handlers.ChangeChannelHandler;
import net.server.channel.handlers.ChangeMapHandler;
import net.server.channel.handlers.ChangeMapSpecialHandler;
import net.server.channel.handlers.CharInfoRequestHandler;
import net.server.channel.handlers.ClickGuideHandler;
import net.server.channel.handlers.CloseChalkboardHandler;
import net.server.channel.handlers.CloseRangeDamageHandler;
import net.server.channel.handlers.CoconutHandler;
import net.server.channel.handlers.CouponCodeHandler;
import net.server.channel.handlers.DamageSummonHandler;
import net.server.channel.handlers.DenyAllianceRequestHandler;
import net.server.channel.handlers.DenyGuildRequestHandler;
import net.server.channel.handlers.DenyPartyRequestHandler;
import net.server.channel.handlers.DistributeAPHandler;
import net.server.channel.handlers.DistributeSPHandler;
import net.server.channel.handlers.DoorHandler;
import net.server.channel.handlers.DueyHandler;
import net.server.channel.handlers.EnterCashShopHandler;
import net.server.channel.handlers.EnterMTSHandler;
import net.server.channel.handlers.FaceExpressionHandler;
import net.server.channel.handlers.FamilyAddHandler;
import net.server.channel.handlers.FamilyPreceptsHandler;
import net.server.channel.handlers.FamilySeparateHandler;
import net.server.channel.handlers.FamilySummonResponseHandler;
import net.server.channel.handlers.FamilyUseHandler;
import net.server.channel.handlers.FieldDamageMobHandler;
import net.server.channel.handlers.FredrickHandler;
import net.server.channel.handlers.GeneralChatHandler;
import net.server.channel.handlers.GiveFameHandler;
import net.server.channel.handlers.GrenadeEffectHandler;
import net.server.channel.handlers.GuildOperationHandler;
import net.server.channel.handlers.HealOvertimeHandler;
import net.server.channel.handlers.HiredMerchantRequest;
import net.server.channel.handlers.InnerPortalHandler;
import net.server.channel.handlers.InventoryMergeHandler;
import net.server.channel.handlers.InventorySortHandler;
import net.server.channel.handlers.ItemMoveHandler;
import net.server.channel.handlers.ItemPickupHandler;
import net.server.channel.handlers.ItemRewardHandler;
import net.server.channel.handlers.KeymapChangeHandler;
import net.server.channel.handlers.LeftKnockbackHandler;
import net.server.channel.handlers.MTSHandler;
import net.server.channel.handlers.MagicDamageHandler;
import net.server.channel.handlers.MakerSkillHandler;
import net.server.channel.handlers.MesoDropHandler;
import net.server.channel.handlers.MessengerHandler;
import net.server.channel.handlers.MobBanishPlayerHandler;
import net.server.channel.handlers.MobDamageMobFriendlyHandler;
import net.server.channel.handlers.MobDamageMobHandler;
import net.server.channel.handlers.MonsterBombHandler;
import net.server.channel.handlers.MonsterBookCoverHandler;
import net.server.channel.handlers.MonsterCarnivalHandler;
import net.server.channel.handlers.MoveDragonHandler;
import net.server.channel.handlers.MoveLifeHandler;
import net.server.channel.handlers.MovePetHandler;
import net.server.channel.handlers.MovePlayerHandler;
import net.server.channel.handlers.MoveSummonHandler;
import net.server.channel.handlers.MultiChatHandler;
import net.server.channel.handlers.NPCAnimationHandler;
import net.server.channel.handlers.NPCMoreTalkHandler;
import net.server.channel.handlers.NPCShopHandler;
import net.server.channel.handlers.NPCTalkHandler;
import net.server.channel.handlers.NewYearCardHandler;
import net.server.channel.handlers.NoteActionHandler;
import net.server.channel.handlers.OpenFamilyHandler;
import net.server.channel.handlers.OpenFamilyPedigreeHandler;
import net.server.channel.handlers.OwlWarpHandler;
import net.server.channel.handlers.PartyOperationHandler;
import net.server.channel.handlers.PartySearchRegisterHandler;
import net.server.channel.handlers.PartySearchStartHandler;
import net.server.channel.handlers.PartySearchUpdateHandler;
import net.server.channel.handlers.PetAutoPotHandler;
import net.server.channel.handlers.PetChatHandler;
import net.server.channel.handlers.PetCommandHandler;
import net.server.channel.handlers.PetExcludeItemsHandler;
import net.server.channel.handlers.PetFoodHandler;
import net.server.channel.handlers.PetLootHandler;
import net.server.channel.handlers.PlayerInteractionHandler;
import net.server.channel.handlers.PlayerLoggedinHandler;
import net.server.channel.handlers.PlayerMapTransitionHandler;
import net.server.channel.handlers.QuestActionHandler;
import net.server.channel.handlers.QuickslotKeyMappedModifiedHandler;
import net.server.channel.handlers.RPSActionHandler;
import net.server.channel.handlers.RaiseIncExpHandler;
import net.server.channel.handlers.RaiseUIStateHandler;
import net.server.channel.handlers.RangedAttackHandler;
import net.server.channel.handlers.ReactorHitHandler;
import net.server.channel.handlers.RemoteGachaponHandler;
import net.server.channel.handlers.RemoteStoreHandler;
import net.server.channel.handlers.ReportHandler;
import net.server.channel.handlers.RingActionHandler;
import net.server.channel.handlers.ScriptedItemHandler;
import net.server.channel.handlers.ScrollHandler;
import net.server.channel.handlers.SkillBookHandler;
import net.server.channel.handlers.SkillEffectHandler;
import net.server.channel.handlers.SkillMacroHandler;
import net.server.channel.handlers.SnowballHandler;
import net.server.channel.handlers.SpawnPetHandler;
import net.server.channel.handlers.SpecialMoveHandler;
import net.server.channel.handlers.SpouseChatHandler;
import net.server.channel.handlers.StorageHandler;
import net.server.channel.handlers.SummonDamageHandler;
import net.server.channel.handlers.TakeDamageHandler;
import net.server.channel.handlers.TouchMonsterDamageHandler;
import net.server.channel.handlers.TouchReactorHandler;
import net.server.channel.handlers.TouchingCashShopHandler;
import net.server.channel.handlers.TransferNameHandler;
import net.server.channel.handlers.TransferNameResultHandler;
import net.server.channel.handlers.TransferWorldHandler;
import net.server.channel.handlers.TrockAddMapHandler;
import net.server.channel.handlers.UseCashItemHandler;
import net.server.channel.handlers.UseCatchItemHandler;
import net.server.channel.handlers.UseChairHandler;
import net.server.channel.handlers.UseDeathItemHandler;
import net.server.channel.handlers.UseGachaExpHandler;
import net.server.channel.handlers.UseHammerHandler;
import net.server.channel.handlers.UseItemEffectHandler;
import net.server.channel.handlers.UseItemHandler;
import net.server.channel.handlers.UseMapleLifeHandler;
import net.server.channel.handlers.UseMountFoodHandler;
import net.server.channel.handlers.UseOwlOfMinervaHandler;
import net.server.channel.handlers.UseSolomonHandler;
import net.server.channel.handlers.UseSummonBagHandler;
import net.server.channel.handlers.UseWaterOfLifeHandler;
import net.server.channel.handlers.WeddingHandler;
import net.server.channel.handlers.WeddingTalkHandler;
import net.server.channel.handlers.WeddingTalkMoreHandler;
import net.server.channel.handlers.WhisperHandler;
import net.server.handlers.CustomPacketHandler;
import net.server.handlers.KeepAliveHandler;
import net.server.handlers.LoginRequiringNoOpHandler;
import net.server.handlers.login.*;
import net.server.handlers.login.AcceptToSHandler;
import net.server.handlers.login.AfterLoginHandler;
import net.server.handlers.login.CharSelectedHandler;
import net.server.handlers.login.CharSelectedWithPicHandler;
import net.server.handlers.login.CharlistRequestHandler;
import net.server.handlers.login.CheckCharNameHandler;
import net.server.handlers.login.CreateCharHandler;
import net.server.handlers.login.DeleteCharHandler;
import net.server.handlers.login.GuestLoginHandler;
import net.server.handlers.login.LoginPasswordHandler;
import net.server.handlers.login.RegisterPicHandler;
import net.server.handlers.login.RegisterPinHandler;
import net.server.handlers.login.RelogRequestHandler;
import net.server.handlers.login.ServerStatusRequestHandler;
import net.server.handlers.login.ServerlistRequestHandler;
import net.server.handlers.login.SetGenderHandler;
import net.server.handlers.login.ViewAllCharHandler;
import net.server.handlers.login.ViewAllCharRegisterPicHandler;
import net.server.handlers.login.ViewAllCharSelectedHandler;
import net.server.handlers.login.ViewAllCharSelectedWithPicHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -286,4 +449,4 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler());
registerHandler(RecvOpcode.CHANGE_QUICKSLOT, new QuickslotKeyMappedModifiedHandler());
}
}
}

View File

@@ -82,6 +82,11 @@ public class ByteBufInPacket implements InPacket {
return byteBuf.readerIndex();
}
@Override
public boolean equals(Object o) {
return o instanceof ByteBufInPacket other && byteBuf.equals(other.byteBuf);
}
@Override
public String toString() {
final int readerIndex = byteBuf.readerIndex();

View File

@@ -91,4 +91,9 @@ public class ByteBufOutPacket implements OutPacket {
public void skip(int numberOfBytes) {
writeBytes(new byte[numberOfBytes]);
}
@Override
public boolean equals(Object o) {
return o instanceof ByteBufOutPacket other && byteBuf.equals(other.byteBuf);
}
}

View File

@@ -24,7 +24,11 @@ package net.server;
import client.Character;
import client.Client;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -124,4 +128,4 @@ public class PlayerStorage {
rlock.unlock();
}
}
}
}

View File

@@ -47,7 +47,17 @@ import net.server.coordinator.session.SessionCoordinator;
import net.server.guild.Alliance;
import net.server.guild.Guild;
import net.server.guild.GuildCharacter;
import net.server.task.*;
import net.server.task.BossLogTask;
import net.server.task.CharacterDiseaseTask;
import net.server.task.CouponTask;
import net.server.task.DueyFredrickTask;
import net.server.task.EventRecallCoordinatorTask;
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.RespawnTask;
import net.server.world.World;
import org.apache.logging.log4j.LogManager;
import org.slf4j.Logger;
@@ -69,8 +79,20 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -80,7 +102,10 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Server {
private static final Logger log = LoggerFactory.getLogger(Server.class);

View File

@@ -41,7 +41,11 @@ import server.TimerManager;
import server.events.gm.Event;
import server.expeditions.Expedition;
import server.expeditions.ExpeditionType;
import server.maps.*;
import server.maps.HiredMerchant;
import server.maps.MapManager;
import server.maps.MapleMap;
import server.maps.MiniDungeon;
import server.maps.MiniDungeonInfo;
import tools.PacketCreator;
import tools.Pair;
@@ -49,14 +53,25 @@ import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public final class Channel {
private static final Logger log = LoggerFactory.getLogger(Channel.class);
@@ -1035,4 +1050,4 @@ public final class Channel {
log.debug("Guest list: {}", ongoingChapelGuests);
log.debug("Starttime: {}", ongoingStartTime);
}
}
}

View File

@@ -21,8 +21,11 @@
*/
package net.server.channel.handlers;
import client.BuffStat;
import client.Character;
import client.*;
import client.Job;
import client.Skill;
import client.SkillFactory;
import client.autoban.AutobanFactory;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
@@ -31,37 +34,96 @@ import constants.game.GameConstants;
import constants.id.ItemId;
import constants.id.MapId;
import constants.id.MobId;
import constants.skills.*;
import constants.skills.Aran;
import constants.skills.Assassin;
import constants.skills.Bandit;
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.ChiefBandit;
import constants.skills.Cleric;
import constants.skills.Corsair;
import constants.skills.Crossbowman;
import constants.skills.Crusader;
import constants.skills.DawnWarrior;
import constants.skills.DragonKnight;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.FPMage;
import constants.skills.FPWizard;
import constants.skills.Fighter;
import constants.skills.Gunslinger;
import constants.skills.Hermit;
import constants.skills.Hero;
import constants.skills.Hunter;
import constants.skills.ILArchMage;
import constants.skills.ILMage;
import constants.skills.Legend;
import constants.skills.Marauder;
import constants.skills.Marksman;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Noblesse;
import constants.skills.Outlaw;
import constants.skills.Page;
import constants.skills.Paladin;
import constants.skills.Ranger;
import constants.skills.Rogue;
import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Spearman;
import constants.skills.SuperGM;
import constants.skills.ThunderBreaker;
import constants.skills.WhiteKnight;
import constants.skills.WindArcher;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import net.server.PlayerBuffValueHolder;
import scripting.AbstractPlayerInteraction;
import server.StatEffect;
import server.TimerManager;
import server.life.*;
import server.life.Element;
import server.life.ElementalEffectiveness;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillId;
import server.life.MobSkillType;
import server.life.Monster;
import server.life.MonsterDropEntry;
import server.life.MonsterInformationProvider;
import server.maps.MapItem;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MapleMap;
import tools.PacketCreator;
import tools.Randomizer;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
import java.util.Map;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
private static final int EXPLODED_MESO_SPREAD_DELAY = 100;
private static final int EXPLODED_MESO_MAX_DELAY = 1000;
public static class AttackInfo {
public int numAttacked, numDamage, numAttackedAndDamage, skill, skilllevel, stance, direction, rangedirection, charge, display;
public Map<Integer, List<Integer>> allDamage;
public Map<Integer, AttackTarget> targets;
public boolean ranged, magic;
public int speed = 4;
public Point position = new Point();
public List<Integer> explodedMesos;
public Short attackDelay;
public StatEffect getAttackEffect(Character chr, Skill theSkill) {
Skill mySkill = theSkill;
@@ -87,6 +149,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
}
}
// TODO: add position
public record AttackTarget(short delay, List<Integer> damageLines) {}
protected void applyAttack(AttackInfo attack, final Character player, int attackCount) {
final MapleMap map = player.getMap();
if (map.isOwnershipRestricted(player)) {
@@ -148,50 +213,14 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
return;
}
//WTF IS THIS F3,1
/*if (attackCount != attack.numDamage && attack.skill != ChiefBandit.MESO_EXPLOSION && attack.skill != NightWalker.VAMPIRE && attack.skill != WindArcher.WIND_SHOT && attack.skill != Aran.COMBO_SMASH && attack.skill != Aran.COMBO_FENRIR && attack.skill != Aran.COMBO_TEMPEST && attack.skill != NightLord.NINJA_AMBUSH && attack.skill != Shadower.NINJA_AMBUSH) {
return;
}*/
int totDamage = 0;
if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
int delay = 0;
for (Integer oned : attack.allDamage.keySet()) {
MapObject mapobject = map.getMapObject(oned);
if (mapobject != null && mapobject.getType() == MapObjectType.ITEM) {
final MapItem mapitem = (MapItem) mapobject;
if (mapitem.getMeso() == 0) { //Maybe it is possible some how?
return;
}
mapitem.lockItem();
try {
if (mapitem.isPickedUp()) {
return;
}
TimerManager.getInstance().schedule(() -> {
mapitem.lockItem();
try {
if (mapitem.isPickedUp()) {
return;
}
map.pickItemDrop(PacketCreator.removeItemFromMap(mapitem.getObjectId(), 4, 0), mapitem);
} finally {
mapitem.unlockItem();
}
}, delay);
delay += 100;
} finally {
mapitem.unlockItem();
}
} else if (mapobject != null && mapobject.getType() != MapObjectType.MONSTER) {
return;
}
}
removeExplodedMesos(map, attack);
}
for (Integer oned : attack.allDamage.keySet()) {
final Monster monster = map.getMonsterByOid(oned);
for (Map.Entry<Integer, AttackTarget> target : attack.targets.entrySet()) {
final Monster monster = map.getMonsterByOid(target.getKey());
if (monster != null) {
double distance = player.getPosition().distanceSq(monster.getPosition());
double distanceToDetect = 200000.0;
@@ -226,7 +255,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
}
int totDamageToOneMonster = 0;
List<Integer> onedList = attack.allDamage.get(oned);
List<Integer> onedList = target.getValue().damageLines();
if (attack.magic) { // thanks BHB, Alex (CanIGetaPR) for noticing no immunity status check here
if (monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
@@ -262,7 +291,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
Skill pickpocket = SkillFactory.getSkill(ChiefBandit.PICKPOCKET);
int picklv = (player.isGM()) ? pickpocket.getMaxLevel() : player.getSkillLevel(pickpocket);
if (picklv > 0) {
int delay = 0;
short delay = 0;
final int maxmeso = player.getBuffedValue(BuffStat.PICKPOCKET);
for (Integer eachd : onedList) {
eachd += Integer.MAX_VALUE;
@@ -275,7 +304,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
eachdf = eachd;
}
TimerManager.getInstance().schedule(() -> map.spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2), delay);
int meso = Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, 1), maxmeso);
Point position = new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY()));
map.spawnMesoDrop(meso, position, monster, player, true, (byte) 2, delay);
delay += 100;
}
}
@@ -301,7 +332,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
List<MonsterDropEntry> toSteal = new ArrayList<>();
toSteal.add(mi.retrieveDrop(monster.getId()).get(i));
map.dropItemsFromMonster(toSteal, player, monster);
map.dropItemsFromMonster(toSteal, player, monster, target.getValue().delay());
monster.addStolen(toSteal.get(0).itemId);
}
}
@@ -421,7 +452,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
StatEffect mortal = mortalBlow.getEffect(skillLevel);
if (monster.getHp() <= (monster.getStats().getHp() * mortal.getX()) / 100) {
if (Randomizer.rand(1, 100) <= mortal.getY()) {
map.damageMonster(player, monster, Integer.MAX_VALUE); // thanks Conrad for noticing reduced EXP gain from skill kill
map.damageMonster(player, monster, Integer.MAX_VALUE, target.getValue().delay()); // thanks Conrad for noticing reduced EXP gain from skill kill
}
}
}
@@ -487,7 +518,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
map.broadcastMessage(PacketCreator.damageMonster(monster.getObjectId(), totDamageToOneMonster));
}
map.damageMonster(player, monster, totDamageToOneMonster);
map.damageMonster(player, monster, totDamageToOneMonster, target.getValue().delay());
}
if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) {
for (MobSkillId msId : monster.getSkills()) {
@@ -514,7 +545,8 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
}
}
private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster, final int damage, int skillid, int fixedTime) {
private static void damageMonsterWithSkill(final Character attacker, final MapleMap map, final Monster monster,
final int damage, int skillid, int fixedTime) {
int animationTime;
if (fixedTime == 0) {
@@ -541,7 +573,7 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
ret.numAttackedAndDamage = p.readByte();
ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
ret.numDamage = ret.numAttackedAndDamage & 0xF;
ret.allDamage = new HashMap<>();
ret.targets = new HashMap<>();
ret.skill = p.readInt();
ret.ranged = ranged;
ret.magic = magic;
@@ -564,41 +596,9 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
ret.direction = p.readByte();
ret.stance = p.readByte();
if (ret.skill == ChiefBandit.MESO_EXPLOSION) {
if (ret.numAttackedAndDamage == 0) {
p.skip(10);
int bullets = p.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt();
p.skip(1);
ret.allDamage.put(mesoid, null);
}
return ret;
} else {
p.skip(6);
}
for (int i = 0; i < ret.numAttacked + 1; i++) {
int oid = p.readInt();
if (i < ret.numAttacked) {
p.skip(12);
int bullets = p.readByte();
List<Integer> allDamageNumbers = new ArrayList<>();
for (int j = 0; j < bullets; j++) {
int damage = p.readInt();
allDamageNumbers.add(damage);
}
ret.allDamage.put(oid, allDamageNumbers);
p.skip(4);
} else {
int bullets = p.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = p.readInt();
p.skip(1);
ret.allDamage.put(mesoid, null);
}
}
}
return ret;
return parseMesoExplosion(p, ret);
}
if (ranged) {
p.readByte();
ret.speed = p.readByte();
@@ -630,9 +630,10 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
calcDmgMax = chr.calculateMaxBaseDamage(chr.getTotalWatk());
}
StatEffect effect = null;
if (ret.skill != 0) {
Skill skill = SkillFactory.getSkill(ret.skill);
StatEffect effect = skill.getEffect(ret.skilllevel);
effect = skill.getEffect(ret.skilllevel);
if (magic) {
// Since the skill is magic based, use the magic formula
@@ -754,10 +755,12 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
}
for (int i = 0; i < ret.numAttacked; i++) {
int oid = p.readInt();
p.skip(14);
List<Integer> allDamageNumbers = new ArrayList<>();
Monster monster = chr.getMap().getMonsterByOid(oid);
p.skip(4);
Point curPos = p.readPos();
Point nextPos = p.readPos();
short delay = p.readShort();
List<Integer> damageLines = new ArrayList<>();
final Monster monster = chr.getMap().getMonsterByOid(oid);
if (chr.getBuffEffect(BuffStat.WK_CHARGE) != null) {
// Charge, so now we need to check elemental effectiveness
int sourceID = chr.getBuffSource(BuffStat.WK_CHARGE);
@@ -871,12 +874,22 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
damage = -Integer.MAX_VALUE + damage - 1;
}
allDamageNumbers.add(damage);
if(effect != null) {
int maxattack = Math.max(effect.getBulletCount(), effect.getAttackCount());
if (shadowPartner) {
maxattack = maxattack * 2;
}
if (ret.numDamage > maxattack) {
AutobanFactory.DAMAGE_HACK.addPoint(chr.getAutobanManager(), "Too many lines: " + ret.numDamage + " Max lines: " + maxattack + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
}
}
damageLines.add(damage);
}
if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) {
p.skip(4);
}
ret.allDamage.put(oid, allDamageNumbers);
ret.targets.put(oid, new AttackTarget(delay, damageLines));
}
if (ret.skill == NightWalker.POISON_BOMB) { // Poison Bomb
p.skip(4);
@@ -885,7 +898,67 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
return ret;
}
private static int rand(int l, int u) {
return (int) ((Math.random() * (u - l + 1)) + l);
private AttackInfo parseMesoExplosion(InPacket p, AttackInfo attackInfo) {
p.skip(6);
Map<Integer, List<Integer>> targetDamage = new HashMap<>();
for (int i = 0; i < attackInfo.numAttacked; i++) {
int mobOid = p.readInt();
p.skip(4);
Point curPos = p.readPos();
Point nextPos = p.readPos();
int damageLines = p.readByte();
List<Integer> allDamageNumbers = new ArrayList<>();
for (int j = 0; j < damageLines; j++) {
int damage = p.readInt();
allDamageNumbers.add(damage);
}
p.skip(4);
targetDamage.put(mobOid, allDamageNumbers);
}
p.skip(4);
List<Integer> explodedMesos = new ArrayList<>();
int explodedMesoCount = p.readByte();
for (int j = 0; j < explodedMesoCount; j++) {
int mesoOid = p.readInt();
p.skip(1);
explodedMesos.add(mesoOid);
}
attackInfo.explodedMesos = explodedMesos;
final short attackDelay = p.readShort();
attackInfo.attackDelay = attackDelay;
Map<Integer, AttackTarget> targets = new HashMap<>();
targetDamage.forEach((id, damage) -> targets.put(id, new AttackTarget(attackDelay, damage)));
attackInfo.targets = targets;
return attackInfo;
}
private void removeExplodedMesos(MapleMap map, AttackInfo attack) {
int index = 0;
for (Integer mesoId : attack.explodedMesos) {
MapObject mapobject = map.getMapObject(mesoId);
if (!(mapobject instanceof MapItem mapItem)) {
return;
}
if (mapItem.getMeso() == 0) {
return;
}
mapItem.lockItem();
try {
if (mapItem.isPickedUp()) {
return;
}
int delay = attack.attackDelay + (index++ % 5) * EXPLODED_MESO_SPREAD_DELAY;
delay = Math.min(delay, EXPLODED_MESO_MAX_DELAY);
map.pickItemDrop(PacketCreator.removeExplodedMesoFromMap(mapItem.getObjectId(), (short) delay), mapItem);
} finally {
mapItem.unlockItem();
}
}
}
}

View File

@@ -26,7 +26,12 @@ import net.packet.InPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.maps.AnimatedMapObject;
import server.movement.*;
import server.movement.AbsoluteLifeMovement;
import server.movement.ChangeEquip;
import server.movement.JumpDownMovement;
import server.movement.LifeMovementFragment;
import server.movement.RelativeLifeMovement;
import server.movement.TeleportMovement;
import tools.exceptions.EmptyMovementException;
import java.awt.*;

View File

@@ -133,7 +133,7 @@ public final class AdminCommandHandler extends AbstractPacketHandler {
for (int x = 0; x < amount; x++) {
Monster monster = (Monster) monsterx.get(x);
if (monster.getId() == mobToKill) {
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true);
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true, (short) 0);
}
}
break;

View File

@@ -21,10 +21,13 @@
*/
package net.server.channel.handlers;
import client.*;
import client.BuddyList;
import client.BuddyList.BuddyAddResult;
import client.Character;
import client.BuddyList.BuddyOperation;
import client.BuddylistEntry;
import client.Character;
import client.CharacterNameAndId;
import client.Client;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import net.server.world.World;

View File

@@ -23,7 +23,14 @@ package net.server.channel.handlers;
import client.Client;
import client.SkillFactory;
import constants.skills.*;
import constants.skills.Bishop;
import constants.skills.Bowmaster;
import constants.skills.Corsair;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.ILArchMage;
import constants.skills.Marksman;
import constants.skills.WindArcher;
import net.AbstractPacketHandler;
import net.PacketHandler;
import net.packet.InPacket;
@@ -53,4 +60,4 @@ public final class CancelBuffHandler extends AbstractPacketHandler implements Pa
break;
}
}
}
}

View File

@@ -116,7 +116,7 @@ public final class CashOperationHandler extends AbstractPacketHandler {
CashItem cItem = CashItemFactory.getItem(p.readInt());
Map<String, String> recipient = Character.getCharacterFromDatabase(p.readString());
String message = p.readString();
if (!canBuy(chr, cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) {
if (!canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID)) || message.isEmpty() || message.length() > 73) {
c.enableCSActions();
return;
}
@@ -405,7 +405,7 @@ public final class CashOperationHandler extends AbstractPacketHandler {
c.sendPacket(PacketCreator.showCash(c.getPlayer()));
} else if (action == 0x2E) { //name change
CashItem cItem = CashItemFactory.getItem(p.readInt());
if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) {
if (cItem == null || !canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID))) {
c.sendPacket(PacketCreator.showCashShopMessage((byte) 0));
c.enableCSActions();
return;
@@ -434,7 +434,7 @@ public final class CashOperationHandler extends AbstractPacketHandler {
c.enableCSActions();
} else if (action == 0x31) { //world transfer
CashItem cItem = CashItemFactory.getItem(p.readInt());
if (cItem == null || !canBuy(chr, cItem, cs.getCash(4))) {
if (cItem == null || !canBuy(chr, cItem, cs.getCash(CashShop.NX_PREPAID))) {
c.sendPacket(PacketCreator.showCashShopMessage((byte) 0));
c.enableCSActions();
return;

View File

@@ -24,26 +24,34 @@ import client.inventory.Item;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import server.CashShop;
import server.CashShop.CashShopSurpriseResult;
import tools.PacketCreator;
import tools.Pair;
import java.util.Optional;
/**
* @author RonanLana
* @author Ponk
*/
public class CashShopSurpriseHandler extends AbstractPacketHandler {
@Override
public final void handlePacket(InPacket p, Client c) {
CashShop cs = c.getPlayer().getCashShop();
if (cs.isOpened()) {
Pair<Item, Item> cssResult = cs.openCashShopSurprise();
if (cssResult != null) {
Item cssItem = cssResult.getLeft(), cssBox = cssResult.getRight();
c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), cssBox.getSN(), cssBox.getQuantity(), cssItem, cssItem.getItemId(), cssItem.getQuantity(), true));
} else {
c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed());
}
if (!cs.isOpened()) {
return;
}
long cashId = p.readLong();
Optional<CashShopSurpriseResult> result = cs.openCashShopSurprise(cashId);
if (result.isEmpty()) {
c.sendPacket(PacketCreator.onCashItemGachaponOpenFailed());
return;
}
Item usedCashShopSurprise = result.get().usedCashShopSurprise();
Item reward = result.get().reward();
c.sendPacket(PacketCreator.onCashGachaponOpenSuccess(c.getAccID(), usedCashShopSurprise.getCashId(),
usedCashShopSurprise.getQuantity(), reward, reward.getItemId(), reward.getQuantity(), true));
}
}

View File

@@ -21,12 +21,22 @@
*/
package net.server.channel.handlers;
import client.BuffStat;
import client.Character;
import client.*;
import client.Client;
import client.Job;
import client.Skill;
import client.SkillFactory;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.id.MapId;
import constants.skills.*;
import constants.skills.Crusader;
import constants.skills.DawnWarrior;
import constants.skills.DragonKnight;
import constants.skills.Hero;
import constants.skills.NightWalker;
import constants.skills.Rogue;
import constants.skills.WindArcher;
import net.packet.InPacket;
import server.StatEffect;
import tools.PacketCreator;
@@ -68,7 +78,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
c.sendPacket(PacketCreator.getEnergy("energy", chr.getDojoEnergy()));
}
chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, attack.speed, attack.direction, attack.display), false, true);
chr.getMap().broadcastMessage(chr, PacketCreator.closeRangeAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, attack.targets, attack.speed, attack.direction,
attack.display), false, true);
int numFinisherOrbs = 0;
Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO);
if (GameConstants.isFinisherSkill(attack.skill)) {
@@ -129,9 +141,9 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
}
if (attack.numAttacked > 0 && attack.skill == DragonKnight.SACRIFICE) {
int totDamageToOneMonster = 0; // sacrifice attacks only 1 mob with 1 attack
final Iterator<List<Integer>> dmgIt = attack.allDamage.values().iterator();
final Iterator<AttackTarget> dmgIt = attack.targets.values().iterator();
if (dmgIt.hasNext()) {
totDamageToOneMonster = dmgIt.next().get(0);
totDamageToOneMonster = dmgIt.next().damageLines().getFirst();
}
chr.safeAddHP(-1 * totDamageToOneMonster * attack.getAttackEffect(chr, null).getX() / 100);
@@ -183,4 +195,4 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
applyAttack(attack, chr, attackCount);
}
}
}

View File

@@ -42,7 +42,11 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**

View File

@@ -25,7 +25,6 @@ import client.Character;
import client.Client;
import client.inventory.Equip;
import client.inventory.Item;
import client.processor.action.BuybackProcessor;
import config.YamlConfig;
import net.AbstractPacketHandler;
import net.packet.InPacket;
@@ -45,136 +44,132 @@ import java.util.List;
public final class EnterMTSHandler extends AbstractPacketHandler {
@Override
public final void handlePacket(InPacket p, Client c) {
public void handlePacket(InPacket p, Client c) {
Character chr = c.getPlayer();
if (!chr.isAlive() && YamlConfig.config.server.USE_BUYBACK_SYSTEM) {
BuybackProcessor.processBuyback(c);
if (!YamlConfig.config.server.USE_MTS) {
c.sendPacket(PacketCreator.enableActions());
} else {
if (!YamlConfig.config.server.USE_MTS) {
c.sendPacket(PacketCreator.enableActions());
return;
}
if (chr.getEventInstance() != null) {
c.sendPacket(PacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event."));
c.sendPacket(PacketCreator.enableActions());
return;
}
if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) {
c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
c.sendPacket(PacketCreator.enableActions());
return;
}
if (FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) {
chr.dropMessage(1, "You can't do it here in this map.");
c.sendPacket(PacketCreator.enableActions());
return;
}
if (!chr.isAlive()) {
c.sendPacket(PacketCreator.enableActions());
return;
}
if (chr.getLevel() < 10) {
c.sendPacket(PacketCreator.blockedMessage2(5));
c.sendPacket(PacketCreator.enableActions());
return;
}
chr.closePlayerInteractions();
chr.closePartySearchInteractions();
chr.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
chr.setAwayFromChannelWorld();
chr.notifyMapTransferToPartner(-1);
chr.removeIncomingInvites();
chr.cancelAllBuffs(true);
chr.cancelAllDebuffs();
chr.cancelBuffExpireTask();
chr.cancelDiseaseExpireTask();
chr.cancelSkillCooldownTask();
chr.cancelExpirationTask();
chr.forfeitExpirableQuests();
chr.cancelQuestExpirationTask();
chr.saveCharToDB();
c.getChannelServer().removePlayer(chr);
chr.getMap().removePlayer(c.getPlayer());
try {
c.sendPacket(PacketCreator.openCashShop(c, true));
} catch (Exception ex) {
ex.printStackTrace();
}
chr.getCashShop().open(true);// xD
c.enableCSActions();
c.sendPacket(PacketCreator.MTSWantedListingOver(0, 0));
c.sendPacket(PacketCreator.showMTSCash(c.getPlayer()));
List<MTSItemInfo> items = new ArrayList<>();
int pages = 0;
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_items WHERE tab = 1 AND transfer = 0 ORDER BY id DESC LIMIT 16, 16");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
if (rs.getInt("type") != 1) {
Item i = new Item(rs.getInt("itemid"), (short) 0, (short) rs.getInt("quantity"));
i.setOwner(rs.getString("owner"));
items.add(new MTSItemInfo(i, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends")));
} else {
Equip equip = new Equip(rs.getInt("itemid"), (byte) rs.getInt("position"), -1);
equip.setOwner(rs.getString("owner"));
equip.setQuantity((short) 1);
equip.setAcc((short) rs.getInt("acc"));
equip.setAvoid((short) rs.getInt("avoid"));
equip.setDex((short) rs.getInt("dex"));
equip.setHands((short) rs.getInt("hands"));
equip.setHp((short) rs.getInt("hp"));
equip.setInt((short) rs.getInt("int"));
equip.setJump((short) rs.getInt("jump"));
equip.setVicious((short) rs.getInt("vicious"));
equip.setFlag((short) rs.getInt("flag"));
equip.setLuk((short) rs.getInt("luk"));
equip.setMatk((short) rs.getInt("matk"));
equip.setMdef((short) rs.getInt("mdef"));
equip.setMp((short) rs.getInt("mp"));
equip.setSpeed((short) rs.getInt("speed"));
equip.setStr((short) rs.getInt("str"));
equip.setWatk((short) rs.getInt("watk"));
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(equip, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends")));
}
}
}
try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items");
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
pages = (int) Math.ceil(rs.getInt(1) / 16);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
c.sendPacket(PacketCreator.sendMTS(items, 1, 0, 0, pages));
c.sendPacket(PacketCreator.transferInventory(getTransfer(chr.getId())));
c.sendPacket(PacketCreator.notYetSoldInv(getNotYetSold(chr.getId())));
return;
}
if (chr.getEventInstance() != null) {
c.sendPacket(PacketCreator.serverNotice(5, "Entering Cash Shop or MTS are disabled when registered on an event."));
c.sendPacket(PacketCreator.enableActions());
return;
}
if (MiniDungeonInfo.isDungeonMap(chr.getMapId())) {
c.sendPacket(PacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon."));
c.sendPacket(PacketCreator.enableActions());
return;
}
if (FieldLimit.CANNOTMIGRATE.check(chr.getMap().getFieldLimit())) {
chr.dropMessage(1, "You can't do it here in this map.");
c.sendPacket(PacketCreator.enableActions());
return;
}
if (!chr.isAlive()) {
c.sendPacket(PacketCreator.enableActions());
return;
}
if (chr.getLevel() < 10) {
c.sendPacket(PacketCreator.blockedMessage2(5));
c.sendPacket(PacketCreator.enableActions());
return;
}
chr.closePlayerInteractions();
chr.closePartySearchInteractions();
chr.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases());
chr.setAwayFromChannelWorld();
chr.notifyMapTransferToPartner(-1);
chr.removeIncomingInvites();
chr.cancelAllBuffs(true);
chr.cancelAllDebuffs();
chr.cancelBuffExpireTask();
chr.cancelDiseaseExpireTask();
chr.cancelSkillCooldownTask();
chr.cancelExpirationTask();
chr.forfeitExpirableQuests();
chr.cancelQuestExpirationTask();
chr.saveCharToDB();
c.getChannelServer().removePlayer(chr);
chr.getMap().removePlayer(c.getPlayer());
try {
c.sendPacket(PacketCreator.openCashShop(c, true));
} catch (Exception ex) {
ex.printStackTrace();
}
chr.getCashShop().open(true);// xD
c.enableCSActions();
c.sendPacket(PacketCreator.MTSWantedListingOver(0, 0));
c.sendPacket(PacketCreator.showMTSCash(c.getPlayer()));
List<MTSItemInfo> items = new ArrayList<>();
int pages = 0;
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM mts_items WHERE tab = 1 AND transfer = 0 ORDER BY id DESC LIMIT 16, 16");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
if (rs.getInt("type") != 1) {
Item i = new Item(rs.getInt("itemid"), (short) 0, (short) rs.getInt("quantity"));
i.setOwner(rs.getString("owner"));
items.add(new MTSItemInfo(i, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends")));
} else {
Equip equip = new Equip(rs.getInt("itemid"), (byte) rs.getInt("position"), -1);
equip.setOwner(rs.getString("owner"));
equip.setQuantity((short) 1);
equip.setAcc((short) rs.getInt("acc"));
equip.setAvoid((short) rs.getInt("avoid"));
equip.setDex((short) rs.getInt("dex"));
equip.setHands((short) rs.getInt("hands"));
equip.setHp((short) rs.getInt("hp"));
equip.setInt((short) rs.getInt("int"));
equip.setJump((short) rs.getInt("jump"));
equip.setVicious((short) rs.getInt("vicious"));
equip.setFlag((short) rs.getInt("flag"));
equip.setLuk((short) rs.getInt("luk"));
equip.setMatk((short) rs.getInt("matk"));
equip.setMdef((short) rs.getInt("mdef"));
equip.setMp((short) rs.getInt("mp"));
equip.setSpeed((short) rs.getInt("speed"));
equip.setStr((short) rs.getInt("str"));
equip.setWatk((short) rs.getInt("watk"));
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(equip, rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1), rs.getInt("id"), rs.getInt("seller"), rs.getString("sellername"), rs.getString("sell_ends")));
}
}
}
try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items");
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
pages = (int) Math.ceil(rs.getInt(1) / 16);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
c.sendPacket(PacketCreator.sendMTS(items, 1, 0, 0, pages));
c.sendPacket(PacketCreator.transferInventory(getTransfer(chr.getId())));
c.sendPacket(PacketCreator.notYetSoldInv(getNotYetSold(chr.getId())));
}
private List<MTSItemInfo> getNotYetSold(int cid) {
@@ -276,4 +271,4 @@ public final class EnterMTSHandler extends AbstractPacketHandler {
}
return items;
}
}
}

View File

@@ -23,7 +23,11 @@ package net.server.channel.handlers;
import client.Character;
import client.Client;
import client.inventory.*;
import client.inventory.Equip;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ModifyInventory;
import config.YamlConfig;
import net.AbstractPacketHandler;
import net.packet.InPacket;

View File

@@ -35,6 +35,7 @@ import net.server.Server;
import net.server.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.CashShop;
import server.ItemInformationProvider;
import server.MTSItemInfo;
import tools.DatabaseConnection;
@@ -48,7 +49,6 @@ import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public final class MTSHandler extends AbstractPacketHandler {
@@ -403,7 +403,7 @@ public final class MTSHandler extends AbstractPacketHandler {
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int price = rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1); // taxes
if (c.getPlayer().getCashShop().getCash(4) >= price) { // FIX
if (c.getPlayer().getCashShop().getCash(CashShop.NX_PREPAID) >= price) { // FIX
boolean alwaysnull = true;
for (Channel cserv : Server.getInstance().getAllChannels()) {
Character victim = cserv.getPlayerStorage().getCharacterById(rs.getInt("seller"));
@@ -460,11 +460,11 @@ public final class MTSHandler extends AbstractPacketHandler {
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int price = rs.getInt("price") + 100 + (int) (rs.getInt("price") * 0.1);
if (c.getPlayer().getCashShop().getCash(4) >= price) {
if (c.getPlayer().getCashShop().getCash(CashShop.NX_PREPAID) >= price) {
for (Channel cserv : Server.getInstance().getAllChannels()) {
Character victim = cserv.getPlayerStorage().getCharacterById(rs.getInt("seller"));
if (victim != null) {
victim.getCashShop().gainCash(4, rs.getInt("price"));
victim.getCashShop().gainCash(CashShop.NX_PREPAID, rs.getInt("price"));
} else {
try (PreparedStatement pse = con.prepareStatement("SELECT accountid FROM characters WHERE id = ?")) {
pse.setInt(1, rs.getInt("seller"));

View File

@@ -21,8 +21,11 @@
*/
package net.server.channel.handlers;
import client.BuffStat;
import client.Character;
import client.*;
import client.Client;
import client.Skill;
import client.SkillFactory;
import config.YamlConfig;
import constants.id.MapId;
import constants.skills.Bishop;
@@ -63,7 +66,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
}
int charge = (attack.skill == Evan.FIRE_BREATH || attack.skill == Evan.ICE_BREATH || attack.skill == FPArchMage.BIG_BANG || attack.skill == ILArchMage.BIG_BANG || attack.skill == Bishop.BIG_BANG) ? attack.charge : -1;
Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, attack.allDamage, charge, attack.speed, attack.direction, attack.display);
Packet packet = PacketCreator.magicAttack(chr, attack.skill, attack.skilllevel, attack.stance,
attack.numAttackedAndDamage, attack.targets, charge, attack.speed, attack.direction, attack.display);
chr.getMap().broadcastMessage(chr, packet, false, true);
StatEffect effect = attack.getAttackEffect(chr, null);
@@ -81,8 +85,8 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
Skill eaterSkill = SkillFactory.getSkill((chr.getJob().getId() - (chr.getJob().getId() % 10)) * 10000);// MP Eater, works with right job
int eaterLevel = chr.getSkillLevel(eaterSkill);
if (eaterLevel > 0) {
for (Integer singleDamage : attack.allDamage.keySet()) {
eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(singleDamage), 0);
for (Integer oid : attack.targets.keySet()) {
eaterSkill.getEffect(eaterLevel).applyPassive(chr, chr.getMap().getMapObject(oid), 0);
}
}
}

View File

@@ -67,7 +67,8 @@ public final class MesoDropHandler extends AbstractPacketHandler {
if (player.attemptCatchFish(meso)) {
player.getMap().disappearingMesoDrop(meso, player, player, player.getPosition());
} else {
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2);
player.getMap().spawnMesoDrop(meso, player.getPosition(), player, player, true, (byte) 2,
(short) 0);
}
}
}
}

View File

@@ -23,23 +23,25 @@ import client.Character;
import client.Client;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import server.life.LifeFactory.BanishInfo;
import server.life.BanishInfo;
import server.life.Monster;
public final class MobBanishPlayerHandler extends AbstractPacketHandler {
@Override
public final void handlePacket(InPacket p, Client c) {
int mobid = p.readInt(); // mob banish handling detected thanks to MedicOP
public void handlePacket(InPacket p, Client c) {
int mobId = p.readInt(); // mob banish handling detected thanks to MedicOP
Character chr = c.getPlayer();
Monster mob = chr.getMap().getMonsterById(mobid);
if (mob != null) {
BanishInfo banishInfo = mob.getBanish();
if (banishInfo != null) {
chr.changeMapBanish(banishInfo.getMap(), banishInfo.getPortal(), banishInfo.getMsg());
}
Monster mob = chr.getMap().getMonsterById(mobId);
if (mob == null) {
return;
}
BanishInfo banishInfo = mob.getBanish();
if (banishInfo == null) {
return;
}
chr.changeMapBanish(banishInfo);
}
}
}

View File

@@ -57,21 +57,24 @@ public final class MobDamageMobHandler extends AbstractPacketHandler {
Monster attacker = map.getMonsterByOid(from);
Monster 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
String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId());
String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId());
log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(),
attackerName, damagedName, dmg, maxDmg);
dmg = maxDmg;
}
map.damageMonster(chr, damaged, dmg);
map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false);
if (attacker == null || damaged == null) {
return;
}
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
String attackerName = MonsterInformationProvider.getInstance().getMobNameFromId(attacker.getId());
String damagedName = MonsterInformationProvider.getInstance().getMobNameFromId(damaged.getId());
log.warn("Chr {} had hypnotized {} to attack {} with damage {} (max: {})", c.getPlayer().getName(),
attackerName, damagedName, dmg, maxDmg);
dmg = maxDmg;
}
map.damageMonster(chr, damaged, dmg);
map.broadcastMessage(chr, PacketCreator.damageMonster(to, dmg), false);
}
private static int calcMaxDamage(Monster attacker, Monster damaged, boolean magic) {

View File

@@ -27,7 +27,12 @@ import config.YamlConfig;
import net.packet.InPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.life.*;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillId;
import server.life.MobSkillType;
import server.life.Monster;
import server.life.MonsterInformationProvider;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MapleMap;
@@ -168,7 +173,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
if (banishPlayers != null) {
for (Character chr : banishPlayers) {
chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg());
chr.changeMapBanish(monster.getBanish());
}
}
}
@@ -176,4 +181,4 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
private static boolean inRangeInclusive(Byte pVal, Integer pMin, Integer pMax) {
return !(pVal < pMin) || (pVal > pMax);
}
}
}

View File

@@ -70,8 +70,6 @@ public final class NPCTalkHandler extends AbstractPacketHandler {
NPCScriptManager.getInstance().start(c, npc.getId(), "gachapon", null);
} else if (npc.getName().endsWith("Maple TV")) {
NPCScriptManager.getInstance().start(c, npc.getId(), "mapleTV", null);
} else if (YamlConfig.config.server.USE_REBIRTH_SYSTEM && npc.getId() == YamlConfig.config.server.REBIRTH_NPC_ID) {
NPCScriptManager.getInstance().start(c, npc.getId(), "rebirth", null);
} else {
boolean hasNpcScript = NPCScriptManager.getInstance().start(c, npc.getId(), oid, null);
if (!hasNpcScript) {
@@ -97,4 +95,4 @@ public final class NPCTalkHandler extends AbstractPacketHandler {
}
}
}
}
}

View File

@@ -39,8 +39,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.ItemInformationProvider;
import server.Trade;
import server.maps.*;
import server.maps.FieldLimit;
import server.maps.HiredMerchant;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MiniGame;
import server.maps.MiniGame.MiniGameType;
import server.maps.PlayerShop;
import server.maps.PlayerShopItem;
import server.maps.Portal;
import tools.PacketCreator;
import java.awt.*;

View File

@@ -21,9 +21,21 @@
*/
package net.server.channel.handlers;
import client.BuddyList;
import client.BuddylistEntry;
import client.Character;
import client.*;
import client.inventory.*;
import client.CharacterNameAndId;
import client.Client;
import client.Disease;
import client.Family;
import client.FamilyEntry;
import client.Mount;
import client.SkillFactory;
import client.inventory.Equip;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.Pet;
import client.keybind.KeyBinding;
import config.YamlConfig;
import constants.game.GameConstants;
@@ -56,8 +68,13 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
public final class PlayerLoggedinHandler extends AbstractPacketHandler {
@@ -431,11 +448,6 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler {
Entry::getValue
));
// Any npc be specified as the rebirth npc. Allow the npc to use custom scripts explicitly.
if (YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
npcsIds.put(YamlConfig.config.server.REBIRTH_NPC_ID, "Rebirth");
}
c.sendPacket(PacketCreator.setNPCScriptable(npcsIds));
}

View File

@@ -21,8 +21,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel.handlers;
import client.BuffStat;
import client.Character;
import client.*;
import client.Client;
import client.Skill;
import client.SkillFactory;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
@@ -32,7 +35,13 @@ import config.YamlConfig;
import constants.id.ItemId;
import constants.id.MapId;
import constants.inventory.ItemConstants;
import constants.skills.*;
import constants.skills.Aran;
import constants.skills.Buccaneer;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Shadower;
import constants.skills.ThunderBreaker;
import constants.skills.WindArcher;
import net.packet.InPacket;
import net.packet.Packet;
import org.slf4j.Logger;
@@ -74,17 +83,23 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
}
if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false);
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
applyAttack(attack, chr, 1);
} else if (attack.skill == ThunderBreaker.SHARK_WAVE && chr.getSkillLevel(ThunderBreaker.SHARK_WAVE) > 0) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false);
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
applyAttack(attack, chr, 1);
for (int i = 0; i < attack.numAttacked; i++) {
chr.handleEnergyChargeGain();
}
} else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_FENRIR || attack.skill == Aran.COMBO_TEMPEST) {
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false);
chr.getMap().broadcastMessage(chr, PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel,
attack.stance, attack.numAttackedAndDamage, 0, attack.targets, attack.speed,
attack.direction, attack.display), false);
if (attack.skill == Aran.COMBO_SMASH && chr.getCombo() >= 30) {
chr.setCombo((short) 0);
applyAttack(attack, chr, 1);
@@ -204,10 +219,14 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
case 3221001: // Pierce
case 5221004: // Rapid Fire
case 13111002: // KoC Hurricane
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display);
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.rangedirection,
attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed,
attack.direction, attack.display);
break;
default:
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display);
packet = PacketCreator.rangedAttack(chr, attack.skill, attack.skilllevel, attack.stance,
attack.numAttackedAndDamage, visProjectile, attack.targets, attack.speed,
attack.direction, attack.display);
break;
}
chr.getMap().broadcastMessage(chr, packet, false, true);
@@ -237,4 +256,4 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
}
}
}
}
}

View File

@@ -22,7 +22,23 @@
package net.server.channel.handlers;
import client.Client;
import constants.skills.*;
import constants.skills.Bishop;
import constants.skills.Bowmaster;
import constants.skills.Brawler;
import constants.skills.ChiefBandit;
import constants.skills.Corsair;
import constants.skills.DarkKnight;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.FPMage;
import constants.skills.Gunslinger;
import constants.skills.Hero;
import constants.skills.ILArchMage;
import constants.skills.Marksman;
import constants.skills.NightWalker;
import constants.skills.Paladin;
import constants.skills.ThunderBreaker;
import constants.skills.WindArcher;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import org.slf4j.Logger;
@@ -65,4 +81,4 @@ public final class SkillEffectHandler extends AbstractPacketHandler {
return;
}
}
}
}

View File

@@ -26,7 +26,13 @@ import client.Client;
import client.Skill;
import client.SkillFactory;
import config.YamlConfig;
import constants.skills.*;
import constants.skills.Brawler;
import constants.skills.Corsair;
import constants.skills.DarkKnight;
import constants.skills.Hero;
import constants.skills.Paladin;
import constants.skills.Priest;
import constants.skills.SuperGM;
import net.AbstractPacketHandler;
import net.packet.InPacket;
import net.server.Server;
@@ -153,4 +159,4 @@ public final class SpecialMoveHandler extends AbstractPacketHandler {
c.sendPacket(PacketCreator.enableActions());
}
}
}
}

View File

@@ -41,31 +41,14 @@ import server.life.MonsterInformationProvider;
import server.maps.Summon;
import tools.PacketCreator;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public final class SummonDamageHandler extends AbstractDealDamageHandler {
private static final Logger log = LoggerFactory.getLogger(SummonDamageHandler.class);
public final class SummonAttackEntry {
private final int monsterOid;
private final int damage;
public SummonAttackEntry(int monsterOid, int damage) {
this.monsterOid = monsterOid;
this.damage = damage;
}
public int getMonsterOid() {
return monsterOid;
}
public int getDamage() {
return damage;
}
}
public record SummonAttackTarget(int monsterOid, int damage, short delay) {}
@Override
public void handlePacket(InPacket p, Client c) {
@@ -86,17 +69,21 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
Skill summonSkill = SkillFactory.getSkill(summon.getSkill());
StatEffect summonEffect = summonSkill.getEffect(summon.getSkillLevel());
p.skip(4);
List<SummonAttackEntry> allDamage = new ArrayList<>();
List<SummonAttackTarget> targets = new ArrayList<>();
byte direction = p.readByte();
int numAttacked = p.readByte();
p.skip(8); // I failed lol (mob x,y and summon x,y), Thanks Gerald
for (int x = 0; x < numAttacked; x++) {
int monsterOid = p.readInt(); // attacked oid
p.skip(18);
p.skip(8);
Point curPos = p.readPos();
Point nextPos = p.readPos();
short delay = p.readShort();
int damage = p.readInt();
allDamage.add(new SummonAttackEntry(monsterOid, damage));
targets.add(new SummonAttackTarget(monsterOid, damage, delay));
}
player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(), direction, allDamage), summon.getPosition());
player.getMap().broadcastMessage(player, PacketCreator.summonAttack(player.getId(), summon.getObjectId(),
direction, targets), summon.getPosition());
if (player.getMap().isOwnershipRestricted(player)) {
return;
@@ -104,25 +91,28 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
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();
Monster target = player.getMap().getMonsterByOid(attackEntry.getMonsterOid());
if (target != null) {
if (damage > maxDmg) {
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit.");
final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(target.getId());
log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})",
c.getPlayer().getName(), summon.getSkill(), mobName, damage, 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);
}
}
player.getMap().damageMonster(player, target, damage);
for (SummonAttackTarget target : targets) {
int damage = target.damage();
Monster mob = player.getMap().getMonsterByOid(target.monsterOid());
if (mob == null) {
continue;
}
if (damage > maxDmg) {
AutobanFactory.DAMAGE_HACK.alert(c.getPlayer(), "Possible packet editing summon damage exploit.");
final String mobName = MonsterInformationProvider.getInstance().getMobNameFromId(mob.getId());
log.info("Possible exploit - chr {} used a summon of skillId {} to attack {} with damage {} (max: {})",
c.getPlayer().getName(), summon.getSkill(), mobName, damage, maxDmg);
damage = maxDmg;
}
if (damage > 0 && summonEffect.getMonsterStati().size() > 0) {
if (summonEffect.makeChanceResult()) {
mob.applyStatus(player, new MonsterStatusEffect(summonEffect.getMonsterStati(), summonSkill, null, false), summonEffect.isPoison(), 4000);
}
}
player.getMap().damageMonster(player, mob, damage, target.delay());
}
if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss

View File

@@ -21,8 +21,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel.handlers;
import client.BuffStat;
import client.Character;
import client.*;
import client.Client;
import client.Skill;
import client.SkillFactory;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
@@ -39,7 +42,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.StatEffect;
import server.life.LifeFactory.loseItem;
import server.life.*;
import server.life.MobAttackInfo;
import server.life.MobAttackInfoFactory;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillType;
import server.life.Monster;
import server.maps.MapObject;
import server.maps.MapleMap;
import tools.PacketCreator;
@@ -281,7 +289,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler {
}
for (Character player : banishPlayers) { // chill, if this list ever gets non-empty an attacker does exist, trust me :)
player.changeMapBanish(attacker.getBanish().getMap(), attacker.getBanish().getPortal(), attacker.getBanish().getMsg());
player.changeMapBanish(attacker.getBanish());
}
}
}

View File

@@ -28,7 +28,11 @@ import net.packet.InPacket;
import tools.DatabaseConnection;
import tools.PacketCreator;
import java.sql.*;
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 static java.util.concurrent.TimeUnit.DAYS;
@@ -81,4 +85,4 @@ public final class TransferNameHandler extends AbstractPacketHandler {
}
c.sendPacket(PacketCreator.sendNameTransferRules(0));
}
}
}

View File

@@ -29,7 +29,11 @@ import net.server.Server;
import tools.DatabaseConnection;
import tools.PacketCreator;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
/**
* @author Ronan
@@ -76,4 +80,4 @@ public final class TransferWorldHandler extends AbstractPacketHandler {
}
c.sendPacket(PacketCreator.sendWorldTransferRules(0, c));
}
}
}

View File

@@ -22,10 +22,22 @@
package net.server.channel.handlers;
import client.Character;
import client.*;
import client.creator.veteran.*;
import client.inventory.*;
import client.Client;
import client.Skill;
import client.SkillFactory;
import client.SkillMacro;
import client.creator.veteran.BowmanCreator;
import client.creator.veteran.MagicianCreator;
import client.creator.veteran.PirateCreator;
import client.creator.veteran.ThiefCreator;
import client.creator.veteran.WarriorCreator;
import client.inventory.Equip;
import client.inventory.Equip.ScrollResult;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ModifyInventory;
import client.inventory.Pet;
import client.inventory.manipulator.InventoryManipulator;
import client.inventory.manipulator.KarmaManipulator;
import client.processor.npc.DueyProcessor;
@@ -46,7 +58,12 @@ import server.ItemInformationProvider;
import server.Shop;
import server.ShopFactory;
import server.TimerManager;
import server.maps.*;
import server.maps.AbstractMapObject;
import server.maps.FieldLimit;
import server.maps.Kite;
import server.maps.MapleMap;
import server.maps.MapleTVEffect;
import server.maps.PlayerShopItem;
import service.NoteService;
import tools.PacketCreator;
import tools.Pair;

View File

@@ -61,7 +61,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
case ItemId.PHEROMONE_PERFUME:
if (mob.getId() == MobId.TAMABLE_HOG) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.HOG, (short) 1, "", -1);
}
@@ -72,7 +72,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if ((abm.getLastSpam(10) + 1000) < currentServerTime()) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.GHOST_SACK, (short) 1, "", -1);
} else {
@@ -90,9 +90,10 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (chr.canHold(ItemId.ARPQ_SPIRIT_JEWEL, 1)) {
if (Math.random() < 0.5) { // 50% chance
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.ARPQ_SPIRIT_JEWEL, (short) 1, "", -1);
chr.updateAriantScore();
} else {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 0));
}
@@ -112,7 +113,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (mob.getId() == MobId.LOST_RUDOLPH) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.TAMED_RUDOLPH, (short) 1, "", -1);
} else {
@@ -125,7 +126,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (mob.getId() == MobId.KING_SLIME_DOJO) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_1, (short) 1, "", -1);
} else {
@@ -138,7 +139,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (mob.getId() == MobId.FAUST_DOJO) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_2, (short) 1, "", -1);
} else {
@@ -151,7 +152,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (mob.getId() == MobId.MUSHMOM_DOJO) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 3)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.MONSTER_MARBLE_3, (short) 1, "", -1);
} else {
@@ -164,7 +165,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (mob.getId() == MobId.POISON_FLOWER) {
if (mob.getHp() < ((mob.getMaxHp() / 10) * 4)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.EPQ_MONSTER_MARBLE, (short) 1, "", -1);
} else {
@@ -178,7 +179,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if ((abm.getLastSpam(10) + 3000) < currentServerTime()) {
abm.spam(10);
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, ItemId.FISH_NET_WITH_A_CATCH, (short) 1, "", -1);
} else {
@@ -201,7 +202,7 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
if (timeCatch != 0 && (abm.getLastSpam(10) + timeCatch) < currentServerTime()) {
if (mobHp != 0 && mob.getHp() < ((mob.getMaxHp() / 100) * mobHp)) {
chr.getMap().broadcastMessage(PacketCreator.catchMonster(monsterid, itemId, (byte) 1));
mob.getMap().killMonster(mob, null, false);
killMonster(mob);
InventoryManipulator.removeById(c, InventoryType.USE, itemId, 1, true, true);
InventoryManipulator.addById(c, itemGanho, (short) 1, "", -1);
} else if (mob.getId() != MobId.P_JUNIOR) {
@@ -219,4 +220,8 @@ public final class UseCatchItemHandler extends AbstractPacketHandler {
// System.out.println("UseCatchItemHandler: \r\n" + slea.toString());
}
}
private static void killMonster(Monster mob) {
mob.getMap().killMonster(mob, null, false, (short) 0);
}
}

View File

@@ -27,7 +27,6 @@ import client.Disease;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.manipulator.InventoryManipulator;
import config.YamlConfig;
import constants.id.ItemId;
import constants.inventory.ItemConstants;
import net.AbstractPacketHandler;
@@ -73,23 +72,8 @@ public final class UseItemHandler extends AbstractPacketHandler {
remove(c, slot);
return;
} else if (ItemConstants.isTownScroll(itemId)) {
int banMap = chr.getMapId();
int banSp = chr.getMap().findClosestPlayerSpawnpoint(chr.getPosition()).getId();
long banTime = currentServerTime();
if (ii.getItemEffect(toUse.getItemId()).applyTo(chr)) {
if (YamlConfig.config.server.USE_BANISHABLE_TOWN_SCROLL) {
chr.setBanishPlayerData(banMap, banSp, banTime);
}
remove(c, slot);
}
return;
} else if (ItemConstants.isAntibanishScroll(itemId)) {
if (ii.getItemEffect(toUse.getItemId()).applyTo(chr)) {
remove(c, slot);
} else {
chr.dropMessage(5, "You cannot recover from a banish state at the moment.");
}
return;
}

View File

@@ -25,8 +25,12 @@ import net.server.Server;
import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType;
import net.server.world.World;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Semaphore;
/**

View File

@@ -33,8 +33,14 @@ import provider.wz.WZFiles;
import tools.PacketCreator;
import tools.Pair;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

View File

@@ -22,7 +22,11 @@ package net.server.coordinator.partysearch;
import client.Character;
import tools.IntervalBuilder;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

View File

@@ -32,8 +32,14 @@ import tools.DatabaseConnection;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

View File

@@ -4,7 +4,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tools.DatabaseConnection;
import java.sql.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

View File

@@ -27,8 +27,14 @@ import server.life.Monster;
import server.maps.MapleMap;
import tools.Pair;
import java.util.*;
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.Map.Entry;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

View File

@@ -42,7 +42,13 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

View File

@@ -37,7 +37,13 @@ import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.*;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Calendar;
public final class LoginPasswordHandler implements PacketHandler {

View File

@@ -29,7 +29,11 @@ import net.packet.InPacket;
import net.server.Server;
import tools.PacketCreator;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
public final class ViewAllCharHandler extends AbstractPacketHandler {
private static final int CHARACTER_LIMIT = 60; // Client will crash if sending 61 or more characters

View File

@@ -24,7 +24,11 @@ import net.server.Server;
import server.TimerManager;
import tools.Pair;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;

View File

@@ -22,7 +22,12 @@ package net.server.services.type;
import net.server.services.BaseService;
import net.server.services.Service;
import net.server.services.ServiceType;
import net.server.services.task.channel.*;
import net.server.services.task.channel.EventService;
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;
/**
* @author Ronan

View File

@@ -32,7 +32,12 @@ import server.maps.MapleMap;
import server.partyquest.MonsterCarnival;
import tools.PacketCreator;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

View File

@@ -46,21 +46,57 @@ import net.server.guild.GuildSummary;
import net.server.services.BaseService;
import net.server.services.ServicesManager;
import net.server.services.type.WorldServices;
import net.server.task.*;
import net.server.task.CharacterAutosaverTask;
import net.server.task.CharacterHpDecreaseTask;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scripting.event.EventInstanceManager;
import server.Storage;
import server.TimerManager;
import server.maps.*;
import server.maps.AbstractMapObject;
import server.maps.HiredMerchant;
import server.maps.MapleMap;
import server.maps.MiniDungeon;
import server.maps.MiniDungeonInfo;
import server.maps.PlayerShop;
import server.maps.PlayerShopItem;
import tools.DatabaseConnection;
import tools.PacketCreator;
import tools.Pair;
import tools.packets.Fishing;
import java.sql.*;
import java.util.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -69,7 +105,10 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.util.concurrent.TimeUnit.*;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* @author kevintjuh93

View File

@@ -278,6 +278,10 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
getPlayer().gainMeso(gain);
}
public void gainMeso(Double gain) {
getPlayer().gainMeso(gain.intValue());
}
public void gainExp(int gain) {
getPlayer().gainExp(gain, true, true);
}

View File

@@ -33,6 +33,7 @@ import server.TimerManager;
import server.life.LifeFactory;
import server.life.Monster;
import server.maps.MapMonitor;
import server.maps.MapleMap;
import server.maps.Reactor;
import server.maps.ReactorDropEntry;
import server.partyquest.CarnivalFactory;
@@ -43,7 +44,6 @@ import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
/**
* @author Lerk
@@ -52,7 +52,6 @@ import java.util.concurrent.ScheduledFuture;
public class ReactorActionManager extends AbstractPlayerInteraction {
private final Reactor reactor;
private final Invocable iv;
private ScheduledFuture<?> sprayTask = null;
public ReactorActionManager(Client c, Reactor reactor, Invocable iv) {
super(c);
@@ -172,7 +171,8 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
int range = maxMeso - minMeso;
int displayDrop = (int) (Math.random() * range) + minMeso;
int mesoDrop = (displayDrop * c.getWorldServer().getMesoRate());
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos, reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2);
reactor.getMap().spawnMesoDrop(mesoDrop, reactor.getMap().calcDropPos(dropPos,
reactor.getPosition()), reactor, c.getPlayer(), false, (byte) 2, (short) 0);
} else {
Item drop;
@@ -182,31 +182,24 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
}
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid);
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, (short) 0);
}
}
} else {
final Reactor r = reactor;
final List<ReactorDropEntry> dropItems = items;
final int worldMesoRate = c.getWorldServer().getMesoRate();
dropPos.x -= (12 * items.size());
sprayTask = TimerManager.getInstance().register(() -> {
if (dropItems.isEmpty()) {
sprayTask.cancel(false);
return;
}
ReactorDropEntry d = dropItems.remove(0);
short delay = 0;
for (ReactorDropEntry d : items) {
if (d.itemId == 0) {
int range = maxMeso - minMeso;
int displayDrop = (int) (Math.random() * range) + minMeso;
int mesoDrop = (displayDrop * worldMesoRate);
r.getMap().spawnMesoDrop(mesoDrop, r.getMap().calcDropPos(dropPos, r.getPosition()), r, chr, false, (byte) 2);
int mesoDrop = displayDrop * worldMesoRate;
MapleMap map = reactor.getMap();
map.spawnMesoDrop(mesoDrop, map.calcDropPos(dropPos, reactor.getPosition()), reactor, chr,
false, (byte) 2, delay);
} else {
Item drop;
final Item drop;
if (ItemConstants.getInventoryType(d.itemId) != InventoryType.EQUIP) {
drop = new Item(d.itemId, (short) 0, (short) 1);
} else {
@@ -214,11 +207,12 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
drop = ii.randomizeStats((Equip) ii.getEquipById(d.itemId));
}
r.getMap().dropFromReactor(getPlayer(), r, drop, dropPos, (short) d.questid);
reactor.getMap().dropFromReactor(getPlayer(), reactor, drop, dropPos, (short) d.questid, delay);
}
dropPos.x += 25;
}, 200);
delay += 200;
}
}
}
@@ -333,4 +327,4 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
getPlayer().getMap().getBlueTeamBuffs().remove(skil);
}
}
}
}

View File

@@ -21,10 +21,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server;
import client.inventory.*;
import client.inventory.Equip;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.Pet;
import config.YamlConfig;
import constants.id.ItemId;
import constants.inventory.ItemConstants;
import net.jcip.annotations.GuardedBy;
import net.server.Server;
import provider.Data;
import provider.DataProvider;
@@ -38,7 +43,13 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -47,8 +58,74 @@ import static java.util.concurrent.TimeUnit.HOURS;
/*
* @author Flav
* @author Ponk
*/
public class CashShop {
public static final int NX_CREDIT = 1;
public static final int MAPLE_POINT = 2;
public static final int NX_PREPAID = 4;
private final int accountId;
private final int characterId;
private int nxCredit;
private int maplePoint;
private int nxPrepaid;
private boolean opened;
private ItemFactory factory;
private final List<Item> inventory = new ArrayList<>();
private final List<Integer> wishList = new ArrayList<>();
private int notes = 0;
private final Lock lock = new ReentrantLock();
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
this.accountId = accountId;
this.characterId = characterId;
if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) {
switch (jobType) {
case 0:
factory = ItemFactory.CASH_EXPLORER;
break;
case 1:
factory = ItemFactory.CASH_CYGNUS;
break;
case 2:
factory = ItemFactory.CASH_ARAN;
break;
}
} else {
factory = ItemFactory.CASH_OVERALL;
}
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) {
ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
this.nxCredit = rs.getInt("nxCredit");
this.maplePoint = rs.getInt("maplePoint");
this.nxPrepaid = rs.getInt("nxPrepaid");
}
}
}
for (Pair<Item, InventoryType> item : factory.loadItems(accountId, false)) {
inventory.add(item.getLeft());
}
try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) {
ps.setInt(1, characterId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
wishList.add(rs.getInt("sn"));
}
}
}
}
}
public static class CashItem {
private final int sn;
@@ -104,19 +181,20 @@ public class CashShop {
if (ItemConstants.EXPIRING_ITEMS) {
if (period == 1) {
switch (itemId) {
case ItemId.DROP_COUPON_2X_4H, ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4));
case ItemId.DROP_COUPON_2X_4H,
ItemId.EXP_COUPON_2X_4H: // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(4));
/*
} else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(3));
*/
break;
case ItemId.EXP_COUPON_3X_2H:
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2));
break;
default:
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1));
break;
break;
case ItemId.EXP_COUPON_3X_2H:
item.setExpiration(Server.getInstance().getCurrentTime() + HOURS.toMillis(2));
break;
default:
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(1));
break;
}
} else {
item.setExpiration(Server.getInstance().getCurrentTime() + DAYS.toMillis(period));
@@ -154,7 +232,6 @@ public class CashShop {
public static class CashItemFactory {
private static volatile Map<Integer, CashItem> items = new HashMap<>();
private static volatile List<Integer> randomitemsns = new ArrayList<>();
private static volatile Map<Integer, List<Integer>> packages = new HashMap<>();
private static volatile List<SpecialCashItem> specialcashitems = new ArrayList<>();
@@ -162,7 +239,6 @@ public class CashShop {
DataProvider etc = DataProviderFactory.getDataProvider(WZFiles.ETC);
Map<Integer, CashItem> loadedItems = new HashMap<>();
List<Integer> onSaleItems = new ArrayList<>();
for (Data item : etc.getData("Commodity.img").getChildren()) {
int sn = DataTool.getIntConvert("SN", item);
int itemId = DataTool.getIntConvert("ItemId", item);
@@ -171,13 +247,8 @@ public class CashShop {
short count = (short) DataTool.getIntConvert("Count", item, 1);
boolean onSale = DataTool.getIntConvert("OnSale", item, 0) == 1;
loadedItems.put(sn, new CashItem(sn, itemId, price, period, count, onSale));
if (onSale) {
onSaleItems.add(sn);
}
}
CashItemFactory.items = loadedItems;
CashItemFactory.randomitemsns = onSaleItems;
Map<Integer, List<Integer>> loadedPackages = new HashMap<>();
for (Data cashPackage : etc.getData("CashPackage.img").getChildren()) {
@@ -204,13 +275,20 @@ public class CashShop {
CashItemFactory.specialcashitems = loadedSpecialItems;
}
public static CashItem getRandomCashItem() {
if (randomitemsns.isEmpty()) {
return null;
public static Optional<CashItem> getRandomCashItem() {
if (items.isEmpty()) {
return Optional.empty();
}
int rnd = (int) (Math.random() * randomitemsns.size());
return items.get(randomitemsns.get(rnd));
List<CashItem> itemPool = items.values().stream()
.filter(CashItem::isOnSale)
.filter(cashItem -> !ItemId.isCashPackage(cashItem.itemId))
.toList();
return Optional.of(getRandomItem(itemPool));
}
private static CashItem getRandomItem(List<CashItem> items) {
return items.get(new Random().nextInt(items.size()));
}
public static CashItem getItem(int sn) {
@@ -234,107 +312,26 @@ public class CashShop {
public static List<SpecialCashItem> getSpecialCashItems() {
return specialcashitems;
}
public static void reloadSpecialCashItems() {//Yay?
List<SpecialCashItem> loadedSpecialItems = new ArrayList<>();
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM specialcashitems");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
loadedSpecialItems.add(new SpecialCashItem(rs.getInt("sn"), rs.getInt("modifier"), rs.getByte("info")));
}
} catch (SQLException ex) {
ex.printStackTrace();
}
CashItemFactory.specialcashitems = loadedSpecialItems;
}
}
private final int accountId;
private final int characterId;
private int nxCredit;
private int maplePoint;
private int nxPrepaid;
private boolean opened;
private ItemFactory factory;
private final List<Item> inventory = new ArrayList<>();
private final List<Integer> wishList = new ArrayList<>();
private int notes = 0;
private final Lock lock = new ReentrantLock();
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
this.accountId = accountId;
this.characterId = characterId;
if (!YamlConfig.config.server.USE_JOINT_CASHSHOP_INVENTORY) {
switch (jobType) {
case 0:
factory = ItemFactory.CASH_EXPLORER;
break;
case 1:
factory = ItemFactory.CASH_CYGNUS;
break;
case 2:
factory = ItemFactory.CASH_ARAN;
break;
}
} else {
factory = ItemFactory.CASH_OVERALL;
}
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT `nxCredit`, `maplePoint`, `nxPrepaid` FROM `accounts` WHERE `id` = ?")) {
ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
this.nxCredit = rs.getInt("nxCredit");
this.maplePoint = rs.getInt("maplePoint");
this.nxPrepaid = rs.getInt("nxPrepaid");
}
}
}
for (Pair<Item, InventoryType> item : factory.loadItems(accountId, false)) {
inventory.add(item.getLeft());
}
try (PreparedStatement ps = con.prepareStatement("SELECT `sn` FROM `wishlists` WHERE `charid` = ?")) {
ps.setInt(1, characterId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
wishList.add(rs.getInt("sn"));
}
}
}
}
public record CashShopSurpriseResult(Item usedCashShopSurprise, Item reward) {
}
public int getCash(int type) {
switch (type) {
case 1:
return nxCredit;
case 2:
return maplePoint;
case 4:
return nxPrepaid;
}
return switch (type) {
case NX_CREDIT -> nxCredit;
case MAPLE_POINT -> maplePoint;
case NX_PREPAID -> nxPrepaid;
default -> 0;
};
return 0;
}
public void gainCash(int type, int cash) {
switch (type) {
case 1:
nxCredit += cash;
break;
case 2:
maplePoint += cash;
break;
case 4:
nxPrepaid += cash;
break;
case NX_CREDIT -> nxCredit += cash;
case MAPLE_POINT -> maplePoint += cash;
case NX_PREPAID -> nxPrepaid += cash;
}
}
@@ -517,47 +514,57 @@ public class CashShop {
}
}
private Item getCashShopItemByItemid(int itemid) {
public Optional<CashShopSurpriseResult> openCashShopSurprise(long cashId) {
lock.lock();
try {
for (Item it : inventory) {
if (it.getItemId() == itemid) {
return it;
}
Optional<Item> maybeCashShopSurprise = getItemByCashId(cashId);
if (maybeCashShopSurprise.isEmpty() ||
maybeCashShopSurprise.get().getItemId() != ItemId.CASH_SHOP_SURPRISE) {
return Optional.empty();
}
Item cashShopSurprise = maybeCashShopSurprise.get();
if (cashShopSurprise.getQuantity() <= 0) {
return Optional.empty();
}
if (getItemsSize() >= 100) {
return Optional.empty();
}
Optional<CashItem> cashItemReward = CashItemFactory.getRandomCashItem();
if (cashItemReward.isEmpty()) {
return Optional.empty();
}
short newQuantity = (short) (cashShopSurprise.getQuantity() - 1);
cashShopSurprise.setQuantity(newQuantity);
if (newQuantity <= 0) {
removeFromInventory(cashShopSurprise);
}
Item itemReward = cashItemReward.get().toItem();
addToInventory(itemReward);
return Optional.of(new CashShopSurpriseResult(cashShopSurprise, itemReward));
} finally {
lock.unlock();
}
return null;
}
public synchronized Pair<Item, Item> openCashShopSurprise() {
Item css = getCashShopItemByItemid(ItemId.CASH_SHOP_SURPRISE);
@GuardedBy("lock")
private Optional<Item> getItemByCashId(long cashId) {
return inventory.stream()
.filter(item -> item.getCashId() == cashId)
.findAny();
}
if (css != null) {
CashItem cItem = CashItemFactory.getRandomCashItem();
if (cItem != null) {
if (css.getQuantity() > 1) {
/* if(NOT ENOUGH SPACE) { looks like we're not dealing with cash inventory limit whatsoever, k then
return null;
} */
css.setQuantity((short) (css.getQuantity() - 1));
} else {
removeFromInventory(css);
}
Item item = cItem.toItem();
addToInventory(item);
return new Pair<>(item, css);
} else {
return null;
}
} else {
return null;
public int getItemsSize() {
lock.lock();
try {
return inventory.size();
} finally {
lock.unlock();
}
}

View File

@@ -1,19 +1,21 @@
package server;
import config.YamlConfig;
import tools.DatabaseConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.sql.Timestamp;
import static java.util.concurrent.TimeUnit.*;
import config.YamlConfig;
import tools.DatabaseConnection;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
public class ExpLogger {
private static final LinkedBlockingQueue<ExpLogRecord> expLoggerQueue = new LinkedBlockingQueue<>();

View File

@@ -21,8 +21,13 @@
*/
package server;
import client.BuffStat;
import client.Character;
import client.*;
import client.Disease;
import client.Job;
import client.Mount;
import client.Skill;
import client.SkillFactory;
import client.inventory.Inventory;
import client.inventory.InventoryType;
import client.inventory.Item;
@@ -33,7 +38,57 @@ import config.YamlConfig;
import constants.id.ItemId;
import constants.id.MapId;
import constants.inventory.ItemConstants;
import constants.skills.*;
import constants.skills.Aran;
import constants.skills.Assassin;
import constants.skills.Bandit;
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.ChiefBandit;
import constants.skills.Cleric;
import constants.skills.Corsair;
import constants.skills.Crossbowman;
import constants.skills.Crusader;
import constants.skills.DarkKnight;
import constants.skills.DawnWarrior;
import constants.skills.DragonKnight;
import constants.skills.Evan;
import constants.skills.FPArchMage;
import constants.skills.FPMage;
import constants.skills.FPWizard;
import constants.skills.Fighter;
import constants.skills.GM;
import constants.skills.Gunslinger;
import constants.skills.Hermit;
import constants.skills.Hero;
import constants.skills.Hunter;
import constants.skills.ILArchMage;
import constants.skills.ILMage;
import constants.skills.ILWizard;
import constants.skills.Legend;
import constants.skills.Magician;
import constants.skills.Marauder;
import constants.skills.Marksman;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Noblesse;
import constants.skills.Outlaw;
import constants.skills.Page;
import constants.skills.Paladin;
import constants.skills.Pirate;
import constants.skills.Priest;
import constants.skills.Ranger;
import constants.skills.Rogue;
import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Spearman;
import constants.skills.SuperGM;
import constants.skills.ThunderBreaker;
import constants.skills.WhiteKnight;
import constants.skills.WindArcher;
import net.packet.Packet;
import net.server.Server;
import net.server.world.Party;
@@ -44,15 +99,27 @@ import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillType;
import server.life.Monster;
import server.maps.*;
import server.maps.Door;
import server.maps.FieldLimit;
import server.maps.MapObject;
import server.maps.MapObjectType;
import server.maps.MapleMap;
import server.maps.Mist;
import server.maps.Portal;
import server.maps.Summon;
import server.maps.SummonMovementType;
import server.partyquest.CarnivalFactory;
import server.partyquest.CarnivalFactory.MCSkill;
import tools.PacketCreator;
import tools.Pair;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.*;
import java.util.Map;
/**
* @author Matze
@@ -910,18 +977,8 @@ public class StatEffect {
Portal pt;
if (moveTo == MapId.NONE) {
if (sourceid != ItemId.ANTI_BANISH_SCROLL) {
target = applyto.getMap().getReturnMap();
pt = target.getRandomPlayerSpawnpoint();
} else {
if (!applyto.canRecoverLastBanish()) {
return false;
}
Pair<Integer, Integer> lastBanishInfo = applyto.getLastBanishData();
target = applyto.getWarpMap(lastBanishInfo.getLeft());
pt = target.getPortal(lastBanishInfo.getRight());
}
target = applyto.getMap().getReturnMap();
pt = target.getRandomPlayerSpawnpoint();
} else {
target = applyto.getClient().getWorldServer().getChannel(applyto.getClient().getChannel()).getMapFactory().getMap(moveTo);
int targetid = target.getId() / 10000000;

View File

@@ -38,7 +38,12 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -358,4 +363,4 @@ public class Storage {
}
}
}
}

View File

@@ -19,7 +19,11 @@
*/
package server;
import java.util.concurrent.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;

Some files were not shown because too many files have changed in this diff Show More