mirror of
https://github.com/mcMMO-Dev/mcMMO.git
synced 2026-02-19 02:12:58 +01:00
Compare commits
427 Commits
1.13-suppo
...
translatio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
483553b89d | ||
|
|
6047a5579b | ||
|
|
a94db1b88a | ||
|
|
f5a397d2d2 | ||
|
|
05af15c06b | ||
|
|
0a59b79ef9 | ||
|
|
441125dbd1 | ||
|
|
eff1ce102f | ||
|
|
705b57a30b | ||
|
|
d4ba9d7605 | ||
|
|
4cd91350db | ||
|
|
6168309ec9 | ||
|
|
95c403a467 | ||
|
|
a598796c99 | ||
|
|
a333f36fd8 | ||
|
|
7c6d5c476d | ||
|
|
e2073ff9f7 | ||
|
|
8f26544188 | ||
|
|
4e21f1a200 | ||
|
|
ab6dbe306d | ||
|
|
38d64f207d | ||
|
|
8e8024e3e1 | ||
|
|
4f161812aa | ||
|
|
11ca0d9ff1 | ||
|
|
4eef4a3e41 | ||
|
|
816b64cb70 | ||
|
|
dceee5554d | ||
|
|
8094be46c8 | ||
|
|
2525ce9abe | ||
|
|
ea70c937f2 | ||
|
|
550a3df616 | ||
|
|
657abb76a7 | ||
|
|
0ffcff53b3 | ||
|
|
33f4ac14b5 | ||
|
|
15d3119627 | ||
|
|
4fd94bfe29 | ||
|
|
ac731258c7 | ||
|
|
7b8c90d362 | ||
|
|
055391e908 | ||
|
|
cd6ce5a19d | ||
|
|
7441d2d8d6 | ||
|
|
7b941baa1c | ||
|
|
0c1fa07079 | ||
|
|
539cd7290e | ||
|
|
406429f4e3 | ||
|
|
421a394f68 | ||
|
|
d4adb490e2 | ||
|
|
de5a8babc5 | ||
|
|
d892bfe83a | ||
|
|
ceaff0c862 | ||
|
|
a118d8465e | ||
|
|
7c156319be | ||
|
|
24b3bf1100 | ||
|
|
41bdca948a | ||
|
|
3cbbf1bee0 | ||
|
|
8fd1af4cbf | ||
|
|
601297799f | ||
|
|
da98be88ad | ||
|
|
1104f48ad5 | ||
|
|
af50699de1 | ||
|
|
1c71f1daf5 | ||
|
|
b7dd491c01 | ||
|
|
3ce0d7b972 | ||
|
|
9dcbccb010 | ||
|
|
c5cbab39b6 | ||
|
|
ca1906fbc5 | ||
|
|
7eab20ef56 | ||
|
|
188b0f9813 | ||
|
|
e6c9cc6fdd | ||
|
|
76ca7cc88f | ||
|
|
335d708848 | ||
|
|
b4179cb9a6 | ||
|
|
8bbf95e9da | ||
|
|
dc805825de | ||
|
|
871ca744d9 | ||
|
|
1b091bdbd5 | ||
|
|
110d9a633b | ||
|
|
584859318b | ||
|
|
1eb2c7b2d7 | ||
|
|
3310a12a95 | ||
|
|
6125b3fbd2 | ||
|
|
0d6b5e2530 | ||
|
|
d679101cfb | ||
|
|
122c8b0faa | ||
|
|
08d1ad4f6c | ||
|
|
8e19e1de15 | ||
|
|
101b0671fe | ||
|
|
e14c53d3ca | ||
|
|
28c0215a93 | ||
|
|
51a373db4f | ||
|
|
1b889b8177 | ||
|
|
4a4124d09f | ||
|
|
ceaf3d46cb | ||
|
|
260d0f9b4a | ||
|
|
d3a7ff8e8c | ||
|
|
9154b3f083 | ||
|
|
c3548be380 | ||
|
|
67bfb40dd5 | ||
|
|
777a9d4185 | ||
|
|
fcd45f3bc1 | ||
|
|
478d542981 | ||
|
|
c6c1c39a98 | ||
|
|
0d6165c0f4 | ||
|
|
f45983949a | ||
|
|
cca0524441 | ||
|
|
e84e9a7dc8 | ||
|
|
a3600e11a9 | ||
|
|
483579503a | ||
|
|
d89ff8b238 | ||
|
|
582ff48b6f | ||
|
|
d6e14ca431 | ||
|
|
0961351ba2 | ||
|
|
0b93a74652 | ||
|
|
2bc2e5bb32 | ||
|
|
98f8e049f3 | ||
|
|
e27d299132 | ||
|
|
7cf40d9dbf | ||
|
|
d6eefac065 | ||
|
|
3036c4ef80 | ||
|
|
a81468ccfc | ||
|
|
2a2a4479ca | ||
|
|
289276d987 | ||
|
|
f771b777b9 | ||
|
|
e4d081f14b | ||
|
|
bba35f8a4e | ||
|
|
8a4410cde8 | ||
|
|
8951c1fd21 | ||
|
|
1f7f71e8b5 | ||
|
|
88b056c38f | ||
|
|
d408782acf | ||
|
|
dfeab6b558 | ||
|
|
f4fe25009d | ||
|
|
905010aacd | ||
|
|
5ea2c493e8 | ||
|
|
734c6f9c62 | ||
|
|
44a3238c79 | ||
|
|
139975958f | ||
|
|
52f9273fa8 | ||
|
|
77ffee2515 | ||
|
|
40118d570c | ||
|
|
8109ecb0e0 | ||
|
|
1147dff4c3 | ||
|
|
c274b21af1 | ||
|
|
2b72e8e69f | ||
|
|
ca511ca0f5 | ||
|
|
cafacc05ab | ||
|
|
2974ed0993 | ||
|
|
861552bcd8 | ||
|
|
7095ddb611 | ||
|
|
f70f2321f2 | ||
|
|
4bb8e20b59 | ||
|
|
e8165321e1 | ||
|
|
4f6706b664 | ||
|
|
665a31fc85 | ||
|
|
1221069854 | ||
|
|
054f8ffe0a | ||
|
|
dbecd289b2 | ||
|
|
bb7989449e | ||
|
|
296c2e99f7 | ||
|
|
4246f59d44 | ||
|
|
87ff69d5c4 | ||
|
|
84cb40521b | ||
|
|
dda894dd9a | ||
|
|
7853efc63d | ||
|
|
11409c07c0 | ||
|
|
7c17126ffb | ||
|
|
05e19b2122 | ||
|
|
3b8fee5134 | ||
|
|
4735b2ff8f | ||
|
|
ac29c51d6e | ||
|
|
ad9fa2dd9f | ||
|
|
1e2f11f4f9 | ||
|
|
3a1f297618 | ||
|
|
8af3f41f12 | ||
|
|
e4d980b135 | ||
|
|
62ce98c8a5 | ||
|
|
31a1fd4ece | ||
|
|
51e22f7904 | ||
|
|
106fd84b7f | ||
|
|
5f99a6cf19 | ||
|
|
aba465e1a8 | ||
|
|
73500aae90 | ||
|
|
c3ec6a0b80 | ||
|
|
509ca5ae7f | ||
|
|
9b3091add4 | ||
|
|
b503cb5d80 | ||
|
|
a631388e5a | ||
|
|
d64b869695 | ||
|
|
8e1c1c9ef4 | ||
|
|
952ee9556a | ||
|
|
3155550931 | ||
|
|
7242a171dd | ||
|
|
0dac5f6b3f | ||
|
|
ff3bbad68c | ||
|
|
5cc473fd96 | ||
|
|
c03907b5a3 | ||
|
|
f9c257b73a | ||
|
|
308e3a4b1f | ||
|
|
99f8f34529 | ||
|
|
5f7f6fc55a | ||
|
|
36932e397d | ||
|
|
732e29f0a5 | ||
|
|
cd0ed4b385 | ||
|
|
03fd558e1b | ||
|
|
6bc57f184a | ||
|
|
6c58e8a243 | ||
|
|
c818bf82b0 | ||
|
|
67a687ee40 | ||
|
|
ec44c99076 | ||
|
|
39a1a07a7d | ||
|
|
0b2d0fa332 | ||
|
|
304a942f51 | ||
|
|
02c1ce749c | ||
|
|
1307169d8b | ||
|
|
8eeebf63e9 | ||
|
|
f7c0c9beb7 | ||
|
|
9ec4f64c70 | ||
|
|
9667eaf045 | ||
|
|
51ade5113f | ||
|
|
0af3c82612 | ||
|
|
4c13c0ec31 | ||
|
|
8ca5a5cdec | ||
|
|
43819605fa | ||
|
|
6e00f7eaa2 | ||
|
|
5d0ce407b7 | ||
|
|
bf70a42f6a | ||
|
|
6e70258f9a | ||
|
|
1ae9e80d96 | ||
|
|
444edb209c | ||
|
|
8a9c836065 | ||
|
|
a19ddb46b6 | ||
|
|
53d6065185 | ||
|
|
a66940b2b8 | ||
|
|
9092e70544 | ||
|
|
517ca6568f | ||
|
|
3368625dde | ||
|
|
2eb330f070 | ||
|
|
74a2485cff | ||
|
|
03efd14ff4 | ||
|
|
e71c95139d | ||
|
|
d9b84b0ab3 | ||
|
|
421d6cff3b | ||
|
|
ccb86264d9 | ||
|
|
ee2a1b332e | ||
|
|
2dda0bf27a | ||
|
|
d347fa9512 | ||
|
|
913323245c | ||
|
|
68ad507be3 | ||
|
|
764b4c20f5 | ||
|
|
a135e08e12 | ||
|
|
214974e3a3 | ||
|
|
3673d3fb7b | ||
|
|
f6ad530660 | ||
|
|
046daa43fa | ||
|
|
cc1c5239dc | ||
|
|
4fafb5731e | ||
|
|
9ab4d59cca | ||
|
|
68864dae2d | ||
|
|
ae6d377abb | ||
|
|
b479d45f14 | ||
|
|
e17afe31a5 | ||
|
|
5f115d5e4a | ||
|
|
1984131874 | ||
|
|
0b4e2b48c9 | ||
|
|
6aa53823f1 | ||
|
|
7f567585e2 | ||
|
|
c6d604bbbc | ||
|
|
5698d8282c | ||
|
|
83636644b1 | ||
|
|
fa6995b9fd | ||
|
|
c83d38b5c2 | ||
|
|
31e9abd8eb | ||
|
|
43b7abafe7 | ||
|
|
483134df10 | ||
|
|
b612a599de | ||
|
|
2cecc91026 | ||
|
|
38bc60ba86 | ||
|
|
4e4d798b1d | ||
|
|
218b2a1a75 | ||
|
|
73ce7e729c | ||
|
|
d67cbe2861 | ||
|
|
c3f38cdabd | ||
|
|
8f55a6277a | ||
|
|
ef71661db1 | ||
|
|
adefda42df | ||
|
|
9c04054017 | ||
|
|
7ab70ba597 | ||
|
|
0abccd105d | ||
|
|
15b34e2056 | ||
|
|
ae551a6bc1 | ||
|
|
98f6eac50c | ||
|
|
963d0a1897 | ||
|
|
705285878f | ||
|
|
e1dcb65888 | ||
|
|
9062dbcaae | ||
|
|
97c98969ff | ||
|
|
3fe47c939a | ||
|
|
46dee3c9b6 | ||
|
|
8e67e2d158 | ||
|
|
f5f4182a90 | ||
|
|
05087e569a | ||
|
|
77bde61f4b | ||
|
|
1297b45ef8 | ||
|
|
83b55db4f8 | ||
|
|
caec01e9fa | ||
|
|
2be67bae19 | ||
|
|
a677450d50 | ||
|
|
1d7919c050 | ||
|
|
a132172507 | ||
|
|
549ef5734e | ||
|
|
77fbf7f51e | ||
|
|
e37820d25e | ||
|
|
00bee60151 | ||
|
|
7679afd3e0 | ||
|
|
0b0408ef98 | ||
|
|
58cde322f5 | ||
|
|
0638f4c437 | ||
|
|
426b2d27e7 | ||
|
|
3ce3ac5350 | ||
|
|
4048ecc764 | ||
|
|
f93a1aa151 | ||
|
|
f6dc76719a | ||
|
|
3035af97a4 | ||
|
|
3832ef3547 | ||
|
|
8b4cea5dab | ||
|
|
9111590dc2 | ||
|
|
ff1bb0deed | ||
|
|
4795143fca | ||
|
|
54eca5b8ba | ||
|
|
11f669f8f4 | ||
|
|
1cda612e86 | ||
|
|
50e4e971d9 | ||
|
|
33dd34931c | ||
|
|
ce230bf403 | ||
|
|
d7b2a98723 | ||
|
|
bfb48ccea3 | ||
|
|
8cd39301a3 | ||
|
|
321684e5f6 | ||
|
|
0b247dfae7 | ||
|
|
d46fae2d1a | ||
|
|
5d6690fc1a | ||
|
|
66425ba48d | ||
|
|
3b3a36c64d | ||
|
|
2433ef5db7 | ||
|
|
663757352a | ||
|
|
1ced5d8ffc | ||
|
|
04c6bd8750 | ||
|
|
b0dc41b4d9 | ||
|
|
92e6cedb14 | ||
|
|
447ce97f24 | ||
|
|
2a471e409c | ||
|
|
08223bd320 | ||
|
|
90480ca626 | ||
|
|
56b031b99d | ||
|
|
f3cbd450d0 | ||
|
|
b176fd9bca | ||
|
|
1c130aad42 | ||
|
|
734dfcdec5 | ||
|
|
c8a32b8b38 | ||
|
|
ed3ec3aa40 | ||
|
|
88d8d0e6bb | ||
|
|
a763f7f025 | ||
|
|
357e9cc08f | ||
|
|
2746bac86a | ||
|
|
95652efbaa | ||
|
|
e70e5b04b5 | ||
|
|
34869914c4 | ||
|
|
a53505ee0b | ||
|
|
ed1831da78 | ||
|
|
ad550759e6 | ||
|
|
984518d9f5 | ||
|
|
e0055eeead | ||
|
|
fe07261846 | ||
|
|
0cbb9b22a5 | ||
|
|
31ccf2ca02 | ||
|
|
23b7e11dd1 | ||
|
|
588b6f3692 | ||
|
|
cdf6e607de | ||
|
|
f61917e8fe | ||
|
|
661564df36 | ||
|
|
0a7c852d89 | ||
|
|
c1379f7ac6 | ||
|
|
22ef134c84 | ||
|
|
7f95956a01 | ||
|
|
40ea101bf6 | ||
|
|
0c8b001d05 | ||
|
|
5dc9c3f732 | ||
|
|
ec574a6b63 | ||
|
|
1ca48051ad | ||
|
|
0f2e1ea740 | ||
|
|
c2d4aeaf85 | ||
|
|
a496b1fd71 | ||
|
|
ac8042f5d8 | ||
|
|
f7eb06e80b | ||
|
|
f4214e2031 | ||
|
|
fd8712ee61 | ||
|
|
f904ac7019 | ||
|
|
3c08af363f | ||
|
|
8295355a5d | ||
|
|
aee81f87f5 | ||
|
|
43d08ec21d | ||
|
|
15e3ad8876 | ||
|
|
a583f557d7 | ||
|
|
0949931a61 | ||
|
|
8efee39f7e | ||
|
|
307b807b38 | ||
|
|
be8a2a4b07 | ||
|
|
30df57fa35 | ||
|
|
467025888c | ||
|
|
21ecf959ea | ||
|
|
d1401d0cba | ||
|
|
b5930fc35d | ||
|
|
9a74ab77f4 | ||
|
|
18d698aad2 | ||
|
|
3329a6c49e | ||
|
|
c07a29ba86 | ||
|
|
b36c4b56c3 | ||
|
|
cee0025147 | ||
|
|
372ad1fac3 | ||
|
|
09cba965d3 | ||
|
|
d862f7f779 | ||
|
|
0adaa0ba66 | ||
|
|
432a4aa330 | ||
|
|
1c490cd463 | ||
|
|
570ab49e55 | ||
|
|
3ed4228b72 | ||
|
|
93fefa031b |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [nossr50] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: nossr50 # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://paypal.me/nossr50 # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# bash stuff
|
||||
*.sh
|
||||
|
||||
# Eclipse stuff
|
||||
/.classpath
|
||||
/.project
|
||||
|
||||
11
1
Normal file
11
1
Normal file
@@ -0,0 +1,11 @@
|
||||
SkillShot tweaks
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
#
|
||||
# On branch master
|
||||
# Your branch is up to date with 'origin/master'.
|
||||
#
|
||||
# Changes to be committed:
|
||||
# modified: src/main/java/com/gmail/nossr50/skills/archery/Archery.java
|
||||
# modified: src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
|
||||
#
|
||||
698
Changelog.txt
698
Changelog.txt
@@ -1,11 +1,695 @@
|
||||
Changelog:
|
||||
Versions without changelogs probably had very small misc fixes, like tweaks to the source code
|
||||
Version 2.1.117
|
||||
Fixed a rare http error when polling Mojang for UUIDs
|
||||
Fixed a bug that allowed duping
|
||||
|
||||
Key:
|
||||
+ Addition
|
||||
= Fix
|
||||
! Change
|
||||
- Removal
|
||||
|
||||
Version 2.1.116
|
||||
Fixed directional plants not maintaining their direction when replanted
|
||||
|
||||
Version 2.1.115
|
||||
Green Thumb now requires a hoe to activate
|
||||
Hoes no longer give free replants
|
||||
You can sneak to break plants with a hoe in your hand (or just put the hoe away)
|
||||
Using a hoe on non-fully grown crops will replant them as a convenience feature
|
||||
New sound option in sounds.yml called 'ITEM_CONSUMED', plays when eating seeds for Green Thumb
|
||||
Cocoa plants now require GT of at least 2 to start at the second stage of growth
|
||||
Green Terra now boosts growth on Green Thumb by 1 stage (doesn't go above the maximum value though)
|
||||
There is now a feature in place to prevent breaking a newly automatically replanted (via green thumb) crop from being breakable for a few seconds after it appears
|
||||
Fixed a bug where Salvage always gave the best results
|
||||
Fixed an issue with arrows causing exceptions with players not yet having data loaded
|
||||
Spectral arrows are now tracked by mcMMO
|
||||
Use minimum level of salvageable properly
|
||||
Fix Axes Critical Strikes default permissions ( new fixed permission: mcmmo.ability.axes.criticalstrikes )
|
||||
Fix potential null pointer exception for salvage
|
||||
Updated locale entry 'Herbalism.SubSkill.GreenTerra.Description'
|
||||
|
||||
Version 2.1.114
|
||||
Fix some more locale usages, should aim to further prevent issues with oddball locales
|
||||
Fixed a bug where newer versions of MySQL did not like our rank command
|
||||
Added a new setting to turn off the Snow Golem Exploit prevention to experience.yml next to the other exploit fixes
|
||||
Fixed a bug which stopped the optional Endermite XP exploit fix from working
|
||||
|
||||
Version 2.1.113
|
||||
Activating Berserk on a soft block (glass, snow, dirts) will break that material right away instead of only breaking subsequent blocks hit by the player
|
||||
Berserk will now break glass and glass pane blocks
|
||||
Hitting glass with berserk readied will activate it
|
||||
Added GLASS settings to sounds.yml for Berserk
|
||||
Fixed bug where BlockCracker didn't work on infested_stone_bricks
|
||||
Fixed a bug where beacons could be duplicated
|
||||
Check player's PTP world permissions before executing a party teleport
|
||||
Improved how mcMMO handles randomness
|
||||
|
||||
Version 2.1.112
|
||||
Correct locale usage for enum access, now enforces using the english locale to prevent issues with oddball locales for configs/commands
|
||||
Fixed a NPE that can occur if a player engages in combat with specific skills before their profile is loaded
|
||||
mcMMO is now more compatible with certain mob stacking plugins
|
||||
Improved behaviour for mob health bars
|
||||
Archery's Skill Shot bonus damage is now multiplicative instead of additive (nerfing early game skill shot ranks)
|
||||
Sweet Berry Bush's default Herbalism XP is now 50 instead of 300 (Update your experience.yml or delete it to generate a new one)
|
||||
|
||||
Version 2.1.111
|
||||
mcMMO is compatible with the following versions of MC: 1.15 / 1.14.4 / 1.14.3 / 1.14.2 / 1.14.1 / 1.14 / 1.13.2
|
||||
Further prevent nesting of bleed damage calls
|
||||
Warn about reparable/salvage configs with missing sections
|
||||
Added Bee combat experience modifier to experience.yml (Update your experience.yml file, see notes)
|
||||
Breaking a Bee Nest will result in Herbalism XP (Update your experience.yml file, see notes)
|
||||
Breeding bees will now result in Taming XP (Update your experience.yml file, see notes)
|
||||
|
||||
NOTES:
|
||||
You will need to update your experience.yml by hand or delete it to get XP from new things in 1.15
|
||||
The default version of mcMMO's experience.yml looks like this: https://paste.gg/p/anonymous/15c34df6e60d45d9a3508b379a5e6514
|
||||
You'll want to add Bee to combat XP section, and Bee_Nest to Herbalism XP, and Bee to Taming XP section.
|
||||
|
||||
Version 2.1.110
|
||||
Fixed a dupe bug
|
||||
Actually fixed Block Cracker
|
||||
You can now crack Infested Stone Bricks with Block Cracker
|
||||
Added Lithuanian locale (thanks Vyciokazz)
|
||||
|
||||
Version 2.1.109
|
||||
Block Cracker will now correctly crack stone_bricks during Berserk again
|
||||
Added Lily_Of_The_Valley to the Bonus Drops list in config.yml (you'll probably want to add this manually) which enables it to double drop
|
||||
Added missing 1.14 blocks to ability/tool activation blacklists
|
||||
Fixed spamming of incompatible worldguard version message
|
||||
Updated Italian locale (thanks Fabrimat)
|
||||
|
||||
Version 2.1.108
|
||||
Fixed an amusing exploit for easy leveling in Acrobatics
|
||||
mcMMO should no longer break if FAWE is being used in conjunction with WG on the server
|
||||
Improved WG compatibility, mcMMO will now check if WG has loaded properly before hooking into it, which will prevent mcMMO from breaking if WG could not load properly.
|
||||
mcMMO now loads after worlds do during start up (which is what it should be doing)
|
||||
|
||||
NOTES:
|
||||
Mobs will now only reward Dodge XP a certain amount of times
|
||||
You can turn off this exploit prevention by disabling ExploitFix.Acrobatics in experience.yml
|
||||
|
||||
Version 2.1.107
|
||||
Fixed an exploit that allowed automated XP gain for Excavation
|
||||
Fixed encoding of russian translation
|
||||
New API event McMMOPlayerProfileLoadEvent (thanks Shrek5InTheatres2019)
|
||||
|
||||
Version 2.1.106
|
||||
Added a few missing blocks to the ability blacklist tables (prevents readying/using abilities on certain blocks that have right click functionality)
|
||||
Updated Japanese locale (thanks ethernetcat)
|
||||
Updated Simplified Chinese locale (thanks Fu_Meng)
|
||||
|
||||
Version 2.1.105
|
||||
mcMMO will reset players scoreboard to the main scoreboard if they move into a blacklisted world
|
||||
Beast lore now shows max jump height and other tweaks (Thanks QuantumToasted)
|
||||
If the server has enabled level caps (which are not on by default) players will be shown that they are at max level instead of seeing how much XP needed to reach an impossible level (thanks Shrek5InTheatres2019)
|
||||
Large update to Italian locale (thanks Leomixer17)
|
||||
|
||||
NOTES:
|
||||
The scoreboard change should result in mcMMO removing scoreboards from players properly when they enter a black listed world. This is a hacky band aid fix as our scoreboard code is archaic and needs to be rewritten in 2.2 and not 2.1. Expect a scoreboard overhaul sometime in the future.
|
||||
|
||||
Version 2.1.104
|
||||
Fixed a bug resulting in many errors when using Rupture
|
||||
Large update to the Japanese locale (thanks ethernetcat)
|
||||
Updated Hungarian locale (thanks andris)
|
||||
|
||||
Version 2.1.103
|
||||
Treasure item amount will default to 1 if not specified instead of breaking mcMMO
|
||||
Fishing treasures will now return the amount defined in treasures config file, an option to randomize amounts will be coming in the future. See notes.
|
||||
Fixed a bug where Tree Feller was only rewarding 1 XP per log broken no matter the circumstances
|
||||
Fixed an issue with salvage checking the incorrect level requirement
|
||||
Updated Italian locale (thanks Leomixer17)
|
||||
Fixed grammar in one of the Salvage strings (thanks QuantumToasted)
|
||||
|
||||
NOTES:
|
||||
Sometime in 2014 a decision was made that any fishing treasure over the amount of 1 will be randomized in its yield, while this is not necessarily a bad thing it means control over the drop is a bit random. I've removed this randomness temporarily, in the future you will be able to chose if the amount is randomized or static.
|
||||
|
||||
Version 2.1.102
|
||||
Scoreboards will now be removed from players who teleport to a blacklisted world
|
||||
Fixed a bug where Rupture could trigger itself
|
||||
|
||||
Version 2.1.101
|
||||
Fixed an exploit where chorus plants could be used to gain automatic XP
|
||||
Added blast furnace, cartography table, grindstone, lectern, loom, scaffolding, smoker, stonecutter, and sweet berry bush to the list of internal blocks that don't trigger tool readying
|
||||
|
||||
Version 2.1.100
|
||||
Fixed a bug where plants could double drop when the skill was not yet unlocked
|
||||
Fixed a bug where plants ALWAYS double dropped
|
||||
mcnotify command now checks that it's being executed by a player
|
||||
Fixed some concurrency concerns around BleedTasks
|
||||
Fixed an NPE that may occur with random chances on a player without loaded data
|
||||
Updated Russian locale (thanks myfbone!)
|
||||
|
||||
Version 2.1.99
|
||||
MASSIVE update to the Russian locale (ru) credit to myfbone
|
||||
Debug mode (/mmodebug) will now print info about XP perks and how they are modifying your incoming XP
|
||||
|
||||
Version 2.1.98
|
||||
Fixed a bug that prevented Taming XP from combat
|
||||
|
||||
Version 2.1.97
|
||||
mcMMO is compatible with 1.14.4 (Didn't require any changes)
|
||||
Fixed a NPE that could occur if a pet participated in combat and its master's mcMMO data was not available
|
||||
mcMMO now properly closes its region files as it creates them (thanks Yukiiro-Nite)
|
||||
Updated Hungarian locale (thanks andris)
|
||||
|
||||
Version 2.1.96
|
||||
Added the setting 'Skills.General.LimitBreak.AllowPVE' to advanced.yml to allow Limit Break damage bonus to apply in PVE again, defaults to false
|
||||
Updated Limit Break locale strings
|
||||
Fixed a few more places where 'Archaeology' was misspelled in the locale
|
||||
Added the setting 'ExploitFix.PistonCheating' to experience.yml at the request of a user
|
||||
Added a missing 's' to Nether_Bricks (thanks Sikatsu) in experience.yml
|
||||
|
||||
NOTES:
|
||||
The Skill Tooltips are a bit limited right now, in the future they will be more flexible. In order to reflect that Limit Break doesn't always work in PVE (now up to server settings) I added a crappy note to its hover window tip. I'll be fixing this in the future.
|
||||
You shouldn't need to update you config entry for Nether_Bricks, I believe that file updates automatically (the old config system is a bit janky, some stuff updates, some other stuff doesn't.)
|
||||
PistonCheating prevents blocks from being marked "natural" once they've been moved, we've never had an option for this before.
|
||||
A discord user requested it, its a strange request but I added it anyways.
|
||||
Of course, it defaults to "true" which prevents cheating with pistons.
|
||||
|
||||
Version 2.1.95
|
||||
Added missing Chorus_Fruit & Chorus_Plant entries to Herbalism's Bonus Drops in config.yml (See notes)
|
||||
Limit Break now does dramatically less damage to players with lower grades of armor
|
||||
Limit Break damage bonuses now only apply to Players
|
||||
Updated in game text to reflect changes to Limit Break
|
||||
Fixed a bug where opponents used your stats instead of their own to activate Iron Grip
|
||||
Fixed a bug preventing Wandering Traders from granting XP
|
||||
Fixed a bug that prevented Chorus Tree's from giving full XP if you broke anything other than the bottom block
|
||||
Fixed a bug which could cause Large Fern's to reward less XP
|
||||
Fixed a bug where certain herbalism crops could have fewer than intended bonus drops
|
||||
Fixed a bug involving Ender Pearl and Acrobatics
|
||||
Added some protection to Acrobatics to prevent gaining too much XP in one Roll.
|
||||
Added 'Carrots, Cocoa, Potatoes, Wheat, Beetroots, Nether_Wart' to Herbalism in experience.yml (See notes)
|
||||
Removed the _Ripe entries from experience.yml (no longer used)
|
||||
Updated locale string 'Swords.SubSkill.SwordsLimitBreak.Description' & 'Swords.SubSkill.SwordsLimitBreak.Stat'
|
||||
Added missing 'Chorus_Flower' entry to herbalism in experience.yml
|
||||
Added some debug messages about XP gains if you are in debug mode
|
||||
Added some debug messages for Acrobatics if you are in debug mode
|
||||
Added some debug messages for Herbalism if you are in debug mode
|
||||
|
||||
NOTES:
|
||||
Add 'Chorus_Fruit' and 'Chorus_Plant' under Bonus_Drops.Herbalism in config.yml or you will not be getting double drops for Chorus Fruit.
|
||||
You shouldn't need to add "Chorus_Flower, Carrots, Cocoa, Potatoes, Wheat, Beetroots, Nether_Wart" to your experience file, it seems that config file updates automatically for missing entries.
|
||||
|
||||
Version 2.1.94
|
||||
2 new devs have joined the mcMMO team (electronicboy, kashike), bringing the active dev team to 3 including myself! Strings relating to authors of mcMMO have been updated to reflect this
|
||||
Fixed a bug where 2 people using Tree Feller could result in the tree being rejected for being too big
|
||||
New command /mmodebug (or /mcmmodebugmode) - Prints useful information when players punch blocks while they are in debug mode
|
||||
mcMMO no longer prints debug information when you attack stuff with the debug stick, use the new command (/mmodebug) instead
|
||||
Added locale string 'mcMMO.Description.FormerDevs'
|
||||
Added locale string 'mcMMO.Template.Prefix'
|
||||
Added locale string 'Commands.Mmodebug.Toggle'
|
||||
Added locale string 'Commands.Description.mmodebug'
|
||||
Tweaked locale string 'mcMMO.Description'
|
||||
Updated Japanese locale (thanks snake)
|
||||
Updated hu_HU locale (thanks andris155)
|
||||
|
||||
Version 2.1.93
|
||||
Fixed a bug where players would be told they could not breed summoned animals when the animals weren't summoned (bug didn't actually do anything besides send you a message)
|
||||
|
||||
Version 2.1.92
|
||||
Call Of The Wild (COTW) no longer cares if entities of the same type are nearby when attempting to summon a new entity
|
||||
Fixed a bug where COTW summon limit was global instead of per player
|
||||
If you are playing in 1.14 mcMMO will now summon cats instead of ocelots
|
||||
The default summon limit for COTW is now per player instead of global which is how it should be, for wolves this defaults to 2, for other entities it defaults to 1
|
||||
The COTW setting named Summon_Max_Amount in config.yml has been renamed to Per_Player_Limit
|
||||
By default players are no longer allowed to breed COTW summoned animals with other animals, you can turn this off (see the notes)
|
||||
If a player tries to breed animals with a COTW animal and the server settings prevent this, they are informed via a message that it is not allowed
|
||||
If the COTW summon has a lifespan, players are now informed about this lifespan when the entity is first summoned
|
||||
COTW entities now send the player a message when they die, time out, or get removed through other means
|
||||
COTW summons now have their name prefixed with some colored text to to make it easier to identify them.
|
||||
COTW summons now despawn if their owner logs out
|
||||
There is now a small 150ms window in which you cannot summon an entity via COTW to prevent accidentally summoning extra entities
|
||||
COTW Horses, Cats, and Wolves now always spawn in as adults
|
||||
Changed the sound effect for COTW (Fireworks -> Pop)
|
||||
Most COTW messages have been tweaked and new COTW messages have been added
|
||||
Added new setting to experience.yml 'ExploitFix.COTWBreeding' - Prevents breeding with COTW summoned entities when set to true, defaults to true
|
||||
Removed the 'mcmmo.ability.taming.callofthewild.renamepets' permission node as it is seen as unnecessary
|
||||
|
||||
Removed locale strings
|
||||
Taming.Summon.Fail.Ocelot
|
||||
Taming.Summon.Fail.Wolf
|
||||
Taming.Summon.Fail.Horse
|
||||
Taming.Summon.Fail.TooMany
|
||||
Taming.Summon.Lifespan
|
||||
Added new locale strings
|
||||
Taming.Summon.COTW.NeedMoreItems
|
||||
Taming.Summon.COTW.BreedingDisallowed
|
||||
Taming.Summon.COTW.Success.WithLifespan
|
||||
Taming.Summon.COTW.Success.WithoutLifespan
|
||||
Taming.Summon.COTW.Limit
|
||||
Taming.Summon.COTW.TimeExpired
|
||||
Tweaked locale string
|
||||
Taming.Summon.Name.Format
|
||||
|
||||
NOTES:
|
||||
I plan to rework Call of The Wild (COTW) significantly in the upcoming content patch, until then I have made several tweaks to it.
|
||||
|
||||
COTW Summoning Requirement Changes
|
||||
It is intentional that you are not supposed to be able to COTW summon something that you already have tamed, but mcMMO was not checking this properly.
|
||||
So previous to this patch, if you tried to summon a wolf and wolves were nearby, it would fail. This is not intentional behaviour and was a bug.
|
||||
The correct behaviour is that if you try to summon a wolf and you already have wolves, it should fail.
|
||||
I was fixing this bug when it occurred to me, why do we even care if nearby tamed wolves owned by you exist if you are trying to summon a temporary one?
|
||||
As a result of this train of thought, I have removed this restriction on COTW and several other tweaks have been made as a result of this line of thinking.
|
||||
There was also an issue involving how many COTW entities could be out at once, for some reason the limit was applied globally and defaulted to 10, so if 10 players had COTW entities out no one else would be able to summon anything. I have fixed this.
|
||||
You can set the lifespan to 0 in the config to make it so that COTW entities last until a server restart or the player logs out.
|
||||
|
||||
COTW Breeding Change
|
||||
It was never intentional for COTW summoned entities to be breedable, by default they will not be. You can change this by setting 'ExploitFix.COTWBreeding' to false in experience.yml
|
||||
|
||||
Version 2.1.91
|
||||
mcMMO is now more compatible with plugins that spawn arrows in unexpected ways, this fixes some NPE in mcMMO when using certain plugins
|
||||
Fixed a bug where Unarmed was using the same CD timer for every player in the server (thanks slop_me)
|
||||
|
||||
Version 2.1.90
|
||||
Salvaged items now travel much slower towards the player
|
||||
Books from salvage will now travel towards the player
|
||||
Salvaged items travelling towards you will now have their speed adjusted based on distance
|
||||
|
||||
Common Materials found when mining have had their XP gains reduced and rare materials have had their XP gains increased
|
||||
Diamond Ore has had its XP increased from 750 to 2400
|
||||
Gold Ore has had its XP increased from 350 to 1300
|
||||
Iron Ore has had its XP increased from 250 to 900
|
||||
Lapis Ore has had its XP increased from 400 to 800
|
||||
Nether Quartz Ore has had its XP increased from 100 to 300
|
||||
Redstone Ore has had its XP increased from 150 to 600
|
||||
Blue_Ice has had its default XP value reduced to 15
|
||||
Packed_Ice has had its default XP value reduced to 15
|
||||
Netherrack has had its default XP value reduced to 15
|
||||
Coal Ore has had its XP increased from 100 to 400
|
||||
Stone has had its XP decreased from 30 to 15
|
||||
Granite has had its XP decreased from 30 to 15
|
||||
Andesite has had its XP decreased from 30 to 15
|
||||
Diorite has had its XP decreased from 30 to 15
|
||||
End Stone has had its XP decreased from 30 to 15
|
||||
Glow Stone has had its XP decreased from 30 to 15
|
||||
End Bricks has had its XP decreased from 200 to 50
|
||||
|
||||
|
||||
NOTES:
|
||||
You can either apply the default XP changes manually to experience.yml or delete it to generate a new file
|
||||
|
||||
Sorry about salvaged items traveling too quickly, in testing on my LAN server with ideal conditions they always entered my inventory and never shot past me so the high speed of the items was not an apparent issue.
|
||||
I've tested the new changes to how they travel towards you pretty thoroughly, they never go fast enough to go past you and they adjust their speed based on distance to you with some speed limitations.
|
||||
In testing the new tweaks work well whether the salvage anvil is above, below, or at the same height as you.
|
||||
|
||||
I'll be tweaking XP values across all skills more carefully in the upcoming content patch due after 2.2. Some tweaks to XP gains will be applied for 2.2.
|
||||
|
||||
Version 2.1.89
|
||||
Many changes were made to this version that affect default values in the config, read the notes carefully if you wish to apply these changes. They are optional.
|
||||
mcMMO is compatible with the new 1.14.3, in addition to this it is still compatible with 1.14.2, 1.14.1, 1.14, and 1.13.2. This did not require any changes to be made to mcMMO, but I thought I'd add this to the notes for those wondering.
|
||||
|
||||
RetroMode is the default level scaling mode again
|
||||
The default level for players has been changed back to 0 from 1 (you can change it back to 1 by editing 'Skills.General.StartingLevel' in advanced.yml) if you wish to apply this change read the notes
|
||||
Super Abilities now scale in length up to level 100/1000 instead of 50/500 by default (you can edit this in advanced.yml under 'Skills.General.Ability.Length.xxx')
|
||||
Early Game Boost now only applies when leveling from level 0 to level 1
|
||||
Removed the config setting 'MaxLevelMultiplier' from experience.yml as it is no longer used.
|
||||
When finding a treasure via Excavation players have a 1-8% chance to have a small amount vanilla experience orbs to be found alongside the treasure, the chance and number of orbs are based on the players Archaeology rank
|
||||
Tweaked the locale string 'Excavation.SubSkill.Archaeology.Description'
|
||||
Added locale string 'Excavation.SubSkill.Archaeology.Stat'
|
||||
Added locale string 'Excavation.SubSkill.Archaeology.Stat.Extra'
|
||||
Tweaked the locale string 'Fishing.SubSkill.MasterAngler.Stat'
|
||||
The interval at which you can gain Acrobatics XP from fall damage has been reduced to 3 seconds from 10 seconds, this will be configurable in 2.2.
|
||||
|
||||
When using WorldGuard (WG) with mcMMO, mcMMO now examines WG more carefully to determine if it is a compatible version or not, see the notes. Keep in mind WG is optional and not needed to run mcMMO.
|
||||
Fixed a bug that could result in Tree Feller failing to remove parts of a tree in snowy biomes. (This fix won't apply retroactively to old trees, see the notes)
|
||||
Updated Japanese locale (thanks snake)
|
||||
Tree Feller and other Super Abilities will damage tools with the Enchantment named 'Durability/Unbreaking' again, this does not apply to the NBT tag named Ubreakable (Unbreakable NBT tag is safe from damage however, try not to confuse them as they share similar names)
|
||||
Added new setting 'ExploitFix.TreeFellerReducedXP' to experience.yml
|
||||
Tree Feller will no longer give full XP for each block destroyed and instead give diminishing returns on XP for each block removed. You can turn this off by setting 'ExploitFix.TreeFellerReducedXP' in experience.yml to false
|
||||
Many skills which used to unlock at level 5/50 now unlock at level 1 instead
|
||||
Arrow Retrieval now unlocks at level 1 in both Standard and RetroMode
|
||||
Skill Shot now unlocks at level 1 in both Standard and RetroMode
|
||||
Dodge now unlocks at level 1 in both Standard and RetroMode
|
||||
Critical Strikes now unlocks at level 1 in both Standard and RetroMode
|
||||
Armor Impact now unlocks at level 1 in both Standard and RetroMode
|
||||
Beast Lore now unlocks at level 1 in both Standard and RetroMode
|
||||
Call Of The Wild now unlocks at level 1 in both Standard and RetroMode
|
||||
Scrap Collector now unlocks at level 1 in both Standard and RetroMode
|
||||
Treasure Hunter now unlocks at level 1 in both Standard and RetroMode
|
||||
Rupture now unlocks at level 1 in both Standard and RetroMode
|
||||
Iron Arm Style now unlocks at level 1 in both Standard and RetroMode
|
||||
Harvest Lumber now unlocks at level 1 in both Standard and RetroMode
|
||||
Archaeology now unlocks at level 1 in both Standard and RetroMode
|
||||
Repair Mastery now unlocks at level 1 in both Standard and RetroMode
|
||||
Double Drops (Herbalism and Mining) now have ranks and unlock at level 1 in both Standard and RetroMode
|
||||
Concoctions rank 2 now unlocks at Level 1 for both Standard and RetroMode
|
||||
Serrated Strikes now unlocks at levels 5/50 instead of 10/100
|
||||
Berserk now unlocks at levels 5/50 instead of 10/100
|
||||
|
||||
NOTES:
|
||||
I'm looking into adding 1.12.2 support sooner than expected.
|
||||
|
||||
Editing your config files is not required for this patch (and never will be), however I would highly recommend it. Read the notes below this line carefully.
|
||||
If you want to update to this patch easily and you don't mind default config values, delete skillranks.yml, advanced.yml, and experience.yml before starting your server with 2.1.89 of mcMMO applied.
|
||||
Many skills now unlock at level 1 and the default starting level is back to 0. It is recommended you change this number from 1 to 0 manually by editing 'Skills.General.StartingLevel' in advanced.yml or deleting advanced.yml and generating a new file when 2.1.89 starts.
|
||||
It is recommended you make the manual edits to skillranks.yml to apply this change or delete skillranks.yml and a new one will be generated once you start 2.1.89.
|
||||
|
||||
Early Game Boost was used to help players get to level 5 quickly by boosting their XP until they got there, this caused some confusion as they appeared to be 'learning a skill' for multiple levels, on RetroMode this lasted until level 50.
|
||||
I have changed Early Game Boost to only last from level 0-1 on both RetroMode and Standard, and now important skills are unlocked at level 1.
|
||||
I was going to wait until 2.2 was done to deploy this change, but 2.2 is some time away and the previous system was causing some confusion.
|
||||
Early Game Boost gives players extra XP for skills that are level 0, you can turn this system off by setting 'EarlyGameBoost.Enabled' in experience.yml to false
|
||||
Early Game Boost will show 'Learning a new skill...' if XP bars are enabled
|
||||
|
||||
https://mcmmo.org/wiki/World_guard - A list of WG flags supported by mcMMO.
|
||||
It is not necessary to have WG installed, but if you do have WG installed mcMMO hooks into it to provide some additional features.
|
||||
Previously mcMMO used to determine if WG was compatible just by checking to see if it was version 7 of WG, however version 7 of WG is not guaranteed to be compatible as necessary classes that mcMMO hooks into were added during its development and some users are still running early dev versions of WG7.
|
||||
In order to decrease the chance of error, mcMMO now uses reflection when checking to see if WG is compatible in addition to checking its version number, if its not compatible mcMMO will print a message and refrain from hooking into WG.
|
||||
WG is an optional dependency for mcMMO, and unfortunately before this change if mcMMO thought you were running a compatible version of WG and it turned out you weren't then mcMMO would not function correctly.
|
||||
|
||||
Archaeology does something now, it will be tweaked again in the future. Previously rank 1 of Archaeology was required to find treasures but that was all it did, now each rank of Archaeology also adds a small chance to find experience orbs when a player finds treasure.
|
||||
|
||||
In a prior patch I removed damage on tools if they had Unbreakable NBT or Unbreaking Enchantment, at the time I made this change I did not realize they were different things and just shared the same name, this patch fixes this mistake. My intention was to prevent damage on tools with the NBT unbreakable tag which makes an item never suffer from durability loss.
|
||||
mcMMO reduces damage to tools from super abilities if they have the "Durability/Unbreaking" enchantment already, this behaviour has been in mcMMO for a long time.
|
||||
|
||||
Tree Feller will now give reduced XP per block destroyed by Tree Feller and will never go below 1 XP per block, you can turn this off with 'ExploitFix.TreeFellerReducedXP' in experience.yml
|
||||
There was a bug fixed in this patch that prevents an issue where Tree Feller was not removing all blocks left behind, unfortunately this will only affect new trees made in the world. The only trees that suffered from this bug were ones in snowy areas that had snow form on them.
|
||||
|
||||
RetroMode and Standard mode will be getting renamed in 2.2, probably to something like 1000-scale and 100-scale respectively.
|
||||
Retro Mode was accidental genius is the short reason for the change to make it the default level scaling for mcMMO again.
|
||||
Standard Mode is not going anywhere, it is just opt-in now instead of the default.
|
||||
If you delete config.yml you will need to turn RetroMode off to use Standard mode as mcMMO doesn't keep track of what level scaling you are using outside of that.
|
||||
|
||||
Version 2.1.88
|
||||
mcMMO is now more compatible with a plugin named Project Korra
|
||||
mcMMO will no longer process combat triggers for damage at or below 0
|
||||
|
||||
Version 2.1.87
|
||||
(Level caps are not on by default in mcMMO, this is something you can turn on)
|
||||
|
||||
Players who reach either the power level cap or skill level cap will now be informed that they have done so
|
||||
XP Bars will no longer be sent to players who have reached the power level or skill level cap respectively
|
||||
Level up messages will no longer be sent to players who have reached the power level or skill level cap respectively
|
||||
Fixed a bug where mcMMO would send level up notifications to a player if the custom level up event from mcMMO was cancelled
|
||||
New locale strings 'LevelCap.PowerLevel' & 'LevelCap.Skill'
|
||||
|
||||
Version 2.1.86
|
||||
Players will no longer be told they got a perfect result when salvaging if they are below max skill level
|
||||
Salvage results will now travel towards the player instead of moving in a random direction
|
||||
(FIX) You cannot view stats of players who are offline if your server limits the range on inspect for you via permissions or settings
|
||||
(FIX) Players will no longer be told they lack skill in salvage if they already qualify for the best possible result for the item
|
||||
(FIX) Fixed a bug where arcane salvage was being used to calculate salvage results instead of scrap collector
|
||||
(FIX) Added the missing locale color for 'Commands.Party.Commands'
|
||||
Modified colors used in locale strings 'Salvage.Skills.Lottery.Normal', 'Salvage.Skills.Lottery.Perfect', and 'Profile.Loading.FailurePlayer'
|
||||
When failing to load a profile mcMMO will increase the amount of time it waits before reattempting to load the profile for each failure
|
||||
|
||||
NOTES:
|
||||
You can (and always have been able) to turn off the inspect range check by setting it to 0 in the config
|
||||
2.2 is around the corner, most things are functional, still a lot of work left to do.
|
||||
|
||||
Version 2.1.85
|
||||
Fixed a nearly 6 year old bug where Super Repair was not included as a child permission under Repair ability permission nodes (which meant some players would not have access to this skill)
|
||||
Fixed a bug that could prevent salvage from working for certain players
|
||||
Renamed the advanced salvage permission node to 'mcmmo.ability.salvage.scrapcollector'
|
||||
Fixed a bug that would send players skill unlock notifications if they did not have the permission node for that skill
|
||||
Dramatically increased the chance of receiving full materials when salvaging
|
||||
|
||||
Version 2.1.84
|
||||
Added some code to make mcMMO more compatible with EpicSpawners
|
||||
|
||||
Version 2.1.83
|
||||
You can no longer set party members on fire with your bow
|
||||
(FIX) Arrow Retrieval will no longer work with piercing enchant on crossbows
|
||||
WG Flags should now correctly recognize when a projectile belongs to a player and respect flag settings
|
||||
Updated hu_HU locale (thanks andris155)
|
||||
|
||||
Version 2.1.82
|
||||
Added new WG flag 'mcmmo-hardcore' if set to negative players will not be penalized by hardcore mode (if hardcore mode is enabled) it defaults to true
|
||||
Added proper error handling when loading parties file
|
||||
Fixed an error that could occur when using mcrank on an offline player
|
||||
You can now use mcrank on offline players by default
|
||||
You can now use inspect on offline players by default
|
||||
Removed the offline inspect/mcrank permissions
|
||||
Updated Chinese locale (thanks to the user named 89009332 from github)
|
||||
Added some redundancy checks when loading profiles (NPC checks to be specific)
|
||||
|
||||
NOTES:
|
||||
There were reasons to prevent inspecting offline players in the past, I don't see any reason for them anymore so I've removed the restriction.
|
||||
|
||||
Version 2.1.81
|
||||
Fixed a bug where Arrow Deflect would never trigger outside of PVP
|
||||
Fixed a bug where failing to salvage enchantments incorrectly colored the text
|
||||
mcMMO no longer allows players to keep enchantments exceeding normal limitations by default when salvaging or repairing (you can turn this off in the config, see below)
|
||||
Added new setting 'ExploitFix.UnsafeEnchantments' to experience.yml, turn this on to allow players to salvage/repair enchantments higher than normal game restrictions
|
||||
|
||||
NOTES:
|
||||
If a player tries to salvage/repair an item with an illegal enchant (for example Sharpness X) it will downgrade that item to the highest legal version when calculating rewards
|
||||
If you don't like this change you can turn it off in experience.yml under 'ExploitFix.UnsafeEnchantments'
|
||||
|
||||
|
||||
Version 2.1.80
|
||||
(Fix) mcMMO now respects the NBT "Unbreakable" tag and does not deal durability damage to items with that tag
|
||||
|
||||
Version 2.1.79
|
||||
Updated Japanese locale (Thanks snake0053)
|
||||
Fixed a NPE that could happen when using Tree Feller with an unenchanted Axe
|
||||
|
||||
Version 2.1.78
|
||||
Shovels no longer take more than 1 diamond to repair
|
||||
Tools with the unbreaking enchantment no longer take extra damage from ability usage, they are still subject to the normal durability damage from breaking blocks though.
|
||||
|
||||
Version 2.1.77
|
||||
Added minimum quantity back to Repair config
|
||||
|
||||
NOTES: I removed this last patch because I did not consider that server admins might be allowing users to repair items without crafting recipes (as of last patch mcMMO determines minimum quantity via counting ingredients in a recipe)
|
||||
If you do not define minimum quantity in the repair config, mcMMO will grab the minimum quantity automatically as I programmed it to do as of last patch, otherwise if it is defined, mcMMO will respect that and use that for calculations.
|
||||
The minimum quanitty should be set to the number of ingredients used to craft the recipe, for example 8 for diamond chestplate etc, you do not need to define this unless you are allowing players to repair custom items.
|
||||
|
||||
Version 2.1.76
|
||||
Advanced Salvage has been renamed to Scrap Collector
|
||||
Scrap Collector has 8 ranks, the first rank is unlocked at level 2 (Level 20 in RetroMode)
|
||||
You can not salvage without at least 1 rank in Scrap Collector now (formerly Advanced Salvage)
|
||||
Fixed a bug where Repair was repairing too much
|
||||
Fixed a bug where Arcane Salvage was used to determine how many materials a player could salvage from an item instead of Scrap Collector (formerly Advanced Salvage)
|
||||
Fixed a bug where messages about an item being too damage to salvage were being sent twice
|
||||
Removed the minimum quantity field from the repair config
|
||||
Removed the item data (metadata) field from repair config as its not used anymore
|
||||
Salvage will no longer return the max amount of materials possible, instead you are guaranteed one item and then some luck is involved on how many items are returned.
|
||||
Updated Chinese locale (thanks to the user named 89009332 from github)
|
||||
|
||||
New locale strings
|
||||
Salvage.Skills.Lottery.Normal
|
||||
Salvage.Skills.Lottery.Perfect
|
||||
Salvage.Skills.Lottery.Untrained
|
||||
Salvage.SubSkill.ScrapCollector.Name
|
||||
Salvage.SubSkill.ScrapCollector.Description
|
||||
Salvage.SubSkill.ScrapCollector.Stat
|
||||
|
||||
(API) SALVAGE_ARCANE_SALVAGE in SubSkillType has been renamed to SALVAGE_SCRAP_COLLECTOR
|
||||
|
||||
NOTES:
|
||||
You do not need to update your configs for this update.
|
||||
|
||||
How Salvage works
|
||||
As an example, say you had enough skill to gain up to 5 items from salvaging something, and that item has enough durability to yield up to 5 materials, salvage will play out like this...
|
||||
First off, you will be guaranteed 1 material, after this you have 80% chance to get the next material, if successful, another dice roll is conducted but lowers your odds by 20%, you chance to succeed will never fall below 33%
|
||||
If you fail a dice roll, it will still conduct dice rolls for the remaining maximum amount of materials returned but your odds of success are only lowered upon a successful dice roll, the sum of the successful dice rolls is used to calculate how many items you are given back, the first item is guaranteed and has no dice rolls, which means for example the Diamond Shovel will always succeed.
|
||||
|
||||
Version 2.1.75
|
||||
Fixed a bug that prevented Fortune from working correctly if a Double Drop was triggered
|
||||
|
||||
Version 2.1.74
|
||||
Fixed a NPE that could occur during certain events if a skill was disabled in coreskills.yml (Sorry!)
|
||||
|
||||
Version 2.1.73
|
||||
Fixed a NPE that could occur if an entire skill was disabled in coreskills.yml
|
||||
|
||||
Version 2.1.72
|
||||
Fixed a NPE if a server shutdown with no player data needing to be saved (the error is harmless but spammy)
|
||||
Fixed a NPE that could occur if Roll was disabled in coreskills.yml
|
||||
|
||||
Version 2.1.71
|
||||
Salvage will now always ask for confirmation before breaking your items (instead of only asking for enchanted items)
|
||||
Repair will now always ask for confirmation before repairing items (instead of only asking for enchanted items)
|
||||
Gold & Iron Blocks will no longer trigger tool ready messages
|
||||
Salvage & Repair anvils will no longer work on multi-item stacks
|
||||
|
||||
NOTES: You can still turn the confirmation off in config.yml
|
||||
|
||||
Version 2.1.70
|
||||
Added new DatabaseAPI to the API package, has features relating to database operations
|
||||
Fixed a bug where shulker boxes (without colors) would activate tool ready messages
|
||||
|
||||
Version 2.1.69
|
||||
Fixed a few places where mcMMO would not save player data immediately which may cause players to lose a few minutes of progress
|
||||
|
||||
A big thanks to Sleepyflea for helping test this patch and report this bug.
|
||||
|
||||
Version 2.1.68
|
||||
Updated Japanese locale (thanks Snake)
|
||||
Fixed a bug where consuming food in the off hand did not trigger the Diet abilities
|
||||
|
||||
Version 2.1.67
|
||||
The XP bar now reflects whether or not the player is receiving the early game boost
|
||||
Players who are receiving an early game boost will be shown "Learning a skill..." as the title of the XP bar while gaining XP
|
||||
New locale string 'XPBar.Template.EarlyGameBoost'
|
||||
|
||||
NOTES:
|
||||
You can turn off this system in experience.yml 'EarlyGameBoost.Enabled'
|
||||
The Early Game Boost is a system in which players receive drastically increased XP gains for the first few levels (until the first set of skills are unlocked)
|
||||
With default settings, this means players will be receiving boosted XP for the first 5 levels (50 for Retro)
|
||||
The Early Game Boost ends when players get the first skill in each ability under default settings
|
||||
The main purpose of this system is to alleviate progression issues with a few skills where the first set of unlocked abilities drastically increase how fun it is to level the skill
|
||||
The secondary purpose of this system is to alleviate any psychological issues when grinding towards the first set of unlocks by making the first set of unlocks happen quickly
|
||||
This system has been in place for a few versions now, but without an in game indicator of what was going on.
|
||||
If you have XP bars off there is still no indicator that this system is in place, I'll address that at some point.
|
||||
|
||||
Version 2.1.66
|
||||
Fixed a bug that could happen if a player was removed from the DB when using MySQL/MariaDB when the user was offline
|
||||
Fixed a minor memory leak for MySQL
|
||||
|
||||
Version 2.1.65
|
||||
Corrected a bug that would cause RetroMode to use Linear formula regardless of setting
|
||||
|
||||
Version 2.1.64
|
||||
Corrected how Standard mode (1-100 scaling) XP to next level was calculated, it is now a true 1:10 ratio with Retro (1-1000) scale, which is how it was intended to be to begin with
|
||||
Fixed a bug that caused skill messages to spam nearby players
|
||||
(API) method to get XP in FormulaManager has been renamed to getXPtoNextLevel(...), this shouldn't break anything as plugins should be using our Experience API methods instead of this
|
||||
(API) Added method getLevel(Player player, PrimarySkillType primarySkillType) to ExperienceAPI.java
|
||||
|
||||
|
||||
NOTE: The net result of this change is it will take a bit longer to level with Standard, but it should not be a drastic change. You might not even notice it.
|
||||
Standard is meant to take the same amount of time to level from levels 1-100 as it takes Retro to do 1-1000, this change corrects from errors in the code that made Standard actually take less XP than Retro despite intending for it to be a cosmetic difference in progression.
|
||||
I made a google sheet visualizing the difference between Standard and Retro Mode using default settings and Linear formula - https://docs.google.com/spreadsheets/d/1VlJtvNHlypACHyz_zulEdhgLwFjL01xMPkqlnu0XBSs/edit?usp=sharing
|
||||
|
||||
Version 2.1.63
|
||||
Fixed Armor Impact not scaling by skill rank
|
||||
Significantly Buffed the amount of durability damage incurred by a successful Armor Impact
|
||||
Added new setting to advanced.yml 'Skills.Axes.ArmorImpact.DamagePerRank' which is multiplied against Armor Impact's skill level to determine damage done to armor
|
||||
Fixed a bug that caused creative mode players to gain XP when qualifying for early game XP boosts
|
||||
Removed the damage cap setting for Armor Impact, it is inherently capped now based on its max rank and damage setting
|
||||
Updated hu_HU locale (thanks andris155)
|
||||
Updated ja_JP locale (thanks snake)
|
||||
|
||||
NOTES:
|
||||
Armor Impact only has a 25% chance to go off, and for it to only incur 20 durability damage seemed incredibly underwhelming, I've buffed it quite a bit.
|
||||
You can change it back to its underwhelming self by setting DamagePerRank to 1.0
|
||||
Based on feedback I may tweak the number again or other properties of this skill
|
||||
|
||||
Version 2.1.62
|
||||
Added a new admin notification system, sensitive commands will print chat messages to "admins" (players with either Operator status or admin chat permission)
|
||||
Added a setting to disable the new admin notifications to config.yml 'General.AdminNotifications' (this will be more configurable in 2.2)
|
||||
OPs and players with the admin chat permission will now see details about XP rate event commands regardless of whether or not the XP rate event messages are enabled
|
||||
Updated hu_HU locale (thanks andris155)
|
||||
Added XP for mining Magma_Block (default 30 XP - Update your config, see notes)
|
||||
Diamond tools & armor in the repair config now have a minimum level of 0 (Update your config, temporary hotfix, 2.2 addresses this issue, see notes)
|
||||
Guardian default combat XP multiplier reduced from 3.0 to 1.0 (update config if you want this change)
|
||||
New locale string - 'Server.ConsoleName' the name of the server console, this will be used in place of player names when sending admin notifications out if the command was used from console
|
||||
New locale string - 'Notifications.Admin.Format.Others' style formatting + prefix for admin notifications used in the other new strings below
|
||||
New locale string - 'Notifications.Admin.Format.Self' style formatting + prefix for admin command confirmations sent to the user who executed the command
|
||||
New locale string - 'Notifications.Admin.XPRate.Start.Self' sent to the user who modifies the XP rate regardless of whether or not messages for the event are enabled
|
||||
New locale string - 'Notifications.Admin.XPRate.Start.Others' details of who started an XP rate event are sent to players who have Operator status or admin chat permission when the command to start or modify XP of an event has been issued
|
||||
New locale string - 'Notifications.Admin.XPRate.End.Self' sent to the user who ended the XP rate event regardless of whether or not messages for the event are enabled
|
||||
New locale string - 'Notifications.Admin.XPRate.End.Others' details of who ended an XP rate event are sent to players who have Operator status or admin chat permission when the command to end the event has been issued
|
||||
|
||||
NOTES:
|
||||
Admin notifications currently only reports use of the XP rate command, it will be expanded to support other commands in upcoming patches
|
||||
Add an entry of 'Magma_Block: 30' under Mining in experience.yml section titled "Experience_Values" (or you can delete the file to generate a new one with default values, 2.2 is coming soon which will have brand new configs so you could just wait for that
|
||||
2.2 is the config rewrite, in this rewrite Repair and Salvage configs have been partially rewritten, it is not intended for Diamond Repair to take until level 50 in Standard level scaling to be available, since it'd be redundant to fix this in 2.1.X when 2.2 fixes it and its about to come out, the minimum level has temporarily been changed to 0. You can either update your repair.vanilla.yml config manually or delete it to generate a new one.
|
||||
|
||||
Admin notifications are a step in the right direction for finding naughty admins in mcMMO, I will be changing which permission node is used for players to be considered an admin in the future as not everyone wants or uses admin chat.
|
||||
I expect to add logging of admin commands which will be viewable in game via command, so you won't have to have access to the servers log files to view who has been executing what.
|
||||
|
||||
Version 2.1.61
|
||||
Fixed the locale string formatting of 'Mining.SubSkill.DoubleDrops.Stat'
|
||||
Updated the Japanese locale (thanks snake0053)
|
||||
Added toggle to turn off event message broadcasts (XP rate) to config.yml - 'General.EventBroadcasts'
|
||||
Added toggle to not inform players of events when they join (XP rate, etc) to config.yml 'General.EventInfoOnPlayerJoin'
|
||||
Added anti-exploit protection for cobble/stone farms which can be automated, to turn this off you can toggle the new config options 'ExploitFix.LavaStoneAndCobbleFarming' in experience.yml
|
||||
|
||||
NOTE: The toggle for event message broadcasts is separate from the titles being shown, that's another config option (titles are the BIG TEXT in the middle of the screen)
|
||||
NOTE: The new anti-stone/cobble automation will not prevent XP gains from obsidian, since Obsidian requires some effort to farm
|
||||
|
||||
Version 2.1.60
|
||||
Fixed a NPE error if a LivingEntity's target was set to null
|
||||
Fixed a bug where tamed mobs could kill themselves if their owner shot them once
|
||||
Corrected a typo when naming entities summoned by COTW (Locale string - Taming.Summon.Name.Format)
|
||||
Fixed a bug where tamed mobs could have hearts instead of their name in their own death messages
|
||||
Fixed a bug where multi-block crops would fail to double/triple drop (Sugar Cane, Cactus, etc)
|
||||
Optimized the bonus drop code to reduce overhead
|
||||
|
||||
Version 2.1.59
|
||||
Raised the overfishing limit from 3 to 10
|
||||
Improved the overfishing messages to be more clear about its mechanics
|
||||
Overfishing locale keys renamed "Fishing.ScarcityTip" and "Fishing.LowResourcesTip"
|
||||
|
||||
NOTES: This and other exploit prevention measures are much more customizable in 2.2, which shouldn't be too far off.
|
||||
|
||||
Version 2.1.58
|
||||
Fixed the wrong locale string being used for Mining Double Drops
|
||||
|
||||
Version 2.1.57
|
||||
Fixed an incredibly rare dupe bug that only happened with VERY specific config options that was extremely difficult to pull off
|
||||
|
||||
Version 2.1.56
|
||||
Very large update to the zh_CN Chinese language file (thanks Dream_m)
|
||||
|
||||
Version 2.1.55
|
||||
Fixed a bug that could occur when adding UUIDs to old outdated DBs
|
||||
|
||||
Version 2.1.54
|
||||
Fixed a bug where the Skill 'Understanding the Art' was preventing vanilla experience orbs from furnaces
|
||||
Fixed 'Understanding the Art' not correctly boosting vanilla XP from furnaces
|
||||
|
||||
Version 2.1.53
|
||||
Fixed a critical bug where players earned too much XP
|
||||
|
||||
Version 2.1.52
|
||||
Updated Japanese locale (thanks snake0053)
|
||||
Added a toggle for the early game XP boost to experience.yml 'EarlyGameBoost.Enabled'
|
||||
Added a max level multiplier for determining early game boosts cutoff to experience.yml 'EarlyGameBoost.MaxLevelMultiplier'
|
||||
|
||||
Version 2.1.51
|
||||
You can now customize a locale outside of the JAR! (Thanks mikroskeem)
|
||||
Added a new locale reload command 'mmolocalereload' (Thanks mikroskeem)
|
||||
Locales can now be overriden by placing a file with an appropriate name inside /plugins/mcMMO/locales/ (Thanks mikroskeem)
|
||||
|
||||
NOTES
|
||||
You can find the up to date current en_US locale entries here: https://github.com/mcMMO-Dev/mcMMO/blob/master/src/main/resources/locale/locale_en_US.properties
|
||||
You do NOT have to replace the whole locale, you can replace only the strings you want to!
|
||||
Locales only support ASCII and UTF16 characters at the moment, so you'll need to run special characters through a UTF16 converter (google it) to get them to work, I'll be fixing this in the future!
|
||||
The locale name must match the internal file you are overriding (ie: locale_en_US.properties)
|
||||
Locale will first check for a users locale file, if it doesn't exist it will use internal resources (files inside the JAR)
|
||||
If a locale is found, it will use locale entries from that file, if any entries are missing, it will use entries from en_US inside the JAR
|
||||
The locale file names are structured like this 'locale_XX_XX.properties', replace XX with your country codes, if you are not overriding en_US you will have to change the targetted locale inside config.yml
|
||||
|
||||
Version 2.1.50
|
||||
Fixed a bug where early game XP boost (level 1-5) didn't function in certain circumstances
|
||||
Updated German locale (thanks OverCrave)
|
||||
Added missing Herbalism XP values for: Bamboo, Cornflower, Lily of the valley, Wither rose (thanks Zed-I)
|
||||
Added missing Mining XP values for: Stone Bricks, Cracked Stone Bricks, Mossy Stone Bricks, Chiseled Stone Bricks, Prismarine Bricks, Dark Prismarine, Sea Lantern (thanks Zed-I)
|
||||
Added missing Combat XP multiplier for: Wandering Trader (thanks Zed-I)
|
||||
|
||||
Notes:
|
||||
If you haven't upgraded mcMMO since version 2.1.47 or before you will not need to do these steps as the experience file will update once automatically.
|
||||
You can either delete experience.yml to generate a new one or edit it manually
|
||||
This is what the default experience.yml looks like
|
||||
https://paste.gg/p/anonymous/946f62ce7dff4ab7a87cae70c0266313
|
||||
|
||||
|
||||
Version 2.1.49
|
||||
Added sweet berry bush to Herbalism XP (Update your experience.yml config or delete it to gen a new one)
|
||||
Fixed a bug where falling blocks were not marked as unnatural in water
|
||||
Fixed a bug where tool lower notifications were being called async when they are sync only
|
||||
Fixed NPE when checking inventory location
|
||||
Improved herbalism compatibility with anti-cheat (thanks LogGits)
|
||||
|
||||
NOTE: This is what the default herbalism XP table looks like
|
||||
https://paste.gg/p/anonymous/1022088f20cf44298870930e9bc58e8a
|
||||
|
||||
Version 2.1.48
|
||||
1.14 Support
|
||||
Added Cats, Foxes, and Pandas to Taming XP rewards
|
||||
Added Cats, Foxes, Pandas, Trader Llamas, Pillagers, and Ravagers to Combat XP rewards
|
||||
"Experience" section of experience.yml has been renamed to "Experience_Values"
|
||||
Dodge now gives 800 XP
|
||||
Roll now gives 600 XP
|
||||
Fall now gives 600 XP
|
||||
|
||||
The first 5/50 levels of skills now give large amounts of XP so players get key early skills much faster
|
||||
Note: First 5 in Standard, first 50 in Retro
|
||||
|
||||
Dev Notes:
|
||||
I will be making a write up soon explaining near future plans for mcMMO and what is going on with the config update, abstraction update, etc...
|
||||
Currently this version of mcMMO will work on both 1.13 and 1.14, in the abstraction update I will expand compatible versions of mcMMO to include: 1.14 / 1.13.2 / 1.12.2 / 1.8.8 / Sponge 1.14
|
||||
It is not necessary to update your configs if you are upgrading from 1.13 -> 1.14, however if you had custom XP values in your experience.yml you will want to update your config.
|
||||
Acrobatics XP was buffed since many AFK counter-measures were put into place to prevent repetitive grinding.
|
||||
Experience node in experience.yml was renamed to "automatically" update configs for the new stuff 1.14
|
||||
There are 4 updates planned for mcMMO, including a patreon rewards update, a large content update, a config update, and backwards compatibility for 1.13/1.12/1.8.8 and support for Sponge
|
||||
|
||||
Version 2.1.47
|
||||
Fix NPE when party leader is offline and players grab a party list
|
||||
|
||||
894
LICENSE
894
LICENSE
@@ -1,346 +1,674 @@
|
||||
TERMS AND CONDITIONS
|
||||
0. Definitions.
|
||||
“This License” refers to version 3 of the GNU General Public License.
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”.
|
||||
“Licensees” and “recipients” may be individuals or organizations.
|
||||
Preamble
|
||||
|
||||
To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission,
|
||||
other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work
|
||||
“based on” the earlier work.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
A “covered work” means either the unmodified Program or a work based on the Program.
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily
|
||||
liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy.
|
||||
Propagation includes copying, distribution (with or without modification), making available to the public, and in some
|
||||
countries other activities as well.
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction
|
||||
with a user through a computer network, with no transfer of a copy, is not conveying.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and
|
||||
prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no
|
||||
warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this
|
||||
License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any
|
||||
non-source form of a work.
|
||||
1. Source Code.
|
||||
|
||||
A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or,
|
||||
in the case of interfaces specified for a particular programming language, one that is widely used among developers
|
||||
working in that language.
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in
|
||||
the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to
|
||||
enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is
|
||||
available to the public in source code form. A “Major Component”, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a
|
||||
compiler used to produce the work, or an object code interpreter used to run it.
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and
|
||||
(for an executable work) run the object code and to modify the work, including scripts to control those activities.
|
||||
However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs
|
||||
which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding
|
||||
Source includes interface definition files associated with source files for the work, and the source code for shared
|
||||
libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data
|
||||
communication or control flow between those subprograms and other parts of the work.
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the
|
||||
Corresponding Source.
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
2. Basic Permissions.
|
||||
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided
|
||||
the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program.
|
||||
The output from running a covered work is covered by this License only if the output, given its content, constitutes a
|
||||
covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without conditions so long as your license
|
||||
otherwise remains in force. You may convey covered works to others for the sole purpose of having them make
|
||||
modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do not control copyright. Those thus making or running
|
||||
the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that
|
||||
prohibit them from making any copies of your copyrighted material outside their relationship with you.
|
||||
2. Basic Permissions.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not
|
||||
allowed; section 10 makes it unnecessary.
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling
|
||||
obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
|
||||
restricting circumvention of such measures.
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the
|
||||
extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you
|
||||
disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users,
|
||||
your or third parties' legal rights to forbid circumvention of technological measures.
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating
|
||||
that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices
|
||||
of the absence of any warranty; and give all recipients a copy of this License along with the Program.
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for
|
||||
a fee.
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source
|
||||
code under the terms of section 4, provided that you also meet all of these conditions:
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
|
||||
b) The work must carry prominent notices stating that it is released under this License and any conditions added under
|
||||
section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
|
||||
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all
|
||||
its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way,
|
||||
but it does not invalidate such permission if you have separately received it.
|
||||
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has
|
||||
interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
|
||||
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of
|
||||
the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or
|
||||
distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the
|
||||
access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other parts of the aggregate.
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License, in one of these ways:
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied
|
||||
by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
|
||||
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied
|
||||
by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for
|
||||
that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all
|
||||
the software in the product that is covered by this License, on a durable physical medium customarily used for software
|
||||
interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2)
|
||||
access to copy the Corresponding Source from a network server at no charge.
|
||||
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source.
|
||||
This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an
|
||||
offer, in accord with subsection 6b.
|
||||
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent
|
||||
access to the Corresponding Source in the same way through the same place at no further charge. You need not require
|
||||
recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a
|
||||
network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports
|
||||
equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it
|
||||
is available for as long as needed to satisfy these requirements.
|
||||
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and
|
||||
Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
|
||||
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library,
|
||||
need not be included in conveying the object code work.
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used
|
||||
for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In
|
||||
determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a
|
||||
particular product received by a particular user, “normally used” refers to a typical or common use of that class of
|
||||
product, regardless of the status of the particular user or of the way in which the particular user actually uses, or
|
||||
expects or is expected to use, the product. A product is a consumer product regardless of whether the product has
|
||||
substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of
|
||||
the product.
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information
|
||||
required to install and execute modified versions of a covered work in that User Product from a modified version of its
|
||||
Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code
|
||||
is in no case prevented or interfered with solely because modification has been made.
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the
|
||||
conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to
|
||||
the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding
|
||||
Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not
|
||||
apply if neither you nor any third party retains the ability to install modified object code on the User Product (for
|
||||
example, the work has been installed in ROM).
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement to continue to provide support
|
||||
service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product
|
||||
in which it has been modified or installed. Access to a network may be denied when the modification itself materially
|
||||
and adversely affects the operation of the network or violates the rules and protocols for communication across the
|
||||
network.
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format
|
||||
that is publicly documented (and with an implementation available to the public in source code form), and must require
|
||||
no special password or key for unpacking, reading or copying.
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
7. Additional Terms.
|
||||
“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its
|
||||
conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were
|
||||
included in this License, to the extent that they are valid under applicable law. If additional permissions apply only
|
||||
to part of the Program, that part may be used separately under those permissions, but the entire Program remains
|
||||
governed by this License without regard to the additional permissions.
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or
|
||||
from any part of it. (Additional permissions may be written to require their own removal in certain cases when you
|
||||
modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have
|
||||
or can give appropriate copyright permission.
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by
|
||||
the copyright holders of that material) supplement the terms of this License with terms:
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
|
||||
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the
|
||||
Appropriate Legal Notices displayed by works containing it; or
|
||||
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be
|
||||
marked in reasonable ways as different from the original version; or
|
||||
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
|
||||
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
|
||||
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified
|
||||
versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual
|
||||
assumptions directly impose on those licensors and authors.
|
||||
All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the
|
||||
Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with
|
||||
a term that is a further restriction, you may remove that term. If a license document contains a further restriction but
|
||||
permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of
|
||||
that license document, provided that the further restriction does not survive such relicensing or conveying.
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a
|
||||
statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as
|
||||
exceptions; the above requirements apply either way.
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
8. Termination.
|
||||
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to
|
||||
propagate or modify it is void, and will automatically terminate your rights under this License (including any patent
|
||||
licenses granted under the third paragraph of section 11).
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated
|
||||
(a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and
|
||||
(b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days
|
||||
after the cessation.
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you
|
||||
of the violation by some reasonable means, this is the first time you have received notice of violation of this License
|
||||
(for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses of parties who have received copies or
|
||||
rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not
|
||||
qualify to receive new licenses for the same material under section 10.
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a
|
||||
covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not
|
||||
require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered
|
||||
work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run,
|
||||
modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third
|
||||
parties with this License.
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or
|
||||
subdividing an organization, or merging organizations. If propagation of a covered work results from an entity
|
||||
transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work
|
||||
the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with
|
||||
reasonable efforts.
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For
|
||||
example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License,
|
||||
and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent
|
||||
claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
11. Patents.
|
||||
A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program
|
||||
is based. The work thus licensed is called the contributor's “contributor version”.
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already
|
||||
acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or
|
||||
selling its contributor version, but do not include claims that would be infringed only as a consequence of further
|
||||
modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent
|
||||
sublicenses in a manner consistent with the requirements of this License.
|
||||
11. Patents.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential
|
||||
patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its
|
||||
contributor version.
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to
|
||||
enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To
|
||||
“grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against
|
||||
the party.
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not
|
||||
available for anyone to copy, free of charge and under the terms of this License, through a publicly available network
|
||||
server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available,
|
||||
or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a
|
||||
manner consistent with the requirements of this License, to extend the patent license to downstream recipients.
|
||||
“Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in
|
||||
a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in
|
||||
that country that you have reason to believe are valid.
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring
|
||||
conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing
|
||||
them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is
|
||||
automatically extended to all recipients of the covered work and works based on it.
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of,
|
||||
or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You
|
||||
may not convey a covered work if you are a party to an arrangement with a third party that is in the business of
|
||||
distributing software, under which you make payment to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a
|
||||
discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from
|
||||
those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered
|
||||
work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to
|
||||
infringement that may otherwise be available to you under applicable patent law.
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this
|
||||
License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to
|
||||
satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence
|
||||
you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further
|
||||
conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License
|
||||
would be to refrain entirely from conveying the Program.
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work
|
||||
licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the
|
||||
resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special
|
||||
requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply
|
||||
to the combination as such.
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to
|
||||
time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new
|
||||
problems or concerns.
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the
|
||||
GNU General Public License “or any later version” applies to it, you have the option of following the terms and
|
||||
conditions either of that numbered version or of any later version published by the Free Software Foundation. If the
|
||||
Program does not specify a version number of the GNU General Public License, you may choose any version ever published
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used,
|
||||
that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the
|
||||
Program.
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However, no additional obligations are imposed
|
||||
on any author or copyright holder as a result of your choosing to follow a later version.
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING
|
||||
THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
|
||||
IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU
|
||||
ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
16. Limitation of Liability.
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO
|
||||
MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
|
||||
LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
|
||||
TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to
|
||||
their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil
|
||||
liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program
|
||||
in return for a fee.
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
31
README.md
31
README.md
@@ -1,32 +1,32 @@
|
||||
# mcMMO
|
||||
## The RPG lovers mod
|
||||
|
||||
## The #1 RPG Mod for Minecraft
|
||||
[](https://translate.mcmmo.org/engage/mcmmo/?utm_source=widget)
|
||||
## Website
|
||||
I'm working on a brand new website for mcMMO
|
||||
|
||||
You can check it out here http://www.mcmmo.org
|
||||
Spigot Resource: https://www.spigotmc.org/resources/official-mcmmo-original-author-returns.64348/
|
||||
|
||||
Spigot Resource: https://spigot.mcmmo.org
|
||||
|
||||
I plan to post links to our new wiki (its still under development), downloads, and dev blogs there.
|
||||
|
||||
|
||||
### Builds
|
||||
Currently, you can obtain our builds via the Spigot resource page: http://spigot.mcmmo.org
|
||||
|
||||
### Brief Description
|
||||
The goal of mcMMO is to take core Minecraft game mechanics and expand them into add an extensive and quality RPG experience. Everything in mcMMO has been carefully thought out and is constantly being improved upon. Currently, mcMMO adds fourteen unique skills to train and level in. Each of these skills is highly customizable through our configuration files, allowing server admins to tweak mcMMO to best suit the needs of his or her server. Know that the mcMMO team is dedicated to providing an ever-evolving experience, and that we carefully read all feedback and bug reports in order to evaluate and balance the mechanics of mcMMO in every update.
|
||||
The goal of mcMMO is to take core Minecraft game mechanics and expand them into an extensive and quality RPG experience. Everything in mcMMO has been carefully thought out and is constantly being improved upon. Currently, mcMMO adds fourteen unique skills to train and level in. Each of these skills is highly customizable through our configuration files, allowing server admins to tweak mcMMO to best suit the needs of his or her server. Know that the mcMMO team is dedicated to providing an ever-evolving experience, and that we carefully read all feedback and bug reports in order to evaluate and balance the mechanics of mcMMO in every update.
|
||||
|
||||
## About the Team
|
||||
In December 2018 nossr50 returned as project lead for mcMMO once again to develop and improve mcMMO.
|
||||
The mcMMO team currently has two members, nossr50 (lead) and t00thpick1 (classic maintainer).
|
||||
mcMMO is currently developed almost entirely by nossr50, many thanks go out to the many developers who have worked on the project over the years.
|
||||
|
||||
### Project Lead & Founder
|
||||
In December 2018, the original author and creator of mcMMO (nossr50) returned and took over the role of project lead once again, to develop and improve mcMMO.
|
||||
#### Project Lead & Founder
|
||||
[](https://github.com/nossr50)
|
||||
|
||||
Other mcMMO Projects
|
||||
#### mcMMO Devs
|
||||
[](https://github.com/nossr50)
|
||||
[](https://github.com/kashike)
|
||||
[](https://github.com/electronicboy)
|
||||
|
||||
### Classic Maintainer
|
||||
#### Classic Maintainer
|
||||
[](https://github.com/t00thpick1)
|
||||
|
||||
## Former Team Members
|
||||
@@ -50,10 +50,13 @@ mcMMO uses Maven 3 to manage dependencies, packaging, and shading of necessary c
|
||||
The typical command used to build mcMMO is: `mvn clean package install`
|
||||
|
||||
Required Libraries:
|
||||
* Bukkit
|
||||
* Spigot
|
||||
* JUnit
|
||||
* WorldGuard 7
|
||||
* bStats Bukkit
|
||||
|
||||
http://spigot.mcmmo.org for more up to date information.
|
||||
https://spigot.mcmmo.org for more up to date information.
|
||||
|
||||
Resource Page
|
||||
|
||||
https://www.spigotmc.org/resources/official-mcmmo-original-author-returns.64348/
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -2,7 +2,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gmail.nossr50.mcMMO</groupId>
|
||||
<artifactId>mcMMO</artifactId>
|
||||
<version>2.1.47</version>
|
||||
<version>2.1.118-SNAPSHOT</version>
|
||||
<name>mcMMO</name>
|
||||
<url>https://github.com/mcMMO-Dev/mcMMO</url>
|
||||
<scm>
|
||||
@@ -167,13 +167,13 @@
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.13.2-R0.1-SNAPSHOT</version>
|
||||
<version>1.15.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sk89q.worldguard</groupId>
|
||||
<artifactId>worldguard-core</artifactId>
|
||||
<version>7.0.0-SNAPSHOT</version>
|
||||
<version>7.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sk89q.worldguard</groupId>
|
||||
|
||||
30
src/main/java/com/gmail/nossr50/api/DatabaseAPI.java
Normal file
30
src/main/java/com/gmail/nossr50/api/DatabaseAPI.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.gmail.nossr50.api;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.PlayerProfile;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DatabaseAPI {
|
||||
|
||||
/**
|
||||
* Checks if a player exists in the mcMMO Database
|
||||
* @param uuid player UUID
|
||||
* @return true if the player exists in the DB, false if they do not
|
||||
*/
|
||||
public boolean doesPlayerExistInDB(String uuid) {
|
||||
return doesPlayerExistInDB(UUID.fromString(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player exists in the mcMMO Database
|
||||
* @param uuid player UUID
|
||||
* @return true if the player exists in the DB, false if they do not
|
||||
*/
|
||||
public boolean doesPlayerExistInDB(UUID uuid) {
|
||||
PlayerProfile playerProfile = mcMMO.getDatabaseManager().loadPlayerProfile(uuid);
|
||||
|
||||
return playerProfile.isLoaded();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -649,11 +649,28 @@ public final class ExperienceAPI {
|
||||
* @return the level of a given skill
|
||||
*
|
||||
* @throws InvalidSkillException if the given skill is not valid
|
||||
* @deprecated Use getLevel(Player player, PrimarySkillType skillType) instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static int getLevel(Player player, String skillType) {
|
||||
return getPlayer(player).getSkillLevel(getSkillType(skillType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level a player has in a specific skill.
|
||||
* </br>
|
||||
* This function is designed for API usage.
|
||||
*
|
||||
* @param player The player to get the level for
|
||||
* @param skillType The skill to get the level for
|
||||
* @return the level of a given skill
|
||||
*
|
||||
* @throws InvalidSkillException if the given skill is not valid
|
||||
*/
|
||||
public static int getLevel(Player player, PrimarySkillType skillType) {
|
||||
return getPlayer(player).getSkillLevel(skillType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level an offline player has in a specific skill.
|
||||
* </br>
|
||||
@@ -995,7 +1012,7 @@ public final class ExperienceAPI {
|
||||
* @throws InvalidFormulaTypeException if the given formulaType is not valid
|
||||
*/
|
||||
public static int getXpNeededToLevel(int level) {
|
||||
return mcMMO.getFormulaManager().getCachedXpToLevel(level, ExperienceConfig.getInstance().getFormulaType());
|
||||
return mcMMO.getFormulaManager().getXPtoNextLevel(level, ExperienceConfig.getInstance().getFormulaType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1009,7 +1026,7 @@ public final class ExperienceAPI {
|
||||
* @throws InvalidFormulaTypeException if the given formulaType is not valid
|
||||
*/
|
||||
public static int getXpNeededToLevel(int level, String formulaType) {
|
||||
return mcMMO.getFormulaManager().getCachedXpToLevel(level, getFormulaType(formulaType));
|
||||
return mcMMO.getFormulaManager().getXPtoNextLevel(level, getFormulaType(formulaType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1085,7 +1102,7 @@ public final class ExperienceAPI {
|
||||
PlayerProfile profile = getOfflineProfile(playerUniqueId);
|
||||
|
||||
profile.addXp(skill, XP);
|
||||
profile.save();
|
||||
profile.save(true);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.bukkit.command.CommandSender;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
public class McImportCommand implements CommandExecutor {
|
||||
int fileAmount;
|
||||
@@ -129,7 +130,7 @@ public class McImportCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
FileWriter out = null;
|
||||
String type = modConfigType.name().toLowerCase();
|
||||
String type = modConfigType.name().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
for (String modName : materialNames.keySet()) {
|
||||
File outputFile = new File(outputFilePath, modName + "." + type + ".yml");
|
||||
|
||||
@@ -23,6 +23,7 @@ public class McmmoCommand implements CommandExecutor {
|
||||
String description = LocaleLoader.getString("mcMMO.Description");
|
||||
String[] mcSplit = description.split(",");
|
||||
sender.sendMessage(mcSplit);
|
||||
sender.sendMessage(LocaleLoader.getString("mcMMO.Description.FormerDevs"));
|
||||
|
||||
if (Config.getInstance().getDonateMessageEnabled()) {
|
||||
sender.sendMessage(LocaleLoader.getString("MOTD.Donate"));
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gmail.nossr50.commands;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.util.commands.CommandUtils;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.command.Command;
|
||||
@@ -14,6 +15,10 @@ import java.util.List;
|
||||
public class McnotifyCommand implements TabExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (CommandUtils.noConsoleUsage(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer((Player) sender);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gmail.nossr50.commands;
|
||||
|
||||
import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.notifications.SensitiveCommandType;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
@@ -44,8 +46,14 @@ public class XprateCommand implements TabExecutor {
|
||||
10, 10*20, 20);
|
||||
}
|
||||
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.Stop"));
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.Stop.Subtitle"));
|
||||
if(Config.getInstance().broadcastEventMessages())
|
||||
{
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.Stop"));
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.Stop.Subtitle"));
|
||||
}
|
||||
|
||||
//Admin notification
|
||||
NotificationManager.processSensitiveCommandNotification(sender, SensitiveCommandType.XPRATE_END);
|
||||
|
||||
mcMMO.p.toggleXpEventEnabled();
|
||||
}
|
||||
@@ -83,20 +91,22 @@ public class XprateCommand implements TabExecutor {
|
||||
|
||||
ExperienceConfig.getInstance().setExperienceGainsGlobalMultiplier(newXpRate);
|
||||
|
||||
if (mcMMO.p.isXPEventEnabled()) {
|
||||
if(AdvancedConfig.getInstance().useTitlesForXPEvent())
|
||||
{
|
||||
NotificationManager.broadcastTitle(mcMMO.p.getServer(),
|
||||
LocaleLoader.getString("Commands.Event.Start"),
|
||||
LocaleLoader.getString("Commands.Event.XP", newXpRate),
|
||||
10, 10*20, 20);
|
||||
}
|
||||
if(AdvancedConfig.getInstance().useTitlesForXPEvent())
|
||||
{
|
||||
NotificationManager.broadcastTitle(mcMMO.p.getServer(),
|
||||
LocaleLoader.getString("Commands.Event.Start"),
|
||||
LocaleLoader.getString("Commands.Event.XP", newXpRate),
|
||||
10, 10*20, 20);
|
||||
}
|
||||
|
||||
if(Config.getInstance().broadcastEventMessages())
|
||||
{
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.Start"));
|
||||
mcMMO.p.getServer().broadcastMessage(LocaleLoader.getString("Commands.Event.XP", newXpRate));
|
||||
}
|
||||
else {
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.xprate.modified", newXpRate));
|
||||
}
|
||||
|
||||
//Admin notification
|
||||
NotificationManager.processSensitiveCommandNotification(sender, SensitiveCommandType.XPRATE_MODIFY, String.valueOf(newXpRate));
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gmail.nossr50.commands.admin;
|
||||
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* @author Mark Vainomaa
|
||||
*/
|
||||
public final class McmmoReloadLocaleCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
if (!Permissions.reloadlocale(sender)) {
|
||||
sender.sendMessage(command.getPermissionMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
LocaleLoader.reloadLocale();
|
||||
sender.sendMessage(LocaleLoader.getString("Locale.Reloaded"));
|
||||
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.gmail.nossr50.commands.admin;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PlayerDebugCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if(sender instanceof Player) {
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer((Player) sender);
|
||||
mcMMOPlayer.toggleDebugMode(); //Toggle debug mode
|
||||
NotificationManager.sendPlayerInformationChatOnlyPrefixed(mcMMOPlayer.getPlayer(), "Commands.Mmodebug.Toggle", String.valueOf(mcMMOPlayer.isDebugMode()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.commands.CommandUtils;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
@@ -12,6 +13,7 @@ import org.bukkit.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class McremoveCommand implements TabExecutor {
|
||||
@Override
|
||||
@@ -24,7 +26,13 @@ public class McremoveCommand implements TabExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mcMMO.getDatabaseManager().removeUser(playerName)) {
|
||||
UUID uuid = null;
|
||||
|
||||
if(Bukkit.getPlayer(playerName) != null) {
|
||||
uuid = Bukkit.getPlayer(playerName).getUniqueId();
|
||||
}
|
||||
|
||||
if (mcMMO.getDatabaseManager().removeUser(playerName, uuid)) {
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.mcremove.Success", playerName));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -30,7 +30,7 @@ public class AddlevelsCommand extends ExperienceCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
EventUtils.handleLevelChangeEvent(player, skill, value, xpRemoved, true, XPGainReason.COMMAND);
|
||||
EventUtils.tryLevelChangeEvent(player, skill, value, xpRemoved, true, XPGainReason.COMMAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,13 +11,15 @@ import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class ConvertExperienceCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
switch (args.length) {
|
||||
case 2:
|
||||
FormulaType previousType = mcMMO.getFormulaManager().getPreviousFormulaType();
|
||||
FormulaType newType = FormulaType.getFormulaType(args[1].toUpperCase());
|
||||
FormulaType newType = FormulaType.getFormulaType(args[1].toUpperCase(Locale.ENGLISH));
|
||||
|
||||
if (newType == FormulaType.UNKNOWN) {
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.mcconvert.Experience.Invalid"));
|
||||
|
||||
@@ -36,7 +36,7 @@ public class MmoeditCommand extends ExperienceCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
EventUtils.handleLevelChangeEventEdit(player, skill, value, xpRemoved, value > skillLevel, XPGainReason.COMMAND, skillLevel);
|
||||
EventUtils.tryLevelEditEvent(player, skill, value, xpRemoved, value > skillLevel, XPGainReason.COMMAND, skillLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -126,7 +126,7 @@ public class SkillresetCommand implements TabExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
EventUtils.handleLevelChangeEvent(player, skill, levelsRemoved, xpRemoved, false, XPGainReason.COMMAND);
|
||||
EventUtils.tryLevelChangeEvent(player, skill, levelsRemoved, xpRemoved, false, XPGainReason.COMMAND);
|
||||
}
|
||||
|
||||
protected boolean permissionsCheckSelf(CommandSender sender) {
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class PartyItemShareCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
@@ -32,7 +34,7 @@ public class PartyItemShareCommand implements CommandExecutor {
|
||||
|
||||
switch (args.length) {
|
||||
case 2:
|
||||
ShareMode mode = ShareMode.getShareMode(args[1].toUpperCase());
|
||||
ShareMode mode = ShareMode.getShareMode(args[1].toUpperCase(Locale.ENGLISH));
|
||||
|
||||
if (mode == null) {
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.Usage.2", "party", "itemshare", "<NONE | EQUAL | RANDOM>"));
|
||||
@@ -57,7 +59,7 @@ public class PartyItemShareCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
try {
|
||||
handleToggleItemShareCategory(party, ItemShareType.valueOf(args[1].toUpperCase()), toggle);
|
||||
handleToggleItemShareCategory(party, ItemShareType.valueOf(args[1].toUpperCase(Locale.ENGLISH)), toggle);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.Usage.2", "party", "itemshare", "<loot | mining | herbalism | woodcutting | misc> <true | false>"));
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardManager;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardUtils;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -168,6 +169,23 @@ public class PtpCommand implements TabExecutor {
|
||||
McMMOPlayer mcMMOTarget = UserManager.getPlayer(targetName);
|
||||
Player target = mcMMOTarget.getPlayer();
|
||||
|
||||
|
||||
if (Config.getInstance().getPTPCommandWorldPermissions()) {
|
||||
World targetWorld = target.getWorld();
|
||||
World playerWorld = player.getWorld();
|
||||
|
||||
if (!Permissions.partyTeleportAllWorlds(player)) {
|
||||
if (!Permissions.partyTeleportWorld(target, targetWorld)) {
|
||||
player.sendMessage(LocaleLoader.getString("Commands.ptp.NoWorldPermissions", targetWorld.getName()));
|
||||
return;
|
||||
}
|
||||
else if (targetWorld != playerWorld && !Permissions.partyTeleportWorld(player, targetWorld)) {
|
||||
player.sendMessage(LocaleLoader.getString("Commands.ptp.NoWorldPermissions", targetWorld.getName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PartyTeleportRecord ptpRecord = mcMMOTarget.getPartyTeleportRecord();
|
||||
|
||||
if (!ptpRecord.isConfirmRequired()) {
|
||||
|
||||
@@ -36,10 +36,6 @@ public class InspectCommand implements TabExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CommandUtils.inspectOffline(sender, profile, Permissions.inspectOffline(sender))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Config.getInstance().getScoreboardsEnabled() && sender instanceof Player && Config.getInstance().getInspectUseBoard()) {
|
||||
ScoreboardManager.enablePlayerInspectScoreboard((Player) sender, profile);
|
||||
|
||||
@@ -70,10 +66,8 @@ public class InspectCommand implements TabExecutor {
|
||||
Player target = mcMMOPlayer.getPlayer();
|
||||
|
||||
if (CommandUtils.hidden(sender, target, Permissions.inspectHidden(sender))) {
|
||||
if (!Permissions.inspectOffline(sender)) {
|
||||
sender.sendMessage(LocaleLoader.getString("Inspect.Offline"));
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(LocaleLoader.getString("Inspect.Offline"));
|
||||
return true;
|
||||
}
|
||||
else if (CommandUtils.tooFar(sender, target, Permissions.inspectFar(sender))) {
|
||||
return true;
|
||||
|
||||
@@ -62,9 +62,6 @@ public class McrankCommand implements TabExecutor {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (CommandUtils.inspectOffline(sender, mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
display(sender, playerName);
|
||||
return true;
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ArcheryCommand extends SkillCommand {
|
||||
|
||||
if(canUseSubskill(player, SubSkillType.ARCHERY_ARCHERY_LIMIT_BREAK)) {
|
||||
messages.add(getStatMessage(SubSkillType.ARCHERY_ARCHERY_LIMIT_BREAK,
|
||||
String.valueOf(CombatUtils.getLimitBreakDamage(player, SubSkillType.ARCHERY_ARCHERY_LIMIT_BREAK))));
|
||||
String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player, SubSkillType.ARCHERY_ARCHERY_LIMIT_BREAK, 1000))));
|
||||
}
|
||||
|
||||
return messages;
|
||||
|
||||
@@ -98,7 +98,7 @@ public class AxesCommand extends SkillCommand {
|
||||
|
||||
if(canUseSubskill(player, SubSkillType.AXES_AXES_LIMIT_BREAK)) {
|
||||
messages.add(getStatMessage(SubSkillType.AXES_AXES_LIMIT_BREAK,
|
||||
String.valueOf(CombatUtils.getLimitBreakDamage(player, SubSkillType.AXES_AXES_LIMIT_BREAK))));
|
||||
String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player, SubSkillType.AXES_AXES_LIMIT_BREAK, 1000))));
|
||||
}
|
||||
|
||||
return messages;
|
||||
|
||||
@@ -3,8 +3,10 @@ package com.gmail.nossr50.commands.skills;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.skills.excavation.ExcavationManager;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.TextComponentFactory;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -43,6 +45,8 @@ public class ExcavationCommand extends SkillCommand {
|
||||
protected List<String> statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) {
|
||||
List<String> messages = new ArrayList<String>();
|
||||
|
||||
ExcavationManager excavationManager = UserManager.getPlayer(player).getExcavationManager();
|
||||
|
||||
if (canGigaDrill) {
|
||||
messages.add(getStatMessage(SubSkillType.EXCAVATION_GIGA_DRILL_BREAKER, gigaDrillBreakerLength)
|
||||
+ (hasEndurance ? LocaleLoader.getString("Perks.ActivationTime.Bonus", gigaDrillBreakerLengthEndurance) : ""));
|
||||
@@ -50,6 +54,14 @@ public class ExcavationCommand extends SkillCommand {
|
||||
//messages.add(LocaleLoader.getString("Excavation.Effect.Length", gigaDrillBreakerLength) + (hasEndurance ? LocaleLoader.getString("Perks.ActivationTime.Bonus", gigaDrillBreakerLengthEndurance) : ""));
|
||||
}
|
||||
|
||||
if(canUseSubskill(player, SubSkillType.EXCAVATION_ARCHAEOLOGY)) {
|
||||
messages.add(getStatMessage(false, false, SubSkillType.EXCAVATION_ARCHAEOLOGY,
|
||||
percent.format(excavationManager.getArchaelogyExperienceOrbChance() / 100.0D)));
|
||||
messages.add(getStatMessage(true, false, SubSkillType.EXCAVATION_ARCHAEOLOGY,
|
||||
String.valueOf(excavationManager.getExperienceOrbsReward())));
|
||||
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class MiningCommand extends SkillCommand {
|
||||
canBiggerBombs = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_BIGGER_BOMBS) && Permissions.biggerBombs(player);
|
||||
canBlast = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_BLAST_MINING) && Permissions.remoteDetonation(player);
|
||||
canDemoExpert = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_DEMOLITIONS_EXPERTISE) && Permissions.demolitionsExpertise(player);
|
||||
canDoubleDrop = Permissions.isSubSkillEnabled(player, SubSkillType.MINING_DOUBLE_DROPS) && !skill.getDoubleDropsDisabled();
|
||||
canDoubleDrop = canUseSubskill(player, SubSkillType.MINING_DOUBLE_DROPS);
|
||||
canSuperBreaker = RankUtils.hasUnlockedSubskill(player, SubSkillType.MINING_SUPER_BREAKER) && Permissions.superBreaker(player);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class MiningCommand extends SkillCommand {
|
||||
}
|
||||
|
||||
if (canDoubleDrop) {
|
||||
messages.add(getStatMessage(SubSkillType.HERBALISM_DOUBLE_DROPS, doubleDropChance)
|
||||
messages.add(getStatMessage(SubSkillType.MINING_DOUBLE_DROPS, doubleDropChance)
|
||||
+ (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", doubleDropChanceLucky) : ""));
|
||||
//messages.add(LocaleLoader.getString("Mining.Effect.DropChance", doubleDropChance) + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", doubleDropChanceLucky) : ""));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SalvageCommand extends SkillCommand {
|
||||
private boolean canAdvancedSalvage;
|
||||
private boolean canScrapCollector;
|
||||
private boolean canArcaneSalvage;
|
||||
|
||||
public SalvageCommand() {
|
||||
@@ -30,7 +30,7 @@ public class SalvageCommand extends SkillCommand {
|
||||
|
||||
@Override
|
||||
protected void permissionsCheck(Player player) {
|
||||
canAdvancedSalvage = canUseSubskill(player, SubSkillType.SALVAGE_ADVANCED_SALVAGE);
|
||||
canScrapCollector = canUseSubskill(player, SubSkillType.SALVAGE_SCRAP_COLLECTOR);
|
||||
canArcaneSalvage = canUseSubskill(player, SubSkillType.SALVAGE_ARCANE_SALVAGE);
|
||||
}
|
||||
|
||||
@@ -39,9 +39,11 @@ public class SalvageCommand extends SkillCommand {
|
||||
List<String> messages = new ArrayList<String>();
|
||||
SalvageManager salvageManager = UserManager.getPlayer(player).getSalvageManager();
|
||||
|
||||
if (canAdvancedSalvage) {
|
||||
messages.add(LocaleLoader.getString("Ability.Generic.Template", LocaleLoader.getString("Salvage.Ability.Bonus.0"),
|
||||
LocaleLoader.getString("Salvage.Ability.Bonus.1", salvageManager.getSalvageableAmount())));
|
||||
if (canScrapCollector) {
|
||||
messages.add(getStatMessage(false, true,
|
||||
SubSkillType.SALVAGE_SCRAP_COLLECTOR,
|
||||
String.valueOf(RankUtils.getRank(player, SubSkillType.SALVAGE_SCRAP_COLLECTOR)),
|
||||
RankUtils.getHighestRankStr(SubSkillType.SALVAGE_SCRAP_COLLECTOR)));
|
||||
}
|
||||
|
||||
if (canArcaneSalvage) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
import com.gmail.nossr50.util.TextComponentFactory;
|
||||
import com.gmail.nossr50.util.commands.CommandUtils;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.random.RandomChanceUtil;
|
||||
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
|
||||
@@ -29,6 +30,7 @@ import org.bukkit.entity.Player;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class SkillCommand implements TabExecutor {
|
||||
@@ -133,7 +135,7 @@ public abstract class SkillCommand implements TabExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage(LocaleLoader.getString("Guides.Available", skillName, skillName.toLowerCase()));
|
||||
player.sendMessage(LocaleLoader.getString("Guides.Available", skillName, skillName.toLowerCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
private void sendSkillCommandHeader(Player player, McMMOPlayer mcMMOPlayer, int skillValue) {
|
||||
@@ -268,19 +270,17 @@ public abstract class SkillCommand implements TabExecutor {
|
||||
return LocaleLoader.getString(templateKey, LocaleLoader.getString(statDescriptionKey, vars));
|
||||
else
|
||||
{
|
||||
String[] mergedList = addItemToFirstPositionOfArray(LocaleLoader.getString(statDescriptionKey), vars);
|
||||
String[] mergedList = NotificationManager.addItemToFirstPositionOfArray(LocaleLoader.getString(statDescriptionKey), vars);
|
||||
return LocaleLoader.getString(templateKey, mergedList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String[] addItemToFirstPositionOfArray(String itemToAdd, String... existingArray) {
|
||||
String[] newArray = new String[existingArray.length + 1];
|
||||
newArray[0] = itemToAdd;
|
||||
|
||||
System.arraycopy(existingArray, 0, newArray, 1, existingArray.length);
|
||||
|
||||
return newArray;
|
||||
protected String getLimitBreakDescriptionParameter() {
|
||||
if(AdvancedConfig.getInstance().canApplyLimitBreakPVE()) {
|
||||
return "(PVP/PVE)";
|
||||
} else {
|
||||
return "(PVP)";
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void dataCalculations(Player player, float skillValue);
|
||||
|
||||
@@ -103,7 +103,7 @@ public class SwordsCommand extends SkillCommand {
|
||||
|
||||
if(canUseSubskill(player, SubSkillType.SWORDS_SWORDS_LIMIT_BREAK)) {
|
||||
messages.add(getStatMessage(SubSkillType.SWORDS_SWORDS_LIMIT_BREAK,
|
||||
String.valueOf(CombatUtils.getLimitBreakDamage(player, SubSkillType.SWORDS_SWORDS_LIMIT_BREAK))));
|
||||
String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player, SubSkillType.SWORDS_SWORDS_LIMIT_BREAK, 1000))));
|
||||
}
|
||||
|
||||
return messages;
|
||||
|
||||
@@ -116,7 +116,7 @@ public class UnarmedCommand extends SkillCommand {
|
||||
|
||||
if(canUseSubskill(player, SubSkillType.UNARMED_UNARMED_LIMIT_BREAK)) {
|
||||
messages.add(getStatMessage(SubSkillType.UNARMED_UNARMED_LIMIT_BREAK,
|
||||
String.valueOf(CombatUtils.getLimitBreakDamage(player, SubSkillType.UNARMED_UNARMED_LIMIT_BREAK))));
|
||||
String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player, SubSkillType.UNARMED_UNARMED_LIMIT_BREAK, 1000))));
|
||||
}
|
||||
|
||||
return messages;
|
||||
|
||||
@@ -164,18 +164,10 @@ public class AdvancedConfig extends AutoUpdateConfigLoader {
|
||||
reason.add("Skills.Axes.GreaterImpact.BonusDamage should be at least 1!");
|
||||
}
|
||||
|
||||
if (getArmorImpactIncreaseLevel() < 1) {
|
||||
reason.add("Skills.Axes.ArmorImpact.IncreaseLevel should be at least 1!");
|
||||
}
|
||||
|
||||
if (getImpactChance() < 1) {
|
||||
reason.add("Skills.Axes.ArmorImpact.Chance should be at least 1!");
|
||||
}
|
||||
|
||||
if (getArmorImpactMaxDurabilityDamage() < 1) {
|
||||
reason.add("Skills.Axes.ArmorImpact.MaxPercentageDurabilityDamage should be at least 1!");
|
||||
}
|
||||
|
||||
if (getSkullSplitterModifier() < 1) {
|
||||
reason.add("Skills.Axes.SkullSplitter.DamageModifier should be at least 1!");
|
||||
}
|
||||
@@ -644,6 +636,8 @@ public class AdvancedConfig extends AutoUpdateConfigLoader {
|
||||
protected void loadKeys() {}
|
||||
|
||||
/* GENERAL */
|
||||
|
||||
public boolean canApplyLimitBreakPVE() { return config.getBoolean("Skills.General.LimitBreak.AllowPVE", false); }
|
||||
public int getStartingLevel() { return config.getInt("Skills.General.StartingLevel", 1); }
|
||||
|
||||
public boolean allowPlayerTips() {
|
||||
@@ -871,17 +865,8 @@ public class AdvancedConfig extends AutoUpdateConfigLoader {
|
||||
public double getGreaterImpactModifier() { return config.getDouble("Skills.Axes.GreaterImpact.KnockbackModifier", 1.5D); }
|
||||
public double getGreaterImpactBonusDamage() { return config.getDouble("Skills.Axes.GreaterImpact.BonusDamage", 2.0D); }
|
||||
|
||||
public int getArmorImpactIncreaseLevel() {
|
||||
int increaseLevel = config.getInt("Skills.Axes.ArmorImpact.IncreaseLevel", 5);
|
||||
|
||||
if(mcMMO.isRetroModeEnabled())
|
||||
return increaseLevel * 10;
|
||||
|
||||
return increaseLevel;
|
||||
}
|
||||
|
||||
public double getImpactChance() { return config.getDouble("Skills.Axes.ArmorImpact.Chance", 25.0D); }
|
||||
public double getArmorImpactMaxDurabilityDamage() { return config.getDouble("Skills.Axes.ArmorImpact.MaxPercentageDurabilityDamage", 20.0D); }
|
||||
public double getImpactDurabilityDamageMultiplier() { return config.getDouble("Skills.Axes.ArmorImpact.DamagePerRank", 6.5D); }
|
||||
|
||||
public double getSkullSplitterModifier() { return config.getDouble("Skills.Axes.SkullSplitter.DamageModifier", 2.0D); }
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ import com.gmail.nossr50.util.StringUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class Config extends AutoUpdateConfigLoader {
|
||||
@@ -198,41 +198,41 @@ public class Config extends AutoUpdateConfigLoader {
|
||||
reason.add("Cannot use the same item for Repair and Salvage anvils!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWMaterial(EntityType.WOLF) == null) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Item_Material is invalid!!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWMaterial(EntityType.OCELOT) == null) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Item_Material is invalid!!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWMaterial(EntityType.HORSE) == null) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Item_Material is invalid!!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWCost(EntityType.WOLF) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Item_Amount should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWCost(EntityType.OCELOT) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Item_Amount should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWCost(EntityType.HORSE) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Item_Amount should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWAmount(EntityType.WOLF) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Summon_Amount should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWAmount(EntityType.OCELOT) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Summon_Amount should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingCOTWAmount(EntityType.HORSE) <= 0) {
|
||||
reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Summon_Amount should be greater than 0!");
|
||||
}
|
||||
// if (getTamingCOTWMaterial(EntityType.WOLF) == null) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Item_Material is invalid!!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWMaterial(EntityType.OCELOT) == null) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Item_Material is invalid!!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWMaterial(EntityType.HORSE) == null) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Item_Material is invalid!!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWCost(EntityType.WOLF) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Item_Amount should be greater than 0!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWCost(EntityType.OCELOT) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Item_Amount should be greater than 0!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWCost(EntityType.HORSE) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Item_Amount should be greater than 0!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWAmount(EntityType.WOLF) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Wolf.Summon_Amount should be greater than 0!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWAmount(EntityType.OCELOT) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Ocelot.Summon_Amount should be greater than 0!");
|
||||
// }
|
||||
//
|
||||
// if (getTamingCOTWAmount(EntityType.HORSE) <= 0) {
|
||||
// reason.add("Skills.Taming.Call_Of_The_Wild.Horse.Summon_Amount should be greater than 0!");
|
||||
// }
|
||||
|
||||
return noErrorsInConfig(reason);
|
||||
}
|
||||
@@ -278,7 +278,7 @@ public class Config extends AutoUpdateConfigLoader {
|
||||
/* Mob Healthbar */
|
||||
public MobHealthbarType getMobHealthbarDefault() {
|
||||
try {
|
||||
return MobHealthbarType.valueOf(config.getString("Mob_Healthbar.Display_Type", "HEARTS").toUpperCase().trim());
|
||||
return MobHealthbarType.valueOf(config.getString("Mob_Healthbar.Display_Type", "HEARTS").toUpperCase(Locale.ENGLISH).trim());
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return MobHealthbarType.HEARTS;
|
||||
@@ -342,6 +342,7 @@ public class Config extends AutoUpdateConfigLoader {
|
||||
public int getMySQLMaxConnections(PoolIdentifier identifier) { return config.getInt("MySQL.Database.MaxConnections." + StringUtils.getCapitalized(identifier.toString()), 30); }
|
||||
public int getMySQLMaxPoolSize(PoolIdentifier identifier) { return config.getInt("MySQL.Database.MaxPoolSize." + StringUtils.getCapitalized(identifier.toString()), 10); }
|
||||
public boolean getMySQLSSL() { return config.getBoolean("MySQL.Server.SSL", true); }
|
||||
public boolean getMySQLDebug() { return config.getBoolean("MySQL.Debug", false); }
|
||||
|
||||
private String getStringIncludingInts(String key) {
|
||||
String str = config.getString(key);
|
||||
@@ -529,12 +530,17 @@ public class Config extends AutoUpdateConfigLoader {
|
||||
public int getSwordsGate() { return config.getInt("Skills.Swords.Ability_Activation_Level_Gate", 10); }
|
||||
|
||||
/* Taming */
|
||||
public Material getTamingCOTWMaterial(EntityType type) { return Material.matchMaterial(config.getString("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Item_Material")); }
|
||||
public int getTamingCOTWCost(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Item_Amount"); }
|
||||
public int getTamingCOTWAmount(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Summon_Amount"); }
|
||||
public int getTamingCOTWLength(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type)+ ".Summon_Length"); }
|
||||
public int getTamingCOTWMaxAmount(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type)+ ".Summon_Max_Amount"); }
|
||||
public double getTamingCOTWRange() { return config.getDouble("Skills.Taming.Call_Of_The_Wild.Range", 40.0D); }
|
||||
// public Material getTamingCOTWMaterial(EntityType type) { return Material.matchMaterial(config.getString("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Item_Material")); }
|
||||
// public int getTamingCOTWCost(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Item_Amount"); }
|
||||
// public int getTamingCOTWAmount(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type) + ".Summon_Amount"); }
|
||||
// public int getTamingCOTWLength(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type)+ ".Summon_Length"); }
|
||||
// public int getTamingCOTWMaxAmount(EntityType type) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + StringUtils.getPrettyEntityTypeString(type)+ ".Summon_Max_Amount"); }
|
||||
|
||||
public Material getTamingCOTWMaterial(String cotwEntity) { return Material.matchMaterial(config.getString("Skills.Taming.Call_Of_The_Wild." + cotwEntity + ".Item_Material")); }
|
||||
public int getTamingCOTWCost(String cotwEntity) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + cotwEntity + ".Item_Amount"); }
|
||||
public int getTamingCOTWAmount(String cotwEntity) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + cotwEntity + ".Summon_Amount"); }
|
||||
public int getTamingCOTWLength(String cotwEntity) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + cotwEntity+ ".Summon_Length"); }
|
||||
public int getTamingCOTWMaxAmount(String cotwEntity) { return config.getInt("Skills.Taming.Call_Of_The_Wild." + cotwEntity+ ".Per_Player_Limit", 1); }
|
||||
|
||||
/* Woodcutting */
|
||||
public boolean getWoodcuttingDoubleDropsEnabled(BlockData material) { return config.getBoolean("Bonus_Drops.Woodcutting." + StringUtils.getFriendlyConfigBlockDataString(material)); }
|
||||
@@ -567,4 +573,9 @@ public class Config extends AutoUpdateConfigLoader {
|
||||
public boolean getPVEEnabled(PrimarySkillType skill) { return config.getBoolean("Skills." + StringUtils.getCapitalized(skill.toString()) + ".Enabled_For_PVE", true); }
|
||||
|
||||
//public float getMasterVolume() { return (float) config.getDouble("Sounds.MasterVolume", 1.0); }
|
||||
|
||||
public boolean broadcastEventMessages() { return config.getBoolean("General.EventBroadcasts", true);}
|
||||
public boolean playerJoinEventInfo() { return config.getBoolean("General.EventInfoOnPlayerJoin", true);}
|
||||
public boolean adminNotifications() { return config.getBoolean("General.AdminNotifications", true);}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,18 +87,18 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
/* Alchemy */
|
||||
for (PotionStage potionStage : PotionStage.values()) {
|
||||
if (getPotionXP(potionStage) < 0) {
|
||||
reason.add("Experience.Alchemy.Potion_Stage_" + potionStage.toNumerical() + " should be at least 0!");
|
||||
reason.add("Experience_Values.Alchemy.Potion_Stage_" + potionStage.toNumerical() + " should be at least 0!");
|
||||
}
|
||||
}
|
||||
|
||||
/* Archery */
|
||||
if (getArcheryDistanceMultiplier() < 0) {
|
||||
reason.add("Experience.Archery.Distance_Multiplier should be at least 0!");
|
||||
reason.add("Experience_Values.Archery.Distance_Multiplier should be at least 0!");
|
||||
}
|
||||
|
||||
/* Combat XP Multipliers */
|
||||
if (getAnimalsXP() < 0) {
|
||||
reason.add("Experience.Combat.Multiplier.Animals should be at least 0!");
|
||||
reason.add("Experience_Values.Combat.Multiplier.Animals should be at least 0!");
|
||||
}
|
||||
|
||||
if (getDodgeXPModifier() < 0) {
|
||||
@@ -117,35 +117,43 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
// TODO: Add validation for each fish type once enum is available.
|
||||
|
||||
if (getFishingShakeXP() <= 0) {
|
||||
reason.add("Experience.Fishing.Shake should be greater than 0!");
|
||||
reason.add("Experience_Values.Fishing.Shake should be greater than 0!");
|
||||
}
|
||||
|
||||
/* Repair */
|
||||
if (getRepairXPBase() <= 0) {
|
||||
reason.add("Experience.Repair.Base should be greater than 0!");
|
||||
reason.add("Experience_Values.Repair.Base should be greater than 0!");
|
||||
}
|
||||
|
||||
/* Taming */
|
||||
if (getTamingXP(EntityType.WOLF) <= 0) {
|
||||
reason.add("Experience.Taming.Animal_Taming.Wolf should be greater than 0!");
|
||||
reason.add("Experience_Values.Taming.Animal_Taming.Wolf should be greater than 0!");
|
||||
}
|
||||
|
||||
if (getTamingXP(EntityType.OCELOT) <= 0) {
|
||||
reason.add("Experience.Taming.Animal_Taming.Ocelot should be greater than 0!");
|
||||
reason.add("Experience_Values.Taming.Animal_Taming.Ocelot should be greater than 0!");
|
||||
}
|
||||
|
||||
return noErrorsInConfig(reason);
|
||||
}
|
||||
|
||||
public boolean isEarlyGameBoostEnabled() { return config.getBoolean("EarlyGameBoost.Enabled", true); }
|
||||
|
||||
/*
|
||||
* FORMULA SETTINGS
|
||||
*/
|
||||
|
||||
/* EXPLOIT TOGGLES */
|
||||
public boolean isSnowExploitPrevented() { return config.getBoolean("ExploitFix.SnowGolemExcavation", true); }
|
||||
public boolean isEndermanEndermiteFarmingPrevented() { return config.getBoolean("ExploitFix.EndermanEndermiteFarms", true); }
|
||||
public boolean isPistonCheatingPrevented() { return config.getBoolean("ExploitFix.PistonCheating", true); }
|
||||
public boolean isPistonExploitPrevented() { return config.getBoolean("ExploitFix.Pistons", false); }
|
||||
public boolean allowUnsafeEnchantments() { return config.getBoolean("ExploitFix.UnsafeEnchantments", false); }
|
||||
public boolean isCOTWBreedingPrevented() { return config.getBoolean("ExploitFix.COTWBreeding", true); }
|
||||
|
||||
public boolean isFishingExploitingPrevented() { return config.getBoolean("ExploitFix.Fishing", true); }
|
||||
public boolean isAcrobaticsExploitingPrevented() { return config.getBoolean("ExploitFix.Acrobatics", true); }
|
||||
public boolean isTreeFellerXPReduced() { return config.getBoolean("ExploitFix.TreeFellerReducedXP", true); }
|
||||
|
||||
/* Curve settings */
|
||||
public FormulaType getFormulaType() { return FormulaType.getFormulaType(config.getString("Experience_Formula.Curve")); }
|
||||
@@ -187,18 +195,18 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
*/
|
||||
|
||||
/* General Settings */
|
||||
public boolean getExperienceGainsPlayerVersusPlayerEnabled() { return config.getBoolean("Experience.PVP.Rewards", true); }
|
||||
public boolean getExperienceGainsPlayerVersusPlayerEnabled() { return config.getBoolean("Experience_Values.PVP.Rewards", true); }
|
||||
|
||||
/* Combat XP Multipliers */
|
||||
public double getCombatXP(EntityType entity) { return config.getDouble("Experience.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_")); }
|
||||
public double getAnimalsXP(EntityType entity) { return config.getDouble("Experience.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_"), getAnimalsXP()); }
|
||||
public double getAnimalsXP() { return config.getDouble("Experience.Combat.Multiplier.Animals", 1.0); }
|
||||
public boolean hasCombatXP(EntityType entity) {return config.contains("Experience.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_")); }
|
||||
public double getCombatXP(EntityType entity) { return config.getDouble("Experience_Values.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_")); }
|
||||
public double getAnimalsXP(EntityType entity) { return config.getDouble("Experience_Values.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_"), getAnimalsXP()); }
|
||||
public double getAnimalsXP() { return config.getDouble("Experience_Values.Combat.Multiplier.Animals", 1.0); }
|
||||
public boolean hasCombatXP(EntityType entity) {return config.contains("Experience_Values.Combat.Multiplier." + StringUtils.getPrettyEntityTypeString(entity).replace(" ", "_")); }
|
||||
|
||||
/* Materials */
|
||||
public int getXp(PrimarySkillType skill, Material data)
|
||||
{
|
||||
String baseString = "Experience." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String explicitString = baseString + StringUtils.getExplicitConfigMaterialString(data);
|
||||
if (config.contains(explicitString))
|
||||
return config.getInt(explicitString);
|
||||
@@ -214,7 +222,7 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
/* Materials */
|
||||
public int getXp(PrimarySkillType skill, BlockData data)
|
||||
{
|
||||
String baseString = "Experience." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String explicitString = baseString + StringUtils.getExplicitConfigBlockDataString(data);
|
||||
if (config.contains(explicitString))
|
||||
return config.getInt(explicitString);
|
||||
@@ -229,7 +237,7 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
|
||||
public boolean doesBlockGiveSkillXP(PrimarySkillType skill, Material data)
|
||||
{
|
||||
String baseString = "Experience." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String explicitString = baseString + StringUtils.getExplicitConfigMaterialString(data);
|
||||
if (config.contains(explicitString))
|
||||
return true;
|
||||
@@ -242,7 +250,7 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
|
||||
public boolean doesBlockGiveSkillXP(PrimarySkillType skill, BlockData data)
|
||||
{
|
||||
String baseString = "Experience." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String baseString = "Experience_Values." + StringUtils.getCapitalized(skill.toString()) + ".";
|
||||
String explicitString = baseString + StringUtils.getExplicitConfigBlockDataString(data);
|
||||
if (config.contains(explicitString))
|
||||
return true;
|
||||
@@ -305,27 +313,29 @@ public class ExperienceConfig extends AutoUpdateConfigLoader {
|
||||
}
|
||||
|
||||
/* Acrobatics */
|
||||
public int getDodgeXPModifier() { return config.getInt("Experience.Acrobatics.Dodge", 120); }
|
||||
public int getRollXPModifier() { return config.getInt("Experience.Acrobatics.Roll", 80); }
|
||||
public int getFallXPModifier() { return config.getInt("Experience.Acrobatics.Fall", 120); }
|
||||
public int getDodgeXPModifier() { return config.getInt("Experience_Values.Acrobatics.Dodge", 120); }
|
||||
public int getRollXPModifier() { return config.getInt("Experience_Values.Acrobatics.Roll", 80); }
|
||||
public int getFallXPModifier() { return config.getInt("Experience_Values.Acrobatics.Fall", 120); }
|
||||
|
||||
public double getFeatherFallXPModifier() { return config.getDouble("Experience.Acrobatics.FeatherFall_Multiplier", 2.0); }
|
||||
public double getFeatherFallXPModifier() { return config.getDouble("Experience_Values.Acrobatics.FeatherFall_Multiplier", 2.0); }
|
||||
|
||||
/* Alchemy */
|
||||
public double getPotionXP(PotionStage stage) { return config.getDouble("Experience.Alchemy.Potion_Stage_" + stage.toNumerical(), 10D); }
|
||||
public double getPotionXP(PotionStage stage) { return config.getDouble("Experience_Values.Alchemy.Potion_Stage_" + stage.toNumerical(), 10D); }
|
||||
|
||||
/* Archery */
|
||||
public double getArcheryDistanceMultiplier() { return config.getDouble("Experience.Archery.Distance_Multiplier", 0.025); }
|
||||
public double getArcheryDistanceMultiplier() { return config.getDouble("Experience_Values.Archery.Distance_Multiplier", 0.025); }
|
||||
|
||||
public int getFishingShakeXP() { return config.getInt("Experience.Fishing.Shake", 50); }
|
||||
public int getFishingShakeXP() { return config.getInt("Experience_Values.Fishing.Shake", 50); }
|
||||
|
||||
/* Repair */
|
||||
public double getRepairXPBase() { return config.getDouble("Experience.Repair.Base", 1000.0); }
|
||||
public double getRepairXP(MaterialType repairMaterialType) { return config.getDouble("Experience.Repair." + StringUtils.getCapitalized(repairMaterialType.toString())); }
|
||||
public double getRepairXPBase() { return config.getDouble("Experience_Values.Repair.Base", 1000.0); }
|
||||
public double getRepairXP(MaterialType repairMaterialType) { return config.getDouble("Experience_Values.Repair." + StringUtils.getCapitalized(repairMaterialType.toString())); }
|
||||
|
||||
/* Taming */
|
||||
public int getTamingXP(EntityType type)
|
||||
{
|
||||
return config.getInt("Experience.Taming.Animal_Taming." + StringUtils.getPrettyEntityTypeString(type));
|
||||
return config.getInt("Experience_Values.Taming.Animal_Taming." + StringUtils.getPrettyEntityTypeString(type));
|
||||
}
|
||||
|
||||
public boolean preventStoneLavaFarming() { return config.getBoolean("ExploitFix.LavaStoneAndCobbleFarming", true);}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,8 @@ import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.skills.repair.repairables.Repairable;
|
||||
import com.gmail.nossr50.skills.repair.repairables.RepairableFactory;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -77,13 +75,6 @@ public class CustomArmorConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
if (repairable) {
|
||||
byte repairData = (byte) config.getInt(armorType + "." + armorName + ".Repair_Material_Data_Value", -1);
|
||||
int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(armorMaterial), repairMaterial, repairData);
|
||||
|
||||
if (repairQuantity == 0) {
|
||||
repairQuantity = config.getInt(armorType + "." + armorName + ".Repair_Material_Quantity", 2);
|
||||
}
|
||||
|
||||
String repairItemName = config.getString(armorType + "." + armorName + ".Repair_Material_Pretty_Name");
|
||||
int repairMinimumLevel = config.getInt(armorType + "." + armorName + ".Repair_MinimumLevel", 0);
|
||||
double repairXpMultiplier = config.getDouble(armorType + "." + armorName + ".Repair_XpMultiplier", 1);
|
||||
@@ -94,7 +85,7 @@ public class CustomArmorConfig extends ConfigLoader {
|
||||
durability = (short) config.getInt(armorType + "." + armorName + ".Durability", 70);
|
||||
}
|
||||
|
||||
repairables.add(RepairableFactory.getRepairable(armorMaterial, repairMaterial, repairData, repairItemName, repairMinimumLevel, repairQuantity, durability, ItemType.ARMOR, MaterialType.OTHER, repairXpMultiplier));
|
||||
repairables.add(RepairableFactory.getRepairable(armorMaterial, repairMaterial, repairItemName, repairMinimumLevel, durability, ItemType.ARMOR, MaterialType.OTHER, repairXpMultiplier));
|
||||
}
|
||||
|
||||
materialList.add(armorMaterial);
|
||||
|
||||
@@ -6,10 +6,8 @@ import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.skills.repair.repairables.Repairable;
|
||||
import com.gmail.nossr50.skills.repair.repairables.RepairableFactory;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -85,13 +83,6 @@ public class CustomToolConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
if (repairable) {
|
||||
byte repairData = (byte) config.getInt(toolType + "." + toolName + ".Repair_Material_Data_Value", -1);
|
||||
int repairQuantity = SkillUtils.getRepairAndSalvageQuantities(new ItemStack(toolMaterial), repairMaterial, repairData);
|
||||
|
||||
if (repairQuantity == 0) {
|
||||
repairQuantity = config.getInt(toolType + "." + toolName + ".Repair_Material_Quantity", 2);
|
||||
}
|
||||
|
||||
String repairItemName = config.getString(toolType + "." + toolName + ".Repair_Material_Pretty_Name");
|
||||
int repairMinimumLevel = config.getInt(toolType + "." + toolName + ".Repair_MinimumLevel", 0);
|
||||
double repairXpMultiplier = config.getDouble(toolType + "." + toolName + ".Repair_XpMultiplier", 1);
|
||||
@@ -102,7 +93,7 @@ public class CustomToolConfig extends ConfigLoader {
|
||||
durability = (short) config.getInt(toolType + "." + toolName + ".Durability", 60);
|
||||
}
|
||||
|
||||
repairables.add(RepairableFactory.getRepairable(toolMaterial, repairMaterial, repairData, repairItemName, repairMinimumLevel, repairQuantity, durability, ItemType.TOOL, MaterialType.OTHER, repairXpMultiplier));
|
||||
repairables.add(RepairableFactory.getRepairable(toolMaterial, repairMaterial, repairItemName, repairMinimumLevel, durability, ItemType.TOOL, MaterialType.OTHER, repairXpMultiplier));
|
||||
}
|
||||
|
||||
double multiplier = config.getDouble(toolType + "." + toolName + ".XP_Modifier", 1.0);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.gmail.nossr50.util.StringUtils;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ItemWeightConfig extends ConfigLoader {
|
||||
private static ItemWeightConfig instance;
|
||||
@@ -29,7 +30,7 @@ public class ItemWeightConfig extends ConfigLoader {
|
||||
HashSet<Material> miscItems = new HashSet<Material>();
|
||||
|
||||
for (String item : config.getStringList("Party_Shareables.Misc_Items")) {
|
||||
Material material = Material.getMaterial(item.toUpperCase());
|
||||
Material material = Material.getMaterial(item.toUpperCase(Locale.ENGLISH));
|
||||
|
||||
if (material != null) {
|
||||
miscItems.add(material);
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.gmail.nossr50.config.skills.repair;
|
||||
import com.gmail.nossr50.config.ConfigLoader;
|
||||
import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.repair.repairables.Repairable;
|
||||
import com.gmail.nossr50.skills.repair.repairables.RepairableFactory;
|
||||
import com.gmail.nossr50.util.ItemUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -27,6 +27,11 @@ public class RepairConfig extends ConfigLoader {
|
||||
protected void loadKeys() {
|
||||
repairables = new ArrayList<Repairable>();
|
||||
|
||||
if (!config.isConfigurationSection("Repairables")) {
|
||||
mcMMO.p.getLogger().severe("Could not find Repairables section in " + fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection("Repairables");
|
||||
Set<String> keys = section.getKeys(false);
|
||||
|
||||
@@ -126,7 +131,6 @@ public class RepairConfig extends ConfigLoader {
|
||||
}
|
||||
}
|
||||
|
||||
byte repairMetadata = (byte) config.getInt("Repairables." + key + ".RepairMaterialMetadata", -1);
|
||||
int minimumLevel = config.getInt("Repairables." + key + ".MinimumLevel");
|
||||
double xpMultiplier = config.getDouble("Repairables." + key + ".XpMultiplier", 1);
|
||||
|
||||
@@ -135,18 +139,14 @@ public class RepairConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
// Minimum Quantity
|
||||
int minimumQuantity = (itemMaterial != null ? SkillUtils.getRepairAndSalvageQuantities(new ItemStack(itemMaterial), repairMaterial, repairMetadata) : config.getInt("Repairables." + key + ".MinimumQuantity", 2));
|
||||
int minimumQuantity = config.getInt("Repairables." + key + ".MinimumQuantity");
|
||||
|
||||
if (minimumQuantity <= 0 && itemMaterial != null) {
|
||||
minimumQuantity = config.getInt("Repairables." + key + ".MinimumQuantity", 2);
|
||||
}
|
||||
|
||||
if (minimumQuantity <= 0) {
|
||||
reason.add("Minimum quantity of " + key + " must be greater than 0!");
|
||||
if(minimumQuantity == 0) {
|
||||
minimumQuantity = -1;
|
||||
}
|
||||
|
||||
if (noErrorsInRepairable(reason)) {
|
||||
Repairable repairable = RepairableFactory.getRepairable(itemMaterial, repairMaterial, repairMetadata, minimumLevel, minimumQuantity, maximumDurability, repairItemType, repairMaterialType, xpMultiplier);
|
||||
Repairable repairable = RepairableFactory.getRepairable(itemMaterial, repairMaterial, null, minimumLevel, maximumDurability, repairItemType, repairMaterialType, xpMultiplier, minimumQuantity);
|
||||
repairables.add(repairable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gmail.nossr50.config.skills.salvage;
|
||||
import com.gmail.nossr50.config.ConfigLoader;
|
||||
import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.salvage.salvageables.Salvageable;
|
||||
import com.gmail.nossr50.skills.salvage.salvageables.SalvageableFactory;
|
||||
import com.gmail.nossr50.util.ItemUtils;
|
||||
@@ -13,6 +14,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class SalvageConfig extends ConfigLoader {
|
||||
@@ -27,6 +29,11 @@ public class SalvageConfig extends ConfigLoader {
|
||||
protected void loadKeys() {
|
||||
salvageables = new ArrayList<Salvageable>();
|
||||
|
||||
if (!config.isConfigurationSection("Salvageables")) {
|
||||
mcMMO.p.getLogger().severe("Could not find Salvageables section in " + fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection("Salvageables");
|
||||
Set<String> keys = section.getKeys(false);
|
||||
|
||||
@@ -72,7 +79,7 @@ public class SalvageConfig extends ConfigLoader {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
salvageMaterialType = MaterialType.valueOf(salvageMaterialTypeString.replace(" ", "_").toUpperCase());
|
||||
salvageMaterialType = MaterialType.valueOf(salvageMaterialTypeString.replace(" ", "_").toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
reason.add(key + " has an invalid MaterialType of " + salvageMaterialTypeString);
|
||||
@@ -106,14 +113,13 @@ public class SalvageConfig extends ConfigLoader {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
salvageItemType = ItemType.valueOf(salvageItemTypeString.replace(" ", "_").toUpperCase());
|
||||
salvageItemType = ItemType.valueOf(salvageItemTypeString.replace(" ", "_").toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
reason.add(key + " has an invalid ItemType of " + salvageItemTypeString);
|
||||
}
|
||||
}
|
||||
|
||||
byte salvageMetadata = (byte) config.getInt("Salvageables." + key + ".SalvageMaterialMetadata", -1);
|
||||
int minimumLevel = config.getInt("Salvageables." + key + ".MinimumLevel");
|
||||
double xpMultiplier = config.getDouble("Salvageables." + key + ".XpMultiplier", 1);
|
||||
|
||||
@@ -122,7 +128,7 @@ public class SalvageConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
// Maximum Quantity
|
||||
int maximumQuantity = (itemMaterial != null ? SkillUtils.getRepairAndSalvageQuantities(new ItemStack(itemMaterial), salvageMaterial, salvageMetadata) : config.getInt("Salvageables." + key + ".MaximumQuantity", 2));
|
||||
int maximumQuantity = (itemMaterial != null ? SkillUtils.getRepairAndSalvageQuantities(itemMaterial, salvageMaterial) : config.getInt("Salvageables." + key + ".MaximumQuantity", 2));
|
||||
|
||||
if (maximumQuantity <= 0 && itemMaterial != null) {
|
||||
maximumQuantity = config.getInt("Salvageables." + key + ".MaximumQuantity", 1);
|
||||
@@ -139,7 +145,7 @@ public class SalvageConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
if (noErrorsInSalvageable(reason)) {
|
||||
Salvageable salvageable = SalvageableFactory.getSalvageable(itemMaterial, salvageMaterial, salvageMetadata, minimumLevel, maximumQuantity, maximumDurability, salvageItemType, salvageMaterialType, xpMultiplier);
|
||||
Salvageable salvageable = SalvageableFactory.getSalvageable(itemMaterial, salvageMaterial, minimumLevel, maximumQuantity, maximumDurability, salvageItemType, salvageMaterialType, xpMultiplier);
|
||||
salvageables.add(salvageable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class TreasureConfig extends ConfigLoader {
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
reason.add("Amount of " + treasureName + " must be greater than 0! " + amount);
|
||||
amount = 1;
|
||||
}
|
||||
|
||||
if (material != null && material.isBlock() && (data > 127 || data < -128)) {
|
||||
|
||||
@@ -30,9 +30,17 @@ public interface DatabaseManager {
|
||||
* Remove a user from the database.
|
||||
*
|
||||
* @param playerName The name of the user to remove
|
||||
* @param uuid player UUID, can be null
|
||||
* @return true if the user was successfully removed, false otherwise
|
||||
*/
|
||||
public boolean removeUser(String playerName);
|
||||
public boolean removeUser(String playerName, UUID uuid);
|
||||
|
||||
/**
|
||||
* Removes any cache used for faster lookups
|
||||
* Currently only used for SQL
|
||||
* @param uuid target UUID to cleanup
|
||||
*/
|
||||
public void cleanupUser(UUID uuid);
|
||||
|
||||
/**
|
||||
* Save a user to the database.
|
||||
|
||||
@@ -185,7 +185,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
|
||||
mcMMO.p.getLogger().info("Purged " + removedPlayers + " users from the database.");
|
||||
}
|
||||
|
||||
public boolean removeUser(String playerName) {
|
||||
public boolean removeUser(String playerName, UUID uuid) {
|
||||
//NOTE: UUID is unused for FlatFile for this interface implementation
|
||||
boolean worked = false;
|
||||
|
||||
BufferedReader in = null;
|
||||
@@ -240,6 +241,11 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
|
||||
return worked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupUser(UUID uuid) {
|
||||
//Not used in FlatFile
|
||||
}
|
||||
|
||||
public boolean saveUser(PlayerProfile profile) {
|
||||
String playerName = profile.getPlayerName();
|
||||
UUID uuid = profile.getUniqueId();
|
||||
|
||||
@@ -31,6 +31,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
private DataSource loadPool;
|
||||
private DataSource savePool;
|
||||
|
||||
private boolean debug = false;
|
||||
|
||||
private ReentrantLock massUpdateLock = new ReentrantLock();
|
||||
|
||||
protected SQLDatabaseManager() {
|
||||
@@ -56,6 +58,8 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
//throw e; // aborts onEnable() Riking if you want to do this, fully implement it.
|
||||
}
|
||||
|
||||
debug = Config.getInstance().getMySQLDebug();
|
||||
|
||||
|
||||
PoolProperties poolProperties = new PoolProperties();
|
||||
poolProperties.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
@@ -172,7 +176,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
mcMMO.p.getLogger().info("Purged " + purged + " users from the database.");
|
||||
}
|
||||
|
||||
public boolean removeUser(String playerName) {
|
||||
public boolean removeUser(String playerName, UUID uuid) {
|
||||
boolean success = false;
|
||||
Connection connection = null;
|
||||
PreparedStatement statement = null;
|
||||
@@ -200,12 +204,20 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if(uuid != null)
|
||||
cleanupUser(uuid);
|
||||
|
||||
Misc.profileCleanup(playerName);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void cleanupUser(UUID uuid) {
|
||||
if(cachedUserIDs.containsKey(uuid))
|
||||
cachedUserIDs.remove(uuid);
|
||||
}
|
||||
|
||||
public boolean saveUser(PlayerProfile profile) {
|
||||
boolean success = true;
|
||||
PreparedStatement statement = null;
|
||||
@@ -335,7 +347,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
public List<PlayerStat> readLeaderboard(PrimarySkillType skill, int pageNumber, int statsPerPage) {
|
||||
List<PlayerStat> stats = new ArrayList<PlayerStat>();
|
||||
|
||||
String query = skill == null ? ALL_QUERY_VERSION : skill.name().toLowerCase();
|
||||
String query = skill == null ? ALL_QUERY_VERSION : skill.name().toLowerCase(Locale.ENGLISH);
|
||||
ResultSet resultSet = null;
|
||||
PreparedStatement statement = null;
|
||||
Connection connection = null;
|
||||
@@ -379,9 +391,9 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
try {
|
||||
connection = getConnection(PoolIdentifier.MISC);
|
||||
for (PrimarySkillType primarySkillType : PrimarySkillType.NON_CHILD_SKILLS) {
|
||||
String skillName = primarySkillType.name().toLowerCase();
|
||||
String skillName = primarySkillType.name().toLowerCase(Locale.ENGLISH);
|
||||
// Get count of all users with higher skill level than player
|
||||
String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
|
||||
String sql = "SELECT COUNT(*) AS 'rank' FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
|
||||
"AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
|
||||
"WHERE user = ?)";
|
||||
|
||||
@@ -415,7 +427,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
statement.close();
|
||||
}
|
||||
|
||||
String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
|
||||
String sql = "SELECT COUNT(*) AS 'rank' FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
|
||||
"WHERE " + ALL_QUERY_VERSION + " > 0 " +
|
||||
"AND " + ALL_QUERY_VERSION + " > " +
|
||||
"(SELECT " + ALL_QUERY_VERSION + " " +
|
||||
@@ -899,7 +911,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
|
||||
int cap = Config.getInstance().getLevelCap(skill);
|
||||
if (cap != Integer.MAX_VALUE) {
|
||||
statement = connection.prepareStatement("UPDATE `" + tablePrefix + "skills` SET `" + skill.name().toLowerCase() + "` = " + cap + " WHERE `" + skill.name().toLowerCase() + "` > " + cap);
|
||||
statement = connection.prepareStatement("UPDATE `" + tablePrefix + "skills` SET `" + skill.name().toLowerCase(Locale.ENGLISH) + "` = " + cap + " WHERE `" + skill.name().toLowerCase(Locale.ENGLISH) + "` > " + cap);
|
||||
statement.executeUpdate();
|
||||
tryClose(statement);
|
||||
}
|
||||
@@ -939,7 +951,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
break;
|
||||
}
|
||||
if (connection == null) {
|
||||
throw new RuntimeException("getConnection() for " + identifier.name().toLowerCase() + " pool timed out. Increase max connections settings.");
|
||||
throw new RuntimeException("getConnection() for " + identifier.name().toLowerCase(Locale.ENGLISH) + " pool timed out. Increase max connections settings.");
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
@@ -1140,6 +1152,10 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
}
|
||||
|
||||
private void printErrors(SQLException ex) {
|
||||
if (debug) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
StackTraceElement element = ex.getStackTrace()[0];
|
||||
mcMMO.p.getLogger().severe("Location: " + element.getClassName() + " " + element.getMethodName() + " " + element.getLineNumber());
|
||||
mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
|
||||
@@ -1246,7 +1262,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
|
||||
mcMMO.p.getLogger().info("Indexing tables, this may take a while on larger databases");
|
||||
|
||||
for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
|
||||
String skill_name = skill.name().toLowerCase();
|
||||
String skill_name = skill.name().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
try {
|
||||
statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_" + skill_name + "` (`" + skill_name + "`) USING BTREE");
|
||||
|
||||
30
src/main/java/com/gmail/nossr50/datatypes/BlockSnapshot.java
Normal file
30
src/main/java/com/gmail/nossr50/datatypes/BlockSnapshot.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.gmail.nossr50.datatypes;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
/**
|
||||
* Contains a snapshot of a block at a specific moment in time
|
||||
* Used to check before/after type stuff
|
||||
*/
|
||||
public class BlockSnapshot {
|
||||
private final Material oldType;
|
||||
private Block blockRef;
|
||||
|
||||
public BlockSnapshot(Material oldType, Block blockRef) {
|
||||
this.oldType = oldType;
|
||||
this.blockRef = blockRef;
|
||||
}
|
||||
|
||||
public Material getOldType() {
|
||||
return oldType;
|
||||
}
|
||||
|
||||
public Block getBlockRef() {
|
||||
return blockRef;
|
||||
}
|
||||
|
||||
public boolean hasChangedType() {
|
||||
return oldType != blockRef.getState().getType();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public enum NotificationType {
|
||||
SUPER_ABILITY("SuperAbilityInteraction"),
|
||||
SUPER_ABILITY_ALERT_OTHERS("SuperAbilityAlertOthers"),
|
||||
ITEM_MESSAGE("ItemMessage"),
|
||||
CHAT_ONLY("ChatOnly"),
|
||||
PARTY_MESSAGE("PartyMessage");
|
||||
|
||||
final String niceName;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.gmail.nossr50.datatypes.meta;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
/**
|
||||
* Stores how many bonus drops a block should give
|
||||
*/
|
||||
public class BonusDropMeta extends FixedMetadataValue {
|
||||
|
||||
public BonusDropMeta(int value, mcMMO plugin) {
|
||||
super(plugin, value);
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,5 @@ public class OldName extends FixedMetadataValue {
|
||||
{
|
||||
super(plugin, oldName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.gmail.nossr50.datatypes.meta;
|
||||
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class RecentlyReplantedCropMeta extends FixedMetadataValue {
|
||||
|
||||
/**
|
||||
* Initializes a FixedMetadataValue with an Object
|
||||
*
|
||||
* @param owningPlugin the {@link Plugin} that created this metadata value
|
||||
*/
|
||||
public RecentlyReplantedCropMeta(Plugin owningPlugin, Boolean recentlyPlanted) {
|
||||
super(owningPlugin, recentlyPlanted);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.gmail.nossr50.datatypes.notifications;
|
||||
|
||||
public enum SensitiveCommandType {
|
||||
XPRATE_MODIFY,
|
||||
XPRATE_END,
|
||||
MMOEDIT
|
||||
}
|
||||
@@ -203,7 +203,7 @@ public class Party {
|
||||
|
||||
public int getXpToLevel() {
|
||||
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
|
||||
return (mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType)) * (getOnlineMembers().size() + Config.getInstance().getPartyXpCurveMultiplier());
|
||||
return (mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType)) * (getOnlineMembers().size() + Config.getInstance().getPartyXpCurveMultiplier());
|
||||
}
|
||||
|
||||
public String getXpToLevelPercentage() {
|
||||
|
||||
@@ -81,6 +81,7 @@ public class McMMOPlayer {
|
||||
private boolean partyChatMode;
|
||||
private boolean adminChatMode;
|
||||
private boolean displaySkillNotifications = true;
|
||||
private boolean debugMode;
|
||||
|
||||
private boolean abilityUse = true;
|
||||
private boolean godMode;
|
||||
@@ -100,9 +101,10 @@ public class McMMOPlayer {
|
||||
|
||||
private boolean isUsingUnarmed;
|
||||
private final FixedMetadataValue playerMetadata;
|
||||
private String playerName;
|
||||
|
||||
public McMMOPlayer(Player player, PlayerProfile profile) {
|
||||
String playerName = player.getName();
|
||||
this.playerName = player.getName();
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
this.player = player;
|
||||
@@ -138,6 +140,12 @@ public class McMMOPlayer {
|
||||
}
|
||||
|
||||
experienceBarManager = new ExperienceBarManager(this);
|
||||
|
||||
debugMode = false; //Debug mode helps solve support issues, players can toggle it on or off
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
/*public void hideXpBar(PrimarySkillType primarySkillType)
|
||||
@@ -145,8 +153,15 @@ public class McMMOPlayer {
|
||||
experienceBarManager.hideExperienceBar(primarySkillType);
|
||||
}*/
|
||||
|
||||
public void processPostXpEvent(XPGainReason xpGainReason, PrimarySkillType primarySkillType, Plugin plugin, XPGainSource xpGainSource)
|
||||
public void processPostXpEvent(PrimarySkillType primarySkillType, Plugin plugin, XPGainSource xpGainSource)
|
||||
{
|
||||
//Check if they've reached the power level cap just now
|
||||
if(hasReachedPowerLevelCap()) {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "LevelCap.PowerLevel", String.valueOf(Config.getInstance().getPowerLevelCap()));
|
||||
} else if(hasReachedLevelCap(primarySkillType)) {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "LevelCap.Skill", String.valueOf(Config.getInstance().getLevelCap(primarySkillType)), primarySkillType.getName());
|
||||
}
|
||||
|
||||
//Updates from Party sources
|
||||
if(xpGainSource == XPGainSource.PARTY_MEMBERS && !ExperienceConfig.getInstance().isPartyExperienceBarsEnabled())
|
||||
return;
|
||||
@@ -425,6 +440,18 @@ public class McMMOPlayer {
|
||||
|
||||
public void togglePartyChatSpying() { chatSpy = !chatSpy;}
|
||||
|
||||
/*
|
||||
* Debug Mode Flags
|
||||
*/
|
||||
|
||||
public boolean isDebugMode() {
|
||||
return debugMode;
|
||||
}
|
||||
|
||||
public void toggleDebugMode() {
|
||||
debugMode = !debugMode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skill notifications
|
||||
*/
|
||||
@@ -454,6 +481,28 @@ public class McMMOPlayer {
|
||||
return powerLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a player is level capped
|
||||
* If they are at the power level cap, this will return true, otherwise it checks their skill level
|
||||
* @param primarySkillType
|
||||
* @return
|
||||
*/
|
||||
public boolean hasReachedLevelCap(PrimarySkillType primarySkillType) {
|
||||
if(hasReachedPowerLevelCap())
|
||||
return true;
|
||||
|
||||
return getSkillLevel(primarySkillType) >= Config.getInstance().getLevelCap(primarySkillType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a player is power level capped
|
||||
* Compares their power level total to the current set limit
|
||||
* @return true if they have reached the power level cap
|
||||
*/
|
||||
public boolean hasReachedPowerLevelCap() {
|
||||
return this.getPowerLevel() >= Config.getInstance().getPowerLevelCap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins an experience gain. The amount will be affected by skill modifiers, global rate, perks, and may be shared with the party
|
||||
*
|
||||
@@ -495,6 +544,9 @@ public class McMMOPlayer {
|
||||
* @param xp Experience amount to process
|
||||
*/
|
||||
public void beginUnsharedXpGain(PrimarySkillType skill, float xp, XPGainReason xpGainReason, XPGainSource xpGainSource) {
|
||||
if(player.getGameMode() == GameMode.CREATIVE)
|
||||
return;
|
||||
|
||||
applyXpGain(skill, modifyXpGain(skill, xp), xpGainReason, xpGainSource);
|
||||
|
||||
if (party == null) {
|
||||
@@ -541,8 +593,11 @@ public class McMMOPlayer {
|
||||
* @param primarySkillType The skill to check
|
||||
*/
|
||||
private void checkXp(PrimarySkillType primarySkillType, XPGainReason xpGainReason, XPGainSource xpGainSource) {
|
||||
if(hasReachedLevelCap(primarySkillType))
|
||||
return;
|
||||
|
||||
if (getSkillXpLevelRaw(primarySkillType) < getXpToLevel(primarySkillType)) {
|
||||
processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
|
||||
processPostXpEvent(primarySkillType, mcMMO.p, xpGainSource);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -559,8 +614,7 @@ public class McMMOPlayer {
|
||||
levelsGained++;
|
||||
}
|
||||
|
||||
if (!EventUtils.handleLevelChangeEvent(player, primarySkillType, levelsGained, xpRemoved, true, xpGainReason)) {
|
||||
processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
|
||||
if (EventUtils.tryLevelChangeEvent(player, primarySkillType, levelsGained, xpRemoved, true, xpGainReason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -575,7 +629,7 @@ public class McMMOPlayer {
|
||||
NotificationManager.sendPlayerLevelUpNotification(this, primarySkillType, levelsGained, profile.getSkillLevel(primarySkillType));
|
||||
|
||||
//UPDATE XP BARS
|
||||
processPostXpEvent(xpGainReason, primarySkillType, mcMMO.p, xpGainSource);
|
||||
processPostXpEvent(primarySkillType, mcMMO.p, xpGainSource);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -750,7 +804,7 @@ public class McMMOPlayer {
|
||||
* @return Modified experience
|
||||
*/
|
||||
private float modifyXpGain(PrimarySkillType primarySkillType, float xp) {
|
||||
if (player.getGameMode() == GameMode.CREATIVE || (primarySkillType.getMaxLevel() <= getSkillLevel(primarySkillType)) || (Config.getInstance().getPowerLevelCap() <= getPowerLevel())) {
|
||||
if ((primarySkillType.getMaxLevel() <= getSkillLevel(primarySkillType)) || (Config.getInstance().getPowerLevelCap() <= getPowerLevel())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -795,7 +849,6 @@ public class McMMOPlayer {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//TODO: This is hacky and temporary solution until skills are move to the new system
|
||||
//Potential problems with this include skills with two super abilities (ie mining)
|
||||
if(!skill.isSuperAbilityUnlocked(getPlayer()))
|
||||
@@ -910,7 +963,7 @@ public class McMMOPlayer {
|
||||
}
|
||||
|
||||
setToolPreparationMode(tool, true);
|
||||
new ToolLowerTask(this, tool).runTaskLaterAsynchronously(mcMMO.p, 4 * Misc.TICK_CONVERSION_FACTOR);
|
||||
new ToolLowerTask(this, tool).runTaskLater(mcMMO.p, 4 * Misc.TICK_CONVERSION_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -926,10 +979,6 @@ public class McMMOPlayer {
|
||||
return (int) (((deactivatedTimestamp + (PerksUtils.handleCooldownPerks(player, ability.getCooldown()) * Misc.TIME_CONVERSION_FACTOR)) - System.currentTimeMillis()) / Misc.TIME_CONVERSION_FACTOR);
|
||||
}
|
||||
|
||||
private boolean hasReachedLevelCap(PrimarySkillType skill) {
|
||||
return (skill.getMaxLevel() < getSkillLevel(skill) + 1) || (Config.getInstance().getPowerLevelCap() < getPowerLevel() + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions are wrapped from PlayerProfile so that we don't always have to store it alongside the McMMOPlayer object.
|
||||
*/
|
||||
@@ -988,11 +1037,11 @@ public class McMMOPlayer {
|
||||
*/
|
||||
public void logout(boolean syncSave) {
|
||||
Player thisPlayer = getPlayer();
|
||||
resetAbilityMode();
|
||||
BleedTimerTask.bleedOut(thisPlayer);
|
||||
BleedTimerTask.bleedOut(getPlayer());
|
||||
cleanup();
|
||||
|
||||
if (syncSave) {
|
||||
getProfile().save();
|
||||
getProfile().save(true);
|
||||
} else {
|
||||
getProfile().scheduleAsyncSave();
|
||||
}
|
||||
@@ -1005,5 +1054,19 @@ public class McMMOPlayer {
|
||||
if (inParty()) {
|
||||
party.removeOnlineMember(thisPlayer);
|
||||
}
|
||||
|
||||
//Remove user from cache
|
||||
mcMMO.getDatabaseManager().cleanupUser(thisPlayer.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup various things related to this player
|
||||
* Such as temporary summons..
|
||||
* Turning off abilities...
|
||||
* Etc...
|
||||
*/
|
||||
public void cleanup() {
|
||||
resetAbilityMode();
|
||||
getTamingManager().cleanupAllSummons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,14 +92,23 @@ public class PlayerProfile {
|
||||
}
|
||||
|
||||
public void scheduleAsyncSave() {
|
||||
new PlayerProfileSaveTask(this).runTaskAsynchronously(mcMMO.p);
|
||||
new PlayerProfileSaveTask(this, false).runTaskAsynchronously(mcMMO.p);
|
||||
}
|
||||
|
||||
public void scheduleSyncSave() {
|
||||
new PlayerProfileSaveTask(this, true).runTask(mcMMO.p);
|
||||
}
|
||||
|
||||
public void scheduleAsyncSaveDelay() {
|
||||
new PlayerProfileSaveTask(this).runTaskLaterAsynchronously(mcMMO.p, 20);
|
||||
new PlayerProfileSaveTask(this, false).runTaskLaterAsynchronously(mcMMO.p, 20);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
@Deprecated
|
||||
public void scheduleSyncSaveDelay() {
|
||||
new PlayerProfileSaveTask(this, true).runTaskLater(mcMMO.p, 20);
|
||||
}
|
||||
|
||||
public void save(boolean useSync) {
|
||||
if (!changed || !loaded) {
|
||||
saveAttempts = 0;
|
||||
return;
|
||||
@@ -121,7 +130,12 @@ public class PlayerProfile {
|
||||
if(saveAttempts < 10)
|
||||
{
|
||||
saveAttempts++;
|
||||
scheduleAsyncSaveDelay();
|
||||
|
||||
if(useSync)
|
||||
scheduleSyncSave(); //Execute sync saves immediately
|
||||
else
|
||||
scheduleAsyncSaveDelay();
|
||||
|
||||
return;
|
||||
} else {
|
||||
mcMMO.p.getLogger().severe("mcMMO has failed to save the profile for "
|
||||
@@ -130,9 +144,9 @@ public class PlayerProfile {
|
||||
" Check your console for errors and inspect your DB for issues.");
|
||||
}
|
||||
|
||||
} else {
|
||||
saveAttempts = 0;
|
||||
}
|
||||
|
||||
saveAttempts = 0;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
@@ -144,7 +158,7 @@ public class PlayerProfile {
|
||||
}
|
||||
|
||||
public void setUniqueId(UUID uuid) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
this.uuid = uuid;
|
||||
}
|
||||
@@ -162,17 +176,24 @@ public class PlayerProfile {
|
||||
}
|
||||
|
||||
public void setMobHealthbarType(MobHealthbarType mobHealthbarType) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
this.mobHealthbarType = mobHealthbarType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the profile as "dirty" which flags a profile to be saved in the next save operation
|
||||
*/
|
||||
public void markProfileDirty() {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
public int getScoreboardTipsShown() {
|
||||
return scoreboardTipsShown;
|
||||
}
|
||||
|
||||
public void setScoreboardTipsShown(int scoreboardTipsShown) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
this.scoreboardTipsShown = scoreboardTipsShown;
|
||||
}
|
||||
@@ -188,12 +209,12 @@ public class PlayerProfile {
|
||||
public int getChimaerWingDATS() { return uniquePlayerData.get(UniqueDataType.CHIMAERA_WING_DATS);}
|
||||
|
||||
protected void setChimaeraWingDATS(int DATS) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
uniquePlayerData.put(UniqueDataType.CHIMAERA_WING_DATS, DATS);
|
||||
}
|
||||
|
||||
public void setUniqueData(UniqueDataType uniqueDataType, int newData) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
uniquePlayerData.put(uniqueDataType, newData);
|
||||
}
|
||||
|
||||
@@ -216,7 +237,7 @@ public class PlayerProfile {
|
||||
* @param DATS the DATS of the ability
|
||||
*/
|
||||
protected void setAbilityDATS(SuperAbilityType ability, long DATS) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
abilityDATS.put(ability, (int) (DATS * .001D));
|
||||
}
|
||||
@@ -225,7 +246,7 @@ public class PlayerProfile {
|
||||
* Reset all ability cooldowns.
|
||||
*/
|
||||
protected void resetCooldowns() {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
for (SuperAbilityType ability : abilityDATS.keySet()) {
|
||||
abilityDATS.put(ability, 0);
|
||||
@@ -253,7 +274,7 @@ public class PlayerProfile {
|
||||
return;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
skillsXp.put(skill, xpLevel);
|
||||
}
|
||||
@@ -261,7 +282,7 @@ public class PlayerProfile {
|
||||
protected float levelUp(PrimarySkillType skill) {
|
||||
float xpRemoved = getXpToLevel(skill);
|
||||
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
skills.put(skill, skills.get(skill) + 1);
|
||||
skillsXp.put(skill, skillsXp.get(skill) - xpRemoved);
|
||||
@@ -280,7 +301,7 @@ public class PlayerProfile {
|
||||
return;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
skillsXp.put(skill, skillsXp.get(skill) - xp);
|
||||
}
|
||||
@@ -290,7 +311,7 @@ public class PlayerProfile {
|
||||
return;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
skillsXp.put(skill, skillsXp.get(skill) - xp);
|
||||
}
|
||||
@@ -306,7 +327,7 @@ public class PlayerProfile {
|
||||
return;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
//Don't allow levels to be negative
|
||||
if(level < 0)
|
||||
@@ -333,7 +354,7 @@ public class PlayerProfile {
|
||||
* @param xp Number of experience to add
|
||||
*/
|
||||
public void addXp(PrimarySkillType skill, float xp) {
|
||||
changed = true;
|
||||
markProfileDirty();
|
||||
|
||||
if (skill.isChildSkill()) {
|
||||
Set<PrimarySkillType> parentSkills = FamilyTree.getParents(skill);
|
||||
@@ -397,7 +418,7 @@ public class PlayerProfile {
|
||||
int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(primarySkillType);
|
||||
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
|
||||
|
||||
return mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType);
|
||||
return mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType);
|
||||
}
|
||||
|
||||
private int getChildSkillLevel(PrimarySkillType primarySkillType) {
|
||||
|
||||
@@ -53,7 +53,7 @@ public enum PrimarySkillType {
|
||||
REPAIR(RepairManager.class, Color.SILVER,
|
||||
ImmutableList.of(SubSkillType.REPAIR_ARCANE_FORGING, SubSkillType.REPAIR_REPAIR_MASTERY, SubSkillType.REPAIR_SUPER_REPAIR)),
|
||||
SALVAGE(SalvageManager.class, Color.ORANGE,
|
||||
ImmutableList.of(SubSkillType.SALVAGE_ADVANCED_SALVAGE, SubSkillType.SALVAGE_ARCANE_SALVAGE)),
|
||||
ImmutableList.of(SubSkillType.SALVAGE_SCRAP_COLLECTOR, SubSkillType.SALVAGE_ARCANE_SALVAGE)),
|
||||
SMELTING(SmeltingManager.class, Color.YELLOW,
|
||||
ImmutableList.of(SubSkillType.SMELTING_UNDERSTANDING_THE_ART, /*SubSkillType.SMELTING_FLUX_MINING,*/ SubSkillType.SMELTING_FUEL_EFFICIENCY, SubSkillType.SMELTING_SECOND_SMELT)),
|
||||
SWORDS(SwordsManager.class, Color.fromRGB(178, 34, 34), SuperAbilityType.SERRATED_STRIKES, ToolType.SWORD,
|
||||
@@ -66,7 +66,7 @@ public enum PrimarySkillType {
|
||||
ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER));
|
||||
|
||||
private Class<? extends SkillManager> managerClass;
|
||||
private Color runescapeColor;
|
||||
private Color skillColor;
|
||||
private SuperAbilityType ability;
|
||||
private ToolType tool;
|
||||
private List<SubSkillType> subSkillTypes;
|
||||
@@ -110,13 +110,13 @@ public enum PrimarySkillType {
|
||||
NON_CHILD_SKILLS = ImmutableList.copyOf(nonChildSkills);
|
||||
}
|
||||
|
||||
private PrimarySkillType(Class<? extends SkillManager> managerClass, Color runescapeColor, List<SubSkillType> subSkillTypes) {
|
||||
this(managerClass, runescapeColor, null, null, subSkillTypes);
|
||||
private PrimarySkillType(Class<? extends SkillManager> managerClass, Color skillColor, List<SubSkillType> subSkillTypes) {
|
||||
this(managerClass, skillColor, null, null, subSkillTypes);
|
||||
}
|
||||
|
||||
private PrimarySkillType(Class<? extends SkillManager> managerClass, Color runescapeColor, SuperAbilityType ability, ToolType tool, List<SubSkillType> subSkillTypes) {
|
||||
private PrimarySkillType(Class<? extends SkillManager> managerClass, Color skillColor, SuperAbilityType ability, ToolType tool, List<SubSkillType> subSkillTypes) {
|
||||
this.managerClass = managerClass;
|
||||
this.runescapeColor = runescapeColor;
|
||||
this.skillColor = skillColor;
|
||||
this.ability = ability;
|
||||
this.tool = tool;
|
||||
this.subSkillTypes = subSkillTypes;
|
||||
@@ -243,7 +243,7 @@ public enum PrimarySkillType {
|
||||
}
|
||||
|
||||
/* public void celebrateLevelUp(Player player) {
|
||||
ParticleEffectUtils.fireworkParticleShower(player, runescapeColor);
|
||||
ParticleEffectUtils.fireworkParticleShower(player, skillColor);
|
||||
}*/
|
||||
|
||||
public boolean shouldProcess(Entity target) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.gmail.nossr50.datatypes.skills;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum SubSkillType {
|
||||
/* !! Warning -- Do not let subskills share a name with any existing PrimarySkillType as it will clash with the static import !! */
|
||||
|
||||
@@ -41,7 +43,7 @@ public enum SubSkillType {
|
||||
FISHING_SHAKE(1),
|
||||
|
||||
/* Herbalism */
|
||||
HERBALISM_DOUBLE_DROPS,
|
||||
HERBALISM_DOUBLE_DROPS(1),
|
||||
HERBALISM_FARMERS_DIET(5),
|
||||
HERBALISM_GREEN_TERRA(1),
|
||||
HERBALISM_GREEN_THUMB(4),
|
||||
@@ -52,7 +54,7 @@ public enum SubSkillType {
|
||||
MINING_BIGGER_BOMBS(1),
|
||||
MINING_BLAST_MINING(8),
|
||||
MINING_DEMOLITIONS_EXPERTISE(1),
|
||||
MINING_DOUBLE_DROPS,
|
||||
MINING_DOUBLE_DROPS(1),
|
||||
MINING_SUPER_BREAKER(1),
|
||||
|
||||
/* Repair */
|
||||
@@ -61,7 +63,7 @@ public enum SubSkillType {
|
||||
REPAIR_SUPER_REPAIR(1),
|
||||
|
||||
/* Salvage */
|
||||
SALVAGE_ADVANCED_SALVAGE(1),
|
||||
SALVAGE_SCRAP_COLLECTOR(8),
|
||||
SALVAGE_ARCANE_SALVAGE(8),
|
||||
|
||||
/* Smelting */
|
||||
@@ -157,7 +159,7 @@ public enum SubSkillType {
|
||||
public String getPermissionNodeAddress()
|
||||
{
|
||||
//TODO: This could be optimized
|
||||
return "mcmmo.ability." + getParentSkill().toString().toLowerCase() + "." + getConfigName(toString()).toLowerCase();
|
||||
return "mcmmo.ability." + getParentSkill().toString().toLowerCase(Locale.ENGLISH) + "." + getConfigName(toString()).toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gmail.nossr50.datatypes.skills;
|
||||
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.BlockUtils;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
@@ -198,7 +199,7 @@ public enum SuperAbilityType {
|
||||
public boolean blockCheck(BlockState blockState) {
|
||||
switch (this) {
|
||||
case BERSERK:
|
||||
return (BlockUtils.affectedByGigaDrillBreaker(blockState) || blockState.getType() == Material.SNOW);
|
||||
return (BlockUtils.affectedByGigaDrillBreaker(blockState) || blockState.getType() == Material.SNOW || mcMMO.getMaterialMapStore().isGlass(blockState.getType()));
|
||||
|
||||
case GIGA_DRILL_BREAKER:
|
||||
return BlockUtils.affectedByGigaDrillBreaker(blockState);
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.ItemUtils;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
@@ -32,6 +33,8 @@ import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class Roll extends AcrobaticsSubSkill {
|
||||
|
||||
|
||||
@@ -99,7 +102,7 @@ public class Roll extends AcrobaticsSubSkill {
|
||||
*/
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return ("mcmmo.ability."+getPrimaryKeyName()+"."+getConfigKeyName()).toLowerCase();
|
||||
return ("mcmmo.ability."+getPrimaryKeyName()+"."+getConfigKeyName()).toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,17 +277,31 @@ public class Roll extends AcrobaticsSubSkill {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.getInventory().getItemInMainHand().getType() == Material.ENDER_PEARL || player.isInsideVehicle()) {
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
|
||||
if (ItemUtils.hasItemInEitherHand(player, Material.ENDER_PEARL) || player.isInsideVehicle()) {
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Acrobatics XP Prevented: Ender Pearl or Inside Vehicle");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(UserManager.getPlayer(player).getAcrobaticsManager().hasFallenInLocationBefore(getBlockLocation(player)))
|
||||
{
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Acrobatics XP Prevented: Fallen in location before");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; //NOT EXPLOITING
|
||||
}
|
||||
|
||||
private float calculateRollXP(Player player, double damage, boolean isRoll) {
|
||||
//Clamp Damage to account for insane DRs
|
||||
damage = Math.min(40, damage);
|
||||
|
||||
ItemStack boots = player.getInventory().getBoots();
|
||||
float xp = (float) (damage * (isRoll ? ExperienceConfig.getInstance().getRollXPModifier() : ExperienceConfig.getInstance().getFallXPModifier()));
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.gmail.nossr50.datatypes.skills.subskills.taming;
|
||||
|
||||
import com.gmail.nossr50.util.StringUtils;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
public enum CallOfTheWildType {
|
||||
WOLF,
|
||||
CAT,
|
||||
HORSE;
|
||||
|
||||
//TODO: This is a hacky fix to make the COTW code in 2.1 more bearable, this will be removed upon the rework planned for COTW
|
||||
public String getConfigEntityTypeEntry() {
|
||||
|
||||
switch(this) {
|
||||
case CAT:
|
||||
return StringUtils.getPrettyEntityTypeString(EntityType.OCELOT); //Even though cats will be summoned in 1.14, we specify Ocelot here. This will be gone in 2.2
|
||||
case WOLF:
|
||||
return StringUtils.getPrettyEntityTypeString(EntityType.WOLF);
|
||||
case HORSE:
|
||||
return StringUtils.getPrettyEntityTypeString(EntityType.HORSE);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.gmail.nossr50.datatypes.skills.subskills.taming;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
/**
|
||||
* Data Container for properties used in summoning an entity via COTW
|
||||
*/
|
||||
public class TamingSummon {
|
||||
|
||||
private Material itemType;
|
||||
private int itemAmountRequired;
|
||||
private int entitiesSummoned;
|
||||
private int summonLifespan;
|
||||
private int summonCap;
|
||||
private CallOfTheWildType callOfTheWildType;
|
||||
private EntityType entityType;
|
||||
|
||||
public TamingSummon(CallOfTheWildType callOfTheWildType, Material itemType, int itemAmountRequired, int entitiesSummoned, int summonLifespan, int summonCap) {
|
||||
this.callOfTheWildType = callOfTheWildType;
|
||||
this.itemType = itemType;
|
||||
this.itemAmountRequired = Math.max(itemAmountRequired, 1);
|
||||
this.entitiesSummoned = Math.max(entitiesSummoned, 1);
|
||||
this.summonLifespan = summonLifespan;
|
||||
this.summonCap = Math.max(summonCap, 1);
|
||||
|
||||
initEntityType();
|
||||
}
|
||||
|
||||
private void initEntityType() {
|
||||
switch(callOfTheWildType) {
|
||||
case WOLF:
|
||||
entityType = EntityType.WOLF;
|
||||
break;
|
||||
case HORSE:
|
||||
entityType = EntityType.HORSE;
|
||||
break;
|
||||
case CAT:
|
||||
if(shouldSpawnCatInsteadOfOcelot()) {
|
||||
//Server is on 1.14 or above
|
||||
entityType = EntityType.CAT;
|
||||
} else {
|
||||
//Server is not on 1.14 or above
|
||||
entityType = EntityType.OCELOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSpawnCatInsteadOfOcelot() {
|
||||
try {
|
||||
Class<?> clazz = Class.forName("org.bukkit.entity.Panda");
|
||||
//Panda exists which means this is at least 1.14, so we should spawn a cat instead of ocelot
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
/*e.printStackTrace();*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public Material getItemType() {
|
||||
return itemType;
|
||||
}
|
||||
|
||||
public int getItemAmountRequired() {
|
||||
return itemAmountRequired;
|
||||
}
|
||||
|
||||
public int getEntitiesSummoned() {
|
||||
return entitiesSummoned;
|
||||
}
|
||||
|
||||
public int getSummonLifespan() {
|
||||
return summonLifespan;
|
||||
}
|
||||
|
||||
public int getSummonCap() {
|
||||
return summonCap;
|
||||
}
|
||||
|
||||
public CallOfTheWildType getCallOfTheWildType() {
|
||||
return callOfTheWildType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gmail.nossr50.events.players;
|
||||
|
||||
import com.gmail.nossr50.datatypes.player.PlayerProfile;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
public class McMMOPlayerProfileLoadEvent extends Event implements Cancellable {
|
||||
private boolean cancelled;
|
||||
private PlayerProfile profile;
|
||||
private Player player;
|
||||
public McMMOPlayerProfileLoadEvent(Player player, PlayerProfile profile){
|
||||
super(!Bukkit.isPrimaryThread());
|
||||
|
||||
this.cancelled = false;
|
||||
this.profile = profile;
|
||||
this.player = player;
|
||||
}
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public PlayerProfile getProfile(){return this.profile;}
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public Player getPlayer() {return player;}
|
||||
}
|
||||
@@ -5,13 +5,13 @@ import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
|
||||
/**
|
||||
* This event is sent for when mcMMO informs a player about various important information
|
||||
*/
|
||||
public class McMMOPlayerNotificationEvent extends PlayerEvent implements Cancellable {
|
||||
public class McMMOPlayerNotificationEvent extends Event implements Cancellable {
|
||||
private boolean isCancelled;
|
||||
/*
|
||||
* Messages can be sent to both places, as configured in advanced.yml
|
||||
@@ -27,7 +27,7 @@ public class McMMOPlayerNotificationEvent extends PlayerEvent implements Cancell
|
||||
protected final NotificationType notificationType;
|
||||
|
||||
public McMMOPlayerNotificationEvent(Player who, NotificationType notificationType, TextComponent notificationTextComponent, ChatMessageType chatMessageType, boolean isMessageAlsoBeingSentToChat) {
|
||||
super(who);
|
||||
super(false);
|
||||
this.notificationType = notificationType;
|
||||
this.notificationTextComponent = notificationTextComponent;
|
||||
this.chatMessageType = chatMessageType;
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.HiddenConfig;
|
||||
import com.gmail.nossr50.config.WorldBlacklist;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.meta.BonusDropMeta;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
|
||||
@@ -13,7 +14,6 @@ import com.gmail.nossr50.events.fake.FakeBlockDamageEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.alchemy.Alchemy;
|
||||
import com.gmail.nossr50.skills.excavation.ExcavationManager;
|
||||
import com.gmail.nossr50.skills.herbalism.Herbalism;
|
||||
import com.gmail.nossr50.skills.herbalism.HerbalismManager;
|
||||
import com.gmail.nossr50.skills.mining.MiningManager;
|
||||
import com.gmail.nossr50.skills.repair.Repair;
|
||||
@@ -29,23 +29,18 @@ import com.gmail.nossr50.util.sounds.SoundManager;
|
||||
import com.gmail.nossr50.util.sounds.SoundType;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardManager;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardUtils;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.*;
|
||||
import org.bukkit.event.entity.EntityChangeBlockEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockListener implements Listener {
|
||||
@@ -58,71 +53,62 @@ public class BlockListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockDropItemEvent(BlockDropItemEvent event)
|
||||
{
|
||||
for(Item item : event.getItems())
|
||||
{
|
||||
ItemStack is = new ItemStack(item.getItemStack());
|
||||
//Track how many "things" are being dropped
|
||||
HashSet<Material> uniqueMaterials = new HashSet<>();
|
||||
boolean dontRewardTE = false; //If we suspect TEs are mixed in with other things don't reward bonus drops for anything that isn't a block
|
||||
int blockCount = 0;
|
||||
|
||||
if(is.getAmount() <= 0)
|
||||
continue;
|
||||
for(Item item : event.getItems()) {
|
||||
//Track unique materials
|
||||
uniqueMaterials.add(item.getItemStack().getType());
|
||||
|
||||
//TODO: Ignore this abomination its rewritten in 2.2
|
||||
if(!Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.MINING, is.getType())
|
||||
&& !Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.HERBALISM, is.getType())
|
||||
//Count blocks as a second failsafe
|
||||
if(item.getItemStack().getType().isBlock())
|
||||
blockCount++;
|
||||
}
|
||||
|
||||
if(uniqueMaterials.size() > 1) {
|
||||
//Too many things are dropping, assume tile entities might be duped
|
||||
//Technically this would also prevent something like coal from being bonus dropped if you placed a TE above a coal ore when mining it but that's pretty edge case and this is a good solution for now
|
||||
dontRewardTE = true;
|
||||
}
|
||||
|
||||
//If there are more than one block in the item list we can't really trust it and will back out of rewarding bonus drops
|
||||
if(blockCount <= 1) {
|
||||
for(Item item : event.getItems())
|
||||
{
|
||||
ItemStack is = new ItemStack(item.getItemStack());
|
||||
|
||||
if(is.getAmount() <= 0)
|
||||
continue;
|
||||
|
||||
//TODO: Ignore this abomination its rewritten in 2.2
|
||||
if(!Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.MINING, is.getType())
|
||||
&& !Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.HERBALISM, is.getType())
|
||||
&& !Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.WOODCUTTING, is.getType()))
|
||||
continue;
|
||||
continue;
|
||||
|
||||
//TODO: Should just store the amount of drops in the metadata itself and use a loop
|
||||
if(event.getBlock().getState().getMetadata(mcMMO.doubleDrops).size() > 0)
|
||||
{
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
event.getBlock().getState().removeMetadata(mcMMO.doubleDrops, plugin);
|
||||
}
|
||||
else if(event.getBlock().getState().getMetadata(mcMMO.tripleDrops).size() > 0)
|
||||
{
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
event.getBlock().getState().removeMetadata(mcMMO.tripleDrops, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onBlockDropItemEvent(BlockDropItemEvent event)
|
||||
{
|
||||
for(Item item : event.getItems())
|
||||
{
|
||||
ItemStack is = new ItemStack(item.getItemStack());
|
||||
|
||||
if(event.getBlock().getMetadata(mcMMO.doubleDrops).size() > 0)
|
||||
{
|
||||
List<MetadataValue> metadataValue = event.getBlock().getMetadata(mcMMO.doubleDrops);
|
||||
|
||||
BonusDrops bonusDrops = (BonusDrops) metadataValue.get(0);
|
||||
Collection<ItemStack> potentialDrops = (Collection<ItemStack>) bonusDrops.value();
|
||||
|
||||
if(potentialDrops.contains(is))
|
||||
{
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
}
|
||||
|
||||
event.getBlock().removeMetadata(mcMMO.doubleDrops, plugin);
|
||||
} else {
|
||||
if(event.getBlock().getMetadata(mcMMO.tripleDrops).size() > 0) {
|
||||
List<MetadataValue> metadataValue = event.getBlock().getMetadata(mcMMO.tripleDrops);
|
||||
|
||||
BonusDrops bonusDrops = (BonusDrops) metadataValue.get(0);
|
||||
Collection<ItemStack> potentialDrops = (Collection<ItemStack>) bonusDrops.value();
|
||||
|
||||
if (potentialDrops.contains(is)) {
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
event.getBlock().getState().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
//If we suspect TEs might be duped only reward block
|
||||
if(dontRewardTE) {
|
||||
if(!is.getType().isBlock()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
event.getBlock().removeMetadata(mcMMO.tripleDrops, plugin);
|
||||
if (event.getBlock().getMetadata(mcMMO.BONUS_DROPS_METAKEY).size() > 0) {
|
||||
BonusDropMeta bonusDropMeta = (BonusDropMeta) event.getBlock().getMetadata(mcMMO.BONUS_DROPS_METAKEY).get(0);
|
||||
int bonusCount = bonusDropMeta.asInt();
|
||||
|
||||
for (int i = 0; i < bonusCount; i++) {
|
||||
event.getBlock().getWorld().dropItemNaturally(event.getBlockState().getLocation(), is);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if(event.getBlock().hasMetadata(mcMMO.BONUS_DROPS_METAKEY))
|
||||
event.getBlock().removeMetadata(mcMMO.BONUS_DROPS_METAKEY, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor BlockPistonExtend events.
|
||||
@@ -135,6 +121,10 @@ public class BlockListener implements Listener {
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
|
||||
return;
|
||||
|
||||
if(!ExperienceConfig.getInstance().isPistonCheatingPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockFace direction = event.getDirection();
|
||||
Block movedBlock = event.getBlock();
|
||||
movedBlock = movedBlock.getRelative(direction, 2);
|
||||
@@ -158,6 +148,10 @@ public class BlockListener implements Listener {
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
|
||||
return;
|
||||
|
||||
if(!ExperienceConfig.getInstance().isPistonCheatingPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get opposite direction so we get correct block
|
||||
BlockFace direction = event.getDirection();
|
||||
Block movedBlock = event.getBlock().getRelative(direction);
|
||||
@@ -181,39 +175,28 @@ public class BlockListener implements Listener {
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
|
||||
return;
|
||||
|
||||
if(BlockUtils.shouldBeWatched(event.getBlock().getState()))
|
||||
BlockState blockState = event.getNewState();
|
||||
|
||||
if(ExperienceConfig.getInstance().isSnowExploitPrevented() && BlockUtils.shouldBeWatched(blockState))
|
||||
{
|
||||
mcMMO.getPlaceStore().setTrue(event.getBlock());
|
||||
mcMMO.getPlaceStore().setTrue(blockState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor falling blocks.
|
||||
*
|
||||
* @param event The event to watch
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onFallingBlock(EntityChangeBlockEvent event) {
|
||||
public void onBlockFormEvent(BlockFormEvent event)
|
||||
{
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
|
||||
return;
|
||||
|
||||
if (BlockUtils.shouldBeWatched(event.getBlock().getState()) && event.getEntityType().equals(EntityType.FALLING_BLOCK)) {
|
||||
if (event.getTo().equals(Material.AIR) && mcMMO.getPlaceStore().isTrue(event.getBlock())) {
|
||||
event.getEntity().setMetadata("mcMMOBlockFall", new FixedMetadataValue( plugin, event.getBlock().getLocation()));
|
||||
} else {
|
||||
List<MetadataValue> values = event.getEntity().getMetadata( "mcMMOBlockFall" );
|
||||
|
||||
if (!values.isEmpty()) {
|
||||
|
||||
if (values.get(0).value() == null) return;
|
||||
Block spawn = ((org.bukkit.Location) values.get(0).value()).getBlock();
|
||||
|
||||
|
||||
mcMMO.getPlaceStore().setTrue( event.getBlock() );
|
||||
mcMMO.getPlaceStore().setFalse( spawn );
|
||||
|
||||
}
|
||||
if(ExperienceConfig.getInstance().preventStoneLavaFarming())
|
||||
{
|
||||
if(event.getNewState().getType() != Material.OBSIDIAN
|
||||
&& BlockUtils.shouldBeWatched(event.getNewState())
|
||||
&& ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, event.getNewState().getBlockData()))
|
||||
{
|
||||
mcMMO.getPlaceStore().setTrue(event.getNewState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,8 +346,13 @@ public class BlockListener implements Listener {
|
||||
* Instead, we check it inside the drops handler.
|
||||
*/
|
||||
if (PrimarySkillType.HERBALISM.getPermissions(player)) {
|
||||
herbalismManager.herbalismBlockCheck(blockState);
|
||||
herbalismManager.processHerbalismBlockBreakEvent(event);
|
||||
}
|
||||
/*
|
||||
* We return here so that we don't unmark any affected blocks
|
||||
* due to special checks managing this on their own:
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* MINING */
|
||||
@@ -435,11 +423,6 @@ public class BlockListener implements Listener {
|
||||
BlockState blockState = event.getBlock().getState();
|
||||
ItemStack heldItem = player.getInventory().getItemInMainHand();
|
||||
|
||||
if (Herbalism.isRecentlyRegrown(blockState)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ItemUtils.isSword(heldItem)) {
|
||||
HerbalismManager herbalismManager = UserManager.getPlayer(player).getHerbalismManager();
|
||||
|
||||
@@ -523,8 +506,23 @@ public class BlockListener implements Listener {
|
||||
else if (mcMMOPlayer.getToolPreparationMode(ToolType.SHOVEL) && ItemUtils.isShovel(heldItem) && BlockUtils.affectedByGigaDrillBreaker(blockState) && Permissions.gigaDrillBreaker(player)) {
|
||||
mcMMOPlayer.checkAbilityActivation(PrimarySkillType.EXCAVATION);
|
||||
}
|
||||
else if (mcMMOPlayer.getToolPreparationMode(ToolType.FISTS) && heldItem.getType() == Material.AIR && (BlockUtils.affectedByGigaDrillBreaker(blockState) || blockState.getType() == Material.SNOW || BlockUtils.affectedByBlockCracker(blockState) && Permissions.berserk(player))) {
|
||||
else if (mcMMOPlayer.getToolPreparationMode(ToolType.FISTS) && heldItem.getType() == Material.AIR && (BlockUtils.affectedByGigaDrillBreaker(blockState)
|
||||
|| mcMMO.getMaterialMapStore().isGlass(blockState.getType())
|
||||
|| blockState.getType() == Material.SNOW
|
||||
|| BlockUtils.affectedByBlockCracker(blockState) && Permissions.berserk(player))) {
|
||||
mcMMOPlayer.checkAbilityActivation(PrimarySkillType.UNARMED);
|
||||
|
||||
if(mcMMOPlayer.getAbilityMode(SuperAbilityType.BERSERK)) {
|
||||
if (SuperAbilityType.BERSERK.blockCheck(blockState) && EventUtils.simulateBlockBreak(blockState.getBlock(), player, true)) {
|
||||
event.setInstaBreak(true);
|
||||
|
||||
if(blockState.getType().getKey().getKey().contains("glass")) {
|
||||
SoundManager.worldSendSound(player.getWorld(), blockState.getLocation(), SoundType.GLASS);
|
||||
} else {
|
||||
SoundManager.sendSound(player, blockState.getLocation(), SoundType.POP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,20 +592,25 @@ public class BlockListener implements Listener {
|
||||
* We don't need to check permissions here because they've already been checked for the ability to even activate.
|
||||
*/
|
||||
if (mcMMOPlayer.getAbilityMode(SuperAbilityType.GREEN_TERRA) && BlockUtils.canMakeMossy(blockState)) {
|
||||
if (mcMMOPlayer.getHerbalismManager().processGreenTerra(blockState)) {
|
||||
if (mcMMOPlayer.getHerbalismManager().processGreenTerraBlockConversion(blockState)) {
|
||||
blockState.update(true);
|
||||
}
|
||||
}
|
||||
else if (mcMMOPlayer.getAbilityMode(SuperAbilityType.BERSERK) && heldItem.getType() == Material.AIR) {
|
||||
if (SuperAbilityType.BERSERK.blockCheck(block.getState()) && EventUtils.simulateBlockBreak(block, player, true)) {
|
||||
event.setInstaBreak(true);
|
||||
SoundManager.sendSound(player, block.getLocation(), SoundType.POP);
|
||||
}
|
||||
else if (mcMMOPlayer.getUnarmedManager().canUseBlockCracker() && BlockUtils.affectedByBlockCracker(blockState) && EventUtils.simulateBlockBreak(block, player, true)) {
|
||||
if (mcMMOPlayer.getUnarmedManager().blockCrackerCheck(blockState)) {
|
||||
else if (mcMMOPlayer.getAbilityMode(SuperAbilityType.BERSERK) && (heldItem.getType() == Material.AIR || Config.getInstance().getUnarmedItemsAsUnarmed())) {
|
||||
if (mcMMOPlayer.getUnarmedManager().canUseBlockCracker() && BlockUtils.affectedByBlockCracker(blockState)) {
|
||||
if (EventUtils.simulateBlockBreak(block, player, true) && mcMMOPlayer.getUnarmedManager().blockCrackerCheck(blockState)) {
|
||||
blockState.update();
|
||||
}
|
||||
}
|
||||
else if (!event.getInstaBreak() && SuperAbilityType.BERSERK.blockCheck(blockState) && EventUtils.simulateBlockBreak(block, player, true)) {
|
||||
event.setInstaBreak(true);
|
||||
|
||||
if(blockState.getType().getKey().getKey().contains("glass")) {
|
||||
SoundManager.worldSendSound(player.getWorld(), block.getLocation(), SoundType.GLASS);
|
||||
} else {
|
||||
SoundManager.sendSound(player, block.getLocation(), SoundType.POP);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mcMMOPlayer.getWoodcuttingManager().canUseLeafBlower(heldItem) && BlockUtils.isLeaves(blockState) && EventUtils.simulateBlockBreak(block, player, true)) {
|
||||
event.setInstaBreak(true);
|
||||
@@ -636,14 +639,16 @@ public class BlockListener implements Listener {
|
||||
debugStickDump(player, blockState);
|
||||
}
|
||||
|
||||
public void debugStickDump(Player player, BlockState blockState) {
|
||||
//TODO: Rewrite this
|
||||
//TODO: Convert into locale strings
|
||||
private void debugStickDump(Player player, BlockState blockState) {
|
||||
//Profile not loaded
|
||||
if(UserManager.getPlayer(player) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(player.getInventory().getItemInMainHand().getType() == Material.DEBUG_STICK)
|
||||
if(UserManager.getPlayer(player).isDebugMode())
|
||||
{
|
||||
if(mcMMO.getPlaceStore().isTrue(blockState))
|
||||
player.sendMessage("[mcMMO DEBUG] This block is not natural and does not reward treasures/XP");
|
||||
@@ -684,10 +689,12 @@ public class BlockListener implements Listener {
|
||||
|
||||
if(ExperienceConfig.getInstance().isExperienceBarsEnabled())
|
||||
player.sendMessage("[mcMMO DEBUG] XP bars are enabled, however you should check per-skill settings to make sure those are enabled.");
|
||||
|
||||
player.sendMessage(ChatColor.RED+"You can turn this debug info off by typing "+ChatColor.GOLD+"/mmodebug");
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupAbilityTools(Player player, McMMOPlayer mcMMOPlayer, BlockState blockState, ItemStack heldItem) {
|
||||
private void cleanupAbilityTools(Player player, McMMOPlayer mcMMOPlayer, BlockState blockState, ItemStack heldItem) {
|
||||
if (HiddenConfig.getInstance().useEnchantmentBuffs()) {
|
||||
if ((ItemUtils.isPickaxe(heldItem) && !mcMMOPlayer.getAbilityMode(SuperAbilityType.SUPER_BREAKER)) || (ItemUtils.isShovel(heldItem) && !mcMMOPlayer.getAbilityMode(SuperAbilityType.GIGA_DRILL_BREAKER))) {
|
||||
SkillUtils.removeAbilityBuff(heldItem);
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.WorldBlacklist;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.meta.OldName;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.subskills.interfaces.InteractType;
|
||||
@@ -18,11 +17,15 @@ import com.gmail.nossr50.skills.mining.BlastMining;
|
||||
import com.gmail.nossr50.skills.mining.MiningManager;
|
||||
import com.gmail.nossr50.skills.taming.Taming;
|
||||
import com.gmail.nossr50.skills.taming.TamingManager;
|
||||
import com.gmail.nossr50.skills.unarmed.UnarmedManager;
|
||||
import com.gmail.nossr50.util.BlockUtils;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.random.RandomChanceUtil;
|
||||
import com.gmail.nossr50.util.skills.CombatUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillActivationType;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardManager;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardUtils;
|
||||
import org.bukkit.Material;
|
||||
@@ -30,22 +33,19 @@ import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.*;
|
||||
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
||||
import org.bukkit.event.entity.EntityDamageEvent.DamageModifier;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.PotionMeta;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EntityListener implements Listener {
|
||||
private final mcMMO plugin;
|
||||
|
||||
@@ -72,10 +72,16 @@ public class EntityListener implements Listener {
|
||||
if(!ExperienceConfig.getInstance().isEndermanEndermiteFarmingPrevented())
|
||||
return;
|
||||
|
||||
//It's rare but targets can be null sometimes
|
||||
if(event.getTarget() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Prevent entities from giving XP if they target endermite
|
||||
if(event.getTarget() instanceof Endermite)
|
||||
{
|
||||
if(event.getEntity().getMetadata(mcMMO.entityMetadataKey) == null || event.getEntity().getMetadata(mcMMO.entityMetadataKey).size() <= 0)
|
||||
if(!event.getEntity().hasMetadata(mcMMO.entityMetadataKey))
|
||||
event.getEntity().setMetadata(mcMMO.entityMetadataKey, mcMMO.metadataValue);
|
||||
}
|
||||
}
|
||||
@@ -106,7 +112,8 @@ public class EntityListener implements Listener {
|
||||
|
||||
ItemStack bow = event.getBow();
|
||||
|
||||
if (bow != null && bow.containsEnchantment(Enchantment.ARROW_INFINITE)) {
|
||||
if (bow != null
|
||||
&& bow.containsEnchantment(Enchantment.ARROW_INFINITE)) {
|
||||
projectile.setMetadata(mcMMO.infiniteArrowKey, mcMMO.metadataValue);
|
||||
}
|
||||
|
||||
@@ -120,9 +127,9 @@ public class EntityListener implements Listener {
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
return;
|
||||
|
||||
if(event.getEntity() instanceof Player)
|
||||
if(event.getEntity().getShooter() instanceof Player)
|
||||
{
|
||||
Player player = (Player) event.getEntity();
|
||||
Player player = (Player) event.getEntity().getShooter();
|
||||
|
||||
/* WORLD GUARD MAIN FLAG CHECK */
|
||||
if(WorldGuardUtils.isWorldGuardLoaded())
|
||||
@@ -130,16 +137,24 @@ public class EntityListener implements Listener {
|
||||
if(!WorldGuardManager.getInstance().hasMainFlag(player))
|
||||
return;
|
||||
}
|
||||
|
||||
Projectile projectile = event.getEntity();
|
||||
|
||||
if(!(projectile instanceof Arrow))
|
||||
return;
|
||||
|
||||
projectile.setMetadata(mcMMO.bowForceKey, new FixedMetadataValue(plugin, 1.0));
|
||||
projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation()));
|
||||
|
||||
for(Enchantment enchantment : player.getInventory().getItemInMainHand().getEnchantments().keySet()) {
|
||||
if(enchantment.getName().equalsIgnoreCase("piercing"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ARCHERY_ARROW_RETRIEVAL, player)) {
|
||||
projectile.setMetadata(mcMMO.trackedArrow, mcMMO.metadataValue);
|
||||
}
|
||||
}
|
||||
|
||||
Projectile projectile = event.getEntity();
|
||||
|
||||
if (!(projectile instanceof Arrow) || projectile.hasMetadata(mcMMO.bowForceKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
projectile.setMetadata(mcMMO.bowForceKey, new FixedMetadataValue(plugin, 1.0));
|
||||
projectile.setMetadata(mcMMO.arrowDistanceKey, new FixedMetadataValue(plugin, projectile.getLocation()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,7 +174,9 @@ public class EntityListener implements Listener {
|
||||
// When the event is fired for the falling block that changes back to a
|
||||
// normal block
|
||||
// event.getBlock().getType() returns AIR
|
||||
if (!BlockUtils.shouldBeWatched(block.getState()) && block.getType() != Material.AIR) {
|
||||
if (!BlockUtils.shouldBeWatched(block.getState())
|
||||
&& block.getState().getType() != Material.WATER
|
||||
&& block.getType() != Material.AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,6 +187,7 @@ public class EntityListener implements Listener {
|
||||
|
||||
if (mcMMO.getPlaceStore().isTrue(block) && !isTracked) {
|
||||
mcMMO.getPlaceStore().setFalse(block);
|
||||
|
||||
entity.setMetadata(mcMMO.entityMetadataKey, mcMMO.metadataValue);
|
||||
}
|
||||
else if (isTracked) {
|
||||
@@ -234,23 +252,24 @@ public class EntityListener implements Listener {
|
||||
Bukkit.broadcastMessage("");
|
||||
}*/
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onEntityDamageLowest(EntityDamageByEntityEvent event)
|
||||
{
|
||||
Entity defender = event.getEntity();
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onEntityCombustByEntityEvent(EntityCombustByEntityEvent event) {
|
||||
//Prevent players from setting fire to each other if they are in the same party
|
||||
if(event.getEntity() instanceof Player) {
|
||||
Player defender = (Player) event.getEntity();
|
||||
|
||||
if(defender.getMetadata(mcMMO.CUSTOM_DAMAGE_METAKEY).size() > 0)
|
||||
{
|
||||
defender.removeMetadata(mcMMO.CUSTOM_DAMAGE_METAKEY, plugin);
|
||||
|
||||
if(defender instanceof Player)
|
||||
{
|
||||
LivingEntity defLive = (LivingEntity) defender;
|
||||
defLive.setHealth(Math.max(0, (defLive.getHealth() - event.getFinalDamage())));
|
||||
event.setCancelled(true);
|
||||
if(event.getCombuster() instanceof Projectile) {
|
||||
Projectile projectile = (Projectile) event.getCombuster();
|
||||
if(projectile.getShooter() instanceof Player) {
|
||||
Player attacker = (Player) projectile.getShooter();
|
||||
if(checkParties(event, defender, attacker))
|
||||
return;
|
||||
}
|
||||
} else if(event.getCombuster() instanceof Player) {
|
||||
Player attacker = (Player) event.getCombuster();
|
||||
if(checkParties(event, defender, attacker))
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +285,25 @@ public class EntityListener implements Listener {
|
||||
Entity defender = event.getEntity();
|
||||
Entity attacker = event.getDamager();
|
||||
|
||||
if(WorldGuardUtils.isWorldGuardLoaded())
|
||||
{
|
||||
if(attacker instanceof Player) {
|
||||
|
||||
if(!WorldGuardManager.getInstance().hasMainFlag((Player) attacker))
|
||||
return;
|
||||
|
||||
} else if(attacker instanceof Projectile) {
|
||||
|
||||
Projectile projectile = (Projectile) attacker;
|
||||
|
||||
if(projectile.getShooter() instanceof Player) {
|
||||
if(!WorldGuardManager.getInstance().hasMainFlag((Player) projectile.getShooter()))
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
return;
|
||||
@@ -274,6 +312,17 @@ public class EntityListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't process this event for marked entities, for players this is handled above,
|
||||
// However, for entities, we do not wanna cancel this event to allow plugins to observe changes
|
||||
// properly
|
||||
if (defender.getMetadata(mcMMO.CUSTOM_DAMAGE_METAKEY).size() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CombatUtils.isProcessingNoInvulnDamage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getEntity() instanceof ArmorStand) {
|
||||
return;
|
||||
}
|
||||
@@ -284,45 +333,7 @@ public class EntityListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if(attacker instanceof Player)
|
||||
{
|
||||
Player player = (Player) attacker;
|
||||
|
||||
/* WORLD GUARD MAIN FLAG CHECK */
|
||||
if(WorldGuardUtils.isWorldGuardLoaded())
|
||||
{
|
||||
if(!WorldGuardManager.getInstance().hasMainFlag(player))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (damage <= 0) {
|
||||
if (defender instanceof Player && attacker instanceof Player) {
|
||||
Player defendingPlayer = (Player) defender;
|
||||
Player attackingPlayer = (Player) attacker;
|
||||
if (event.getDamage(DamageModifier.ABSORPTION) > 0) {
|
||||
//If friendly fire is off don't allow players to hurt one another
|
||||
if(!Config.getInstance().getPartyFriendlyFire())
|
||||
if ((PartyManager.inSameParty(defendingPlayer, attackingPlayer) || PartyManager.areAllies(defendingPlayer, attackingPlayer)) && !(Permissions.friendlyFire(attackingPlayer) && Permissions.friendlyFire(defendingPlayer))) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
As far as I can tell at one point we registered meta-data about custom damage and we no longer do that.
|
||||
|
||||
if (defender.hasMetadata(mcMMO.customDamageKey)) {
|
||||
defender.removeMetadata(mcMMO.customDamageKey, plugin);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (Misc.isNPCEntity(defender) || !defender.isValid() || !(defender instanceof LivingEntity)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(defender) || !defender.isValid() || !(defender instanceof LivingEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -332,18 +343,11 @@ public class EntityListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Misc.isNPCEntity(attacker)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(attacker)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker instanceof Projectile) {
|
||||
ProjectileSource projectileSource = ((Projectile) attacker).getShooter();
|
||||
|
||||
if (projectileSource instanceof LivingEntity) {
|
||||
attacker = (LivingEntity) projectileSource;
|
||||
}
|
||||
}
|
||||
else if (attacker instanceof Tameable) {
|
||||
if (attacker instanceof Tameable) {
|
||||
AnimalTamer animalTamer = ((Tameable) attacker).getOwner();
|
||||
|
||||
if (animalTamer != null && ((OfflinePlayer) animalTamer).isOnline()) {
|
||||
@@ -356,26 +360,66 @@ public class EntityListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
if (defender instanceof Player && attacker instanceof Player) {
|
||||
//Friendly fire checks
|
||||
if (defender instanceof Player) {
|
||||
Player defendingPlayer = (Player) defender;
|
||||
Player attackingPlayer = (Player) attacker;
|
||||
Player attackingPlayer;
|
||||
|
||||
if (!UserManager.hasPlayerDataKey(defendingPlayer) || !UserManager.hasPlayerDataKey(attackingPlayer)) {
|
||||
return;
|
||||
}
|
||||
//If the attacker is a Player or a projectile beloning to a player
|
||||
if(attacker instanceof Projectile || attacker instanceof Player) {
|
||||
if(attacker instanceof Projectile) {
|
||||
Projectile projectile = (Projectile) attacker;
|
||||
if(((Projectile) attacker).getShooter() instanceof Player) {
|
||||
attackingPlayer = (Player) projectile.getShooter();
|
||||
|
||||
// We want to make sure we're not gaining XP or applying abilities
|
||||
// when we hit ourselves
|
||||
if (defendingPlayer.equals(attackingPlayer)) {
|
||||
return;
|
||||
}
|
||||
//Check for party friendly fire and cancel the event
|
||||
if (checkParties(event, defendingPlayer, attackingPlayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Party Friendly Fire
|
||||
if(!Config.getInstance().getPartyFriendlyFire())
|
||||
if ((PartyManager.inSameParty(defendingPlayer, attackingPlayer) || PartyManager.areAllies(defendingPlayer, attackingPlayer)) && !(Permissions.friendlyFire(attackingPlayer) && Permissions.friendlyFire(defendingPlayer))) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//Deflect checks
|
||||
final McMMOPlayer mcMMOPlayer = UserManager.getPlayer(defendingPlayer);
|
||||
if (mcMMOPlayer != null) {
|
||||
UnarmedManager unarmedManager = mcMMOPlayer.getUnarmedManager();
|
||||
|
||||
if (unarmedManager.canDeflect()) {
|
||||
if (unarmedManager.deflectCheck()) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attackingPlayer = (Player) attacker;
|
||||
//Check for party friendly fire and cancel the event
|
||||
if (checkParties(event, defendingPlayer, attackingPlayer))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Required setup for processCombatAttack
|
||||
if(attacker instanceof Projectile) {
|
||||
ProjectileSource shooter = ((Projectile) attacker).getShooter();
|
||||
if(shooter instanceof LivingEntity) {
|
||||
attacker = (LivingEntity) shooter;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This was put here to solve a plugin conflict with a mod called Project Korra
|
||||
* Project Korra sends out a damage event with exactly 0 damage
|
||||
* mcMMO does some calculations for the damage in an event and it ends up dividing by zero,
|
||||
* as a result of the modifiers for the event being 0 and the damage set for this event being 0.
|
||||
*
|
||||
* Surprising this kind of thing
|
||||
*
|
||||
*/
|
||||
if(damage <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CombatUtils.processCombatAttack(event, attacker, target);
|
||||
@@ -384,25 +428,39 @@ public class EntityListener implements Listener {
|
||||
/**
|
||||
* This sets entity names back to whatever they are supposed to be
|
||||
*/
|
||||
if(!(attacker instanceof Player) && defender instanceof Player)
|
||||
if(event.getFinalDamage() >= target.getHealth())
|
||||
{
|
||||
if(event.getFinalDamage() >= ((LivingEntity) defender).getHealth())
|
||||
if(attacker instanceof LivingEntity)
|
||||
{
|
||||
List<MetadataValue> metadataValue = attacker.getMetadata("mcMMO_oldName");
|
||||
|
||||
if(metadataValue.size() <= 0)
|
||||
return;
|
||||
|
||||
if(metadataValue != null)
|
||||
{
|
||||
OldName oldName = (OldName) metadataValue.get(0);
|
||||
attacker.setCustomName(oldName.asString());
|
||||
attacker.setCustomNameVisible(false);
|
||||
}
|
||||
CombatUtils.fixNames((LivingEntity) attacker);
|
||||
}
|
||||
|
||||
CombatUtils.fixNames(target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean checkParties(Cancellable event, Player defendingPlayer, Player attackingPlayer) {
|
||||
if (!UserManager.hasPlayerDataKey(defendingPlayer) || !UserManager.hasPlayerDataKey(attackingPlayer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We want to make sure we're not gaining XP or applying abilities
|
||||
// when we hit ourselves
|
||||
if (defendingPlayer.equals(attackingPlayer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Party Friendly Fire
|
||||
if(!Config.getInstance().getPartyFriendlyFire())
|
||||
if ((PartyManager.inSameParty(defendingPlayer, attackingPlayer)
|
||||
|| PartyManager.areAllies(defendingPlayer, attackingPlayer))
|
||||
&& !(Permissions.friendlyFire(attackingPlayer)
|
||||
&& Permissions.friendlyFire(defendingPlayer))) {
|
||||
event.setCancelled(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,7 +516,7 @@ public class EntityListener implements Listener {
|
||||
}
|
||||
*/
|
||||
|
||||
if (Misc.isNPCEntity(entity) || !entity.isValid() || !(entity instanceof LivingEntity)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(entity) || !entity.isValid() || !(entity instanceof LivingEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -595,7 +653,7 @@ public class EntityListener implements Listener {
|
||||
|
||||
LivingEntity entity = event.getEntity();
|
||||
|
||||
if (Misc.isNPCEntity(entity)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -620,7 +678,7 @@ public class EntityListener implements Listener {
|
||||
* @param event
|
||||
* The event to watch
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onEntityDeath(EntityDeathEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
@@ -628,7 +686,7 @@ public class EntityListener implements Listener {
|
||||
|
||||
LivingEntity entity = event.getEntity();
|
||||
|
||||
if (Misc.isNPCEntity(entity)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -641,7 +699,7 @@ public class EntityListener implements Listener {
|
||||
* @param event
|
||||
* The event to watch
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onCreatureSpawn(CreatureSpawnEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
@@ -671,6 +729,28 @@ public class EntityListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onEntityBreed(EntityBreedEvent event) {
|
||||
if(ExperienceConfig.getInstance().isCOTWBreedingPrevented()) {
|
||||
if(event.getFather().hasMetadata(mcMMO.COTW_TEMPORARY_SUMMON) || event.getMother().hasMetadata(mcMMO.COTW_TEMPORARY_SUMMON)) {
|
||||
event.setCancelled(true);
|
||||
Animals mom = (Animals) event.getMother();
|
||||
Animals father = (Animals) event.getFather();
|
||||
|
||||
//Prevent love mode spam
|
||||
mom.setLoveModeTicks(0);
|
||||
father.setLoveModeTicks(0);
|
||||
|
||||
//Inform the player
|
||||
if(event.getBreeder() instanceof Player) {
|
||||
Player player = (Player) event.getBreeder();
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Taming.Summon.COTW.BreedingDisallowed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ExplosionPrime events that involve modifying the event.
|
||||
*
|
||||
@@ -764,27 +844,6 @@ public class EntityListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle EntityExplode events that involve modifying the event.
|
||||
*
|
||||
* @param event
|
||||
* The event to modify
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onEntityExplodeMonitor(EntityExplodeEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld()))
|
||||
return;
|
||||
|
||||
Entity entity = event.getEntity();
|
||||
|
||||
if (!(entity instanceof TNTPrimed) || !entity.hasMetadata(mcMMO.tntsafeMetadataKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.blockList().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle FoodLevelChange events that involve modifying the event.
|
||||
*
|
||||
@@ -830,12 +889,24 @@ public class EntityListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
//Determine which hand is eating food
|
||||
//The main hand is used over the off hand if they both have food, so check the main hand first
|
||||
Material foodInHand;
|
||||
|
||||
if(mcMMO.getMaterialMapStore().isFood(player.getInventory().getItemInMainHand().getType())) {
|
||||
foodInHand = player.getInventory().getItemInMainHand().getType();
|
||||
} else if(mcMMO.getMaterialMapStore().isFood(player.getInventory().getItemInOffHand().getType())) {
|
||||
foodInHand = player.getInventory().getItemInOffHand().getType();
|
||||
} else {
|
||||
return; //Not Food
|
||||
}
|
||||
|
||||
/*
|
||||
* Some foods have 3 ranks Some foods have 5 ranks The number of ranks
|
||||
* is based on how 'common' the item is We can adjust this quite easily
|
||||
* if we find something is giving too much of a bonus
|
||||
*/
|
||||
switch (player.getInventory().getItemInMainHand().getType()) {
|
||||
switch (foodInHand) {
|
||||
case BAKED_POTATO: /*
|
||||
* RESTORES 3 HUNGER - RESTORES 5 1/2 HUNGER @
|
||||
* 1000
|
||||
@@ -917,7 +988,7 @@ public class EntityListener implements Listener {
|
||||
|
||||
LivingEntity entity = event.getEntity();
|
||||
|
||||
if (!UserManager.hasPlayerDataKey(player) || Misc.isNPCEntity(entity) || entity.hasMetadata(mcMMO.entityMetadataKey)) {
|
||||
if (!UserManager.hasPlayerDataKey(player) || Misc.isNPCEntityExcludingVillagers(entity) || entity.hasMetadata(mcMMO.entityMetadataKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,14 @@ import org.bukkit.event.Event;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
public class InteractionManager {
|
||||
private static HashMap<InteractType, ArrayList<Interaction>> interactRegister;
|
||||
private static HashMap<String, AbstractSubSkill> subSkillNameMap; //Used for mmoinfo optimization
|
||||
private static ArrayList<AbstractSubSkill> subSkillList;
|
||||
|
||||
/**
|
||||
* Registers subskills with the Interaction registration
|
||||
* @param abstractSubSkill the target subskill to register
|
||||
*/
|
||||
public static void registerSubSkill(AbstractSubSkill abstractSubSkill)
|
||||
{
|
||||
public static void initMaps() {
|
||||
/* INIT MAPS */
|
||||
if(interactRegister == null)
|
||||
interactRegister = new HashMap<>();
|
||||
@@ -30,7 +26,14 @@ public class InteractionManager {
|
||||
|
||||
if(subSkillNameMap == null)
|
||||
subSkillNameMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers subskills with the Interaction registration
|
||||
* @param abstractSubSkill the target subskill to register
|
||||
*/
|
||||
public static void registerSubSkill(AbstractSubSkill abstractSubSkill)
|
||||
{
|
||||
//Store a unique copy of each subskill
|
||||
if(!subSkillList.contains(abstractSubSkill))
|
||||
subSkillList.add(abstractSubSkill);
|
||||
@@ -45,7 +48,7 @@ public class InteractionManager {
|
||||
//Register skill
|
||||
arrayRef.add(abstractSubSkill);
|
||||
|
||||
String lowerCaseName = abstractSubSkill.getConfigKeyName().toLowerCase();
|
||||
String lowerCaseName = abstractSubSkill.getConfigKeyName().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
//Register in name map
|
||||
if(subSkillNameMap.get(lowerCaseName) == null)
|
||||
@@ -62,7 +65,7 @@ public class InteractionManager {
|
||||
*/
|
||||
public static AbstractSubSkill getAbstractByName(String name)
|
||||
{
|
||||
return subSkillNameMap.get(name.toLowerCase());
|
||||
return subSkillNameMap.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +76,9 @@ public class InteractionManager {
|
||||
*/
|
||||
public static void processEvent(Event event, mcMMO plugin, InteractType curInteractType)
|
||||
{
|
||||
if(interactRegister.get(curInteractType) == null)
|
||||
return;
|
||||
|
||||
for(Interaction interaction : interactRegister.get(curInteractType))
|
||||
{
|
||||
interaction.doInteraction(event, plugin);
|
||||
|
||||
@@ -190,7 +190,8 @@ public class InventoryListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
int exp = UserManager.getPlayer(player).getSmeltingManager().vanillaXPBoost(event.getExpToDrop());
|
||||
int xpToDrop = event.getExpToDrop();
|
||||
int exp = UserManager.getPlayer(player).getSmeltingManager().vanillaXPBoost(xpToDrop);
|
||||
event.setExpToDrop(exp);
|
||||
}
|
||||
|
||||
@@ -397,8 +398,10 @@ public class InventoryListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onInventoryMoveItemEvent(InventoryMoveItemEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getSource().getLocation().getWorld()))
|
||||
return;
|
||||
|
||||
if(event.getSource().getLocation() != null)
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getSource().getLocation().getWorld()))
|
||||
return;
|
||||
|
||||
Inventory inventory = event.getDestination();
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ import com.gmail.nossr50.datatypes.chat.ChatMode;
|
||||
import com.gmail.nossr50.datatypes.party.Party;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
|
||||
import com.gmail.nossr50.events.fake.FakePlayerAnimationEvent;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.party.ShareHandler;
|
||||
@@ -24,11 +27,14 @@ import com.gmail.nossr50.skills.salvage.SalvageManager;
|
||||
import com.gmail.nossr50.skills.taming.TamingManager;
|
||||
import com.gmail.nossr50.util.*;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import com.gmail.nossr50.util.sounds.SoundManager;
|
||||
import com.gmail.nossr50.util.sounds.SoundType;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardManager;
|
||||
import com.gmail.nossr50.worldguard.WorldGuardUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
@@ -45,6 +51,8 @@ import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
private final mcMMO plugin;
|
||||
|
||||
@@ -64,8 +72,16 @@ public class PlayerListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||
/* WORLD BLACKLIST CHECK */
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld()))
|
||||
if(WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld())) {
|
||||
//Remove scoreboards
|
||||
ScoreboardManager.teardownPlayer(event.getPlayer());
|
||||
return;
|
||||
} else if(WorldBlacklist.isWorldBlacklisted(event.getFrom().getWorld())) {
|
||||
//This only fires if they are traveling to a non-blacklisted world from a blacklisted world
|
||||
|
||||
//Setup scoreboards
|
||||
ScoreboardManager.setupPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
|
||||
@@ -88,7 +104,6 @@ public class PlayerListener implements Listener {
|
||||
|
||||
UserManager.getPlayer(player).actualizeTeleportATS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PlayerDeathEvents at the lowest priority.
|
||||
* <p>
|
||||
@@ -275,7 +290,7 @@ public class PlayerListener implements Listener {
|
||||
//TODO Update to new API once available! Waiting for case CAUGHT_TREASURE:
|
||||
Item fishingCatch = (Item) event.getCaught();
|
||||
|
||||
if (Config.getInstance().getFishingOverrideTreasures() &&
|
||||
if (Config.getInstance(). getFishingOverrideTreasures() &&
|
||||
fishingCatch.getItemStack().getType() != Material.SALMON &&
|
||||
fishingCatch.getItemStack().getType() != Material.COD &&
|
||||
fishingCatch.getItemStack().getType() != Material.TROPICAL_FISH &&
|
||||
@@ -383,7 +398,7 @@ public class PlayerListener implements Listener {
|
||||
{
|
||||
if(fishingManager.isExploitingFishing(event.getHook().getLocation().toVector()))
|
||||
{
|
||||
player.sendMessage(LocaleLoader.getString("Fishing.Scarcity"));
|
||||
player.sendMessage(LocaleLoader.getString("Fishing.ScarcityTip", 3));
|
||||
event.setExpToDrop(0);
|
||||
Item caughtItem = (Item) caught;
|
||||
caughtItem.remove();
|
||||
@@ -446,6 +461,11 @@ public class PlayerListener implements Listener {
|
||||
Item drop = event.getItem();
|
||||
ItemStack dropStack = drop.getItemStack();
|
||||
|
||||
//Remove tracking
|
||||
if(drop.hasMetadata(mcMMO.trackedArrow)) {
|
||||
drop.removeMetadata(mcMMO.trackedArrow, mcMMO.p);
|
||||
}
|
||||
|
||||
if (drop.hasMetadata(mcMMO.disarmedItemKey)) {
|
||||
if (!player.getName().equals(drop.getMetadata(mcMMO.disarmedItemKey).get(0).asString())) {
|
||||
event.setCancelled(true);
|
||||
@@ -500,7 +520,9 @@ public class PlayerListener implements Listener {
|
||||
}
|
||||
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
mcMMOPlayer.logout(false);
|
||||
//There's an issue with using Async saves on player quit
|
||||
//Basically there are conditions in which an async task does not execute fast enough to save the data if the server shutdown shortly after this task was scheduled
|
||||
mcMMOPlayer.logout(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,10 +538,6 @@ public class PlayerListener implements Listener {
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (Misc.isNPCEntity(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Delay loading for 3 seconds in case the player has a save task running, its hacky but it should do the trick
|
||||
new PlayerProfileLoadingTask(player).runTaskLaterAsynchronously(mcMMO.p, 60);
|
||||
|
||||
@@ -527,7 +545,7 @@ public class PlayerListener implements Listener {
|
||||
Motd.displayAll(player);
|
||||
}
|
||||
|
||||
if (plugin.isXPEventEnabled()) {
|
||||
if (plugin.isXPEventEnabled() && Config.getInstance().playerJoinEventInfo()) {
|
||||
player.sendMessage(LocaleLoader.getString("XPRate.Event", ExperienceConfig.getInstance().getExperienceGainsGlobalMultiplier()));
|
||||
}
|
||||
}
|
||||
@@ -599,27 +617,34 @@ public class PlayerListener implements Listener {
|
||||
|
||||
if (!Config.getInstance().getAbilitiesOnlyActivateWhenSneaking() || player.isSneaking()) {
|
||||
/* REPAIR CHECKS */
|
||||
if (type == Repair.anvilMaterial && PrimarySkillType.REPAIR.getPermissions(player) && mcMMO.getRepairableManager().isRepairable(heldItem)) {
|
||||
if (type == Repair.anvilMaterial
|
||||
&& PrimarySkillType.REPAIR.getPermissions(player)
|
||||
&& mcMMO.getRepairableManager().isRepairable(heldItem)
|
||||
&& heldItem.getAmount() <= 1) {
|
||||
RepairManager repairManager = mcMMOPlayer.getRepairManager();
|
||||
event.setCancelled(true);
|
||||
|
||||
// Make sure the player knows what he's doing when trying to repair an enchanted item
|
||||
if (!(heldItem.getEnchantments().size() > 0) || repairManager.checkConfirmation(true)) {
|
||||
if (repairManager.checkConfirmation(true)) {
|
||||
repairManager.handleRepair(heldItem);
|
||||
player.updateInventory();
|
||||
}
|
||||
}
|
||||
/* SALVAGE CHECKS */
|
||||
else if (type == Salvage.anvilMaterial && PrimarySkillType.SALVAGE.getPermissions(player) && mcMMO.getSalvageableManager().isSalvageable(heldItem)) {
|
||||
SalvageManager salvageManager = UserManager.getPlayer(player).getSalvageManager();
|
||||
event.setCancelled(true);
|
||||
else if (type == Salvage.anvilMaterial
|
||||
&& PrimarySkillType.SALVAGE.getPermissions(player)
|
||||
&& RankUtils.hasUnlockedSubskill(player, SubSkillType.SALVAGE_SCRAP_COLLECTOR)
|
||||
&& mcMMO.getSalvageableManager().isSalvageable(heldItem)
|
||||
&& heldItem.getAmount() <= 1) {
|
||||
SalvageManager salvageManager = UserManager.getPlayer(player).getSalvageManager();
|
||||
event.setCancelled(true);
|
||||
|
||||
// Make sure the player knows what he's doing when trying to salvage an enchanted item
|
||||
if (!(heldItem.getEnchantments().size() > 0) || salvageManager.checkConfirmation(true)) {
|
||||
SkillUtils.handleAbilitySpeedDecrease(player);
|
||||
salvageManager.handleSalvage(block.getLocation(), heldItem);
|
||||
player.updateInventory();
|
||||
}
|
||||
// Make sure the player knows what he's doing when trying to salvage an enchanted item
|
||||
if (salvageManager.checkConfirmation(true)) {
|
||||
SkillUtils.handleAbilitySpeedDecrease(player);
|
||||
salvageManager.handleSalvage(block.getLocation(), heldItem);
|
||||
player.updateInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* BLAST MINING CHECK */
|
||||
@@ -703,7 +728,8 @@ public class PlayerListener implements Listener {
|
||||
//Spam Fishing Detection
|
||||
if(event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_AIR)
|
||||
{
|
||||
if(heldItem.getType() == Material.FISHING_ROD || player.getInventory().getItemInOffHand().getType() == Material.FISHING_ROD)
|
||||
if(ExperienceConfig.getInstance().isFishingExploitingPrevented()
|
||||
&& (heldItem.getType() == Material.FISHING_ROD || player.getInventory().getItemInOffHand().getType() == Material.FISHING_ROD))
|
||||
{
|
||||
if(player.isInsideVehicle() && (player.getVehicle() instanceof Minecart || player.getVehicle() instanceof PoweredMinecart))
|
||||
{
|
||||
@@ -757,16 +783,17 @@ public class PlayerListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
FakePlayerAnimationEvent fakeSwing = new FakePlayerAnimationEvent(event.getPlayer()); //PlayerAnimationEvent compat
|
||||
if (herbalismManager.canGreenThumbBlock(blockState)) {
|
||||
Bukkit.getPluginManager().callEvent(fakeSwing);
|
||||
player.getInventory().setItemInMainHand(new ItemStack(Material.WHEAT_SEEDS, heldItem.getAmount() - 1));
|
||||
|
||||
if (herbalismManager.processGreenThumbBlocks(blockState) && EventUtils.simulateBlockBreak(block, player, false)) {
|
||||
blockState.update(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* SHROOM THUMB CHECK */
|
||||
else if (herbalismManager.canUseShroomThumb(blockState)) {
|
||||
Bukkit.getPluginManager().callEvent(fakeSwing);
|
||||
event.setCancelled(true);
|
||||
if (herbalismManager.processShroomThumb(blockState) && EventUtils.simulateBlockBreak(block, player, false)) {
|
||||
blockState.update(true);
|
||||
@@ -812,13 +839,13 @@ public class PlayerListener implements Listener {
|
||||
Material type = heldItem.getType();
|
||||
TamingManager tamingManager = mcMMOPlayer.getTamingManager();
|
||||
|
||||
if (type == Config.getInstance().getTamingCOTWMaterial(EntityType.WOLF)) {
|
||||
if (type == Config.getInstance().getTamingCOTWMaterial(CallOfTheWildType.WOLF.getConfigEntityTypeEntry())) {
|
||||
tamingManager.summonWolf();
|
||||
}
|
||||
else if (type == Config.getInstance().getTamingCOTWMaterial(EntityType.OCELOT)) {
|
||||
else if (type == Config.getInstance().getTamingCOTWMaterial(CallOfTheWildType.CAT.getConfigEntityTypeEntry())) {
|
||||
tamingManager.summonOcelot();
|
||||
}
|
||||
else if (type == Config.getInstance().getTamingCOTWMaterial(EntityType.HORSE)) {
|
||||
else if (type == Config.getInstance().getTamingCOTWMaterial(CallOfTheWildType.HORSE.getConfigEntityTypeEntry())) {
|
||||
tamingManager.summonHorse();
|
||||
}
|
||||
|
||||
@@ -838,7 +865,7 @@ public class PlayerListener implements Listener {
|
||||
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
if (Misc.isNPCEntity(player) || !UserManager.hasPlayerDataKey(player)) {
|
||||
if (Misc.isNPCEntityExcludingVillagers(player) || !UserManager.hasPlayerDataKey(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -884,12 +911,12 @@ public class PlayerListener implements Listener {
|
||||
if (!Config.getInstance().getLocale().equalsIgnoreCase("en_US")) {
|
||||
String message = event.getMessage();
|
||||
String command = message.substring(1).split(" ")[0];
|
||||
String lowerCaseCommand = command.toLowerCase();
|
||||
String lowerCaseCommand = command.toLowerCase(Locale.ENGLISH);
|
||||
|
||||
// Do these ACTUALLY have to be lower case to work properly?
|
||||
for (PrimarySkillType skill : PrimarySkillType.values()) {
|
||||
String skillName = skill.toString().toLowerCase();
|
||||
String localizedName = skill.getName().toLowerCase();
|
||||
String skillName = skill.toString().toLowerCase(Locale.ENGLISH);
|
||||
String localizedName = skill.getName().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
if (lowerCaseCommand.equals(localizedName)) {
|
||||
event.setMessage(message.replace(command, skillName));
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent;
|
||||
import com.gmail.nossr50.events.experience.McMMOPlayerXpGainEvent;
|
||||
import com.gmail.nossr50.events.skills.abilities.McMMOPlayerAbilityActivateEvent;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.player.PlayerLevelUtils;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
@@ -74,6 +75,11 @@ public class SelfListener implements Listener {
|
||||
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
||||
PrimarySkillType primarySkillType = event.getSkill();
|
||||
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage(event.getSkill().toString() + " XP Gained");
|
||||
mcMMOPlayer.getPlayer().sendMessage("Incoming Raw XP: "+event.getRawXpGained());
|
||||
}
|
||||
|
||||
//WorldGuard XP Check
|
||||
if(event.getXpGainReason() == XPGainReason.PVE ||
|
||||
event.getXpGainReason() == XPGainReason.PVP ||
|
||||
@@ -86,6 +92,10 @@ public class SelfListener implements Listener {
|
||||
{
|
||||
event.setRawXpGained(0);
|
||||
event.setCancelled(true);
|
||||
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("No WG XP Flag - New Raw XP: "+event.getRawXpGained());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,15 +105,30 @@ public class SelfListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ExperienceConfig.getInstance().isEarlyGameBoostEnabled())
|
||||
{
|
||||
|
||||
int earlyGameBonusXP = 0;
|
||||
|
||||
//Give some bonus XP for low levels
|
||||
if(PlayerLevelUtils.qualifiesForEarlyGameBoost(mcMMOPlayer, primarySkillType))
|
||||
{
|
||||
earlyGameBonusXP += (mcMMOPlayer.getXpToLevel(primarySkillType) * 0.05);
|
||||
event.setRawXpGained(event.getRawXpGained() + earlyGameBonusXP);
|
||||
}
|
||||
}
|
||||
|
||||
int threshold = ExperienceConfig.getInstance().getDiminishedReturnsThreshold(primarySkillType);
|
||||
|
||||
if (threshold <= 0 || !ExperienceConfig.getInstance().getDiminishedReturnsEnabled()) {
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Final Raw XP: "+event.getRawXpGained());
|
||||
}
|
||||
// Diminished returns is turned off
|
||||
return;
|
||||
}
|
||||
|
||||
final float rawXp = event.getRawXpGained();
|
||||
if (rawXp < 0) {
|
||||
if (event.getRawXpGained() <= 0) {
|
||||
// Don't calculate for XP subtraction
|
||||
return;
|
||||
}
|
||||
@@ -112,6 +137,8 @@ public class SelfListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
final float rawXp = event.getRawXpGained();
|
||||
|
||||
float guaranteedMinimum = ExperienceConfig.getInstance().getDiminishedReturnsCap() * rawXp;
|
||||
|
||||
float modifiedThreshold = (float) (threshold / primarySkillType.getXpModifier() * ExperienceConfig.getInstance().getExperienceGainsGlobalMultiplier());
|
||||
@@ -141,5 +168,11 @@ public class SelfListener implements Listener {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Final Raw XP: "+event.getRawXpGained());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,14 +4,20 @@ import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class LocaleLoader {
|
||||
private static final String BUNDLE_ROOT = "com.gmail.nossr50.locale.locale";
|
||||
private static Map<String, String> bundleCache = new HashMap<>();
|
||||
private static ResourceBundle bundle = null;
|
||||
private static ResourceBundle filesystemBundle = null;
|
||||
private static ResourceBundle enBundle = null;
|
||||
|
||||
private LocaleLoader() {};
|
||||
@@ -32,25 +38,44 @@ public final class LocaleLoader {
|
||||
initialize();
|
||||
}
|
||||
|
||||
try {
|
||||
return getString(key, bundle, messageArguments);
|
||||
}
|
||||
catch (MissingResourceException ex) {
|
||||
try {
|
||||
return getString(key, enBundle, messageArguments);
|
||||
}
|
||||
catch (MissingResourceException ex2) {
|
||||
if (!key.contains("Guides")) {
|
||||
mcMMO.p.getLogger().warning("Could not find locale string: " + key);
|
||||
}
|
||||
|
||||
return '!' + key + '!';
|
||||
}
|
||||
}
|
||||
String rawMessage = bundleCache.computeIfAbsent(key, LocaleLoader::getRawString);
|
||||
return formatString(rawMessage, messageArguments);
|
||||
}
|
||||
|
||||
private static String getString(String key, ResourceBundle bundle, Object... messageArguments) throws MissingResourceException {
|
||||
return formatString(bundle.getString(key), messageArguments);
|
||||
/**
|
||||
* Reloads locale
|
||||
*/
|
||||
public static void reloadLocale() {
|
||||
bundle = null;
|
||||
filesystemBundle = null;
|
||||
enBundle = null;
|
||||
bundleCache = new HashMap<>(); // Cheaper to replace than clear()
|
||||
initialize();
|
||||
}
|
||||
|
||||
private static String getRawString(String key) {
|
||||
if (filesystemBundle != null) {
|
||||
try {
|
||||
return filesystemBundle.getString(key);
|
||||
}
|
||||
catch (MissingResourceException ignored) {}
|
||||
}
|
||||
|
||||
try {
|
||||
return bundle.getString(key);
|
||||
}
|
||||
catch (MissingResourceException ignored) {}
|
||||
|
||||
try {
|
||||
return enBundle.getString(key);
|
||||
}
|
||||
catch (MissingResourceException ignored) {
|
||||
if (!key.contains("Guides")) {
|
||||
mcMMO.p.getLogger().warning("Could not find locale string: " + key);
|
||||
}
|
||||
|
||||
return '!' + key + '!';
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatString(String string, Object... messageArguments) {
|
||||
@@ -85,6 +110,19 @@ public final class LocaleLoader {
|
||||
locale = new Locale(myLocale[0], myLocale[1]);
|
||||
}
|
||||
|
||||
if (locale == null) {
|
||||
throw new IllegalStateException("Failed to parse locale string '" + Config.getInstance().getLocale() + "'");
|
||||
}
|
||||
|
||||
Path localePath = Paths.get(mcMMO.getLocalesDirectory() + "locale_" + locale.toString() + ".properties");
|
||||
if (Files.exists(localePath) && Files.isRegularFile(localePath)) {
|
||||
try (Reader localeReader = Files.newBufferedReader(localePath)) {
|
||||
mcMMO.p.getLogger().log(Level.INFO, "Loading locale from {0}", localePath);
|
||||
filesystemBundle = new PropertyResourceBundle(localeReader);
|
||||
} catch (IOException e) {
|
||||
mcMMO.p.getLogger().log(Level.WARNING, "Failed to load locale from " + localePath, e);
|
||||
}
|
||||
}
|
||||
bundle = ResourceBundle.getBundle(BUNDLE_ROOT, locale);
|
||||
enBundle = ResourceBundle.getBundle(BUNDLE_ROOT, Locale.US);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManager;
|
||||
import com.gmail.nossr50.util.blockmeta.chunkmeta.ChunkManagerFactory;
|
||||
import com.gmail.nossr50.util.commands.CommandRegistrationManager;
|
||||
import com.gmail.nossr50.util.experience.FormulaManager;
|
||||
import com.gmail.nossr50.util.player.PlayerLevelUtils;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
@@ -61,6 +62,7 @@ import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class mcMMO extends JavaPlugin {
|
||||
/* Managers */
|
||||
@@ -72,12 +74,15 @@ public class mcMMO extends JavaPlugin {
|
||||
private static FormulaManager formulaManager;
|
||||
private static HolidayManager holidayManager;
|
||||
private static UpgradeManager upgradeManager;
|
||||
private static MaterialMapStore materialMapStore;
|
||||
private static PlayerLevelUtils playerLevelUtils;
|
||||
|
||||
/* Blacklist */
|
||||
private static WorldBlacklist worldBlacklist;
|
||||
|
||||
/* File Paths */
|
||||
private static String mainDirectory;
|
||||
private static String localesDirectory;
|
||||
private static String flatFileDirectory;
|
||||
private static String usersFile;
|
||||
private static String modDirectory;
|
||||
@@ -102,22 +107,24 @@ public class mcMMO extends JavaPlugin {
|
||||
private static boolean isRetroModeEnabled;
|
||||
|
||||
/* Metadata Values */
|
||||
public final static String REPLANT_META_KEY = "mcMMO: Recently Replanted";
|
||||
public static final String FISH_HOOK_REF_METAKEY = "mcMMO: Fish Hook Tracker";
|
||||
public static final String DODGE_TRACKER = "mcMMO: Dodge Tracker";
|
||||
public static final String CUSTOM_DAMAGE_METAKEY = "mcMMO: Custom Damage";
|
||||
public static final String COTW_TEMPORARY_SUMMON = "mcMMO: COTW Entity";
|
||||
public final static String entityMetadataKey = "mcMMO: Spawned Entity";
|
||||
public final static String blockMetadataKey = "mcMMO: Piston Tracking";
|
||||
public final static String furnaceMetadataKey = "mcMMO: Tracked Furnace";
|
||||
public final static String tntMetadataKey = "mcMMO: Tracked TNT";
|
||||
public final static String funfettiMetadataKey = "mcMMO: Funfetti";
|
||||
public final static String tntsafeMetadataKey = "mcMMO: Safe TNT";
|
||||
public final static String customNameKey = "mcMMO: Custom Name";
|
||||
public final static String customVisibleKey = "mcMMO: Name Visibility";
|
||||
public final static String droppedItemKey = "mcMMO: Tracked Item";
|
||||
public final static String infiniteArrowKey = "mcMMO: Infinite Arrow";
|
||||
public final static String trackedArrow = "mcMMO: Tracked Arrow";
|
||||
public final static String bowForceKey = "mcMMO: Bow Force";
|
||||
public final static String arrowDistanceKey = "mcMMO: Arrow Distance";
|
||||
public final static String doubleDrops = "mcMMO: Double Drops";
|
||||
public final static String tripleDrops = "mcMMO: Triple Drops";
|
||||
public final static String BONUS_DROPS_METAKEY = "mcMMO: Double Drops";
|
||||
//public final static String customDamageKey = "mcMMO: Custom Damage";
|
||||
public final static String disarmedItemKey = "mcMMO: Disarmed Item";
|
||||
public final static String playerDataKey = "mcMMO: Player Data";
|
||||
@@ -243,10 +250,24 @@ public class mcMMO extends JavaPlugin {
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
//Init Material Maps
|
||||
materialMapStore = new MaterialMapStore();
|
||||
|
||||
//Init player level values
|
||||
playerLevelUtils = new PlayerLevelUtils();
|
||||
|
||||
//Init the blacklist
|
||||
worldBlacklist = new WorldBlacklist(this);
|
||||
}
|
||||
|
||||
public static PlayerLevelUtils getPlayerLevelUtils() {
|
||||
return playerLevelUtils;
|
||||
}
|
||||
|
||||
public static MaterialMapStore getMaterialMapStore() {
|
||||
return materialMapStore;
|
||||
}
|
||||
|
||||
private void checkForOutdatedAPI() {
|
||||
try {
|
||||
Class<?> checkForClass = Class.forName("org.bukkit.event.block.BlockDropItemEvent");
|
||||
@@ -267,9 +288,9 @@ public class mcMMO extends JavaPlugin {
|
||||
|
||||
private ServerSoftwareType getServerSoftware()
|
||||
{
|
||||
if(Bukkit.getVersion().toLowerCase().contains("paper"))
|
||||
if(Bukkit.getVersion().toLowerCase(Locale.ENGLISH).contains("paper"))
|
||||
return ServerSoftwareType.PAPER;
|
||||
else if(Bukkit.getVersion().toLowerCase().contains("spigot"))
|
||||
else if(Bukkit.getVersion().toLowerCase(Locale.ENGLISH).contains("spigot"))
|
||||
return ServerSoftwareType.SPIGOT;
|
||||
else
|
||||
return ServerSoftwareType.CRAFTBUKKIT;
|
||||
@@ -291,8 +312,9 @@ public class mcMMO extends JavaPlugin {
|
||||
@Override
|
||||
public void onLoad()
|
||||
{
|
||||
if(getServer().getPluginManager().getPlugin("WorldGuard") != null)
|
||||
if(getServer().getPluginManager().getPlugin("WorldGuard") != null) {
|
||||
WorldGuardManager.getInstance().registerFlags();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,9 +323,9 @@ public class mcMMO extends JavaPlugin {
|
||||
@Override
|
||||
public void onDisable() {
|
||||
try {
|
||||
Alchemy.finishAllBrews(); // Finish all partially complete AlchemyBrewTasks to prevent vanilla brewing continuation on restart
|
||||
UserManager.saveAll(); // Make sure to save player information if the server shuts down
|
||||
UserManager.clearAll();
|
||||
Alchemy.finishAllBrews(); // Finish all partially complete AlchemyBrewTasks to prevent vanilla brewing continuation on restart
|
||||
PartyManager.saveParties(); // Save our parties
|
||||
|
||||
//TODO: Needed?
|
||||
@@ -315,7 +337,8 @@ public class mcMMO extends JavaPlugin {
|
||||
placeStore.saveAll(); // Save our metadata
|
||||
placeStore.cleanUp(); // Cleanup empty metadata stores
|
||||
}
|
||||
catch (NullPointerException e) { e.printStackTrace(); }
|
||||
|
||||
catch (Exception e) { e.printStackTrace(); }
|
||||
|
||||
debug("Canceling all tasks...");
|
||||
getServer().getScheduler().cancelTasks(this); // This removes our tasks
|
||||
@@ -349,6 +372,10 @@ public class mcMMO extends JavaPlugin {
|
||||
return mainDirectory;
|
||||
}
|
||||
|
||||
public static String getLocalesDirectory() {
|
||||
return localesDirectory;
|
||||
}
|
||||
|
||||
public static String getFlatFileDirectory() {
|
||||
return flatFileDirectory;
|
||||
}
|
||||
@@ -424,6 +451,7 @@ public class mcMMO extends JavaPlugin {
|
||||
private void setupFilePaths() {
|
||||
mcmmo = getFile();
|
||||
mainDirectory = getDataFolder().getPath() + File.separator;
|
||||
localesDirectory = mainDirectory + "locales" + File.separator;
|
||||
flatFileDirectory = mainDirectory + "flatfile" + File.separator;
|
||||
usersFile = flatFileDirectory + "mcmmo.users";
|
||||
modDirectory = mainDirectory + "mods" + File.separator;
|
||||
@@ -477,6 +505,8 @@ public class mcMMO extends JavaPlugin {
|
||||
|
||||
File currentFlatfilePath = new File(flatFileDirectory);
|
||||
currentFlatfilePath.mkdirs();
|
||||
File localesDirectoryPath = new File(localesDirectory);
|
||||
localesDirectoryPath.mkdirs();
|
||||
}
|
||||
|
||||
private void loadConfigFiles() {
|
||||
@@ -544,6 +574,8 @@ public class mcMMO extends JavaPlugin {
|
||||
* Acrobatics skills
|
||||
*/
|
||||
|
||||
InteractionManager.initMaps(); //Init maps
|
||||
|
||||
if(CoreSkillsConfig.getInstance().isPrimarySkillEnabled(PrimarySkillType.ACROBATICS))
|
||||
{
|
||||
System.out.println("[mcMMO]" + " enabling Acrobatics Skills");
|
||||
|
||||
@@ -591,45 +591,53 @@ public final class PartyManager {
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration partiesFile = YamlConfiguration.loadConfiguration(partyFile);
|
||||
try {
|
||||
YamlConfiguration partiesFile;
|
||||
partiesFile = YamlConfiguration.loadConfiguration(partyFile);
|
||||
|
||||
ArrayList<Party> hasAlly = new ArrayList<Party>();
|
||||
ArrayList<Party> hasAlly = new ArrayList<Party>();
|
||||
|
||||
for (String partyName : partiesFile.getConfigurationSection("").getKeys(false)) {
|
||||
Party party = new Party(partyName);
|
||||
for (String partyName : partiesFile.getConfigurationSection("").getKeys(false)) {
|
||||
Party party = new Party(partyName);
|
||||
|
||||
String[] leaderSplit = partiesFile.getString(partyName + ".Leader").split("[|]");
|
||||
party.setLeader(new PartyLeader(UUID.fromString(leaderSplit[0]), leaderSplit[1]));
|
||||
party.setPassword(partiesFile.getString(partyName + ".Password"));
|
||||
party.setLocked(partiesFile.getBoolean(partyName + ".Locked"));
|
||||
party.setLevel(partiesFile.getInt(partyName + ".Level"));
|
||||
party.setXp(partiesFile.getInt(partyName + ".Xp"));
|
||||
String[] leaderSplit = partiesFile.getString(partyName + ".Leader").split("[|]");
|
||||
party.setLeader(new PartyLeader(UUID.fromString(leaderSplit[0]), leaderSplit[1]));
|
||||
party.setPassword(partiesFile.getString(partyName + ".Password"));
|
||||
party.setLocked(partiesFile.getBoolean(partyName + ".Locked"));
|
||||
party.setLevel(partiesFile.getInt(partyName + ".Level"));
|
||||
party.setXp(partiesFile.getInt(partyName + ".Xp"));
|
||||
|
||||
if (partiesFile.getString(partyName + ".Ally") != null) {
|
||||
hasAlly.add(party);
|
||||
if (partiesFile.getString(partyName + ".Ally") != null) {
|
||||
hasAlly.add(party);
|
||||
}
|
||||
|
||||
party.setXpShareMode(ShareMode.getShareMode(partiesFile.getString(partyName + ".ExpShareMode", "NONE")));
|
||||
party.setItemShareMode(ShareMode.getShareMode(partiesFile.getString(partyName + ".ItemShareMode", "NONE")));
|
||||
|
||||
for (ItemShareType itemShareType : ItemShareType.values()) {
|
||||
party.setSharingDrops(itemShareType, partiesFile.getBoolean(partyName + ".ItemShareType." + itemShareType.toString(), true));
|
||||
}
|
||||
|
||||
LinkedHashMap<UUID, String> members = party.getMembers();
|
||||
|
||||
for (String memberEntry : partiesFile.getStringList(partyName + ".Members")) {
|
||||
String[] memberSplit = memberEntry.split("[|]");
|
||||
members.put(UUID.fromString(memberSplit[0]), memberSplit[1]);
|
||||
}
|
||||
|
||||
parties.add(party);
|
||||
}
|
||||
|
||||
party.setXpShareMode(ShareMode.getShareMode(partiesFile.getString(partyName + ".ExpShareMode", "NONE")));
|
||||
party.setItemShareMode(ShareMode.getShareMode(partiesFile.getString(partyName + ".ItemShareMode", "NONE")));
|
||||
mcMMO.p.debug("Loaded (" + parties.size() + ") Parties...");
|
||||
|
||||
for (ItemShareType itemShareType : ItemShareType.values()) {
|
||||
party.setSharingDrops(itemShareType, partiesFile.getBoolean(partyName + ".ItemShareType." + itemShareType.toString(), true));
|
||||
for (Party party : hasAlly) {
|
||||
party.setAlly(PartyManager.getParty(partiesFile.getString(party.getName() + ".Ally")));
|
||||
}
|
||||
|
||||
LinkedHashMap<UUID, String> members = party.getMembers();
|
||||
|
||||
for (String memberEntry : partiesFile.getStringList(partyName + ".Members")) {
|
||||
String[] memberSplit = memberEntry.split("[|]");
|
||||
members.put(UUID.fromString(memberSplit[0]), memberSplit[1]);
|
||||
}
|
||||
|
||||
parties.add(party);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mcMMO.p.debug("Loaded (" + parties.size() + ") Parties...");
|
||||
|
||||
for (Party party : hasAlly) {
|
||||
party.setAlly(PartyManager.getParty(partiesFile.getString(party.getName() + ".Ally")));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,23 +6,20 @@ import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class MobHealthDisplayUpdaterTask extends BukkitRunnable {
|
||||
private LivingEntity target;
|
||||
private String oldName;
|
||||
private boolean oldNameVisible;
|
||||
|
||||
public MobHealthDisplayUpdaterTask(LivingEntity target) {
|
||||
if (target.isValid()) {
|
||||
this.target = target;
|
||||
this.oldName = target.getMetadata(mcMMO.customNameKey).get(0).asString();
|
||||
this.oldNameVisible = target.getMetadata(mcMMO.customVisibleKey).get(0).asBoolean();
|
||||
}
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (target != null && target.isValid()) {
|
||||
target.setCustomNameVisible(oldNameVisible);
|
||||
target.setCustomName(oldName);
|
||||
if (target.hasMetadata(mcMMO.customNameKey)) {
|
||||
target.setCustomName(target.getMetadata(mcMMO.customNameKey).get(0).asString());
|
||||
target.removeMetadata(mcMMO.customNameKey, mcMMO.p);
|
||||
}
|
||||
|
||||
if (target.hasMetadata(mcMMO.customVisibleKey)) {
|
||||
target.setCustomNameVisible(target.getMetadata(mcMMO.customVisibleKey).get(0).asBoolean());
|
||||
target.removeMetadata(mcMMO.customVisibleKey, mcMMO.p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class SaveTimerTask extends BukkitRunnable {
|
||||
int count = 1;
|
||||
|
||||
for (McMMOPlayer mcMMOPlayer : UserManager.getPlayers()) {
|
||||
new PlayerProfileSaveTask(mcMMOPlayer.getProfile()).runTaskLaterAsynchronously(mcMMO.p, count);
|
||||
new PlayerProfileSaveTask(mcMMOPlayer.getProfile(), false).runTaskLaterAsynchronously(mcMMO.p, count);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,16 +41,16 @@ public class McrankCommandDisplayTask extends BukkitRunnable {
|
||||
}
|
||||
|
||||
private void displayChat() {
|
||||
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
||||
// Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
||||
Integer rank;
|
||||
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Heading"));
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName));
|
||||
|
||||
for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
|
||||
if (!skill.getPermissions(player)) {
|
||||
continue;
|
||||
}
|
||||
// if (!skill.getPermissions(player)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
rank = skills.get(skill);
|
||||
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", skill.getName(), (rank == null ? LocaleLoader.getString("Commands.mcrank.Unranked") : rank)));
|
||||
|
||||
@@ -68,15 +68,18 @@ public class UUIDUpdateAsyncTask extends BukkitRunnable {
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Handle 429
|
||||
if (e.getMessage().contains("429")) {
|
||||
size += userNamesSection.size();
|
||||
try {
|
||||
Thread.sleep(LIMIT_PERIOD);
|
||||
} catch (InterruptedException ex) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
if(e.getMessage() != null)
|
||||
{
|
||||
if (e.getMessage().contains("429")) {
|
||||
size += userNamesSection.size();
|
||||
try {
|
||||
Thread.sleep(LIMIT_PERIOD);
|
||||
} catch (InterruptedException ex) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUIDs!", e);
|
||||
|
||||
@@ -6,8 +6,10 @@ import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.party.PartyManager;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
@@ -51,6 +53,23 @@ public class TeleportationWarmup extends BukkitRunnable {
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.getInstance().getPTPCommandWorldPermissions()) {
|
||||
World targetWorld = targetPlayer.getWorld();
|
||||
World playerWorld = teleportingPlayer.getWorld();
|
||||
|
||||
if (!Permissions.partyTeleportAllWorlds(teleportingPlayer)) {
|
||||
if (!Permissions.partyTeleportWorld(targetPlayer, targetWorld)) {
|
||||
teleportingPlayer.sendMessage(LocaleLoader.getString("Commands.ptp.NoWorldPermissions", targetWorld.getName()));
|
||||
return;
|
||||
}
|
||||
else if (targetWorld != playerWorld && !Permissions.partyTeleportWorld(teleportingPlayer, targetWorld)) {
|
||||
teleportingPlayer.sendMessage(LocaleLoader.getString("Commands.ptp.NoWorldPermissions", targetWorld.getName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EventUtils.handlePartyTeleportEvent(teleportingPlayer, targetPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gmail.nossr50.datatypes.player.PlayerProfile;
|
||||
import com.gmail.nossr50.locale.LocaleLoader;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.runnables.commands.McScoreboardKeepTask;
|
||||
import com.gmail.nossr50.util.EventUtils;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.player.UserManager;
|
||||
import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
|
||||
@@ -30,6 +31,11 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
|
||||
// DO NOT MODIFY THE McMMOPLAYER FROM THIS CODE
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (Misc.isNPCIncludingVillagers(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Quit if they logged out
|
||||
if (!player.isOnline()) {
|
||||
mcMMO.p.getLogger().info("Aborting profile loading recovery for " + player.getName() + " - player logged out");
|
||||
@@ -37,9 +43,11 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
|
||||
}
|
||||
|
||||
PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(player.getName(), player.getUniqueId(), true);
|
||||
|
||||
// If successful, schedule the apply
|
||||
if (profile.isLoaded()) {
|
||||
new ApplySuccessfulProfile(new McMMOPlayer(player, profile)).runTask(mcMMO.p);
|
||||
EventUtils.callPlayerProfileLoadEvent(player, profile);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +68,7 @@ public class PlayerProfileLoadingTask extends BukkitRunnable {
|
||||
// Increment attempt counter and try
|
||||
attempt++;
|
||||
|
||||
new PlayerProfileLoadingTask(player, attempt).runTaskLaterAsynchronously(mcMMO.p, 100);
|
||||
new PlayerProfileLoadingTask(player, attempt).runTaskLaterAsynchronously(mcMMO.p, (100 + (attempt * 100)));
|
||||
}
|
||||
|
||||
private class ApplySuccessfulProfile extends BukkitRunnable {
|
||||
|
||||
@@ -5,13 +5,15 @@ import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class PlayerProfileSaveTask extends BukkitRunnable {
|
||||
private PlayerProfile playerProfile;
|
||||
private boolean isSync;
|
||||
|
||||
public PlayerProfileSaveTask(PlayerProfile playerProfile) {
|
||||
public PlayerProfileSaveTask(PlayerProfile playerProfile, boolean isSync) {
|
||||
this.playerProfile = playerProfile;
|
||||
this.isSync = isSync;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
playerProfile.save();
|
||||
playerProfile.save(isSync);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class AbilityDisableTask extends BukkitRunnable {
|
||||
|
||||
|
||||
SkillUtils.sendSkillMessage(player, NotificationType.SUPER_ABILITY_ALERT_OTHERS, ability.getAbilityPlayerOff());
|
||||
new AbilityCooldownTask(mcMMOPlayer, ability).runTaskLaterAsynchronously(mcMMO.p, PerksUtils.handleCooldownPerks(player, ability.getCooldown()) * Misc.TICK_CONVERSION_FACTOR);
|
||||
new AbilityCooldownTask(mcMMOPlayer, ability).runTaskLater(mcMMO.p, PerksUtils.handleCooldownPerks(player, ability.getCooldown()) * Misc.TICK_CONVERSION_FACTOR);
|
||||
}
|
||||
|
||||
private void resendChunkRadiusAt(Player player, int radius) {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class AlchemyBrewTask extends BukkitRunnable {
|
||||
brewTimer = DEFAULT_BREW_TICKS;
|
||||
|
||||
if (player != null
|
||||
&& !Misc.isNPCEntity(player)
|
||||
&& !Misc.isNPCEntityExcludingVillagers(player)
|
||||
&& Permissions.isSubSkillEnabled(player, SubSkillType.ALCHEMY_CATALYSIS)
|
||||
&& UserManager.getPlayer(player) != null) {
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.gmail.nossr50.util.skills.CombatUtils;
|
||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||
import com.gmail.nossr50.util.sounds.SoundManager;
|
||||
import com.gmail.nossr50.util.sounds.SoundType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -21,9 +22,11 @@ import java.util.Map.Entry;
|
||||
|
||||
public class BleedTimerTask extends BukkitRunnable {
|
||||
private static Map<LivingEntity, BleedContainer> bleedList = new HashMap<LivingEntity, BleedContainer>();
|
||||
private static boolean isIterating = false;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
isIterating = true;
|
||||
Iterator<Entry<LivingEntity, BleedContainer>> bleedIterator = bleedList.entrySet().iterator();
|
||||
|
||||
while (bleedIterator.hasNext()) {
|
||||
@@ -135,6 +138,7 @@ public class BleedTimerTask extends BukkitRunnable {
|
||||
|
||||
// Bukkit.broadcastMessage(debugMessage);
|
||||
}
|
||||
isIterating = false;
|
||||
}
|
||||
|
||||
public static BleedContainer copyContainer(BleedContainer container)
|
||||
@@ -171,6 +175,12 @@ public class BleedTimerTask extends BukkitRunnable {
|
||||
* @param ticks Number of bleeding ticks
|
||||
*/
|
||||
public static void add(LivingEntity entity, LivingEntity attacker, int ticks, int bleedRank, int toolTier) {
|
||||
if (!Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalStateException("Cannot add bleed task async!");
|
||||
}
|
||||
|
||||
if (isIterating) throw new IllegalStateException("Cannot add task while iterating timers!");
|
||||
|
||||
if(toolTier < 4)
|
||||
ticks = Math.max(1, (ticks / 3));
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.gmail.nossr50.runnables.skills;
|
||||
|
||||
import com.gmail.nossr50.datatypes.meta.RecentlyReplantedCropMeta;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.Ageable;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class DelayedCropReplant extends BukkitRunnable {
|
||||
|
||||
private final int desiredCropAge;
|
||||
private final Location cropLocation;
|
||||
private final Material cropMaterial;
|
||||
private boolean wasImmaturePlant;
|
||||
private final BlockBreakEvent blockBreakEvent;
|
||||
private BlockFace cropFace;
|
||||
|
||||
/**
|
||||
* Replants a crop after a delay setting the age to desiredCropAge
|
||||
* @param cropState target {@link BlockState}
|
||||
* @param desiredCropAge desired age of the crop
|
||||
*/
|
||||
public DelayedCropReplant(BlockBreakEvent blockBreakEvent, BlockState cropState, int desiredCropAge, boolean wasImmaturePlant) {
|
||||
BlockData cropData = cropState.getBlockData();
|
||||
|
||||
if(cropData instanceof Directional) {
|
||||
Directional cropDir = (Directional) cropData;
|
||||
cropFace = cropDir.getFacing();
|
||||
}
|
||||
|
||||
//The plant was either immature or something cancelled the event, therefor we need to treat it differently
|
||||
this.blockBreakEvent = blockBreakEvent;
|
||||
this.wasImmaturePlant = wasImmaturePlant;
|
||||
this.cropMaterial = cropState.getType();
|
||||
this.desiredCropAge = desiredCropAge;
|
||||
this.cropLocation = cropState.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Block cropBlock = cropLocation.getBlock();
|
||||
BlockState currentState = cropBlock.getState();
|
||||
|
||||
//Remove the metadata marking the block as recently replanted
|
||||
new markPlantAsOld(blockBreakEvent.getBlock().getLocation()).runTaskLater(mcMMO.p, 10);
|
||||
|
||||
if(blockBreakEvent.isCancelled()) {
|
||||
wasImmaturePlant = true;
|
||||
}
|
||||
|
||||
//Two kinds of air in Minecraft
|
||||
if(currentState.getType().equals(cropMaterial) || currentState.getType().equals(Material.AIR) || currentState.getType().equals(Material.CAVE_AIR)) {
|
||||
// if(currentState.getBlock().getRelative(BlockFace.DOWN))
|
||||
//The space is not currently occupied by a block so we can fill it
|
||||
cropBlock.setType(cropMaterial);
|
||||
|
||||
|
||||
//Get new state (necessary?)
|
||||
BlockState newState = cropBlock.getState();
|
||||
BlockData newData = newState.getBlockData();
|
||||
|
||||
int age = 0;
|
||||
|
||||
//Crop age should always be 0 if the plant was immature
|
||||
if(!wasImmaturePlant) {
|
||||
age = desiredCropAge;
|
||||
//Otherwise make the plant the desired age
|
||||
}
|
||||
|
||||
if(newData instanceof Directional) {
|
||||
//Cocoa Version
|
||||
Directional directional = (Directional) newState.getBlockData();
|
||||
directional.setFacing(cropFace);
|
||||
|
||||
newState.setBlockData(directional);
|
||||
}
|
||||
|
||||
//Age the crop
|
||||
Ageable ageable = (Ageable) newState.getBlockData();
|
||||
ageable.setAge(age);
|
||||
newState.setBlockData(ageable);
|
||||
|
||||
|
||||
newState.update(true);
|
||||
|
||||
//Play an effect
|
||||
ParticleEffectUtils.playGreenThumbEffect(cropLocation);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class markPlantAsOld extends BukkitRunnable {
|
||||
|
||||
private final Location cropLoc;
|
||||
|
||||
public markPlantAsOld(Location cropLoc) {
|
||||
this.cropLoc = cropLoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Block cropBlock = cropLoc.getBlock();
|
||||
if(cropBlock.getMetadata(mcMMO.REPLANT_META_KEY).size() > 0)
|
||||
cropBlock.setMetadata(mcMMO.REPLANT_META_KEY, new RecentlyReplantedCropMeta(mcMMO.p, false));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.gmail.nossr50.runnables.skills;
|
||||
|
||||
import com.gmail.nossr50.datatypes.BlockSnapshot;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DelayedHerbalismXPCheckTask extends BukkitRunnable {
|
||||
|
||||
private final McMMOPlayer mcMMOPlayer;
|
||||
private final ArrayList<BlockSnapshot> chorusBlocks;
|
||||
|
||||
public DelayedHerbalismXPCheckTask(McMMOPlayer mcMMOPlayer, ArrayList<BlockSnapshot> chorusBlocks) {
|
||||
this.mcMMOPlayer = mcMMOPlayer;
|
||||
this.chorusBlocks = chorusBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mcMMOPlayer.getHerbalismManager().awardXPForBlockSnapshots(chorusBlocks);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.skills.SkillManager;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
@@ -20,6 +21,8 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LightningStrike;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
public class AcrobaticsManager extends SkillManager {
|
||||
|
||||
@@ -29,7 +32,7 @@ public class AcrobaticsManager extends SkillManager {
|
||||
}
|
||||
|
||||
private long rollXPCooldown = 0;
|
||||
private long rollXPInterval = (1000 * 10); //1 Minute
|
||||
private long rollXPInterval = (1000 * 3); //1 Minute
|
||||
private long rollXPIntervalLengthen = (1000 * 10); //10 Seconds
|
||||
private LimitedSizeList fallLocationMap;
|
||||
|
||||
@@ -81,7 +84,7 @@ public class AcrobaticsManager extends SkillManager {
|
||||
* @param damage The amount of damage initially dealt by the event
|
||||
* @return the modified event damage if the ability was successful, the original event damage otherwise
|
||||
*/
|
||||
public double dodgeCheck(double damage) {
|
||||
public double dodgeCheck(Entity attacker, double damage) {
|
||||
double modifiedDamage = Acrobatics.calculateModifiedDodgeDamage(damage, Acrobatics.dodgeDamageModifier);
|
||||
Player player = getPlayer();
|
||||
|
||||
@@ -92,11 +95,26 @@ public class AcrobaticsManager extends SkillManager {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Combat.Proc");
|
||||
}
|
||||
|
||||
//Check respawn to prevent abuse
|
||||
if (SkillUtils.cooldownExpired(mcMMOPlayer.getRespawnATS(), Misc.PLAYER_RESPAWN_COOLDOWN_SECONDS)) {
|
||||
applyXpGain((float) (damage * Acrobatics.dodgeXpModifier), XPGainReason.PVP);
|
||||
if(!(attacker instanceof Player)) {
|
||||
//Check to see how many dodge XP rewards this mob has handed out
|
||||
if(attacker.hasMetadata(mcMMO.DODGE_TRACKER) && ExperienceConfig.getInstance().isAcrobaticsExploitingPrevented()) {
|
||||
//If Dodge XP has been handed out 5 times then consider it being exploited
|
||||
MetadataValue metadataValue = attacker.getMetadata(mcMMO.DODGE_TRACKER).get(0);
|
||||
int count = attacker.getMetadata(mcMMO.DODGE_TRACKER).get(0).asInt();
|
||||
|
||||
if(count <= 5) {
|
||||
applyXpGain((float) (damage * Acrobatics.dodgeXpModifier), XPGainReason.PVE);
|
||||
attacker.setMetadata(mcMMO.DODGE_TRACKER, new FixedMetadataValue(mcMMO.p, count + 1));
|
||||
}
|
||||
} else {
|
||||
applyXpGain((float) (damage * Acrobatics.dodgeXpModifier), XPGainReason.PVE);
|
||||
attacker.setMetadata(mcMMO.DODGE_TRACKER, new FixedMetadataValue(mcMMO.p, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check respawn to prevent abuse
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +66,10 @@ public class Archery {
|
||||
{
|
||||
double damageBonusPercent = getDamageBonusPercent(player);
|
||||
double newDamage = oldDamage + (oldDamage * damageBonusPercent);
|
||||
return Math.min(newDamage, Archery.skillShotMaxBonusDamage);
|
||||
return Math.min(newDamage, (oldDamage + Archery.skillShotMaxBonusDamage));
|
||||
}
|
||||
|
||||
public static double getDamageBonusPercent(Player player) {
|
||||
return ((RankUtils.getRank(player, SubSkillType.ARCHERY_SKILL_SHOT)) * AdvancedConfig.getInstance().getSkillShotRankDamageMultiplier()) / 100.0D;
|
||||
return ((RankUtils.getRank(player, SubSkillType.ARCHERY_SKILL_SHOT)) * (AdvancedConfig.getInstance().getSkillShotRankDamageMultiplier()) / 100.0D);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
@@ -52,6 +53,10 @@ public class ArcheryManager extends SkillManager {
|
||||
* @param damager The {@link Entity} who shot the arrow
|
||||
*/
|
||||
public double distanceXpBonusMultiplier(LivingEntity target, Entity damager) {
|
||||
//Hacky Fix - some plugins spawn arrows and assign them to players after the ProjectileLaunchEvent fires
|
||||
if(!damager.hasMetadata(mcMMO.arrowDistanceKey))
|
||||
return damager.getLocation().distance(target.getLocation());
|
||||
|
||||
Location firedLocation = (Location) damager.getMetadata(mcMMO.arrowDistanceKey).get(0).value();
|
||||
Location targetLocation = target.getLocation();
|
||||
|
||||
@@ -67,9 +72,10 @@ public class ArcheryManager extends SkillManager {
|
||||
*
|
||||
* @param target The {@link LivingEntity} damaged by the arrow
|
||||
*/
|
||||
public void retrieveArrows(LivingEntity target) {
|
||||
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.ARCHERY_ARROW_RETRIEVAL, getPlayer())) {
|
||||
public void retrieveArrows(LivingEntity target, Projectile projectile) {
|
||||
if(projectile.hasMetadata(mcMMO.trackedArrow)) {
|
||||
Archery.incrementTrackerValue(target);
|
||||
projectile.removeMetadata(mcMMO.trackedArrow, mcMMO.p); //Only 1 entity per projectile
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ public class Axes {
|
||||
public static double criticalHitPVPModifier = AdvancedConfig.getInstance().getCriticalStrikesPVPModifier();
|
||||
public static double criticalHitPVEModifier = AdvancedConfig.getInstance().getCriticalStrikesPVEModifier();
|
||||
|
||||
public static int impactIncreaseLevel = AdvancedConfig.getInstance().getArmorImpactIncreaseLevel();
|
||||
public static double impactChance = AdvancedConfig.getInstance().getImpactChance();
|
||||
public static double impactMaxDurabilityModifier = AdvancedConfig.getInstance().getArmorImpactMaxDurabilityDamage() / 100D;
|
||||
|
||||
public static double greaterImpactBonusDamage = AdvancedConfig.getInstance().getGreaterImpactBonusDamage();
|
||||
public static double greaterImpactChance = AdvancedConfig.getInstance().getGreaterImpactChance();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gmail.nossr50.skills.axes;
|
||||
|
||||
import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
@@ -113,19 +114,19 @@ public class AxesManager extends SkillManager {
|
||||
* @param target The {@link LivingEntity} being affected by Impact
|
||||
*/
|
||||
public void impactCheck(LivingEntity target) {
|
||||
int durabilityDamage = getImpactDurabilityDamage();
|
||||
double durabilityDamage = getImpactDurabilityDamage();
|
||||
|
||||
for (ItemStack armor : target.getEquipment().getArmorContents()) {
|
||||
if (armor != null && ItemUtils.isArmor(armor)) {
|
||||
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_STATIC_CHANCE, SubSkillType.AXES_ARMOR_IMPACT, getPlayer())) {
|
||||
SkillUtils.handleDurabilityChange(armor, durabilityDamage, Axes.impactMaxDurabilityModifier);
|
||||
SkillUtils.handleDurabilityChange(armor, durabilityDamage, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getImpactDurabilityDamage() {
|
||||
return 1 + (getSkillLevel() / Axes.impactIncreaseLevel);
|
||||
public double getImpactDurabilityDamage() {
|
||||
return AdvancedConfig.getInstance().getImpactDurabilityDamageMultiplier() * RankUtils.getRank(getPlayer(), SubSkillType.AXES_ARMOR_IMPACT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gmail.nossr50.util.StringUtils;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ChildConfig extends AutoUpdateConfigLoader {
|
||||
public ChildConfig() {
|
||||
@@ -27,7 +28,7 @@ public class ChildConfig extends AutoUpdateConfigLoader {
|
||||
|
||||
for (String name : config.getStringList(StringUtils.getCapitalized(skill.name()))) {
|
||||
try {
|
||||
PrimarySkillType parentSkill = PrimarySkillType.valueOf(name.toUpperCase());
|
||||
PrimarySkillType parentSkill = PrimarySkillType.valueOf(name.toUpperCase(Locale.ENGLISH));
|
||||
FamilyTree.enforceNotChildSkill(parentSkill);
|
||||
parentSkills.add(parentSkill);
|
||||
}
|
||||
@@ -45,7 +46,7 @@ public class ChildConfig extends AutoUpdateConfigLoader {
|
||||
* If they're dedicated enough to have modified it, they can have the errors it may produce.
|
||||
* Alternatively, this can be used to allow child skills to be parent skills, provided there are no circular dependencies this is an advanced sort of configuration.
|
||||
*/
|
||||
parentSkills.add(PrimarySkillType.valueOf(name.toUpperCase()));
|
||||
parentSkills.add(PrimarySkillType.valueOf(name.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,12 @@ import com.gmail.nossr50.skills.SkillManager;
|
||||
import com.gmail.nossr50.util.Misc;
|
||||
import com.gmail.nossr50.util.Permissions;
|
||||
import com.gmail.nossr50.util.random.RandomChanceUtil;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.ExperienceOrb;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
@@ -40,6 +43,13 @@ public class ExcavationManager extends SkillManager {
|
||||
for (ExcavationTreasure treasure : treasures) {
|
||||
if (skillLevel >= treasure.getDropLevel()
|
||||
&& RandomChanceUtil.checkRandomChanceExecutionSuccess(getPlayer(), PrimarySkillType.EXCAVATION, treasure.getDropChance())) {
|
||||
|
||||
//Spawn Vanilla XP orbs if a dice roll succeeds
|
||||
if(RandomChanceUtil.rollDice(getArchaelogyExperienceOrbChance(), 100)) {
|
||||
ExperienceOrb experienceOrb = (ExperienceOrb) getPlayer().getWorld().spawnEntity(location, EntityType.EXPERIENCE_ORB);
|
||||
experienceOrb.setExperience(getExperienceOrbsReward());
|
||||
}
|
||||
|
||||
xp += treasure.getXp();
|
||||
Misc.dropItem(location, treasure.getDrop());
|
||||
}
|
||||
@@ -50,6 +60,18 @@ public class ExcavationManager extends SkillManager {
|
||||
applyXpGain(xp, XPGainReason.PVE);
|
||||
}
|
||||
|
||||
public int getExperienceOrbsReward() {
|
||||
return 1 * getArchaeologyRank();
|
||||
}
|
||||
|
||||
public double getArchaelogyExperienceOrbChance() {
|
||||
return getArchaeologyRank() * 2;
|
||||
}
|
||||
|
||||
public int getArchaeologyRank() {
|
||||
return RankUtils.getRank(getPlayer(), SubSkillType.EXCAVATION_ARCHAEOLOGY);
|
||||
}
|
||||
|
||||
public void printExcavationDebug(Player player, BlockState blockState)
|
||||
{
|
||||
if (Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.EXCAVATION_ARCHAEOLOGY)) {
|
||||
|
||||
@@ -44,7 +44,7 @@ import java.util.*;
|
||||
|
||||
public class FishingManager extends SkillManager {
|
||||
public static final int FISHING_ROD_CAST_CD_MILLISECONDS = 100;
|
||||
public static final int OVERFISH_LIMIT = 4;
|
||||
public static final int OVERFISH_LIMIT = 10;
|
||||
private final long FISHING_COOLDOWN_SECONDS = 1000L;
|
||||
|
||||
private long fishingRodCastTimestamp = 0L;
|
||||
@@ -139,7 +139,7 @@ public class FishingManager extends SkillManager {
|
||||
|
||||
if(fishCaughtCounter + 1 == OVERFISH_LIMIT)
|
||||
{
|
||||
getPlayer().sendMessage(LocaleLoader.getString("Fishing.LowResources"));
|
||||
getPlayer().sendMessage(LocaleLoader.getString("Fishing.LowResourcesTip", 3));
|
||||
}
|
||||
|
||||
//If the new bounding box does not intersect with the old one, then update our bounding box reference
|
||||
@@ -480,9 +480,10 @@ public class FishingManager extends SkillManager {
|
||||
treasureDrop.setDurability((short) (Misc.getRandom().nextInt(maxDurability)));
|
||||
}
|
||||
|
||||
if (treasureDrop.getAmount() > 1) {
|
||||
//TODO: Add option to randomize the amount rewarded
|
||||
/*if (treasureDrop.getAmount() > 1) {
|
||||
treasureDrop.setAmount(Misc.getRandom().nextInt(treasureDrop.getAmount()) + 1);
|
||||
}
|
||||
}*/
|
||||
|
||||
treasure.setDrop(treasureDrop);
|
||||
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
package com.gmail.nossr50.skills.herbalism;
|
||||
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.util.BlockUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class Herbalism {
|
||||
|
||||
/**
|
||||
@@ -43,129 +36,6 @@ public class Herbalism {
|
||||
}
|
||||
}
|
||||
|
||||
private static int calculateChorusPlantDrops(Block target, boolean triple, HerbalismManager herbalismManager) {
|
||||
return calculateChorusPlantDropsRecursive(target, new HashSet<>(), triple, herbalismManager);
|
||||
}
|
||||
|
||||
private static int calculateChorusPlantDropsRecursive(Block target, HashSet<Block> traversed, boolean triple, HerbalismManager herbalismManager) {
|
||||
if (target.getType() != Material.CHORUS_PLANT)
|
||||
return 0;
|
||||
|
||||
// Prevent any infinite loops, who needs more than 64 chorus anyways
|
||||
if (traversed.size() > 64)
|
||||
return 0;
|
||||
|
||||
if (!traversed.add(target))
|
||||
return 0;
|
||||
|
||||
int dropAmount = 0;
|
||||
|
||||
if (mcMMO.getPlaceStore().isTrue(target))
|
||||
mcMMO.getPlaceStore().setFalse(target);
|
||||
else
|
||||
{
|
||||
dropAmount++;
|
||||
|
||||
if(herbalismManager.checkDoubleDrop(target.getState()))
|
||||
BlockUtils.markDropsAsBonus(target.getState(), triple);
|
||||
}
|
||||
|
||||
for (BlockFace blockFace : new BlockFace[] { BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST ,BlockFace.WEST})
|
||||
dropAmount += calculateChorusPlantDropsRecursive(target.getRelative(blockFace, 1), traversed, triple, herbalismManager);
|
||||
|
||||
return dropAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the drop amounts for multi block plants based on the blocks
|
||||
* relative to them.
|
||||
*
|
||||
* @param blockState
|
||||
* The {@link BlockState} of the bottom block of the plant
|
||||
* @return the number of bonus drops to award from the blocks in this plant
|
||||
*/
|
||||
protected static int countAndMarkDoubleDropsMultiBlockPlant(BlockState blockState, boolean triple, HerbalismManager herbalismManager) {
|
||||
Block block = blockState.getBlock();
|
||||
Material blockType = blockState.getType();
|
||||
int dropAmount = mcMMO.getPlaceStore().isTrue(block) ? 0 : 1;
|
||||
|
||||
if (blockType == Material.CHORUS_PLANT) {
|
||||
dropAmount = 1;
|
||||
|
||||
if (block.getRelative(BlockFace.DOWN, 1).getType() == Material.END_STONE) {
|
||||
dropAmount = calculateChorusPlantDrops(block, triple, herbalismManager);
|
||||
}
|
||||
} else {
|
||||
// Handle the two blocks above it - cacti & sugar cane can only grow 3 high naturally
|
||||
for (int y = 1; y < 255; y++) {
|
||||
Block relativeBlock = block.getRelative(BlockFace.UP, y);
|
||||
|
||||
if (relativeBlock.getType() != blockType) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mcMMO.getPlaceStore().isTrue(relativeBlock)) {
|
||||
mcMMO.getPlaceStore().setFalse(relativeBlock);
|
||||
} else {
|
||||
dropAmount++;
|
||||
|
||||
if(herbalismManager.checkDoubleDrop(relativeBlock.getState()))
|
||||
BlockUtils.markDropsAsBonus(relativeBlock.getState(), triple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dropAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the drop amounts for kelp plants based on the blocks
|
||||
* relative to them.
|
||||
*
|
||||
* @param blockState
|
||||
* The {@link BlockState} of the bottom block of the plant
|
||||
* @return the number of bonus drops to award from the blocks in this plant
|
||||
*/
|
||||
protected static int countAndMarkDoubleDropsKelp(BlockState blockState, boolean triple, HerbalismManager herbalismManager) {
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
int kelpMaxHeight = 255;
|
||||
int amount = 1;
|
||||
|
||||
// Handle the two blocks above it - cacti & sugar cane can only grow 3 high naturally
|
||||
for (int y = 1; y < kelpMaxHeight; y++) {
|
||||
Block relativeUpBlock = block.getRelative(BlockFace.UP, y);
|
||||
|
||||
if(!isKelp(relativeUpBlock))
|
||||
break;
|
||||
|
||||
amount += 1;
|
||||
|
||||
if(herbalismManager.checkDoubleDrop(relativeUpBlock.getState()))
|
||||
BlockUtils.markDropsAsBonus(relativeUpBlock.getState(), triple);
|
||||
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
private static int addKelpDrops(int dropAmount, Block relativeBlock) {
|
||||
if (isKelp(relativeBlock) && !mcMMO.getPlaceStore().isTrue(relativeBlock)) {
|
||||
dropAmount++;
|
||||
} else {
|
||||
mcMMO.getPlaceStore().setFalse(relativeBlock);
|
||||
}
|
||||
|
||||
return dropAmount;
|
||||
}
|
||||
|
||||
private static boolean isKelp(Block relativeBlock) {
|
||||
Material kelptype_1 = Material.KELP_PLANT;
|
||||
Material kelptype_2 = Material.KELP;
|
||||
|
||||
return relativeBlock.getType() == kelptype_1 || relativeBlock.getType() == kelptype_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert blocks affected by the Green Thumb & Green Terra abilities.
|
||||
*
|
||||
@@ -186,14 +56,4 @@ public class Herbalism {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the block has a recently grown crop from Green Thumb
|
||||
*
|
||||
* @param blockState
|
||||
* The {@link BlockState} to check green thumb regrown for
|
||||
* @return true if the block is recently regrown, false otherwise
|
||||
*/
|
||||
public static boolean isRecentlyRegrown(BlockState blockState) {
|
||||
return blockState.hasMetadata(mcMMO.greenThumbDataKey) && !SkillUtils.cooldownExpired(blockState.getMetadata(mcMMO.greenThumbDataKey).get(0).asInt(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.gmail.nossr50.skills.herbalism;
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.config.treasure.TreasureConfig;
|
||||
import com.gmail.nossr50.datatypes.BlockSnapshot;
|
||||
import com.gmail.nossr50.datatypes.experience.XPGainReason;
|
||||
import com.gmail.nossr50.datatypes.experience.XPGainSource;
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.mods.CustomBlock;
|
||||
import com.gmail.nossr50.datatypes.meta.RecentlyReplantedCropMeta;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
import com.gmail.nossr50.datatypes.skills.SubSkillType;
|
||||
@@ -13,7 +15,8 @@ import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
|
||||
import com.gmail.nossr50.datatypes.skills.ToolType;
|
||||
import com.gmail.nossr50.datatypes.treasure.HylianTreasure;
|
||||
import com.gmail.nossr50.mcMMO;
|
||||
import com.gmail.nossr50.runnables.skills.HerbalismBlockUpdaterTask;
|
||||
import com.gmail.nossr50.runnables.skills.DelayedCropReplant;
|
||||
import com.gmail.nossr50.runnables.skills.DelayedHerbalismXPCheckTask;
|
||||
import com.gmail.nossr50.skills.SkillManager;
|
||||
import com.gmail.nossr50.util.*;
|
||||
import com.gmail.nossr50.util.player.NotificationManager;
|
||||
@@ -22,15 +25,23 @@ import com.gmail.nossr50.util.random.RandomChanceUtil;
|
||||
import com.gmail.nossr50.util.skills.RankUtils;
|
||||
import com.gmail.nossr50.util.skills.SkillActivationType;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import com.gmail.nossr50.util.sounds.SoundManager;
|
||||
import com.gmail.nossr50.util.sounds.SoundType;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.Ageable;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class HerbalismManager extends SkillManager {
|
||||
@@ -38,10 +49,6 @@ public class HerbalismManager extends SkillManager {
|
||||
super(mcMMOPlayer, PrimarySkillType.HERBALISM);
|
||||
}
|
||||
|
||||
public boolean canBlockCheck() {
|
||||
return !(Config.getInstance().getHerbalismPreventAFK() && getPlayer().isInsideVehicle());
|
||||
}
|
||||
|
||||
public boolean canGreenThumbBlock(BlockState blockState) {
|
||||
if(!RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.HERBALISM_GREEN_THUMB))
|
||||
return false;
|
||||
@@ -78,7 +85,7 @@ public class HerbalismManager extends SkillManager {
|
||||
return mcMMOPlayer.getToolPreparationMode(ToolType.HOE) && Permissions.greenTerra(getPlayer());
|
||||
}
|
||||
|
||||
public boolean canGreenTerraPlant() {
|
||||
public boolean isGreenTerraActive() {
|
||||
return mcMMOPlayer.getAbilityMode(SuperAbilityType.GREEN_TERRA);
|
||||
}
|
||||
|
||||
@@ -98,7 +105,7 @@ public class HerbalismManager extends SkillManager {
|
||||
* @param blockState The {@link BlockState} to check ability activation for
|
||||
* @return true if the ability was successful, false otherwise
|
||||
*/
|
||||
public boolean processGreenTerra(BlockState blockState) {
|
||||
public boolean processGreenTerraBlockConversion(BlockState blockState) {
|
||||
Player player = getPlayer();
|
||||
|
||||
if (!Permissions.greenThumbBlock(player, blockState.getType())) {
|
||||
@@ -120,65 +127,447 @@ public class HerbalismManager extends SkillManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blockState The {@link BlockState} to check ability activation for
|
||||
* Handles herbalism abilities and XP rewards from a BlockBreakEvent
|
||||
* @param blockBreakEvent The Block Break Event to process
|
||||
*/
|
||||
public void herbalismBlockCheck(BlockState blockState) {
|
||||
public void processHerbalismBlockBreakEvent(BlockBreakEvent blockBreakEvent) {
|
||||
Player player = getPlayer();
|
||||
Material material = blockState.getType();
|
||||
boolean oneBlockPlant = isOneBlockPlant(material);
|
||||
|
||||
// Prevents placing and immediately breaking blocks for exp
|
||||
if (oneBlockPlant && mcMMO.getPlaceStore().isTrue(blockState)) {
|
||||
if (Config.getInstance().getHerbalismPreventAFK() && player.isInsideVehicle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canBlockCheck()) {
|
||||
return;
|
||||
}
|
||||
//Check if the plant was recently replanted
|
||||
if(blockBreakEvent.getBlock().getBlockData() instanceof Ageable) {
|
||||
Ageable ageableCrop = (Ageable) blockBreakEvent.getBlock().getBlockData();
|
||||
|
||||
int amount;
|
||||
int xp;
|
||||
boolean greenTerra = mcMMOPlayer.getAbilityMode(skill.getAbility());
|
||||
|
||||
if (mcMMO.getModManager().isCustomHerbalismBlock(blockState)) {
|
||||
CustomBlock customBlock = mcMMO.getModManager().getBlock(blockState);
|
||||
xp = customBlock.getXpGain();
|
||||
|
||||
if (Permissions.isSubSkillEnabled(player, SubSkillType.HERBALISM_DOUBLE_DROPS) && customBlock.isDoubleDropEnabled()) {
|
||||
if(checkDoubleDrop(blockState))
|
||||
BlockUtils.markDropsAsBonus(blockState, greenTerra);
|
||||
}
|
||||
}
|
||||
else {
|
||||
xp = ExperienceConfig.getInstance().getXp(skill, blockState.getBlockData());
|
||||
|
||||
if (!oneBlockPlant) {
|
||||
//Kelp is actually two blocks mixed together
|
||||
if(material == Material.KELP_PLANT || material == Material.KELP) {
|
||||
amount = Herbalism.countAndMarkDoubleDropsKelp(blockState, greenTerra,this);
|
||||
} else {
|
||||
amount = Herbalism.countAndMarkDoubleDropsMultiBlockPlant(blockState, greenTerra, this);
|
||||
if(blockBreakEvent.getBlock().getMetadata(mcMMO.REPLANT_META_KEY).size() >= 1) {
|
||||
if(blockBreakEvent.getBlock().getMetadata(mcMMO.REPLANT_META_KEY).get(0).asBoolean()) {
|
||||
if(isAgeableMature(ageableCrop)) {
|
||||
blockBreakEvent.getBlock().removeMetadata(mcMMO.REPLANT_META_KEY, mcMMO.p);
|
||||
} else {
|
||||
//Crop is recently replanted to back out of destroying it
|
||||
blockBreakEvent.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xp *= amount;
|
||||
} else {
|
||||
/* MARK SINGLE BLOCK CROP FOR DOUBLE DROP */
|
||||
if(checkDoubleDrop(blockState))
|
||||
BlockUtils.markDropsAsBonus(blockState, greenTerra);
|
||||
}
|
||||
|
||||
if (Permissions.greenThumbPlant(player, material)) {
|
||||
processGreenThumbPlants(blockState, greenTerra);
|
||||
}
|
||||
}
|
||||
|
||||
applyXpGain(xp, XPGainReason.PVE);
|
||||
/*
|
||||
* There are single-block plants and multi-block plants in Minecraft
|
||||
* In order to give out proper rewards, we need to collect all blocks that would be broken from this event
|
||||
*/
|
||||
|
||||
//Grab all broken blocks
|
||||
HashSet<Block> brokenBlocks = getBrokenHerbalismBlocks(blockBreakEvent);
|
||||
|
||||
if(brokenBlocks.size() == 0)
|
||||
return;
|
||||
|
||||
//Handle rewards, xp, ability interactions, etc
|
||||
processHerbalismOnBlocksBroken(blockBreakEvent, brokenBlocks);
|
||||
}
|
||||
|
||||
public boolean isOneBlockPlant(Material material) {
|
||||
return !(material == Material.CACTUS || material == Material.CHORUS_PLANT
|
||||
|| material == Material.SUGAR_CANE || material == Material.KELP_PLANT || material == Material.KELP
|
||||
|| material == Material.TALL_SEAGRASS || material == Material.TALL_GRASS);
|
||||
/**
|
||||
* Process rewards for a set of plant blocks for Herbalism
|
||||
* @param blockBreakEvent the block break event
|
||||
* @param brokenPlants plant blocks to process
|
||||
*/
|
||||
private void processHerbalismOnBlocksBroken(BlockBreakEvent blockBreakEvent, HashSet<Block> brokenPlants) {
|
||||
BlockState originalBreak = blockBreakEvent.getBlock().getState();
|
||||
boolean greenThumbActivated = false;
|
||||
|
||||
//TODO: The design of Green Terra needs to change, this is a mess
|
||||
if(Permissions.greenThumbPlant(getPlayer(), originalBreak.getType())) {
|
||||
if(!getPlayer().isSneaking()) {
|
||||
greenThumbActivated = processGreenThumbPlants(originalBreak, blockBreakEvent, isGreenTerraActive());
|
||||
}
|
||||
}
|
||||
|
||||
//When replanting a immature crop we cancel the block break event and back out
|
||||
if(greenThumbActivated) {
|
||||
if(originalBreak.getBlock().getBlockData() instanceof Ageable) {
|
||||
Ageable ageableCrop = (Ageable) originalBreak.getBlock().getBlockData();
|
||||
if(!isAgeableMature(ageableCrop)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark blocks for double drops
|
||||
* Be aware of the hacky interactions we are doing with Chorus Plants
|
||||
*/
|
||||
checkDoubleDropsOnBrokenPlants(blockBreakEvent.getPlayer(), brokenPlants);
|
||||
|
||||
//It would take an expensive algorithm to predict which parts of a Chorus Tree will break as a result of root break
|
||||
//So this hacky method is used instead
|
||||
ArrayList<BlockSnapshot> delayedChorusBlocks = new ArrayList<>(); //Blocks that will be checked in future ticks
|
||||
HashSet<Block> noDelayPlantBlocks = new HashSet<>(); //Blocks that will be checked immediately
|
||||
|
||||
for(Block brokenPlant : brokenPlants) {
|
||||
/*
|
||||
* This check is to make XP bars appear to work properly with Chorus Trees by giving XP for the originalBreak immediately instead of later
|
||||
*/
|
||||
if(brokenPlant.getLocation().equals(originalBreak.getBlock().getLocation())) {
|
||||
//If its the same block as the original, we are going to directly check it for being a valid XP gain and add it to the nonChorusBlocks list even if its a chorus block
|
||||
//This stops a delay from happening when bringing up the XP bar for chorus trees
|
||||
if(!mcMMO.getPlaceStore().isTrue(originalBreak)) {
|
||||
//Even if its a chorus block, the original break will be moved to nonChorusBlocks for immediate XP rewards
|
||||
noDelayPlantBlocks.add(brokenPlant);
|
||||
} else {
|
||||
if(isChorusTree(brokenPlant.getType())) {
|
||||
//If its a chorus tree AND it was marked as true in the placestore then we add this block to the list of chorus blocks
|
||||
delayedChorusBlocks.add(new BlockSnapshot(brokenPlant.getType(), brokenPlant));
|
||||
} else {
|
||||
noDelayPlantBlocks.add(brokenPlant); //If its not a chorus plant that was marked as unnatural but it was marked unnatural, put it in the nodelay list to be handled
|
||||
}
|
||||
}
|
||||
} else if(isChorusTree(brokenPlant.getType())) {
|
||||
//Chorus Blocks get checked for XP several ticks later to avoid expensive calculations
|
||||
delayedChorusBlocks.add(new BlockSnapshot(brokenPlant.getType(), brokenPlant));
|
||||
} else {
|
||||
noDelayPlantBlocks.add(brokenPlant);
|
||||
}
|
||||
}
|
||||
|
||||
//Give out XP to the non-chorus blocks
|
||||
if(noDelayPlantBlocks.size() > 0) {
|
||||
//Note: Will contain 1 chorus block if the original block was a chorus block, this is to prevent delays for the XP bar
|
||||
awardXPForPlantBlocks(noDelayPlantBlocks);
|
||||
}
|
||||
|
||||
if(delayedChorusBlocks.size() > 0) {
|
||||
//Check XP for chorus blocks
|
||||
DelayedHerbalismXPCheckTask delayedHerbalismXPCheckTask = new DelayedHerbalismXPCheckTask(mcMMOPlayer, delayedChorusBlocks);
|
||||
|
||||
//Large delay because the tree takes a while to break
|
||||
delayedHerbalismXPCheckTask.runTaskLater(mcMMO.p, 20); //Calculate Chorus XP + Bonus Drops 1 tick later
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for double drops on a collection of broken blocks
|
||||
* If a double drop has occurred, it will be marked here for bonus drops
|
||||
* @param player player who broke the blocks
|
||||
* @param brokenPlants the collection of broken plants
|
||||
*/
|
||||
public void checkDoubleDropsOnBrokenPlants(Player player, Collection<Block> brokenPlants) {
|
||||
|
||||
//Only proceed if skill unlocked and permission enabled
|
||||
if (!RankUtils.hasUnlockedSubskill(player, SubSkillType.HERBALISM_DOUBLE_DROPS)
|
||||
|| !Permissions.isSubSkillEnabled(player, SubSkillType.HERBALISM_DOUBLE_DROPS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(Block brokenPlant : brokenPlants) {
|
||||
BlockState brokenPlantState = brokenPlant.getState();
|
||||
BlockData plantData = brokenPlantState.getBlockData();
|
||||
|
||||
//Check for double drops
|
||||
if(!mcMMO.getPlaceStore().isTrue(brokenPlant)) {
|
||||
|
||||
/*
|
||||
*
|
||||
* Natural Blocks
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
//Not all things that are natural should give double drops, make sure its fully mature as well
|
||||
if(plantData instanceof Ageable) {
|
||||
Ageable ageable = (Ageable) plantData;
|
||||
|
||||
if(isAgeableMature(ageable) || isBizarreAgeable(plantData)) {
|
||||
if(checkDoubleDrop(brokenPlantState)) {
|
||||
markForBonusDrops(brokenPlantState);
|
||||
}
|
||||
}
|
||||
} else if(checkDoubleDrop(brokenPlantState)) {
|
||||
//Add metadata to mark this block for double or triple drops
|
||||
markForBonusDrops(brokenPlantState);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
*
|
||||
* Unnatural Blocks
|
||||
*
|
||||
*/
|
||||
|
||||
//If its a Crop we need to reward XP when its fully grown
|
||||
if(isAgeableAndFullyMature(plantData) && !isBizarreAgeable(plantData)) {
|
||||
//Add metadata to mark this block for double or triple drops
|
||||
markForBonusDrops(brokenPlantState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if BlockData is ageable and we can trust that age for Herbalism rewards/XP reasons
|
||||
* @param blockData target BlockData
|
||||
* @return returns true if the ageable is trustworthy for Herbalism XP / Rewards
|
||||
*/
|
||||
public boolean isBizarreAgeable(BlockData blockData) {
|
||||
if(blockData instanceof Ageable) {
|
||||
//Catcus and Sugar Canes cannot be trusted
|
||||
switch(blockData.getMaterial()) {
|
||||
case CACTUS:
|
||||
case SUGAR_CANE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void markForBonusDrops(BlockState brokenPlantState) {
|
||||
//Add metadata to mark this block for double or triple drops
|
||||
boolean awardTriple = mcMMOPlayer.getAbilityMode(SuperAbilityType.GREEN_TERRA);
|
||||
BlockUtils.markDropsAsBonus(brokenPlantState, awardTriple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block is an ageable and if that ageable is fully mature
|
||||
* @param plantData target plant
|
||||
* @return returns true if the block is both an ageable and fully mature
|
||||
*/
|
||||
public boolean isAgeableAndFullyMature(BlockData plantData) {
|
||||
return plantData instanceof Ageable && isAgeableMature((Ageable) plantData);
|
||||
}
|
||||
|
||||
public void awardXPForPlantBlocks(HashSet<Block> brokenPlants) {
|
||||
int xpToReward = 0;
|
||||
|
||||
for(Block brokenPlantBlock : brokenPlants) {
|
||||
BlockState brokenBlockNewState = brokenPlantBlock.getState();
|
||||
BlockData plantData = brokenBlockNewState.getBlockData();
|
||||
|
||||
if(mcMMO.getPlaceStore().isTrue(brokenBlockNewState)) {
|
||||
/*
|
||||
*
|
||||
* Unnatural Blocks
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
//If its a Crop we need to reward XP when its fully grown
|
||||
if(isAgeableAndFullyMature(plantData) && !isBizarreAgeable(plantData)) {
|
||||
xpToReward += ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, brokenBlockNewState.getType());
|
||||
}
|
||||
|
||||
//Mark it as natural again as it is being broken
|
||||
mcMMO.getPlaceStore().setFalse(brokenBlockNewState);
|
||||
} else {
|
||||
/*
|
||||
*
|
||||
* Natural Blocks
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
//Calculate XP
|
||||
if(plantData instanceof Ageable) {
|
||||
Ageable plantAgeable = (Ageable) plantData;
|
||||
|
||||
if(isAgeableMature(plantAgeable) || isBizarreAgeable(plantData)) {
|
||||
xpToReward += ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, brokenBlockNewState.getType());
|
||||
}
|
||||
|
||||
} else {
|
||||
xpToReward += ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, brokenPlantBlock.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Plants processed: "+brokenPlants.size());
|
||||
}
|
||||
|
||||
//Reward XP
|
||||
if(xpToReward > 0) {
|
||||
applyXpGain(xpToReward, XPGainReason.PVE, XPGainSource.SELF);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAgeableMature(Ageable ageable) {
|
||||
return ageable.getAge() == ageable.getMaximumAge()
|
||||
&& ageable.getAge() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Award XP for any blocks that used to be something else but are now AIR
|
||||
* @param brokenPlants snapshot of broken blocks
|
||||
*/
|
||||
public void awardXPForBlockSnapshots(ArrayList<BlockSnapshot> brokenPlants) {
|
||||
/*
|
||||
* This handles XP for blocks that we need to check are broken after the fact
|
||||
* This only applies to chorus trees right now
|
||||
*/
|
||||
int xpToReward = 0;
|
||||
int blocksGivingXP = 0;
|
||||
|
||||
for(BlockSnapshot blockSnapshot : brokenPlants) {
|
||||
BlockState brokenBlockNewState = blockSnapshot.getBlockRef().getState();
|
||||
|
||||
//Remove metadata from the snapshot of blocks
|
||||
if(brokenBlockNewState.hasMetadata(mcMMO.BONUS_DROPS_METAKEY)) {
|
||||
brokenBlockNewState.removeMetadata(mcMMO.BONUS_DROPS_METAKEY, mcMMO.p);
|
||||
}
|
||||
|
||||
//If the block is not AIR that means it wasn't broken
|
||||
if(brokenBlockNewState.getType() != Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mcMMO.getPlaceStore().isTrue(brokenBlockNewState)) {
|
||||
//Mark it as natural again as it is being broken
|
||||
mcMMO.getPlaceStore().setFalse(brokenBlockNewState);
|
||||
} else {
|
||||
//TODO: Do we care about chorus flower age?
|
||||
//Calculate XP for the old type
|
||||
xpToReward += ExperienceConfig.getInstance().getXp(PrimarySkillType.HERBALISM, blockSnapshot.getOldType());
|
||||
blocksGivingXP++;
|
||||
}
|
||||
}
|
||||
|
||||
if(mcMMOPlayer.isDebugMode()) {
|
||||
mcMMOPlayer.getPlayer().sendMessage("Chorus Plants checked for XP: "+brokenPlants.size());
|
||||
mcMMOPlayer.getPlayer().sendMessage("Valid Chorus Plant XP Gains: "+blocksGivingXP);
|
||||
}
|
||||
|
||||
//Reward XP
|
||||
if(xpToReward > 0) {
|
||||
applyXpGain(xpToReward, XPGainReason.PVE, XPGainSource.SELF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and return plant blocks from a BlockBreakEvent
|
||||
* @param blockBreakEvent target event
|
||||
* @return a set of plant-blocks that were broken as a result of this event
|
||||
*/
|
||||
private HashSet<Block> getBrokenHerbalismBlocks(BlockBreakEvent blockBreakEvent) {
|
||||
//Get an updated capture of this block
|
||||
BlockState originalBlockBlockState = blockBreakEvent.getBlock().getState();
|
||||
Material originalBlockMaterial = originalBlockBlockState.getType();
|
||||
HashSet<Block> blocksBroken = new HashSet<>(); //Blocks broken
|
||||
|
||||
//Check if this block is a one block plant or not
|
||||
boolean oneBlockPlant = isOneBlockPlant(originalBlockMaterial);
|
||||
|
||||
if(oneBlockPlant) {
|
||||
//If the block is a one-block plant return only that
|
||||
blocksBroken.add(originalBlockBlockState.getBlock());
|
||||
} else {
|
||||
//If the block is a multi-block structure, capture a set of all blocks broken and return that
|
||||
blocksBroken = getBrokenBlocksMultiBlockPlants(originalBlockBlockState, blockBreakEvent);
|
||||
}
|
||||
|
||||
//Return all broken plant-blocks
|
||||
return blocksBroken;
|
||||
}
|
||||
|
||||
private HashSet<Block> getBrokenChorusBlocks(BlockState originalBreak) {
|
||||
return grabChorusTreeBrokenBlocksRecursive(originalBreak.getBlock(), new HashSet<>());
|
||||
}
|
||||
|
||||
private HashSet<Block> grabChorusTreeBrokenBlocksRecursive(Block currentBlock, HashSet<Block> traversed) {
|
||||
if (!isChorusTree(currentBlock.getType()))
|
||||
return traversed;
|
||||
|
||||
// Prevent any infinite loops, who needs more than 256 chorus anyways
|
||||
if (traversed.size() > 256)
|
||||
return traversed;
|
||||
|
||||
if (!traversed.add(currentBlock))
|
||||
return traversed;
|
||||
|
||||
//Grab all Blocks in the Tree
|
||||
for (BlockFace blockFace : new BlockFace[] { BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST ,BlockFace.WEST})
|
||||
grabChorusTreeBrokenBlocksRecursive(currentBlock.getRelative(blockFace, 1), traversed);
|
||||
|
||||
traversed.add(currentBlock);
|
||||
|
||||
return traversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab a set of all plant blocks that are broken as a result of this event
|
||||
* The method to grab these blocks is a bit hacky and does not hook into the API
|
||||
* Basically we expect the blocks to be broken if this event is not cancelled and we determine which block are broken on our end rather than any event state captures
|
||||
*
|
||||
* @param blockBreakEvent target event
|
||||
* @return a set of plant-blocks broken from this event
|
||||
*/
|
||||
protected HashSet<Block> getBrokenBlocksMultiBlockPlants(BlockState originalBlockBroken, BlockBreakEvent blockBreakEvent) {
|
||||
//Track the broken blocks
|
||||
HashSet<Block> brokenBlocks;
|
||||
|
||||
if (isChorusBranch(originalBlockBroken.getType())) {
|
||||
brokenBlocks = getBrokenChorusBlocks(originalBlockBroken);
|
||||
} else {
|
||||
brokenBlocks = getBlocksBrokenAbove(originalBlockBroken);
|
||||
}
|
||||
|
||||
return brokenBlocks;
|
||||
}
|
||||
|
||||
private boolean isChorusBranch(Material blockType) {
|
||||
return blockType == Material.CHORUS_PLANT;
|
||||
}
|
||||
|
||||
private boolean isChorusTree(Material blockType) {
|
||||
return blockType == Material.CHORUS_PLANT || blockType == Material.CHORUS_FLOWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs blocks upwards from a target block
|
||||
* A lot of Plants/Crops in Herbalism only break vertically from a broken block
|
||||
* The vertical search returns early if it runs into anything that is not a multi-block plant
|
||||
* Multi-block plants are hard-coded and kept in {@link MaterialMapStore}
|
||||
*
|
||||
* @param breakPointBlockState The point of the "break"
|
||||
* @return A set of blocks above the target block which can be assumed to be broken
|
||||
*/
|
||||
private HashSet<Block> getBlocksBrokenAbove(BlockState breakPointBlockState) {
|
||||
HashSet<Block> brokenBlocks = new HashSet<>();
|
||||
Block block = breakPointBlockState.getBlock();
|
||||
|
||||
//Add the initial block to the set
|
||||
brokenBlocks.add(block);
|
||||
|
||||
//Limit our search
|
||||
int maxHeight = 255;
|
||||
|
||||
// Search vertically for multi-block plants, exit early if any non-multi block plants
|
||||
for (int y = 1; y < maxHeight; y++) {
|
||||
//TODO: Should this grab state? It would be more expensive..
|
||||
Block relativeUpBlock = block.getRelative(BlockFace.UP, y);
|
||||
|
||||
//Abandon our search if the block isn't multi
|
||||
if(!mcMMO.getMaterialMapStore().isMultiBlockPlant(relativeUpBlock.getType()))
|
||||
break;
|
||||
|
||||
brokenBlocks.add(relativeUpBlock);
|
||||
}
|
||||
|
||||
return brokenBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the plant is considered a one block plant
|
||||
* This is determined by seeing if it exists in a hard-coded collection of Multi-Block plants
|
||||
* @param material target plant material
|
||||
* @return true if the block is not contained in the collection of multi-block plants
|
||||
*/
|
||||
private boolean isOneBlockPlant(Material material) {
|
||||
return !mcMMO.getMaterialMapStore().isMultiBlockPlant(material);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +575,7 @@ public class HerbalismManager extends SkillManager {
|
||||
* @param blockState target block state
|
||||
* @return true if double drop succeeds
|
||||
*/
|
||||
public boolean checkDoubleDrop(BlockState blockState)
|
||||
private boolean checkDoubleDrop(BlockState blockState)
|
||||
{
|
||||
return BlockUtils.checkDoubleDrops(getPlayer(), blockState, skill, SubSkillType.HERBALISM_DOUBLE_DROPS);
|
||||
}
|
||||
@@ -277,15 +666,38 @@ public class HerbalismManager extends SkillManager {
|
||||
return Herbalism.convertShroomThumb(blockState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the delayed replant task and turns
|
||||
* @param desiredCropAge the desired age of the crop
|
||||
* @param blockBreakEvent the {@link BlockBreakEvent} this crop was involved in
|
||||
* @param cropState the {@link BlockState} of the crop
|
||||
*/
|
||||
private void startReplantTask(int desiredCropAge, BlockBreakEvent blockBreakEvent, BlockState cropState, boolean isImmature) {
|
||||
//Mark the plant as recently replanted to avoid accidental breakage
|
||||
new DelayedCropReplant(blockBreakEvent, cropState, desiredCropAge, isImmature).runTaskLater(mcMMO.p, 20 * 2);
|
||||
blockBreakEvent.getBlock().setMetadata(mcMMO.REPLANT_META_KEY, new RecentlyReplantedCropMeta(mcMMO.p, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the Green Thumb ability for plants.
|
||||
*
|
||||
* @param blockState The {@link BlockState} to check ability activation for
|
||||
* @param greenTerra boolean to determine if greenTerra is active or not
|
||||
*/
|
||||
private void processGreenThumbPlants(BlockState blockState, boolean greenTerra) {
|
||||
if (!BlockUtils.isFullyGrown(blockState))
|
||||
return;
|
||||
private boolean processGreenThumbPlants(BlockState blockState, BlockBreakEvent blockBreakEvent, boolean greenTerra) {
|
||||
if(!ItemUtils.isHoe(blockBreakEvent.getPlayer().getInventory().getItemInMainHand())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockData blockData = blockState.getBlockData();
|
||||
|
||||
if (!(blockData instanceof Ageable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ageable ageable = (Ageable) blockData;
|
||||
|
||||
//If the ageable is NOT mature and the player is NOT using a hoe, abort
|
||||
|
||||
Player player = getPlayer();
|
||||
PlayerInventory playerInventory = player.getInventory();
|
||||
@@ -317,37 +729,49 @@ public class HerbalismManager extends SkillManager {
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack seedStack = new ItemStack(seed);
|
||||
|
||||
if (!greenTerra && !RandomChanceUtil.checkRandomChanceExecutionSuccess(player, SubSkillType.HERBALISM_GREEN_THUMB, true)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handleBlockState(blockState, greenTerra)) {
|
||||
return;
|
||||
|
||||
if (!playerInventory.containsAtLeast(seedStack, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ItemUtils.isHoe(getPlayer().getInventory().getItemInMainHand()))
|
||||
{
|
||||
if (!playerInventory.containsAtLeast(seedStack, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
playerInventory.removeItem(seedStack);
|
||||
player.updateInventory(); // Needed until replacement available
|
||||
if (!processGrowingPlants(blockState, ageable, blockBreakEvent, greenTerra)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
new HerbalismBlockUpdaterTask(blockState).runTaskLater(mcMMO.p, 0);
|
||||
playerInventory.removeItem(seedStack);
|
||||
player.updateInventory(); // Needed until replacement available
|
||||
//Play sound
|
||||
SoundManager.sendSound(player, player.getLocation(), SoundType.ITEM_CONSUMED);
|
||||
return true;
|
||||
// new HerbalismBlockUpdaterTask(blockState).runTaskLater(mcMMO.p, 0);
|
||||
}
|
||||
|
||||
private boolean handleBlockState(BlockState blockState, boolean greenTerra) {
|
||||
int greenThumbStage = getGreenThumbStage();
|
||||
private boolean processGrowingPlants(BlockState blockState, Ageable ageable, BlockBreakEvent blockBreakEvent, boolean greenTerra) {
|
||||
//This check is needed
|
||||
if(isBizarreAgeable(ageable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blockState.setMetadata(mcMMO.greenThumbDataKey, new FixedMetadataValue(mcMMO.p, (int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)));
|
||||
Ageable crops = (Ageable) blockState.getBlockData();
|
||||
int finalAge = 0;
|
||||
int greenThumbStage = getGreenThumbStage(greenTerra);
|
||||
|
||||
//Immature plants will start over at 0
|
||||
if(!isAgeableMature(ageable)) {
|
||||
// blockBreakEvent.setCancelled(true);
|
||||
startReplantTask(0, blockBreakEvent, blockState, true);
|
||||
// blockState.setType(Material.AIR);
|
||||
blockBreakEvent.setDropItems(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (blockState.getType()) {
|
||||
|
||||
@@ -355,46 +779,47 @@ public class HerbalismManager extends SkillManager {
|
||||
case CARROTS:
|
||||
case WHEAT:
|
||||
|
||||
if (greenTerra) {
|
||||
crops.setAge(3);
|
||||
}
|
||||
else {
|
||||
crops.setAge(greenThumbStage);
|
||||
}
|
||||
finalAge = getGreenThumbStage(greenTerra);
|
||||
break;
|
||||
|
||||
case BEETROOTS:
|
||||
case NETHER_WART:
|
||||
|
||||
if (greenTerra || greenThumbStage > 2) {
|
||||
crops.setAge(2);
|
||||
finalAge = 2;
|
||||
}
|
||||
else if (greenThumbStage == 2) {
|
||||
crops.setAge(1);
|
||||
finalAge = 1;
|
||||
}
|
||||
else {
|
||||
crops.setAge(0);
|
||||
finalAge = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case COCOA:
|
||||
|
||||
if (greenTerra || getGreenThumbStage() > 1) {
|
||||
crops.setAge(1);
|
||||
if (getGreenThumbStage(greenTerra) >= 2) {
|
||||
finalAge = 1;
|
||||
}
|
||||
else {
|
||||
crops.setAge(0);
|
||||
finalAge = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
blockState.setBlockData(crops);
|
||||
|
||||
//Start the delayed replant
|
||||
startReplantTask(finalAge, blockBreakEvent, blockState, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getGreenThumbStage() {
|
||||
private int getGreenThumbStage(boolean greenTerraActive) {
|
||||
if(greenTerraActive)
|
||||
return Math.min(RankUtils.getHighestRank(SubSkillType.HERBALISM_GREEN_THUMB),
|
||||
RankUtils.getRank(getPlayer(), SubSkillType.HERBALISM_GREEN_THUMB) + 1);
|
||||
|
||||
return RankUtils.getRank(getPlayer(), SubSkillType.HERBALISM_GREEN_THUMB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ public class MiningManager extends SkillManager {
|
||||
return getSkillLevel() >= BlastMining.getBiggerBombsUnlockLevel() && Permissions.biggerBombs(getPlayer());
|
||||
}
|
||||
|
||||
public boolean canDoubleDrop() {
|
||||
return RankUtils.hasUnlockedSubskill(getPlayer(), SubSkillType.MINING_DOUBLE_DROPS) && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.MINING_DOUBLE_DROPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process double drops & XP gain for Mining.
|
||||
*
|
||||
@@ -73,15 +77,12 @@ public class MiningManager extends SkillManager {
|
||||
return;
|
||||
}
|
||||
|
||||
Material material = blockState.getType();
|
||||
|
||||
if (mcMMOPlayer.getAbilityMode(skill.getAbility())) {
|
||||
SkillUtils.handleDurabilityChange(getPlayer().getInventory().getItemInMainHand(), Config.getInstance().getAbilityToolDamage());
|
||||
}
|
||||
|
||||
if ((mcMMO.getModManager().isCustomMiningBlock(blockState) && !mcMMO.getModManager().getBlock(blockState).isDoubleDropEnabled())) {
|
||||
if(!Config.getInstance().getDoubleDropsEnabled(PrimarySkillType.MINING, blockState.getType()) || !canDoubleDrop())
|
||||
return;
|
||||
}
|
||||
|
||||
boolean silkTouch = player.getInventory().getItemInMainHand().containsEnchantment(Enchantment.SILK_TOUCH);
|
||||
|
||||
@@ -133,6 +134,7 @@ public class MiningManager extends SkillManager {
|
||||
int xp = 0;
|
||||
|
||||
float oreBonus = (float) (getOreBonus() / 100);
|
||||
//TODO: Pretty sure something is fucked with debrisReduction stuff
|
||||
float debrisReduction = (float) (getDebrisReduction() / 100);
|
||||
int dropMultiplier = getDropMultiplier();
|
||||
|
||||
@@ -144,7 +146,8 @@ public class MiningManager extends SkillManager {
|
||||
if (BlockUtils.isOre(blockState)) {
|
||||
ores.add(blockState);
|
||||
}
|
||||
else {
|
||||
//Server bug that allows beacons to be duped when yield is set to 0
|
||||
else if(blockState.getType() != Material.BEACON && blockState.getType() != Material.SHULKER_BOX) {
|
||||
debris.add(blockState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ public class RepairManager extends SkillManager {
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
|
||||
Material repairMaterial = repairable.getRepairMaterial();
|
||||
byte repairMaterialMetadata = repairable.getRepairMaterialMetadata();
|
||||
ItemStack toRemove = new ItemStack(repairMaterial);
|
||||
|
||||
short startDurability = item.getDurability();
|
||||
@@ -111,10 +110,6 @@ public class RepairManager extends SkillManager {
|
||||
|
||||
String materialsNeeded = "";
|
||||
|
||||
if (repairMaterialMetadata != (byte) -1 && !inventory.containsAtLeast(toRemove, 1)) {
|
||||
materialsNeeded += ":" + repairMaterialMetadata;
|
||||
}
|
||||
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Skills.NeedMore.Extra", prettyName, materialsNeeded);
|
||||
return;
|
||||
}
|
||||
@@ -130,7 +125,7 @@ public class RepairManager extends SkillManager {
|
||||
|
||||
// Lets get down to business,
|
||||
// To defeat, the huns.
|
||||
int baseRepairAmount = repairable.getBaseRepairDurability(); // Did they send me daughters?
|
||||
int baseRepairAmount = repairable.getBaseRepairDurability(item); // Did they send me daughters?
|
||||
short newDurability = repairCalculate(startDurability, baseRepairAmount); // When I asked for sons?
|
||||
|
||||
// Call event
|
||||
@@ -144,15 +139,16 @@ public class RepairManager extends SkillManager {
|
||||
}
|
||||
|
||||
// Remove the item
|
||||
if (repairMaterialMetadata == -1) {
|
||||
toRemove = inventory.getItem(inventory.first(repairMaterial)).clone();
|
||||
toRemove.setAmount(1);
|
||||
}
|
||||
toRemove = inventory.getItem(inventory.first(repairMaterial)).clone();
|
||||
toRemove.setAmount(1);
|
||||
|
||||
inventory.removeItem(toRemove);
|
||||
|
||||
// Give out XP like candy
|
||||
applyXpGain((float) ((getPercentageRepaired(startDurability, newDurability, repairable.getMaximumDurability()) * repairable.getXpMultiplier()) * ExperienceConfig.getInstance().getRepairXPBase() * ExperienceConfig.getInstance().getRepairXP(repairable.getRepairMaterialType())), XPGainReason.PVE);
|
||||
applyXpGain((float) ((getPercentageRepaired(startDurability, newDurability, repairable.getMaximumDurability())
|
||||
* repairable.getXpMultiplier())
|
||||
* ExperienceConfig.getInstance().getRepairXPBase()
|
||||
* ExperienceConfig.getInstance().getRepairXP(repairable.getRepairMaterialType())), XPGainReason.PVE);
|
||||
|
||||
// BWONG BWONG BWONG
|
||||
if (Config.getInstance().getRepairAnvilUseSoundsEnabled()) {
|
||||
@@ -332,10 +328,19 @@ public class RepairManager extends SkillManager {
|
||||
boolean downgraded = false;
|
||||
|
||||
for (Entry<Enchantment, Integer> enchant : enchants.entrySet()) {
|
||||
int enchantLevel = enchant.getValue();
|
||||
|
||||
if(!ExperienceConfig.getInstance().allowUnsafeEnchantments()) {
|
||||
if(enchantLevel > enchant.getKey().getMaxLevel()) {
|
||||
enchantLevel = enchant.getKey().getMaxLevel();
|
||||
|
||||
item.addEnchantment(enchant.getKey(), enchantLevel);
|
||||
}
|
||||
}
|
||||
|
||||
Enchantment enchantment = enchant.getKey();
|
||||
|
||||
if (RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getKeepEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING))) {
|
||||
int enchantLevel = enchant.getValue();
|
||||
|
||||
if (ArcaneForging.arcaneForgingDowngrades && enchantLevel > 1
|
||||
&& (!RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(100 - getDowngradeEnchantChance(), getPlayer(), SubSkillType.REPAIR_ARCANE_FORGING)))) {
|
||||
@@ -351,13 +356,13 @@ public class RepairManager extends SkillManager {
|
||||
Map<Enchantment, Integer> newEnchants = item.getEnchantments();
|
||||
|
||||
if (newEnchants.isEmpty()) {
|
||||
NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE_FAILED, "Repair.Arcane.Fail");
|
||||
NotificationManager.sendPlayerInformationChatOnly(getPlayer(), "Repair.Arcane.Fail");
|
||||
}
|
||||
else if (downgraded || newEnchants.size() < enchants.size()) {
|
||||
NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE_FAILED, "Repair.Arcane.Downgrade");
|
||||
NotificationManager.sendPlayerInformationChatOnly(getPlayer(), "Repair.Arcane.Downgrade");
|
||||
}
|
||||
else {
|
||||
NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Repair.Arcane.Perfect");
|
||||
NotificationManager.sendPlayerInformationChatOnly(getPlayer(), "Repair.Arcane.Perfect");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gmail.nossr50.skills.repair.repairables;
|
||||
import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
|
||||
public interface Repairable {
|
||||
@@ -20,13 +21,6 @@ public interface Repairable {
|
||||
*/
|
||||
public Material getRepairMaterial();
|
||||
|
||||
/**
|
||||
* Gets the metadata byte value of the material used to repair this item
|
||||
*
|
||||
* @return the byte metadata of the repair material
|
||||
*/
|
||||
public byte getRepairMaterialMetadata();
|
||||
|
||||
/**
|
||||
* Gets the pretty name of the material used to repair this item
|
||||
*
|
||||
@@ -71,7 +65,7 @@ public interface Repairable {
|
||||
*
|
||||
* @return the base repair durability
|
||||
*/
|
||||
public short getBaseRepairDurability();
|
||||
public short getBaseRepairDurability(ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Gets the minimum repair level needed to repair this item
|
||||
|
||||
@@ -6,16 +6,23 @@ import org.bukkit.Material;
|
||||
|
||||
|
||||
public class RepairableFactory {
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, byte repairMetadata, int minimumQuantity, short maximumDurability) {
|
||||
return getRepairable(itemMaterial, repairMaterial, repairMetadata, null, 0, minimumQuantity, maximumDurability, ItemType.OTHER, MaterialType.OTHER, 1);
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, short maximumDurability) {
|
||||
return getRepairable(itemMaterial, repairMaterial, null, 0, maximumDurability, ItemType.OTHER, MaterialType.OTHER, 1);
|
||||
}
|
||||
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, byte repairMetadata, int minimumLevel, int minimumQuantity, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
return getRepairable(itemMaterial, repairMaterial, repairMetadata, null, minimumLevel, minimumQuantity, maximumDurability, repairItemType, repairMaterialType, xpMultiplier);
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, int minimumLevel, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
return getRepairable(itemMaterial, repairMaterial, null, minimumLevel, maximumDurability, repairItemType, repairMaterialType, xpMultiplier);
|
||||
}
|
||||
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, byte repairMetadata, String repairMaterialPrettyName, int minimumLevel, int minimumQuantity, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, String repairMaterialPrettyName,
|
||||
int minimumLevel, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
// TODO: Add in loading from config what type of repairable we want.
|
||||
return new SimpleRepairable(itemMaterial, repairMaterial, repairMetadata, repairMaterialPrettyName, minimumLevel, minimumQuantity, maximumDurability, repairItemType, repairMaterialType, xpMultiplier);
|
||||
return new SimpleRepairable(itemMaterial, repairMaterial, repairMaterialPrettyName, minimumLevel, maximumDurability, repairItemType, repairMaterialType, xpMultiplier);
|
||||
}
|
||||
|
||||
public static Repairable getRepairable(Material itemMaterial, Material repairMaterial, String repairMaterialPrettyName,
|
||||
int minimumLevel, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier, int minQuantity) {
|
||||
// TODO: Add in loading from config what type of repairable we want.
|
||||
return new SimpleRepairable(itemMaterial, repairMaterial, repairMaterialPrettyName, minimumLevel, maximumDurability, repairItemType, repairMaterialType, xpMultiplier, minQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,44 @@ package com.gmail.nossr50.skills.repair.repairables;
|
||||
|
||||
import com.gmail.nossr50.datatypes.skills.ItemType;
|
||||
import com.gmail.nossr50.datatypes.skills.MaterialType;
|
||||
import com.gmail.nossr50.util.skills.SkillUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
|
||||
public class SimpleRepairable implements Repairable {
|
||||
private final Material itemMaterial, repairMaterial;
|
||||
private final int minimumQuantity, minimumLevel;
|
||||
private final short maximumDurability, baseRepairDurability;
|
||||
private final byte repairMetadata;
|
||||
private final int minimumLevel;
|
||||
private final short maximumDurability;
|
||||
private String repairMaterialPrettyName;
|
||||
private final ItemType repairItemType;
|
||||
private final MaterialType repairMaterialType;
|
||||
private final double xpMultiplier;
|
||||
private int minQuantity = -1;
|
||||
|
||||
protected SimpleRepairable(Material type, Material repairMaterial, byte repairMetadata, String repairMaterialPrettyName, int minimumLevel, int minimumQuantity, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
protected SimpleRepairable(Material type, Material repairMaterial, String repairMaterialPrettyName, int minimumLevel, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier) {
|
||||
this.itemMaterial = type;
|
||||
this.repairMaterial = repairMaterial;
|
||||
this.repairMetadata = repairMetadata;
|
||||
this.repairMaterialPrettyName = repairMaterialPrettyName;
|
||||
this.repairItemType = repairItemType;
|
||||
this.repairMaterialType = repairMaterialType;
|
||||
this.minimumLevel = minimumLevel;
|
||||
this.minimumQuantity = minimumQuantity;
|
||||
this.maximumDurability = maximumDurability;
|
||||
this.baseRepairDurability = (short) (maximumDurability / minimumQuantity);
|
||||
this.xpMultiplier = xpMultiplier;
|
||||
}
|
||||
|
||||
protected SimpleRepairable(Material type, Material repairMaterial, String repairMaterialPrettyName, int minimumLevel, short maximumDurability, ItemType repairItemType, MaterialType repairMaterialType, double xpMultiplier, int minQuantity) {
|
||||
this.itemMaterial = type;
|
||||
this.repairMaterial = repairMaterial;
|
||||
this.repairMaterialPrettyName = repairMaterialPrettyName;
|
||||
this.repairItemType = repairItemType;
|
||||
this.repairMaterialType = repairMaterialType;
|
||||
this.minimumLevel = minimumLevel;
|
||||
this.maximumDurability = maximumDurability;
|
||||
this.xpMultiplier = xpMultiplier;
|
||||
this.minQuantity = minQuantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material getItemMaterial() {
|
||||
return itemMaterial;
|
||||
@@ -39,11 +50,6 @@ public class SimpleRepairable implements Repairable {
|
||||
return repairMaterial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getRepairMaterialMetadata() {
|
||||
return repairMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepairMaterialPrettyName() {
|
||||
return repairMaterialPrettyName;
|
||||
@@ -61,7 +67,10 @@ public class SimpleRepairable implements Repairable {
|
||||
|
||||
@Override
|
||||
public int getMinimumQuantity() {
|
||||
return minimumQuantity;
|
||||
if(minQuantity == -1)
|
||||
return Math.max(SkillUtils.getRepairAndSalvageQuantities(itemMaterial, repairMaterial), 1);
|
||||
else
|
||||
return minQuantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,8 +79,8 @@ public class SimpleRepairable implements Repairable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getBaseRepairDurability() {
|
||||
return baseRepairDurability;
|
||||
public short getBaseRepairDurability(ItemStack itemStack) {
|
||||
return (short) (maximumDurability / getMinimumQuantity());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,7 +10,7 @@ public class Salvage {
|
||||
/*public static int salvageMaxPercentageLevel = AdvancedConfig.getInstance().getSalvageMaxPercentageLevel();
|
||||
public static double salvageMaxPercentage = AdvancedConfig.getInstance().getSalvageMaxPercentage();
|
||||
|
||||
public static int advancedSalvageUnlockLevel = RankUtils.getRankUnlockLevel(SubSkillType.SALVAGE_ADVANCED_SALVAGE, 1);*/
|
||||
public static int advancedSalvageUnlockLevel = RankUtils.getRankUnlockLevel(SubSkillType.SALVAGE_SCRAP_COLLECTOR, 1);*/
|
||||
|
||||
public static boolean arcaneSalvageDowngrades = AdvancedConfig.getInstance().getArcaneSalvageEnchantDowngradeEnabled();
|
||||
public static boolean arcaneSalvageEnchantLoss = AdvancedConfig.getInstance().getArcaneSalvageEnchantLossEnabled();
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gmail.nossr50.skills.salvage;
|
||||
|
||||
import com.gmail.nossr50.config.AdvancedConfig;
|
||||
import com.gmail.nossr50.config.Config;
|
||||
import com.gmail.nossr50.config.experience.ExperienceConfig;
|
||||
import com.gmail.nossr50.datatypes.interactions.NotificationType;
|
||||
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
||||
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
||||
@@ -65,7 +66,7 @@ public class SalvageManager extends SkillManager {
|
||||
|
||||
Salvageable salvageable = mcMMO.getSalvageableManager().getSalvageable(item.getType());
|
||||
|
||||
if (item.getItemMeta().isUnbreakable()) {
|
||||
if (item.getItemMeta() != null && item.getItemMeta().isUnbreakable()) {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Anvil.Unbreakable");
|
||||
return;
|
||||
}
|
||||
@@ -81,30 +82,23 @@ public class SalvageManager extends SkillManager {
|
||||
return;
|
||||
}
|
||||
|
||||
/*int skillLevel = getSkillLevel();
|
||||
int minimumSalvageableLevel = salvageable.getMinimumLevel();*/
|
||||
/*int skillLevel = getSkillLevel();*/
|
||||
int minimumSalvageableLevel = salvageable.getMinimumLevel();
|
||||
|
||||
// Level check
|
||||
if (!RankUtils.hasUnlockedSubskill(player, SubSkillType.SALVAGE_ARCANE_SALVAGE)) {
|
||||
if (getSkillLevel() < minimumSalvageableLevel) {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.REQUIREMENTS_NOT_MET, "Salvage.Skills.Adept.Level", String.valueOf(RankUtils.getUnlockLevel(SubSkillType.SALVAGE_ARCANE_SALVAGE)), StringUtils.getPrettyItemString(item.getType()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.getDurability() != 0 && (!RankUtils.hasUnlockedSubskill(player, SubSkillType.SALVAGE_ADVANCED_SALVAGE) || !Permissions.advancedSalvage(player))) {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Salvage.Skills.Adept.Damaged");
|
||||
return;
|
||||
}
|
||||
int potentialSalvageYield = Salvage.calculateSalvageableAmount(item.getDurability(), salvageable.getMaximumDurability(), salvageable.getMaximumQuantity());
|
||||
|
||||
int salvageableAmount = Salvage.calculateSalvageableAmount(item.getDurability(), salvageable.getMaximumDurability(), salvageable.getMaximumQuantity());
|
||||
|
||||
if (salvageableAmount == 0) {
|
||||
if (potentialSalvageYield <= 0) {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Salvage.Skills.TooDamaged");
|
||||
player.sendMessage(LocaleLoader.getString("Salvage.Skills.TooDamaged"));
|
||||
return;
|
||||
}
|
||||
|
||||
salvageableAmount = Math.min(salvageableAmount, getSalvageableAmount()); // Always get at least something back, if you're capable of salvaging it.
|
||||
|
||||
potentialSalvageYield = Math.min(potentialSalvageYield, getSalvageLimit()); // Always get at least something back, if you're capable of salvaging it.
|
||||
|
||||
player.getInventory().setItemInMainHand(new ItemStack(Material.AIR));
|
||||
location.add(0.5, 1, 0.5);
|
||||
@@ -116,25 +110,58 @@ public class SalvageManager extends SkillManager {
|
||||
enchantBook = arcaneSalvageCheck(enchants);
|
||||
}
|
||||
|
||||
ItemStack salvageResults = new ItemStack(salvageable.getSalvageMaterial(), salvageableAmount);
|
||||
//Lottery on Salvageable Amount
|
||||
|
||||
int lotteryResults = 1;
|
||||
int chanceOfSuccess = 99;
|
||||
|
||||
for(int x = 0; x < potentialSalvageYield-1; x++) {
|
||||
|
||||
if(RandomChanceUtil.rollDice(chanceOfSuccess, 100)) {
|
||||
chanceOfSuccess-=3;
|
||||
chanceOfSuccess = Math.max(chanceOfSuccess, 90);
|
||||
|
||||
lotteryResults+=1;
|
||||
}
|
||||
}
|
||||
|
||||
if(lotteryResults == potentialSalvageYield && potentialSalvageYield != 1 && RankUtils.isPlayerMaxRankInSubSkill(player, SubSkillType.SALVAGE_ARCANE_SALVAGE)) {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.Lottery.Perfect", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
|
||||
} else if(salvageable.getMaximumQuantity() == 1 || getSalvageLimit() >= salvageable.getMaximumQuantity()) {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.Lottery.Normal", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
|
||||
} else {
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.Lottery.Untrained", String.valueOf(lotteryResults), StringUtils.getPrettyItemString(item.getType()));
|
||||
}
|
||||
|
||||
ItemStack salvageResults = new ItemStack(salvageable.getSalvageMaterial(), lotteryResults);
|
||||
|
||||
//Call event
|
||||
if (EventUtils.callSalvageCheckEvent(player, item, salvageResults, enchantBook).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Location anvilLoc = location.clone();
|
||||
Location playerLoc = player.getLocation().clone();
|
||||
double distance = anvilLoc.distance(playerLoc);
|
||||
|
||||
double speedLimit = .6;
|
||||
double minSpeed = .3;
|
||||
|
||||
//Clamp the speed and vary it by distance
|
||||
double vectorSpeed = Math.min(speedLimit, Math.max(minSpeed, distance * .2));
|
||||
|
||||
//Add a very small amount of height
|
||||
anvilLoc.add(0, .1, 0);
|
||||
|
||||
if (enchantBook != null) {
|
||||
Misc.dropItem(location, enchantBook);
|
||||
Misc.spawnItemTowardsLocation(anvilLoc.clone(), playerLoc.clone(), enchantBook, vectorSpeed);
|
||||
}
|
||||
|
||||
Misc.dropItems(location, salvageResults, 1);
|
||||
Misc.spawnItemTowardsLocation(anvilLoc.clone(), playerLoc.clone(), salvageResults, vectorSpeed);
|
||||
|
||||
// BWONG BWONG BWONG - CLUNK!
|
||||
if (Config.getInstance().getSalvageAnvilUseSoundsEnabled()) {
|
||||
// SoundManager.sendSound(player, player.getLocation(), SoundType.ANVIL);
|
||||
SoundManager.sendSound(player, player.getLocation(), SoundType.ITEM_BREAK);
|
||||
|
||||
//player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE, "Salvage.Skills.Success");
|
||||
@@ -144,8 +171,8 @@ public class SalvageManager extends SkillManager {
|
||||
return Math.min((((Salvage.salvageMaxPercentage / Salvage.salvageMaxPercentageLevel) * getSkillLevel()) / 100.0D), Salvage.salvageMaxPercentage / 100.0D);
|
||||
}*/
|
||||
|
||||
public int getSalvageableAmount() {
|
||||
return (RankUtils.getRank(getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE) * 1);
|
||||
public int getSalvageLimit() {
|
||||
return (RankUtils.getRank(getPlayer(), SubSkillType.SALVAGE_SCRAP_COLLECTOR));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +223,7 @@ public class SalvageManager extends SkillManager {
|
||||
Player player = getPlayer();
|
||||
|
||||
if (!RankUtils.hasUnlockedSubskill(player, SubSkillType.SALVAGE_ARCANE_SALVAGE) || !Permissions.arcaneSalvage(player)) {
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Salvage.Skills.ArcaneFailed");
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.ArcaneFailed");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -207,15 +234,24 @@ public class SalvageManager extends SkillManager {
|
||||
int arcaneFailureCount = 0;
|
||||
|
||||
for (Entry<Enchantment, Integer> enchant : enchants.entrySet()) {
|
||||
|
||||
int enchantLevel = enchant.getValue();
|
||||
|
||||
if(!ExperienceConfig.getInstance().allowUnsafeEnchantments()) {
|
||||
if(enchantLevel > enchant.getKey().getMaxLevel()) {
|
||||
enchantLevel = enchant.getKey().getMaxLevel();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Salvage.arcaneSalvageEnchantLoss
|
||||
|| Permissions.hasSalvageEnchantBypassPerk(player)
|
||||
|| RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getExtractFullEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
|
||||
enchantMeta.addStoredEnchant(enchant.getKey(), enchant.getValue(), true);
|
||||
enchantMeta.addStoredEnchant(enchant.getKey(), enchantLevel, true);
|
||||
}
|
||||
else if (enchant.getValue() > 1
|
||||
else if (enchantLevel > 1
|
||||
&& Salvage.arcaneSalvageDowngrades
|
||||
&& RandomChanceUtil.checkRandomChanceExecutionSuccess(new RandomChanceSkillStatic(getExtractPartialEnchantChance(), getPlayer(), SubSkillType.SALVAGE_ARCANE_SALVAGE))) {
|
||||
enchantMeta.addStoredEnchant(enchant.getKey(), enchant.getValue() - 1, true);
|
||||
enchantMeta.addStoredEnchant(enchant.getKey(), enchantLevel - 1, true);
|
||||
downgraded = true;
|
||||
} else {
|
||||
arcaneFailureCount++;
|
||||
@@ -224,11 +260,11 @@ public class SalvageManager extends SkillManager {
|
||||
|
||||
if(failedAllEnchants(arcaneFailureCount, enchants.entrySet().size()))
|
||||
{
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Salvage.Skills.ArcaneFailed");
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.ArcaneFailed");
|
||||
return null;
|
||||
} else if(downgraded)
|
||||
{
|
||||
NotificationManager.sendPlayerInformation(player, NotificationType.SUBSKILL_MESSAGE_FAILED, "Salvage.Skills.ArcanePartial");
|
||||
NotificationManager.sendPlayerInformationChatOnly(player, "Salvage.Skills.ArcanePartial");
|
||||
}
|
||||
|
||||
book.setItemMeta(enchantMeta);
|
||||
|
||||
@@ -19,13 +19,6 @@ public interface Salvageable {
|
||||
*/
|
||||
public Material getSalvageMaterial();
|
||||
|
||||
/**
|
||||
* Gets the metadata byte value of the items dropped when salvaging this item
|
||||
*
|
||||
* @return the byte metadata of the salvage drop
|
||||
*/
|
||||
public byte getSalvageMaterialMetadata();
|
||||
|
||||
/**
|
||||
* Gets the ItemType value for this salvageable item
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user