From 4b890ea7ecdce4edb10b69d935c97a29d96e87d2 Mon Sep 17 00:00:00 2001 From: lnkosadmin Date: Sun, 5 Apr 2026 19:44:57 +0800 Subject: [PATCH] add item commands --- LICENSE | 339 ++++++++++++ README.md | 48 ++ dependency-reduced-pom.xml | 96 ++++ pom.xml | 87 +++ src/main/java/ldcr/BedwarsXP/BedwarsXP.java | 105 ++++ src/main/java/ldcr/BedwarsXP/Config.java | 145 +++++ .../java/ldcr/BedwarsXP/EventListeners.java | 208 ++++++++ .../ldcr/BedwarsXP/XPShop/ShopReplacer.java | 102 ++++ .../ldcr/BedwarsXP/XPShop/XPItemShop.java | 502 ++++++++++++++++++ .../BedwarsXP/XPShop/XPVillagerTrade.java | 46 ++ .../java/ldcr/BedwarsXP/api/XPManager.java | 92 ++++ .../api/events/BedwarsXPDeathDropXPEvent.java | 58 ++ .../command/BedwarsXPCommandListener.java | 91 ++++ .../command/EditXPCommandListener.java | 74 +++ .../ldcr/BedwarsXP/utils/ActionBarUtils.java | 108 ++++ .../BedwarsXP/utils/BedwarsGameUtils.java | 37 ++ .../ldcr/BedwarsXP/utils/ReflectionUtils.java | 30 ++ .../ldcr/BedwarsXP/utils/ResourceUtils.java | 19 + .../BedwarsXP/utils/SendMessageUtils.java | 9 + .../ldcr/BedwarsXP/utils/SoundMachine.java | 21 + .../java/ldcr/BedwarsXP/utils/YamlUtils.java | 17 + .../GithubUpdateChecker/UpdateChecker.java | 44 ++ src/main/resources/config.yml | 62 +++ src/main/resources/enabledGames.yml | 1 + src/main/resources/language-en.yml | 81 +++ src/main/resources/language.yml | 77 +++ src/main/resources/plugin.yml | 18 + 27 files changed, 2517 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 dependency-reduced-pom.xml create mode 100644 pom.xml create mode 100644 src/main/java/ldcr/BedwarsXP/BedwarsXP.java create mode 100644 src/main/java/ldcr/BedwarsXP/Config.java create mode 100644 src/main/java/ldcr/BedwarsXP/EventListeners.java create mode 100644 src/main/java/ldcr/BedwarsXP/XPShop/ShopReplacer.java create mode 100644 src/main/java/ldcr/BedwarsXP/XPShop/XPItemShop.java create mode 100644 src/main/java/ldcr/BedwarsXP/XPShop/XPVillagerTrade.java create mode 100644 src/main/java/ldcr/BedwarsXP/api/XPManager.java create mode 100644 src/main/java/ldcr/BedwarsXP/api/events/BedwarsXPDeathDropXPEvent.java create mode 100644 src/main/java/ldcr/BedwarsXP/command/BedwarsXPCommandListener.java create mode 100644 src/main/java/ldcr/BedwarsXP/command/EditXPCommandListener.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/ActionBarUtils.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/BedwarsGameUtils.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/ReflectionUtils.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/ResourceUtils.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/SendMessageUtils.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/SoundMachine.java create mode 100644 src/main/java/ldcr/BedwarsXP/utils/YamlUtils.java create mode 100644 src/main/java/sakura/kooi/utils/GithubUpdateChecker/UpdateChecker.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/enabledGames.yml create mode 100644 src/main/resources/language-en.yml create mode 100644 src/main/resources/language.yml create mode 100644 src/main/resources/plugin.yml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b73255d --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# BedwarsXP + +[![License](https://img.shields.io/github/license/SakuraKoi/BedwarsXP?style=flat-square)](LICENSE) + +BedwarsXP - BedwarsRel addon (A Minecraft Server Plugin) + +BedwarsRel起床战争小游戏插件的经验起床Addon + +## 功能特性 + +- **经验经济系统**:将 BedwarsRel 的资源经济转换为经验值经济 +- **资源兑换商店**:添加经验兑换分类,允许玩家将资源转换为经验 +- **命令商店物品支持**:支持配置执行命令的商店物品(如权限、传送等命令) +- **全经验模式**:支持将所有商店物品转换为经验交易 + +## 最近更新 + +### 新增:命令商店物品支持 + +现在支持在商店中配置执行命令的物品,而非仅给予物品: + +```yaml +shop: + - item1: + type: EXP_BOTTLE + amount: 10 + reward: + type: STONE + meta: + ==: ItemMeta + meta-type: UNSPECIFIC + display-name: §eVIP权限 + commands: + - "lp user %player% parent add vip" +``` + +- `%player%` 变量会被替换为购买玩家的名称 +- 支持 XP 交易和传统物品交易两种模式 +- 购买时会自动扣除相应的经验或资源 + +## 协议 + +本项目基于 [GPL-2.0](LICENSE) 协议开源。 + +> This program is free software; you can redistribute it and/or modify +> it under the terms of the GNU General Public License as published by +> the Free Software Foundation; either version 2 of the License, or +> (at your option) any later version. diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml new file mode 100644 index 0000000..40f912f --- /dev/null +++ b/dependency-reduced-pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + sakura.kooi + BedwarsXP + 2.1.3 + + + + maven-compiler-plugin + 3.8.1 + + 8 + 8 + true + + + + maven-shade-plugin + 3.1.0 + + + package + + shade + + + + + + + org.bstats + sakura.kooi.utils.lib.bstats + + + + + + + + + spigot + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + org.projectlombok + lombok + 1.18.20 + provided + + + org.spigotmc + spigot-api + 1.12-R0.1-SNAPSHOT + provided + + + commons-lang + commons-lang + + + json-simple + com.googlecode.json-simple + + + guava + com.google.guava + + + gson + com.google.code.gson + + + snakeyaml + org.yaml + + + bungeecord-chat + net.md-5 + + + + + io.github.bedwarsrel + BedwarsRel + 1.3.6 + provided + + + + UTF-8 + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1fe7998 --- /dev/null +++ b/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + sakura.kooi + BedwarsXP + 2.1.3 + + + UTF-8 + + + + spigot + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.bstats + bstats-bukkit + 3.0.0 + compile + + + + org.projectlombok + lombok + 1.18.20 + provided + + + + org.spigotmc + spigot-api + 1.12-R0.1-SNAPSHOT + provided + + + + io.github.bedwarsrel + BedwarsRel + 1.3.6 + provided + + + + + + + maven-compiler-plugin + 3.8.1 + + 8 + 8 + true + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + + org.bstats + sakura.kooi.utils.lib.bstats + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/src/main/java/ldcr/BedwarsXP/BedwarsXP.java b/src/main/java/ldcr/BedwarsXP/BedwarsXP.java new file mode 100644 index 0000000..3ba9d16 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/BedwarsXP.java @@ -0,0 +1,105 @@ +package ldcr.BedwarsXP; + +import ldcr.BedwarsXP.command.BedwarsXPCommandListener; +import ldcr.BedwarsXP.command.EditXPCommandListener; +import ldcr.BedwarsXP.utils.ActionBarUtils; +import ldcr.BedwarsXP.utils.ReflectionUtils; +import lombok.Getter; +import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; +import sakura.kooi.utils.GithubUpdateChecker.UpdateChecker; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class BedwarsXP extends JavaPlugin { + @Getter + private static final Map l18nCache = new HashMap<>(); + @Getter + private static BedwarsXP instance; + @Getter + private static CommandSender consoleSender; + @Getter + private static String updateUrl = null; + + public static String l18n(String key, String... replacement) { + String message; + if (l18nCache.containsKey(key)) { + message = l18nCache.get(key); + } else { + message = Config.getLanguageYaml().getString(key, "LANG_NOT_FOUND_" + key); + l18nCache.put(key, message); + } + for (int i = 0, length = replacement.length / 2; i < length; i++) { + message = message.replace(replacement[i * 2], replacement[i * 2 + 1]); + } + return message; + } + + public static void sendConsoleMessage(String str) { + consoleSender.sendMessage("§6§lBedwarsXP §7>> " + str); + } + + @Override + public void onEnable() { + instance = this; + consoleSender = Bukkit.getConsoleSender(); + + try { + sendConsoleMessage("§bLoading BedwarsXP... Version." + getDescription().getVersion()); + Config.loadConfig(); + if (!detectBedwarsRelVersion()) { + Bukkit.getPluginManager().disablePlugin(this); + return; + } + ActionBarUtils.load(); + Bukkit.getPluginManager().registerEvents(new EventListeners(), this); + getCommand("bedwarsxp").setExecutor(new BedwarsXPCommandListener()); + getCommand("bedwarsxpedit").setExecutor(new EditXPCommandListener()); + } catch (Exception e) { + sendConsoleMessage("§c§lERROR: §c-----------------------------------"); + e.printStackTrace(); + sendConsoleMessage("§c§lERROR: §c-----------------------------------"); + sendConsoleMessage("§c§lERROR: §c" + l18n("ERROR_OCCURRED_WHILE_LOADING")); + sendConsoleMessage("§c§lERROR: §e ↓↓ << " + l18n("REPORT_ISSUE_HERE") + " >> ↓↓ "); + sendConsoleMessage("§c§lERROR: §c https://github.com/SakuraKoi/BedwarsXP/issues/1"); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + sendConsoleMessage("§b" + l18n("SUCCESSFULLY_LOADED") + " By.SakuraKooi"); + sendConsoleMessage("§e ↓↓ << " + l18n("REPORT_ISSUE_AND_SUGGESTION_HERE") + " >> ↓↓ "); + sendConsoleMessage("§c https://github.com/SakuraKoi/BedwarsXP/issues/1"); + // Update checker and metrics + if (!Config.disableUpdateChecker) { + Bukkit.getScheduler().runTaskLater(this, () -> { + try { + new UpdateChecker("SakuraKoi", "BedwarsXP", "v" + getDescription().getVersion(), link -> { + updateUrl = link; + sendConsoleMessage("§b" + l18n("HAS_UPDATE", "%link%", link)); + }).check(); + } catch (IOException ignored) {} + }, 100); + } + try { + new Metrics(this, 3999); + } catch (Exception ignored) {} + } + + private boolean detectBedwarsRelVersion() { + sendConsoleMessage("§a" + l18n("FINDING_BEDWARSREL")); + if (ReflectionUtils.isClassFound("io.github.yannici.bedwars.Main") || ReflectionUtils.isClassFound("io.github.bedwarsrel.BedwarsRel.Main")) { + sendConsoleMessage("§c" + l18n("BEDWARSREL_NOT_SUPPORTED")); + sendConsoleMessage("§c" + l18n("PLEASE_UPDATE_BEDWARSREL")); + return false; + } else if (ReflectionUtils.isClassFound("io.github.bedwarsrel.BedwarsRel")) { + sendConsoleMessage("§a" + l18n("BEDWARSREL_SUPPORTED")); + return true; + } else { + sendConsoleMessage("§c§lERROR: §c" + l18n("BEDWARSREL_NOT_FOUND")); + return false; + } + } +} diff --git a/src/main/java/ldcr/BedwarsXP/Config.java b/src/main/java/ldcr/BedwarsXP/Config.java new file mode 100644 index 0000000..cbaba21 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/Config.java @@ -0,0 +1,145 @@ +package ldcr.BedwarsXP; + +import ldcr.BedwarsXP.utils.YamlUtils; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class Config { + public static final Map resources = new EnumMap<>(Material.class); + public static final Set resourceskey = new HashSet<>(); + private static final int CONFIG_VERSION = 2; + private static final Set enabledGameList = new HashSet<>(); + public static boolean disableUpdateChecker; + + public static String xpMessage; + public static boolean addResShop; + + public static double deathCost; + public static double deathDrop; + public static boolean dontDropExpBottle; + public static int maxXP; + public static String maxXPMessage; + + public static boolean fullXPBedwars; + private static YamlConfiguration enabledGamesYaml; + private static File enabledGamesFile; + @Getter + private static YamlConfiguration languageYaml; + + public static void loadConfig() { + BedwarsXP.sendConsoleMessage("§b正在加载语言文件... | Loading language configuration..."); + File languageFile = new File("plugins/BedwarsXP/language.yml"); + if (!languageFile.exists()) { + BedwarsXP.sendConsoleMessage("§b语言文件不存在,正在创建..."); + BedwarsXP.sendConsoleMessage("§bWanna english? Just overwrites language.yml with language-en.yml :)"); + BedwarsXP.getInstance().saveResource("language.yml", true); + BedwarsXP.getInstance().saveResource("language-en.yml", true); + } + try { + languageYaml = YamlUtils.loadYamlUTF8(languageFile); + } catch (IOException e) { + languageYaml = new YamlConfiguration(); + BedwarsXP.sendConsoleMessage("§c语言文件加载失败 | Failed to load language"); + e.printStackTrace(); + } + BedwarsXP.sendConsoleMessage("§b" + BedwarsXP.l18n("LOADING_CONFIGURATION")); + File configFile = new File("plugins/BedwarsXP/config.yml"); + if (!configFile.exists()) { + BedwarsXP.sendConsoleMessage("§b" + BedwarsXP.l18n("CONFIGURATION_FILE_NOT_EXISTS")); + BedwarsXP.getInstance().saveResource("config.yml", true); + } + YamlConfiguration configYaml = YamlConfiguration.loadConfiguration(configFile); + + if (configYaml.getInt("ConfigVersion") < CONFIG_VERSION) { + BedwarsXP.sendConsoleMessage("§4" + BedwarsXP.l18n("OLD_VERSION_CONFIGURATION")); + configFile.renameTo(new File("plugins/BedwarsXP/config.bak.yml")); + BedwarsXP.sendConsoleMessage("§c" + BedwarsXP.l18n("OLD_CONFIGURATION_BACKUPED")); + BedwarsXP.getInstance().saveResource("config.yml", true); + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("NEW_CONFIGURATION_SAVED")); + configFile = new File("plugins/BedwarsXP/config.yml"); + configYaml = YamlConfiguration.loadConfiguration(configFile); + } + + disableUpdateChecker = configYaml.getBoolean("Disable_UpdateChecker", true); + + xpMessage = configYaml.getString("Message").replaceAll("&", "§").replaceAll("§§", "§"); + addResShop = configYaml.getBoolean("Add_Res_Shop"); + if (addResShop) { + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("RESOURCE_SHOP_ENABLED")); + } + + deathCost = configYaml.getInt("DeathCostXP", 0) / 100.0; + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("DEATH_COST_XP_PERCENT", "%percent%", String.valueOf(deathCost * 100))); + + deathDrop = configYaml.getInt("DeathDropXP", 0) / 100.0; + BedwarsXP.sendConsoleMessage( + "§6§lBedwarsXP §7>> §a" + (deathDrop == 0 ? BedwarsXP.l18n("DEATH_DROP_XP_DISABLED") : BedwarsXP.l18n("DEATH_DROP_XP_PERCEMT", "%percent%", String.valueOf(deathDrop * 100)))); + dontDropExpBottle = configYaml.getBoolean("DontDropExpBottle", false); + if (dontDropExpBottle) + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("DEATH_DROP_EXP_BOTTLE_DISABLED")); + + maxXP = configYaml.getInt("MaxXP"); + maxXPMessage = configYaml.getString("MaxXPMessage").replaceAll("&", "§").replaceAll("§§", "§"); + BedwarsXP.sendConsoleMessage("§a" + (maxXP == 0 ? BedwarsXP.l18n("MAX_XP_LIMIT_DISABLED") : BedwarsXP.l18n("MAX_XP_LIMIT_ENABLED", "%value%", String.valueOf(maxXP)))); + + fullXPBedwars = configYaml.getBoolean("Full_XP_Bedwars"); + if (fullXPBedwars) { + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("ALL_TRADES_USE_XP_ENABLED")); + } + + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("LOADING_RESOURCES_VALUE")); + ConfigurationSection resourceSection = io.github.bedwarsrel.BedwarsRel.getInstance().getConfig() + .getConfigurationSection("resource"); + for (String key : resourceSection.getKeys(false)) { + @SuppressWarnings("unchecked") + List> resourceList = (List>) io.github.bedwarsrel.BedwarsRel + .getInstance().getConfig().getList("resource." + key + ".item"); + for (Map resource : resourceList) { + ItemStack itemStack = ItemStack.deserialize(resource); + Material mat = itemStack.getType(); + int xp = configYaml.getInt("XP." + key, 0); + resources.put(mat, xp); + resourceskey.add(key); + BedwarsXP.sendConsoleMessage("§a" + BedwarsXP.l18n("FOUNDED_RESOURCE", + "%resource%", key, + "%material%", mat.toString(), + "%value%", String.valueOf(xp))); + } + } + + enabledGamesFile = new File("plugins/BedwarsXP/enabledGames.yml"); + if (!enabledGamesFile.exists()) { + BedwarsXP.sendConsoleMessage("§c" + BedwarsXP.l18n("WARN_YOU_NEEDS_ENABLE_BEDWARSXP_MANUALLY")); + BedwarsXP.getInstance().saveResource("enabledGames.yml", true); + } + enabledGamesYaml = YamlConfiguration.loadConfiguration(enabledGamesFile); + enabledGameList.addAll(enabledGamesYaml.getStringList("enabledGame")); + } + + public static String setGameEnableXP(String bw, boolean isEnabled) { + if (isEnabled) { + enabledGameList.add(bw); + } else { + enabledGameList.remove(bw); + } + enabledGamesYaml.set("enabledGame", new ArrayList<>(enabledGameList)); + try { + enabledGamesYaml.save(enabledGamesFile); + } catch (IOException e) { + e.printStackTrace(); + return e.getLocalizedMessage(); + } + return ""; + } + + public static boolean isGameEnabledXP(String bw) { + return enabledGameList.contains(bw); + } +} diff --git a/src/main/java/ldcr/BedwarsXP/EventListeners.java b/src/main/java/ldcr/BedwarsXP/EventListeners.java new file mode 100644 index 0000000..677e1bd --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/EventListeners.java @@ -0,0 +1,208 @@ +package ldcr.BedwarsXP; + +import io.github.bedwarsrel.BedwarsRel; +import io.github.bedwarsrel.events.BedwarsGameEndEvent; +import io.github.bedwarsrel.events.BedwarsGameStartEvent; +import io.github.bedwarsrel.game.Game; +import ldcr.BedwarsXP.XPShop.ShopReplacer; +import ldcr.BedwarsXP.api.XPManager; +import ldcr.BedwarsXP.api.events.BedwarsXPDeathDropXPEvent; +import ldcr.BedwarsXP.utils.ResourceUtils; +import ldcr.BedwarsXP.utils.SoundMachine; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Collections; + +public class EventListeners implements Listener { + @SuppressWarnings("deprecation") + @EventHandler + public void onItemPickup(PlayerPickupItemEvent e) { + Player p = e.getPlayer(); + Game bw = checkGame(p); + if (bw == null) return; + + Item entity = e.getItem(); + ItemStack stack = entity.getItemStack(); + if (stack == null) return; + Integer count; + if (stack.hasItemMeta() && stack.getItemMeta().getDisplayName() != null && stack.getItemMeta().getDisplayName().equals("§b§l&BedwarsXP_DroppedXP")) { + count = Integer.parseInt(stack.getItemMeta().getLore().get(0)); + } else { + count = ResourceUtils.convertResToXP(stack); + } + + if (count == null) + return; + + if (pickupXP(bw, p, count)) { + e.setCancelled(true); + entity.remove(); + } else { + e.setCancelled(true); + entity.setPickupDelay(10); + } + } + + private boolean pickupXP(Game bw, Player player, int count) { + if (count <= 0) return true; + + XPManager xpman = XPManager.getXPManager(bw.getName()); + // if current XP > maxXP -> deny pickup + if (Config.maxXP != 0 && xpman.getXP(player) >= Config.maxXP) { + xpman.sendMaxXPMessage(player); + return false; + } + int added = xpman.getXP(player) + count; + int leftXP = 0; + // if after pickup XP>maxXP -> set XP = maxXP + if (Config.maxXP != 0 && added > Config.maxXP) { + leftXP = added - Config.maxXP; + added = Config.maxXP; + } + xpman.setXP(player, added); + player.playSound(player.getLocation(), SoundMachine.get("ORB_PICKUP", "ENTITY_EXPERIENCE_ORB_PICKUP"), 0.2F, 1.5F); + xpman.sendXPMessage(player, count); + if (leftXP > 0) { + dropXPBottle(player, leftXP); + } + return true; + } + + private void dropXPBottle(Player player, int xp) { + ItemStack dropStack = new ItemStack(Material.EXP_BOTTLE, 16); + ItemMeta meta = dropStack.getItemMeta(); + meta.setDisplayName("§b§l&BedwarsXP_DroppedXP"); + meta.setLore(Collections.singletonList(String.valueOf(xp))); + meta.addEnchant(Enchantment.LOOT_BONUS_MOBS, 1, true); + dropStack.setItemMeta(meta); + Item droppedItem = player.getWorld().dropItemNaturally(player.getLocation().add(0, 1, 0), dropStack); + droppedItem.setPickupDelay(40); + } + + @EventHandler + public void onAnvilOpen(InventoryOpenEvent e) { + if (e.getPlayer() == null) + return; + if (e.getInventory() == null) + return; + Game bw = checkGame((Player) e.getPlayer()); + if (bw == null) return; + if (e.getInventory().getType().equals(InventoryType.ANVIL)) { + e.setCancelled(true); + } + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent e) { + Player p = e.getEntity(); + Game bw = checkGame(p); + if (bw == null) return; + + XPManager xpman = XPManager.getXPManager(bw.getName()); + // 计算死亡扣除经验值 + int costed = (int) (xpman.getXP(p) * Config.deathCost); + // 计算死亡掉落经验值 + int dropped = 0; + if (Config.deathDrop > 0) { + dropped = (int) (costed * Config.deathDrop); + } + BedwarsXPDeathDropXPEvent event = new BedwarsXPDeathDropXPEvent(bw.getName(), p, dropped, costed); + Bukkit.getPluginManager().callEvent(event); + costed = event.getXPCost(); + dropped = event.getXPDropped(); + // 扣除经验 + int to = xpman.getXP(p) - costed; + if (to < 0) { + to = 0; + } + e.setNewLevel(to); + xpman.setXP(p, to); + // 掉落经验 + if (dropped < 1) + return; + if (Config.dontDropExpBottle) { + EntityDamageEvent ev = p.getLastDamageCause(); + if (ev instanceof EntityDamageByEntityEvent) { + Object killer = ((EntityDamageByEntityEvent) ev).getDamager(); + if (killer instanceof Projectile) { + killer = ((Projectile) killer).getShooter(); + } + if (killer instanceof Player) { + pickupXP(bw, (Player) killer, dropped); + return; + } + } + } + dropXPBottle(p, dropped); + } + + @EventHandler + public void onBedWarsStart(BedwarsGameStartEvent e) { + if (e.isCancelled()) + return; + if (!Config.isGameEnabledXP(e.getGame().getName())) + return; + ShopReplacer.replaceShop(e.getGame().getName(), BedwarsXP.getConsoleSender()); + } + + @EventHandler + public void onBedWarsEnd(BedwarsGameEndEvent e) { + if (!Config.isGameEnabledXP(e.getGame().getName())) + return; + XPManager.reset(e.getGame().getName()); + } + + @EventHandler + public void onPlayerTeleport(PlayerTeleportEvent e) { // 在玩家传送后更新经验条 + Player p = e.getPlayer(); + Game bw = checkGame(p); + if (bw == null) return; + Bukkit.getScheduler().runTaskLater(BedwarsXP.getInstance(), + () -> XPManager.getXPManager(bw.getName()).updateXPBar(p), 5); + + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent e) { + Game bw = checkGame(e.getPlayer()); + if (bw == null) return; + XPManager.getXPManager(bw.getName()).updateXPBar(e.getPlayer()); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent e) { + if (BedwarsXP.getUpdateUrl() != null && e.getPlayer().hasPermission("bedwarsxp.admin")) { + Bukkit.getScheduler().runTaskLater(BedwarsXP.getInstance(), () -> { + if (e.getPlayer().isOnline()) + e.getPlayer().sendMessage("§6§lBedwarsXP §7>> §b" + BedwarsXP.l18n("HAS_UPDATE", "%link%", BedwarsXP.getUpdateUrl())); + }, 30); + } + } + + private Game checkGame(Player player) { + Game bw = BedwarsRel.getInstance().getGameManager().getGameOfPlayer(player); + if (bw == null) + return null; + if (!Config.isGameEnabledXP(bw.getName())) + return null; + return bw; + } +} diff --git a/src/main/java/ldcr/BedwarsXP/XPShop/ShopReplacer.java b/src/main/java/ldcr/BedwarsXP/XPShop/ShopReplacer.java new file mode 100644 index 0000000..f6c49a6 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/XPShop/ShopReplacer.java @@ -0,0 +1,102 @@ +package ldcr.BedwarsXP.XPShop; + +import io.github.bedwarsrel.BedwarsRel; +import io.github.bedwarsrel.game.Game; +import io.github.bedwarsrel.shop.NewItemShop; +import io.github.bedwarsrel.villager.MerchantCategory; +import io.github.bedwarsrel.villager.MerchantCategoryComparator; +import io.github.bedwarsrel.villager.VillagerTrade; +import ldcr.BedwarsXP.BedwarsXP; +import ldcr.BedwarsXP.Config; +import ldcr.BedwarsXP.utils.ReflectionUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.Map.Entry; + +public class ShopReplacer implements Runnable { + private final Game game; + private final CommandSender s; + + private ShopReplacer(String e, CommandSender sender) { + s = sender; + game = BedwarsRel.getInstance().getGameManager().getGame(e); + } + + public static void replaceShop(String bw, CommandSender sender) { + if (!Config.isGameEnabledXP(bw)) + return; + Bukkit.getScheduler().runTaskLater(BedwarsXP.getInstance(), new ShopReplacer(bw, sender), 20); + } + + @Override + public void run() { + HashMap map = game.getItemShopCategories(); + if (Config.fullXPBedwars) { + for (Entry en : map.entrySet()) { + MerchantCategory m = en.getValue(); + ArrayList t = m.getOffers(); + ArrayList n = new ArrayList<>(); + for (VillagerTrade villagerTrade : t) { + if (villagerTrade == null) + continue; + n.add(new XPVillagerTrade(villagerTrade)); + } + try { + ReflectionUtils.setPrivateValue(m, "offers", n); + } catch (Exception e1) { + s.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_OCCURRED_REPLACE_SHOP", "%game%", game.getName())); + e1.printStackTrace(); + } + map.put(en.getKey(), m); + } + } + if (Config.addResShop) { + ArrayList trades = new ArrayList<>(); + for (String key : Config.resourceskey) { + @SuppressWarnings("unchecked") + List> resourceList = (List>) io.github.bedwarsrel.BedwarsRel.getInstance().getConfig().getList("resource." + key + ".item"); + for (Map resource : resourceList) { + ItemStack itemStack = ItemStack.deserialize(resource); + if (itemStack != null) { + trades.add(new XPVillagerTrade(itemStack)); + } + } + } + MerchantCategory mc = new MerchantCategory(BedwarsXP.l18n("SHOP_XP_EXCHANGE_TITLE"), Material.EXP_BOTTLE, trades, Collections.singletonList(BedwarsXP.l18n("SHOP_XP_EXCHANGE_LORE")), 3, "bw.base"); + map.put(Material.EXP_BOTTLE, mc); + } + // 检查并显示带命令的物品数量 + int commandItems = 0; + for (MerchantCategory cat : map.values()) { + for (VillagerTrade trade : cat.getOffers()) { + if (trade.hasCommands()) { + commandItems++; + } + } + } + if (commandItems > 0) { + s.sendMessage("§6§lBedwarsXP §7>> §a检测到 " + commandItems + " 个命令商店物品"); + } + try { + Field itemshops = ReflectionUtils.getField(game, "newItemShops"); + itemshops.setAccessible(true); + HashMap shops = new HashMap<>(); + List order = new ArrayList<>(map.values()); + order.sort(new MerchantCategoryComparator()); + for (Player pl : game.getPlayers()) { + shops.put(pl, new XPItemShop(order, game)); + } + ReflectionUtils.setPrivateValue(game, "newItemShops", shops); + s.sendMessage("§6§lBedwarsXP §7>> §b" + BedwarsXP.l18n("SUCCESSFULLY_REPLACED_SHOP", "%game%", game.getName())); + } catch (Exception e) { + s.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_OCCURRED_WHILE_INITALIZING_XP_SHOP", "%game%", game.getName())); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/ldcr/BedwarsXP/XPShop/XPItemShop.java b/src/main/java/ldcr/BedwarsXP/XPShop/XPItemShop.java new file mode 100644 index 0000000..30e6cfc --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/XPShop/XPItemShop.java @@ -0,0 +1,502 @@ +package ldcr.BedwarsXP.XPShop; + +import io.github.bedwarsrel.BedwarsRel; +import io.github.bedwarsrel.game.Game; +import io.github.bedwarsrel.shop.NewItemShop; +import io.github.bedwarsrel.utils.ChatWriter; +import io.github.bedwarsrel.utils.Utils; +import io.github.bedwarsrel.villager.MerchantCategory; +import io.github.bedwarsrel.villager.VillagerTrade; +import ldcr.BedwarsXP.BedwarsXP; +import ldcr.BedwarsXP.api.XPManager; +import ldcr.BedwarsXP.utils.SoundMachine; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.PotionMeta; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +public class XPItemShop extends NewItemShop { + private final Game bedwars; + private final List categories; + private MerchantCategory currentCategory = null; + XPItemShop(List cate, Game bw) { + super(cate); + categories = cate; + bedwars = bw; + } + + @Override + public List getCategories() { + return categories; + } + + @Override + public boolean hasOpenCategory() { + return currentCategory != null; + } + + @Override + public boolean hasOpenCategory(MerchantCategory category) { + if (currentCategory == null) + return false; + + return currentCategory.equals(category); + } + + private int getCategoriesSize(Player player) { + int size = 0; + for (MerchantCategory cat : categories) { + if (cat.getMaterial() != null && (player == null || player.hasPermission(cat.getPermission()))) { + size++; + } + } + return size; + } + + @Override + public void openCategoryInventory(Player player) { + int catSize = getCategoriesSize(player); + int nom = catSize % 9 == 0 ? 9 : catSize % 9; + int size = catSize + 9 - nom + 9; + + Inventory inventory = Bukkit.createInventory(player, size, BedwarsRel._l("ingame.shop.name")); + + addCategoriesToInventory(inventory, player); + + Game game = BedwarsRel.getInstance().getGameManager().getGameOfPlayer(player); + ItemStack stack; + + if (game != null) { + if (game.getPlayerSettings(player).oneStackPerShift()) { + stack = new ItemStack(Material.BUCKET, 1); + ItemMeta meta = stack.getItemMeta(); + + meta.setDisplayName(ChatColor.AQUA + BedwarsRel._l("default.currently") + ": " + ChatColor.WHITE + BedwarsRel._l("ingame.shop.onestackpershift")); + + meta.setLore(new ArrayList<>()); + stack.setItemMeta(meta); + } else { + stack = new ItemStack(Material.LAVA_BUCKET, 1); + ItemMeta meta = stack.getItemMeta(); + + meta.setDisplayName(ChatColor.AQUA + BedwarsRel._l("default.currently") + ": " + ChatColor.WHITE + BedwarsRel._l("ingame.shop.fullstackpershift")); + + meta.setLore(new ArrayList<>()); + stack.setItemMeta(meta); + } + + inventory.setItem(size - 4, stack); + } + player.openInventory(inventory); + } + + private void addCategoriesToInventory(Inventory inventory, Player player) { + for (MerchantCategory category : categories) { + if (category.getMaterial() == null) { + BedwarsRel.getInstance().getServer().getConsoleSender().sendMessage(ChatWriter.pluginMessage(ChatColor.RED + "Careful: Not supported material in shop category '" + category.getName() + "'")); + } else if (player == null || player.hasPermission(category.getPermission())) { + ItemStack is = new ItemStack(category.getMaterial(), 1); + ItemMeta im = is.getItemMeta(); + + if (currentCategory != null && currentCategory.equals(category)) { + im.addEnchant(Enchantment.DAMAGE_ALL, 1, true); + } + + im.setDisplayName(category.getName()); + im.setLore(category.getLores()); + is.setItemMeta(im); + + inventory.addItem(is); + } + } + } + + private int getInventorySize(int itemAmount) { + int nom = itemAmount % 9 == 0 ? 9 : itemAmount % 9; + return itemAmount + 9 - nom; + } + + @Override + public void handleInventoryClick(InventoryClickEvent ice, Game game, Player player) { + if (!hasOpenCategory()) { + handleCategoryInventoryClick(ice, game, player); + } else { + handleBuyInventoryClick(ice, game, player); + } + } + + private void handleCategoryInventoryClick(InventoryClickEvent ice, Game game, Player player) { + int catSize = getCategoriesSize(player); + int sizeCategories = getInventorySize(catSize) + 9; + int rawSlot = ice.getRawSlot(); + + if (rawSlot >= getInventorySize(catSize) && rawSlot < sizeCategories) { + ice.setCancelled(true); + + if (ice.getCurrentItem().getType() == Material.BUCKET) { + game.getPlayerSettings(player).setOneStackPerShift(false); + player.playSound(player.getLocation(), SoundMachine.get("CLICK", "UI_BUTTON_CLICK"), 10.0F, 1.0F); + openCategoryInventory(player); + return; + } + if (ice.getCurrentItem().getType() == Material.LAVA_BUCKET) { + game.getPlayerSettings(player).setOneStackPerShift(true); + player.playSound(player.getLocation(), SoundMachine.get("CLICK", "UI_BUTTON_CLICK"), 10.0F, 1.0F); + openCategoryInventory(player); + return; + } + + } + + if (rawSlot >= sizeCategories) { + if (ice.isShiftClick()) { + ice.setCancelled(true); + return; + } + + ice.setCancelled(false); + return; + } + + MerchantCategory clickedCategory = getCategoryByMaterial(ice.getCurrentItem().getType()); + if (clickedCategory == null) { + if (ice.isShiftClick()) { + ice.setCancelled(true); + return; + } + + ice.setCancelled(false); + return; + } + + openBuyInventory(clickedCategory, player, game); + } + + private void openBuyInventory(MerchantCategory category, Player player, Game game) { + ArrayList offers = category.getOffers(); + int sizeCategories = getCategoriesSize(player); + int sizeItems = offers.size(); + int invSize = getBuyInventorySize(sizeCategories, sizeItems); + + player.playSound(player.getLocation(), SoundMachine.get("CLICK", "UI_BUTTON_CLICK"), 10.0F, 1.0F); + + currentCategory = category; + Inventory buyInventory = Bukkit.createInventory(player, invSize, BedwarsRel._l("ingame.shop.name")); + addCategoriesToInventory(buyInventory, player); + + for (int i = 0; i < offers.size(); i++) { + VillagerTrade trade = offers.get(i); + if (trade.getItem1().getType() != Material.AIR || trade.getRewardItem().getType() != Material.AIR) { + int slot = getInventorySize(sizeCategories) + i; + ItemStack tradeStack = toItemStack(trade, player, game); + + buyInventory.setItem(slot, tradeStack); + } + } + player.openInventory(buyInventory); + } + + private int getBuyInventorySize(int sizeCategories, int sizeOffers) { + return getInventorySize(sizeCategories) + getInventorySize(sizeOffers); + } + + @SuppressWarnings("deprecation") + private ItemStack toItemStack(VillagerTrade trade, Player player, Game game) { + ItemStack tradeStack = trade.getRewardItem().clone(); + Method colorable = Utils.getColorableMethod(tradeStack.getType()); + ItemMeta meta = tradeStack.getItemMeta(); + ItemStack item1 = trade.getItem1(); + ItemStack item2 = trade.getItem2(); + if (tradeStack.getType().equals(Material.STAINED_GLASS) || tradeStack.getType().equals(Material.WOOL) || tradeStack.getType().equals(Material.STAINED_CLAY)) { + tradeStack.setDurability(game.getPlayerTeam(player).getColor().getDyeColor().getWoolData()); + } else if (colorable != null) { + colorable.setAccessible(true); + try { + colorable.invoke(meta, game.getPlayerTeam(player).getColor().getColor()); + } catch (Exception e) { + e.printStackTrace(); + } + } + List lores = meta.getLore(); + if (lores == null) { + lores = new ArrayList<>(); + } + if (trade instanceof XPVillagerTrade) { + lores.add(BedwarsXP.l18n("SHOP_TRADE_XP", "%xp%", String.valueOf(((XPVillagerTrade) trade).getXp()))); + } else { + lores.add(ChatColor.WHITE + String.valueOf(item1.getAmount()) + " " + item1.getItemMeta().getDisplayName()); + if (item2 != null) { + lores.add(ChatColor.WHITE + String.valueOf(item2.getAmount()) + " " + item2.getItemMeta().getDisplayName()); + } + } + meta.setLore(lores); + tradeStack.setItemMeta(meta); + return tradeStack; + } + + private void handleBuyInventoryClick(InventoryClickEvent ice, Game game, Player player) { + int sizeCategories = getCategoriesSize(player); + ArrayList offers = currentCategory.getOffers(); + int sizeItems = offers.size(); + int totalSize = getBuyInventorySize(sizeCategories, sizeItems); + + ItemStack item = ice.getCurrentItem(); + boolean cancel = false; + int bought = 0; + boolean oneStackPerShift = game.getPlayerSettings(player).oneStackPerShift(); + + if (currentCategory == null) { + player.closeInventory(); + return; + } + + if (ice.getRawSlot() < sizeCategories) { + ice.setCancelled(true); + + if (item == null) + return; + if (item.getType().equals(currentCategory.getMaterial())) { + currentCategory = null; + openCategoryInventory(player); + } else { + handleCategoryInventoryClick(ice, game, player); + } + } else if (ice.getRawSlot() < totalSize) { + ice.setCancelled(true); + + if (item == null || item.getType() == Material.AIR) + return; + + MerchantCategory category = currentCategory; + VillagerTrade trade = getTradingItem(category, item, game, player); + + if (trade == null) { + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] getTradingItem returned null!"); + return; + } + + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] getTradingItem returned trade: " + trade.getClass().getName()); + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] trade.hasCommands in handleBuy: " + trade.hasCommands()); + if (trade.getRewardItem() != null) { + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] reward item: " + trade.getRewardItem().getType()); + } + + player.playSound(player.getLocation(), SoundMachine.get("ITEM_PICKUP", "ENTITY_ITEM_PICKUP"), 10.0F, 1.0F); + + if (!hasEnoughRessource(player, trade)) { + player.sendMessage(ChatWriter.pluginMessage(ChatColor.RED + BedwarsRel._l("errors.notenoughress"))); + return; + } + + if (ice.isShiftClick()) { + while (hasEnoughRessource(player, trade) && !cancel) { + cancel = !buyItem(trade, ice.getCurrentItem(), player); + if (!cancel && oneStackPerShift) { + bought += item.getAmount(); + cancel = bought + item.getAmount() > 64; + } + } + } else { + buyItem(trade, ice.getCurrentItem(), player); + } + } else { + ice.setCancelled(ice.isShiftClick()); + } + } + + private boolean buyItem(VillagerTrade trade, ItemStack item, Player player) { + PlayerInventory inventory = player.getInventory(); + boolean success = true; + + // 调试输出 + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] buyItem called, trade class: " + trade.getClass().getName()); + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] trade.hasCommands(): " + trade.hasCommands()); + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] trade.getCommands(): " + trade.getCommands()); + + // 检查是否有命令,如果有则执行命令而不是给予物品 + if (trade.hasCommands()) { + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop] 执行命令物品,玩家: " + player.getName() + ", 命令数: " + trade.getCommands().size()); + if (!(trade instanceof XPVillagerTrade)) { + // 非XP trade 扣除物品 + int item1ToPay = trade.getItem1().getAmount(); + Iterator stackIterator = inventory.all(trade.getItem1().getType()).entrySet().iterator(); + int firstItem1 = inventory.first(trade.getItem1()); + takeItem(inventory, item1ToPay, stackIterator, firstItem1); + if (trade.getItem2() != null) { + int item2ToPay = trade.getItem2().getAmount(); + stackIterator = inventory.all(trade.getItem2().getType()).entrySet().iterator(); + int firstItem2 = inventory.first(trade.getItem2()); + takeItem(inventory, item2ToPay, stackIterator, firstItem2); + } + } else { + // XP trade 扣除经验 + XPManager.getXPManager(bedwars.getName()).takeXP(player, ((XPVillagerTrade) trade).getXp()); + } + // 执行命令 + for (String command : trade.getCommands()) { + String formattedCommand = command.replace("%player%", player.getName()); + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop] 执行命令: " + formattedCommand); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); + } + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop] 命令执行完成"); + return success; + } + + // 没有命令,正常给予物品 + if (!(trade instanceof XPVillagerTrade)) { + // 非XPtrade 使用旧式购买 + int item1ToPay = trade.getItem1().getAmount(); + Iterator stackIterator = inventory.all(trade.getItem1().getType()).entrySet().iterator(); + int firstItem1 = inventory.first(trade.getItem1()); + takeItem(inventory, item1ToPay, stackIterator, firstItem1); + if (trade.getItem2() != null) { + int item2ToPay = trade.getItem2().getAmount(); + stackIterator = inventory.all(trade.getItem2().getType()).entrySet().iterator(); + + int firstItem2 = inventory.first(trade.getItem2()); + takeItem(inventory, item2ToPay, stackIterator, firstItem2); + } + } else { + // Already checked has enough XP + XPManager.getXPManager(bedwars.getName()).takeXP(player, ((XPVillagerTrade) trade).getXp()); + } + ItemStack addingItem = item.clone(); + ItemMeta meta = addingItem.getItemMeta(); + List lore = meta.getLore(); + + if (!lore.isEmpty()) { + lore.remove(lore.size() - 1); + if (trade.getItem2() != null && !(trade instanceof XPVillagerTrade)) { + lore.remove(lore.size() - 1); + } + } + + meta.setLore(lore); + addingItem.setItemMeta(meta); + + HashMap notStored = inventory.addItem(addingItem); + if (notStored.size() > 0) { + ItemStack notAddedItem = notStored.get(0); + int removingAmount = addingItem.getAmount() - notAddedItem.getAmount(); + addingItem.setAmount(removingAmount); + inventory.removeItem(addingItem); + + inventory.addItem(trade.getItem1()); + if (trade.getItem2() != null) { + inventory.addItem(trade.getItem2()); + } + + success = false; + } + + player.updateInventory(); + return success; + } + + private void takeItem(PlayerInventory inventory, int item1ToPay, Iterator stackIterator, int firstItem1) { + if (firstItem1 > -1) { + inventory.clear(firstItem1); + } else { + while (stackIterator.hasNext()) { + Entry entry = (Entry) stackIterator.next(); + ItemStack stack = (ItemStack) entry.getValue(); + + int endAmount = stack.getAmount() - item1ToPay; + if (endAmount < 0) { + endAmount = 0; + } + + item1ToPay -= stack.getAmount(); + stack.setAmount(endAmount); + inventory.setItem((Integer) entry.getKey(), stack); + + if (item1ToPay <= 0) { + break; + } + } + } + } + + private boolean hasEnoughRessource(Player player, VillagerTrade trade) { + if (trade instanceof XPVillagerTrade) + return XPManager.getXPManager(bedwars.getName()).hasEnoughXP(player, ((XPVillagerTrade) trade).getXp()); + else { + ItemStack item1 = trade.getItem1(); + ItemStack item2 = trade.getItem2(); + PlayerInventory inventory = player.getInventory(); + + if (item2 != null) { + return inventory.contains(item1.getType(), item1.getAmount()) && inventory.contains(item2.getType(), item2.getAmount()); + } else return inventory.contains(item1.getType(), item1.getAmount()); + } + } + + private VillagerTrade getTradingItem(MerchantCategory category, ItemStack stack, Game game, Player player) { + for (VillagerTrade trade : category.getOffers()) { + if (trade.getItem1().getType() != Material.AIR || trade.getRewardItem().getType() != Material.AIR) { + // 获取原始 reward 物品(不经过 toItemStack 处理) + ItemStack reward = trade.getRewardItem(); + + // 首先比较类型 + if (reward.getType() != stack.getType()) { + continue; + } + + // 对于末影箱特殊处理 + if (reward.getType() == Material.ENDER_CHEST && stack.getType() == Material.ENDER_CHEST) + return trade; + + // 对于药水类特殊处理 + if (reward.getType() == Material.POTION || + (!BedwarsRel.getInstance().getCurrentVersion().startsWith("v1_9") && + (reward.getType().equals(Material.valueOf("TIPPED_ARROW")) || + reward.getType().equals(Material.valueOf("LINGERING_POTION")) || + reward.getType().equals(Material.valueOf("SPLASH_POTION"))))) { + if (((PotionMeta) reward.getItemMeta()).getCustomEffects().equals(((PotionMeta) stack.getItemMeta()).getCustomEffects())) + return trade; + } + + // 对于普通物品,比较显示名 + ItemMeta rewardMeta = reward.getItemMeta(); + ItemMeta stackMeta = stack.getItemMeta(); + String rewardName = rewardMeta != null ? rewardMeta.getDisplayName() : null; + String stackName = stackMeta != null ? stackMeta.getDisplayName() : null; + + if ((rewardName == null && stackName == null) || + (rewardName != null && rewardName.equals(stackName))) { + Bukkit.getConsoleSender().sendMessage("[BW-XP-Shop-DEBUG] Matched trade: name=" + rewardName + ", hasCommands=" + trade.hasCommands()); + return trade; + } + } + } + return null; + } + + private MerchantCategory getCategoryByMaterial(Material material) { + for (MerchantCategory category : categories) { + if (category.getMaterial() == material) + return category; + } + + return null; + } + + @Override + public void setCurrentCategory(MerchantCategory category) { + currentCategory = category; + } +} \ No newline at end of file diff --git a/src/main/java/ldcr/BedwarsXP/XPShop/XPVillagerTrade.java b/src/main/java/ldcr/BedwarsXP/XPShop/XPVillagerTrade.java new file mode 100644 index 0000000..e4b4d8c --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/XPShop/XPVillagerTrade.java @@ -0,0 +1,46 @@ +package ldcr.BedwarsXP.XPShop; + +import io.github.bedwarsrel.villager.VillagerTrade; +import ldcr.BedwarsXP.utils.ResourceUtils; +import lombok.Setter; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class XPVillagerTrade extends VillagerTrade { + @Setter + private int xp = 0; + + public XPVillagerTrade(VillagerTrade t) { + super(t.getItem1(), t.getItem2(), t.getRewardItem(), t.getCommands()); + setXp(ResourceUtils.convertResToXPExact(t.getItem1()) + ResourceUtils.convertResToXPExact(t.getItem2())); + } + + public XPVillagerTrade(ItemStack convert) { + super(convert, null, convert); + setXp(ResourceUtils.convertResToXP(convert)); + } + + public XPVillagerTrade(int xp, ItemStack rewardItem) { + super(new ItemStack(Material.EXP_BOTTLE, xp), rewardItem); + setXp(xp); + } + + public XPVillagerTrade(int xp, ItemStack rewardItem, List commands) { + super(new ItemStack(Material.EXP_BOTTLE, xp), rewardItem, commands); + setXp(xp); + } + + /** + * @deprecated It will be removed in later version, use getXp() instead + */ + @Deprecated + public int getXP() { + return this.xp; + } + + public int getXp() { + return this.xp; + } +} diff --git a/src/main/java/ldcr/BedwarsXP/api/XPManager.java b/src/main/java/ldcr/BedwarsXP/api/XPManager.java new file mode 100644 index 0000000..1b250b5 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/api/XPManager.java @@ -0,0 +1,92 @@ +package ldcr.BedwarsXP.api; + +import ldcr.BedwarsXP.Config; +import ldcr.BedwarsXP.utils.ActionBarUtils; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class XPManager { + private static final Map managerMap = new HashMap<>(); + private final Map xp = new HashMap<>(); + private final HashMap messageTimeMap = new HashMap<>(); + private final HashMap messageCountMap = new HashMap<>(); + + public static XPManager getXPManager(String bedwarsGame) { + if (!managerMap.containsKey(bedwarsGame)) { + managerMap.put(bedwarsGame, new XPManager()); + } + return managerMap.get(bedwarsGame); + } + + public static void reset(String bedwarsGame) { + managerMap.remove(bedwarsGame); + } + + public void updateXPBar(Player player) { + player.setLevel(get(player)); + } + + private void set(Player player, int count) { + xp.put(player.getUniqueId(), count); + updateXPBar(player); + } + + private int get(Player player) { + Integer value = xp.get(player.getUniqueId()); + if (value == null) { + value = 0; + xp.put(player.getUniqueId(), 0); + } + return value; + } + + public void setXP(Player player, int count) { + set(player, count); + } + + public int getXP(Player player) { + return get(player); + } + + public void addXP(Player player, int count) { + set(player, get(player) + count); + } + + public boolean takeXP(Player player, int count) { + if (!hasEnoughXP(player, count)) + return false; + set(player, get(player) - count); + return true; + } + + public boolean hasEnoughXP(Player player, int count) { + return get(player) >= count; + } + + public void sendXPMessage(Player player, int count) { + if (!messageTimeMap.containsKey(player.getUniqueId())) { + messageTimeMap.put(player.getUniqueId(), System.currentTimeMillis()); + } + if (!messageCountMap.containsKey(player.getUniqueId())) { + messageCountMap.put(player.getUniqueId(), 0); + } + if (System.currentTimeMillis() - messageTimeMap.get(player.getUniqueId()) > 500) { + messageCountMap.put(player.getUniqueId(), 0); + } + messageTimeMap.put(player.getUniqueId(), System.currentTimeMillis()); + int c = messageCountMap.get(player.getUniqueId()) + count; + messageCountMap.put(player.getUniqueId(), c); + if (!Config.xpMessage.isEmpty()) { + ActionBarUtils.sendActionBar(player, Config.xpMessage.replaceAll("%xp%", Integer.toString(c))); + } + } + + public void sendMaxXPMessage(Player player) { + if (!Config.maxXPMessage.isEmpty()) { + ActionBarUtils.sendActionBar(player, Config.maxXPMessage); + } + } +} diff --git a/src/main/java/ldcr/BedwarsXP/api/events/BedwarsXPDeathDropXPEvent.java b/src/main/java/ldcr/BedwarsXP/api/events/BedwarsXPDeathDropXPEvent.java new file mode 100644 index 0000000..b90b735 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/api/events/BedwarsXPDeathDropXPEvent.java @@ -0,0 +1,58 @@ +package ldcr.BedwarsXP.api.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BedwarsXPDeathDropXPEvent extends Event { + private final String game; + private final Player player; + private int deathCost; + private int deathDropped; + public BedwarsXPDeathDropXPEvent(String game, Player p, int dropped, int cost) { + this.game = game; + player = p; + deathCost = cost; + deathDropped = dropped; + } + + @Override + public HandlerList getHandlers() { + return new HandlerList(); + } + + public String getGameName() { + return game; + } + + public Player getDeadPlayer() { + return player; + } + + public int getXPCost() { + return deathCost; + } + + public void setXPCost(int drop) { + deathCost = drop; + } + + public int getXPDropped() { + return deathDropped; + } + + public void setXPDropped(int deathDropped) { + this.deathDropped = deathDropped; + } + + @Deprecated + public int getXPCosted() { + return deathCost; + } + + @Deprecated + public void setXPCosted(int drop) { + deathCost = drop; + } + +} diff --git a/src/main/java/ldcr/BedwarsXP/command/BedwarsXPCommandListener.java b/src/main/java/ldcr/BedwarsXP/command/BedwarsXPCommandListener.java new file mode 100644 index 0000000..2106c0a --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/command/BedwarsXPCommandListener.java @@ -0,0 +1,91 @@ +package ldcr.BedwarsXP.command; + +import ldcr.BedwarsXP.BedwarsXP; +import ldcr.BedwarsXP.Config; +import ldcr.BedwarsXP.utils.BedwarsGameUtils; +import ldcr.BedwarsXP.utils.SendMessageUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +public class BedwarsXPCommandListener implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command arg1, String arg2, String[] args) { + if (args.length == 0) { + SendMessageUtils.sendMessage(sender, + "§6§lBedwarsXP §7>> §bBedwarsXP v." + BedwarsXP.getInstance().getDescription().getVersion() + " §lBy.SakuraKooi", + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_RELOAD"), + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_ENABLE"), + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_DISABLE")); + return true; + } + if (!sender.hasPermission("bedwarsxp.admin")) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("YOU_DONT_HAVE_PERMISSION_TO_EXECUTE_THIS_COMMAND")); + return true; + } + switch (args[0].toLowerCase()) { + case "reload": { + Config.loadConfig(); + BedwarsXP.getL18nCache().clear(); + sender.sendMessage("§6§lBedwarsXP §7>> §b" + BedwarsXP.l18n("SUCCESSFULLY_RELOADED")); + if (BedwarsGameUtils.isAnyBedwarsRunning()) { + SendMessageUtils.sendMessage(sender, + "§6§lBedwarsXP §7>> §b" + BedwarsXP.l18n("RELOAD_GAME_RUNNING"), + "§6§lBedwarsXP §7>> §b" + BedwarsXP.l18n("UPDATE_RUNNING_GAME")); + BedwarsGameUtils.replaceAllShop(sender); + } + return true; + } + case "enable": { + if (args.length != 2) { + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_ENABLE")); + return true; + } + if (!BedwarsGameUtils.isGameExists(args[1])) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_GAME_NOT_FOUND", "%game%", args[1])); + return true; + } + String result = Config.setGameEnableXP(args[1], true); + if (result.equals("")) { + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("GAME_XP_ENABLED", "%game%", args[1])); + if (BedwarsGameUtils.isGameRunning(args[1])) { + sender.sendMessage("§6§lBedwarsXP §7>> §4" + BedwarsXP.l18n("GAME_IS_RUNNING_RESTART_REQUIRED", "%game%", args[1])); + } + } else { + SendMessageUtils.sendMessage(sender, "§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_OCCURRED"), "§6§lBedwarsXP §7>> §c" + result); + } + return true; + } + case "disable": { + if (args.length != 2) { + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_DISABLE")); + return true; + } + if (!BedwarsGameUtils.isGameExists(args[1])) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_GAME_NOT_FOUND", "%game%", args[1])); + return true; + } + String result = Config.setGameEnableXP(args[1], false); + if (result.equals("")) { + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("GAME_XP_DISABLED", "%game%", args[1])); + if (BedwarsGameUtils.isGameRunning(args[1])) { + sender.sendMessage("§6§lBedwarsXP §7>> §4" + BedwarsXP.l18n("GAME_IS_RUNNING_RESTART_REQUIRED", "%game%", args[1])); + } + } else { + SendMessageUtils.sendMessage(sender, "§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("ERROR_OCCURRED"), "§6§lBedwarsXP §7>> §c" + result); + } + return true; + } + default: { + SendMessageUtils.sendMessage(sender, + "§6§lBedwarsXP §7>> §bBedwarsXP v." + BedwarsXP.getInstance().getDescription().getVersion() + " §lBy.SakuraKooi", + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_RELOAD"), + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_ENABLE"), + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_MAIN_DISABLE")); + return true; + } + } + } + +} diff --git a/src/main/java/ldcr/BedwarsXP/command/EditXPCommandListener.java b/src/main/java/ldcr/BedwarsXP/command/EditXPCommandListener.java new file mode 100644 index 0000000..1823ab7 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/command/EditXPCommandListener.java @@ -0,0 +1,74 @@ +package ldcr.BedwarsXP.command; + +import io.github.bedwarsrel.BedwarsRel; +import io.github.bedwarsrel.game.Game; +import ldcr.BedwarsXP.BedwarsXP; +import ldcr.BedwarsXP.Config; +import ldcr.BedwarsXP.api.XPManager; +import ldcr.BedwarsXP.utils.SendMessageUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class EditXPCommandListener implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command arg1, String arg2, String[] args) { + if (args.length < 3) { + SendMessageUtils.sendMessage(sender, + "§6§lBedwarsXP §7>> §bBedwarsXP v." + BedwarsXP.getInstance().getDescription().getVersion() + " §lBy.SakuraKooi", + "§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("HELP_EDITXP")); + return true; + } + if (!sender.hasPermission("bedwarsxp.admin")) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("YOU_DONT_HAVE_PERMISSION_TO_EXECUTE_THIS_COMMAND")); + return true; + } + String user = args[1]; + OfflinePlayer offPlayer = Bukkit.getPlayer(user); + if (offPlayer != null) { + if (offPlayer.isOnline()) { + Player p = offPlayer.getPlayer(); + Game bw = BedwarsRel.getInstance().getGameManager().getGameOfPlayer(p); + if (bw == null) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("EDITXP_PLAYER_NOT_IN_GAME", "%player%", p.getName())); + return true; + } + if (!Config.isGameEnabledXP(bw.getName())) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("EDITXP_GAME_IS_NOT_XP_MODE", "%player%", p.getName())); + return true; + } + int xp; + try { + xp = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("EDITXP_XP_IS_NOT_A_NUMBER")); + return true; + } + if ("set".equalsIgnoreCase(args[0])) { + XPManager.getXPManager(bw.getName()).setXP(p, xp); + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("EDITXP_XP_HAS_BEEN_SET_TO", "%player%", p.getName(), "%xp%", String.valueOf(xp))); + } else if ("add".equalsIgnoreCase(args[0])) { + int current = XPManager.getXPManager(bw.getName()).getXP(p); + XPManager.getXPManager(bw.getName()).setXP(p, current + xp); + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("EDITXP_XP_HAS_BEEN_SET_TO", "%player%", p.getName(), "%xp%", String.valueOf(current + xp))); + } else if ("take".equalsIgnoreCase(args[0])) { + int current = XPManager.getXPManager(bw.getName()).getXP(p); + XPManager.getXPManager(bw.getName()).setXP(p, current - xp); + sender.sendMessage("§6§lBedwarsXP §7>> §a" + BedwarsXP.l18n("EDITXP_XP_HAS_BEEN_SET_TO", "%player%", p.getName(), "%xp%", String.valueOf(current - xp))); + } else { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("HELP_EDITXP")); + } + } else { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("EDITXP_PLAYER_NOT_IN_GAME", "%player%", offPlayer.getName())); + } + } else { + sender.sendMessage("§6§lBedwarsXP §7>> §c" + BedwarsXP.l18n("EDITXP_PLAYER_NOT_IN_GAME", "%player%", args[1])); + } + return true; + } + +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/ActionBarUtils.java b/src/main/java/ldcr/BedwarsXP/utils/ActionBarUtils.java new file mode 100644 index 0000000..830d72c --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/ActionBarUtils.java @@ -0,0 +1,108 @@ +package ldcr.BedwarsXP.utils; + +import ldcr.BedwarsXP.BedwarsXP; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ActionBarUtils { + private static boolean USE_CHAT = false; + private static boolean USE_1_7_NMS = false; + private static boolean USE_1_8_NMS = false; + private static boolean USE_1_11_API = false; + + private static Class classCraftPlayer; + private static Class classPacketPlayOutChat; + private static Class classPacket; + private static Class classChatSerializer; + private static Method methodSerializeMessage; + private static Class classIChatBaseComponent; + private static Class classChatComponentText; + private static Method methodGetHandle = null; + private static Field fieldPlayerConnection = null; + private static Method methodSendPacket = null; + + public static void load() { + String NMS_VERSION = Bukkit.getServer().getClass().getPackage().getName(); + NMS_VERSION = NMS_VERSION.substring(NMS_VERSION.lastIndexOf('.') + 1); + + if (NMS_VERSION.equalsIgnoreCase("v1_8_R1") || NMS_VERSION.equalsIgnoreCase("v1_7_")) { + USE_1_7_NMS = true; + } else { + try { + int ver = Integer.parseInt(NMS_VERSION.split("_")[1]); + if (ver >= 11) { + USE_1_11_API = true; + } else { + USE_1_8_NMS = true; + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) { + BedwarsXP.sendConsoleMessage("§cERROR: " + BedwarsXP.l18n("ERROR_UNSUPPORTED_VERSION_ACTIONBAR_MAY_NOT_WORK")); + USE_CHAT = true; + } + } + + if (USE_1_7_NMS || USE_1_8_NMS) { + try { + classCraftPlayer = Class.forName("org.bukkit.craftbukkit." + NMS_VERSION + ".entity.CraftPlayer"); + classPacketPlayOutChat = Class.forName("net.minecraft.server." + NMS_VERSION + ".PacketPlayOutChat"); + classIChatBaseComponent = Class.forName("net.minecraft.server." + NMS_VERSION + ".IChatBaseComponent"); + classPacket = Class.forName("net.minecraft.server." + NMS_VERSION + ".Packet"); + if (USE_1_7_NMS) { + classChatSerializer = Class.forName("net.minecraft.server." + NMS_VERSION + ".ChatSerializer"); + methodSerializeMessage = classChatSerializer.getDeclaredMethod("a", String.class); + } else { + classChatComponentText = Class.forName("net.minecraft.server." + NMS_VERSION + ".ChatComponentText"); + } + } catch (ReflectiveOperationException e) { + BedwarsXP.sendConsoleMessage("§cERROR: " + BedwarsXP.l18n("ERROR_UNSUPPORTED_VERSION_ACTIONBAR_MAY_NOT_WORK")); + USE_1_7_NMS = false; + USE_1_8_NMS = false; + USE_CHAT = true; + } + } + } + + public static void sendActionBar(Player player, String message) { + if (USE_CHAT) { + player.sendMessage(message); + } else if (USE_1_11_API) { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + } else if (USE_1_7_NMS || USE_1_8_NMS) { + try { + Object objectCraftPlayer = classCraftPlayer.cast(player); + Object objectPacketChat; + Object objectChatComponent; + if (USE_1_7_NMS) { + objectChatComponent = classIChatBaseComponent.cast(methodSerializeMessage.invoke(classChatSerializer, "{\"text\": \"" + message + "\"}")); + } else { + objectChatComponent = classChatComponentText.getConstructor(String.class).newInstance(message); + } + objectPacketChat = classPacketPlayOutChat.getConstructor(classIChatBaseComponent, byte.class).newInstance(objectChatComponent, (byte) 2); + if (methodGetHandle == null) + methodGetHandle = classCraftPlayer.getDeclaredMethod("getHandle"); + Object objectEntityPlayer = methodGetHandle.invoke(objectCraftPlayer); + if (fieldPlayerConnection == null) + fieldPlayerConnection = objectEntityPlayer.getClass().getDeclaredField("playerConnection"); + Object objectPlayerConnection = fieldPlayerConnection.get(objectEntityPlayer); + if (methodSendPacket == null) + methodSendPacket = objectPlayerConnection.getClass().getDeclaredMethod("sendPacket", classPacket); + methodSendPacket.invoke(objectPlayerConnection, objectPacketChat); + } catch (ReflectiveOperationException e) { // Reflection exception caught -> dont retry, switch to backend + e.printStackTrace(); + USE_1_7_NMS = false; + USE_1_8_NMS = false; + USE_CHAT = true; + player.sendMessage(message); + } catch (Exception e) { // send message to chat instead, retry actionbar later + e.printStackTrace(); + player.sendMessage(message); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/ldcr/BedwarsXP/utils/BedwarsGameUtils.java b/src/main/java/ldcr/BedwarsXP/utils/BedwarsGameUtils.java new file mode 100644 index 0000000..5ecbed4 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/BedwarsGameUtils.java @@ -0,0 +1,37 @@ +package ldcr.BedwarsXP.utils; + +import io.github.bedwarsrel.BedwarsRel; +import io.github.bedwarsrel.game.Game; +import io.github.bedwarsrel.game.GameState; +import ldcr.BedwarsXP.XPShop.ShopReplacer; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; + +public class BedwarsGameUtils { + public static boolean isGameExists(String bw) { + return BedwarsRel.getInstance().getGameManager().getGame(bw) != null; + } + + public static boolean isGameRunning(String bw) { + return BedwarsRel.getInstance().getGameManager().getGame(bw).getState().equals(GameState.RUNNING); + } + + public static boolean isAnyBedwarsRunning() { + ArrayList bw = BedwarsRel.getInstance().getGameManager().getGames(); + for (Game game : bw) { + if (game.getState().equals(GameState.RUNNING)) + return true; + } + return false; + } + + public static void replaceAllShop(CommandSender sender) { + ArrayList bw = BedwarsRel.getInstance().getGameManager().getGames(); + for (Game game : bw) { + if (game.getState().equals(GameState.RUNNING)) { + ShopReplacer.replaceShop(game.getName(), sender); + } + } + } +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/ReflectionUtils.java b/src/main/java/ldcr/BedwarsXP/utils/ReflectionUtils.java new file mode 100644 index 0000000..09fa5a6 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/ReflectionUtils.java @@ -0,0 +1,30 @@ +package ldcr.BedwarsXP.utils; + +import java.lang.reflect.Field; + +public class ReflectionUtils { + private ReflectionUtils() { + } + + public static void setPrivateValue(T r, String f, R value) throws ReflectiveOperationException { + Class clazz = r.getClass(); + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + field.set(r, value); + field.setAccessible(false); + } + + public static Field getField(T r, String f) throws ReflectiveOperationException { + Class clazz = r.getClass(); + return clazz.getDeclaredField(f); + } + + public static boolean isClassFound(String className) { + try { + Class.forName(className); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/ResourceUtils.java b/src/main/java/ldcr/BedwarsXP/utils/ResourceUtils.java new file mode 100644 index 0000000..b50b393 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/ResourceUtils.java @@ -0,0 +1,19 @@ +package ldcr.BedwarsXP.utils; + +import ldcr.BedwarsXP.Config; +import org.bukkit.inventory.ItemStack; + +public class ResourceUtils { + public static Integer convertResToXP(ItemStack stack) { + if (stack == null) + return null; + if (Config.resources.containsKey(stack.getType())) + return Config.resources.get(stack.getType()) * stack.getAmount(); + return null; + } + + public static int convertResToXPExact(ItemStack item) { + Integer result = convertResToXP(item); + return result == null ? 0 : result; + } +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/SendMessageUtils.java b/src/main/java/ldcr/BedwarsXP/utils/SendMessageUtils.java new file mode 100644 index 0000000..4f1db4f --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/SendMessageUtils.java @@ -0,0 +1,9 @@ +package ldcr.BedwarsXP.utils; + +import org.bukkit.command.CommandSender; + +public class SendMessageUtils { + public static void sendMessage(CommandSender sender, String... message) { + sender.sendMessage(message); + } +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/SoundMachine.java b/src/main/java/ldcr/BedwarsXP/utils/SoundMachine.java new file mode 100644 index 0000000..00f7e3c --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/SoundMachine.java @@ -0,0 +1,21 @@ +package ldcr.BedwarsXP.utils; + +import org.bukkit.Sound; + +public class SoundMachine { + public static Sound get(String v18, String v19) { + try { + // Try get old sound name + return Sound.valueOf(v18); + } catch (IllegalArgumentException e1) { + try { + // Try get new sound name + return Sound.valueOf(v19); + } catch (IllegalArgumentException e2) { + // not found + return null; + } + } + } + +} diff --git a/src/main/java/ldcr/BedwarsXP/utils/YamlUtils.java b/src/main/java/ldcr/BedwarsXP/utils/YamlUtils.java new file mode 100644 index 0000000..5c801e7 --- /dev/null +++ b/src/main/java/ldcr/BedwarsXP/utils/YamlUtils.java @@ -0,0 +1,17 @@ +package ldcr.BedwarsXP.utils; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class YamlUtils { + public static YamlConfiguration loadYamlUTF8(File file) throws IOException { + String yaml = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + StringReader sr = new StringReader(yaml); + return YamlConfiguration.loadConfiguration(sr); + } +} diff --git a/src/main/java/sakura/kooi/utils/GithubUpdateChecker/UpdateChecker.java b/src/main/java/sakura/kooi/utils/GithubUpdateChecker/UpdateChecker.java new file mode 100644 index 0000000..61efb8d --- /dev/null +++ b/src/main/java/sakura/kooi/utils/GithubUpdateChecker/UpdateChecker.java @@ -0,0 +1,44 @@ +package sakura.kooi.utils.GithubUpdateChecker; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.AllArgsConstructor; +import lombok.Cleanup; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.function.Consumer; + +@AllArgsConstructor +public class UpdateChecker { + private String user; + private String repo; + private String currentVersion; + private Consumer callback; + + public void check() throws IOException { + URL url = new URL("https://api.github.com/repos/" + user + "/" + repo + "/releases/latest"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + @Cleanup BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + StringBuilder result = new StringBuilder(); + while ((line = rd.readLine()) != null) { + result.append(line); + } + JsonParser parser = new JsonParser(); + + JsonObject json = parser.parse(result.toString()).getAsJsonObject(); + if (json.has("tag_name") && json.has("html_url")) { + String tag = json.get("tag_name").getAsString(); + String link = json.get("html_url").getAsString(); + + if (currentVersion.compareTo(tag) < 0) { + callback.accept(link); + } + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..342db74 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,62 @@ +############################### +# BedwarsXP 经验起床插件 # +# By.SakuraKooi # +############################### + +# 关闭更新检查 +Disable_UpdateChecker: false + +#设置资源可兑换为的XP数 +XP: + # 铜可兑换为的XP + bronze: 1 + # 铁可兑换为的XP + iron: 5 + # 金可兑换为的XP + gold: 10 + # 如有其他自行添加的资源也可以在此处按格式添加 + #custom: 233 +# 请务必保证起床插件中配置的资源在此处都有对应经验值 + +#获得XP的提示 +# 使用%xp%代替获得的XP +# 插件会尝试使用ActionBar来发送,如发送失败(版本不匹配等原因)会使用聊天发送 +# 更改为空('')来禁用发送信息 +Message: '&a你获得了 &6&l%xp% &a点经验' + +#是否在商店新增将经验重新兑换回铜铁金的菜单 +Add_Res_Shop: true + +#玩家死亡扣除经验 +# 扣除的经验 = 玩家拥有经验 x (DeathCostXP)% +# 请填写0-100 (百分比) +# 设置为0关闭此功能 +# 固定扣除功能已被移除 +DeathCostXP: 0 + +#玩家死亡掉落经验 +# 开启此项后玩家死亡扣除的经验将掉在地上, 可以被其他玩家捡起 +# 掉落的经验 = 玩家拥有经验 x (DeathCostXP)% x (DeathDropXP)% +# 请填写0-100 (百分比) +# 设置为0关闭此功能 +DeathDropXP: 100 + +#死亡直接把经验添加给击杀者而不是掉落经验瓶 +DontDropExpBottle: false + +#玩家可携带的最大经验 +# 超过该经验将无法捡取 +# 设置为0关闭此功能 +MaxXP: 0 +MaxXPMessage: '&4你携带的经验已达上限' + +#完全经验起床模式 +# 设置为false : 玩家捡取资源获得经验,在商店将经验重新兑换回铜铁金购买东西 +# 设置为true : 玩家捡取资源获得经验,并可以在商店直接使用经验购买东西 +Full_XP_Bedwars: true + +#配置文件版本 +# 请务必不要动这个 +# 乱动此项导致配置文件被重置概不负责→_→ +ConfigVersion: 2 + diff --git a/src/main/resources/enabledGames.yml b/src/main/resources/enabledGames.yml new file mode 100644 index 0000000..b24ba24 --- /dev/null +++ b/src/main/resources/enabledGames.yml @@ -0,0 +1 @@ +enabledGame: diff --git a/src/main/resources/language-en.yml b/src/main/resources/language-en.yml new file mode 100644 index 0000000..aa3d650 --- /dev/null +++ b/src/main/resources/language-en.yml @@ -0,0 +1,81 @@ +# Wanna English? Just overwrites language.yml with this file :) + +# You should replace single quote(') with two single quote('') +# eg: player's -> player''s + +# Loading configuration +LOADING_CONFIGURATION: 'Loading configuration...' +CONFIGURATION_FILE_NOT_EXISTS: 'Configuration file not exists, create it...' +OLD_VERSION_CONFIGURATION: 'Your configuration is outdated and cannot be load!' +OLD_CONFIGURATION_BACKUPED: 'Your configuration has been renamed to [config.bak.yml], creating new one...' +NEW_CONFIGURATION_SAVED: 'New configuration is created, continue...' + +RESOURCE_SHOP_ENABLED: 'Enabled XP-Resource shop' +DEATH_COST_XP_PERCENT: 'Death will take %percent%% XP' +DEATH_DROP_XP_DISABLED: 'Disabled Death-drop-XP' +DEATH_DROP_XP_PERCEMT: 'Death will drop %percent%% XP' +DEATH_DROP_EXP_BOTTLE_DISABLED: 'Disabled Death-drop-ExpBottle (Directly add to killer instead)' +MAX_XP_LIMIT_DISABLED: 'Disabled Max-XP-limit' +MAX_XP_LIMIT_ENABLED: 'Max XP limit is %value%' +ALL_TRADES_USE_XP_ENABLED: 'Enabled All-trade-use-XP' + +LOADING_RESOURCES_VALUE: 'Loading resource data...' +FOUNDED_RESOURCE: 'Resource %resource% (%material%) = %value%XP' + +WARN_YOU_NEEDS_ENABLE_BEDWARSXP_MANUALLY: 'You need to use [/bwxp enable] to enable XP mode for a game!' + +# Enabling plugin +FINDING_BEDWARSREL: 'Finding BedwarsRel...' +BEDWARSREL_NOT_SUPPORTED: 'Sorry, Your BedwarsRel is not supported!' +PLEASE_UPDATE_BEDWARSREL: 'Please update your BedwarsRel.' +BEDWARSREL_SUPPORTED: 'Supported BedwarsRel found!' +BEDWARSREL_NOT_FOUND: 'BedwarsRel not found!' + +ERROR_OCCURED_WHILE_LOADING: 'An error occurred while loading BedwarsXP' +REPORT_ISSUE_HERE: 'Report it at here' + +SUCCESSFULLY_LOADED: 'BedwarsXP has been successfully loaded!' +REPORT_ISSUE_AND_SUGGESTION_HERE: 'BUG Report | Suggestions' + +# Commands +YOU_DONT_HAVE_PERMISSION_TO_EXECUTE_THIS_COMMAND: 'You dont have permission to execute this command' +UNEXCEPTED_EXCEPTION_CAUGHT: 'An unexpected error caught while executing the command.' +ERROR_OCCURED: 'Sorry, An error occurred.' + +# Main command +HELP_MAIN_RELOAD: '/bwxp reload Reload BedwarsXP' +HELP_MAIN_ENABLE: '/bwxp enable Enable XP mode' +HELP_MAIN_DISABLE: '/bwxp disable Disable XP mode' + +SUCCESSFULLY_RELOADED: 'BedwarsXP reloaded~' +RELOAD_GAME_RUNNING: 'Some map is running!' +UPDATE_RUNNING_GAME: 'Updating running games...' + +ERROR_GAME_NOT_FOUND: 'Sorry, Map %game% cannot be found' +GAME_XP_ENABLED: 'Enabled XP mode for map %game% !' +GAME_XP_DISABLED: 'Disabled XP mode for map %game% !' +GAME_IS_RUNNING_RESTART_REQUIRED: 'Map %game% is running, please restart it manually!' + +# Edit XP command +HELP_EDITXP: '/editxp Modify a player''s XP' +EDITXP_PLAYER_NOT_IN_GAME: 'Player %player% is not in game!' +EDITXP_GAME_IS_NOT_XP_MODE: 'The map that %player% playing is not in XP mode!' +EDITXP_XP_IS_NOT_A_NUMBER: 'The XP you typed is not a valid number!' +EDITXP_XP_HAS_BEEN_SET_TO: 'Player %player%''s XP has been set to %xp%' + +# Shop replacer +ERROR_OCCURED_REPLACE_SHOP: 'Failed to replace shop for map %game%' +SUCCESSFULLY_REPLACED_SHOP: 'Successfully replaced shop for map %game%!' +ERROR_OCCURED_WHILE_INITALIZING_XP_SHOP: 'An error occurred while initializing XP shop for map %game%' + +SHOP_XP_EXCHANGE_TITLE: '§6§lXP Shop' +SHOP_XP_EXCHANGE_LORE: '§aBuy resource with XP' + +SHOP_TRADE_XP: '§a%xp% XP' + +# Update checker +HAS_UPDATE: 'Your BedwarsXP is outdated! Update now -> %link%' + +# Others +ERROR_UNSUPPORTED_VERSION_ACTIONBAR_MAY_NOT_WORK: 'Unsupported server version, ActionBar may not works' +ERROR_OCCURRED_WHILE_LOADING: 'An unexpected error occurred while loading plugin' \ No newline at end of file diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml new file mode 100644 index 0000000..3f2805b --- /dev/null +++ b/src/main/resources/language.yml @@ -0,0 +1,77 @@ +# Loading configuration + +LOADING_CONFIGURATION: '正在加载配置文件...' +CONFIGURATION_FILE_NOT_EXISTS: '配置文件不存在, 正在创建...' +OLD_VERSION_CONFIGURATION: '您的配置文件版本过老无法加载' +OLD_CONFIGURATION_BACKUPED: '已备份您的配置文件为 [config.bak.yml] ,开始初始化新版本配置文件' +NEW_CONFIGURATION_SAVED: '配置文件初始化完成, 继续加载配置...' + +RESOURCE_SHOP_ENABLED: '已启用经验兑换资源商店' +DEATH_COST_XP_PERCENT: '死亡扣除 %percent%% 经验' +DEATH_DROP_XP_DISABLED: '死亡掉落经验已关闭' +DEATH_DROP_XP_PERCEMT: '死亡掉落经验占扣除经验 %percent%%' +DEATH_DROP_EXP_BOTTLE_DISABLED: '死亡掉落经验瓶已关闭 (直接添加给击杀者)' +MAX_XP_LIMIT_DISABLED: '最大经验限制已关闭' +MAX_XP_LIMIT_ENABLED: '最大经验限制设置为 %value%' +ALL_TRADES_USE_XP_ENABLED: '完全经验起床模式已启动' + +LOADING_RESOURCES_VALUE: '开始加载资源价值数据' +FOUNDED_RESOURCE: '发现资源 %resource% 物品:%material% 价值 %value%' + +WARN_YOU_NEEDS_ENABLE_BEDWARSXP_MANUALLY: '注意,在新版本中你需要手动使用/bwxp enable来启用游戏的经验起床模式' + +# Enabling plugin +FINDING_BEDWARSREL: '正在寻找BedwarsRel插件...' +BEDWARSREL_NOT_SUPPORTED: '抱歉, BedwarsXP不再支持旧版BedwarsRel!' +PLEASE_UPDATE_BEDWARSREL: '请更新你的BedwarsRel至1.3.6以上版本.' +BEDWARSREL_SUPPORTED: '已发现受支持的BedwarsRel插件!' +BEDWARSREL_NOT_FOUND: '没有找到支持的BedwarsRel! 你可能没有安装或使用了不受支持的版本!' + +ERROR_OCCURED_WHILE_LOADING: 'BedwarsXP加载出错' +REPORT_ISSUE_HERE: '请前往此处反馈' + +SUCCESSFULLY_LOADED: '成功加载BedwarsXP经验起床插件' +REPORT_ISSUE_AND_SUGGESTION_HERE: 'BUG反馈 | 提交建议' + +# Commands +YOU_DONT_HAVE_PERMISSION_TO_EXECUTE_THIS_COMMAND: '你没有权限执行此命令' +UNEXCEPTED_EXCEPTION_CAUGHT: '在执行您的命令时出现了异常错误' +ERROR_OCCURED: '错误: 在执行您的操作时发生了错误' + +# Main command +HELP_MAIN_RELOAD: '/bwxp reload 重载插件配置' +HELP_MAIN_ENABLE: '/bwxp enable <游戏> 激活指定游戏的经验起床模式' +HELP_MAIN_DISABLE: '/bwxp disable <游戏> 禁用指定游戏的经验起床模式' + +SUCCESSFULLY_RELOADED: '成功重载配置文件~' +RELOAD_GAME_RUNNING: '当前有游戏正在运行' +UPDATE_RUNNING_GAME: '开始更新运行中的游戏的经验商店' + +ERROR_GAME_NOT_FOUND: '错误: 找不到游戏 %game%' +GAME_XP_ENABLED: '成功激活了游戏 %game% 的经验起床模式' +GAME_XP_DISABLED: '成功禁用了游戏 %game% 的经验起床模式' +GAME_IS_RUNNING_RESTART_REQUIRED: '游戏 %game% 正在运行,请手动重启游戏' + +# Edit XP command +HELP_EDITXP: '/editxp <玩家> <经验值> 设置其他玩家的经验值' +EDITXP_PLAYER_NOT_IN_GAME: '玩家 %player% 不在游戏中!' +EDITXP_GAME_IS_NOT_XP_MODE: '玩家 %player% 所在的游戏没有开启经验起床模式!' +EDITXP_XP_IS_NOT_A_NUMBER: '输入经验值的不是一个有效数字!' +EDITXP_XP_HAS_BEEN_SET_TO: '玩家 %player% 的经验值已被设置为 %xp%' + +# Shop replacer +ERROR_OCCURED_REPLACE_SHOP: '为地图 %game% 替换原始商店为经验商店失败' +SUCCESSFULLY_REPLACED_SHOP: '为地图 %game% 替换经验商店成功!' +ERROR_OCCURED_WHILE_INITALIZING_XP_SHOP: '为地图 %game% 初始化经验商店时出错' + +SHOP_XP_EXCHANGE_TITLE: '§6§l经验兑换资源' +SHOP_XP_EXCHANGE_LORE: '§a将你的经验兑换成物品' + +SHOP_TRADE_XP: '§a%xp% 经验' + +# Update checker +HAS_UPDATE: 'BedwarsXP 发布了新版本! 下载 -> %link%' + +# Others +ERROR_UNSUPPORTED_VERSION_ACTIONBAR_MAY_NOT_WORK: '解析服务端版本失败, ActionBar提示可能出错' +ERROR_OCCURRED_WHILE_LOADING: '加载插件时发生了异常错误' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..5e76793 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,18 @@ +name: BedwarsXP +main: ldcr.BedwarsXP.BedwarsXP +version: 2.1.3 +author: SakuraKooi +depend: [BedwarsRel] +commands: + bedwarsxp: + description: 经验起床 + usage: /bedwarsxp + aliases: [bwxp] + bedwarsxpedit: + description: 修改玩家经验值 + usage: /bedwarsxpedit + aliases: [editxp] +permissions: + bedwarsxp.admin: + description: 经验起床相关管理权限. + default: op