Compare commits

..

138 Commits

Author SHA1 Message Date
3440c117be Aktualizovat MMOCore-API/pom.xml 2024-09-12 14:30:00 +02:00
edae411983 revert 480ceb18f8
revert Aktualizovat pom.xml
2024-09-12 14:26:03 +02:00
480ceb18f8 Aktualizovat pom.xml 2024-09-12 12:53:49 +02:00
Jules
0e365c58c8 Fixed quest GUI showing dupes 2024-09-07 18:21:26 +02:00
Jules
88760db078 Fixes Parties throwing an error when players log out 2024-09-07 18:07:53 +02:00
Jules
cdfbb6ef09 Fixes times-claimed not being properly cleared when using /mmocore admin reset all <player> 2024-09-07 17:52:18 +02:00
Jules
6d71c775f6 Fixes 'hide-flags' not working in GUI's 2024-08-27 18:39:45 -07:00
Jules
22e5947112 Fixed an issue with player loading when guild data file is missing 2024-08-26 12:56:00 -07:00
Jules
ed9a9353aa Support for MM 5.7.1 2024-08-14 15:00:27 -07:00
Jules
d792b84219 Fixes custom model data of skill icons in 1.20.6+ 2024-08-14 13:48:58 -07:00
Jules
84e587ba0b Fixes skill tree points consumption dupe bug 2024-08-14 11:36:10 -07:00
Jules
15e759a70b Improves default skill tree node lore 2024-08-14 11:21:57 -07:00
Jules
cba1631d44 Fixes points spent in skill tree not updating when unlocking a node. 2024-08-14 11:21:55 -07:00
Jules
62e1ace7b5 Fixed loot chests not loading. Requires latest ML 2024-07-27 20:59:07 -07:00
Jules
cb92620b1b Skill config files now only generate once and never again 2024-07-27 15:01:27 -07:00
Jules
d0b27c84b2 Fixed MythicDungeons throwing an error message 2024-07-26 16:39:15 -07:00
Jules
fa40f3707d Fixed default skill configs not loading 2024-07-26 16:05:31 -07:00
Jules
25ca6e32a4 Fixed log stripping giving no exp (woodcutting professions) 2024-07-26 15:47:35 -07:00
Jules
61efa519c2 Minor performance improvement for custom mining 2024-07-26 15:32:48 -07:00
Jules
b80cda490c New capacity and shuffle option for block/fishing drop tables 2024-07-26 15:03:40 -07:00
Jules
5759f78924 Fixed plugin crash when inputting an invalid party module 2024-07-26 14:15:41 -07:00
Jules
44f846f591 New config folders for storing and organizing skills, waypoints, loot chests, exp tables, attributes. 2024-07-26 14:03:51 -07:00
Jules
d248f11a0e Support for MythicDungeons & Heroes party systems 2024-07-26 14:02:50 -07:00
Jules
25509be21b Compiles again on Java 8 2024-07-26 12:12:33 -07:00
Jules
dcd1b55099 More 1.21 fixes 2024-07-25 21:06:32 -07:00
Jules
d7ff655c86 Added 1.20+ attributes to default configs 2024-07-25 18:12:25 -07:00
Jules
94139bbe75 Option to hide tooltips from GUI items 2024-07-24 19:15:13 -07:00
Jules
6398872a9e Console warnings changed to info 2024-07-13 01:47:05 -07:00
Jules
0553f0bfe5 Fixed an issue with chest tier rolls 2024-07-13 01:12:30 -07:00
Jules
040e7360df Fixed vanilla-exp in fishing drop tables. experience is no longer necessary 2024-06-20 19:27:09 -07:00
Jules
ee2793d04d Fixed party buffs stacking when using /mmocore reload 2024-06-20 19:14:19 -07:00
Jules
1c415c0511 Fixed player attribute data not loading from SQL database 2024-06-19 20:44:48 -07:00
Jules
1661b8d6eb Fixed passive skills being castable thru combos 2024-06-14 00:43:30 -07:00
Jules
e17a8f903a Now built against Paper 1.20.6 2024-06-13 01:02:36 -07:00
Jules
151eebcde4 Changed placeholders in default player-stats.yml to reduce possible confusion 2024-06-13 00:19:41 -07:00
Jules
2f70c86b7e Fixed collecting stats applying even when a block cannot be broken 2024-06-13 00:10:50 -07:00
Jules
e2c9074084 /mmocore admin reset all now properly resets skills 2024-06-04 01:05:51 -07:00
Jules
fb84e2f96e Fixed NPE when clicking certain skill tree nodes 2024-06-03 22:48:11 -07:00
Jules
5c2bd9b637 Removed use of deprecated API 2024-05-24 20:02:56 -07:00
Jules
b150817964 Default configs no longer use %player% 2024-05-24 19:42:08 -07:00
Jules
7edd17962c Improved some config error messages 2024-05-24 19:41:49 -07:00
Jules
daa052cabd Fixed harmless NPE in the party UI 2024-05-24 18:51:58 -07:00
Jules
bded64d44a Fixed level_skill_tree_node sound not working 2024-05-24 18:46:40 -07:00
Jules
da0e16f1b3 Fixed skill buffs temporarily not working on login 2024-05-24 16:27:00 -07:00
Jules
93317c3e39 New %mmocore_id_bound_<slot>% PAPI placeholder 2024-05-24 14:50:02 -07:00
Jules
2a17aa1a58 Fixed quest triggers for the next objective not triggering 2024-05-23 23:47:13 -07:00
Jules
6e739b3787 Fixed some exp sources working across some classes/professions 2024-05-23 23:34:39 -07:00
Jules
0e5ac9ce3a Fixed damagedealt{} exp source crashing the server when giving a cookie to a parrot 2024-05-23 18:33:05 -07:00
Jules
dad9db0edd Fixed colors not working on profession/class holograms 2024-05-23 16:43:06 -07:00
Jules
bbe7d2b9b8 Fixed exp holograms always showing for certain professions 2024-05-23 16:22:01 -07:00
Jules
d9fa24a730 Fixed custom fishing not taking into account Unbreaking enchant 2024-05-23 15:59:10 -07:00
Jules
caf6b38363 New placeholder for getting parameter value of bound skill 2024-05-23 15:13:54 -07:00
Jules
4f832195e5 Support for latest ML math formula parser 2024-05-13 22:49:17 -07:00
Jules
a8e5a3eaec New features for skill combos: new sound, key to quit casting, ability to keep casting after a combo 2024-05-09 22:09:15 -07:00
Jules
3df5676304 Fixed permanent skills not triggering when locked/unlocked 2024-05-09 18:01:36 -07:00
Jules
952b543b45 Can no longer bind passive permanent skills 2024-05-09 17:17:19 -07:00
Jules
2d08d4403b Compatibility with new MythicDungeons API 2024-05-01 22:42:29 -07:00
Jules
be62d73fcb Fixed a bug with '/mmocore admin exportdata' 2024-04-25 19:49:48 -07:00
Jules
7593226610 Fixed waypoint default warp time not working. Waypoint warp time bypass permission is no longer a default op permission. 2024-04-25 17:55:48 -07:00
Jules
974dbe1eac Default backstab lore now includes skill cooldown 2024-04-24 22:49:07 -07:00
Jules
9546a95a59 Debug messages duh 2024-04-24 21:59:27 -07:00
Jules
3248ffe541 Fixed /skills not working when a class has too many slots configured 2024-04-23 22:54:49 -07:00
Jules
176657e3fd Changed outdated config.yml comment 2024-04-18 17:55:12 -07:00
Jules
991397606f Fixed MMOInv health buffs not working on login 2024-04-18 15:15:24 -07:00
Jules
3962c73515 Reduced confusion in default fishing profession config 2024-04-17 23:46:01 -07:00
Jules
a33d4061cd Fixed %mmocore_attribute_points_spent_<attr>% 2024-04-14 14:14:18 -07:00
Jules
8a017a6109 Fixed waypoint lore not working in UI 2024-04-10 07:13:21 -07:00
Jules
3b1ab33e2f Fixed Profile API version 2024-04-07 00:01:39 -07:00
Jules
558a5f5a01 Added one option to prevent players from upgrading skills using the skill UI 2024-04-06 23:31:27 -07:00
Jules
d4bea3dba7 Fixed /rpg reload increasing player skill levels when using skill trees 2024-04-06 23:15:33 -07:00
Jules
3231a69bf3 Removed <message_empty_...> user feedback on empty config message 2024-04-06 21:48:29 -07:00
Jules
8a8ec67f14 Fixed an on-login issue with non-vanilla resources 2024-03-29 00:47:03 +01:00
Jules
79d763718d More default settings for SQL data storage 2024-02-21 18:07:11 +01:00
Jules
5e466dd8c2 Improved handling of async tasks (autosave, on server shutdown) 2024-02-21 13:11:23 +01:00
Jules
1e370864cd Improved handling of async tasks (autosave, on server shutdown) 2024-02-21 13:05:14 +01:00
Jules
223a8fadb7 Added missing message to default messages.yml 2024-02-20 17:54:37 +01:00
Jules
13e808ba37 Fixed on-login health issue 2024-02-20 13:37:29 +01:00
Jules
0fd1f6bbce Removed use of deprecated methods 2024-02-20 00:27:54 +01:00
Jules
a653ffbfe1 Fixed resources not saving 2024-02-19 17:05:18 +01:00
Jules
1a1346d3bc Fixed NPE with guild gui 2024-02-19 12:52:44 +01:00
Jules
d5ffd04117 Fixed health issue on login 2024-02-11 23:36:21 +01:00
Jules
68fbfc3576 Support for Uclans API 7 2024-02-11 17:17:35 +01:00
Jules
e4f1400a31 Hotfix for on-login issue 2023-11-27 11:25:56 +01:00
Jules
47cd5c15c9 Fixed login trigger type not working on class skills & scripts 2023-11-27 00:11:12 +01:00
Jules
947637127e Fixed attributes not reseting with /mmocore reload 2023-11-26 17:42:45 +01:00
Jules
f6d094fb6e Fixed DROP forcing a left click with key combo casting 2023-11-25 16:56:06 +01:00
Jules
36b5be13be Fixed empty messages sent to chat/action bar 2023-11-25 15:23:34 +01:00
Jules
c211c05a67 Fixed item repair exp dupe glitch 2023-11-25 15:07:09 +01:00
Jules
22744b181b Fixed 1.20+ skull texture application 2023-11-06 02:56:07 +01:00
Jules
3c043babd5 Fixed stuck player messages 2023-11-06 00:37:20 +01:00
Jules
7d93031aeb Better handling of fake events 2023-11-06 00:28:03 +01:00
Jules
44d0785f73 Increased event priorities of exp sources 2023-11-05 23:21:58 +01:00
Jules
c8f1f9fefd Update README.md 2023-11-04 15:29:34 +01:00
Jules
76b0d51b2a Fixed a bug when logging off while in combat 2023-10-27 20:29:42 +02:00
Jules
71d0f7219f Fixed placeholders with proxy-based profiles 2023-10-22 02:11:45 +02:00
Jules
f79a66df80 Merge remote-tracking branch 'origin/master' 2023-10-22 01:07:11 +02:00
Jules
29a88a59fe Fixed skill upgrading gui 2023-10-22 01:06:57 +02:00
Jules
ede2991066 Fixed skill scroller, added option for action bar format 2023-10-22 01:03:28 +02:00
Jules
e784230ce7 Combos now ignore unused keys 2023-10-22 00:42:39 +02:00
Jules
88d02f140f Skill casting timeout, fixed casting particles 2023-10-22 00:03:35 +02:00
Jules
6565d7106b Fixed skill buffs not applying on cooldown & other mods 2023-10-16 01:40:38 +02:00
Jules
160c5d5699 Fixed exp resetting on death 2023-10-15 18:43:06 +02:00
Jules
dbe5aa5fab Fixed attributes not reloading when switching profiles 2023-10-14 15:30:36 +02:00
Jules
7810e3695b Fixed attributes not reseting 2023-10-14 12:42:27 +02:00
Jules
bc9f5ac6f8 Tree node will get latest level lore available 2023-10-08 01:48:36 +02:00
Jules
6bcfc0aad8 Added 'name' placeholder to skill tree nodes 2023-10-08 01:25:10 +02:00
Jules
90706ae97c Default color for skill tree node names 2023-10-08 01:22:44 +02:00
Jules
35484b5893 Removed "size" from skill trees 2023-10-08 00:36:07 +02:00
Jules
2d779d19bf Fixed an issue w/ casting mode on logout 2023-10-02 00:01:49 +02:00
Jules
d6ec76573c Fixed issue with quest list custom model data 2023-10-01 16:17:51 +02:00
Jules
5c185476dd Enter/quit combat skill triggers 2023-09-30 15:28:37 +02:00
Jules
b8e02c945f 1.20.2 support 2023-09-30 13:11:30 +02:00
Jules
760f7f6b0b Commented out nonsense 2023-09-18 13:06:09 +02:00
Taner
ed97665e27
Update example-drop-tables.yml to remove ;period= 2023-09-10 22:25:24 +00:00
Ka0rX
c04d46d662 Fixed issue #909 & #926 related to a bug when making a friend request. Make sure to reload messages.yml to fix this bug ! 2023-09-07 11:41:07 +01:00
Ka0rX
36f99a6cb4 Bug Fixing 2023-09-07 11:38:36 +01:00
Ka0rX
2f91a58a26 Fixed issue #922 related to stats related to class/profession being lost when reloading/quitting. 2023-09-06 21:48:58 +01:00
Ka0rX
09ea9ab3a9 Fixed issue #917 about a bug when unbinding a skill from a slot while being in a key combo. 2023-09-06 18:13:42 +01:00
Ka0rX
a794f81a5f Fixed issue #920 about placeholder API placeholders not working in skill-list.yml & fixed other bugs related to selected item in skill-list 2023-09-06 16:37:40 +01:00
Ka0rX
40b59e4b70 Added the possibilty to have force a specific item to appear when a skill is bound to a slot for the skill UI. 2023-09-06 15:18:25 +01:00
Ka0rX
1607b0f773 Fix for subclass select UI 2023-09-06 15:17:28 +01:00
Ka0rX
2a802e40bf Fixed issue #932 about Out of Mana message not being sent when it should. 2023-09-05 21:56:52 +01:00
Ka0rX
3fa278a920 Bug Fix 2023-09-05 21:07:30 +01:00
Ka0rX
55953d41ce Fixed issue #931 related to waypoints lore being inverted in the UI. Now you can use the placeholder {lore} in the UI config to display the waypoint specific lore. 2023-09-05 19:32:08 +01:00
Ka0rX
7b45b54793 Little enhancement for class select GUI. 2023-09-05 18:59:28 +01:00
Jules
3efc5b86fb More proxy profiles fixing 2023-08-27 22:26:04 +02:00
Jules
d050b708cc Changed player data setup event priority 2023-08-27 19:31:12 +02:00
Jules
730dd80418 Updated dependency nb 2023-08-27 18:20:06 +02:00
Jules
c0a27f5cc0 Fixed force-class-selection when using proxy-based profiles 2023-08-27 18:16:13 +02:00
Jules
b12c6142a3 Clarified default config.yml 2023-08-27 17:44:00 +02:00
Jules
e854663cff Updates to ProfileAPI 1.1 2023-08-27 16:55:09 +02:00
Jules
e98a31bdbc Useless import 2023-08-26 20:42:55 +02:00
Jules
10fb433291 Fixed NPE when repairing null item 2023-08-16 22:56:46 +02:00
Jules
13ada015c9 Fixes player combat updating on fake damage events 2023-07-24 11:25:45 +02:00
Ka0rX
3d48a7e7c0 Merge remote-tracking branch 'origin/master' 2023-07-16 13:58:36 +01:00
Ka0rX
1dc2d31360 Skill Tree Display Fix. 2023-07-14 17:35:52 +01:00
Jules
fadb7d7762 Hopefully fixed compile issue 2023-07-11 19:49:28 +02:00
Jules
c859c0d2e0 Support for latest MythicLib 2023-07-11 19:44:06 +02:00
267 changed files with 4858 additions and 4616 deletions

View File

@ -1,15 +0,0 @@
<settings>
<servers>
<server>
<id>nexus</id>
<username>${env.M2_REPO_USER}</username>
<password>${env.M2_REPO_PASS}</password>
<blocked>false</blocked>
</server>
<server>
<id>lumine</id>
<username>${env.M2_REPO_USER}</username>
<password>${env.M2_REPO_PASS}</password>
</server>
</servers>
</settings>

View File

@ -85,13 +85,13 @@
</repository>
<repository>
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<repository>
@ -104,16 +104,39 @@
<id>simonsators Repo</id>
<url>https://simonsator.de/repo</url>
</repository>
<!-- Heroes Repository
<repository>
<id>herocraft</id>
<url>https://nexus.hc.to/content/repositories/pub_releases</url>
</repository> -->
</repositories>
<dependencies>
<!-- Spigot API -->
<!-- Paper API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-1.17.1</artifactId>
<version>dev</version>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Extra libs -->
@ -129,20 +152,30 @@
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Plugin dependencies -->
<dependency>
<groupId>io.lumine</groupId>
<artifactId>Mythic-Dist</artifactId>
<version>5.0.1</version>
<version>5.7.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.playavalon</groupId>
<artifactId>MythicDungeons</artifactId>
<version>1.4.1-SNAPSHOT-CUSTOM</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>fr.phoenixdevt</groupId>
<artifactId>Profile-API</artifactId>
<version>1.0</version>
<version>1.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
@ -152,6 +185,7 @@
<artifactId>placeholderapi</artifactId>
<version>2.9.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -159,6 +193,7 @@
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -166,6 +201,18 @@
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.2-SNAPSHOT</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -173,6 +220,7 @@
<artifactId>ProtocolLib</artifactId>
<version>4.8.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -180,6 +228,7 @@
<artifactId>Citizens</artifactId>
<version>2.0.30</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Party plugins -->
@ -188,6 +237,7 @@
<artifactId>DevelopmentPAFSpigot</artifactId>
<version>1.0.65</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -195,6 +245,7 @@
<artifactId>Party-and-Friends-MySQL-Edition-Spigot-API</artifactId>
<version>1.5.4-RELEASE</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -202,6 +253,7 @@
<artifactId>spigot-party-api-for-party-and-friends</artifactId>
<version>1.0.4-RELEASE</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -209,13 +261,7 @@
<artifactId>OBTeam</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.playavalon</groupId>
<artifactId>DungeonParties</artifactId>
<version>1.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -223,6 +269,7 @@
<artifactId>Parties</artifactId>
<version>3.1.14</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -230,6 +277,7 @@
<artifactId>mcMMO</artifactId>
<version>2.1.209</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -237,6 +285,15 @@
<artifactId>DungeonsXL</artifactId>
<version>0.18-SNAPSHOT-1149</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.herocraftonline</groupId>
<artifactId>Heroes</artifactId>
<version>1.9.22</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -244,6 +301,7 @@
<artifactId>Bedrock</artifactId>
<version>1.2.5</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -251,6 +309,7 @@
<artifactId>Dungeons</artifactId>
<version>1.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Quest Plugins -->
@ -259,24 +318,28 @@
<artifactId>Quests</artifactId>
<version>4.4.1-b340</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>fr.skytasul.quests</groupId>
<artifactId>BeautyQuests</artifactId>
<version>0.19.5</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.guillaumevdn</groupId>
<artifactId>QuestCreator</artifactId>
<version>6.39.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.guillaumevdn</groupId>
<artifactId>GCore</artifactId>
<version>8.39.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Guild plugins -->
@ -289,10 +352,11 @@
</dependency>
<dependency>
<groupId>me.ulrich</groupId>
<artifactId>UltimateClans</artifactId>
<version>6.0.2</version>
<groupId>com.github.UlrichBR</groupId>
<artifactId>UClansV7-API</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -300,6 +364,7 @@
<artifactId>Guilds</artifactId>
<version>3.5.6.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
@ -307,6 +372,7 @@
<artifactId>Kingdoms</artifactId>
<version>1.15.5</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>

View File

@ -4,6 +4,7 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import io.lumine.mythic.lib.metrics.bukkit.Metrics;
import io.lumine.mythic.lib.util.MMOPlugin;
import io.lumine.mythic.lib.version.SpigotPlugin;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.AttributeModifier;
@ -49,19 +50,18 @@ import net.Indyuce.mmocore.script.mechanic.ManaMechanic;
import net.Indyuce.mmocore.script.mechanic.StaminaMechanic;
import net.Indyuce.mmocore.script.mechanic.StelliumMechanic;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import net.Indyuce.mmocore.spawnpoint.SpawnPoint;
import net.Indyuce.mmocore.skill.trigger.MMOCoreTriggerType;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.event.EventPriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.logging.Level;
public class MMOCore extends JavaPlugin {
public class MMOCore extends MMOPlugin {
public static MMOCore plugin;
public final WaypointManager waypointManager = new WaypointManager();
public final SoundManager soundManager = new SoundManager();
@ -81,10 +81,6 @@ public class MMOCore extends JavaPlugin {
public final MMOLoadManager loadManager = new MMOLoadManager();
public final RestrictionManager restrictionManager = new RestrictionManager();
public final SkillTreeManager skillTreeManager = new SkillTreeManager();
public final SpawnPointManager spawnPointManager = new SpawnPointManager();
public final PluginMessageManager pluginMessageManager = new PluginMessageManager();
public final StatManager statManager = new StatManager();
public final GuildDataManager nativeGuildManager = new YAMLGuildDataManager();
public final PlayerDataManager playerDataManager = new PlayerDataManager(this);
@ -121,6 +117,7 @@ public class MMOCore extends JavaPlugin {
MythicLib.plugin.getEntities().registerRelationHandler(new PartyRelationHandler());
MythicLib.plugin.getEntities().registerRelationHandler(new GuildRelationHandler());
MythicLib.plugin.getModifiers().registerModifierType("attribute", configObject -> new AttributeModifier(configObject));
MMOCoreTriggerType.registerAll();
// Custom scripts
MythicLib.plugin.getSkills().registerMechanic("mana", config -> new ManaMechanic(config));
@ -160,7 +157,7 @@ public class MMOCore extends JavaPlugin {
}
if (getConfig().isConfigurationSection("default-playerdata"))
dataProvider.getDataManager().loadDefaultData(getConfig().getConfigurationSection("default-playerdata"));
playerDataManager.loadDefaultData(getConfig().getConfigurationSection("default-playerdata"));
if (Bukkit.getPluginManager().getPlugin("Vault") != null) economy = new VaultEconomy();
@ -186,10 +183,6 @@ public class MMOCore extends JavaPlugin {
MMOCore.plugin.getLogger().log(Level.INFO, "Hooked onto MythicMobs");
}
//Registers plugin message channel to teleport players to other servers.
MMOCore.plugin.getServer().getMessenger().registerOutgoingPluginChannel(MMOCore.plugin, "BungeeCord");
/*
* Resource regeneration. Must check if entity is dead otherwise regen
* will make the 'respawn' button glitched plus HURT entity effect bug
@ -206,21 +199,6 @@ public class MMOCore extends JavaPlugin {
}
}.runTaskTimer(MMOCore.plugin, 100, 20);
new BukkitRunnable() {
@Override
public void run() {
for (PlayerData playerData : PlayerData.getAll()) {
for (SpawnPoint spawnPoint : spawnPointManager.getAll()) {
if (!playerData.hasUnlocked(spawnPoint) && spawnPoint.matchesCondition(playerData)) {
playerData.unlock(spawnPoint);
}
}
}
}
}.runTaskTimer(this, 5*20L, 5 * 20L);
/*
* For the sake of the lord, make sure they aren't using MMOItems Mana and
* Stamina Addon...This should prevent a couple error reports produced by people
@ -229,8 +207,6 @@ public class MMOCore extends JavaPlugin {
if (Bukkit.getPluginManager().getPlugin("MMOMana") != null) {
getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "MMOCore is not meant to be used with MMOItems ManaAndStamina");
getLogger().log(Level.SEVERE, ChatColor.DARK_RED + "Please read the installation guide!");
Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] MMOCore is not meant to be used with MMOItems ManaAndStamina");
Bukkit.broadcastMessage(ChatColor.DARK_RED + "[MMOCore] Please read the installation guide!");
return;
}
@ -240,10 +216,10 @@ public class MMOCore extends JavaPlugin {
try {
String partyPluginName = UtilityMethods.enumName(getConfig().getString("party-plugin"));
PartyModuleType moduleType = PartyModuleType.valueOf(partyPluginName);
Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed");
Validate.isTrue(moduleType.isValid(), "Plugin " + moduleType.getPluginName() + " is not installed");
partyModule = moduleType.provideModule();
getLogger().log(Level.WARNING, "Hooked parties onto " + moduleType.getPluginName());
} catch (RuntimeException exception) {
getLogger().log(Level.INFO, "Hooked parties onto " + moduleType.getPluginName());
} catch (Throwable exception) {
getLogger().log(Level.WARNING, "Could not initialize party module: " + exception.getMessage());
partyModule = new MMOCorePartyModule();
}
@ -254,7 +230,7 @@ public class MMOCore extends JavaPlugin {
GuildModuleType moduleType = GuildModuleType.valueOf(pluginName);
Validate.isTrue(moduleType.isValid(), "Plugin '" + moduleType.name() + "' is not installed");
guildModule = moduleType.provideModule();
getLogger().log(Level.WARNING, "Hooked guilds onto " + moduleType.getPluginName());
getLogger().log(Level.INFO, "Hooked guilds onto " + moduleType.getPluginName());
} catch (RuntimeException exception) {
getLogger().log(Level.WARNING, "Could not initialize guild module: " + exception.getMessage());
guildModule = new MMOCoreGuildModule();
@ -262,9 +238,8 @@ public class MMOCore extends JavaPlugin {
// Skill casting
try {
SkillCastingMode mode = SkillCastingMode.valueOf(UtilityMethods.enumName(getConfig().getString("skill-casting.mode")));
final SkillCastingMode mode = SkillCastingMode.valueOf(UtilityMethods.enumName(getConfig().getString("skill-casting.mode")));
mode.setCurrent(getConfig().getConfigurationSection("skill-casting"));
} catch (RuntimeException exception) {
getLogger().log(Level.WARNING, "Could not load skill casting: " + exception.getMessage());
}
@ -282,10 +257,10 @@ public class MMOCore extends JavaPlugin {
* that after registering all the professses otherwise the player datas can't
* recognize what profess the player has and professes will be lost
*/
playerDataManager.initialize(EventPriority.NORMAL, EventPriority.NORMAL);
playerDataManager.initialize(EventPriority.LOW, EventPriority.NORMAL);
// load guild data after loading player data
dataProvider.getGuildManager().load();
nativeGuildManager.load();
// Toggleable Commands
ToggleableCommand.register();
@ -300,12 +275,11 @@ public class MMOCore extends JavaPlugin {
public void onDisable() {
// Save guild info
for (Guild guild : dataProvider.getGuildManager().getAll())
dataProvider.getGuildManager().save(guild);
for (Guild guild : nativeGuildManager.getAll())
nativeGuildManager.save(guild);
// Close MySQL data provider (memory leaks)
playerDataManager.saveAll(false);
playerDataManager.getDataHandler().close();
// Close player data manager
playerDataManager.close();
// Reset active blocks
mineManager.resetRemainingBlocks();
@ -329,8 +303,6 @@ public class MMOCore extends JavaPlugin {
configManager = new ConfigManager();
if (clearBefore)
MythicLib.plugin.getSkills().initialize(true);
skillManager.initialize(clearBefore);
mineManager.initialize(clearBefore);
partyManager.initialize(clearBefore);
@ -353,8 +325,6 @@ public class MMOCore extends JavaPlugin {
requestManager.initialize(clearBefore);
soundManager.initialize(clearBefore);
configItems.initialize(clearBefore);
spawnPointManager.initialize(clearBefore);
pluginMessageManager.initialize(clearBefore);
//Needs to be loaded after the class manager.
InventoryManager.load();

View File

@ -2,47 +2,143 @@ package net.Indyuce.mmocore.api;
import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ConfigMessage {
private final List<String> messages;
private final String key;
private final List<String> lines = new ArrayList<>();
private final boolean papiPlaceholders, actionbar, raw;
public ConfigMessage(String key) {
messages = MMOCore.plugin.configManager.getMessage(key);
private ConfigMessage(@NotNull String key) {
this.key = key;
// Initialize message list
final Object obj = MMOCore.plugin.configManager.getMessageObject(key);
if (obj == null) lines.add("<message_not_found:'" + key + "'>");
else if (obj instanceof List<?>) lines.addAll((List<String>) obj);
else {
final String tostr = obj.toString();
if (!tostr.isEmpty()) lines.add(tostr);
}
// Does message include placeholders
boolean hasPlaceholders = false;
for (String str : lines)
if (str.contains("%")) {
hasPlaceholders = true;
break;
}
this.papiPlaceholders = hasPlaceholders;
// Is message for action bar
actionbar = !lines.isEmpty() && lines.get(0).startsWith("%");
if (actionbar) lines.set(0, lines.get(0).substring(1));
// Are messages raw (JSON format)
raw = !lines.isEmpty() && (lines.get(0).startsWith("{") || lines.get(0).startsWith("["));
}
public ConfigMessage addPlaceholders(String... placeholders) {
for (int n = 0; n < messages.size(); n++) {
String line = messages.get(n);
/**
* Useful for things like indicators or specific lore lines
* which are string tags not requiring more than one string
* object. An empty return value is accepted as some features
* do require the ability to fully remove text.
*
* @return First line of message, if it exists.
*/
@NotNull
public String asLine() {
return lines.isEmpty() ? "" : lines.get(0);
}
@NotNull
public List<String> getLines() {
return lines;
}
@NotNull
public ConfigMessage addPlaceholders(@NotNull Object... placeholders) {
for (int n = 0; n < lines.size(); n++) {
String line = lines.get(n);
// Apply placeholders to string
for (int j = 0; j < placeholders.length - 1; j += 2) {
String placeholder = placeholders[j];
if (line.contains("{" + placeholder + "}"))
line = line.replace("{" + placeholder + "}", placeholders[j + 1]);
final String placeholder = String.valueOf(placeholders[j]);
line = line.replace("{" + placeholder + "}", String.valueOf(placeholders[j + 1]));
}
messages.set(n, line);
lines.set(n, line);
}
return this;
}
public void send(CommandSender sender) {
messages.forEach(line -> sender.sendMessage(format(sender, line)));
@Deprecated
public void sendAsJSon(Player player) {
send(player);
}
public void send(Player player) {
for (String line : lines) send(player, line);
}
public void send(Collection<? extends Player> players) {
players.forEach(player -> messages.forEach(line -> player.sendMessage(format(player, line))));
for (Player player : players) for (String line : lines) send(player, line);
}
public void sendAsJSon(Player player) {
messages.forEach(line -> MythicLib.plugin.getVersion().getWrapper().sendJson(player, format(player, line)));
/**
* Sends a line of text to a target player
*
* @param player Player to send message to. His player
* data is not necessarily fully loaded
* @param messageFormat Raw/normal message to send
*/
private void send(@NotNull Player player, String messageFormat) {
Validate.notNull(player, "Player cannot be null");
final String rawMessage = format(player, messageFormat);
final PlayerData playerData = PlayerData.has(player) ? PlayerData.get(player) : null;
// Handle special case with player data + action bar
if (playerData != null && playerData.isOnline() && actionbar) {
playerData.displayActionBar(rawMessage, raw);
return;
}
// Normal sender
if (this.raw) {
if (actionbar) MythicLib.plugin.getVersion().getWrapper().sendActionBarRaw(player, rawMessage);
else MythicLib.plugin.getVersion().getWrapper().sendJson(player, rawMessage);
} else {
if (actionbar)
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(rawMessage));
else player.sendMessage(rawMessage);
}
}
private String format(CommandSender sender, String input) {
String str = MythicLib.plugin.parseColors(input);
return sender instanceof Player ? MMOCore.plugin.placeholderParser.parse((OfflinePlayer) sender, str) : str;
@NotNull
private String format(@Nullable Player player, String input) {
input = MythicLib.plugin.parseColors(input);
if (!papiPlaceholders || player == null) return input; // Optimization
return MMOCore.plugin.placeholderParser.parse(player, input);
}
@NotNull
public static ConfigMessage fromKey(@NotNull String key, Object... placeholders) {
Validate.notNull(MMOCore.plugin.configManager, "MMOCore has not finished enabling");
final ConfigMessage message = new ConfigMessage(key);
if (placeholders.length != 0) message.addPlaceholders(placeholders);
return message;
}
}

View File

@ -25,7 +25,7 @@ public class MMOCoreAPI {
}
public PlayerData getPlayerData(OfflinePlayer player) {
return PlayerData.get(player.getUniqueId());
return PlayerData.get(player);
}
public boolean isInSameParty(Player player1, Player player2) {

View File

@ -12,8 +12,8 @@ public enum SoundEvent {
SELECT_CLASS,
LEVEL_ATTRIBUTE,
RESET_ATTRIBUTES,
RESET_SKILLS
,NOT_ENOUGH_POINTS,
RESET_SKILLS,
NOT_ENOUGH_POINTS,
CANCEL_QUEST,
START_QUEST,
CLOSE_LOOT_CHEST,

View File

@ -24,6 +24,7 @@ public class SoundObject {
private final float volume;
private final float pitch;
@Deprecated
public SoundObject(String input) {
String[] split = input.split(",");
@ -43,6 +44,7 @@ public class SoundObject {
pitch = split.length > 2 ? Float.parseFloat(split[2]) : 1;
}
@Deprecated
public SoundObject(ConfigurationSection config) {
String input = config.getString("sound");
@ -65,32 +67,39 @@ public class SoundObject {
* @return If this object is custom a custom sound, potentially
* from a resource pack
*/
@Deprecated
public boolean isCustom() {
return sound == null;
}
@Nullable
@Deprecated
public Sound getSound() {
return sound;
}
@Nullable
@Deprecated
public String getKey() {
return key;
}
@Deprecated
public float getVolume() {
return volume;
}
@Deprecated
public float getPitch() {
return pitch;
}
@Deprecated
public void playTo(Player player) {
playTo(player, volume, pitch);
}
@Deprecated
public void playTo(Player player, float volume, float pitch) {
if (isCustom())
player.playSound(player.getLocation(), key, volume, pitch);
@ -98,10 +107,12 @@ public class SoundObject {
player.playSound(player.getLocation(), sound, volume, pitch);
}
@Deprecated
public void playAt(Location loc) {
playAt(loc, volume, pitch);
}
@Deprecated
public void playAt(Location loc, float volume, float pitch) {
if (isCustom())
loc.getWorld().playSound(loc, key, volume, pitch);

View File

@ -43,7 +43,7 @@ public class BlockInfo {
options.put(option, config.getBoolean("options." + key));
} catch (IllegalArgumentException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING,
"Could not load option '" + key + "' from block info '" + block.generateKey() + "': " + exception.getMessage());
"Could not load option '" + key + "' from block info '" + block.display() + "': " + exception.getMessage());
}
if (config.contains("triggers")) {
@ -55,7 +55,7 @@ public class BlockInfo {
triggers.add(MMOCore.plugin.loadManager.loadTrigger(new MMOLineConfig(key)));
} catch (IllegalArgumentException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING,
"Could not load trigger '" + key + "' from block info '" + block.generateKey() + "': " + exception.getMessage());
"Could not load trigger '" + key + "' from block info '" + block.display() + "': " + exception.getMessage());
}
}
@ -65,13 +65,13 @@ public class BlockInfo {
if (condition instanceof BlockCondition)
conditions.add((BlockCondition) condition);
}
}
public boolean getOption(BlockInfoOption option) {
return options.getOrDefault(option, option.getDefault());
}
@NotNull
public BlockType getBlock() {
return block;
}
@ -85,6 +85,7 @@ public class BlockInfo {
return table != null;
}
@Deprecated
public List<ItemStack> collectDrops(LootBuilder builder) {
return table != null ? table.collect(builder) : new ArrayList<>();
}

View File

@ -1,29 +1,31 @@
package net.Indyuce.mmocore.api.block;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface BlockType {
/**
* Called when placing temporary blocks
*/
void place(BlockInfo.RegeneratingBlock placed);
/**
* Called when placing temporary blocks
*/
void place(@NotNull BlockInfo.RegeneratingBlock placed);
/**
* Called when regenerating an older block with block regen
*/
void regenerate(BlockInfo.RegeneratingBlock regenerating);
/**
* Called when regenerating an older block with block regen
*/
void regenerate(@NotNull BlockInfo.RegeneratingBlock regenerating);
/**
* Generates a key used to store the BlockInfo instance in the manager map,
* the key depends on the block type to make sure there is no interference
*/
String generateKey();
@NotNull String display();
/**
* Applies some extra break restrictions; returns TRUE if the block can be
* broken. This method is used to prevent non mature crops from being broken
* for example
*/
boolean breakRestrictions(Block block);
/**
* Applies some extra break restrictions; returns TRUE if the block can be
* broken. This method is used to prevent non mature crops from being broken
* for example
*/
boolean breakRestrictions(@NotNull Block block);
int hashCode();
boolean equals(@Nullable Object obj);
}

View File

@ -1,59 +1,73 @@
package net.Indyuce.mmocore.api.block;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import org.bukkit.Location;
import org.bukkit.block.Block;
import net.Indyuce.mmocore.api.block.BlockInfo.RegeneratingBlock;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmocore.api.block.BlockInfo.RegeneratingBlock;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.util.Objects;
public class SkullBlockType implements BlockType {
private final String value;
private final String value;
public SkullBlockType(MMOLineConfig config) {
config.validate("value");
public SkullBlockType(MMOLineConfig config) {
config.validate("value");
value = config.getString("value");
}
value = config.getString("value");
}
public SkullBlockType(Block block) {
value = MythicLib.plugin.getVersion().getWrapper().getSkullValue(block);
}
public SkullBlockType(Block block) {
value = MythicLib.plugin.getVersion().getWrapper().getSkullValue(block);
}
public String getValue() {
return value;
}
public String getValue() {
return value;
}
@Override
public void place(RegeneratingBlock block) {
Location loc = block.getLocation();
loc.getBlock().setType(VersionMaterial.PLAYER_HEAD.toMaterial());
@Override
public void place(RegeneratingBlock block) {
Location loc = block.getLocation();
loc.getBlock().setType(Material.PLAYER_HEAD);
// save skull orientation if replaced block is a player head
if (MMOCoreUtils.isPlayerHead(block.getBlockData().getMaterial()))
loc.getBlock().setBlockData(block.getBlockData());
// save skull orientation if replaced block is a player head
if (MMOCoreUtils.isPlayerHead(block.getBlockData().getMaterial()))
loc.getBlock().setBlockData(block.getBlockData());
MythicLib.plugin.getVersion().getWrapper().setSkullValue(loc.getBlock(), value);
}
MythicLib.plugin.getVersion().getWrapper().setSkullValue(loc.getBlock(), value);
}
@Override
public void regenerate(RegeneratingBlock block) {
Location loc = block.getLocation();
// This makes sure that if a skull loses its original rotation
// it can revert back to it when the base block is regenerated
loc.getBlock().setBlockData(block.getBlockData());
MythicLib.plugin.getVersion().getWrapper().setSkullValue(loc.getBlock(), value);
}
@Override
public void regenerate(RegeneratingBlock block) {
Location loc = block.getLocation();
// This makes sure that if a skull loses its original rotation
// it can revert back to it when the base block is regenerated
loc.getBlock().setBlockData(block.getBlockData());
MythicLib.plugin.getVersion().getWrapper().setSkullValue(loc.getBlock(), value);
}
@Override
public String generateKey() {
return "vanilla-skull-" + value;
}
@Override
public String display() {
return "Skull{" + value + "}";
}
@Override
public boolean breakRestrictions(Block block) {
return true;
}
@Override
public boolean breakRestrictions(Block block) {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SkullBlockType that = (SkullBlockType) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}

View File

@ -1,5 +1,7 @@
package net.Indyuce.mmocore.api.block;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.block.BlockInfo.RegeneratingBlock;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
@ -7,63 +9,75 @@ import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
import org.bukkit.block.data.BlockData;
import net.Indyuce.mmocore.api.block.BlockInfo.RegeneratingBlock;
import io.lumine.mythic.lib.api.MMOLineConfig;
import java.util.Objects;
public class VanillaBlockType implements BlockType {
private final Material type;
private final Material type;
/*
* allows to plant back crops with a custom age so that it does not always
* have to full grow again-
*/
private final int age;
/*
* allows to plant back crops with a custom age so that it does not always
* have to full grow again-
*/
private final int age;
public VanillaBlockType(MMOLineConfig config) {
config.validate("type");
public VanillaBlockType(MMOLineConfig config) {
config.validate("type");
type = Material.valueOf(config.getString("type").toUpperCase().replace("-", "_").replace(" ", "_"));
age = config.getInt("age", 0);
type = Material.valueOf(config.getString("type").toUpperCase().replace("-", "_").replace(" ", "_"));
age = config.getInt("age", 0);
Validate.isTrue(age >= 0 && age < 8, "Age must be between 0 and 7");
}
Validate.isTrue(age >= 0 && age < 8, "Age must be between 0 and 7");
}
public VanillaBlockType(Block block) {
type = block.getType();
age = 0;
}
public VanillaBlockType(Block block) {
type = block.getType();
age = 0;
}
public Material getType() {
return type;
}
public Material getType() {
return type;
}
@Override
public void place(RegeneratingBlock block) {
Location loc = block.getLocation();
block.getLocation().getBlock().setType(type);
@Override
public void place(RegeneratingBlock block) {
Location loc = block.getLocation();
block.getLocation().getBlock().setType(type);
BlockData state = block.getLocation().getBlock().getBlockData();
if (age > 0 && state instanceof Ageable) {
((Ageable) state).setAge(age);
loc.getBlock().setBlockData(state);
}
}
BlockData state = block.getLocation().getBlock().getBlockData();
if (age > 0 && state instanceof Ageable) {
((Ageable) state).setAge(age);
loc.getBlock().setBlockData(state);
}
}
@Override
public void regenerate(RegeneratingBlock block) {
Location loc = block.getLocation();
loc.getBlock().setType(type);
// Sets the original blocks old data (only when regenerating)
loc.getBlock().setBlockData(block.getBlockData());
}
@Override
public void regenerate(RegeneratingBlock block) {
Location loc = block.getLocation();
loc.getBlock().setType(type);
// Sets the original blocks old data (only when regenerating)
loc.getBlock().setBlockData(block.getBlockData());
}
@Override
public String generateKey() {
return "vanilla-block-" + type.name();
}
@Override
public String display() {
return "Vanilla{" + type.name() + "}";
}
@Override
public boolean breakRestrictions(Block block) {
return age == 0 || (block.getBlockData() instanceof Ageable && ((Ageable) block.getBlockData()).getAge() >= age);
}
@Override
public boolean breakRestrictions(Block block) {
return age == 0 || (block.getBlockData() instanceof Ageable && ((Ageable) block.getBlockData()).getAge() >= age);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VanillaBlockType that = (VanillaBlockType) o;
return type == that.type;
}
@Override
public int hashCode() {
return Objects.hash(type);
}
}

View File

@ -5,6 +5,7 @@ import java.util.Set;
import java.util.UUID;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.util.item.CurrencyItemBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
@ -28,7 +29,7 @@ public class Withdraw implements Listener {
public Withdraw(Player player) {
this.player = player;
}
public Player getPlayer() {
return player;
}
@ -38,7 +39,7 @@ public class Withdraw implements Listener {
return;
withdrawing.add(player.getUniqueId());
MMOCore.plugin.configManager.getSimpleMessage("withdrawing").send(player);
ConfigMessage.fromKey("withdrawing").send(player);
Bukkit.getPluginManager().registerEvents(this, MMOCore.plugin);
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, this::close, 20 * 20);
}
@ -60,7 +61,7 @@ public class Withdraw implements Listener {
if (!event.getPlayer().equals(player))
return;
MMOCore.plugin.configManager.getSimpleMessage("withdraw-cancel").send(player);
ConfigMessage.fromKey("withdraw-cancel").send(player);
close();
}
@ -75,13 +76,13 @@ public class Withdraw implements Listener {
try {
worth = Integer.parseInt(event.getMessage());
} catch (Exception e) {
MMOCore.plugin.configManager.getSimpleMessage("wrong-number", "arg", event.getMessage()).send(player);
ConfigMessage.fromKey("wrong-number").addPlaceholders("arg", event.getMessage()).send(player);
return;
}
int left = (int) (MMOCore.plugin.economy.getEconomy().getBalance(player) - worth);
if (left < 0) {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-money", "left", "" + -left).send(player);
ConfigMessage.fromKey("not-enough-money").addPlaceholders("left", -left).send(player);
return;
}
@ -91,7 +92,7 @@ public class Withdraw implements Listener {
MMOCore.plugin.economy.getEconomy().withdrawPlayer(player, worth);
withdrawAlgorythm(worth);
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
MMOCore.plugin.configManager.getSimpleMessage("withdrew", "worth", "" + worth).send(player);
ConfigMessage.fromKey("withdrew").addPlaceholders("worth", worth).send(player);
});
}

View File

@ -4,7 +4,7 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.event.HandlerList;
/**
* @deprecated Use {@link AsyncPlayerDataLoadEvent} instead
* @deprecated Use {@link io.lumine.mythic.lib.api.event.SynchronizedDataLoadEvent} instead
*/
@Deprecated
public class PlayerDataLoadEvent extends PlayerDataEvent {

View File

@ -7,11 +7,17 @@ import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class PlayerEnterCastingModeEvent extends PlayerDataEvent implements Cancellable {
private static final HandlerList handlerList = new HandlerList();
private boolean cancelled = false;
private static final HandlerList HANDLERS = new HandlerList();
@Deprecated
public PlayerEnterCastingModeEvent(@NotNull Player who) {
super(PlayerData.get(who.getUniqueId()));
super(PlayerData.get(who));
}
public PlayerEnterCastingModeEvent(@NotNull PlayerData playerData) {
super(playerData);
}
@Override
@ -30,7 +36,7 @@ public class PlayerEnterCastingModeEvent extends PlayerDataEvent implements Canc
return getHandlerList();
}
public static HandlerList getHandlerList(){
return handlerList;
public static HandlerList getHandlerList() {
return HANDLERS;
}
}

View File

@ -8,13 +8,18 @@ import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class PlayerExitCastingModeEvent extends PlayerDataEvent implements Cancellable {
private static final HandlerList handlerList = new HandlerList();
private boolean cancelled = false;
private static final HandlerList HANDLERS = new HandlerList();
@Deprecated
public PlayerExitCastingModeEvent(@NotNull Player who) {
super(PlayerData.get(who.getUniqueId()));
super(PlayerData.get(who));
}
public PlayerExitCastingModeEvent(@NotNull PlayerData who) {
super(who);
}
@Override
public boolean isCancelled() {
@ -29,10 +34,10 @@ public class PlayerExitCastingModeEvent extends PlayerDataEvent implements Cance
@NotNull
@Override
public HandlerList getHandlers() {
return handlerList;
return HANDLERS;
}
public static HandlerList getHandlerList() {
return handlerList;
return HANDLERS;
}
}

View File

@ -11,9 +11,6 @@ import net.Indyuce.mmocore.api.quest.trigger.*;
import net.Indyuce.mmocore.experience.source.*;
import net.Indyuce.mmocore.loot.chest.condition.*;
import net.Indyuce.mmocore.loot.droptable.dropitem.*;
import net.Indyuce.mmocore.spawnpoint.def.DefaultSpawnOption;
import net.Indyuce.mmocore.spawnpoint.def.LastOption;
import net.Indyuce.mmocore.spawnpoint.def.SpawnPointOption;
import org.bukkit.configuration.ConfigurationSection;
public class DefaultMMOLoader extends MMOLoader {
@ -26,12 +23,9 @@ public class DefaultMMOLoader extends MMOLoader {
if (config.getKey().equals("stat"))
return new StatTrigger(config);
if (config.getKey().equals("unlock_slot"))
if(config.getKey().equals("unlock_slot"))
return new UnlockSlotTrigger(config);
if (config.getKey().equals("unlock_spawn_point"))
return new UnlockSpawnPointTrigger(config);
if (config.getKey().equals("unlock_skill"))
return new UnlockSkillTrigger(config);
@ -41,7 +35,7 @@ public class DefaultMMOLoader extends MMOLoader {
if (config.getKey().equals("levelup_skill"))
return new LevelUpSkillTrigger(config);
if (config.getKey().equals("skill_buff") || config.getKey().equals("skill_modifier"))
if (config.getKey().equals("skill_buff")||config.getKey().equals("skill_modifier"))
return new SkillModifierTrigger(config);
if (config.getKey().equals("message"))
@ -210,13 +204,4 @@ public class DefaultMMOLoader extends MMOLoader {
return null;
}
@Override
public DefaultSpawnOption loadDefaultSpawnOption(MMOLineConfig config) {
if (config.getKey().equals("last"))
return new LastOption(config);
if (config.getKey().equals("spawnpoint"))
return new SpawnPointOption(config);
return null;
}
}

View File

@ -8,7 +8,6 @@ import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.loot.chest.condition.Condition;
import net.Indyuce.mmocore.loot.droptable.dropitem.DropItem;
import net.Indyuce.mmocore.api.block.BlockType;
import net.Indyuce.mmocore.spawnpoint.def.DefaultSpawnOption;
import org.bukkit.configuration.ConfigurationSection;
/**
@ -41,9 +40,4 @@ public class MMOLoader {
public BlockType loadBlockType(MMOLineConfig config) {
return null;
}
public DefaultSpawnOption loadDefaultSpawnOption(MMOLineConfig config) {
return null;
}
}

View File

@ -2,11 +2,10 @@ package net.Indyuce.mmocore.api.player;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.player.MMOPlayerData;
import io.lumine.mythic.lib.api.stat.StatInstance;
import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.data.SynchronizedDataHolder;
import io.lumine.mythic.lib.player.cooldown.CooldownMap;
import io.lumine.mythic.lib.util.Closeable;
import io.lumine.mythic.lib.version.VParticle;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.SoundEvent;
@ -22,13 +21,13 @@ import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.api.player.social.FriendRequest;
import net.Indyuce.mmocore.api.player.stats.PlayerStats;
import net.Indyuce.mmocore.api.quest.PlayerQuests;
import net.Indyuce.mmocore.api.quest.trigger.StatTrigger;
import net.Indyuce.mmocore.api.quest.trigger.Trigger;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.experience.ExperienceTableClaimer;
import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.experience.droptable.ExperienceItem;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.guild.provided.Guild;
@ -46,10 +45,10 @@ import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.skill.cast.SkillCastingInstance;
import net.Indyuce.mmocore.skill.cast.SkillCastingMode;
import net.Indyuce.mmocore.skilltree.NodeIncrementResult;
import net.Indyuce.mmocore.skilltree.SkillTreeNode;
import net.Indyuce.mmocore.skilltree.SkillTreeStatus;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.Indyuce.mmocore.spawnpoint.SpawnPoint;
import net.Indyuce.mmocore.waypoint.Waypoint;
import net.Indyuce.mmocore.waypoint.WaypointOption;
import net.md_5.bungee.api.ChatMessageType;
@ -65,36 +64,40 @@ import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerData, Closeable, ExperienceTableClaimer, ClassDataContainer {
public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerData, Closeable, ClassDataContainer {
/**
* Can be null, the {@link #getProfess()} method will return the
* player class, or the default one if this field is null.
* <p>
* NEVER access the player class using this field, you must
* use the {@link #getProfess()} method instead
*/
@Nullable
private PlayerClass profess;
private int level, classPoints, skillPoints, attributePoints, attributeReallocationPoints, skillTreeReallocationPoints, skillReallocationPoints;
private double experience;
private double mana, stamina, stellium;
/**
* Health is stored in playerData because when saving the playerData we can't access the player health anymore as the payer is Offline.
* Saving resources (especially health) right in player data fixes TONS of issues.
*/
private double health;
private double health, mana, stamina, stellium;
private Guild guild;
private SpawnPoint lastSpawnPoint;
private SpawnPoint lastUsedSpawnPoint;
private SkillCastingInstance skillCasting;
private final PlayerQuests questData;
private final PlayerStats playerStats;
private final List<UUID> friends = new ArrayList<>();
/**
* @deprecated Use {@link #hasUnlocked(Unlockable)} instead
* TODO Use {@link #hasUnlocked(Unlockable)} instead
* <p>
* Merge waypoints with unlocked items, and create a method
* plugin-scope to check if some item is class-specific and
* should be reset when switching class
*/
@Deprecated
private final Set<String> waypoints = new HashSet<>();
@ -106,17 +109,13 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
private final Map<PlayerActivity, Long> lastActivity = new HashMap<>();
private final CombatHandler combat = new CombatHandler(this);
/**
* Cached for easier access. Amount of points spent in each skill tree.
*/
private final Map<SkillTree, Integer> pointSpent = new HashMap<>();
/**
* Cached for easier access. Current status of each skill tree node.
*/
private final Map<SkillTreeNode, SkillTreeStatus> nodeStates = new HashMap<>();
private final Map<SkillTreeNode, Integer> nodeLevels = new HashMap<>();
private final Map<String, Integer> skillTreePoints = new HashMap<>();
private final Map<SkillTree, Integer> skillTreePointsSpent = new HashMap<>();
/**
* Saves the namespacedkeys of the items that have been unlocked in the form "namespace:key".
@ -128,17 +127,17 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
/**
* Saves the amount of times the player has claimed some
* exp item in exp tables, for both exp tables used
* item in any experience table. The key used in the map
* is the identifier of the exp table item.
*/
private final Map<String, Integer> tableItemClaims = new HashMap<>();
private boolean shouldTeleportWhenJoin;
// NON-FINAL player data stuff made public to facilitate field change
public boolean noCooldown;
public long lastDropEvent;
public PlayerData(MMOPlayerData mmoData) {
super(mmoData);
super(MMOCore.plugin, mmoData);
questData = new PlayerQuests(this);
playerStats = new PlayerStats(this);
@ -155,13 +154,9 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void reload() {
try {
profess = profess == null ? null : MMOCore.plugin.classManager.get(profess.getId());
getStats().updateStats();
} catch (NullPointerException exception) {
MMOCore.log(Level.SEVERE, "[Userdata] Could not find class " + getProfess().getId() + " while refreshing player data.");
}
//We remove all the stats and buffs associated to triggers.
getMMOPlayerData().getStatMap().getInstances().forEach(statInstance -> statInstance.removeIf(key -> key.startsWith(Trigger.TRIGGER_PREFIX)));
getMMOPlayerData().getSkillModifierMap().getInstances().forEach(skillModifierInstance -> skillModifierInstance.removeIf(key -> key.startsWith(Trigger.TRIGGER_PREFIX)));
final Iterator<Map.Entry<Integer, BoundSkillInfo>> ite = new HashMap(boundSkills).entrySet().iterator();
while (ite.hasNext())
try {
@ -183,6 +178,42 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
if (!nodeLevels.containsKey(node)) nodeLevels.put(node, 0);
setupSkillTree();
applyTemporaryTriggers();
getStats().updateStats();
}
@Deprecated
public void setupRemovableTrigger() {
applyTemporaryTriggers();
}
/**
* Some triggers are marked with the {@link Removable} interface as
* they are non-permanent triggers, and they need to be re-applied
* everytime their MMOPlayerData gets flushed from the MythicLib cache
* (everytime the player logs out).
* <p>
* This method goes through all the player's experience tables that
* they have spent points into and register all their non-permanent triggers.
*/
public void applyTemporaryTriggers() {
// Remove all stats and buffs associated to triggers
resetTriggerStats();
// Experience tables from main class
if (getProfess().hasExperienceTable())
getProfess().getExperienceTable().applyTemporaryTriggers(this, getProfess());
// Experience tables from professions
for (Profession profession : MMOCore.plugin.professionManager.getAll())
if (profession.hasExperienceTable())
profession.getExperienceTable().applyTemporaryTriggers(this, profession);
// Experience tables from skill tree nodes
for (SkillTree skillTree : MMOCore.plugin.skillTreeManager.getAll())
for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().applyTemporaryTriggers(this, node);
}
public void setupSkillTree() {
@ -190,39 +221,27 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Node states setup
for (SkillTree skillTree : getProfess().getSkillTrees())
skillTree.setupNodeStates(this);
// Stat triggers setup
for (SkillTree skillTree : MMOCore.plugin.skillTreeManager.getAll())
for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().claimRemovableTrigger(this, node);
}
/**
* This method is called when a player connects to a server and has shouldTeleportWhenJoin set to true.
* If the player is alive it means that he has been teleported to this server because of spawnpoints.
* If he is dead this will be done when he respawns.
*/
public void setupSpawnPoint() {
if (lastSpawnPoint != null && isOnline() && shouldTeleportWhenJoin && !lastSpawnPoint.isOtherServer()
&& !getPlayer().isDead()) {
MMOCore.plugin.spawnPointManager.getSpawnPoint(lastSpawnPoint.getId()).whenRespawn(this);
shouldTeleportWhenJoin = false;
}
public int getPointsSpent(@NotNull SkillTree skillTree) {
return skillTreePointsSpent.getOrDefault(skillTree, 0);
}
@Deprecated
public int getPointSpent(SkillTree skillTree) {
return pointSpent.getOrDefault(skillTree, 0);
return getPointsSpent(skillTree);
}
public void setSkillTreePoints(String treeId, int points) {
skillTreePoints.put(treeId, points);
public void setSkillTreePoints(@NotNull String treeId, int points) {
if (points <= 0) skillTreePoints.remove(treeId);
else skillTreePoints.put(treeId, points);
}
public void giveSkillTreePoints(String id, int val) {
skillTreePoints.put(id, skillTreePoints.getOrDefault(id, 0) + val);
public void giveSkillTreePoints(@NotNull String id, int val) {
skillTreePoints.merge(id, Math.max(0, val), (points, ignored) -> Math.max(0, points + val));
}
public int countSkillTreePoints(SkillTree skillTree) {
public int countSkillTreePoints(@NotNull SkillTree skillTree) {
return nodeLevels.keySet().stream().filter(node -> node.getTree().equals(skillTree)).mapToInt(node -> nodeLevels.get(node) * node.getSkillTreePointsConsumed()).sum();
}
@ -230,6 +249,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* Make a copy to make sure that the object
* created is independent of the state of playerData.
*/
@NotNull
public Map<String, Integer> mapSkillTreePoints() {
return new HashMap(skillTreePoints);
}
@ -242,15 +262,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return result;
}
public void clearSkillTreePoints() {
skillTreePoints.clear();
}
public void clearNodeTimesClaimed() {
final Iterator<String> ite = tableItemClaims.keySet().iterator();
while (ite.hasNext()) if (ite.next().startsWith(SkillTreeNode.KEY_PREFIX)) ite.remove();
}
public Set<Map.Entry<String, Integer>> getNodeLevelsEntrySet() {
HashMap<String, Integer> nodeLevelsString = new HashMap<>();
for (SkillTreeNode node : nodeLevels.keySet())
@ -259,13 +270,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
}
public void resetTriggerStats() {
for (StatInstance instance : getMMOPlayerData().getStatMap().getInstances()) {
Iterator<StatModifier> iter = instance.getModifiers().iterator();
while (iter.hasNext()) {
StatModifier modifier = iter.next();
if (modifier.getKey().startsWith(StatTrigger.TRIGGER_PREFIX)) iter.remove();
}
}
getMMOPlayerData().getStatMap().getInstances().forEach(statInstance -> statInstance.removeIf(Trigger.STAT_MODIFIER_KEY::equals));
getMMOPlayerData().getSkillModifierMap().removeModifiers(Trigger.STAT_MODIFIER_KEY);
}
@Override
@ -275,16 +281,48 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return mapped;
}
public void clearNodeLevels() {
nodeLevels.clear();
pointSpent.clear();
@Deprecated
public void clearSkillTrees() {
resetSkillTrees();
}
public boolean canIncrementNodeLevel(SkillTreeNode node) {
SkillTreeStatus skillTreeStatus = nodeStates.get(node);
//Check the State of the node
if (skillTreeStatus != SkillTreeStatus.UNLOCKED && skillTreeStatus != SkillTreeStatus.UNLOCKABLE) return false;
return node.hasPermissionRequirement(this) && getNodeLevel(node) < node.getMaxLevel() && (skillTreePoints.getOrDefault(node.getTree().getId(), 0) + skillTreePoints.getOrDefault("global", 0) >= node.getSkillTreePointsConsumed());
public void resetSkillTrees() {
// Un-apply triggers
for (SkillTree tree : getProfess().getSkillTrees())
for (SkillTreeNode node : tree.getNodes())
node.resetAdvancement(this, false);
// Node levels, states and points spent
nodeLevels.clear();
nodeStates.clear();
skillTreePointsSpent.clear();
// Skill tree points
skillTreePoints.clear();
// Clear node claim count
tableItemClaims.keySet().removeIf(s -> s.startsWith(SkillTreeNode.KEY_PREFIX));
}
@NotNull
public NodeIncrementResult canIncrementNodeLevel(@NotNull SkillTreeNode node) {
final SkillTreeStatus skillTreeStatus = nodeStates.get(node);
// Check node state
if (skillTreeStatus != SkillTreeStatus.UNLOCKED && skillTreeStatus != SkillTreeStatus.UNLOCKABLE)
return NodeIncrementResult.LOCKED_NODE;
// Check permission
if (!node.hasPermissionRequirement(this)) return NodeIncrementResult.PERMISSION_DENIED;
// Max node level
if (getNodeLevel(node) >= node.getMaxLevel()) return NodeIncrementResult.MAX_LEVEL_REACHED;
final int skillTreePoints = this.skillTreePoints.getOrDefault(node.getTree().getId(), 0) + this.skillTreePoints.getOrDefault("global", 0);
if (skillTreePoints < node.getSkillTreePointsConsumed()) return NodeIncrementResult.NOT_ENOUGH_POINTS;
return NodeIncrementResult.SUCCESS;
}
/**
@ -292,32 +330,39 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* Consumes skill tree points from the tree first and then consumes the
* global skill-tree points ('all')
*/
public void incrementNodeLevel(SkillTreeNode node) {
setNodeLevel(node, getNodeLevel(node) + 1);
// Claims the nodes experience table.
node.getExperienceTable().claim(this, getNodeLevel(node), node);
public void incrementNodeLevel(@NotNull SkillTreeNode node) {
final int newLevel = addNodeLevels(node, 1);
node.updateAdvancement(this, newLevel); // Claim the node exp table
if (nodeStates.get(node) == SkillTreeStatus.UNLOCKABLE) setNodeState(node, SkillTreeStatus.UNLOCKED);
int pointToWithdraw = node.getSkillTreePointsConsumed();
if (skillTreePoints.get(node.getTree().getId()) > 0) {
int pointWithdrawn = Math.min(pointToWithdraw, skillTreePoints.get(node.getTree().getId()));
withdrawSkillTreePoints(node.getTree().getId(), pointWithdrawn);
pointToWithdraw -= pointWithdrawn;
}
if (pointToWithdraw > 0) withdrawSkillTreePoints("global", pointToWithdraw);
// We unload the nodeStates map (for the skill tree) and reload it completely
for (SkillTreeNode node1 : node.getTree().getNodes())
nodeStates.remove(node1);
// Update node state
nodeStates.compute(node, (key, status) -> status == SkillTreeStatus.UNLOCKABLE ? SkillTreeStatus.UNLOCKED : status);
// Consume skill tree points
final AtomicInteger cost = new AtomicInteger(node.getSkillTreePointsConsumed());
skillTreePoints.computeIfPresent(node.getTree().getId(), (key, points) -> {
final int withdrawn = Math.min(points, cost.get());
cost.set(cost.get() - withdrawn);
return points <= withdrawn ? null : points - withdrawn;
});
if (cost.get() > 0) withdrawSkillTreePoints("global", cost.get());
// Reload node states from full skill tree
for (SkillTreeNode node1 : node.getTree().getNodes()) nodeStates.remove(node1);
node.getTree().setupNodeStates(this);
}
@Deprecated
public int getSkillTreePoint(String treeId) {
return getSkillTreePoints(treeId);
}
public int getSkillTreePoints(@NotNull String treeId) {
return skillTreePoints.getOrDefault(treeId, 0);
}
public void withdrawSkillTreePoints(String treeId, int withdraw) {
skillTreePoints.put(treeId, skillTreePoints.get(treeId) - withdraw);
public void withdrawSkillTreePoints(@NotNull String treeId, int withdrawn) {
final int cost = Math.max(0, withdrawn);
skillTreePoints.computeIfPresent(treeId, (ignored, points) -> cost >= points ? null : points - cost);
}
public void setNodeState(SkillTreeNode node, SkillTreeStatus skillTreeStatus) {
@ -336,15 +381,24 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return nodeLevels.getOrDefault(node, 0);
}
public void setNodeLevel(SkillTreeNode node, int nodeLevel) {
int delta = (nodeLevel - nodeLevels.getOrDefault(node, 0)) * node.getSkillTreePointsConsumed();
pointSpent.put(node.getTree(), pointSpent.getOrDefault(node.getTree(), 0) + delta);
nodeLevels.put(node, nodeLevel);
public void setNodeLevel(@NotNull SkillTreeNode node, int nodeLevel) {
nodeLevels.compute(node, (ignored, currentLevelInteger) -> {
final int currentLevel = currentLevelInteger == null ? 0 : currentLevelInteger;
final int delta = (nodeLevel - currentLevel) * node.getSkillTreePointsConsumed();
skillTreePointsSpent.merge(node.getTree(), delta, (level, ignored2) -> level + delta);
return nodeLevel;
});
}
public int addNodeLevels(@NotNull SkillTreeNode node, int increment) {
final int delta = increment * node.getSkillTreePointsConsumed();
skillTreePointsSpent.merge(node.getTree(), delta, (points, ignored) -> points + delta);
return nodeLevels.merge(node, increment, (level, ignored) -> level + increment);
}
public void resetSkillTree(SkillTree skillTree) {
for (SkillTreeNode node : skillTree.getNodes()) {
node.getExperienceTable().reset(this, node);
node.resetAdvancement(this, true);
setNodeLevel(node, 0);
nodeStates.remove(node);
}
@ -355,10 +409,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return new HashMap<>(nodeStates);
}
public void clearNodeStates() {
nodeStates.clear();
}
@Override
public Map<String, Integer> getNodeTimesClaimed() {
Map<String, Integer> result = new HashMap<>();
@ -383,11 +433,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
*/
public boolean unlock(Unlockable unlockable) {
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot unlock an item unlocked by default");
unlockable.whenUnlocked(this);
final boolean wasLocked = unlockedItems.add(unlockable.getUnlockNamespacedKey());
// Call the event synchronously
if (wasLocked)
if (wasLocked) {
unlockable.whenUnlocked(this);
Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemUnlockedEvent(this, unlockable.getUnlockNamespacedKey())));
}
return wasLocked;
}
@ -399,11 +449,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
*/
public boolean lock(Unlockable unlockable) {
Validate.isTrue(!unlockable.isUnlockedByDefault(), "Cannot lock an item unlocked by default");
unlockable.whenLocked(this);
boolean wasUnlocked = unlockedItems.remove(unlockable.getUnlockNamespacedKey());
if (wasUnlocked)
//Calls the event synchronously
if (wasUnlocked) {
unlockable.whenLocked(this);
Bukkit.getScheduler().runTask(MythicLib.plugin, () -> Bukkit.getPluginManager().callEvent(new ItemLockedEvent(this, unlockable.getUnlockNamespacedKey())));
}
return wasUnlocked;
}
@ -416,6 +466,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
this.unlockedItems.addAll(unlockedItems);
}
@Deprecated
public void resetTimesClaimed() {
tableItemClaims.clear();
}
@ -442,7 +493,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
boundSkills.forEach((slot, info) -> info.close());
// Stop skill casting
if (isCasting()) leaveSkillCasting();
if (isCasting()) leaveSkillCasting(true);
}
public List<UUID> getFriends() {
@ -473,27 +524,6 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
this.lastActivity.put(activity, timestamp);
}
public SpawnPoint getLastSpawnPoint() {
return lastSpawnPoint;
}
public SpawnPoint getLastUsedSpawnPoint() {
return lastUsedSpawnPoint;
}
public void setLastSpawnPoint(SpawnPoint lastSpawnPoint) {
this.lastSpawnPoint = lastSpawnPoint;
}
public void setLastUsedSpawnPoint(SpawnPoint lastUsedSpawnPoint) {
this.lastUsedSpawnPoint = this.lastUsedSpawnPoint;
}
public void setShouldTeleportWhenJoin(boolean shouldTeleport) {
this.shouldTeleportWhenJoin = shouldTeleport;
}
@Override
public long getLastLogin() {
return getMMOPlayerData().getLastLogActivity();
@ -539,10 +569,10 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return sum;
}
@Deprecated
@NotNull
public List<ClassSkill> getUnlockedSkills() {
return getProfess().getSkills().stream()
.filter((classSkill) -> hasUnlocked(classSkill))
.filter(skill -> hasUnlocked(skill) && hasUnlockedLevel(skill))
.collect(Collectors.toList());
}
@ -561,22 +591,38 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return skillTreeReallocationPoints;
}
@Override
public int getClaims(ExperienceObject object, ExperienceTable table, ExperienceItem item) {
public int getClaims(@NotNull ExperienceObject object, @NotNull ExperienceItem item) {
final ExperienceTable table = object.getExperienceTable();
return getClaims(object.getKey() + "." + table.getId() + "." + item.getId());
}
public int getClaims(String key) {
/**
* @param key The identifier of an exp table item.
* @return Amount of times an item has been claimed
* inside an experience table.
*/
public int getClaims(@NotNull String key) {
return tableItemClaims.getOrDefault(key, 0);
}
@Override
public void setClaims(@NotNull ExperienceObject object, @NotNull ExperienceItem item, int times) {
final ExperienceTable table = object.getExperienceTable();
setClaims(object.getKey() + "." + table.getId() + "." + item.getId(), times);
}
public void setClaims(@NotNull String itemKey, int times) {
if (times <= 0) tableItemClaims.remove(itemKey);
else tableItemClaims.put(itemKey, times);
}
@Deprecated
public void setClaims(ExperienceObject object, ExperienceTable table, ExperienceItem item, int times) {
setClaims(object.getKey() + "." + table.getId() + "." + item.getId(), times);
}
public void setClaims(String key, int times) {
tableItemClaims.put(key, times);
@Deprecated
public int getClaims(ExperienceObject object, ExperienceTable table, ExperienceItem item) {
return getClaims(object.getKey() + "." + table.getId() + "." + item.getId());
}
public Map<String, Integer> getItemClaims() {
@ -600,9 +646,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void setLevel(int level) {
this.level = Math.max(1, level);
if (isOnline()) {
getStats().updateStats();
if (isSynchronized()) getStats().updateStats();
refreshVanillaExp();
}
}
@ -760,7 +805,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
setLastActivity(PlayerActivity.FRIEND_REQUEST);
FriendRequest request = new FriendRequest(this, target);
new ConfigMessage("friend-request").addPlaceholders("player", getPlayer().getName(), "uuid", request.getUniqueId().toString()).sendAsJSon(target.getPlayer());
ConfigMessage.fromKey("friend-request").addPlaceholders("player", getPlayer().getName(), "uuid", request.getUniqueId().toString()).send(target.getPlayer());
MMOCore.plugin.requestManager.registerRequest(request);
}
@ -790,14 +835,15 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
public void run() {
if (!isOnline() || getPlayer().getLocation().getBlockX() != x || getPlayer().getLocation().getBlockY() != y || getPlayer().getLocation().getBlockZ() != z) {
MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_CANCELLED).playTo(getPlayer());
MMOCore.plugin.configManager.getSimpleMessage("warping-canceled").send(getPlayer());
if (isOnline()) {
MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_CANCELLED).playTo(getPlayer());
ConfigMessage.fromKey("warping-canceled").send(getPlayer());
}
giveStellium(cost, PlayerResourceUpdateEvent.UpdateReason.USE_WAYPOINT);
cancel();
return;
}
MMOCore.plugin.configManager.getSimpleMessage("warping-comencing", "left", String.valueOf((warpTime - t) / 20)).send(getPlayer());
if (hasPerm || t++ >= warpTime) {
getPlayer().teleport(target.getLocation());
getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20, 1, false, false));
@ -806,10 +852,11 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return;
}
ConfigMessage.fromKey("warping-comencing", "left", String.valueOf((warpTime - t) / 20)).send(getPlayer());
MMOCore.plugin.soundManager.getSound(SoundEvent.WARP_CHARGE).playTo(getPlayer(), 1, (float) (.5 + t * 1.5 / warpTime));
final double r = Math.sin((double) t / warpTime * Math.PI);
for (double j = 0; j < Math.PI * 2; j += Math.PI / 4)
getPlayer().getLocation().getWorld().spawnParticle(Particle.REDSTONE, getPlayer().getLocation().add(Math.cos((double) 5 * t / warpTime + j) * r, (double) 2 * t / warpTime, Math.sin((double) 5 * t / warpTime + j) * r), 1, new Particle.DustOptions(Color.PURPLE, 1.25f));
getPlayer().getLocation().getWorld().spawnParticle(VParticle.REDSTONE.get(), getPlayer().getLocation().add(Math.cos((double) 5 * t / warpTime + j) * r, (double) 2 * t / warpTime, Math.sin((double) 5 * t / warpTime + j) * r), 1, new Particle.DustOptions(Color.PURPLE, 1.25f));
}
}.runTaskTimer(MMOCore.plugin, 0, 1);
}
@ -874,7 +921,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Experience hologram
if (hologramLocation != null && isOnline())
MMOCoreUtils.displayIndicator(hologramLocation, MMOCore.plugin.configManager.getSimpleMessage("exp-hologram", "exp", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())).message());
MMOCoreUtils.displayIndicator(hologramLocation, ConfigMessage.fromKey("exp-hologram", "exp", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())).asLine());
experience = Math.max(0, experience + event.getExperience());
@ -891,16 +938,15 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
level = getLevel() + 1;
// Apply class experience table
if (getProfess().hasExperienceTable())
getProfess().getExperienceTable().claim(this, level, getProfess());
getProfess().updateAdvancement(this, level);
}
if (level > oldLevel) {
Bukkit.getPluginManager().callEvent(new PlayerLevelUpEvent(this, null, oldLevel, level));
if (isOnline()) {
new ConfigMessage("level-up").addPlaceholders("level", String.valueOf(level)).send(getPlayer());
ConfigMessage.fromKey("level-up").addPlaceholders("level", String.valueOf(level)).send(getPlayer());
MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_UP).playTo(getPlayer());
new SmallParticleEffect(getPlayer(), Particle.SPELL_INSTANT);
new SmallParticleEffect(getPlayer(), VParticle.INSTANT_EFFECT.get());
}
getStats().updateStats();
}
@ -987,6 +1033,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
stellium = Math.max(0, Math.min(stellium + event.getAmount(), max));
}
@Deprecated
@Override
public double getHealth() {
return isOnline() ? getPlayer().getHealth() : health;
@ -1006,6 +1053,10 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return stellium;
}
public double getCachedHealth() {
return health;
}
public PlayerStats getStats() {
return playerStats;
}
@ -1046,23 +1097,16 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Deprecated
public boolean setSkillCasting(@NotNull SkillCastingInstance skillCasting) {
Validate.isTrue(!isCasting(), "Player already in casting mode");
PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(getPlayer());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
skillCasting.close();
setSkillCasting();
return true;
return setSkillCasting();
}
/**
* @return true if the PlayerEnterCastingModeEvent successfully put the player into casting mode, otherwise if the event is cancelled, returns false.
* @apiNote Changed to a boolean to reflect the cancellation state of the event being fired
* @return If the PlayerEnterCastingModeEvent successfully put the player
* into casting mode, otherwise if the event is cancelled, returns false.
*/
public boolean setSkillCasting() {
Validate.isTrue(!isCasting(), "Player already in casting mode");
PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(getPlayer());
PlayerEnterCastingModeEvent event = new PlayerEnterCastingModeEvent(this);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
@ -1076,22 +1120,23 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
}
/**
* API Method to leave casting mode and fire the PlayerExitCastingModeEvent
*
* @return true if the skill casting mode was left, or false if the event was cancelled, keeping the player in casting mode.
* @return If player successfully left skill casting i.e the Bukkit
* event has not been cancelled
*/
public boolean leaveSkillCasting() {
return leaveSkillCasting(false);
}
/**
* @param skipEvent Skip Firing the PlayerExitCastingModeEvent
* @return true if the PlayerExitCastingModeEvent is not cancelled, or if the event is skipped.
* @param skipEvent Skip firing the exit event
* @return If player successfully left skill casting i.e the Bukkit
* event has not been cancelled
*/
public boolean leaveSkillCasting(boolean skipEvent) {
Validate.isTrue(isCasting(), "Player not in casting mode");
if (!skipEvent) {
PlayerExitCastingModeEvent event = new PlayerExitCastingModeEvent(getPlayer());
PlayerExitCastingModeEvent event = new PlayerExitCastingModeEvent(this);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
}
@ -1102,9 +1147,18 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return true;
}
public void displayActionBar(String message) {
public void displayActionBar(@NotNull String message) {
displayActionBar(message, false);
}
public void displayActionBar(@NotNull String message, boolean raw) {
// TODO add an option to disable action-bar properly in all casting modes
if (ChatColor.stripColor(message).isEmpty()) return;
setLastActivity(PlayerActivity.ACTION_BAR_MESSAGE);
getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
if (raw) MythicLib.plugin.getVersion().getWrapper().sendActionBarRaw(getPlayer(), message);
else getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
}
@Deprecated
@ -1114,7 +1168,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Deprecated
public void setAttribute(String id, int value) {
attributes.setBaseAttribute(id, value);
attributes.getInstance(id).setBase(value);
}
@Override
@ -1200,6 +1254,7 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
// Clear bound skills
boundSkills.forEach((slot, info) -> info.close());
boundSkills.clear();
applyTemporaryTriggers();
// Update stats
if (isOnline()) getStats().updateStats();
@ -1211,7 +1266,8 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
@Nullable
public ClassSkill getBoundSkill(int slot) {
return boundSkills.containsKey(slot) ? boundSkills.get(slot).getClassSkill() : null;
final BoundSkillInfo found = boundSkills.get(slot);
return found != null ? found.getClassSkill() : null;
}
@Deprecated
@ -1227,24 +1283,39 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
*/
public void bindSkill(int slot, @NotNull ClassSkill skill) {
Validate.notNull(skill, "Skill cannot be null");
if (slot <= 0) return;
if (slot >= 0) {
// Unbinds the previous skill (important for passive skills)
unbindSkill(slot);
final SkillSlot skillSlot = getProfess().getSkillSlot(slot);
boundSkills.put(slot, new BoundSkillInfo(skillSlot, skill, this));
// Friendly error in case server owner makes a skill permanent while players have already bound it
if (skill.isPermanent()) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Attempted to bind permanent skill " + skill.getSkill().getName() + " to player " + getUniqueId());
return;
}
// Unbinds the previous skill (important for passive skills)
unbindSkill(slot);
final SkillSlot skillSlot = getProfess().getSkillSlot(slot);
boundSkills.put(slot, new BoundSkillInfo(skillSlot, skill, this));
}
public void unbindSkill(int slot) {
@Nullable
public BoundSkillInfo unbindSkill(int slot) {
final @Nullable BoundSkillInfo boundSkillInfo = boundSkills.remove(slot);
if (boundSkillInfo != null) boundSkillInfo.close();
return boundSkillInfo;
}
public List<ClassSkill> getBoundSkills() {
return boundSkills.values().stream().map(BoundSkillInfo::getClassSkill).collect(Collectors.toList());
@NotNull
public Map<Integer, BoundSkillInfo> getBoundSkills() {
return boundSkills;
}
/**
* @return If the player has at least one active skill bound
*/
public boolean hasActiveSkillBound() {
for (BoundSkillInfo bound : boundSkills.values())
if (!bound.isPassive()) return true;
return false;
}
@NotNull
@ -1292,12 +1363,16 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
return getMMOPlayerData().hashCode();
}
public static PlayerData get(OfflinePlayer player) {
public static PlayerData get(@NotNull MMOPlayerData playerData) {
return get(playerData.getPlayer());
}
public static PlayerData get(@NotNull OfflinePlayer player) {
return get(player.getUniqueId());
}
public static PlayerData get(UUID uuid) {
return MMOCore.plugin.dataProvider.getDataManager().get(uuid);
public static PlayerData get(@NotNull UUID uuid) {
return MMOCore.plugin.playerDataManager.get(uuid);
}
/**
@ -1329,10 +1404,10 @@ public class PlayerData extends SynchronizedDataHolder implements OfflinePlayerD
* @return If player data for that player is loaded
*/
public static boolean has(UUID uuid) {
return MMOCore.plugin.dataProvider.getDataManager().isLoaded(uuid);
return MMOCore.plugin.playerDataManager.isLoaded(uuid);
}
public static Collection<PlayerData> getAll() {
return MMOCore.plugin.dataProvider.getDataManager().getLoaded();
return MMOCore.plugin.playerDataManager.getLoaded();
}
}

View File

@ -1,36 +1,30 @@
package net.Indyuce.mmocore.api.player.attribute;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.api.player.MMOPlayerData;
import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.api.stat.api.InstanceModifier;
import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import io.lumine.mythic.lib.player.modifier.PlayerModifier;
import io.lumine.mythic.lib.util.configobject.ConfigObject;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.apache.commons.lang.Validate;
import java.text.DecimalFormat;
import java.util.Objects;
import java.util.UUID;
public class AttributeModifier extends PlayerModifier {
public class AttributeModifier extends InstanceModifier {
private final String attribute;
private final double value;
private final ModifierType type;
private static final DecimalFormat oneDigit = MythicLib.plugin.getMMOConfig().newDecimalFormat("0.#");
/**
* Flat attribute modifier (simplest modifier you can think about)
*/
public AttributeModifier(String key, String attribute, double value) {
this(key, attribute, value, ModifierType.FLAT, EquipmentSlot.OTHER, ModifierSource.OTHER);
super(key, value);
this.attribute = attribute;
}
/**
* Attribute modifier given by an external mecanic, like a party buff, item set bonuses,
* skills or abilities... Anything apart from items and armor.
* Attribute modifier given by an external mechanic, like a party buff, item
* set bonuses, skills or abilities... Anything apart from items and armor.
*/
public AttributeModifier(String key, String attribute, double value, ModifierType type) {
this(key, attribute, value, type, EquipmentSlot.OTHER, ModifierSource.OTHER);
@ -47,11 +41,23 @@ public class AttributeModifier extends PlayerModifier {
* @param source Type of the item granting the stat modifier
*/
public AttributeModifier(String key, String attribute, double value, ModifierType type, EquipmentSlot slot, ModifierSource source) {
super(key, slot, source);
this(UUID.randomUUID(), key, attribute, value, type, slot, source);
}
/**
* Attribute modifier given by an item, either a weapon or an armor piece.
*
* @param key Player modifier key
* @param attribute Attribute being modified
* @param value Value of stat modifier
* @param type Is the modifier flat or multiplicative
* @param slot Slot of the item granting the stat modifier
* @param source Type of the item granting the stat modifier
*/
public AttributeModifier(UUID uniqueId, String key, String attribute, double value, ModifierType type, EquipmentSlot slot, ModifierSource source) {
super(uniqueId, key, slot, source, value, type);
this.attribute = attribute;
this.value = value;
this.type = type;
}
/**
@ -62,22 +68,14 @@ public class AttributeModifier extends PlayerModifier {
* @param str The string to be parsed
*/
public AttributeModifier(String key, String attribute, String str) {
super(key, EquipmentSlot.OTHER, ModifierSource.OTHER);
super(key, EquipmentSlot.OTHER, ModifierSource.OTHER, str);
Validate.notNull(str, "String cannot be null");
Validate.notEmpty(str, "String cannot be empty");
type = str.toCharArray()[str.length() - 1] == '%' ? ModifierType.RELATIVE : ModifierType.FLAT;
value = Double.parseDouble(type == ModifierType.RELATIVE ? str.substring(0, str.length() - 1) : str);
this.attribute = attribute;
}
public AttributeModifier(ConfigObject object) {
super(object.getString("key"), EquipmentSlot.OTHER, ModifierSource.OTHER);
super(object);
String str = Objects.requireNonNull(object.getString("value"));
type = str.toCharArray()[str.length() - 1] == '%' ? ModifierType.RELATIVE : ModifierType.FLAT;
value = Double.parseDouble(type == ModifierType.RELATIVE ? str.substring(0, str.length() - 1) : str);
this.attribute = object.getString("attribute");
}
@ -85,14 +83,6 @@ public class AttributeModifier extends PlayerModifier {
return attribute;
}
public ModifierType getType() {
return type;
}
public double getValue() {
return value;
}
/**
* Used to multiply some existing stat modifier by a constant, usually an
* integer, for instance when MMOCore party modifiers scale with the
@ -101,24 +91,19 @@ public class AttributeModifier extends PlayerModifier {
* @param coef The multiplicative constant
* @return A new instance of StatModifier with modified value
*/
public StatModifier multiply(double coef) {
return new StatModifier(getKey(), attribute, value * coef, type, getSlot(), getSource());
public AttributeModifier multiply(double coef) {
return new AttributeModifier(getUniqueId(), getKey(), attribute, value * coef, type, getSlot(), getSource());
}
@Override
public void register(MMOPlayerData mmoPlayerData) {
PlayerData playerData = PlayerData.get(mmoPlayerData.getUniqueId());
PlayerData playerData = PlayerData.get(mmoPlayerData);
playerData.getAttributes().getInstance(attribute).addModifier(this);
}
@Override
public void unregister(MMOPlayerData mmoPlayerData) {
PlayerData playerData = PlayerData.get(mmoPlayerData.getUniqueId());
PlayerData playerData = PlayerData.get(mmoPlayerData);
playerData.getAttributes().getInstance(attribute).removeModifier(getKey());
}
@Override
public String toString() {
return oneDigit.format(value) + (type == io.lumine.mythic.lib.player.modifier.ModifierType.RELATIVE ? "%" : "");
}
}

View File

@ -30,7 +30,7 @@ public class MMOCoreAttributeStatHandler extends StatHandler {
@Override
public void runUpdate(StatInstance instance) {
try {
final PlayerData playerData = MMOCore.plugin.dataProvider.getDataManager().get(instance.getMap().getPlayerData().getUniqueId());
final PlayerData playerData = PlayerData.get(instance.getMap().getPlayerData());
playerData.getAttributes().getInstance(attr).updateStats();
} catch (NullPointerException exception) {
// Player data is not loaded yet so there's nothing to update.

View File

@ -85,7 +85,7 @@ public class PlayerAttribute implements ExperienceObject {
@NotNull
@Override
public ExperienceTable getExperienceTable() {
return Objects.requireNonNull(expTable);
return Objects.requireNonNull(expTable, "Attribute has no exp table");
}
@Override

View File

@ -1,13 +1,14 @@
package net.Indyuce.mmocore.api.player.attribute;
import io.lumine.mythic.lib.gson.Gson;
import io.lumine.mythic.lib.gson.JsonElement;
import io.lumine.mythic.lib.gson.JsonObject;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.player.modifier.Closeable;
import io.lumine.mythic.lib.api.stat.StatInstance;
import io.lumine.mythic.lib.gson.JsonElement;
import io.lumine.mythic.lib.gson.JsonObject;
import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import io.lumine.mythic.lib.util.Closeable;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.apache.commons.lang.Validate;
@ -32,11 +33,9 @@ public class PlayerAttributes {
public void load(ConfigurationSection config) {
for (String key : config.getKeys(false))
try {
String id = key.toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'");
PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id);
AttributeInstance ins = new AttributeInstance(attribute.getId());
final String id = key.toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute called '" + id + "'");
final AttributeInstance ins = new AttributeInstance(id);
ins.setBase(config.getInt(key));
instances.put(id, ins);
} catch (IllegalArgumentException exception) {
@ -56,15 +55,12 @@ public class PlayerAttributes {
}
public void load(String json) {
Gson parser = new Gson();
JsonObject jo = parser.fromJson(json, JsonObject.class);
JsonObject jo = MythicLib.plugin.getGson().fromJson(json, JsonObject.class);
for (Entry<String, JsonElement> entry : jo.entrySet()) {
try {
String id = entry.getKey().toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute '" + id + "'");
PlayerAttribute attribute = MMOCore.plugin.attributeManager.get(id);
AttributeInstance ins = new AttributeInstance(attribute.getId());
final String id = entry.getKey().toLowerCase().replace("_", "-").replace(" ", "-");
Validate.isTrue(MMOCore.plugin.attributeManager.has(id), "Could not find attribute called '" + id + "'");
final AttributeInstance ins = new AttributeInstance(id);
ins.setBase(entry.getValue().getAsInt());
instances.put(id, ins);
} catch (IllegalArgumentException exception) {
@ -113,13 +109,14 @@ public class PlayerAttributes {
return n;
}
// TODO have it extend ModifiedInstance
public class AttributeInstance {
private int spent;
private final String id, enumName;
private final Map<String, AttributeModifier> map = new HashMap<>();
public AttributeInstance(String id) {
public AttributeInstance(@NotNull String id) {
this.id = id;
this.enumName = UtilityMethods.enumName(this.id);
}
@ -141,7 +138,7 @@ public class PlayerAttributes {
}
public void addBase(int value) {
setBase(spent + value);
setBase(getBase() + value);
}
/*
@ -181,8 +178,7 @@ public class PlayerAttributes {
public AttributeModifier addModifier(AttributeModifier modifier) {
final AttributeModifier current = map.put(modifier.getKey(), modifier);
if (current != null && current instanceof Closeable)
((Closeable) current).close();
if (current instanceof Closeable) ((Closeable) current).close();
updateStats();
return current;
@ -215,6 +211,12 @@ public class PlayerAttributes {
public void updateStats() {
final PlayerAttribute attr = MMOCore.plugin.attributeManager.get(id);
final int total = getTotal();
// Remove ALL stat modifiers
for (StatInstance ins : data.getMMOPlayerData().getStatMap().getInstances())
ins.removeIf(str -> str.equals("attribute." + id));
// Register new stat modifiers
attr.getBuffs().forEach(buff -> buff.multiply(total).register(data.getMMOPlayerData()));
}
@ -223,10 +225,9 @@ public class PlayerAttributes {
}
}
@Deprecated
public void setBaseAttribute(String id, int value) {
getInstances().forEach(ins -> {
if (ins.getId().equals(id))
ins.setBase(value);
});
AttributeInstance ins = instances.get(id);
if (ins != null) ins.setBase(value);
}
}

View File

@ -1,12 +1,9 @@
package net.Indyuce.mmocore.api.player.profess;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.player.EquipmentSlot;
import io.lumine.mythic.lib.api.util.PostLoadObject;
import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.skill.PassiveSkill;
import io.lumine.mythic.lib.script.Script;
@ -14,42 +11,43 @@ import io.lumine.mythic.lib.skill.SimpleSkill;
import io.lumine.mythic.lib.skill.Skill;
import io.lumine.mythic.lib.skill.handler.MythicLibSkillHandler;
import io.lumine.mythic.lib.skill.trigger.TriggerType;
import io.lumine.mythic.lib.version.VersionMaterial;
import io.lumine.mythic.lib.util.FileUtils;
import io.lumine.mythic.lib.util.PostLoadAction;
import io.lumine.mythic.lib.util.PreloadedObject;
import io.lumine.mythic.lib.version.VParticle;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.event.EventTrigger;
import net.Indyuce.mmocore.api.player.profess.resource.ManaDisplayOptions;
import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.api.player.profess.resource.ResourceRegeneration;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.ExpCurve;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import net.Indyuce.mmocore.loot.chest.particle.CastingParticle;
import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.skill.cast.ComboMap;
import net.Indyuce.mmocore.experience.ExperienceObject;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.Level;
public class PlayerClass extends PostLoadObject implements ExperienceObject {
public class PlayerClass implements ExperienceObject, PreloadedObject {
private final String name, id, actionBarFormat;
private final List<String> description = new ArrayList<>(), attrDescription = new ArrayList<>();
private final ItemStack icon;
@ -68,7 +66,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
@Nullable
private final CastingParticle castParticle;
private final Map<Integer, SkillSlot> skillSlots = new HashMap<>();
private final List<SkillSlot> skillSlots = new ArrayList<>();
private final List<SkillTree> skillTrees = new ArrayList<>();
private final List<PassiveSkill> classScripts = new LinkedList();
private final Map<String, LinearValue> stats = new HashMap<>();
@ -82,27 +80,33 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
@Deprecated
private final Map<String, EventTrigger> eventTriggers = new HashMap<>();
public PlayerClass(String id, FileConfiguration config) {
super(config);
private final PostLoadAction postLoadAction = new PostLoadAction(config -> {
if (config.contains("subclasses"))
for (String key : config.getConfigurationSection("subclasses").getKeys(false))
try {
subclasses.add(new Subclass(
MMOCore.plugin.classManager
.getOrThrow(key.toUpperCase().replace("-", "_").replace(" ", "_")),
config.getInt("subclasses." + key)));
} catch (IllegalArgumentException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load subclass '" + key + "' from class '"
+ getId() + "': " + exception.getMessage());
}
});
public PlayerClass(String id, ConfigurationSection config) {
postLoadAction.cacheConfig(config);
this.id = id.toUpperCase().replace("-", "_").replace(" ", "_");
name = MythicLib.plugin.parseColors(config.getString("display.name", "INVALID DISPLAY NAME"));
icon = MMOCoreUtils.readIcon(config.getString("display.item", "BARRIER"));
if (config.contains("display.texture") && icon.getType() == VersionMaterial.PLAYER_HEAD.toMaterial())
try {
ItemMeta meta = icon.getItemMeta();
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
GameProfile gp = new GameProfile(UUID.randomUUID(), null);
gp.getProperties().put("textures", new Property("textures", config.getString("display.texture")));
profileField.set(meta, gp);
icon.setItemMeta(meta);
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
| SecurityException exception) {
throw new IllegalArgumentException("Could not apply playerhead texture: " + exception.getMessage());
}
if (config.contains("display.texture") && icon.getType() == Material.PLAYER_HEAD) {
ItemMeta meta = icon.getItemMeta();
UtilityMethods.setTextureValue((SkullMeta) meta, config.getString("display.texture"));
icon.setItemMeta(meta);
}
for (String string : config.getStringList("display.lore"))
description.add(ChatColor.GRAY + MythicLib.plugin.parseColors(string));
@ -184,15 +188,13 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
}
// Skill slots
for (int i = 1; i < MMOCore.plugin.configManager.maxSkillSlots + 1; i++)
try {
if (config.contains("skill-slots." + i))
skillSlots.put(i, new SkillSlot(config.getConfigurationSection("skill-slots." + i)));
else
skillSlots.put(i, new SkillSlot(i, 0, "true", "&eSkill Slot " + MMOCoreUtils.intToRoman(i), new ArrayList<>(), false, true, new ArrayList<>()));
} catch (RuntimeException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load skill slot '" + String.valueOf(i) + "' from class '" + getId() + "': " + exception.getMessage());
}
if (config.isConfigurationSection("skill-slots"))
FileUtils.iterateConfigSectionList(
config.getConfigurationSection("skill-slots"),
skillSlots,
SkillSlot::new,
index -> new SkillSlot(index, 0, "true", "&eUnconfigured Skill Slot " + MMOCoreUtils.intToRoman(index), new ArrayList<>(), false, true, new ArrayList<>()),
(key, exception) -> MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load skill slot '" + key + "' from class '" + getId() + "': " + exception.getMessage()));
// Class skills
for (RegisteredSkill registered : MMOCore.plugin.skillManager.getAll()) {
@ -256,8 +258,6 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
* option was not provided.
*/
public PlayerClass(String id, String name, Material material) {
super(null);
this.id = id;
this.name = name;
manaDisplay = ManaDisplayOptions.DEFAULT;
@ -266,7 +266,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
expCurve = ExpCurve.DEFAULT;
expTable = null;
comboMap = null;
castParticle = new CastingParticle(Particle.SPELL_INSTANT);
castParticle = new CastingParticle(VParticle.INSTANT_EFFECT.get());
actionBarFormat = "";
this.icon = new ItemStack(material);
setOption(ClassOption.DISPLAY, false);
@ -275,21 +275,6 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
resourceHandlers.put(resource, new ResourceRegeneration(resource));
}
@Override
protected void whenPostLoaded(ConfigurationSection config) {
if (config.contains("subclasses"))
for (String key : config.getConfigurationSection("subclasses").getKeys(false))
try {
subclasses.add(new Subclass(
MMOCore.plugin.classManager
.getOrThrow(key.toUpperCase().replace("-", "_").replace(" ", "_")),
config.getInt("subclasses." + key)));
} catch (IllegalArgumentException exception) {
MMOCore.plugin.getLogger().log(Level.WARNING, "Could not load subclass '" + key + "' from class '"
+ getId() + "': " + exception.getMessage());
}
}
public String getId() {
return id;
}
@ -300,7 +285,7 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
@Override
public String getKey() {
return "class." + getId();
return "class_" + getId();
}
@NotNull
@ -321,6 +306,12 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
return displayOrder;
}
@NotNull
@Override
public PostLoadAction getPostLoadAction() {
return postLoadAction;
}
@Override
public ExpCurve getExpCurve() {
return expCurve;
@ -436,19 +427,21 @@ public class PlayerClass extends PostLoadObject implements ExperienceObject {
}
public boolean hasSlot(int slot) {
return skillSlots.containsKey(slot);
return 1 <= slot && slot <= skillSlots.size();
}
public List<SkillTree> getSkillTrees() {
return skillTrees;
}
@Nullable
public SkillSlot getSkillSlot(int slot) {
return skillSlots.get(slot);
return hasSlot(slot) ? skillSlots.get(slot - 1) : null;
}
public Collection<SkillSlot> getSlots() {
return skillSlots.values();
@NotNull
public List<SkillSlot> getSlots() {
return skillSlots;
}
@NotNull

View File

@ -5,6 +5,7 @@ import io.lumine.mythic.lib.gson.JsonObject;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttributes;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.player.ClassDataContainer;
import net.Indyuce.mmocore.skill.RegisteredSkill;
@ -107,7 +108,7 @@ public class SavedClassInformation implements ClassDataContainer {
for (Entry<String, JsonElement> entry : json.getAsJsonObject("bound-skills").entrySet())
boundSkills.put(Integer.parseInt(entry.getKey()), entry.getValue().getAsString());
if (json.has("unlocked-items"))
for ( JsonElement unlockedItem : json.get("unlocked-items").getAsJsonArray())
for (JsonElement unlockedItem : json.get("unlocked-items").getAsJsonArray())
unlockedItems.add(unlockedItem.getAsString());
}
@ -124,13 +125,13 @@ public class SavedClassInformation implements ClassDataContainer {
this.stellium = data.getStellium();
this.stamina = data.getStamina();
data.mapAttributeLevels().forEach((key, val) -> this.attributeLevels.put(key, val));
data.mapSkillLevels().forEach((key, val) -> skillLevels.put(key, val));
data.mapSkillTreePoints().forEach((key, val) -> skillTreePoints.put(key, val));
data.getNodeLevels().forEach((node, level) -> nodeLevels.put(node, level));
data.getNodeTimesClaimed().forEach((key, val) -> nodeTimesClaimed.put(key, val));
data.mapBoundSkills().forEach((slot, skill) -> boundSkills.put(slot, skill));
data.getUnlockedItems().forEach(item -> unlockedItems.add(item));
attributeLevels.putAll(data.mapAttributeLevels());
skillLevels.putAll(data.mapSkillLevels());
skillTreePoints.putAll(data.mapSkillTreePoints());
nodeLevels.putAll(data.getNodeLevels());
nodeTimesClaimed.putAll(data.getNodeTimesClaimed());
boundSkills.putAll(data.mapBoundSkills());
unlockedItems.addAll(data.getUnlockedItems());
}
@Override
@ -293,22 +294,18 @@ public class SavedClassInformation implements ClassDataContainer {
if (!player.getProfess().hasOption(ClassOption.DEFAULT) || MMOCore.plugin.configManager.saveDefaultClassInfo)
player.applyClassInfo(player.getProfess(), new SavedClassInformation(player));
// Remove perm stats for nodes and class
for (SkillTree skillTree : player.getProfess().getSkillTrees())
for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().removePermStats(player, node);
if (player.getProfess().hasExperienceTable())
player.getProfess().getExperienceTable().removePermStats(player, player.getProfess());
// Remove class permanent buffs
player.getProfess().resetAdvancement(player, false);
/*
* Resets information which much be reset after everything is saved.
*/
player.mapSkillLevels().forEach((skill, level) -> player.resetSkillLevel(skill));
player.getAttributes().getInstances().forEach(ins -> ins.setBase(0));
player.clearSkillTreePoints();
player.clearNodeLevels();
player.clearNodeStates();
player.clearNodeTimesClaimed();
for (PlayerAttribute attribute : MMOCore.plugin.attributeManager.getAll()) {
attribute.resetAdvancement(player, false);
player.getAttributes().getInstance(attribute).setBase(0);
}
player.resetSkillTrees();
/*
* Reads this class info, applies it to the player. set class after
@ -327,7 +324,7 @@ public class SavedClassInformation implements ClassDataContainer {
player.bindSkill(slot, profess.getSkill(boundSkills.get(slot)));
skillLevels.forEach(player::setSkillLevel);
attributeLevels.forEach((id, pts) -> player.getAttributes().setBaseAttribute(id, pts));
attributeLevels.forEach((id, pts) -> player.getAttributes().getInstance(id).setBase(pts));
// Careful, the global points must not be forgotten.
player.setSkillTreePoints("global", skillTreePoints.getOrDefault("global", 0));
@ -342,23 +339,17 @@ public class SavedClassInformation implements ClassDataContainer {
// Add the values to the times claimed table and claims the corresponding stat triggers.
nodeTimesClaimed.forEach((str, val) -> player.setClaims(str, val));
// We claim back the stats triggers for all the skill tree nodes of the new class.
for (SkillTree skillTree : profess.getSkillTrees())
for (SkillTreeNode node : skillTree.getNodes())
node.getExperienceTable().claimRemovableTrigger(player, node);
profess.getExperienceTable().claimRemovableTrigger(player, profess);
// Unload current class information
player.unloadClassInfo(profess);
// This needs to be done at the end to make sure the MAX_HEALTH/MAX_MANA/... stats are loaded.
player.getPlayer().setHealth(MMOCoreUtils.fixResource(health, player.getPlayer().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
player.setHealth(health);
player.setMana(mana);
player.setStellium(stellium);
player.setStamina(stamina);
// Updates level on exp bar
player.refreshVanillaExp();
player.applyTemporaryTriggers();
player.getStats().updateStats();
}
}

View File

@ -21,7 +21,7 @@ public class AttackEventTrigger implements EventTriggerHandler {
// We don't want players dying by themselves when using an enderpearl.
if (event.getPlayer().equals(event.getEntity())) return;
PlayerData player = PlayerData.get(event.getData().getUniqueId());
PlayerData player = PlayerData.get(event.getData());
PlayerClass profess = player.getProfess();
for (DamageType type : event.getAttack().getDamage().collectTypes()) {

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.api.player.profess.event.trigger;
import io.lumine.mythic.lib.UtilityMethods;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
@ -17,6 +18,8 @@ public class BlockBrokenTrigger implements EventTriggerHandler {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void a(BlockBreakEvent event) {
if (UtilityMethods.isFake(event)) return;
PlayerData player = PlayerData.get(event.getPlayer());
if (player.getProfess().hasEventTriggers("break-block"))
player.getProfess().getEventTriggers("break-block").getTriggers().forEach(trigger -> trigger.apply(player));

View File

@ -1,6 +1,6 @@
package net.Indyuce.mmocore.api.player.social;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerActivity;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.Sound;
@ -21,8 +21,8 @@ public class FriendRequest extends Request {
getCreator().addFriend(getTarget().getUniqueId());
getTarget().addFriend(getCreator().getUniqueId());
if (getCreator().isOnline()) {
MMOCore.plugin.configManager.getSimpleMessage("now-friends", "player", getTarget().getPlayer().getName()).send(getCreator().getPlayer());
MMOCore.plugin.configManager.getSimpleMessage("now-friends", "player", getCreator().getPlayer().getName()).send(getTarget().getPlayer());
ConfigMessage.fromKey("now-friends", "player", getTarget().getPlayer().getName()).send(getCreator().getPlayer());
ConfigMessage.fromKey("now-friends", "player", getCreator().getPlayer().getName()).send(getTarget().getPlayer());
}
}
}

View File

@ -8,16 +8,13 @@ import io.lumine.mythic.lib.player.modifier.ModifierSource;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import io.lumine.mythic.lib.player.skill.PassiveSkill;
import io.lumine.mythic.lib.player.skill.PassiveSkillMap;
import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.player.stats.StatInfo;
import net.Indyuce.mmocore.skill.ClassSkill;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class PlayerStats {
private final PlayerData data;
@ -49,7 +46,7 @@ public class PlayerStats {
}
public double getStat(String stat) {
return getMap().getInstance(stat).getTotal();
return getMap().getStat(stat);
}
/**
@ -70,14 +67,24 @@ public class PlayerStats {
return data.getProfess().calculateStat(stat, profession == null ? data.getLevel() : data.getCollectionSkills().getLevel(profession));
}
public void updateStats() {
updateStats(false);
}
/**
* Used to update MMOCore stat modifiers due to class and send them over to
* MythicLib. Must be ran everytime the player levels up or changes class.
* MythicLib. Must be ran everytime the player levels up, changes class or
* when the plugin reloads.
* <p>
* This is also called when reloading the plugin to make class setup easier,
* see {@link PlayerData#reload()} for more info
* Login scripts are a pretty special case of scripts/skills since they are
* not loaded yet when MythicLib triggers them naturally. Therefore, they
* need to be cast as soon as they are loaded into the MMOCore player data.
*
* @param castLoginScripts Should login scripts be cast
*/
public synchronized void updateStats() {
public synchronized void updateStats(boolean castLoginScripts) {
// Update player stats
for (String stat : MMOCore.plugin.statManager.getRegistered()) {
final StatInstance instance = getMap().getInstance(stat);
final StatInstance.ModifierPacket packet = instance.newPacket();
@ -94,24 +101,32 @@ public class PlayerStats {
packet.runUpdate();
}
/*
* This is here because it requires updates for the same reasons
* as statistics (when the player level changes, when his class
* changes, when he logs on..)
*
* This updates the player's PASSIVE skills
*/
// Updates the player's unbindable CLASS passive skills
final PassiveSkillMap skillMap = data.getMMOPlayerData().getPassiveSkillMap();
skillMap.removeModifiers("MMOCorePermanentSkill");
for (ClassSkill skill : data.getProfess().getSkills())
if (skill.isPermanent()
&& skill.getSkill().getTrigger() != TriggerType.LOGIN
&& data.hasUnlocked(skill)
&& data.hasUnlockedLevel(skill))
skillMap.addModifier(skill.toPassive(data));
skillMap.removeModifiers("MMOCorePassiveSkillNotBound");
data.getProfess().getSkills()
.stream()
.filter((classSkill) -> !classSkill.needsBound()&&classSkill.getSkill().getTrigger().isPassive() && data.hasUnlocked(classSkill) && data.hasUnlockedLevel(classSkill))
.forEach(classSkill -> skillMap.addModifier(classSkill.toPassive(data)));
// This updates the player's class SCRIPTS
// Updates the player's CLASS scripts
skillMap.removeModifiers("MMOCoreClassScript");
for (PassiveSkill script : data.getProfess().getScripts())
skillMap.addModifier(script);
if (script.getType() != TriggerType.LOGIN) skillMap.addModifier(script);
// If data hasn't been synchronized yet, cast LOGIN scripts
if (castLoginScripts) {
// Call class login skills
for (ClassSkill skill : data.getProfess().getSkills())
if (skill.getSkill().getTrigger() == TriggerType.LOGIN)
skill.toCastable(data).cast(data.getMMOPlayerData());
// Call class login scripts
for (PassiveSkill skill : data.getProfess().getScripts())
if (skill.getType() == TriggerType.LOGIN) skill.getTriggeredSkill().cast(data.getMMOPlayerData());
}
}
}

View File

@ -1,6 +1,6 @@
package net.Indyuce.mmocore.api.quest;
import io.lumine.mythic.lib.gson.Gson;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.gson.JsonElement;
import io.lumine.mythic.lib.gson.JsonObject;
import io.lumine.mythic.lib.util.Closeable;
@ -99,8 +99,7 @@ public class PlayerQuests implements Closeable {
}
public void load(String json) {
Gson parser = new Gson();
JsonObject jo = parser.fromJson(json, JsonObject.class);
JsonObject jo = MythicLib.plugin.getGson().fromJson(json, JsonObject.class);
if (jo.has("current")) {
JsonObject cur = jo.getAsJsonObject("current");
try {

View File

@ -1,12 +1,8 @@
package net.Indyuce.mmocore.api.quest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.util.PostLoadAction;
import io.lumine.mythic.lib.util.PreloadedObject;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.objective.Objective;
@ -14,26 +10,32 @@ import net.Indyuce.mmocore.experience.Profession;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.util.PostLoadObject;
import java.util.*;
import java.util.logging.Level;
public class Quest extends PostLoadObject {
private final String id;
private final String name;
public class Quest implements PreloadedObject {
private final String id, name;
private final List<Quest> parents = new ArrayList<>();
private final List<Objective> objectives = new ArrayList<>();
private final List<String> lore;
private final int mainLevelRestriction;
private final Map<Profession, Integer> levelRestrictions = new HashMap<>();
// cooldown in millis
// Cooldown in millis
private final long cooldown;
public Quest(String id, FileConfiguration config) {
super(config);
private final PostLoadAction postLoadAction = new PostLoadAction(config -> {
// Load parent quests
if (config.contains("parent"))
for (String parent : config.getStringList("parent"))
parents.add(MMOCore.plugin.questManager.getOrThrow(parent.toLowerCase().replace(" ", "-").replace("_", "-")));
});
public Quest(String id, ConfigurationSection config) {
postLoadAction.cacheConfig(config);
this.id = id.toLowerCase().replace("_", "-").replace(" ", "-");
cooldown = (long) (config.contains("delay") ? config.getDouble("delay") * 60 * 60 * 1000 : -1);
@ -69,11 +71,10 @@ public class Quest extends PostLoadObject {
}
}
@NotNull
@Override
protected void whenPostLoaded(ConfigurationSection config) {
if (config.contains("parent"))
for (String parent : config.getStringList("parent"))
parents.add(MMOCore.plugin.questManager.getOrThrow(parent.toLowerCase().replace(" ", "-").replace("_", "-")));
public PostLoadAction getPostLoadAction() {
return postLoadAction;
}
public String getId() {

View File

@ -5,63 +5,63 @@ import net.Indyuce.mmocore.api.quest.objective.Objective;
import io.lumine.mythic.lib.MythicLib;
public class QuestProgress {
private final Quest quest;
private final PlayerData player;
private final Quest quest;
private final PlayerData player;
private int objective;
private ObjectiveProgress objectiveProgress;
private int objective;
private ObjectiveProgress objectiveProgress;
public QuestProgress(Quest quest, PlayerData player) {
this(quest, player, 0);
}
public QuestProgress(Quest quest, PlayerData player) {
this(quest, player, 0);
}
public QuestProgress(Quest quest, PlayerData player, int objective) {
this.quest = quest;
this.player = player;
this.objective = objective;
objectiveProgress = nextObjective().newProgress(this);
}
public QuestProgress(Quest quest, PlayerData player, int objective) {
this.quest = quest;
this.player = player;
public Quest getQuest() {
return quest;
}
this.objective = objective;
objectiveProgress = nextObjective().newProgress(this);
}
public PlayerData getPlayer() {
return player;
}
public Quest getQuest() {
return quest;
}
public int getObjectiveNumber() {
return objective;
}
public PlayerData getPlayer() {
return player;
}
public ObjectiveProgress getProgress() {
return objectiveProgress;
}
public int getObjectiveNumber() {
return objective;
}
private Objective nextObjective() {
return quest.getObjectives().get(objective);
}
public ObjectiveProgress getProgress() {
return objectiveProgress;
}
public void completeObjective() {
objective++;
objectiveProgress.close();
private Objective nextObjective() {
return quest.getObjectives().get(objective);
}
public void completeObjective() {
objective++;
objectiveProgress.close();
final ObjectiveProgress finishedObjectiveProgress = objectiveProgress;
// end quest
if (objective >= quest.getObjectives().size())
player.getQuestData().finishCurrent();
else
objectiveProgress = nextObjective().newProgress(this);
// Start next objective, or end quest.
if (objective >= quest.getObjectives().size()) player.getQuestData().finishCurrent();
else objectiveProgress = nextObjective().newProgress(this);
player.getQuestData().updateBossBar();
player.getQuestData().updateBossBar();
/*
* Apply triggers only at the end! It comes handy when starting another
* quest in some storyline using triggers from the previous quest.
*/
finishedObjectiveProgress.getObjective().getTriggers().forEach(trigger -> trigger.schedule(getPlayer()));
}
// apply triggers at the end so the quest is ended when a trigger quest start is launched.
objectiveProgress.getObjective().getTriggers().forEach(trigger -> trigger.schedule(getPlayer()));
}
public String getFormattedLore() {
return MythicLib.plugin.parseColors(objectiveProgress.formatLore(objectiveProgress.getObjective().getDefaultLore()));
}
public String getFormattedLore() {
return MythicLib.plugin.parseColors(objectiveProgress.formatLore(objectiveProgress.getObjective().getDefaultLore()));
}
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.api.quest.objective;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.api.quest.ObjectiveProgress;
import net.Indyuce.mmocore.api.event.CustomBlockMineEvent;
import net.Indyuce.mmocore.api.quest.QuestProgress;
@ -42,6 +43,8 @@ public class MineBlockObjective extends Objective {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void a(BlockBreakEvent event) {
if(!getQuestProgress().getPlayer().isOnline()) return;
if (UtilityMethods.isFake(event)) return;
if ((!playerPlaced) && event.getBlock().hasMetadata("player_placed"))
return;
if (event.getPlayer().equals(getQuestProgress().getPlayer().getPlayer()) && event.getBlock().getType() == block) {

View File

@ -8,7 +8,6 @@ import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import javax.annotation.Nullable;
import java.util.Objects;
public class BindSkillTrigger extends Trigger implements Removable {
private final RegisteredSkill skill;
@ -19,14 +18,13 @@ public class BindSkillTrigger extends Trigger implements Removable {
config.validateKeys("skill", "slot");
slot = config.getInt("slot");
skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(config.getString("skill")));
skill = MMOCore.plugin.skillManager.getSkillOrThrow(config.getString("skill"));
}
@Override
public void apply(PlayerData playerData) {
final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill);
if (found != null)
playerData.bindSkill(slot, found);
if (found != null) playerData.bindSkill(slot, found);
}
@Override

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.api.quest.trigger;
import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.Bukkit;
@ -23,7 +24,9 @@ public class CommandTrigger extends Trigger {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), format(player.getPlayer()));
}
@BackwardsCompatibility(version = "1.12-SNAPSHOT")
private String format(Player player) {
// TODO remove use of confusing non-PAPI %player% placeholder
return MMOCore.plugin.placeholderParser.parse(player, command.replace("%player%", player.getName()));
}
}

View File

@ -6,8 +6,6 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import java.util.Objects;
public class LevelUpSkillTrigger extends Trigger implements Removable {
private final RegisteredSkill skill;
private final int amount;
@ -17,7 +15,7 @@ public class LevelUpSkillTrigger extends Trigger implements Removable {
config.validateKeys("skill", "amount");
amount = config.getInt("amount");
skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(config.getString("skill")));
skill = MMOCore.plugin.skillManager.getSkillOrThrow(config.getString("skill"));
}
@Override

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.api.quest.trigger;
import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.bukkit.entity.Player;
@ -22,7 +23,9 @@ public class MessageTrigger extends Trigger {
player.getPlayer().sendMessage(format(player.getPlayer()));
}
@BackwardsCompatibility(version = "1.12-SNAPSHOT")
private String format(Player player) {
// TODO remove use of confusing non-PAPI %player% placeholder
return MMOCore.plugin.placeholderParser.parse(player, message.replace("%player%", player.getName()));
}
}

View File

@ -8,16 +8,17 @@ import io.lumine.mythic.lib.skill.handler.SkillHandler;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class SkillModifierTrigger extends Trigger implements Removable {
private final SkillModifier mod;
private final String buffKey = TRIGGER_PREFIX + "." + UUID.randomUUID();
private final double amount;
public class SkillModifierTrigger extends Trigger implements Removable, Temporary {
private SkillModifier mod;
private boolean mutable = true;
public SkillModifierTrigger(MMOLineConfig config) {
super(config);
@ -25,16 +26,18 @@ public class SkillModifierTrigger extends Trigger implements Removable {
config.validateKeys("modifier");
config.validateKeys("amount");
amount = config.getDouble("amount");
final String skillModifier = config.getString("modifier");
final double amount = config.getDouble("amount");
final String parameter = config.getString("modifier");
final String formula = config.getString("formula", "true");
final ModifierType type = config.contains("type") ? ModifierType.valueOf(UtilityMethods.enumName(config.getString("type"))) : ModifierType.FLAT;
List<SkillHandler<?>> targetSkills = new ArrayList<>();
for (RegisteredSkill skill : MMOCore.plugin.skillManager.getAll())
if (skill.matchesFormula(formula))
targetSkills.add(skill.getHandler());
final List<SkillHandler<?>> targetSkills = MMOCore.plugin.skillManager.getAll().stream().filter(skill -> skill.matchesFormula(formula)).map(RegisteredSkill::getHandler).collect(Collectors.toList());
mod = new SkillModifier(buffKey, skillModifier, targetSkills, amount, type);
mod = new SkillModifier(Trigger.STAT_MODIFIER_KEY, parameter, targetSkills, amount, type);
}
public void updateKey(@NotNull String key) {
Validate.isTrue(mutable, "No longer mutable");
this.mod = new SkillModifier(key, mod.getParameter(), mod.getSkills(), mod.getValue(), mod.getType());
}
public List<SkillHandler<?>> getTargetSkills() {
@ -56,6 +59,7 @@ public class SkillModifierTrigger extends Trigger implements Removable {
* to a dynamically chosen skill handler.
*/
public void apply(PlayerData playerData, SkillHandler<?> skill) {
mutable = false;
mod.register(playerData.getMMOPlayerData(), skill);
}

View File

@ -5,14 +5,12 @@ import io.lumine.mythic.lib.api.stat.modifier.StatModifier;
import io.lumine.mythic.lib.player.modifier.ModifierType;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import org.apache.commons.lang.Validate;
import java.util.UUID;
public class StatTrigger extends Trigger implements Removable {
private final StatModifier statModifier;
public class StatTrigger extends Trigger implements Removable, Temporary {
private final StatModifier modifier;
private final String stat;
private final String modifierKey = TRIGGER_PREFIX + "." + UUID.randomUUID();
private final double amount;
public StatTrigger(MMOLineConfig config) {
@ -25,20 +23,18 @@ public class StatTrigger extends Trigger implements Removable {
Validate.isTrue(type.equals("FLAT") || type.equals("RELATIVE"));
stat = config.getString("stat");
amount = config.getDouble("amount");
statModifier = new StatModifier(modifierKey, stat, amount, ModifierType.valueOf(type));
modifier = new StatModifier(Trigger.STAT_MODIFIER_KEY, stat, amount, ModifierType.valueOf(type));
}
@Override
public void apply(PlayerData player) {
StatModifier prevModifier = player.getMMOPlayerData().getStatMap().getInstance(stat).getModifier(modifierKey);
if (prevModifier == null)
statModifier.register(player.getMMOPlayerData());
else
prevModifier.add(amount).register(player.getMMOPlayerData());
StatModifier prevModifier = player.getMMOPlayerData().getStatMap().getInstance(stat).getModifier(modifier.getUniqueId());
if (prevModifier == null) modifier.register(player.getMMOPlayerData());
else prevModifier.add(amount).register(player.getMMOPlayerData());
}
@Override
public void remove(PlayerData playerData) {
playerData.getMMOPlayerData().getStatMap().getInstance(stat).remove(modifierKey);
modifier.unregister(playerData.getMMOPlayerData());
}
}

View File

@ -7,7 +7,7 @@ import org.bukkit.Bukkit;
public abstract class Trigger {
public static String TRIGGER_PREFIX = "mmocore_trigger";
public static String STAT_MODIFIER_KEY = "mmocore_trigger";
private final long delay;
public Trigger(MMOLineConfig config) {

View File

@ -8,7 +8,6 @@ import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import javax.annotation.Nullable;
import java.util.Objects;
public class UnlockSkillTrigger extends Trigger implements Removable {
private final RegisteredSkill skill;
@ -17,20 +16,18 @@ public class UnlockSkillTrigger extends Trigger implements Removable {
super(config);
config.validateKeys("skill");
skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(config.getString("skill")));
skill = MMOCore.plugin.skillManager.getSkillOrThrow(config.getString("skill"));
}
@Override
public void apply(PlayerData playerData) {
final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill);
if (found != null)
playerData.unlock(found);
if (found != null) playerData.unlock(found);
}
@Override
public void remove(PlayerData playerData) {
final @Nullable ClassSkill found = playerData.getProfess().getSkill(skill);
if (found != null)
playerData.lock(found);
if (found != null) playerData.lock(found);
}
}

View File

@ -13,26 +13,23 @@ public class UnlockSlotTrigger extends Trigger implements Removable {
public UnlockSlotTrigger(MMOLineConfig config) {
super(config);
config.validateKeys("slot");
try {
slot = Integer.parseInt(config.getString("slot"));
}catch(NumberFormatException e){
throw new IllegalArgumentException("The slot should be a number");
} catch (NumberFormatException exception) {
throw new IllegalArgumentException("Slot should be a number");
}
Validate.isTrue(slot > 0 && slot <= MMOCore.plugin.configManager.maxSkillSlots, "The slot should be between 1 and " + MMOCore.plugin.configManager.maxSkillSlots);
Validate.isTrue(slot > 0, "Slot number must be positive");
}
@Override
public void apply(PlayerData player) {
final SkillSlot skillSlot = player.getProfess().getSkillSlot(slot);
if (!player.hasUnlocked(skillSlot))
player.unlock(skillSlot);
player.unlock(player.getProfess().getSkillSlot(slot));
}
@Override
public void remove(PlayerData player) {
final SkillSlot skillSlot = player.getProfess().getSkillSlot(slot);
if (player.hasUnlocked(skillSlot))
player.lock(skillSlot);
player.lock(player.getProfess().getSkillSlot(slot));
}
}

View File

@ -1,33 +0,0 @@
package net.Indyuce.mmocore.api.quest.trigger;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.spawnpoint.SpawnPoint;
import org.apache.commons.lang.Validate;
public class UnlockSpawnPointTrigger extends Trigger implements Removable {
private final SpawnPoint spawnPoint;
public UnlockSpawnPointTrigger(MMOLineConfig config) {
super(config);
config.validateKeys("spawn-point");
Validate.isTrue(MMOCore.plugin.spawnPointManager.isSpawnPoint(config.getString("spawn-point")), config.getString("spawn-point") + " is not a valid spawn point");
spawnPoint = MMOCore.plugin.spawnPointManager.getSpawnPoint(config.getString("spawn-point"));
}
@Override
public void apply(PlayerData player) {
if (!player.hasUnlocked(spawnPoint))
player.unlock(spawnPoint);
}
@Override
public void remove(PlayerData player) {
if (player.hasUnlocked(spawnPoint))
player.lock(spawnPoint);
}
}

View File

@ -2,6 +2,12 @@ package net.Indyuce.mmocore.api.quest.trigger.api;
import net.Indyuce.mmocore.api.player.PlayerData;
/**
* Cancelable triggers cause problems when letting the player reset
* their advancement on things they can spend points in/level up.
* If you give access to some resource to the player via a trigger,
* you must take it away when resetting their progression.
*/
public interface Removable {
public void remove(PlayerData playerData);
}

View File

@ -0,0 +1,12 @@
package net.Indyuce.mmocore.api.quest.trigger.api;
/**
* Non-permanent triggers are triggers which are not saved
* by the player and taken off when the player logs off,
* for instance temporary player modifiers. They need to
* be re-applied everytime the player logs back.
*
* @author jules
*/
public interface Temporary extends Removable {
}

View File

@ -1,15 +1,14 @@
package net.Indyuce.mmocore.api.util;
import com.google.common.collect.MultimapBuilder;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.gson.JsonArray;
import io.lumine.mythic.lib.gson.JsonObject;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.hologram.Hologram;
import io.lumine.mythic.lib.version.VersionMaterial;
import io.lumine.mythic.lib.version.VEnchantment;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
@ -18,11 +17,13 @@ import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.player.PlayerItemDamageEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import java.io.ByteArrayInputStream;
@ -34,21 +35,37 @@ public class MMOCoreUtils {
return item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName();
}
/**
* If a given player is not in the server cache, no information
* cannot be retrieved from that player (without using requests
* to MC servers obviously). In that case, the instance of
* OfflinePlayer is pretty much useless and it only wraps its
* UUID which was already known beforehand.
*
* @param player Offline player instance to test
* @return Is the instance valid
*/
public static boolean isInvalid(OfflinePlayer player) {
return player.getName() == null;
}
@Deprecated
public static String displayName(ItemStack item) {
return item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName()
: caseOnWords(item.getType().name().replace("_", " "));
: UtilityMethods.caseOnWords(item.getType().name().replace("_", " "));
}
/**
* @param current Current value of resource
* @param maxStat Maximum value of resource
* @return Clamped resource value. If the provided current value is 0,
* this function will return the maximum resource value.
* this function will return the maximum resource value.
*/
public static double fixResource(double current, double maxStat) {
return current == 0 ? maxStat : Math.max(0, Math.min(current, maxStat));
}
@Deprecated
public static String caseOnWords(String s) {
StringBuilder builder = new StringBuilder(s);
boolean isLastSpace = true;
@ -67,32 +84,9 @@ public class MMOCoreUtils {
return str.toLowerCase().replace("_", "-").replace(" ", "-");
}
/**
* @param value an integer you want to convert
* @return the string representing the integer but with roman letters
*/
@Deprecated
public static String toRomanNumerals(int value) {
LinkedHashMap<String, Integer> roman_numerals = new LinkedHashMap<String, Integer>();
roman_numerals.put("M", 1000);
roman_numerals.put("CM", 900);
roman_numerals.put("D", 500);
roman_numerals.put("CD", 400);
roman_numerals.put("C", 100);
roman_numerals.put("XC", 90);
roman_numerals.put("L", 50);
roman_numerals.put("XL", 40);
roman_numerals.put("X", 10);
roman_numerals.put("IX", 9);
roman_numerals.put("V", 5);
roman_numerals.put("IV", 4);
roman_numerals.put("I", 1);
String res = "";
for (Map.Entry<String, Integer> entry : roman_numerals.entrySet()) {
int matches = value / entry.getValue();
res += repeat(entry.getKey(), matches);
value = value % entry.getValue();
}
return res;
return intToRoman(value);
}
private static String repeat(String s, int n) {
@ -116,18 +110,38 @@ public class MMOCoreUtils {
* @param message Message to display
*/
public static void displayIndicator(Location loc, String message) {
Hologram holo = Hologram.create(loc, Arrays.asList(message));
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, () -> holo.despawn(), 20);
Hologram holo = Hologram.create(loc, MythicLib.plugin.parseColors(Collections.singletonList(message)));
Bukkit.getScheduler().runTaskLater(MMOCore.plugin, holo::despawn, 20);
}
public static boolean isPlayerHead(Material material) {
return material == VersionMaterial.PLAYER_HEAD.toMaterial() || material == VersionMaterial.PLAYER_WALL_HEAD.toMaterial();
return material == Material.PLAYER_HEAD || material == Material.PLAYER_WALL_HEAD;
}
public static ItemStack readIcon(String string) throws IllegalArgumentException {
String[] split = string.split(":");
Material material = Material.valueOf(split[0].toUpperCase().replace("-", "_").replace(" ", "_"));
return split.length > 1 ? MythicLib.plugin.getVersion().getWrapper().textureItem(material, Integer.parseInt(split[1])) : new ItemStack(material);
public static void addAllItemFlags(@NotNull ItemMeta meta) {
meta.addItemFlags(ItemFlag.values());
// Fix 1.20.6+ Paper bug that sucks. HIDE_ATTRIBUTES no longer works when item attribute list is empty
// TODO refactor with GUI update.
try {
meta.setAttributeModifiers(MultimapBuilder.hashKeys(0).hashSetValues(0).build());
} catch (Exception exception) {
// Not needed
}
}
@NotNull
public static ItemStack readIcon(String string) {
final String[] split = string.split(":");
final ItemStack item = new ItemStack(Material.valueOf(split[0].toUpperCase().replace("-", "_").replace(" ", "_")));
if (split.length > 1) {
final ItemMeta meta = item.getItemMeta();
meta.setCustomModelData(Integer.parseInt(split[1]));
item.setItemMeta(meta);
}
return item;
}
public static int getWorth(ItemStack[] items) {
@ -243,6 +257,7 @@ public class MMOCoreUtils {
return entities;
}
@Deprecated
public static void heal(LivingEntity target, double value) {
double max = target.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
double gain = Math.min(max, target.getHealth() + value) - target.getHealth();
@ -253,6 +268,8 @@ public class MMOCoreUtils {
target.setHealth(target.getHealth() + gain);
}
private static final Random RANDOM = new Random();
/**
* Method used when mining a custom block or fishing, as the corresponding
* interaction event is cancelled durability is not handled. This method is
@ -268,16 +285,20 @@ public class MMOCoreUtils {
*/
public static void decreaseDurability(Player player, EquipmentSlot slot, int damage) {
ItemStack item = player.getInventory().getItem(slot);
ItemStack item = UtilityMethods.getHandItem(player, slot);
if (item == null || item.getType().getMaxDurability() == 0 || item.getItemMeta().isUnbreakable())
return;
// Check unbreakable, ignore if necessary
final ItemMeta meta = item.getItemMeta();
final int unbreakingLevel = meta.getEnchantLevel(VEnchantment.UNBREAKING.get());
if (unbreakingLevel > 0 && RANDOM.nextInt(unbreakingLevel + 1) != 0) return;
PlayerItemDamageEvent event = new PlayerItemDamageEvent(player, item, damage);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled())
return;
ItemMeta meta = item.getItemMeta();
final int newDamage = event.getDamage() + ((Damageable) meta).getDamage();
if (newDamage >= item.getType().getMaxDurability()) {
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1F, 1F);
@ -294,4 +315,11 @@ public class MMOCoreUtils {
public static Location getCenterLocation(Entity entity) {
return entity.getBoundingBox().getCenter().toLocation(entity.getWorld());
}
public static void debug(String message) {
message = ChatColor.YELLOW + "Debug> " + ChatColor.WHITE + message;
for (Player player : Bukkit.getOnlinePlayers())
player.sendMessage(message);
Bukkit.getConsoleSender().sendMessage(message);
}
}

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmocore.api.util.input;
import net.Indyuce.mmocore.MMOCore;
import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.api.ConfigMessage;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -12,8 +13,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.Consumer;
import io.lumine.mythic.lib.MythicLib;
@Deprecated
public class AnvilGUI extends PlayerInput {
private final int containerId;
@ -24,7 +23,7 @@ public class AnvilGUI extends PlayerInput {
ItemStack paper = new ItemStack(Material.PAPER);
ItemMeta paperMeta = paper.getItemMeta();
paperMeta.setDisplayName(MMOCore.plugin.configManager.getSimpleMessage("player-input.anvil." + type.getLowerCaseName()).message());
paperMeta.setDisplayName(ConfigMessage.fromKey("player-input.anvil." + type.getLowerCaseName()).asLine());
paper.setItemMeta(paperMeta);
MythicLib.plugin.getVersion().getWrapper().handleInventoryCloseEvent(player);

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmocore.api.util.input;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.gui.api.PluginInventory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -37,8 +38,7 @@ public class ChatInput extends PlayerInput {
this.lastOpened = lastOpened;
player.closeInventory();
MMOCore.plugin.configManager.getSimpleMessage("player-input.chat." + inputType.getLowerCaseName()).send(player);
MMOCore.plugin.configManager.getSimpleMessage("player-input.chat.cancel").send(player);
ConfigMessage.fromKey("player-input.chat." + inputType.getLowerCaseName()).send(player);
}
@Override
@ -56,7 +56,7 @@ public class ChatInput extends PlayerInput {
if (event.getMessage().equals("cancel")) {
if (lastOpened != null)
Bukkit.getScheduler().runTask(MMOCore.plugin, () -> lastOpened.open());
MMOCore.plugin.configManager.getSimpleMessage("player-input.chat." + inputType.getLowerCaseName() + "-cancel").send(getPlayer());
ConfigMessage.fromKey("player-input.chat." + inputType.getLowerCaseName() + "-cancel").send(getPlayer());
} else
// Run sync
Bukkit.getScheduler().runTask(MMOCore.plugin, () -> output(event.getMessage()));

View File

@ -29,7 +29,6 @@ public abstract class PlayerInput implements Listener {
public enum InputType {
FRIEND_REQUEST,
PARTY_INVITE,
GUILD_INVITE,
GUILD_CREATION_TAG,

View File

@ -27,7 +27,7 @@ public class DepositCommand extends RegisteredCommand {
// if (sender instanceof Player)
// if (!isNearEnderchest(((Player) sender).getLocation())) {
// sender.sendMessage(MMOCore.plugin.configManager.getSimpleMessage("stand-near-enderchest"));
// sender.sendMessage(ConfigMessage.fromKey("stand-near-enderchest"));
// return true;
// }

View File

@ -49,7 +49,7 @@ public class GuildCommand extends RegisteredCommand {
final Request req = MMOCore.plugin.requestManager.getRequest(uuid);
Validate.isTrue(!req.isTimedOut() && req instanceof GuildInvite);
invite = (GuildInvite) req;
Validate.isTrue(MMOCore.plugin.dataProvider.getGuildManager().isRegistered(invite.getGuild()));
Validate.isTrue(MMOCore.plugin.nativeGuildManager.isRegistered(invite.getGuild()));
} catch (Exception exception) {
return true;
}

View File

@ -3,6 +3,7 @@ package net.Indyuce.mmocore.command;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.comp.flags.CustomFlag;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.command.api.RegisteredCommand;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
@ -26,15 +27,15 @@ public class PvpModeCommand extends RegisteredCommand {
}
if (!sender.hasPermission("mmocore.pvpmode")) {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-perms").send((Player) sender);
ConfigMessage.fromKey("not-enough-perms").send((Player) sender);
return false;
}
final PlayerData playerData = PlayerData.get(((Player) sender).getUniqueId());
final PlayerData playerData = PlayerData.get((Player) sender);
// Command cooldown
if (playerData.getCooldownMap().isOnCooldown(COOLDOWN_KEY)) {
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cooldown", "remaining", MythicLib.plugin.getMMOConfig().decimal.format(playerData.getCooldownMap().getCooldown(COOLDOWN_KEY))).send((Player) sender);
ConfigMessage.fromKey("pvp-mode.cooldown", "remaining", MythicLib.plugin.getMMOConfig().decimal.format(playerData.getCooldownMap().getCooldown(COOLDOWN_KEY))).send((Player) sender);
return true;
}
@ -45,12 +46,12 @@ public class PvpModeCommand extends RegisteredCommand {
if (playerData.getCombat().isInPvpMode() &&
MythicLib.plugin.getFlags().isFlagAllowed(playerData.getPlayer(), CustomFlag.PVP_MODE)) {
playerData.getCombat().setInvulnerable(MMOCore.plugin.configManager.pvpModeInvulnerabilityTimeCommand);
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.toggle.on-invulnerable", "time",
ConfigMessage.fromKey("pvp-mode.toggle.on-invulnerable", "time",
MythicLib.plugin.getMMOConfig().decimal.format(MMOCore.plugin.configManager.pvpModeInvulnerabilityTimeCommand)).send(playerData.getPlayer());
// Just send message otherwise
} else
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.toggle." + (playerData.getCombat().isInPvpMode() ? "on" : "off") + "-safe").send((Player) sender);
ConfigMessage.fromKey("pvp-mode.toggle." + (playerData.getCombat().isInPvpMode() ? "on" : "off") + "-safe").send((Player) sender);
return true;
}
}

View File

@ -2,8 +2,9 @@ package net.Indyuce.mmocore.command;
import io.lumine.mythic.lib.UtilityMethods;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.event.MMOCommandEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.command.api.RegisteredCommand;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import net.Indyuce.mmocore.manager.InventoryManager;
@ -19,16 +20,15 @@ import java.util.stream.Collectors;
public class SkillTreesCommand extends RegisteredCommand {
public SkillTreesCommand(ConfigurationSection config) {
super(config, ToggleableCommand.SKILL_TREES);
}
@Override
public boolean execute(@NotNull CommandSender sender, String s, String[] args) {
if (!sender.hasPermission("mmocore.skilltrees"))
return false;
if (!(sender instanceof Player player))
if (!(sender instanceof Player))
return false;
final Player player = (Player) sender;
PlayerData data = PlayerData.get(player);
MMOCommandEvent event = new MMOCommandEvent(data, "skilltrees");
Bukkit.getServer().getPluginManager().callEvent(event);
@ -45,7 +45,7 @@ public class SkillTreesCommand extends RegisteredCommand {
InventoryManager.TREE_VIEW.newInventory(data).open();
return true;
} else {
MMOCore.plugin.configManager.getSimpleMessage("no-skill-tree").send(player);
ConfigMessage.fromKey("no-skill-tree").send(player);
return false;
}
}
@ -73,7 +73,5 @@ public class SkillTreesCommand extends RegisteredCommand {
sender.sendMessage(ChatColor.RED + "Usage: /skilltrees");
return false;
}
}
}

View File

@ -1,11 +1,11 @@
package net.Indyuce.mmocore.command;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.event.MMOCommandEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.command.api.RegisteredCommand;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import net.Indyuce.mmocore.manager.InventoryManager;
import net.Indyuce.mmocore.api.event.MMOCommandEvent;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
@ -27,7 +27,7 @@ public class SkillsCommand extends RegisteredCommand {
if(event.isCancelled()) return true;
if (data.getUnlockedSkills().isEmpty()) {
MMOCore.plugin.configManager.getSimpleMessage("no-class-skill").send((Player) sender);
ConfigMessage.fromKey("no-class-skill").send((Player) sender);
return true;
}

View File

@ -1,6 +1,8 @@
package net.Indyuce.mmocore.command;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.eco.Withdraw;
import net.Indyuce.mmocore.command.api.RegisteredCommand;
import net.Indyuce.mmocore.command.api.ToggleableCommand;
import org.apache.commons.lang.Validate;
@ -11,8 +13,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import net.Indyuce.mmocore.api.eco.Withdraw;
public class WithdrawCommand extends RegisteredCommand {
public WithdrawCommand(ConfigurationSection config) {
super(config, ToggleableCommand.WITHDRAW);
@ -37,7 +37,7 @@ public class WithdrawCommand extends RegisteredCommand {
amount = Integer.parseInt(amountArgument);
Validate.isTrue(amount >= 0);
} catch (IllegalArgumentException exception) {
sender.sendMessage(MMOCore.plugin.configManager.getSimpleMessage("wrong-number", "arg", "" + args[0]).message());
sender.sendMessage(ConfigMessage.fromKey("wrong-number", "arg", "" + args[0]).asLine());
return true;
}
@ -50,14 +50,14 @@ public class WithdrawCommand extends RegisteredCommand {
int left = (int) MMOCore.plugin.economy.getEconomy().getBalance(player) - amount;
if (left < 0) {
MMOCore.plugin.configManager.getSimpleMessage("not-enough-money", "left", "" + -left).send(player);
ConfigMessage.fromKey("not-enough-money", "left", "" + -left).send(player);
return true;
}
MMOCore.plugin.economy.getEconomy().withdrawPlayer(player, amount);
request.withdrawAlgorythm(amount);
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
MMOCore.plugin.configManager.getSimpleMessage("withdrew", "worth", "" + amount).send(player);
ConfigMessage.fromKey("withdrew", "worth", amount).send(player);
return true;
}
}

View File

@ -58,8 +58,7 @@ public class CommandVerbose {
SKILL_TREE_POINTS,
RESET,
RESOURCE,
WAYPOINT,
SPAWN_POINT;
WAYPOINT;
}
private enum VerboseValue {

View File

@ -29,8 +29,7 @@ public class AdminCommandTreeNode extends CommandTreeNode {
addChild(new PointsCommandTreeNode("attr-realloc", this, PlayerData::setAttributeReallocationPoints, PlayerData::giveAttributeReallocationPoints, PlayerData::getAttributeReallocationPoints));
addChild(new PointsCommandTreeNode("skill-realloc", this, PlayerData::setSkillReallocationPoints, PlayerData::giveSkillReallocationPoints, PlayerData::getSkillReallocationPoints));
addChild(new PointsCommandTreeNode("skill-tree-realloc", this, PlayerData::setSkillTreeReallocationPoints, PlayerData::giveSkillTreeReallocationPoints, PlayerData::getSkillTreeReallocationPoints));
addChild(new SkillTreePointsCommandTreeNode(this, (playerData, integer, s) -> playerData.setSkillTreePoints(s, integer), (playerData, integer, s) -> playerData.giveSkillTreePoints(s, integer), ((playerData, s) -> playerData.getSkillTreePoint(s))));
addChild(new SpawnPointCommandTreeNode(this));
addChild(new SkillTreePointsCommandTreeNode(this, (playerData, integer, s) -> playerData.setSkillTreePoints(s, integer), (playerData, integer, s) -> playerData.giveSkillTreePoints(s, integer), PlayerData::getSkillTreePoints));
for (PlayerResource res : PlayerResource.values())
addChild(new ResourceCommandTreeNode(res.name().toLowerCase(), this, res));
}

View File

@ -1,20 +1,20 @@
package net.Indyuce.mmocore.command.rpg.admin;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.event.PlayerChangeClassEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.PlayerClass;
import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import net.Indyuce.mmocore.api.player.profess.SavedClassInformation;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
public class ClassCommandTreeNode extends CommandTreeNode {
public ClassCommandTreeNode(CommandTreeNode parent) {
super(parent, "class");
@ -52,9 +52,9 @@ public class ClassCommandTreeNode extends CommandTreeNode {
return CommandResult.SUCCESS;
(data.hasSavedClass(profess) ? data.getClassInfo(profess)
: new SavedClassInformation(MMOCore.plugin.dataProvider.getDataManager().getDefaultData())).load(profess, data);
: new SavedClassInformation(MMOCore.plugin.playerDataManager.getDefaultData())).load(profess, data);
if (data.isOnline()) {
MMOCore.plugin.configManager.getSimpleMessage("class-select", "class", profess.getName()).send(data.getPlayer());
ConfigMessage.fromKey("class-select", "class", profess.getName()).send(data.getPlayer());
data.getPlayer().playSound(data.getPlayer().getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1, 1);
}

View File

@ -1,22 +1,13 @@
package net.Indyuce.mmocore.command.rpg.admin;
import io.lumine.mythic.lib.api.player.MMOPlayerData;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.data.DataExport;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.manager.data.sql.SQLDataHandler;
import net.Indyuce.mmocore.manager.data.yaml.YAMLPlayerDataHandler;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
/**
* This command allows to transfer data from your actual storage type
@ -27,82 +18,15 @@ public class ExportDataTreeNode extends CommandTreeNode {
super(parent, "exportdata");
}
/**
* Amount of requests generated every batch
*/
private static final int BATCH_AMOUNT = 50;
/**
* Period in ticks
*/
private static final int BATCH_PERIOD = 20;
private static final DecimalFormat decFormat = new DecimalFormat("0.#");
@Override
@NotNull
public CommandResult execute(CommandSender sender, String[] strings) {
if (!MMOCore.plugin.dataProvider.getDataManager().getLoaded().isEmpty()) {
sender.sendMessage("Please make sure no players are logged in when using this command. " +
"If you are still seeing this message, restart your server and execute this command before any player logs in.");
return CommandResult.FAILURE;
}
// Export YAML to SQL
final boolean result = new DataExport<>(MMOCore.plugin.playerDataManager, sender).start(
() -> new YAMLPlayerDataHandler(MMOCore.plugin),
() -> new SQLDataHandler(new SQLDataSource(MMOCore.plugin)));
final List<UUID> playerIds = Arrays.stream(new File(MMOCore.plugin.getDataFolder() + "/userdata").listFiles())
.map(file -> UUID.fromString(file.getName().replace(".yml", ""))).collect(Collectors.toList());
// Initialize fake SQL data provider
final SQLDataHandler sqlHandler;
try {
sqlHandler = new SQLDataHandler(new SQLDataSource(MMOCore.plugin));
} catch (RuntimeException exception) {
sender.sendMessage("Could not initialize SQL provider (see console for stack trace): " + exception.getMessage());
return CommandResult.FAILURE;
}
final double timeEstimation = (double) playerIds.size() / BATCH_AMOUNT * BATCH_PERIOD / 20;
sender.sendMessage("Exporting " + playerIds.size() + " player data(s).. See console for details");
sender.sendMessage("Minimum expected time: " + decFormat.format(timeEstimation) + "s");
// Save player data
new BukkitRunnable() {
int errorCount = 0;
int batchCounter = 0;
@Override
public void run() {
for (int i = 0; i < BATCH_AMOUNT; i++) {
final int index = BATCH_AMOUNT * batchCounter + i;
/*
* Saving is done. Close connection to avoid memory
* leaks and ouput the results to the command executor
*/
if (index >= playerIds.size()) {
cancel();
sqlHandler.close();
MMOCore.plugin.getLogger().log(Level.WARNING, "Exported " + playerIds.size() + " player datas to SQL database. Total errors: " + errorCount);
return;
}
final UUID playerId = playerIds.get(index);
try {
final PlayerData offlinePlayerData = new PlayerData(new MMOPlayerData(playerId));
MMOCore.plugin.dataProvider.getDataManager().getDataHandler().loadData(offlinePlayerData);
// Player data is loaded, now it gets saved through SQL
sqlHandler.saveData(offlinePlayerData, false);
} catch (RuntimeException exception) {
errorCount++;
exception.printStackTrace();
}
}
batchCounter++;
}
}.runTaskTimerAsynchronously(MMOCore.plugin, 0, BATCH_PERIOD);
return CommandResult.SUCCESS;
return result ? CommandResult.SUCCESS : CommandResult.FAILURE;
}
}

View File

@ -1,29 +1,33 @@
package net.Indyuce.mmocore.command.rpg.admin;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttributes;
import net.Indyuce.mmocore.skilltree.tree.SkillTree;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import net.Indyuce.mmocore.experience.Profession;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import java.util.HashSet;
public class ResetCommandTreeNode extends CommandTreeNode {
public ResetCommandTreeNode(CommandTreeNode parent) {
super(parent, "reset");
addChild(new ResetClassesCommandTreeNode(this));
addChild(new ResetLevelsCommandTreeNode(this));
addChild(new ResetSkillsCommandTreeNode(this));
addChild(new ResetAllCommandTreeNode(this));
addChild(new ResetQuestsCommandTreeNode(this));
addChild(new ResetAttributesCommandTreeNode(this));
addChild(new ResetWaypointsCommandTreeNode(this));
addChild(new ResetSkillTreesCommandTreeNode(this));
addChild(new ResetAllCommandTreeNode(this));
}
@Override
@ -40,8 +44,7 @@ public class ResetCommandTreeNode extends CommandTreeNode {
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
@ -49,214 +52,245 @@ public class ResetCommandTreeNode extends CommandTreeNode {
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
MMOCore.plugin.dataProvider.getDataManager().getDefaultData().apply(data);
data.setExperience(0);
for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
data.getCollectionSkills().setExperience(profession, 0);
data.getCollectionSkills().setLevel(profession, 0);
}
MMOCore.plugin.classManager.getAll().forEach(data::unloadClassInfo);
data.getAttributes().getInstances().forEach(ins -> ins.setBase(0));
data.mapSkillLevels().forEach((skill, level) -> data.resetSkillLevel(skill));
data.setSkillTreePoints("global", 0);
for (SkillTree skillTree : data.getProfess().getSkillTrees()) {
data.resetSkillTree(skillTree);
data.setSkillTreePoints(skillTree.getId(), 0);
}
data.resetTimesClaimed();
for(int slot:data.mapBoundSkills().keySet())
data.unbindSkill(slot);
data.getQuestData().resetFinishedQuests();
data.getQuestData().start(null);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s data was succesfully reset.");
return CommandResult.SUCCESS;
}
}
public static class ResetWaypointsCommandTreeNode extends CommandTreeNode {
public ResetWaypointsCommandTreeNode(CommandTreeNode parent) {
super(parent, "waypoints");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
final boolean givePoints = args.length > 4 && args[4].equalsIgnoreCase("-reallocate");
PlayerData data = PlayerData.get(player);
data.getWaypoints().clear();
return CommandResult.SUCCESS;
}
}
public static class ResetQuestsCommandTreeNode extends CommandTreeNode {
public ResetQuestsCommandTreeNode(CommandTreeNode parent) {
super(parent, "quests");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
data.getQuestData().resetFinishedQuests();
data.getQuestData().start(null);
return CommandResult.SUCCESS;
}
}
public static class ResetSkillsCommandTreeNode extends CommandTreeNode {
public ResetSkillsCommandTreeNode(CommandTreeNode parent) {
super(parent, "skills");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
data.mapSkillLevels().forEach((skill, level) -> data.resetSkillLevel(skill));
while (data.hasSkillBound(0))
data.unbindSkill(0);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s skill data was succesfully reset.");
return CommandResult.SUCCESS;
}
}
public static class ResetSkillTreesCommandTreeNode extends CommandTreeNode {
public ResetSkillTreesCommandTreeNode(CommandTreeNode parent) {
super(parent, "skill-trees");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
for (SkillTree skillTree : data.getProfess().getSkillTrees())
data.resetSkillTree(skillTree);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s skill-tree data was succesfully reset.");
return CommandResult.SUCCESS;
}
}
public class ResetAttributesCommandTreeNode extends CommandTreeNode {
public ResetAttributesCommandTreeNode(CommandTreeNode parent) {
super(parent, "attributes");
addParameter(Parameter.PLAYER);
addParameter(new Parameter("(-reallocate)", (explore, list) -> list.add("-reallocate")));
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
/*
* force reallocating of player attribute points
*/
if (args.length > 4 && args[4].equalsIgnoreCase("-reallocate")) {
int points = 0;
for (PlayerAttributes.AttributeInstance ins : data.getAttributes().getInstances()) {
points += ins.getBase();
ins.setBase(0);
}
data.giveAttributePoints(points);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s attribute points spendings were successfully reset.");
return CommandResult.SUCCESS;
}
data.getAttributes().getInstances().forEach(ins -> ins.setBase(0));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s attributes were succesfully reset.");
return CommandResult.SUCCESS;
}
}
public static class ResetLevelsCommandTreeNode extends CommandTreeNode {
public ResetLevelsCommandTreeNode(CommandTreeNode parent) {
super(parent, "levels");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get(player);
data.setLevel(MMOCore.plugin.dataProvider.getDataManager().getDefaultData().getLevel());
data.setExperience(0);
for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
data.getCollectionSkills().setExperience(profession, 0);
data.getCollectionSkills().setLevel(profession, 0);
profession.getExperienceTable().reset(data, profession);
}
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET,
ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s levels were succesfully reset.");
ResetClassesCommandTreeNode.resetClasses(data);
ResetLevelsCommandTreeNode.resetLevels(data);
ResetSkillsCommandTreeNode.resetSkills(data);
ResetQuestsCommandTreeNode.resetQuests(data);
ResetAttributesCommandTreeNode.resetAttributes(data, givePoints);
ResetWaypointsCommandTreeNode.resetWaypoints(data);
ResetSkillTreesCommandTreeNode.resetSkillTrees(data);
// Reset times-claimed not being properly emptied otherwise
data.getItemClaims().clear();
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s data was successfully reset.");
return CommandResult.SUCCESS;
}
}
}
class ResetWaypointsCommandTreeNode extends CommandTreeNode {
public ResetWaypointsCommandTreeNode(CommandTreeNode parent) {
super(parent, "waypoints");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetWaypoints(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s waypoints were successfully reset.");
return CommandResult.SUCCESS;
}
static void resetWaypoints(@NotNull PlayerData playerData) {
playerData.getWaypoints().clear();
}
}
class ResetQuestsCommandTreeNode extends CommandTreeNode {
public ResetQuestsCommandTreeNode(CommandTreeNode parent) {
super(parent, "quests");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetQuests(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s quests were successfully reset.");
return CommandResult.SUCCESS;
}
static void resetQuests(@NotNull PlayerData data) {
data.getQuestData().resetFinishedQuests();
data.getQuestData().start(null);
}
}
class ResetSkillsCommandTreeNode extends CommandTreeNode {
public ResetSkillsCommandTreeNode(CommandTreeNode parent) {
super(parent, "skills");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetSkills(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s skill data was successfully reset.");
return CommandResult.SUCCESS;
}
static void resetSkills(@NotNull PlayerData data) {
data.mapSkillLevels().forEach((skill, ignored) -> data.resetSkillLevel(skill));
while (data.hasSkillBound(0)) data.unbindSkill(0);
data.setUnlockedItems(new HashSet<>()); // TODO class-specific unlockables etc.
}
}
class ResetSkillTreesCommandTreeNode extends CommandTreeNode {
public ResetSkillTreesCommandTreeNode(CommandTreeNode parent) {
super(parent, "skill-trees");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetSkillTrees(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s skill-tree data was successfully reset.");
return CommandResult.SUCCESS;
}
// TODO option to reallocate skill tree points instead of not giving any back
static void resetSkillTrees(@NotNull PlayerData data) {
data.resetSkillTrees();
}
}
class ResetAttributesCommandTreeNode extends CommandTreeNode {
public ResetAttributesCommandTreeNode(CommandTreeNode parent) {
super(parent, "attributes");
addParameter(Parameter.PLAYER);
addParameter(new Parameter("(-reallocate)", (explore, list) -> list.add("-reallocate")));
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
final boolean givePoints = args.length > 4 && args[4].equalsIgnoreCase("-reallocate");
resetAttributes(PlayerData.get(player), givePoints);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s attributes were successfully reset.");
return CommandResult.SUCCESS;
}
static void resetAttributes(@NotNull PlayerData data, boolean givePoints) {
// Give back attribute points
if (givePoints) {
int points = 0;
for (PlayerAttributes.AttributeInstance ins : data.getAttributes().getInstances()) {
points += ins.getBase();
ins.setBase(0);
}
data.giveAttributePoints(points);
return;
}
for (PlayerAttribute attribute : MMOCore.plugin.attributeManager.getAll()) {
attribute.resetAdvancement(data, true);
data.getAttributes().getInstance(attribute).setBase(0);
}
}
}
class ResetLevelsCommandTreeNode extends CommandTreeNode {
public ResetLevelsCommandTreeNode(CommandTreeNode parent) {
super(parent, "levels");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetLevels(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s levels were successfully reset.");
return CommandResult.SUCCESS;
}
static void resetLevels(@NotNull PlayerData data) {
// Class
data.setLevel(MMOCore.plugin.playerDataManager.getDefaultData().getLevel());
data.setExperience(0);
data.getProfess().resetAdvancement(data, true);
// Professions
for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
data.getCollectionSkills().setExperience(profession, 0);
data.getCollectionSkills().setLevel(profession, 0);
profession.resetAdvancement(data, true);
}
}
}
class ResetClassesCommandTreeNode extends CommandTreeNode {
public ResetClassesCommandTreeNode(CommandTreeNode parent) {
super(parent, "classes");
addParameter(Parameter.PLAYER);
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 4) return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
resetClasses(PlayerData.get(player));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.RESET, ChatColor.GOLD + player.getName() + ChatColor.YELLOW + "'s classes were successfully reset.");
return CommandResult.SUCCESS;
}
static void resetClasses(@NotNull PlayerData data) {
MMOCore.plugin.classManager.getAll().forEach(data::unloadClassInfo);
MMOCore.plugin.playerDataManager.getDefaultData().apply(data);
data.setClass(MMOCore.plugin.classManager.getDefaultClass());
}
}

View File

@ -30,7 +30,7 @@ public class SaveDataTreeNode extends CommandTreeNode {
return CommandResult.FAILURE;
}
MMOCore.plugin.dataProvider.getDataManager().getDataHandler().saveData(PlayerData.get(player), false);
MMOCore.plugin.playerDataManager.getDataHandler().saveData(PlayerData.get(player), false);
return CommandResult.SUCCESS;
}

View File

@ -68,7 +68,6 @@ public class SkillCommandTreeNode extends CommandTreeNode {
return CommandResult.FAILURE;
}
int amount;
try {
amount = Integer.parseInt(args[5]);
@ -78,7 +77,7 @@ public class SkillCommandTreeNode extends CommandTreeNode {
}
int value = change.apply(playerData.getSkillLevel(skill), amount);
playerData.setSkillLevel(skill, value);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + player.getName() + ChatColor.YELLOW
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + player.getName() + ChatColor.YELLOW
+ " is now level " + ChatColor.GOLD + value + ChatColor.YELLOW + " for " + skill.getName() + ".");
return CommandResult.SUCCESS;
}
@ -101,31 +100,31 @@ public class SkillCommandTreeNode extends CommandTreeNode {
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
sender.sendMessage(ChatColor.RED + "Could not find player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData playerData = PlayerData.get(player);
ClassSkill skill = playerData.getProfess().getSkill(args[4]);
if (skill == null) {
sender.sendMessage(ChatColor.RED + "The player's class doesn't have a skill called " + args[4] + ".");
sender.sendMessage(ChatColor.RED + "Class doesn't have a skill called " + args[4] + ".");
return CommandResult.FAILURE;
}
if (lock) {
if (!playerData.hasUnlocked(skill)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill " + skill.getSkill().getName() + " is already locked" + " for " + player.getName());
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "Skill " + skill.getSkill().getName() + " already locked for " + player.getName());
return CommandResult.SUCCESS;
}
playerData.lock(skill);
} else {
if (playerData.hasUnlocked(skill)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The skill " + skill.getSkill().getName() + " is already unlocked" + " for " + player.getName());
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "Skill " + skill.getSkill().getName() + " already unlocked for " + player.getName());
return CommandResult.SUCCESS;
}
playerData.unlock(skill);
}
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill.getSkill().getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName()));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "Skill " + ChatColor.GOLD + skill.getSkill().getName() + ChatColor.YELLOW + " now " + (lock ? "locked" : "unlocked") + " for " + ChatColor.GOLD + player.getName());
return CommandResult.SUCCESS;
}
}

View File

@ -4,6 +4,7 @@ import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.binding.BoundSkillInfo;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import net.Indyuce.mmocore.skill.ClassSkill;
@ -53,12 +54,8 @@ public class SlotCommandTreeNode extends CommandTreeNode {
sender.sendMessage(ChatColor.RED + "The slot can't be negative.");
return CommandResult.FAILURE;
}
if (slot > MMOCore.plugin.configManager.maxSkillSlots) {
sender.sendMessage(ChatColor.RED + "The slot can't be higher than " + MMOCore.plugin.configManager.maxSkillSlots + ".");
return CommandResult.FAILURE;
}
SkillSlot skillSlot = playerData.getProfess().getSkillSlot(slot);
if(skillSlot.isUnlockedByDefault()){
if (skillSlot.isUnlockedByDefault()) {
sender.sendMessage(ChatColor.RED + "You can't lock a skill that is unlocked by default.");
return CommandResult.FAILURE;
}
@ -76,7 +73,7 @@ public class SlotCommandTreeNode extends CommandTreeNode {
}
playerData.unlock(skillSlot);
}
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill slot " + skillSlot.getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName()));
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "The skill slot " + skillSlot.getName() + " is now " + (lock ? "locked" : "unlocked" + " for " + player.getName()));
return CommandResult.SUCCESS;
}
}
@ -117,7 +114,7 @@ public class SlotCommandTreeNode extends CommandTreeNode {
}
playerData.bindSkill(slot, skill);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill.getSkill().getHandler().getId() + " is now bound to the slot " + slot);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + "Skill " + ChatColor.GOLD + skill.getSkill().getHandler().getId() + ChatColor.YELLOW + " now bound to slot " + ChatColor.GOLD + slot);
return CommandResult.SUCCESS;
}
}
@ -147,11 +144,10 @@ public class SlotCommandTreeNode extends CommandTreeNode {
sender.sendMessage(ChatColor.RED + args[4] + " is not a valid number.");
return CommandResult.FAILURE;
}
String skill = playerData.hasSkillBound(slot) ? playerData.getBoundSkill(slot).getSkill().getHandler().getId() : "none";
if (playerData.hasSkillBound(slot))
playerData.unbindSkill(slot);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.GOLD + "The skill " + skill + " has been unbounded from the slot " + slot);
final BoundSkillInfo found = playerData.unbindSkill(slot);
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.YELLOW + (found != null ?
"Skill " + ChatColor.GOLD + found.getClassSkill().getSkill().getName() + ChatColor.YELLOW + " was taken off the slot " + ChatColor.GOLD + slot :
"Could not find skill at slot " + ChatColor.GOLD + slot));
return CommandResult.SUCCESS;
}
}

View File

@ -1,81 +0,0 @@
package net.Indyuce.mmocore.command.rpg.admin;
import io.lumine.mythic.lib.command.api.CommandTreeNode;
import io.lumine.mythic.lib.command.api.Parameter;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.skill.binding.SkillSlot;
import net.Indyuce.mmocore.command.api.CommandVerbose;
import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.spawnpoint.SpawnPoint;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class SpawnPointCommandTreeNode extends CommandTreeNode {
public SpawnPointCommandTreeNode(CommandTreeNode parent) {
super(parent, "slot");
addChild(new LockSpawnPointCommand(this, "lock", true));
addChild(new LockSpawnPointCommand(this, "unlock", false));
}
public class LockSpawnPointCommand extends CommandTreeNode {
private final boolean lock;
public LockSpawnPointCommand(CommandTreeNode parent, String id, boolean lock) {
super(parent, id);
this.lock = lock;
addParameter(Parameter.PLAYER);
addParameter(new Parameter("spawnpoint",
(explorer, list) -> MMOCore.plugin.spawnPointManager.getAll().stream().map(spawnPoint -> spawnPoint.getId()).forEach(list::add)));
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 5)
return CommandResult.THROW_USAGE;
Player player = Bukkit.getPlayer(args[3]);
if (player == null) {
sender.sendMessage(ChatColor.RED + "Could not find the player called " + args[3] + ".");
return CommandResult.FAILURE;
}
PlayerData playerData = PlayerData.get(player);
if (!MMOCore.plugin.spawnPointManager.isSpawnPoint(args[4])) {
sender.sendMessage(ChatColor.RED + "Could not find the spawnpoint called " + args[4] + ".");
return CommandResult.FAILURE;
}
SpawnPoint spawnPoint = MMOCore.plugin.spawnPointManager.getSpawnPoint(args[4]);
if (lock) {
if (!playerData.hasUnlocked(spawnPoint)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The spawn point " +
spawnPoint.getId() + " is already locked for " + player.getName());
return CommandResult.SUCCESS;
}
playerData.lock(spawnPoint);
} else {
if (playerData.hasUnlocked(spawnPoint)) {
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SKILL, ChatColor.RED + "The spawn point " +
spawnPoint.getId() + " is already unlocked for " + player.getName());
return CommandResult.SUCCESS;
}
playerData.unlock(spawnPoint);
}
CommandVerbose.verbose(sender, CommandVerbose.CommandType.SPAWN_POINT, ChatColor.GOLD + "The spawn point " +
spawnPoint.getId() + " is now " + (lock ? "locked" : "unlocked for " + player.getName()));
return CommandResult.SUCCESS;
}
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
return CommandResult.THROW_USAGE;
}
}

View File

@ -49,7 +49,7 @@ public class CreateCommandTreeNode extends CommandTreeNode {
if (args[2].equalsIgnoreCase("main")) {
MMOCore.plugin.boosterManager.register(new Booster(args.length > 5 ? args[5] : null, extra, length));
new ConfigMessage("booster-main").addPlaceholders("multiplier", "" + (1 + extra)).send(Bukkit.getOnlinePlayers());
ConfigMessage.fromKey("booster-main").addPlaceholders("multiplier", "" + (1 + extra)).send(Bukkit.getOnlinePlayers());
Bukkit.getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1));
return CommandResult.SUCCESS;
}
@ -62,7 +62,7 @@ public class CreateCommandTreeNode extends CommandTreeNode {
Profession profession = MMOCore.plugin.professionManager.get(format);
MMOCore.plugin.boosterManager.register(new Booster(args.length > 5 ? args[5] : null, profession, extra, length));
new ConfigMessage("booster-skill").addPlaceholders("multiplier", "" + (1 + extra), "profession", profession.getName())
ConfigMessage.fromKey("booster-skill").addPlaceholders("multiplier", "" + (1 + extra), "profession", profession.getName())
.send(Bukkit.getOnlinePlayers());
Bukkit.getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1));
return CommandResult.SUCCESS;

View File

@ -12,30 +12,30 @@ import org.bukkit.entity.Player;
public class StatModifiersCommandTreeNode extends CommandTreeNode {
public StatModifiersCommandTreeNode(CommandTreeNode parent) {
super(parent, "statmods");
public StatModifiersCommandTreeNode(CommandTreeNode parent) {
super(parent, "statmods");
addParameter(new Parameter("<stat>", (explorer, list) -> list.add("STAT_ID")));
}
}
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 3)
return CommandResult.THROW_USAGE;
@Override
public CommandResult execute(CommandSender sender, String[] args) {
if (args.length < 3)
return CommandResult.THROW_USAGE;
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command can only be used by a player.");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get((Player) sender);
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "This command can only be used by a player.");
return CommandResult.FAILURE;
}
PlayerData data = PlayerData.get((Player) sender);
StatInstance instance = data.getMMOPlayerData().getStatMap().getInstance(UtilityMethods.enumName(args[2]));
sender.sendMessage("Stat Modifiers (" + instance.getKeys().size() + "):");
for (String key : instance.getKeys()) {
StatModifier mod = instance.getModifier(key);
sender.sendMessage("- " + key + ": " + mod.getValue() + " " + mod.getType().name());
}
StatInstance instance = data.getMMOPlayerData().getStatMap().getInstance(UtilityMethods.enumName(args[2]));
sender.sendMessage("Stat Modifiers (" + instance.getKeys().size() + "):");
for (String key : instance.getKeys()) {
StatModifier mod = instance.getModifier(key);
sender.sendMessage("-> '" + key + "' " + mod.getValue() + " " + mod.getType().name() + " " + mod.getSlot() + " " + mod.getSource());
}
return CommandResult.SUCCESS;
}
return CommandResult.SUCCESS;
}
}

View File

@ -4,7 +4,7 @@ import io.lumine.mythic.api.adapters.AbstractItemStack;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.api.drops.DropMetadata;
import io.lumine.mythic.api.drops.IItemDrop;
import io.lumine.mythic.bukkit.adapters.BukkitItemStack;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.core.drops.Drop;
import net.Indyuce.mmocore.util.item.CurrencyItemBuilder;
@ -27,7 +27,8 @@ public class CurrencyItemDrop extends Drop implements IItemDrop {
@Override
public AbstractItemStack getDrop(DropMetadata dropMetadata, double v) {
return new BukkitItemStack(new CurrencyItemBuilder(key, random(minw, maxw)).build());
// Not great wrt to performance. Should build the item like MM does
return BukkitAdapter.adapt(new CurrencyItemBuilder(key, random(minw, maxw)).build());
}
private int random(int a, int b) {

View File

@ -4,7 +4,7 @@ import io.lumine.mythic.api.adapters.AbstractItemStack;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.api.drops.DropMetadata;
import io.lumine.mythic.api.drops.IItemDrop;
import io.lumine.mythic.bukkit.adapters.BukkitItemStack;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.core.drops.Drop;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem;
@ -49,7 +49,8 @@ public class GoldPouchDrop extends Drop implements IItemDrop {
}
nbt.addTag(new ItemTag("RpgPouchSize", 18), new ItemTag("RpgPouchMob", true), new ItemTag("RpgPouchInventory", MMOCoreUtils.toBase64(content)));
return new BukkitItemStack(nbt.toItem());
// Not great wrt to performance. Should build the item like MM does
return BukkitAdapter.adapt(nbt.toItem());
}
private ItemStack setAmount(ItemStack item, int amount) {

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.comp.placeholder;
import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility;
import org.bukkit.OfflinePlayer;
import io.lumine.mythic.lib.MythicLib;
@ -7,7 +8,9 @@ import io.lumine.mythic.lib.MythicLib;
public class DefaultParser implements PlaceholderParser {
@Override
@BackwardsCompatibility(version = "1.12-SNAPSHOT")
public String parse(OfflinePlayer player, String string) {
// TODO remove use of confusing non-PAPI %player% placeholder
return MythicLib.plugin.parseColors(string.replace("%player%", player.getName()));
}
}

View File

@ -12,8 +12,10 @@ import net.Indyuce.mmocore.api.quest.PlayerQuests;
import net.Indyuce.mmocore.experience.PlayerProfessions;
import net.Indyuce.mmocore.experience.Profession;
import net.Indyuce.mmocore.party.AbstractParty;
import net.Indyuce.mmocore.skill.CastableSkill;
import net.Indyuce.mmocore.skill.ClassSkill;
import net.Indyuce.mmocore.skill.RegisteredSkill;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
@ -58,10 +60,11 @@ public class RPGPlaceholders extends PlaceholderExpansion {
public String onRequest(OfflinePlayer player, String identifier) {
if (!PlayerData.has(player.getUniqueId()))
return null;
final PlayerData playerData = PlayerData.get(player);
PlayerData playerData = PlayerData.get(player);
if (identifier.equals("mana_icon"))
return playerData.getProfess().getManaDisplay().getIcon();
if (identifier.equals("mana_name"))
return playerData.getProfess().getManaDisplay().getName();
@ -70,24 +73,49 @@ public class RPGPlaceholders extends PlaceholderExpansion {
else if (identifier.startsWith("skill_level_")) {
String id = identifier.substring(12);
RegisteredSkill skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(id), "Could not find skill with ID '" + id + "'");
RegisteredSkill skill = MMOCore.plugin.skillManager.getSkillOrThrow(id);
return String.valueOf(playerData.getSkillLevel(skill));
} else if (identifier.startsWith("skill_modifier_") || identifier.startsWith("skill_parameter_")) {
String[] ids = (identifier.startsWith("skill_modifier_") ? identifier.substring(15) : identifier.substring(16)).split(":");
String parameterId = ids[0];
String skillId = ids[1];
RegisteredSkill skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(skillId), "Could not find skill with ID '" + skillId + "'");
ClassSkill classSkill = playerData.getProfess().getSkill(skill);
double value = classSkill.toCastable(playerData).getParameter(parameterId);
}
/*
* Given a skill slot number (integer) and a parameter name,
* return the player's value of that skill parameter from that
* specific skill slot.
*/
else if (identifier.startsWith("bound_skill_parameter_")) {
final String[] ids = identifier.substring(22).split(":");
final String parameterId = ids[0];
final int skillSlot = Integer.parseInt(ids[1]);
final ClassSkill found = playerData.getBoundSkill(skillSlot);
if (found == null) return "";
final CastableSkill castable = found.toCastable(playerData);
final double value = playerData.getMMOPlayerData().getSkillModifierMap().calculateValue(castable, parameterId);
return MythicLib.plugin.getMMOConfig().decimal.format(value);
} else if (identifier.startsWith("attribute_points_spent_")) {
String attributeId = identifier.substring(31);
PlayerAttributes.AttributeInstance attributeInstance = Objects.requireNonNull(playerData.getAttributes().getInstance(attributeId), "Could not find attribute with ID '" + attributeId + "'");
return String.valueOf(attributeInstance.getSpent());
} else if (identifier.equals("level_percent")) {
}
// Returns a player's value of a skill parameter.
else if (identifier.startsWith("skill_modifier_") || identifier.startsWith("skill_parameter_")) {
final String[] ids = identifier.substring(identifier.startsWith("skill_modifier_") ? 15 : 16).split(":");
final String parameterId = ids[0];
final String skillId = ids[1];
final RegisteredSkill skill = Objects.requireNonNull(MMOCore.plugin.skillManager.getSkill(skillId), "Could not find skill with ID '" + skillId + "'");
final CastableSkill castable = playerData.getProfess().getSkill(skill).toCastable(playerData);
final double value = playerData.getMMOPlayerData().getSkillModifierMap().calculateValue(castable, parameterId);
return MythicLib.plugin.getMMOConfig().decimal.format(value);
}
else if (identifier.startsWith("attribute_points_spent_")) {
final String attributeId = identifier.substring(23);
final PlayerAttributes.AttributeInstance attributeInstance = Objects.requireNonNull(playerData.getAttributes().getInstance(attributeId), "Could not find attribute with ID '" + attributeId + "'");
return String.valueOf(attributeInstance.getBase());
}
else if (identifier.equals("level_percent")) {
double current = playerData.getExperience(), next = playerData.getLevelUpExperience();
return MythicLib.plugin.getMMOConfig().decimal.format(current / next * 100);
} else if (identifier.equals("health"))
}
else if (identifier.equals("health"))
return StatManager.format("MAX_HEALTH", player.getPlayer().getHealth());
else if (identifier.equals("max_health"))
@ -127,19 +155,42 @@ public class RPGPlaceholders extends PlaceholderExpansion {
else if (identifier.startsWith("since_last_hit"))
return playerData.isInCombat() ? MythicLib.plugin.getMMOConfig().decimal.format((System.currentTimeMillis() - playerData.getCombat().getLastHit()) / 1000.) : "-1";
// Returns the bound skill ID
else if (identifier.startsWith("id_bound_")) {
final int slot = Math.max(1, Integer.parseInt(identifier.substring(9)));
final ClassSkill info = playerData.getBoundSkill(slot);
return info == null ? "" : info.getSkill().getHandler().getId();
}
// Returns the casting slot taking into account the skill slot offset
else if (identifier.startsWith("cast_slot_offset_")) {
final Player online = player.getPlayer();
Validate.notNull(online, "Player is offline");
final int slot = Integer.parseInt(identifier.substring(17));
return String.valueOf(slot + (online.getInventory().getHeldItemSlot() < slot ? 1 : 0));
}
// Is there a passive skill bound to given slot
else if (identifier.startsWith("passive_bound_")) {
final int slot = Integer.parseInt(identifier.substring(14));
final ClassSkill skill = playerData.getBoundSkill(slot);
return String.valueOf(skill != null && skill.getSkill().getTrigger().isPassive());
}
// Returns the bound skill name
else if (identifier.startsWith("bound_")) {
int slot = Math.max(0, Integer.parseInt(identifier.substring(6)));
if (playerData.hasSkillBound(slot)) {
ClassSkill skill = playerData.getBoundSkill(slot);
return (playerData.getCooldownMap().isOnCooldown(skill) ? ChatColor.RED : ChatColor.GREEN) + skill.getSkill().getName();
} else
return MMOCore.plugin.configManager.noSkillBoundPlaceholder;
} else if (identifier.startsWith("cooldown_bound_")) {
final int slot = Math.max(1, Integer.parseInt(identifier.substring(6)));
final ClassSkill skill = playerData.getBoundSkill(slot);
if (skill == null) return MMOCore.plugin.configManager.noSkillBoundPlaceholder;
return (playerData.getCooldownMap().isOnCooldown(skill) ? ChatColor.RED : ChatColor.GREEN) + skill.getSkill().getName();
}
// Returns cooldown of skill bound at given slot
else if (identifier.startsWith("cooldown_bound_")) {
int slot = Math.max(0, Integer.parseInt(identifier.substring(15)));
if (playerData.hasSkillBound(slot))
return "" + playerData.getCooldownMap().getCooldown(playerData.getBoundSkill(slot));
else
return MMOCore.plugin.configManager.noSkillBoundPlaceholder;
return Double.toString(playerData.getCooldownMap().getCooldown(playerData.getBoundSkill(slot)));
else return MMOCore.plugin.configManager.noSkillBoundPlaceholder;
} else if (identifier.startsWith("profession_experience_"))
return MythicLib.plugin.getMMOConfig().decimal.format(
playerData.getCollectionSkills().getExperience(identifier.substring(22).replace(" ", "-").replace("_", "-").toLowerCase()));

View File

@ -1,22 +1,23 @@
package net.Indyuce.mmocore.comp.profile;
import fr.phoenixdevt.profile.ProfileDataModule;
import fr.phoenixdevt.profile.ProfileProvider;
import fr.phoenixdevt.profile.event.ProfileCreateEvent;
import fr.phoenixdevt.profile.event.ProfileRemoveEvent;
import fr.phoenixdevt.profile.event.ProfileSelectEvent;
import fr.phoenixdevt.profile.event.ProfileUnloadEvent;
import fr.phoenixdevt.profile.placeholder.PlaceholderRequest;
import fr.phoenixdevt.profiles.ProfileDataModule;
import fr.phoenixdevt.profiles.ProfileProvider;
import fr.phoenixdevt.profiles.event.ProfileCreateEvent;
import fr.phoenixdevt.profiles.event.ProfileRemoveEvent;
import fr.phoenixdevt.profiles.event.ProfileSelectEvent;
import fr.phoenixdevt.profiles.event.ProfileUnloadEvent;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.event.SynchronizedDataLoadEvent;
import io.lumine.mythic.lib.comp.profile.ProfileMode;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.manager.InventoryManager;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class ForceClassProfileDataModule implements ProfileDataModule, Listener {
public class ForceClassProfileDataModule implements ProfileDataModule {
public ForceClassProfileDataModule() {
final ProfileProvider<?> provider = Bukkit.getServicesManager().getRegistration(ProfileProvider.class).getProvider();
provider.registerModule(this);
@ -27,27 +28,24 @@ public class ForceClassProfileDataModule implements ProfileDataModule, Listener
return MMOCore.plugin;
}
@Override
public boolean hasPlaceholders() {
return false;
}
@Override
public String getIdentifier() {
return "mmocore_force_class";
}
@Override
public void processPlaceholderRequest(PlaceholderRequest placeholderRequest) {
throw new RuntimeException("Not supported");
}
/**
* Force class before profile creation
*/
@EventHandler
public void onProfileCreate(ProfileCreateEvent event) {
final PlayerData playerData = PlayerData.get(event.getPlayerData().getUniqueId());
// Proxy-based profiles
if (MythicLib.plugin.getProfileMode() == ProfileMode.PROXY) {
event.validate(this);
return;
}
final PlayerData playerData = PlayerData.get(event.getPlayerData().getPlayer());
InventoryManager.CLASS_SELECT.newInventory(playerData, () -> event.validate(this)).open();
}
@ -58,6 +56,16 @@ public class ForceClassProfileDataModule implements ProfileDataModule, Listener
public void onDataLoad(SynchronizedDataLoadEvent event) {
if (event.getManager().getOwningPlugin().equals(MMOCore.plugin)) {
final PlayerData playerData = (PlayerData) event.getHolder();
// Proxy-based profiles
if (!event.hasProfileEvent()) {
Validate.isTrue(MythicLib.plugin.getProfileMode() == ProfileMode.PROXY, "Listened to a data load event with no profile event attached but proxy-based profiles are disabled");
if (playerData.getProfess().equals(MMOCore.plugin.classManager.getDefaultClass()))
InventoryManager.CLASS_SELECT.newInventory(playerData, () -> {
}).open();
return;
}
final ProfileSelectEvent event1 = (ProfileSelectEvent) event.getProfileEvent();
// Validate if necessary

View File

@ -1,9 +1,10 @@
package net.Indyuce.mmocore.comp.profile;
import fr.phoenixdevt.profile.ProfileDataModule;
import fr.phoenixdevt.profile.event.ProfileCreateEvent;
import fr.phoenixdevt.profile.event.ProfileRemoveEvent;
import fr.phoenixdevt.profile.placeholder.PlaceholderRequest;
import fr.phoenixdevt.profiles.ProfileDataModule;
import fr.phoenixdevt.profiles.event.ProfileCreateEvent;
import fr.phoenixdevt.profiles.event.ProfileRemoveEvent;
import fr.phoenixdevt.profiles.placeholder.PlaceholderProcessor;
import fr.phoenixdevt.profiles.placeholder.PlaceholderRequest;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.player.MMOPlayerData;
import net.Indyuce.mmocore.MMOCore;
@ -11,10 +12,9 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.PlayerAttribute;
import net.Indyuce.mmocore.experience.Profession;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class MMOCoreProfileDataModule implements ProfileDataModule, Listener {
public class MMOCoreProfileDataModule implements ProfileDataModule, PlaceholderProcessor {
@Override
public JavaPlugin getOwningPlugin() {
@ -22,13 +22,13 @@ public class MMOCoreProfileDataModule implements ProfileDataModule, Listener {
}
@Override
public boolean hasPlaceholders() {
return true;
public String getIdentifier() {
return "mmocore";
}
@Override
public String getIdentifier() {
return "mmocore";
public ProfileDataModule getDataModule() {
return this;
}
@Override

View File

@ -10,6 +10,7 @@ import com.sk89q.worldguard.session.MoveType;
import com.sk89q.worldguard.session.Session;
import io.lumine.mythic.lib.MythicLib;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
public class PvPFlagHandler extends MMOCoreFlagHandler {
@ -50,7 +51,7 @@ public class PvPFlagHandler extends MMOCoreFlagHandler {
// Send message
if (canSendMessage()) {
lastMessage = System.currentTimeMillis();
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.enter.pvp-mode-on", "time",
ConfigMessage.fromKey("pvp-mode.enter.pvp-mode-on", "time",
MythicLib.plugin.getMMOConfig().decimal.format(MMOCore.plugin.configManager.pvpModeInvulnerabilityTimeRegionChange)).send(playerData.getPlayer());
}
}

View File

@ -12,6 +12,7 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.comp.flags.CustomFlag;
import io.lumine.mythic.lib.comp.flags.WorldGuardFlags;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.command.PvpModeCommand;
import java.util.Objects;
@ -64,7 +65,7 @@ public class PvPModeHandler extends MMOCoreFlagHandler {
final boolean pvpEnabled = playerData.getCombat().isInPvpMode() && !playerData.getCombat().canQuitPvpMode() && pvpFlag;
lastMessage = System.currentTimeMillis();
final double remaining = (playerData.getCombat().getLastHit() + MMOCore.plugin.configManager.pvpModeCombatTimeout * 1000.0D - System.currentTimeMillis()) / 1000.0D;
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.leave.pvp-" + (pvpEnabled ? "allowed" : "denied"), "remaining",
ConfigMessage.fromKey("pvp-mode.leave.pvp-" + (pvpEnabled ? "allowed" : "denied"), "remaining",
(MythicLib.plugin.getMMOConfig()).decimal.format(remaining)).send(playerData.getPlayer());
}
} else if (newPvpMode && !lastPvpMode) {
@ -80,7 +81,7 @@ public class PvPModeHandler extends MMOCoreFlagHandler {
// Send message
if (canSendMessage()) {
lastMessage = System.currentTimeMillis();
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.enter.pvp-mode-" + (applyInvulnerability ? "on" : "off"), "time",
ConfigMessage.fromKey("pvp-mode.enter.pvp-mode-" + (applyInvulnerability ? "on" : "off"), "time",
MythicLib.plugin.getMMOConfig().decimal.format(MMOCore.plugin.configManager.pvpModeInvulnerabilityTimeRegionChange)).send(playerData.getPlayer());
}
}

View File

@ -6,6 +6,7 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.comp.flags.CustomFlag;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerData;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
@ -53,14 +54,14 @@ public class PvPModeListener implements Listener {
if (targetData.getLevel() < minLevel) {
event.setCancelled(true);
if (event.getDamage() > 0)
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.low-level-target").send(source);
ConfigMessage.fromKey("pvp-mode.cannot-hit.low-level-target").send(source);
return;
}
if (sourceData.getLevel() < minLevel) {
event.setCancelled(true);
if (event.getDamage() > 0)
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.low-level-self").send(source);
ConfigMessage.fromKey("pvp-mode.cannot-hit.low-level-self").send(source);
return;
}
@ -68,7 +69,7 @@ public class PvPModeListener implements Listener {
if (maxLevelDiff > 0 && Math.abs(targetData. getLevel() - sourceData.getLevel()) > maxLevelDiff) {
event.setCancelled(true);
if (event.getDamage() > 0)
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.high-level-difference").send(source);
ConfigMessage.fromKey("pvp-mode.cannot-hit.high-level-difference").send(source);
return;
}
}
@ -80,7 +81,7 @@ public class PvPModeListener implements Listener {
if (targetData.getCombat().isInvulnerable()) {
if (event.getDamage() > 0) {
final long left = targetData.getCombat().getInvulnerableTill() - System.currentTimeMillis();
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.invulnerable-target",
ConfigMessage.fromKey("pvp-mode.cannot-hit.invulnerable-target",
"left", MythicLib.plugin.getMMOConfig().decimal.format(left / 1000d)).send(source);
}
event.setCancelled(true);
@ -91,7 +92,7 @@ public class PvPModeListener implements Listener {
if (!MMOCore.plugin.configManager.pvpModeInvulnerabilityCanDamage && sourceData.getCombat().isInvulnerable()) {
if (event.getDamage() > 0) {
final long left = sourceData.getCombat().getInvulnerableTill() - System.currentTimeMillis();
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.invulnerable-self",
ConfigMessage.fromKey("pvp-mode.cannot-hit.invulnerable-self",
"left", MythicLib.plugin.getMMOConfig().decimal.format(left / 1000d)).send(source);
}
event.setCancelled(true);
@ -108,14 +109,14 @@ public class PvPModeListener implements Listener {
if (!targetData.getCombat().isInPvpMode()) {
event.setCancelled(true);
if (event.getDamage() > 0)
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.pvp-mode-disabled-target").send(source);
ConfigMessage.fromKey("pvp-mode.cannot-hit.pvp-mode-disabled-target").send(source);
}
// Attacker has not enabled PvP mode
else if (!sourceData.getCombat().isInPvpMode()) {
event.setCancelled(true);
if (event.getDamage() > 0)
MMOCore.plugin.configManager.getSimpleMessage("pvp-mode.cannot-hit.pvp-mode-disabled-self").send(source);
ConfigMessage.fromKey("pvp-mode.cannot-hit.pvp-mode-disabled-self").send(source);
}
}
}

View File

@ -31,25 +31,29 @@ public class ExpCurve {
/**
* Reads an exp curve from a text file, one line after the other. Each exp
* value has to be the only thing written on every line
*
*
* @param file Text file to read data from
* @throws IOException IO exception when reading file
*/
public ExpCurve(File file) throws IOException {
this.id = file.getName().replace(".txt", "").toLowerCase().replace("_", "-").replace(" ", "-");
public ExpCurve(File file) {
this.id = file.getName().replace(".txt", "").toLowerCase().replace("_", "-").replace(" ", "-");
BufferedReader reader = new BufferedReader(new FileReader(file));
String readLine;
while ((readLine = reader.readLine()) != null)
experience.add(Integer.valueOf(readLine));
reader.close();
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String readLine;
while ((readLine = reader.readLine()) != null)
experience.add(Integer.valueOf(readLine));
reader.close();
Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
}
Validate.isTrue(!experience.isEmpty(), "There must be at least one exp value in your exp curve");
} catch(Throwable throwable) {
throw new RuntimeException(throwable);
}
}
/**
* Public constructor for external plugins
*
*
* @param id Some unique identifier to let other plugin features refer
* to your exp curve.
* @param values The exp values, at to be at least one or the constructor

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.experience;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
import org.jetbrains.annotations.NotNull;
@ -10,7 +11,8 @@ import javax.annotation.Nullable;
* General implementation for professions, classes and attributes.
* <p>
* An experience object is a type of object that can level up.
* It has an experience curve and table and can receive EXP
* It has an experience curve and table and can receive EXP. It
* is what most resembles the Mythic abstraction of "archetypes".
*
* @author jules
*/
@ -36,4 +38,17 @@ public interface ExperienceObject extends ExperienceDispenser {
ExperienceTable getExperienceTable();
boolean hasExperienceTable();
/**
* Resets the advancement of an archetype for a player. This only
* applies to the object's experience table though, and does not
* actually decrease class level/profession level & exp bar.
*/
public default void resetAdvancement(@NotNull PlayerData playerData, boolean levels) {
if (hasExperienceTable()) getExperienceTable().unclaim(playerData, this, levels);
}
public default void updateAdvancement(@NotNull PlayerData playerData, int newLevel) {
if (hasExperienceTable()) getExperienceTable().claim(playerData, newLevel, this);
}
}

View File

@ -1,21 +0,0 @@
package net.Indyuce.mmocore.experience;
import net.Indyuce.mmocore.experience.droptable.ExperienceItem;
import net.Indyuce.mmocore.experience.droptable.ExperienceTable;
/**
* Professions and classes share the same properties because
* they have both exp curves and tables.
* <p>
* A 'claimer' is an object that can claim exp tables and therefore
* needs to save how many times it has already claimed some item
* before.
* <p>
* Since MMOCore 1.9 it's all centralized in the player class data
*/
public interface ExperienceTableClaimer {
int getClaims(ExperienceObject object, ExperienceTable table, ExperienceItem item);
void setClaims(ExperienceObject object, ExperienceTable table, ExperienceItem item, int claims);
}

View File

@ -2,9 +2,9 @@ package net.Indyuce.mmocore.experience;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.gson.Gson;
import io.lumine.mythic.lib.gson.JsonElement;
import io.lumine.mythic.lib.gson.JsonObject;
import io.lumine.mythic.lib.version.VParticle;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.SoundEvent;
@ -18,7 +18,6 @@ import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
@ -86,7 +85,7 @@ public class PlayerProfessions {
* When loading data through SQL
*/
public void load(String json) {
JsonObject obj = new Gson().fromJson(json, JsonObject.class);
JsonObject obj = MythicLib.plugin.getGson().fromJson(json, JsonObject.class);
// Load profession exp and levels
for (Entry<String, JsonElement> entry : obj.entrySet())
@ -100,14 +99,6 @@ public class PlayerProfessions {
if (obj.has("timesClaimed"))
for (Entry<String, JsonElement> entry : obj.getAsJsonObject("timesClaimed").entrySet())
playerData.getItemClaims().put("profession." + entry.getKey(), entry.getValue().getAsInt());
for (Profession profession : MMOCore.plugin.professionManager.getAll()) {
if (profession.hasExperienceTable())
profession.getExperienceTable().claimRemovableTrigger(playerData, profession);
}
if (playerData.getProfess().hasExperienceTable())
playerData.getProfess().getExperienceTable().claimRemovableTrigger(playerData, playerData.getProfess());
}
public PlayerData getPlayerData() {
@ -163,11 +154,11 @@ public class PlayerProfessions {
return profession.hasMaxLevel() && getLevel(profession) >= profession.getMaxLevel();
}
public void giveExperience(Profession profession, double value, EXPSource source) {
public void giveExperience(@NotNull Profession profession, double value, @NotNull EXPSource source) {
giveExperience(profession, value, source, null, true);
}
public void giveExperience(Profession profession, double value, EXPSource source, @Nullable Location hologramLocation, boolean splitExp) {
public void giveExperience(@NotNull Profession profession, double value, @NotNull EXPSource source, @Nullable Location hologramLocation, boolean splitExp) {
Validate.isTrue(playerData.isOnline(), "Cannot give experience to offline player");
if (value <= 0) {
exp.put(profession.getId(), Math.max(0, exp.getOrDefault(profession.getId(), 0d) + value));
@ -198,8 +189,8 @@ public class PlayerProfessions {
return;
// Display hologram
if (hologramLocation != null)
MMOCoreUtils.displayIndicator(hologramLocation.add(.5, 1.5, .5), MMOCore.plugin.configManager.getSimpleMessage("exp-hologram", "exp", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())).message());
if (hologramLocation != null && profession.getOption(Profession.ProfessionOption.EXP_HOLOGRAMS))
MMOCoreUtils.displayIndicator(hologramLocation.add(.5, 1.5, .5), ConfigMessage.fromKey("exp-hologram", "exp", MythicLib.plugin.getMMOConfig().decimal.format(event.getExperience())).asLine());
exp.put(profession.getId(), Math.max(0, exp.getOrDefault(profession.getId(), 0d) + event.getExperience()));
int level, oldLevel = getLevel(profession);
@ -223,25 +214,24 @@ public class PlayerProfessions {
playerData.giveExperience(profession.getExperience().calculate(level), null);
// Apply profession experience table
if (profession.hasExperienceTable())
profession.getExperienceTable().claim(playerData, level, profession);
profession.updateAdvancement(playerData, level);
}
if (check) {
Bukkit.getPluginManager().callEvent(new PlayerLevelUpEvent(playerData, profession, oldLevel, level));
new SmallParticleEffect(playerData.getPlayer(), Particle.SPELL_INSTANT);
new ConfigMessage("profession-level-up").addPlaceholders("level", String.valueOf(level), "profession", profession.getName())
new SmallParticleEffect(playerData.getPlayer(), VParticle.INSTANT_EFFECT.get());
ConfigMessage.fromKey("profession-level-up").addPlaceholders("level", String.valueOf(level), "profession", profession.getName())
.send(playerData.getPlayer());
MMOCore.plugin.soundManager.getSound(SoundEvent.LEVEL_UP).playTo(playerData.getPlayer());
playerData.getStats().updateStats();
}
StringBuilder bar = new StringBuilder("" + ChatColor.BOLD);
StringBuilder bar = new StringBuilder(ChatColor.BOLD.toString());
int chars = (int) (exp / needed * 20);
for (int j = 0; j < 20; j++)
bar.append(j == chars ? "" + ChatColor.WHITE + ChatColor.BOLD : "").append("|");
bar.append(j == chars ? ChatColor.WHITE.toString() + ChatColor.BOLD : "").append("|");
if (playerData.isOnline())
MMOCore.plugin.configManager.getSimpleMessage("exp-notification", "profession", profession.getName(), "progress", bar.toString(), "ratio",
ConfigMessage.fromKey("exp-notification", "profession", profession.getName(), "progress", bar.toString(), "ratio",
MythicLib.plugin.getMMOConfig().decimal.format(exp / needed * 100)).send(playerData.getPlayer());
}
}

View File

@ -1,7 +1,8 @@
package net.Indyuce.mmocore.experience;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.util.PostLoadObject;
import io.lumine.mythic.lib.util.PostLoadAction;
import io.lumine.mythic.lib.util.PreloadedObject;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.math.formula.LinearValue;
@ -18,7 +19,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
public class Profession extends PostLoadObject implements ExperienceObject {
public class Profession implements ExperienceObject, PreloadedObject {
private final String id, name;
private final int maxLevel;
private final Map<ProfessionOption, Boolean> options = new HashMap<>();
@ -31,8 +32,14 @@ public class Profession extends PostLoadObject implements ExperienceObject {
*/
private final LinearValue experience;
public Profession(String id, FileConfiguration config) {
super(config);
private final PostLoadAction postLoadAction = new PostLoadAction(config -> {
// Link profession to hardcoded profession manager
MMOCore.plugin.professionManager.loadProfessionConfigurations(this, config);
});
public Profession(String id, ConfigurationSection config) {
postLoadAction.cacheConfig(config);
this.id = id.toLowerCase().replace("_", "-").replace(" ", "-");
this.name = config.getString("name");
@ -74,9 +81,10 @@ public class Profession extends PostLoadObject implements ExperienceObject {
}
}
@NotNull
@Override
protected void whenPostLoaded(ConfigurationSection configurationSection) {
MMOCore.plugin.professionManager.loadProfessionConfigurations(this, configurationSection);
public PostLoadAction getPostLoadAction() {
return postLoadAction;
}
public boolean getOption(ProfessionOption option) {
@ -93,7 +101,7 @@ public class Profession extends PostLoadObject implements ExperienceObject {
@Override
public String getKey() {
return "profession." + getId();
return "profession_" + getId();
}
@Override
@ -124,8 +132,7 @@ public class Profession extends PostLoadObject implements ExperienceObject {
}
@Override
public void giveExperience(PlayerData playerData, double experience, @Nullable Location hologramLocation, EXPSource source) {
hologramLocation = !getOption(Profession.ProfessionOption.EXP_HOLOGRAMS) ? null : hologramLocation;
public void giveExperience(@NotNull PlayerData playerData, double experience, @Nullable Location hologramLocation, @NotNull EXPSource source) {
playerData.getCollectionSkills().giveExperience(this, experience, EXPSource.SOURCE, hologramLocation, true);
}

View File

@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Used to differenciate between the main class experience and
* Used to differentiate between the main class experience and
* experience given in a specific profession. Also being used to
* monitor EXP holograms.
*

View File

@ -3,9 +3,9 @@ package net.Indyuce.mmocore.experience.droptable;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.quest.trigger.StatTrigger;
import net.Indyuce.mmocore.api.quest.trigger.Trigger;
import net.Indyuce.mmocore.api.quest.trigger.api.Removable;
import net.Indyuce.mmocore.api.quest.trigger.api.Temporary;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
@ -132,12 +132,11 @@ public class ExperienceItem {
}
/**
* Used when a player connects back to give back all the stats that he should have.
*
* @param playerData
* Used when a player logs back, in order to apply again
* all the temporary triggers.
*/
public void applyRemovableTrigger(PlayerData playerData) {
public void applyTemporaryTriggers(PlayerData playerData) {
for (Trigger trigger : triggers)
if (trigger instanceof Removable) trigger.apply(playerData);
if (trigger instanceof Temporary) trigger.apply(playerData);
}
}

View File

@ -5,6 +5,7 @@ import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.ExperienceObject;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@ -36,61 +37,49 @@ public class ExperienceTable {
}
/**
* Called when a player levels up one of his professions
* Called when a player levels up something.
*
* @param levelingUp Player leveling up
* @param professionLevel New profession level
* @param object Either profession or class leveling up
* @param object The object being level up. This MUST be the parent object
* owning the calling experience table! In other words,
* <code>object.getExperienceTable() == this</code> must remain true.
*/
public void claim(PlayerData levelingUp, int professionLevel, ExperienceObject object) {
public void claim(@NotNull PlayerData levelingUp, int professionLevel, @NotNull ExperienceObject object) {
for (ExperienceItem item : items) {
int timesClaimed = levelingUp.getClaims(object, this, item);
if (!item.roll(professionLevel, timesClaimed))
continue;
final int timesClaimed = levelingUp.getClaims(object, item);
if (!item.roll(professionLevel, timesClaimed)) continue;
levelingUp.setClaims(object, this, item, timesClaimed + 1);
levelingUp.setClaims(object, item, timesClaimed + 1);
item.applyTriggers(levelingUp);
}
}
/**
* Called when a player changes its class.
* Removes the perm stat but keeps the item claims in memory.
*/
public void removePermStats(PlayerData playerData, ExperienceObject object) {
public void unclaim(@NotNull PlayerData playerData, @NotNull ExperienceObject object, boolean reset) {
for (ExperienceItem item : items) {
int timesClaimed = playerData.getClaims(object, this, item);
for (int i = 0; i < timesClaimed; i++)
// Undo triggers
for (int i = 0; i < playerData.getClaims(object, item); i++)
item.removeTriggers(playerData);
// Reset levels
if (reset) playerData.setClaims(object, item, 0);
}
}
/**
* Called when the progression is reset(e.g skill tree reallocation)
*/
public void reset(PlayerData playerData, ExperienceObject object) {
for (ExperienceItem item : items) {
int timesClaimed = playerData.getClaims(object, this, item);
playerData.setClaims(object, this, item, 0);
for (int i = 0; i < timesClaimed; i++)
item.removeTriggers(playerData);
}
}
/**
* Called when a player joins and all the removable triggers get claimed back.
* Called when a player joins. All non-permanent/temporary triggers
* must be granted back to the player, including player modifiers.
*
* @param data PlayerData
* @param object Either profession, skillTreeNode or class leveling up
*/
public void claimRemovableTrigger(PlayerData data, ExperienceObject object) {
public void applyTemporaryTriggers(@NotNull PlayerData data, @NotNull ExperienceObject object) {
for (ExperienceItem item : items) {
int timesClaimed = data.getClaims(object, this, item);
final int timesClaimed = data.getClaims(object, item);
for (int i = 0; i < timesClaimed; i++)
item.applyRemovableTrigger(data);
item.applyTemporaryTriggers(data);
}
}
}

View File

@ -3,9 +3,9 @@ package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -44,44 +44,46 @@ public class BrewPotionExperienceSource extends ExperienceSource<PotionMeta> {
@Override
public ExperienceSourceManager<BrewPotionExperienceSource> newManager() {
return new ExperienceSourceManager<BrewPotionExperienceSource>() {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void a(BrewEvent event) {
Optional<Player> playerOpt = getNearbyPlayer(event.getBlock().getLocation());
if (!playerOpt.isPresent())
return;
final ItemStack found = findPotion(event.getContents());
if (found != null)
Bukkit.getScheduler().scheduleSyncDelayedTask(MMOCore.plugin, () -> {
ItemStack brewn = findPotion(event.getContents());
if (brewn == null)
return;
PlayerData data = PlayerData.get(playerOpt.get());
for (BrewPotionExperienceSource source : getSources())
if (source.matches(data, (PotionMeta) brewn.getItemMeta()))
new PotionUpgrade(found, brewn).process(data.getPlayer());
});
}
};
return new Manager();
}
private ItemStack findPotion(BrewerInventory inv) {
for (int j = 0; j < 3; j++) {
ItemStack item = inv.getItem(j);
if (item != null && item.hasItemMeta() && item.getItemMeta() instanceof PotionMeta)
return item;
private static class Manager extends ExperienceSourceManager<BrewPotionExperienceSource> {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(BrewEvent event) {
Optional<Player> playerOpt = getNearbyPlayer(event.getBlock().getLocation());
if (!playerOpt.isPresent())
return;
final ItemStack found = findPotion(event.getContents());
if (found != null)
Bukkit.getScheduler().scheduleSyncDelayedTask(MMOCore.plugin, () -> {
ItemStack brewn = findPotion(event.getContents());
if (brewn == null)
return;
PlayerData data = PlayerData.get(playerOpt.get());
for (BrewPotionExperienceSource source : getSources())
if (source.matches(data, (PotionMeta) brewn.getItemMeta()))
new PotionUpgrade(found, brewn).process(source, data.getPlayer());
});
}
private ItemStack findPotion(BrewerInventory inv) {
for (int j = 0; j < 3; j++) {
ItemStack item = inv.getItem(j);
if (item != null && item.hasItemMeta() && item.getItemMeta() instanceof PotionMeta)
return item;
}
return null;
}
private Optional<Player> getNearbyPlayer(Location loc) {
return loc.getWorld().getPlayers().stream().filter(player -> player.getLocation().distanceSquared(loc) < 100).findAny();
}
return null;
}
private Optional<Player> getNearbyPlayer(Location loc) {
return loc.getWorld().getPlayers().stream().filter(player -> player.getLocation().distanceSquared(loc) < 100).findAny();
}
public class PotionUpgrade {
private static class PotionUpgrade {
/*
* if the potion was extended using redstone or upgraded using
@ -161,14 +163,14 @@ public class BrewPotionExperienceSource extends ExperienceSource<PotionMeta> {
// effect.getType() == type).findFirst();
// }
public void process(Player player) {
public void process(BrewPotionExperienceSource source, Player player) {
/*
* calculate extra exp due to extra effects
*/
// exp += getTotal(mapEffectDurations());
getDispenser().giveExperience(PlayerData.get(player), exp * multiplier, player.getLocation(), EXPSource.SOURCE);
source.getDispenser().giveExperience(PlayerData.get(player), exp * source.multiplier, player.getLocation(), EXPSource.SOURCE);
}
}
}

View File

@ -11,7 +11,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerMoveEvent;
import static org.bukkit.event.EventPriority.HIGHEST;
import static org.bukkit.event.EventPriority.MONITOR;
public class ClimbExperienceSource extends SpecificExperienceSource<Material> {
//Can be Ladder,Vines,Twisting Vines,Weeping Vines.
@ -36,28 +35,11 @@ public class ClimbExperienceSource extends SpecificExperienceSource<Material> {
type = Material.valueOf(str);
}
}
@Override
public ExperienceSourceManager<ClimbExperienceSource> newManager() {
return new ExperienceSourceManager<ClimbExperienceSource>() {
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onClimb(PlayerMoveEvent e) {
double delta=e.getTo().getBlockY()-e.getFrom().getBlockY();
if (delta > 0) {
if (e.getPlayer().hasMetadata("NPC"))
return;
PlayerData playerData = PlayerData.get(e.getPlayer());
for (ClimbExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getFrom().getBlock().getType()))
source.giveExperience(playerData, delta, null);
}
}
}
};
return new Manager();
}
@Override
@ -71,6 +53,22 @@ public class ClimbExperienceSource extends SpecificExperienceSource<Material> {
if (type.equals(Material.TWISTING_VINES))
return material.equals(Material.TWISTING_VINES) || material.equals(Material.TWISTING_VINES_PLANT);
return material.equals(type);
}
private static class Manager extends ExperienceSourceManager<ClimbExperienceSource> {
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onClimb(PlayerMoveEvent e) {
double delta = e.getTo().getBlockY() - e.getFrom().getBlockY();
if (delta > 0) {
if (e.getPlayer().hasMetadata("NPC"))
return;
PlayerData playerData = PlayerData.get(e.getPlayer());
for (ClimbExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getFrom().getBlock().getType()))
source.giveExperience(playerData, delta, null);
}
}
}
}
}

View File

@ -3,8 +3,8 @@ package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
@ -30,51 +30,7 @@ public class CraftItemExperienceSource extends SpecificExperienceSource<Material
@Override
public ExperienceSourceManager<CraftItemExperienceSource> newManager() {
return new ExperienceSourceManager<CraftItemExperienceSource>() {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void a(CraftItemEvent event) {
if (event.getAction() == InventoryAction.NOTHING ||
event.getInventory().getResult() == null) return;
PlayerData data = PlayerData.get((Player) event.getWhoClicked());
/**
* This makes sure that the crafting recipe was performed correctly.
*
* In some scenarii, the CraftItemEvent (which is only a click event
* by the way) is not cancelled, but the item is not crafted which makes
* EXP duplication a game breaking glitch. MMOCore make sure that at
* least one ingredient has lowered in amount.
*
* The second objective of that check is to deduce the amount of items that
* were crafted during this event. For that, it finds the item with the lowest
* amount in the crafting matrix and see how many items disappeared,
* multiplied by the recipe output amount (works for a shift click).
*
* References:
* - https://git.lumine.io/mythiccraft/mmocore/-/issues/102
* - https://www.spigotmc.org/threads/how-to-get-amount-of-item-crafted.377598/
*/
final int index = getLowerAmountIngredientIndex(event.getInventory().getMatrix());
final int oldAmount = event.getInventory().getMatrix()[index].getAmount();
final int itemsCraftedPerRecipe = event.getInventory().getResult().getAmount();
final Material resultType = event.getInventory().getResult().getType();
Bukkit.getScheduler().runTask(MMOCore.plugin, () -> {
// First check
int newAmount = getAmount(event.getInventory().getMatrix()[index]);
if (newAmount >= oldAmount)
return;
// Deduce amount crafted
int amountCrafted = (event.getClick().isShiftClick() ? oldAmount - newAmount : 1) * itemsCraftedPerRecipe;
for (CraftItemExperienceSource source : getSources())
if (source.matches(data, resultType))
source.giveExperience(data, amountCrafted, event.getInventory().getLocation());
});
}
};
return new Manager();
}
@Override
@ -82,23 +38,68 @@ public class CraftItemExperienceSource extends SpecificExperienceSource<Material
return material == obj;
}
private int getAmount(@Nullable ItemStack item) {
return item == null || item.getType() == Material.AIR ? 0 : item.getAmount();
}
private static class Manager extends ExperienceSourceManager<CraftItemExperienceSource> {
private int getLowerAmountIngredientIndex(ItemStack[] matrix) {
int lower = Integer.MAX_VALUE;
int index = -1;
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void a(CraftItemEvent event) {
if (event.getAction() == InventoryAction.NOTHING || event.getInventory().getResult() == null) return;
for (int i = 0; i < matrix.length; i++) {
ItemStack checked = matrix[i];
if (checked != null && checked.getType() != Material.AIR && checked.getAmount() > 0 && checked.getAmount() < lower) {
lower = checked.getAmount();
index = i;
}
PlayerData data = PlayerData.get((Player) event.getWhoClicked());
/**
* This makes sure that the crafting recipe was performed correctly.
*
* In some scenarii, the CraftItemEvent (which is only a click event
* by the way) is not cancelled, but the item is not crafted which makes
* EXP duplication a game breaking glitch. MMOCore make sure that at
* least one ingredient has lowered in amount.
*
* The second objective of that check is to deduce the amount of items that
* were crafted during this event. For that, it finds the item with the lowest
* amount in the crafting matrix and see how many items disappeared,
* multiplied by the recipe output amount (works for a shift click).
*
* References:
* - https://git.lumine.io/mythiccraft/mmocore/-/issues/102
* - https://www.spigotmc.org/threads/how-to-get-amount-of-item-crafted.377598/
*/
final int index = getLowerAmountIngredientIndex(event.getInventory().getMatrix());
final int oldAmount = event.getInventory().getMatrix()[index].getAmount();
final int itemsCraftedPerRecipe = event.getInventory().getResult().getAmount();
final Material resultType = event.getInventory().getResult().getType();
Bukkit.getScheduler().runTask(MMOCore.plugin, () -> {
// First check
int newAmount = getAmount(event.getInventory().getMatrix()[index]);
if (newAmount >= oldAmount) return;
// Deduce amount crafted
int amountCrafted = (event.getClick().isShiftClick() ? oldAmount - newAmount : 1) * itemsCraftedPerRecipe;
for (CraftItemExperienceSource source : getSources())
if (source.matches(data, resultType))
source.giveExperience(data, amountCrafted, event.getInventory().getLocation());
});
}
Validate.isTrue(index != -1, "No item in matrix");
return index;
private int getAmount(@Nullable ItemStack item) {
return item == null || item.getType() == Material.AIR ? 0 : item.getAmount();
}
private int getLowerAmountIngredientIndex(ItemStack[] matrix) {
int lower = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < matrix.length; i++) {
ItemStack checked = matrix[i];
if (checked != null && checked.getType() != Material.AIR && checked.getAmount() > 0 && checked.getAmount() < lower) {
lower = checked.getAmount();
index = i;
}
}
Validate.isTrue(index != -1, "No item in matrix");
return index;
}
}
}

View File

@ -1,16 +1,16 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.api.event.PlayerAttackEvent;
import io.lumine.mythic.lib.damage.DamagePacket;
import io.lumine.mythic.lib.damage.DamageType;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.apache.commons.lang.Validate;
import org.bukkit.attribute.Attribute;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import java.util.Arrays;
import java.util.Objects;
@ -18,7 +18,7 @@ import java.util.stream.Collectors;
import static org.bukkit.event.EventPriority.MONITOR;
public class DamageDealtExperienceSource extends SpecificExperienceSource<DamageType> {
public class DamageDealtExperienceSource extends SpecificExperienceSource<Void> {
private final DamageType type;
/**
@ -28,51 +28,40 @@ public class DamageDealtExperienceSource extends SpecificExperienceSource<Damage
public DamageDealtExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
super(dispenser, config);
if (!config.contains("type"))
type = null;
if (!config.contains("type")) type = null;
else {
String str = config.getString("type").toUpperCase().replace("-", "_");
String str = UtilityMethods.enumName(config.getString("type"));
//Checks if the damage type correspond to a value of the damage type enum
Validate.isTrue(Arrays.stream(DamageType.values()).map(Objects::toString).collect(Collectors.toList()).contains(str),
"Type value not allowed. Type value allowed: magic, physical, weapon, skill, projectile," +
" unarmed, on-hit, minion, dot.");
Validate.isTrue(Arrays.stream(DamageType.values()).map(Objects::toString).collect(Collectors.toList()).contains(str), "Type value not allowed. Type value allowed: magic, physical, weapon, skill, projectile," + " unarmed, on-hit, minion, dot.");
type = DamageType.valueOf(str);
}
}
@Override
public ExperienceSourceManager<DamageDealtExperienceSource> newManager() {
return new ExperienceSourceManager<DamageDealtExperienceSource>() {
//It isn't triggered when the PlayerAttackEvent gets cancelled
@EventHandler(priority = MONITOR,ignoreCancelled = true)
public void onDamageDealt(PlayerAttackEvent e) {
PlayerData playerData = PlayerData.get(e.getPlayer());
for (DamageDealtExperienceSource source : getSources()) {
double value = 0;
for (DamagePacket packet : e.getDamage().getPackets()) {
for (DamageType damageType : packet.getTypes()) {
if (source.matchesParameter(playerData, damageType))
value += packet.getFinalValue();
}
}
source.giveExperience(playerData, value, null);
}
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, DamageType damageType) {
if (type == null) {
return true;
}
else {
return type.equals(damageType);
}
public boolean matchesParameter(PlayerData player, Void v) {
return true;
}
private static class Manager extends ExperienceSourceManager<DamageDealtExperienceSource> {
@EventHandler(priority = MONITOR, ignoreCancelled = true)
public void onDamageDealt(PlayerAttackEvent event) {
final PlayerData playerData = PlayerData.get(event.getPlayer());
for (DamageDealtExperienceSource source : getSources())
if (source.matches(playerData, null)) {
double value = source.type == null ? event.getDamage().getDamage() : event.getDamage().getDamage(source.type);
if (value == 0) continue;
// Cannot count more than the entity's max health
final double enemyMaxHealth = event.getEntity().getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
value = Math.min(value, enemyMaxHealth);
source.giveExperience(playerData, value, null);
}
}
}
}

View File

@ -1,13 +1,15 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import io.lumine.mythic.lib.util.Lazy;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.apache.commons.lang.Validate;
import org.bukkit.OfflinePlayer;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageEvent;
@ -41,34 +43,40 @@ public class DamageTakenExperienceSource extends SpecificExperienceSource<Entity
@Override
public ExperienceSourceManager<DamageTakenExperienceSource> newManager() {
return new ExperienceSourceManager<DamageTakenExperienceSource>() {
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onDamageTaken(EntityDamageEvent e) {
if (e.getEntity() instanceof Player && !e.getEntity().hasMetadata("NPC")) {
double amount = e.getDamage();
PlayerData playerData = PlayerData.get((OfflinePlayer) e.getEntity());
//We wait 2 tick to check if the player is Dead
new BukkitRunnable() {
@Override
public void run() {
for (DamageTakenExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getCause()))
source.giveExperience(playerData, amount, null);
}
}
}.runTaskLater(MMOCore.plugin, 2);
}
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, EntityDamageEvent.DamageCause damageCause) {
if (player.getPlayer().isDead())
return false;
if (cause == null)
return true;
return damageCause.equals(cause);
if (player.getPlayer().isDead()) return false;
return cause == null || damageCause.equals(cause);
}
private static class Manager extends ExperienceSourceManager<DamageTakenExperienceSource> {
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onDamageTaken(EntityDamageEvent event) {
if (!UtilityMethods.isRealPlayer(event.getEntity())) return;
final PlayerData playerData = PlayerData.get((Player) event.getEntity());
final Lazy<Double> effectiveDamage = Lazy.of(() -> {
final double eventDamage = event.getDamage();
final double maxHealth = ((Player) event.getEntity()).getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
return Math.min(eventDamage, maxHealth);
});
// Wait 2 tick to check if the player died
new BukkitRunnable() {
@Override
public void run() {
for (DamageTakenExperienceSource source : getSources())
if (source.matchesParameter(playerData, event.getCause())) {
// System.out.println("-> " + effectiveDamage.get());
source.giveExperience(playerData, effectiveDamage.get(), null);
}
}
}.runTaskLater(MMOCore.plugin, 2);
}
}
}

View File

@ -33,19 +33,7 @@ public class EatExperienceSource extends SpecificExperienceSource<ItemStack> {
@Override
public ExperienceSourceManager<EatExperienceSource> newManager() {
return new ExperienceSourceManager<EatExperienceSource>() {
@EventHandler(priority = MONITOR,ignoreCancelled = true)
public void a(PlayerItemConsumeEvent e) {
if(!e.getPlayer().hasMetadata("NPC")) {
PlayerData playerData = PlayerData.get(e.getPlayer());
for (EatExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getItem()))
source.giveExperience(playerData, 1, null);
}
}
}
};
return new Manager();
}
@Override
@ -55,4 +43,18 @@ public class EatExperienceSource extends SpecificExperienceSource<ItemStack> {
return type.equals(obj.getType());
}
private static class Manager extends ExperienceSourceManager<EatExperienceSource> {
@EventHandler(priority = MONITOR,ignoreCancelled = true)
public void a(PlayerItemConsumeEvent e) {
if(!e.getPlayer().hasMetadata("NPC")) {
PlayerData playerData = PlayerData.get(e.getPlayer());
for (EatExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getItem()))
source.giveExperience(playerData, 1, null);
}
}
}
}
}

View File

@ -4,9 +4,9 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.experience.EXPSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.ExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler;
@ -41,28 +41,30 @@ public class EnchantItemExperienceSource extends ExperienceSource<Void> {
@Override
public ExperienceSourceManager<EnchantItemExperienceSource> newManager() {
return new ExperienceSourceManager<EnchantItemExperienceSource>() {
return new Manager();
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void a(EnchantItemEvent event) {
PlayerData player = PlayerData.get(event.getEnchanter());
for (EnchantItemExperienceSource source : getSources())
if (source.matches(player, null)) {
Map<Enchantment, Integer> applicableEnchants = new HashMap<>(event.getEnchantsToAdd());
private static class Manager extends ExperienceSourceManager<EnchantItemExperienceSource> {
// Filter out enchants if required
if (!source.enchants.isEmpty())
applicableEnchants.keySet().removeIf(enchantment -> !source.enchants.contains(enchantment));
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(EnchantItemEvent event) {
PlayerData player = PlayerData.get(event.getEnchanter());
for (EnchantItemExperienceSource source : getSources())
if (source.matches(player, null)) {
Map<Enchantment, Integer> applicableEnchants = new HashMap<>(event.getEnchantsToAdd());
if (applicableEnchants.isEmpty())
continue;
// Filter out enchants if required
if (!source.enchants.isEmpty())
applicableEnchants.keySet().removeIf(enchantment -> !source.enchants.contains(enchantment));
double exp = 0;
for (Entry<Enchantment, Integer> entry : applicableEnchants.entrySet())
exp += MMOCore.plugin.enchantManager.getBaseExperience(entry.getKey()) * entry.getValue();
getDispenser().giveExperience(player, exp, event.getEnchantBlock().getLocation(), EXPSource.SOURCE);
}
}
};
if (applicableEnchants.isEmpty())
continue;
double exp = 0;
for (Entry<Enchantment, Integer> entry : applicableEnchants.entrySet())
exp += MMOCore.plugin.enchantManager.getBaseExperience(entry.getKey()) * entry.getValue();
source.getDispenser().giveExperience(player, exp, event.getEnchantBlock().getLocation(), EXPSource.SOURCE);
}
}
}
}

View File

@ -2,8 +2,8 @@ package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.bukkit.Material;
import org.bukkit.entity.Item;
@ -25,26 +25,28 @@ public class FishItemExperienceSource extends SpecificExperienceSource<ItemStack
@Override
public ExperienceSourceManager<FishItemExperienceSource> newManager() {
return new ExperienceSourceManager<FishItemExperienceSource>() {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void a(PlayerFishEvent event) {
if (event.getState() == State.CAUGHT_FISH) {
ItemStack caught = ((Item) event.getCaught()).getItemStack();
if (caught.hasItemMeta())
return;
PlayerData data = PlayerData.get(event.getPlayer());
for (FishItemExperienceSource source : getSources())
if (source.matches(data, caught))
source.giveExperience(data, caught.getAmount(), event.getHook().getLocation().add(0, 1.0f, 0));
}
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, ItemStack obj) {
return obj.getType() == material;
}
private static class Manager extends ExperienceSourceManager<FishItemExperienceSource> {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(PlayerFishEvent event) {
if (event.getState() == State.CAUGHT_FISH) {
ItemStack caught = ((Item) event.getCaught()).getItemStack();
if (caught.hasItemMeta())
return;
PlayerData data = PlayerData.get(event.getPlayer());
for (FishItemExperienceSource source : getSources())
if (source.matches(data, caught))
source.giveExperience(data, caught.getAmount(), event.getHook().getLocation().add(0, 1.0f, 0));
}
}
}
}

View File

@ -33,20 +33,22 @@ public class FromExperienceSource extends ExperienceSource {
@Override
public ExperienceSourceManager<FromExperienceSource> newManager() {
return new ExperienceSourceManager<>() {
/**
* Used to register all the children experience sources.
*/
@Override
public void registerSource(FromExperienceSource source) {
source.experienceSources.forEach(expSource -> MMOCore.plugin.experience.registerSource(expSource));
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, Object obj) {
return false;
}
private static class Manager extends ExperienceSourceManager<FromExperienceSource> {
/**
* Used to register all the children experience sources.
*/
@Override
public void registerSource(FromExperienceSource source) {
source.experienceSources.forEach(expSource -> MMOCore.plugin.experience.registerSource(expSource));
}
}
}

View File

@ -38,44 +38,47 @@ public class KillMobExperienceSource extends SpecificExperienceSource<Entity> {
@Override
public ExperienceSourceManager<KillMobExperienceSource> newManager() {
return new ExperienceSourceManager<KillMobExperienceSource>() {
/**
* This map is used to keep track of the last player who
* hit some entity. It is flushed on entity death.
*/
private final FlushableRegistry<UUID, UUID> registry = new FlushableRegistry<>((entity, attacker) -> Bukkit.getEntity(entity) == null, 20 * 60);
@Override
public void whenClosed() {
registry.close();
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void registerLastAttacker(PlayerAttackEvent event) {
registry.getRegistry().put(event.getEntity().getUniqueId(), event.getAttacker().getData().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR)
public void giveExp(EntityDeathEvent event) {
// Always remove entry from map
final @Nullable UUID lastAttacker = this.registry.getRegistry().remove(event.getEntity().getUniqueId());
if (lastAttacker == null) return;
if (event.getEntity().getPersistentDataContainer().has(new NamespacedKey(MMOCore.plugin, "spawner_spawned"), PersistentDataType.STRING))
return;
final PlayerData data = PlayerData.get(lastAttacker);
for (KillMobExperienceSource source : getSources())
if (source.matches(data, event.getEntity()))
source.giveExperience(data, 1, MMOCoreUtils.getCenterLocation(event.getEntity()));
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, Entity obj) {
return obj.getType() == type && (displayName == null || displayName.equals(obj.getCustomName()));
}
private static class Manager extends ExperienceSourceManager<KillMobExperienceSource> {
/**
* This map is used to keep track of the last player who
* hit some entity. It is flushed on entity death.
*/
private final FlushableRegistry<UUID, UUID> registry = new FlushableRegistry<>((entity, attacker) -> Bukkit.getEntity(entity) == null, 20 * 60);
@Override
public void whenClosed() {
registry.close();
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void registerLastAttacker(PlayerAttackEvent event) {
registry.getRegistry().put(event.getEntity().getUniqueId(), event.getAttacker().getPlayer().getUniqueId());
}
@EventHandler(priority = EventPriority.MONITOR)
public void giveExp(EntityDeathEvent event) {
// Always remove entry from map
final @Nullable UUID lastAttacker = this.registry.getRegistry().remove(event.getEntity().getUniqueId());
if (lastAttacker == null) return;
if (event.getEntity().getPersistentDataContainer().has(new NamespacedKey(MMOCore.plugin, "spawner_spawned"), PersistentDataType.STRING))
return;
final PlayerData data = PlayerData.get(lastAttacker);
for (KillMobExperienceSource source : getSources())
if (source.matches(data, event.getEntity()))
source.giveExperience(data, 1, MMOCoreUtils.getCenterLocation(event.getEntity()));
}
}
}

View File

@ -1,10 +1,11 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.bukkit.GameMode;
import org.bukkit.Material;
@ -21,7 +22,7 @@ public class MineBlockExperienceSource extends SpecificExperienceSource<Material
/**
* Set to false by default.
*
* <p>
* When set to true, the exp source will trigger when breaking
* blocks that were placed by players. This can be used for crops
*/
@ -39,37 +40,36 @@ public class MineBlockExperienceSource extends SpecificExperienceSource<Material
@Override
public ExperienceSourceManager<MineBlockExperienceSource> newManager() {
return
new ExperienceSourceManager<MineBlockExperienceSource>() {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(BlockBreakEvent event) {
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL)
return;
PlayerData data = PlayerData.get(event.getPlayer());
for (MineBlockExperienceSource source : getSources()) {
if (source.silkTouch && hasSilkTouch(event.getPlayer().getInventory().getItemInMainHand()))
continue;
if (source.crop && !MythicLib.plugin.getVersion().getWrapper().isCropFullyGrown(event.getBlock()))
continue;
if (!source.playerPlaced && event.getBlock().hasMetadata("player_placed"))
continue;
if (source.matches(data, event.getBlock().getType()))
source.giveExperience(data, 1, event.getBlock().getLocation());
}
}
};
}
private boolean hasSilkTouch(ItemStack item) {
return item != null && item.hasItemMeta() && item.getItemMeta().hasEnchant(Enchantment.SILK_TOUCH);
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, Material obj) {
return material == obj;
}
private static class Manager extends ExperienceSourceManager<MineBlockExperienceSource> {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(BlockBreakEvent event) {
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
if (UtilityMethods.isFake(event)) return;
PlayerData data = PlayerData.get(event.getPlayer());
for (MineBlockExperienceSource source : getSources()) {
if (source.silkTouch && hasSilkTouch(event.getPlayer().getInventory().getItemInMainHand())) continue;
if (source.crop && !MythicLib.plugin.getVersion().getWrapper().isCropFullyGrown(event.getBlock()))
continue;
if (!source.playerPlaced && event.getBlock().hasMetadata("player_placed")) continue;
if (source.matches(data, event.getBlock().getType()))
source.giveExperience(data, 1, event.getBlock().getLocation());
}
}
private boolean hasSilkTouch(ItemStack item) {
return item != null && item.hasItemMeta() && item.getItemMeta().hasEnchant(Enchantment.SILK_TOUCH);
}
}
}

View File

@ -36,27 +36,7 @@ public class MoveExperienceSource extends SpecificExperienceSource {
@Override
public ExperienceSourceManager<MoveExperienceSource> newManager() {
return new ExperienceSourceManager<MoveExperienceSource>() {
@EventHandler(priority = MONITOR,ignoreCancelled = true)
public void onMove(PlayerMoveEvent e) {
double deltax = e.getTo().getBlockX() - e.getFrom().getBlockX();
double deltay = e.getTo().getBlockY() - e.getFrom().getBlockY();
double deltaz = e.getTo().getBlockZ() - e.getFrom().getBlockZ();
if (deltax != 0 || deltay != 0 || deltaz != 0) {
double delta = Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
if (e.getPlayer().hasMetadata("NPC"))
return;
Player player = e.getPlayer();
PlayerData playerData = PlayerData.get(player);
for (MoveExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, null)) {
giveExperience(playerData, delta, null);
}
}
}
}
};
return new Manager();
}
@Override
@ -69,7 +49,7 @@ public class MoveExperienceSource extends SpecificExperienceSource {
FLY((p) -> p.isFlying() || p.isGliding()),
SWIM((p) -> p.getLocation().getBlock().isLiquid()),
SPRINT(Player::isSprinting),
WALK((p) -> !p.isSneaking() && !p.isSprinting() &&((Entity)p).isOnGround()&& !p.isFlying() && !p.getLocation().getBlock().isLiquid());
WALK((p) -> !p.isSneaking() && !p.isSprinting() && ((Entity) p).isOnGround() && !p.isFlying() && !p.getLocation().getBlock().isLiquid());
private final Function<Player, Boolean> matching;
@ -82,4 +62,24 @@ public class MoveExperienceSource extends SpecificExperienceSource {
}
}
private static class Manager extends ExperienceSourceManager<MoveExperienceSource> {
@EventHandler(priority = MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent e) {
double deltax = e.getTo().getBlockX() - e.getFrom().getBlockX();
double deltay = e.getTo().getBlockY() - e.getFrom().getBlockY();
double deltaz = e.getTo().getBlockZ() - e.getFrom().getBlockZ();
if (deltax != 0 || deltay != 0 || deltaz != 0) {
double delta = Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
if (e.getPlayer().hasMetadata("NPC"))
return;
Player player = e.getPlayer();
PlayerData playerData = PlayerData.get(player);
for (MoveExperienceSource source : getSources())
if (source.matchesParameter(playerData, null))
source.giveExperience(playerData, delta, null);
}
}
}
}

View File

@ -2,8 +2,8 @@ package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import org.bukkit.GameMode;
import org.bukkit.Material;
@ -23,23 +23,26 @@ public class PlaceBlockExperienceSource extends SpecificExperienceSource<Materia
@Override
public ExperienceSourceManager<PlaceBlockExperienceSource> newManager() {
return new ExperienceSourceManager<PlaceBlockExperienceSource>() {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(BlockPlaceEvent event) {
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL)
return;
PlayerData data = PlayerData.get(event.getPlayer());
for (PlaceBlockExperienceSource source : getSources())
if (source.matches(data, event.getBlock().getType()))
source.giveExperience(data, 1, event.getBlock().getLocation());
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, Material obj) {
return material == obj;
}
private static class Manager extends ExperienceSourceManager<PlaceBlockExperienceSource> {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(BlockPlaceEvent event) {
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL)
return;
PlayerData data = PlayerData.get(event.getPlayer());
for (PlaceBlockExperienceSource source : getSources())
if (source.matches(data, event.getBlock().getType()))
source.giveExperience(data, 1, event.getBlock().getLocation());
}
}
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
@ -14,14 +15,13 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.util.Objects;
public class PlayExperienceSource extends SpecificExperienceSource {
private final World world;
private final double x1, x2, z1, z2;
private final boolean inCombat;
/**
* Experience source giving the specified amount of xp to all the players online each second in certain world bounds.
* If no bounds are given, it will give the xp to every player online. You can also specifiy if the player
* If no bounds are given, it will give the xp to every player online. You can also specify if the player
* has to be inCombat or not to get the xp.
*/
public PlayExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
@ -47,7 +47,7 @@ public class PlayExperienceSource extends SpecificExperienceSource {
@Override
public ExperienceSourceManager<PlayExperienceSource> newManager() {
return new PlayingExperienceSourceManager();
return new Manager();
}
@ -64,20 +64,19 @@ public class PlayExperienceSource extends SpecificExperienceSource {
}
private class PlayingExperienceSourceManager extends ExperienceSourceManager<PlayExperienceSource> {
private static class Manager extends ExperienceSourceManager<PlayExperienceSource> {
public PlayingExperienceSourceManager() {
public Manager() {
new BukkitRunnable() {
@Override
public void run() {
Bukkit.getOnlinePlayers().forEach((player) -> {
if (!player.hasMetadata("NPC")) {
Bukkit.getOnlinePlayers().forEach(player -> {
if (UtilityMethods.isRealPlayer(player)) {
PlayerData playerData = PlayerData.get(player);
for (PlayExperienceSource source : getSources()) {
for (PlayExperienceSource source : getSources())
if (source.matchesParameter(playerData, null))
giveExperience(playerData, 1, null);
}
source. giveExperience(playerData, 1, null);
}
});
}

View File

@ -1,7 +1,8 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import io.lumine.mythic.lib.util.FlushableRegistry;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
@ -16,10 +17,8 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -30,8 +29,7 @@ public class ProjectileExperienceSource extends SpecificExperienceSource<Project
public ProjectileExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
super(dispenser, config);
if (!config.contains("type"))
projectileType = null;
if (!config.contains("type")) projectileType = null;
else {
String str = config.getString("type").toUpperCase().replace("-", "_");
Validate.isTrue(Arrays.stream(ProjectileType.values()).map(ProjectileType::toString).collect(Collectors.toList()).contains(str));
@ -41,76 +39,17 @@ public class ProjectileExperienceSource extends SpecificExperienceSource<Project
@Override
public ExperienceSourceManager<ProjectileExperienceSource> newManager() {
return new ExperienceSourceManager<ProjectileExperienceSource>() {
HashMap<Projectile, Location> projectiles = new HashMap<>();
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onHit(ProjectileHitEvent e) {
if (e.getHitBlock() != null && projectiles.containsKey(e.getEntity()))
projectiles.remove(e.getEntity());
}
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent e) {
if (e.getEntity() instanceof Projectile) {
Projectile projectile = (Projectile) e.getEntity();
if (!projectiles.containsKey(projectile))
return;
if (projectile.getShooter() instanceof Player && !((Player) projectile.getShooter()).hasMetadata("NPC")) {
Player player = (Player) projectile.getShooter();
PlayerData playerData = PlayerData.get(player);
double distance = projectiles.get(projectile).distance(e.getEntity().getLocation());
for (ProjectileExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, projectile))
source.giveExperience(playerData, e.getFinalDamage() * distance, null);
}
}
projectiles.remove(projectile);
}
}
//Mark every arrow with the the location at which it was shot to calculate the distance
@EventHandler
public void onLaunch(ProjectileLaunchEvent e) {
if (e.getEntity().getShooter() instanceof Player) {
Player player = (Player) e.getEntity().getShooter();
if (player.hasMetadata("NPC"))
return;
projectiles.put(e.getEntity(), e.getLocation());
//Remove the projectile 15 s after it was launched
new BukkitRunnable() {
@Override
public void run() {
projectiles.remove(e.getEntity());
}
}.runTaskLater(MMOCore.plugin, 60 * 20L);
}
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, Projectile projectile) {
if (projectileType == null)
return true;
if (projectileType == null) return true;
return projectileType.matches(projectile);
}
public enum ProjectileType {
ARROW((p) -> p instanceof Arrow),
TRIDENT((p) -> p instanceof Trident);
ARROW((p) -> p instanceof Arrow), TRIDENT((p) -> p instanceof Trident);
private final Function<Projectile, Boolean> matching;
@ -124,4 +63,41 @@ public class ProjectileExperienceSource extends SpecificExperienceSource<Project
}
}
private static class Manager extends ExperienceSourceManager<ProjectileExperienceSource> {
private final FlushableRegistry<Projectile, Location> projectiles = new FlushableRegistry<>((proj, loc) -> proj.isDead(), 20 * 60);
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onHit(ProjectileHitEvent e) {
if (e.getHitBlock() != null) projectiles.getRegistry().remove(e.getEntity());
}
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onDamage(EntityDamageByEntityEvent e) {
if (e.getEntity() instanceof Projectile) {
Projectile projectile = (Projectile) e.getEntity();
Location loc = projectiles.getRegistry().get(projectile);
if (loc == null) return;
if (projectile.getShooter() instanceof Player && !((Player) projectile.getShooter()).hasMetadata("NPC")) {
Player player = (Player) projectile.getShooter();
PlayerData playerData = PlayerData.get(player);
double distance = loc.distance(e.getEntity().getLocation());
for (ProjectileExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, projectile))
source.giveExperience(playerData, e.getFinalDamage() * distance, null);
}
}
}
}
// Mark every arrow with the location at which it was shot to calculate the distance
@EventHandler
public void onLaunch(ProjectileLaunchEvent e) {
if (e.getEntity().getShooter() instanceof Player && UtilityMethods.isRealPlayer((Player) e.getEntity().getShooter())) {
projectiles.getRegistry().put(e.getEntity(), e.getLocation());
}
}
}
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
@ -40,20 +41,20 @@ public class RepairItemExperienceSource extends ExperienceSource<ItemStack> {
@Override
public ExperienceSourceManager<RepairItemExperienceSource> newManager() {
return new CustomExperienceManager();
return new Manager();
}
private class CustomExperienceManager extends ExperienceSourceManager<RepairItemExperienceSource> {
private static class Manager extends ExperienceSourceManager<RepairItemExperienceSource> {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void a(InventoryClickEvent event) {
if (event.getInventory() == null || event.getInventory().getType() != InventoryType.ANVIL || event.getSlot() != 2)
if (event.getInventory() == null || event.getInventory().getType() != InventoryType.ANVIL || event.getRawSlot() != 2)
return;
// Check if there's exp associated to it
final ItemStack item = event.getCurrentItem();
if (!MMOCore.plugin.smithingManager.hasExperience(item.getType()))
return;
final @Nullable ItemStack item = event.getCurrentItem();
if (UtilityMethods.isAir(item)) return;
if (!MMOCore.plugin.smithingManager.hasExperience(item.getType())) return;
final PlayerData data = PlayerData.get((Player) event.getWhoClicked());
for (RepairItemExperienceSource source : getSources())
@ -79,8 +80,8 @@ public class RepairItemExperienceSource extends ExperienceSource<ItemStack> {
*/
final double exp = MMOCore.plugin.smithingManager.getBaseExperience(item.getType())
* Math.max(0, ((Damageable) old.getItemMeta()).getDamage() - ((Damageable) item.getItemMeta()).getDamage()) / 100;
getDispenser().giveExperience(data, exp, data.getPlayer().getLocation(), EXPSource.SOURCE);
source.getDispenser().giveExperience(data, exp, data.getPlayer().getLocation(), EXPSource.SOURCE);
}
}
}
}
}

View File

@ -1,12 +1,13 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
import net.Indyuce.mmocore.experience.source.type.SpecificExperienceSource;
import net.Indyuce.mmocore.manager.profession.ExperienceSourceManager;
import net.Indyuce.mmocore.api.event.PlayerResourceUpdateEvent;
import org.apache.commons.lang.Validate;
import org.bukkit.event.EventHandler;
@ -21,39 +22,37 @@ public class ResourceExperienceSource extends SpecificExperienceSource<PlayerRes
*/
public ResourceExperienceSource(ExperienceDispenser dispenser, MMOLineConfig config) {
super(dispenser, config);
if (!config.contains("type"))
resource = null;
if (!config.contains("type")) resource = null;
else {
String str = config.getString("type").toUpperCase().replace("-", "_");
Validate.isTrue(str.equals("MANA") || str.equals("STELLIUM") || str.equals("STAMINA"),
"ResourceExperienceSource problem: The resource can only be mana, stamina or STELLIUM");
Validate.isTrue(str.equals("MANA") || str.equals("STELLIUM") || str.equals("STAMINA"), "ResourceExperienceSource problem: The resource can only be mana, stamina or STELLIUM");
resource = PlayerResource.valueOf(str);
}
}
@Override
public ExperienceSourceManager<ResourceExperienceSource> newManager() {
return new ExperienceSourceManager<ResourceExperienceSource>() {
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onResource(PlayerResourceUpdateEvent e) {
if (e.getPlayer().hasMetadata("NPC"))
return;
PlayerData playerData = PlayerData.get(e.getPlayer());
if(e.getAmount()<0)
for (ResourceExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, e.getResource()))
source.giveExperience(playerData, -e.getAmount(), null);
}
}
};
return new Manager();
}
@Override
public boolean matchesParameter(PlayerData player, PlayerResource obj) {
if (resource == null)
return !obj.equals(PlayerResource.HEALTH);
if (resource == null) return !obj.equals(PlayerResource.HEALTH);
return resource.equals(obj);
}
private static class Manager extends ExperienceSourceManager<ResourceExperienceSource> {
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onResource(PlayerResourceUpdateEvent event) {
if (!UtilityMethods.isRealPlayer(event.getPlayer())) return;
PlayerData playerData = PlayerData.get(event.getPlayer());
if (event.getAmount() >= 0) return;
for (ResourceExperienceSource source : getSources())
if (source.matchesParameter(playerData, event.getResource()))
source.giveExperience(playerData, -event.getAmount(), null);
}
}
}

View File

@ -1,5 +1,6 @@
package net.Indyuce.mmocore.experience.source;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.MMOLineConfig;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.experience.dispenser.ExperienceDispenser;
@ -40,29 +41,7 @@ public class RideExperienceSource extends SpecificExperienceSource<EntityType> {
@Override
public ExperienceSourceManager<RideExperienceSource> newManager() {
return new ExperienceSourceManager<RideExperienceSource>() {
@EventHandler(priority = HIGHEST,ignoreCancelled = true)
public void onRide(PlayerMoveEvent e) {
if (e.getPlayer().isInsideVehicle()) {
double deltax = e.getTo().getBlockX() - e.getFrom().getBlockX();
double deltay = e.getTo().getBlockY() - e.getFrom().getBlockY();
double deltaz = e.getTo().getBlockZ() - e.getFrom().getBlockZ();
if (deltax != 0 && deltay != 0 && deltaz != 0) {
double delta = Math.sqrt(deltax * deltax + deltay * deltay + deltaz * deltaz);
if (e.getPlayer().hasMetadata("NPC"))
return;
PlayerData playerData = PlayerData.get(e.getPlayer());
Entity vehicle = e.getPlayer().getVehicle();
for (RideExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, vehicle.getType()))
giveExperience(playerData, e.getFrom().distance(e.getTo()), null);
}
}
}
}
};
return new Manager();
}
@Override
@ -72,4 +51,25 @@ public class RideExperienceSource extends SpecificExperienceSource<EntityType> {
return type.equals(obj);
}
private static class Manager extends ExperienceSourceManager<RideExperienceSource> {
@EventHandler(priority = HIGHEST, ignoreCancelled = true)
public void onRide(PlayerMoveEvent event) {
if (!event.getPlayer().isInsideVehicle()) return;
double deltax = event.getTo().getBlockX() - event.getFrom().getBlockX();
double deltay = event.getTo().getBlockY() - event.getFrom().getBlockY();
double deltaz = event.getTo().getBlockZ() - event.getFrom().getBlockZ();
if (deltax != 0 || deltay != 0 || deltaz != 0) {
if (!UtilityMethods.isRealPlayer(event.getPlayer())) return;
PlayerData playerData = PlayerData.get(event.getPlayer());
Entity vehicle = event.getPlayer().getVehicle();
for (RideExperienceSource source : getSources()) {
if (source.matchesParameter(playerData, vehicle.getType()))
source.giveExperience(playerData, event.getFrom().distance(event.getTo()), null);
}
}
}
}
}

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