1
0
mirror of https://github.com/mcMMO-Dev/mcMMO.git synced 2026-02-19 02:12:58 +01:00

Compare commits

..

427 Commits

Author SHA1 Message Date
Shane Freeder
483553b89d add translation badge
is pretti?
2020-02-27 20:41:15 +00:00
Shane Freeder
6047a5579b Translated using Weblate (English (United States))
Currently translated at 100.0% (1021 of 1021 strings)

Translation: mcmmo/mcmmo-master
Translate-URL: https://translate.mcmmo.org/projects/mcmmo/mcmmo-master/en_US/
2020-02-27 20:26:56 +00:00
Shane Freeder
a94db1b88a Listen to me 2020-02-27 19:56:07 +00:00
Shane Freeder
f5a397d2d2 Translated using Weblate (English (United States))
Currently translated at 100.0% (1021 of 1021 strings)

Translation: mcmmo/mcmmo-master
Translate-URL: https://translate.mcmmo.org/projects/mcmmo/mcmmo-master/en_US/
2020-02-27 19:56:07 +00:00
Shane Freeder
05af15c06b Translated using Weblate (English (United States))
Currently translated at 100.0% (1021 of 1021 strings)

Translation: mcmmo/mcmmo-master
Translate-URL: https://translate.mcmmo.org/projects/mcmmo/mcmmo-master/en_US/
2020-02-27 19:56:07 +00:00
nossr50
0a59b79ef9 Dev mode 2020-02-27 10:50:56 -08:00
nossr50
441125dbd1 2.1.117 2020-02-27 07:36:41 -08:00
nossr50
eff1ce102f Merge branch 'master' of github.com:mcMMO-Dev/mcmmo 2020-02-24 16:01:18 -08:00
nossr50
705b57a30b dev mode 2020-02-24 16:01:10 -08:00
Shane Freeder
d4ba9d7605 Reduce mojang profile API call limit (Fixes #4121) 2020-02-22 11:12:52 +00:00
nossr50
4cd91350db 2.1.116 2020-02-20 17:35:33 -08:00
nossr50
6168309ec9 2.1.115 2020-02-19 17:41:47 -08:00
nossr50
95c403a467 More tweaks to GT 2020-02-19 17:31:00 -08:00
nossr50
a598796c99 More tweaks to GT 2020-02-19 17:20:05 -08:00
nossr50
a333f36fd8 Fix sneak check on GT 2020-02-19 16:19:19 -08:00
nossr50
7c6d5c476d Green Thumb requires a hoe, sneak to break with a hoe 2020-02-19 16:14:21 -08:00
nossr50
e2073ff9f7 Immature crop replanting, green thumb tweaks, replant accidental break
protection
2020-02-19 15:58:53 -08:00
nossr50
8f26544188 Immature plants will be replanted if weilding a hoe 2020-02-19 13:51:10 -08:00
nossr50
4e21f1a200 tweak and fix salvage result chance 2020-02-19 13:27:21 -08:00
nossr50
ab6dbe306d Fix potential NPE for salvage 2020-02-19 11:38:48 -08:00
Shane Freeder
38d64f207d Fix default permission for Critical Strikes 2020-02-12 02:06:49 +00:00
Shane Freeder
8e8024e3e1 update changelog 2020-02-09 22:50:28 +00:00
Shane Freeder
4f161812aa Use minimum level of salvagable properly 2020-02-09 22:47:20 +00:00
Shane Freeder
11ca0d9ff1 Fix 1.13.x archery compat 2020-02-04 23:36:54 +00:00
Shane Freeder
4eef4a3e41 Fix CCE from spectral arrows (Fixes #4114) 2020-02-04 23:29:51 +00:00
Shane Freeder
816b64cb70 Track spectral arrows for combat 2020-01-28 16:37:02 +00:00
Shane Freeder
dceee5554d Fix CombatUtils NPE due to arrows from unloaded players 2020-01-28 16:33:30 +00:00
nossr50
8094be46c8 Dev mode 2020-01-27 19:01:08 -08:00
nossr50
2525ce9abe 2.1.114 2020-01-27 18:53:39 -08:00
nossr50
ea70c937f2 Fix rank for new versions of MySQL 2020-01-27 18:45:19 -08:00
Shane Freeder
550a3df616 place SnowGolemExcavation exploit fix under the correct section 2020-01-28 00:12:50 +00:00
apachezy
657abb76a7 "EndermanEndermiteFarms" does not work. (#4111)
This error causes the "ExploitFix.EndermanEndermiteFarms" option in ExperienceConfig to not work.
2020-01-28 00:11:57 +00:00
Shane Freeder
0ffcff53b3 Further address locale issues (Targets #4083) 2020-01-26 17:48:14 +00:00
nossr50
33f4ac14b5 Add toggle for snow golem exploit to experience.yml - SnowGolemExcavation 2020-01-24 18:28:41 -08:00
nossr50
15d3119627 Berserk now breaks a soft block if it was used to activate it 2020-01-24 01:25:55 -08:00
nossr50
4fd94bfe29 2.1.113 2020-01-24 01:11:23 -08:00
nossr50
ac731258c7 Berserk now breaks glass and makes noise when doing so 2020-01-24 01:09:19 -08:00
nossr50
7b8c90d362 BlockCracker fix for infested stone bricks 2020-01-24 00:56:16 -08:00
nossr50
055391e908 Hacky fix for spigot bug 2020-01-24 00:43:18 -08:00
nossr50
cd6ce5a19d Correct changelog 2020-01-23 20:34:54 -08:00
nossr50
7441d2d8d6 Merge branch 'master' of github.com:mcMMO-Dev/mcmmo 2020-01-23 20:34:13 -08:00
nossr50
7b941baa1c Update changelog 2020-01-23 20:34:00 -08:00
Lane
0c1fa07079 Fixed typo in config for potato (#4096) 2020-01-17 07:23:28 +00:00
andrewkm
539cd7290e Retro mode documentation. (#4104)
This commit will save lives, believe me.
2020-01-17 07:21:31 +00:00
Shane Freeder
406429f4e3 Use ThreadedLocalRandom for RNG utils 2020-01-16 18:21:35 +00:00
t00thpick1
421a394f68 Another check for PTP exploit 2020-01-15 13:49:11 -08:00
t00thpick1
d4adb490e2 Fixing PTP exploit 2020-01-15 13:32:10 -08:00
nossr50
de5a8babc5 dev mode 2020-01-15 13:21:33 -08:00
nossr50
d892bfe83a 2.1.112 2020-01-14 20:10:33 -08:00
nossr50
ceaff0c862 SkillShot tweaks 2020-01-14 20:09:41 -08:00
nossr50
a118d8465e Sweet Berry Bush nerf 2020-01-14 19:57:38 -08:00
nossr50
7c156319be Skill Shot is now multiplicative instead of additive 2020-01-14 19:53:37 -08:00
nossr50
24b3bf1100 NPE fix for combat on unloaded profiles 2020-01-14 19:44:17 -08:00
nossr50
41bdca948a Update POM for 1.15.1 2020-01-13 19:28:28 -08:00
Shane Freeder
3cbbf1bee0 Move valid check higher up, no need to set additional metadata 2019-12-31 12:17:03 +00:00
Shane Freeder
8fd1af4cbf Don't cache mob infomation in the updater task 2019-12-31 12:16:12 +00:00
Shane Freeder
601297799f Enforce locale usage for enums to ensure correct casing (Fixes #4086) 2019-12-16 01:05:47 +00:00
nossr50
da98be88ad Update changelog 2019-12-10 21:51:41 -08:00
nossr50
1104f48ad5 2.1.111 2019-12-10 21:46:09 -08:00
nossr50
af50699de1 1.15 changes 2019-12-10 21:35:44 -08:00
Shane Freeder
1c71f1daf5 Update changelog 2019-11-13 19:56:07 +00:00
Shane Freeder
b7dd491c01 Prevent nesting of bleed damage
This commit prevents the nesting of damage event processing in general in
regards to bleed, health related stuff will need a further glance over down
the line, however; This will fix a major problematic area
2019-11-13 19:53:02 +00:00
nossr50
3ce0d7b972 dev mode 2019-11-10 19:51:47 -08:00
Robert Alan Chapton
9dcbccb010 Merge pull request #4078 from Ineusia/master
Missing Section Error Messages (Repair & Salvage Config)
2019-11-10 18:18:12 -08:00
Ineusia
c5cbab39b6 Update RepairConfig.java
Removed accidental whitespace
2019-11-10 11:56:32 -06:00
Ineusia
ca1906fbc5 Added error message for missing Salvageables section (rather than throw NPE) 2019-11-08 01:15:32 -06:00
Ineusia
7eab20ef56 Added error message for missing Repairables section (rather than throw NPE) 2019-11-08 01:11:29 -06:00
nossr50
188b0f9813 2.1.110 2019-10-18 15:48:53 -07:00
nossr50
e6c9cc6fdd Actually fixed BlockCracker + One more type of block can be cracked 2019-10-18 15:48:12 -07:00
nossr50
76ca7cc88f Additional dupe failsafes 2019-10-18 15:37:36 -07:00
nossr50
335d708848 Bump version 2019-10-18 15:24:46 -07:00
nossr50
b4179cb9a6 Fixed a dupe bug 2019-10-18 15:23:15 -07:00
Robert Alan Chapton
8bbf95e9da Merge pull request #4065 from Vyciokazz/master
locale_lt_LT.properties
2019-10-18 01:10:09 -07:00
nossr50
dc805825de 2.1.109 2019-10-17 20:16:01 -07:00
Robert Alan Chapton
871ca744d9 Merge pull request #4070 from Fabrimat/patch-4
Update locale_it.properties
2019-10-17 10:01:26 -07:00
Fabrizio La Rosa
1b091bdbd5 Update locale_it.properties
Fixed definetly /mctop and fixed special characters.
2019-10-17 13:37:00 +02:00
Robert Alan Chapton
110d9a633b Update FUNDING.yml 2019-10-02 13:26:34 -07:00
Robert Alan Chapton
584859318b Create FUNDING.yml 2019-10-02 13:06:56 -07:00
Robert A. C
1eb2c7b2d7 Merge pull request #4068 from ethernetcat/master
Update locale_ja_JP.properties
2019-10-01 14:37:21 -04:00
SNAKE
3310a12a95 Update locale_ja_JP.properties 2019-10-02 01:43:51 +09:00
Shane Freeder
6125b3fbd2 Don't spam incompatible WG error 2019-09-27 18:57:25 +01:00
Robert A. C
0d6b5e2530 Merge pull request #4064 from Fabrimat/patch-3
Update locale_it.properties
2019-09-23 10:46:29 -07:00
Fabrizio La Rosa
d679101cfb Update locale_it.properties
Spaces doesn't work in commands like "/mctop Tiro con l'arco"
2019-09-23 18:40:56 +02:00
Shane Freeder
122c8b0faa Added missing 1.14 blocks to ability/tool activation blacklists (Fixes #4050) 2019-09-22 04:30:24 +01:00
nossr50
08d1ad4f6c Dev mode 2019-09-19 18:45:09 -07:00
nossr50
8e19e1de15 2.1.108 2019-09-19 18:33:33 -07:00
nossr50
101b0671fe compatibility with FAWE ( :| ) 2019-09-19 18:31:18 -07:00
nossr50
e14c53d3ca Load after worlds 2019-09-19 18:02:23 -07:00
nossr50
28c0215a93 Catch more exceptions when doing WG reflection checks 2019-09-19 17:21:25 -07:00
nossr50
51a373db4f Fixing an Acrobatics exploit 2019-09-17 18:59:44 -07:00
Vyciokazz
1b889b8177 locale_lt_LT.properties
I translated in Lithuanian language manny required info for player and player skils, in time i update this translation.
2019-09-18 00:14:14 +03:00
nossr50
4a4124d09f 2.1.107 2019-09-14 16:08:20 -07:00
nossr50
ceaf3d46cb Update changelog 2019-09-14 16:06:14 -07:00
Robert A. C
260d0f9b4a Merge pull request #4051 from Shrek5InTheatres2019/master
McMMOPlayerProfileLoadEvent for the api
2019-09-14 16:05:26 -07:00
Shane Freeder
d3a7ff8e8c Correctly encode Russian translation file (#4058) 2019-09-14 23:58:46 +01:00
nossr50
9154b3f083 Fixes #4056 2019-09-14 15:44:58 -07:00
Shrek5InTheatres2019
c3548be380 deleting one character variables for good 2019-09-09 17:56:06 -07:00
Shrek5InTheatres2019
67bfb40dd5 fixing the things 2019-09-09 16:50:05 -07:00
Robert A. C
777a9d4185 Merge pull request #4052 from andris155/patch-1
Update locale_hu_HU.properties
2019-09-09 15:06:02 -06:00
András Marczinkó
fcd45f3bc1 Update locale_hu_HU.properties 2019-09-07 13:24:05 +02:00
Shrek5InTheatres2019
478d542981 removing this task that i once thought i needed and now realize that it is completely unnecessary 2019-09-06 16:46:38 -07:00
Shrek5InTheatres2019
c6c1c39a98 player profile load event from issue request #4045 2019-09-06 16:44:58 -07:00
nossr50
0d6165c0f4 dev mode 2019-09-06 15:37:54 -06:00
nossr50
f45983949a 2.1.106 2019-09-06 15:21:13 -06:00
nossr50
cca0524441 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-09-05 18:59:08 -06:00
nossr50
e84e9a7dc8 more blacklisted blocks 2019-09-05 18:58:50 -06:00
Robert A. C
a3600e11a9 Merge pull request #4048 from ethernetcat/master
Update locale_ja_JP.properties
2019-09-03 14:10:50 -06:00
Robert A. C
483579503a Merge pull request #4047 from 89009332/patch-10
Update locale_zh_CN.properties
2019-09-03 14:10:37 -06:00
SNAKE
d89ff8b238 Update locale_ja_JP.properties 2019-09-04 01:05:33 +09:00
89009332
582ff48b6f Update locale_zh_CN.properties 2019-09-03 19:50:02 +08:00
nossr50
d6e14ca431 Dev mode 2019-09-02 17:10:40 -06:00
nossr50
0961351ba2 2.1.105 2019-09-02 16:56:16 -06:00
Robert A. C
0b93a74652 Merge pull request #4041 from Shrek5InTheatres2019/master
Implementing request from issue #3918
2019-09-02 16:53:50 -06:00
Robert A. C
2bc2e5bb32 Merge pull request #4042 from QuantumToasted/master
Support jump strength stats for horses in Beast Lore
2019-09-02 15:18:10 -06:00
Daniel Jarski
98f8e049f3 Update to Beast Lore
Removed movement speed information for llamas, and added jump strength info for horses. Also added another locale string to display this information.
2019-08-30 23:14:39 -05:00
Shrek5InTheatres2019
e27d299132 removing unnecessary code 2019-08-29 23:16:26 -07:00
Shrek5InTheatres2019
7cf40d9dbf Implementing request from issue #3918 2019-08-29 18:51:48 -07:00
Daniel Jarski
d6eefac065 Merge pull request #1 from mcMMO-Dev/master
update
2019-08-29 19:53:42 -05:00
nossr50
3036c4ef80 Update locale 2019-08-28 14:29:14 -06:00
Robert A. C
a81468ccfc Merge pull request #4040 from Leomixer17/patch-1
Update locale_it.properties
2019-08-28 14:00:05 -06:00
Leomixer17
2a2a4479ca Update locale_it.properties 2019-08-27 17:17:34 +02:00
Leomixer17
289276d987 Update locale_it.properties 2019-08-27 17:06:20 +02:00
Leomixer17
f771b777b9 Update locale_it.properties 2019-08-27 04:38:35 +02:00
Robert A. C
e4d081f14b Merge pull request #4039 from Mixpa/master
simply if code
2019-08-26 14:34:36 -06:00
nossr50
bba35f8a4e Set player scoreboards to main server scoreboard upon entering a blacklisted world 2019-08-26 14:15:42 -06:00
Shane Freeder
8a4410cde8 Add a configuration to print jdbc exceptions
By default, mcmmo tries to pretty print errors, which is fine for many cases,
however; deeper nested errors can lack information, e.g. networking related issues,
while the pretty print is less intimitating and provides useful information to users,
surpressing the trace can prevent debugging these deeper issues, this config option serves
as a compromise between scaring users and having a means to find these deeper issues.
2019-08-26 05:29:29 +01:00
Shane Freeder
8951c1fd21 Back in dev 2019-08-26 05:28:21 +01:00
nossr50
1f7f71e8b5 2.1.104 2019-08-25 12:06:07 -06:00
Robert A. C
88b056c38f Merge pull request #4035 from andris155/patch-2
Update locale_hu_HU.properties
2019-08-25 12:03:34 -06:00
Robert A. C
d408782acf Merge pull request #4037 from ethernetcat/master
Update locale_ja_JP.properties
2019-08-25 12:03:24 -06:00
Mixpa
dfeab6b558 simply if code 2019-08-25 21:51:19 +08:00
SNAKE
f4fe25009d Convert to UTF16 2019-08-25 05:37:58 +09:00
SNAKE
905010aacd Update locale_ja_JP.properties 2019-08-25 04:01:17 +09:00
Shane Freeder
5ea2c493e8 Don't process mcmmo damage for invuln (#4028) 2019-08-24 17:40:46 +01:00
András Marczinkó
734c6f9c62 Update locale_hu_HU.properties 2019-08-24 10:23:10 +02:00
nossr50
44a3238c79 Dev mode 2019-08-23 21:14:23 -06:00
nossr50
139975958f 2.1.103 2019-08-23 21:00:50 -06:00
nossr50
52f9273fa8 Fishing drops are no longer random amounts 2019-08-23 20:51:32 -06:00
nossr50
77ffee2515 Fixed a bug where Tree Feller only rewarded 1 XP per block 2019-08-23 13:08:04 -06:00
nossr50
40118d570c Update notes 2019-08-23 12:19:52 -06:00
Robert A. C
8109ecb0e0 Merge pull request #4032 from Leomixer17/patch-1
Update locale_it.properties
2019-08-22 19:17:28 -06:00
Leomixer17
1147dff4c3 Update locale_it.properties 2019-08-23 03:02:15 +02:00
Leomixer17
c274b21af1 Update locale_it.properties 2019-08-23 02:51:20 +02:00
nossr50
2b72e8e69f Making Limit Break code clearer to understand 2019-08-22 14:54:47 -06:00
Robert A. C
ca511ca0f5 Merge pull request #4029 from QuantumToasted/master
Fix spelling error in Guilds.Salvage.Section.0
2019-08-22 14:20:56 -06:00
Daniel Jarski
cafacc05ab Fix spelling error in Guilds.Salvage.Section.0
"...use an gold block" -> "...use a gold block"
2019-08-22 13:05:54 -05:00
Shane Freeder
2974ed0993 correct item salvage level check 2019-08-22 00:38:29 +01:00
Robert A. C
861552bcd8 Merge pull request #4027 from QuantumToasted/master
Add additional info about horse speed in Beast Lore
2019-08-21 14:06:15 -07:00
nossr50
7095ddb611 Dev mode 2019-08-21 14:17:27 -06:00
Daniel Jarski
f70f2321f2 Add additional info about horse speed in Beast Lore
An additional string containing info about the horse's movement speed (in blocks per second) is now concat'd to the end of the Beast Lore info. Also, added a Combat.BeastLoreHorseSpeed locale string to service this info.
2019-08-21 13:10:43 -05:00
nossr50
4bb8e20b59 2.1.102 2019-08-21 04:57:02 -07:00
Shane Freeder
e8165321e1 Fix bleed tasks invoking ruptures
Also, made the mcmmo custom damage checks handle nested calls
2019-08-21 01:05:01 +01:00
nossr50
4f6706b664 Test 2019-08-19 15:01:45 -07:00
Robert Chapton
665a31fc85 Players moving to a blacklisted world will have scoreboards removed 2019-08-19 14:49:21 -07:00
nossr50
1221069854 Dev mode 2019-08-11 23:58:13 -04:00
Robert A. C
054f8ffe0a Merge pull request #4021 from ethernetcat/master
Update locale_ja_JP.properties
2019-08-11 23:57:22 -04:00
Shane Freeder
dbecd289b2 Check configuration for fishing spam prevention (#4010) 2019-08-12 03:54:45 +01:00
Shane Freeder
bb7989449e Do not allow additions of timer tasks while iterating
Follow up on:
e4d980b135
Should help debug #4019 (and let us know when this actually occurs)
2019-08-12 02:31:51 +01:00
SNAKE0053
296c2e99f7 Update locale_ja_JP.properties 2019-08-12 02:35:23 +09:00
nossr50
4246f59d44 2.1.101 2019-08-09 17:18:26 -04:00
nossr50
87ff69d5c4 Add fix to changelog 2019-08-07 15:30:48 -07:00
Shane Freeder
84cb40521b Don't unmark herbalism blocks early (Fixes #4015) 2019-08-06 00:52:23 +01:00
nossr50
dda894dd9a dev mode 2019-08-05 10:46:26 -04:00
nossr50
7853efc63d 2.1.100 2019-08-05 10:43:05 -04:00
nossr50
11409c07c0 Fixed a bug with herbalism double drops 2019-08-05 10:39:33 -04:00
Robert A. C
7c17126ffb Merge pull request #4012 from myfbone/master
Another portion of Russian translation
2019-08-04 19:19:59 -07:00
myfbone
05e19b2122 Small correction of xp desc 2019-08-03 08:25:57 +03:00
myfbone
3b8fee5134 Some color corrections and added missing translation 2019-08-03 08:10:31 +03:00
Shane Freeder
4735b2ff8f Use the full GPLv3 license file 2019-08-02 16:02:07 +01:00
Shane Freeder
ac29c51d6e Fixed an NPE that may occur with random chances on a player without loaded data
Also correct changelog version
2019-08-02 03:34:26 +01:00
myfbone
ad9fa2dd9f Add some missing translations 2019-08-01 14:51:14 +03:00
Robert A. C
1e2f11f4f9 Merge pull request #4009 from myfbone/master
some russian translation corrections
2019-07-31 06:26:34 -07:00
myfbone
3a1f297618 new available version message now points to spigot instead of bukkit 2019-07-30 02:37:58 +03:00
myfbone
8af3f41f12 Стрельба (shooting) was renamed to Луки (bows) to reflect that this skill is about bows, but not about tridens and crossbows 2019-07-30 02:36:01 +03:00
Shane Freeder
e4d980b135 Fixed some concurrency concerns around BleedTasks 2019-07-29 17:41:57 +01:00
Shane Freeder
62ce98c8a5 validate that mcnotify isn't being ran by the console 2019-07-29 17:23:21 +01:00
myfbone
31a1fd4ece Fixed records translation in fishing extra and drop rate 2019-07-28 20:04:56 +03:00
nossr50
51e22f7904 Dev mode 2019-07-27 18:47:38 -07:00
Robert A. C
106fd84b7f Merge pull request #4006 from myfbone/master
Small corrections. Also shorted two word skill names to one word in order to be able running /skillname command
2019-07-26 20:13:59 -07:00
myfbone
5f99a6cf19 Merge branch 'master' of https://github.com/myfbone/mcMMO 2019-07-26 21:46:45 +03:00
myfbone
aba465e1a8 Small corrections. Alse shorted two word skill names to one word in order to be able running /skillname command 2019-07-26 21:46:24 +03:00
nossr50
73500aae90 2.1.99 2019-07-25 21:53:45 -07:00
nossr50
c3ec6a0b80 Debug message color tweaks 2019-07-25 21:50:17 -07:00
nossr50
509ca5ae7f 毎日... 2019-07-25 21:47:11 -07:00
nossr50
9b3091add4 Debug mode will now dump info about XP perks and how they are modifying
incoming XP gains
2019-07-25 21:43:19 -07:00
nossr50
b503cb5d80 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-07-25 21:26:31 -07:00
Robert A. C
a631388e5a Merge pull request #4003 from myfbone/master
Updated Russian translation up to the last version
2019-07-25 20:15:25 -07:00
myfbone
d64b869695 Updated Russian translation up to the last version 2019-07-26 06:09:24 +03:00
nossr50
8e1c1c9ef4 2.1.98 2019-07-24 20:11:54 -07:00
nossr50
952ee9556a Fixing Taming XP 2019-07-24 19:57:58 -07:00
nossr50
3155550931 Dev Mode again 2019-07-23 23:04:11 -07:00
nossr50
7242a171dd Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-07-22 20:12:35 -07:00
nossr50
0dac5f6b3f 2.1.97 2019-07-22 20:12:10 -07:00
Robert A. C
ff3bbad68c Merge pull request #3991 from andris155/patch-7
Update locale_hu_HU.properties
2019-07-22 20:09:53 -07:00
Robert A. C
5cc473fd96 Merge pull request #3996 from Yukiiro-Nite/close-files-after-world-unload
Close all the files after unload instead of before
2019-07-22 20:08:51 -07:00
Yukiiro-Nite
c03907b5a3 Close all the files after unload instead of before
- This ensures that all file handles are closed after a world is
unloaded, since the unloadChunk function calls saveChunk, which
opens files.
- Should resolve issue #3995
2019-07-21 01:01:42 -04:00
András Marczinkó
f9c257b73a Update locale_hu_HU.properties 2019-07-13 12:43:07 +02:00
Robert A. C
308e3a4b1f Merge pull request #3988 from EvilOlaf/patch-6
Update locale_de.properties
2019-07-11 08:33:47 -07:00
Werner
99f8f34529 Update locale_de.properties 2019-07-11 16:27:14 +02:00
nossr50
5f7f6fc55a Fixed a NPE with taming. 2019-07-10 05:03:33 -07:00
nossr50
36932e397d 2.1.96 2019-07-08 07:57:07 -07:00
nossr50
732e29f0a5 Add missing 's' to Nether_Bricks 2019-07-08 03:53:00 -07:00
nossr50
cd0ed4b385 Dev mode 2019-07-07 06:13:33 -07:00
nossr50
03fd558e1b Add toggle to allow piston cheating 2019-07-07 06:11:15 -07:00
nossr50
6bc57f184a 2.1.95 2019-07-07 02:48:17 -07:00
nossr50
6c58e8a243 Limit Break Nerfs 2019-07-07 02:33:39 -07:00
nossr50
c818bf82b0 Cap how much XP can be given for Roll based on damage 2019-07-07 02:07:55 -07:00
nossr50
67a687ee40 Fix Ender Pearls not preventing XP gains in offhand 2019-07-07 01:55:23 -07:00
nossr50
ec44c99076 Many Herbalism bug fixes 2019-07-07 01:42:57 -07:00
nossr50
39a1a07a7d Add missing Chorus_Flower xp entry for Herbalism to experience.yml 2019-07-05 06:20:44 -07:00
nossr50
0b2d0fa332 Make the last bug fix compatible with 1.13 2019-07-05 06:14:54 -07:00
nossr50
304a942f51 Fixed a bug preventing wandering traders from granting XP 2019-07-05 05:51:41 -07:00
nossr50
02c1ce749c 2.1.94 2019-07-02 17:29:57 -07:00
nossr50
1307169d8b Debug Stick is now replaced by /mmodebug 2019-07-02 17:29:33 -07:00
nossr50
8eeebf63e9 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-07-02 16:23:14 -07:00
nossr50
f7c0c9beb7 Add new mcMMO devs to readme.md and plugin.yml 2019-07-02 16:22:56 -07:00
Robert A. C
9ec4f64c70 Merge pull request #3978 from snake0053/master
Fix spelling
2019-07-02 15:09:15 -07:00
SNAKE0053
9667eaf045 Fix spelling 2019-07-03 06:51:06 +09:00
nossr50
51ade5113f Adjust changelog 2019-07-02 14:50:34 -07:00
nossr50
0af3c82612 Fixed a bug that could prevent a Tree from being removed by Tree Feller
if 2 or more players were using Tree Feller simultaneously
2019-07-02 14:46:55 -07:00
Robert A. C
4c13c0ec31 Merge pull request #3977 from andris155/patch-1
Update locale_hu_HU.properties
2019-07-02 14:46:33 -07:00
Robert A. C
8ca5a5cdec Merge pull request #3976 from snake0053/update_japanese_local
Update locale_ja_JP.properties
2019-07-02 14:46:22 -07:00
András Marczinkó
43819605fa Update locale_hu_HU.properties 2019-07-02 23:44:23 +02:00
SNAKE0053
6e00f7eaa2 Update locale_ja_JP.properties 2019-07-03 06:41:29 +09:00
nossr50
5d0ce407b7 2.1.93 2019-07-02 13:47:37 -07:00
nossr50
bf70a42f6a Fix breeding sending incorrect messages 2019-07-02 12:45:25 -07:00
nossr50
6e70258f9a 2.1.92 2019-07-02 01:44:01 -07:00
nossr50
1ae9e80d96 Prevent spam from breeding events 2019-07-02 01:31:49 -07:00
nossr50
444edb209c Remove summoned entities on server shutdown 2019-07-02 01:14:11 -07:00
nossr50
8a9c836065 More locale tweaks for COTW 2019-07-02 00:45:08 -07:00
nossr50
a19ddb46b6 Fixing some bugs, tweaking some strings for COTW 2019-07-02 00:32:38 -07:00
nossr50
53d6065185 Numerous COTW tweaks 2019-07-01 23:50:30 -07:00
nossr50
a66940b2b8 Unarmed no longer uses a serverwide cooldown for all players for bonus
damage
2019-06-30 12:19:45 -07:00
nossr50
9092e70544 Check the projectile type instead of main hand item 2019-06-25 22:44:14 -07:00
nossr50
517ca6568f Increase compatiblility with crazy enchants 2019-06-25 22:29:02 -07:00
nossr50
3368625dde 2.1.90 2019-06-25 18:21:14 -07:00
nossr50
2eb330f070 Mining XP values tweaked 2019-06-25 18:20:56 -07:00
nossr50
74a2485cff Multiple tweaks to how salvaged items travel towards a player 2019-06-25 18:02:16 -07:00
nossr50
03efd14ff4 2.1.89 2019-06-24 23:33:17 -07:00
nossr50
e71c95139d RetroMode is now the default mode. 2019-06-24 23:31:31 -07:00
nossr50
d9b84b0ab3 Check mining rank before processing double drops 2019-06-24 23:25:15 -07:00
nossr50
421d6cff3b Tweak archaeology numbers 2019-06-24 22:48:22 -07:00
nossr50
ccb86264d9 Update excavation command 2019-06-24 22:31:21 -07:00
nossr50
ee2a1b332e tweak Archaeology rewards 2019-06-24 22:30:20 -07:00
nossr50
2dda0bf27a Fix some WG logic + Archaeology stat display 2019-06-24 21:25:51 -07:00
nossr50
d347fa9512 Add information about Archaeology to /excavation and tweak some locale
strings related to excavation
2019-06-24 20:54:26 -07:00
nossr50
913323245c Archaeology now does something 2019-06-24 20:27:50 -07:00
nossr50
68ad507be3 Skills which used to unlock at level 5 now unlock at level 1 and some
tweaks to  ranks of other skills, and length scaling for super
abilities. Early game boost has also been modified.
2019-06-24 20:17:35 -07:00
nossr50
764b4c20f5 Tools with durability enchant will be damaged properly by super
abilities again (at a reduced rated)
2019-06-24 17:49:02 -07:00
nossr50
a135e08e12 Improved WG compatibility + Tree Feller will now damage unbreaking
enchant tools again
2019-06-24 17:32:05 -07:00
Robert A. C
214974e3a3 Merge pull request #3968 from snake0053/master
Update locale_ja_JP.properties
2019-06-23 12:01:53 -07:00
nossr50
3673d3fb7b Tree Feller bug fix 2019-06-23 12:00:49 -07:00
nossr50
f6ad530660 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-06-23 11:56:41 -07:00
SNAKE0053
046daa43fa Update locale_ja_JP.properties 2019-06-24 00:11:24 +09:00
Robert A. C
cc1c5239dc Merge pull request #3967 from EvilOlaf/patch-5
Update locale_de.properties
2019-06-23 01:26:09 -07:00
Werner
4fafb5731e Update locale_de.properties 2019-06-23 10:00:07 +02:00
nossr50
9ab4d59cca mcMMO will ignore damage triggers for events with a value of zero 2019-06-22 22:35:25 -07:00
nossr50
68864dae2d 2.1.87 2019-06-22 15:06:24 -07:00
nossr50
ae6d377abb Fixing a mistake when grabbing level cap 2019-06-22 15:03:51 -07:00
nossr50
b479d45f14 Level Cap bugfixes and tweaks 2019-06-22 14:48:16 -07:00
nossr50
e17afe31a5 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-06-22 13:05:58 -07:00
Robert A. C
5f115d5e4a Merge pull request #3966 from EvilOlaf/patch-1
Update locale_de.properties
2019-06-22 08:28:33 -07:00
Werner
1984131874 Update locale_de.properties 2019-06-22 13:33:03 +02:00
nossr50
0b4e2b48c9 Dev mode 2019-06-21 12:45:31 -07:00
nossr50
6aa53823f1 Small changelog edits 2019-06-21 09:10:53 -07:00
nossr50
7f567585e2 2.1.86 2019-06-21 08:12:00 -07:00
nossr50
c6d604bbbc Inspect, Salvage, and Locale fixes 2019-06-21 08:05:57 -07:00
nossr50
5698d8282c 2.1.85 2019-06-20 00:02:48 -07:00
nossr50
83636644b1 2.1.84 2019-06-18 15:28:56 -07:00
nossr50
fa6995b9fd You can no longer set fire to party members 2019-06-18 14:04:42 -07:00
nossr50
c83d38b5c2 Arrow Retrieval will no longer work with piercing enchant 2019-06-18 13:06:26 -07:00
Robert A. C
31e9abd8eb Merge pull request #3960 from andris155/patch-1
Update locale_hu_HU.properties
2019-06-18 09:00:34 -07:00
András Marczinkó
43b7abafe7 Update locale_hu_HU.properties 2019-06-18 16:29:06 +02:00
András Marczinkó
483134df10 Update locale_hu_HU.properties 2019-06-18 16:22:48 +02:00
nossr50
b612a599de 2.1.82 2019-06-17 08:26:21 -07:00
nossr50
2cecc91026 WG Hardcore penalty flag 'mcmmo-hardcore' 2019-06-17 08:25:37 -07:00
nossr50
38bc60ba86 Inspecting offline players no longer requires permissions + fixing
errors when using mcrank on an offline player
2019-06-17 08:19:15 -07:00
nossr50
4e4d798b1d Add proper error handling to file loading 2019-06-17 07:38:30 -07:00
nossr50
218b2a1a75 Double check that a Player is not an NPC when loading profiles 2019-06-17 07:00:39 -07:00
Robert A. C
73ce7e729c Merge pull request #3958 from 89009332/patch-9
Update locale_zh_CN.properties
2019-06-16 06:15:34 -07:00
89009332
d67cbe2861 Update locale_zh_CN.properties 2019-06-16 21:09:34 +08:00
nossr50
c3f38cdabd A few typos in javdocs 2019-06-15 21:22:21 -07:00
nossr50
8f55a6277a Fix Repair enchant logic 2019-06-14 20:44:33 -07:00
nossr50
ef71661db1 2.1.81 2019-06-14 20:39:57 -07:00
nossr50
adefda42df Dev mode 2019-06-12 14:43:13 -07:00
nossr50
9c04054017 2.1.80 2019-06-12 12:23:58 -07:00
nossr50
7ab70ba597 2.1.79 2019-06-12 11:05:16 -07:00
Robert A. C
0abccd105d Merge pull request #3953 from snake0053/master
Update locale_ja_JP.properties
2019-06-12 10:43:08 -07:00
SNAKE0053
15b34e2056 Update locale_ja_JP.properties 2019-06-13 02:35:39 +09:00
nossr50
ae551a6bc1 2.1.78 2019-06-12 10:22:20 -07:00
nossr50
98f6eac50c Super Abilities don't do extra durability damage to tools with the
unbreaking enchantment
2019-06-12 10:18:43 -07:00
nossr50
963d0a1897 Fixed a bug where shovels took too many materials to repair 2019-06-12 06:20:20 -07:00
nossr50
705285878f Diagnosing myself with dyslexia 2019-06-11 03:57:28 -07:00
nossr50
e1dcb65888 Add back in the comment about min quantity 2019-06-11 03:52:45 -07:00
nossr50
9062dbcaae Add minimum quantity back to the repair config 2019-06-11 03:50:13 -07:00
nossr50
97c98969ff 2.1.76 2019-06-10 22:49:47 -07:00
nossr50
3fe47c939a You can not salvage without 1 rank in Scrap Collector now (unlocks at
level 2)
2019-06-10 22:46:03 -07:00
nossr50
46dee3c9b6 Numerous tweaks to salvage + fixing repair bugs 2019-06-10 22:37:20 -07:00
nossr50
8e67e2d158 Merge branch 'master' of github.com:mcMMO-Dev/mcMMO 2019-06-10 16:33:30 -07:00
nossr50
f5f4182a90 2.1.75 2019-06-10 16:14:24 -07:00
Robert A. C
05087e569a Merge pull request #3950 from 89009332/patch-8
Update locale_zh_CN.properties
2019-06-09 20:16:38 -07:00
89009332
77bde61f4b Update locale_zh_CN.properties 2019-06-10 09:17:07 +08:00
nossr50
1297b45ef8 2.1.74 2019-06-09 07:08:26 -07:00
nossr50
83b55db4f8 2.1.73 2019-06-09 05:53:39 -07:00
nossr50
caec01e9fa 2.1.72 2019-06-09 05:12:56 -07:00
nossr50
2be67bae19 Fixed a NPE that could occur if Roll was disabled in coreskills.yml 2019-06-09 05:10:30 -07:00
nossr50
a677450d50 Fix NPE on server shutdown with no user data 2019-06-09 05:07:34 -07:00
nossr50
1d7919c050 2.1.71 2019-06-08 10:21:12 -07:00
nossr50
a132172507 You can no longer repair/salvage item stacks with more than one item 2019-06-08 10:20:22 -07:00
nossr50
549ef5734e Repair now always requires confirmation 2019-06-07 10:32:55 -07:00
nossr50
77fbf7f51e Salvage always asks for confirmation before breaking an item 2019-06-07 10:26:49 -07:00
nossr50
e37820d25e 2.1.70 2019-06-07 05:14:51 -07:00
nossr50
00bee60151 Prevent tool ready messages on shulker boxes 2019-06-07 05:13:37 -07:00
nossr50
7679afd3e0 Add new DatabaseAPI 2019-06-06 23:15:18 -07:00
nossr50
0b0408ef98 2.1.70 dev mode 2019-06-06 21:42:12 -07:00
nossr50
58cde322f5 Add shoutout to changelog 2019-06-06 01:53:59 -07:00
nossr50
0638f4c437 2.1.69 2019-06-05 23:50:39 -07:00
nossr50
426b2d27e7 Fix NPE for new data tracker 2019-06-05 21:43:07 -07:00
nossr50
3ce3ac5350 mcMMO should no longer lose a few minutes of player data from shutting
down the server
2019-06-05 21:11:44 -07:00
nossr50
4048ecc764 Fixing some imports 2019-06-02 22:42:03 -07:00
nossr50
f93a1aa151 2.1.68 - You can use food in the off hand with the diet abilities now. 2019-06-02 22:41:13 -07:00
nossr50
f6dc76719a Update changelog 2019-06-02 20:10:40 -07:00
Robert A. C
3035af97a4 Merge pull request #3940 from snake0053/master
Update locale_ja_JP.properties
2019-06-02 20:08:26 -07:00
SNAKE0053
3832ef3547 Update locale_ja_JP.properties 2019-06-02 05:50:26 +09:00
nossr50
8b4cea5dab Chimaera Wing should not be disabled by default 2019-06-01 12:26:11 -07:00
nossr50
9111590dc2 2.1.67 2019-05-31 19:46:14 -07:00
nossr50
ff1bb0deed 2.1.66 2019-05-29 11:01:02 -07:00
nossr50
4795143fca Fixed a memory leak in MySQL that would cause errors if users were
removed from the DB
2019-05-29 10:14:04 -07:00
nossr50
54eca5b8ba Fixed a bug where RetroMode would always use Linear formula 2019-05-27 20:13:22 -07:00
nossr50
11f669f8f4 2.1.64 2019-05-27 19:27:35 -07:00
nossr50
1cda612e86 Fixed a bug that would spam other players when someone used an ability 2019-05-27 19:25:36 -07:00
nossr50
50e4e971d9 Standard level scaling now mirrors RetroMode in a true 1:10 parity 2019-05-27 17:37:54 -07:00
nossr50
33dd34931c Add missing credits to snake for ja_JP 2019-05-21 15:24:45 -07:00
nossr50
ce230bf403 2.1.63 2019-05-21 13:28:22 -07:00
Robert A. C
d7b2a98723 Merge pull request #3934 from andris155/patch-6
Update locale_hu_HU.properties
2019-05-21 13:27:34 -07:00
nossr50
bfb48ccea3 Buffed Impact durability damage and fixed it 2019-05-21 13:13:17 -07:00
András Marczinkó
8cd39301a3 Update locale_hu_HU.properties 2019-05-21 19:11:28 +02:00
nossr50
321684e5f6 Fixed a bug where creative players could gain XP if they qualified for
early game XP boosts
2019-05-19 17:34:51 -07:00
Robert A. C
0b247dfae7 Merge pull request #3932 from snake0053/japanese-translate
Update locale_ja_JP.properties
2019-05-18 11:55:19 -07:00
SNAKE0053
d46fae2d1a Update locale_ja_JP.properties 2019-05-19 03:46:48 +09:00
nossr50
5d6690fc1a 2.1.62 2019-05-18 11:06:48 -07:00
nossr50
66425ba48d Add UUID to admin notifications 2019-05-18 10:59:52 -07:00
nossr50
3b3a36c64d Updating changelog 2019-05-18 10:51:21 -07:00
nossr50
2433ef5db7 Move admin notification toggle check 2019-05-18 10:47:37 -07:00
nossr50
663757352a Tweak xp rate admin notification messages 2019-05-18 10:45:50 -07:00
nossr50
1ced5d8ffc Confirmation messages for admin commands are now moved into the
convenience methods in NotificationManager
2019-05-18 10:40:06 -07:00
nossr50
04c6bd8750 Admin notifications convenience methods handled by NotificationManager 2019-05-18 10:09:45 -07:00
nossr50
b0dc41b4d9 Encapsulate admin notifications + add prefix 2019-05-18 08:12:32 -07:00
nossr50
92e6cedb14 This is why you don't program when you haven't slept in 20 hours 2019-05-18 08:04:55 -07:00
nossr50
447ce97f24 Send message to correct player 2019-05-18 08:03:39 -07:00
nossr50
2a471e409c Fix mistake in new admin notification locale 2019-05-18 08:02:16 -07:00
nossr50
08223bd320 Hotfix for repairing diamond, nerf guardian xp rate 2019-05-18 08:01:15 -07:00
nossr50
90480ca626 Add missing Magma_Block xp value 2019-05-17 15:45:07 -07:00
Robert A. C
56b031b99d Merge pull request #3930 from andris155/patch-1
Update locale_hu_HU.properties
2019-05-17 04:28:15 -07:00
nossr50
f3cbd450d0 Admin chat now sends useful information to admins/adminchat permissible
players
2019-05-17 04:27:27 -07:00
András Marczinkó
b176fd9bca Update locale_hu_HU.properties 2019-05-17 13:08:49 +02:00
nossr50
1c130aad42 2.1.61 - Some fixes + Exploit prevention 2019-05-16 13:30:43 -07:00
nossr50
734dfcdec5 Add toggle to disable event information on player join 2019-05-16 11:40:47 -07:00
nossr50
c8a32b8b38 Add toggle for event broadcasts - General.EventBroadcasts 2019-05-16 11:34:26 -07:00
nossr50
ed3ec3aa40 Add note about JP locale update 2019-05-16 11:29:45 -07:00
nossr50
88d8d0e6bb Fix the Double Drop string in the mining command 2019-05-16 11:20:51 -07:00
Robert A. C
a763f7f025 Merge pull request #3928 from snake0053/translate
Update locale_ja_JP.properties
2019-05-15 09:52:54 -07:00
SNAKE0053
357e9cc08f Update locale_ja_JP.properties 2019-05-16 01:11:25 +09:00
nossr50
2746bac86a 2.1.60 2019-05-14 16:16:24 -07:00
nossr50
95652efbaa Fix bonus drops on multi-block crops 2019-05-14 16:14:01 -07:00
nossr50
e70e5b04b5 Remove unecessary parameter 2019-05-14 15:29:15 -07:00
nossr50
34869914c4 Wolfs no longer kill themselves + tamed mob heart death message bug 2019-05-14 15:14:30 -07:00
nossr50
a53505ee0b Raised overfishing limit + improved tips 2019-05-13 08:53:22 -07:00
nossr50
ed1831da78 Fixed the wrong locale string being used for Mining DD 2019-05-11 12:45:00 -07:00
nossr50
ad550759e6 Fixed an incredibly rare and difficult to perform dupe bug 2019-05-08 10:24:34 -07:00
Robert A. C
984518d9f5 Merge pull request #3925 from SpiderRobotMan/patch-1
Fix small grammar mistake in README
2019-05-07 06:40:19 -07:00
Daniel Bethel
e0055eeead Fix small grammar mistake in README 2019-05-07 09:37:08 -04:00
nossr50
fe07261846 Very large update to the chinese language 2.1.56 2019-05-06 03:52:22 -07:00
Robert A. C
0cbb9b22a5 Merge pull request #3922 from 89009332/patch-7
Update locale_zh_CN.properties
2019-05-06 00:29:28 -07:00
89009332
31ccf2ca02 Update locale_zh_CN.properties 2019-05-06 15:25:41 +08:00
nossr50
23b7e11dd1 Fixed bugs that could occur while updating old DBs to have UUIDs 2019-05-05 16:41:42 -07:00
nossr50
588b6f3692 Fixed the furnace XP bug 2019-05-04 04:08:56 -07:00
nossr50
cdf6e607de 2.1.53 - Fixed critical XP bug 2019-05-03 15:04:40 -07:00
nossr50
f61917e8fe 2.1.52 2019-05-03 01:08:29 -07:00
Robert A. C
661564df36 Merge pull request #3914 from snake0053/master
Update locale_ja_JP.properties
2019-05-01 14:39:56 -07:00
SNAKE0053
0a7c852d89 Update locale_ja_JP.properties 2019-05-01 16:57:58 +09:00
nossr50
c1379f7ac6 2.1.51 - mikroskeem patch 2019-04-30 17:26:14 -07:00
nossr50
22ef134c84 Update changelog 2019-04-30 17:20:23 -07:00
Robert A. C
7f95956a01 Merge pull request #3911 from mikroskeem/feature/locale-reload-command
Add a command to reload locale
2019-04-30 11:33:08 -07:00
Mark Vainomaa
40ea101bf6 Add a command to reload locale 2019-04-30 21:19:55 +03:00
Robert A. C
0c8b001d05 Merge pull request #3910 from mikroskeem/feature/locales-in-plugin-data-folder
Try to load locale from plugin data folder first
2019-04-30 10:33:28 -07:00
Mark Vainomaa
5dc9c3f732 Note user that locale is loaded from locales directory 2019-04-30 20:14:42 +03:00
Mark Vainomaa
ec574a6b63 Cache raw messages from resource bundles
Should speed things up a bit as next message lookups are from single
hashmap instead
2019-04-30 17:58:37 +03:00
Mark Vainomaa
1ca48051ad Dedicated directory for locales 2019-04-30 14:08:43 +03:00
Mark Vainomaa
0f2e1ea740 Fall back to locale loaded from JAR when filesystem one doesn't have requested key 2019-04-30 09:29:04 +03:00
Mark Vainomaa
c2d4aeaf85 Try to load locale from plugin data folder first 2019-04-30 09:29:04 +03:00
nossr50
a496b1fd71 Dev mode 2019-04-29 18:54:00 -07:00
nossr50
ac8042f5d8 update gitignore 2019-04-28 06:52:16 -07:00
nossr50
f7eb06e80b 2.1.50 2019-04-27 06:39:06 -07:00
nossr50
f4214e2031 changelog update 2019-04-27 00:00:52 -07:00
Robert A. C
fd8712ee61 Merge pull request #3905 from Zed-I/patch-6
Added some missing blocks to Herbalism/Mining
2019-04-26 23:50:43 -07:00
BuildTools
f904ac7019 Fixing 1.13.2 bamboo material errors 2019-04-26 23:26:00 -07:00
Robert A. C
3c08af363f Merge pull request #3904 from OverCrave/master
Minor German Locale Update
2019-04-26 14:17:54 -07:00
Robert A. C
8295355a5d Merge pull request #3906 from Zed-I/patch-7
Added bamboo to the non-2 high plant blocks
2019-04-26 14:17:36 -07:00
Zed-I
aee81f87f5 Added bamboo to the non-2 high plant blocks 2019-04-26 18:16:42 +02:00
Zed-I
43d08ec21d Added some missing blocks to Herbalism/Mining
Added some missing entries to herbalism mining and one to combat.
Reasoning for each xp below:

bamboo - 10 Cant grow nearly as tall as kelp
cornflower - 150 same as the other 1 high tall flowers (excluding poppy, dandelion and allium)
lily_of_the_valley - 150 same as the other 1 high tall flowers (excluding poppy, dandelion and allium)
wither_rose - 500. Quite hard to harvest. You need a wither to kill a mob and have the block not get destroyed by the wither blast. If the block where to get destroyed the flower just drops instead and you dont get any xp.

The whole stone brick family get the same xp as nether brick, 50
stone_bricks
cracked_stone_bricks
mossy_stone_bricks
chiseled_stone_bricks

The rest of the prismarine family continue with the same xp as their main block, 70
prismarine_bricks
dark_prismarine
sea_lantern

wandering_trader  - 1.0 same as normal villagers
2019-04-26 18:08:12 +02:00
OverCrave
15e3ad8876 add missing translations 2019-04-26 17:58:23 +02:00
OverCrave
a583f557d7 remove unused translations 2019-04-26 17:49:35 +02:00
nossr50
0949931a61 2.1.49 2019-04-25 18:56:49 -07:00
nossr50
8efee39f7e Add sweet berrys to Herbalism XP tables (update configs) 2019-04-25 18:54:41 -07:00
Robert A. C
307b807b38 Merge pull request #3788 from LogGits/patch-5
Herbalism compatibility with PlayerAnimationEvent
2019-04-25 18:50:27 -07:00
nossr50
be8a2a4b07 Fixes #3902 - NPE in Inventory Listener 2019-04-25 18:49:18 -07:00
nossr50
30df57fa35 Fix async tool lower 2019-04-25 18:46:49 -07:00
nossr50
467025888c Fixed #3858 - Falling blocks in water not getting tracked 2019-04-25 18:28:24 -07:00
nossr50
21ecf959ea updating pom 2019-04-24 22:12:49 -07:00
nossr50
d1401d0cba change targetted API 2019-04-24 21:11:27 -07:00
Robert A. C
b5930fc35d Merge pull request #3897 from Zed-I/patch-4
Added excavation treasure to coarse dirt
2019-04-24 18:23:29 -07:00
Robert A. C
9a74ab77f4 Merge pull request #3900 from Leomixer17/patch-1
Update locale_it.properties
2019-04-24 18:22:45 -07:00
nossr50
18d698aad2 2.1.48 2019-04-24 16:40:28 -07:00
Leomixer17
3329a6c49e Update locale_it.properties 2019-04-24 23:09:03 +02:00
nossr50
c07a29ba86 mcMMO should work on both 1.13 and 1.14 now 2019-04-23 08:20:00 -07:00
nossr50
b36c4b56c3 first 5 levels in all skills are now much easier to level 2019-04-23 04:21:25 -07:00
nossr50
cee0025147 Bonus XP for early combat levels 2019-04-23 03:52:48 -07:00
nossr50
372ad1fac3 Experience settings for 114 are now under Experience_114 in experience.yml 2019-04-23 03:37:43 -07:00
nossr50
09cba965d3 Switch to GSON since mojang lib does not exist in 1.14 2019-04-23 02:12:22 -07:00
nossr50
d862f7f779 Tool lower task sync 2019-04-23 01:13:21 -07:00
nossr50
0adaa0ba66 PlayerNotification now marked async 2019-04-23 01:10:09 -07:00
nossr50
432a4aa330 1.14 is required for this version of mcMMO 2019-04-23 00:54:49 -07:00
nossr50
1c490cd463 1.14 Compatibility (this build won't run on 1.13 anymore) 2019-04-22 23:54:02 -07:00
Zed-I
570ab49e55 Added excavation treasure to coarse dirt
Added coarse dirt to all drops that dirt can drop.
Also added grass block to the quartz drop since there is no reason not to.
2019-04-20 16:35:01 +02:00
LogGits
3ed4228b72 Added imports 2019-02-16 05:40:49 +10:00
LogGits
93fefa031b Herbalism compatibility with PlayerAnimationEvent
This change in theory should make McMMO herbalism compatible with plugins that utilise PlayerAnimationEvent. The main reason that I want to add this is to fix `no swing` false positives in `AAC` (though didn't get to test this). 

I would appreciate if someone could test this PR and verify that its working without any issues. (I am busy for the next few days so I haven't been able to test it. In theory it looks like it should work. I could have checked if the event `isCancelled()` however, this doesn't seem necessary quite yet. Though, if it is needed I can add it 👍 

Please don't merge this unless it's confirmed completely working. I will find some time to test it out within the next few days.
2019-02-12 13:26:03 +10:00
160 changed files with 9201 additions and 3526 deletions

12
.github/FUNDING.yml vendored Normal file
View 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
View File

@@ -1,3 +1,6 @@
# bash stuff
*.sh
# Eclipse stuff
/.classpath
/.project

11
1 Normal file
View 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
#

View File

@@ -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
View File

@@ -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>.

View File

@@ -1,32 +1,32 @@
# mcMMO
## The RPG lovers mod
## The #1 RPG Mod for Minecraft
[![Translation status](https://translate.mcmmo.org/widgets/mcmmo/-/svg-badge.svg)](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
[![nossr50](http://www.gravatar.com/avatar/f2ee41eedfd645fb4a3a2c8f6cb1b18c.png)](https://github.com/nossr50)
Other mcMMO Projects
#### mcMMO Devs
[![nossr50](http://www.gravatar.com/avatar/f2ee41eedfd645fb4a3a2c8f6cb1b18c.png)](https://github.com/nossr50)
[![kashike](https://secure.gravatar.com/avatar/b5e86d6d443b957fd5cdee55501f3799.png)](https://github.com/kashike)
[![electronicboy](https://secure.gravatar.com/avatar/44759c38d311ce09596de6a2d5b88036.png)](https://github.com/electronicboy)
### Classic Maintainer
#### Classic Maintainer
[![t00thpick1](http://www.gravatar.com/avatar/ee23c7794a0c40120c3474287c7bce06.png)](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/

View File

@@ -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>

View 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();
}
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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"));

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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"));

View File

@@ -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

View File

@@ -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) {

View File

@@ -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>"));

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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) : ""));
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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); }

View File

@@ -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);}
}

View File

@@ -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);}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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)) {

View File

@@ -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.

View File

@@ -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();

View File

@@ -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");

View 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();
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -12,4 +12,5 @@ public class OldName extends FixedMetadataValue {
{
super(plugin, oldName);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,7 @@
package com.gmail.nossr50.datatypes.notifications;
public enum SensitiveCommandType {
XPRATE_MODIFY,
XPRATE_END,
MMOEDIT
}

View File

@@ -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() {

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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);
}
/**

View File

@@ -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);

View File

@@ -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()));

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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));

View File

@@ -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());
}
}
}

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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")));
}
}
/**

View File

@@ -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);
}
}

View File

@@ -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++;
}

View File

@@ -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)));

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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));

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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();

View File

@@ -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);
}
/**

View File

@@ -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)));
}
}

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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);

View File

@@ -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