commit 536c02b57e334adc5f8f7ec1574d6a65b06b577c Author: Coldsmile_7 Date: Thu Oct 1 14:48:33 2026 +0800 feat: initial CMagicGem reimplementation diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a87d264 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..834050e --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ + +# Purchased reference plugin and local inspection output +reference/ + +# Local Paper integration-test server +test-server/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..62d856a --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# CMagicGem + +CMagicGem 是 MagicGem 的 Paper 1.21.x 重实现,目标运行范围为 Minecraft/Paper 1.21–1.21.11。项目使用 Java 21 和 Paper API,不包含原插件的网络授权逻辑。 + +## 安装 + +1. 使用 Java 21 启动 Paper 1.21.x。 +2. 将 `CMagicGem-*.jar` 放入服务器的 `plugins` 目录。 +3. 保留旧插件数据时,不要删除 `plugins/MagicGem`。CMagicGem 首次启动会把尚不存在的配置安全复制到 `plugins/CMagicGem`,不会修改旧目录。 +4. 移除旧 MagicGem JAR 后启动服务器,并检查控制台中的配置加载结果。 + +主命令是 `/cmgem`,别名是 `/cmg`。插件不会注册 `/mgem`,因此可与旧插件并存进行迁移核验。 + +## 命令 + +- `/cmgem reload [gem|skill]` +- `/cmgem give <玩家> <宝石> [数量]` +- `/cmgem apply <玩家> <宝石> [槽位]` +- `/cmgem remove <玩家> <宝石> [槽位]` +- `/cmgem get`、`/cmgem view`、`/cmgem embed [界面]` +- `/cmgem signal <玩家|@all> <信号>` +- `/cmgem material|lore|enchant|nbt` + +## 可选兼容 + +按需安装 PlaceholderAPI、Vault、PlayerPoints、MythicMobs 4/5、MMOItems、MythicArtifacts/MythicCrucible、Residence、PvPManager、EcoEnchants 或 GoldenEnchants。所有集成都采用软依赖;缺少对应插件时,只有相关条件或奖励不可用。 + +## 构建 + +```powershell +mvn clean package +``` + +产物位于 `target/cmagicgem-0.1.0-SNAPSHOT.jar`。 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e4152ba --- /dev/null +++ b/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + cn.infstar + cmagicgem + 0.1.0-SNAPSHOT + jar + + cmagicgem + + + 21 + 1.21-R0.1-SNAPSHOT + 2.16.1 + 5.14.0 + UTF-8 + + + + clean package + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.3 + + + package + + shade + + + + + false + + + de.tr7zw.changeme.nbtapi + cn.infstar.cmagicgem.libs.nbtapi + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + + + + + src/main/resources + true + + + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + codemc + https://repo.codemc.io/repository/maven-public/ + + + lumine + https://mvn.lumine.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + ${paper.version} + provided + + + de.tr7zw + item-nbt-api + ${nbt-api.version} + + + io.lumine + Mythic-Dist + 5.13.0 + provided + true + + + io.lumine.xikage + MythicMobs + 4.11.0-BETA + provided + true + + + * + * + + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + diff --git a/src/main/java/cn/infstar/cMagicGem/CMagicGem.java b/src/main/java/cn/infstar/cMagicGem/CMagicGem.java new file mode 100644 index 0000000..4165494 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/CMagicGem.java @@ -0,0 +1,139 @@ +package cn.infstar.cmagicgem; + +import cn.infstar.cmagicgem.api.event.CMagicReloadEvent; +import cn.infstar.cmagicgem.command.CMagicGemCommand; +import cn.infstar.cmagicgem.condition.ConditionEngine; +import cn.infstar.cmagicgem.config.PluginSettings; +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.expression.Expression; +import cn.infstar.cmagicgem.expression.DynamicExpression; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.gem.GemService; +import cn.infstar.cmagicgem.gui.GemBrowser; +import cn.infstar.cmagicgem.gui.GemGuiManager; +import cn.infstar.cmagicgem.integration.EconomyBridge; +import cn.infstar.cmagicgem.integration.MythicBridge; +import cn.infstar.cmagicgem.integration.PlaceholderBridge; +import cn.infstar.cmagicgem.integration.PlayerPointsBridge; +import cn.infstar.cmagicgem.integration.ProtectionBridge; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.item.ItemMatcher; +import cn.infstar.cmagicgem.listener.GemUseListener; +import cn.infstar.cmagicgem.migration.LegacyDirectoryImporter; +import cn.infstar.cmagicgem.reward.RewardEngine; +import cn.infstar.cmagicgem.skill.SkillRegistry; +import cn.infstar.cmagicgem.skill.SkillEngine; +import cn.infstar.cmagicgem.util.MessageDispatcher; +import de.tr7zw.changeme.nbtapi.NBT; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.Objects; + +public final class CMagicGem extends JavaPlugin { + private volatile PluginSettings settings; + private GemRegistry gemRegistry; + private SkillRegistry skillRegistry; + private GemGuiManager guiManager; + private LanguageManager languages; + private EconomyBridge economy; + private PlayerPointsBridge points; + private PlaceholderBridge placeholders; + private MythicBridge mythic; + + @Override + public void onLoad() { + NBT.preloadApi(); + } + + @Override + public void onEnable() { + new LegacyDirectoryImporter(this).importIfPresent(); + saveDefaultConfig(); + for (String resource : BUNDLED_RESOURCES) saveBundledResource(resource); + languages = new LanguageManager(this); + languages.reload(); + DirectiveParser directiveParser = new DirectiveParser(); + Expression expression = new Expression(); + placeholders = new PlaceholderBridge(this); + DynamicExpression dynamicExpression = new DynamicExpression(expression, placeholders); + economy = new EconomyBridge(this); + points = new PlayerPointsBridge(this); + mythic = new MythicBridge(this); + ProtectionBridge protection = new ProtectionBridge(this); + gemRegistry = new GemRegistry(this, directiveParser); + skillRegistry = new SkillRegistry(this, directiveParser); + ItemCodec itemCodec = new ItemCodec(this, gemRegistry); + ItemMatcher itemMatcher = new ItemMatcher(itemCodec); + MessageDispatcher messages = new MessageDispatcher(this, itemCodec, directiveParser, placeholders, languages); + ConditionEngine conditionEngine = new ConditionEngine(dynamicExpression, itemMatcher, languages); + RewardEngine rewardEngine = new RewardEngine(this, itemCodec, itemMatcher, gemRegistry, skillRegistry, directiveParser, + dynamicExpression, placeholders, economy, points, mythic); + GemService gemService = new GemService(gemRegistry, itemCodec, itemMatcher, rewardEngine, + dynamicExpression, conditionEngine, languages); + SkillEngine skillEngine = new SkillEngine(this, skillRegistry, itemCodec, directiveParser, + dynamicExpression, conditionEngine, rewardEngine, mythic, protection, messages); + mythic.enableExtensions(gemRegistry, itemCodec, protection, skillEngine::mythicKill); + rewardEngine.skillInvoker(skillEngine::invoke); + GemBrowser gemBrowser = new GemBrowser(gemRegistry, itemCodec, languages); + guiManager = new GemGuiManager(this, gemRegistry, gemService, itemCodec, itemMatcher, messages, languages); + reloadAll(); + + CMagicGemCommand command = new CMagicGemCommand(gemRegistry, gemService, itemCodec, gemBrowser, + guiManager, skillEngine, messages, languages, this::reloadScope); + PluginCommand pluginCommand = Objects.requireNonNull(getCommand("cmgem")); + pluginCommand.setExecutor(command); + pluginCommand.setTabCompleter(command); + getServer().getPluginManager().registerEvents(new GemUseListener(gemService, itemCodec, messages, () -> settings), this); + getServer().getPluginManager().registerEvents(gemBrowser, this); + getServer().getPluginManager().registerEvents(guiManager, this); + getServer().getPluginManager().registerEvents(skillEngine, this); + getLogger().info("Integrations: Vault=" + economy.status() + + ", PlayerPoints=" + points.status() + + ", PlaceholderAPI=" + placeholders.status() + + ", MythicMobs=" + mythic.status()); + getLogger().info("CMagicGem 已启用,命令 /cmgem,加载 " + gemRegistry.all().size() + " 颗宝石。"); + } + + public void reloadAll() { + reloadScope("all"); + } + + private void reloadScope(String scope) { + boolean all = scope.equalsIgnoreCase("all"); + boolean gems = all || scope.equalsIgnoreCase("gem"); + boolean skills = all || scope.equalsIgnoreCase("skill"); + if (all) { + reloadConfig(); + if (languages != null) languages.reload(); + settings = PluginSettings.load(getConfig()); + if (economy != null) economy.refresh(); + if (points != null) points.refresh(); + if (placeholders != null) placeholders.refresh(); + if (mythic != null) mythic.refresh(); + } + if (gems && gemRegistry != null) gemRegistry.reload(); + if (skills && skillRegistry != null) { + if (settings.skillEngine()) skillRegistry.reload(); + else skillRegistry.clear(); + } + if ((all || gems) && guiManager != null) guiManager.reload(); + if (isEnabled()) getServer().getPluginManager().callEvent(new CMagicReloadEvent()); + } + + private void saveBundledResource(String path) { + File file = new File(getDataFolder(), path); + if (!file.exists()) saveResource(path, false); + } + + private static final String[] BUNDLED_RESOURCES = { + "gems/ExampleGem.yml", "gems/AttributeGem.yml", "gems/ButtonGem.yml", "gems/EmbedGem.yml", + "gems/EnchantGem.yml", "gems/FunctionGem.yml", "gems/LoreGem.yml", "gems/MythicGem.yml", + "gems/PlayerGem.yml", "gems/PotionGem.yml", + "skills/ExampleSkills.yml", "skills/DefaultBuffs.yml", "skills/DefaultMMSkills.yml", + "skills/DefaultSkills.yml", "gui/Default.yml", "gui/gui.yml", "gui/rune.yml", + "languages/en_US.yml", "languages/zh_CN.yml" + }; +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicEquipmentChangeEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicEquipmentChangeEvent.java new file mode 100644 index 0000000..f8245e2 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicEquipmentChangeEvent.java @@ -0,0 +1,36 @@ +package cn.infstar.cmagicgem.api.event; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class CMagicEquipmentChangeEvent extends PlayerEvent implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final String slot; + private final ItemStack oldItem; + private final ItemStack newItem; + private final Cause cause; + private boolean cancelled; + + public CMagicEquipmentChangeEvent(Player player, String slot, ItemStack oldItem, ItemStack newItem, Cause cause) { + super(player); + this.slot = slot; + this.oldItem = oldItem == null ? null : oldItem.clone(); + this.newItem = newItem == null ? null : newItem.clone(); + this.cause = cause; + } + + public String getSlot() { return slot; } + public ItemStack getOldItem() { return oldItem == null ? null : oldItem.clone(); } + public ItemStack getNewItem() { return newItem == null ? null : newItem.clone(); } + public Cause getCause() { return cause; } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } + + public enum Cause { ARMOR, HELD, SWAP, INVENTORY, BREAK, DEATH, CUSTOM } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicGemUseEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicGemUseEvent.java new file mode 100644 index 0000000..f08dd54 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicGemUseEvent.java @@ -0,0 +1,28 @@ +package cn.infstar.cmagicgem.api.event; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; + +public final class CMagicGemUseEvent extends PlayerEvent implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final GemDefinition gem; + private final boolean use; + private boolean cancelled; + + public CMagicGemUseEvent(Player player, GemDefinition gem, boolean use) { + super(player); + this.gem = gem; + this.use = use; + } + + public GemDefinition getGem() { return gem; } + public boolean isUse() { return use; } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicJumpEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicJumpEvent.java new file mode 100644 index 0000000..187bf3a --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicJumpEvent.java @@ -0,0 +1,30 @@ +package cn.infstar.cmagicgem.api.event; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.jetbrains.annotations.NotNull; + +public final class CMagicJumpEvent extends PlayerEvent implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private Location from; + private Location to; + private boolean cancelled; + + public CMagicJumpEvent(Player player, Location from, Location to) { + super(player); + this.from = from.clone(); + this.to = to.clone(); + } + + public Location getFrom() { return from.clone(); } + public void setFrom(Location from) { this.from = from.clone(); } + public Location getTo() { return to.clone(); } + public void setTo(Location to) { this.to = to.clone(); } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicPotionBuffEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicPotionBuffEvent.java new file mode 100644 index 0000000..1c56530 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicPotionBuffEvent.java @@ -0,0 +1,38 @@ +package cn.infstar.cmagicgem.api.event; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; + +public final class CMagicPotionBuffEvent extends PlayerEvent implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final LivingEntity target; + private final PotionEffectType type; + private int amplifier; + private int durationTicks; + private boolean cancelled; + + public CMagicPotionBuffEvent(Player player, LivingEntity target, PotionEffectType type, + int amplifier, int durationTicks) { + super(player); + this.target = target; + this.type = type; + this.amplifier = amplifier; + this.durationTicks = durationTicks; + } + + public LivingEntity getTarget() { return target; } + public PotionEffectType getType() { return type; } + public int getAmplifier() { return amplifier; } + public void setAmplifier(int amplifier) { this.amplifier = Math.max(0, amplifier); } + public int getDurationTicks() { return durationTicks; } + public void setDurationTicks(int durationTicks) { this.durationTicks = Math.max(1, durationTicks); } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicReloadEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicReloadEvent.java new file mode 100644 index 0000000..0b2cfa4 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicReloadEvent.java @@ -0,0 +1,16 @@ +package cn.infstar.cmagicgem.api.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class CMagicReloadEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + + public CMagicReloadEvent() { + super(false); + } + + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillLoadEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillLoadEvent.java new file mode 100644 index 0000000..ebe9b3b --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillLoadEvent.java @@ -0,0 +1,26 @@ +package cn.infstar.cmagicgem.api.event; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public final class CMagicSkillLoadEvent extends Event implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final String name; + private final ConfigurationSection config; + private boolean cancelled; + + public CMagicSkillLoadEvent(String name, ConfigurationSection config) { + this.name = name; + this.config = config; + } + + public String getName() { return name; } + public ConfigurationSection getConfig() { return config; } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillUseEvent.java b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillUseEvent.java new file mode 100644 index 0000000..b4c301d --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/api/event/CMagicSkillUseEvent.java @@ -0,0 +1,29 @@ +package cn.infstar.cmagicgem.api.event; + +import cn.infstar.cmagicgem.skill.SkillDefinition; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public final class CMagicSkillUseEvent extends PlayerEvent implements Cancellable { + private static final HandlerList HANDLERS = new HandlerList(); + private final SkillDefinition skill; + private final ItemStack item; + private boolean cancelled; + + public CMagicSkillUseEvent(Player player, SkillDefinition skill, ItemStack item) { + super(player); + this.skill = skill; + this.item = item; + } + + public SkillDefinition getSkill() { return skill; } + public ItemStack getItem() { return item; } + @Override public boolean isCancelled() { return cancelled; } + @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + @Override public @NotNull HandlerList getHandlers() { return HANDLERS; } + public static HandlerList getHandlerList() { return HANDLERS; } +} diff --git a/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java b/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java new file mode 100644 index 0000000..87a6fc4 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/command/CMagicGemCommand.java @@ -0,0 +1,401 @@ +package cn.infstar.cmagicgem.command; + +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.gem.GemService; +import cn.infstar.cmagicgem.gem.GemUseResult; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.gui.GemBrowser; +import cn.infstar.cmagicgem.gui.GemGuiManager; +import cn.infstar.cmagicgem.skill.SkillEngine; +import cn.infstar.cmagicgem.util.MessageDispatcher; +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.NBTType; +import de.tr7zw.changeme.nbtapi.iface.ReadableNBT; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; + +public final class CMagicGemCommand implements CommandExecutor, TabCompleter { + private final GemRegistry registry; + private final GemService gemService; + private final ItemCodec itemCodec; + private final Consumer reload; + private final GemBrowser browser; + private final SkillEngine skillEngine; + private final GemGuiManager guiManager; + private final MessageDispatcher messages; + private final LanguageManager languages; + + public CMagicGemCommand(GemRegistry registry, GemService gemService, ItemCodec itemCodec, GemBrowser browser, + GemGuiManager guiManager, SkillEngine skillEngine, MessageDispatcher messages, + LanguageManager languages, Consumer reload) { + this.registry = registry; + this.gemService = gemService; + this.itemCodec = itemCodec; + this.reload = reload; + this.browser = browser; + this.skillEngine = skillEngine; + this.guiManager = guiManager; + this.messages = messages; + this.languages = languages; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0 || args[0].equalsIgnoreCase("help")) return help(sender); + String subcommand = args[0].toLowerCase(Locale.ROOT); + try { + return switch (subcommand) { + case "reload" -> reload(sender, args); + case "give" -> give(sender, args); + case "apply" -> apply(sender, args); + case "remove" -> remove(sender, args); + case "list" -> list(sender); + case "get" -> browse(sender, true); + case "view" -> browse(sender, false); + case "material" -> inspectMaterial(sender); + case "lore" -> inspectLore(sender); + case "enchant" -> inspectEnchant(sender); + case "nbt" -> inspectNbt(sender, args); + case "signal" -> signal(sender, args); + case "embed" -> embed(sender, args); + case "addblock" -> addBlock(sender, args); + case "delblock" -> deleteBlock(sender); + default -> help(sender); + }; + } catch (IllegalArgumentException exception) { + sender.sendMessage(itemCodec.color("&c" + exception.getMessage())); + return true; + } + } + + private boolean help(CommandSender sender) { + sender.sendMessage(itemCodec.color("&6CMagicGem &7- &f/cmgem")); + List help = languages.section("command.help"); + if (help.isEmpty()) help = List.of("&e/cmgem give <玩家> <宝石> [数量]", + "&e/cmgem apply <玩家> <宝石> [槽位]", "&e/cmgem remove <玩家> <宝石> [槽位]", + "&e/cmgem list | material | lore | enchant | nbt | reload", + "&e/cmgem embed [界面] | signal <玩家> <信号>"); + help.forEach(line -> sender.sendMessage(itemCodec.color(line))); + return true; + } + + private boolean signal(CommandSender sender, String[] args) { + requireAdmin(sender); + if (args.length < 3) throw new IllegalArgumentException("用法: /cmgem signal <玩家> <信号>"); + String signal = String.join(" ", Arrays.copyOfRange(args, 2, args.length)); + if (args[1].equalsIgnoreCase("@all")) { + Bukkit.getOnlinePlayers().forEach(player -> skillEngine.signal(player, signal)); + sender.sendMessage(itemCodec.color(languages.text("cmagic.command.signal_all", "&a已向所有在线玩家发送技能信号。"))); + return true; + } + Player player = requirePlayer(args[1]); + skillEngine.signal(player, signal); + sender.sendMessage(itemCodec.color(languages.text("cmagic.command.signal_player", "&a已向 {player} 发送技能信号。", + "player", player.getName()))); + return true; + } + + private boolean embed(CommandSender sender, String[] args) { + if (!(sender instanceof Player player)) throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + if (!hasPermission(sender, "embed")) throw new IllegalArgumentException(languages.text("command.no_permission", "&c你没有权限")); + if (args.length >= 2 && args[1].equalsIgnoreCase("addblock")) + return addBlock(sender, Arrays.copyOfRange(args, 1, args.length)); + if (args.length >= 2 && args[1].equalsIgnoreCase("delblock")) return deleteBlock(sender); + String name = args.length < 2 ? null : String.join(" ", Arrays.copyOfRange(args, 1, args.length)); + if (!guiManager.open(player, name)) throw new IllegalArgumentException(languages.text("gem.embed.not_found", "&c未知镶嵌台: {gui}", + "gui", name == null ? "default" : name)); + return true; + } + + private boolean addBlock(CommandSender sender, String[] args) { + requireAdmin(sender); + if (!(sender instanceof Player player)) throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + if (args.length < 2) throw new IllegalArgumentException("用法: /cmgem addblock <界面>"); + var block = player.getLocation().clone().subtract(0, 1, 0).getBlock(); + String name = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); + if (!guiManager.addBlock(name, block.getLocation())) throw new IllegalArgumentException("未知界面或保存失败: " + name); + sender.sendMessage(itemCodec.color(languages.text("command.embed.addblock", "&a已绑定镶嵌台方块。"))); + return true; + } + + private boolean deleteBlock(CommandSender sender) { + requireAdmin(sender); + if (!(sender instanceof Player player)) throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + var block = player.getLocation().clone().subtract(0, 1, 0).getBlock(); + if (!guiManager.deleteBlock(block.getLocation())) throw new IllegalArgumentException("脚下方块未绑定镶嵌台"); + sender.sendMessage(itemCodec.color(languages.text("command.embed.delblock", "&a已解除镶嵌台方块。"))); + return true; + } + + private boolean reload(CommandSender sender, String[] args) { + requireAdmin(sender); + String scope = args.length < 2 ? "all" : args[1].toLowerCase(Locale.ROOT); + if (!Set.of("all", "gem", "skill").contains(scope)) + throw new IllegalArgumentException("用法: /cmgem reload [gem|skill]"); + reload.accept(scope); + String fallback = switch (scope) { + case "gem" -> "&a宝石配置已重载,共加载 {amount} 颗宝石。"; + case "skill" -> "&a技能配置已重载。"; + default -> "&aCMagicGem 已安全重载,共加载 {amount} 颗宝石。"; + }; + sender.sendMessage(itemCodec.color(languages.text("cmagic.command.reload_" + scope, fallback, + "amount", registry.all().size()))); + return true; + } + + private boolean give(CommandSender sender, String[] args) { + requireAdmin(sender); + if (args.length < 3) throw new IllegalArgumentException("用法: /cmgem give <玩家> <宝石> [数量]"); + Player player = requirePlayer(args[1]); + GemDefinition gem = requireGem(joinGemName(args, 2, args.length > 3 && integer(args[args.length - 1]) ? args.length - 1 : args.length)); + int amount = args.length > 3 && integer(args[args.length - 1]) ? Integer.parseInt(args[args.length - 1]) : 1; + gemService.give(player, gem, amount); + sender.sendMessage(itemCodec.color(languages.text("cmagic.command.give", "&a已给予 {player} {amount} 个 {gem}", + "player", player.getName(), "amount", amount, "gem", gem.name()))); + return true; + } + + private boolean apply(CommandSender sender, String[] args) { + requireAdmin(sender); + if (args.length < 3) throw new IllegalArgumentException("用法: /cmgem apply <玩家> <宝石> [槽位]"); + Player player = requirePlayer(args[1]); + String possibleSlot = args[args.length - 1]; + EquipmentSlot slot = args.length > 3 ? parseSlot(possibleSlot) : EquipmentSlot.HAND; + int nameEnd = args.length > 3 && slot != null ? args.length - 1 : args.length; + if (slot == null) slot = EquipmentSlot.HAND; + GemDefinition gem = requireGem(joinGemName(args, 2, nameEnd)); + ItemStack target = getSlot(player.getInventory(), slot); + GemUseResult result = gemService.use(player, gem, target); + if (result.successful() || result.consumed()) setSlot(player.getInventory(), slot, result.target()); + messages.send(sender, result.message()); + return true; + } + + private boolean remove(CommandSender sender, String[] args) { + requireAdmin(sender); + if (args.length < 3) throw new IllegalArgumentException("用法: /cmgem remove <玩家> <宝石> [槽位]"); + Player player = requirePlayer(args[1]); + EquipmentSlot slot = args.length > 3 ? parseSlot(args[args.length - 1]) : EquipmentSlot.HAND; + int nameEnd = args.length > 3 && slot != null ? args.length - 1 : args.length; + if (slot == null) slot = EquipmentSlot.HAND; + String gemName = joinGemName(args, 2, nameEnd); + ItemStack target = getSlot(player.getInventory(), slot); + GemUseResult result = gemService.remove(player, gemName, target); + if (result.successful() || result.target() != target) setSlot(player.getInventory(), slot, result.target()); + messages.send(sender, result.message()); + return true; + } + + private boolean list(CommandSender sender) { + if (!hasPermission(sender, "view")) throw new IllegalArgumentException(languages.text("command.no_permission", "&c你没有权限")); + sender.sendMessage(itemCodec.color(languages.text("cmagic.command.list", "&6已加载宝石 (&e{amount}&6):", + "amount", registry.all().size()))); + sender.sendMessage(itemCodec.color("&f" + String.join("&7, &f", registry.names()))); + return true; + } + + private boolean browse(CommandSender sender, boolean allowTake) { + if (!(sender instanceof Player player)) throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + if (allowTake) requireAdmin(sender); + else if (!hasPermission(sender, "view")) throw new IllegalArgumentException(languages.text("command.no_permission", "&c你没有权限")); + browser.open(player, allowTake, 0); + return true; + } + + private boolean inspectMaterial(CommandSender sender) { + ItemStack item = hand(sender); + sender.sendMessage(itemCodec.color("&eMaterial: &f" + item.getType().name())); + return true; + } + + private boolean inspectLore(CommandSender sender) { + ItemStack item = hand(sender); + if (!item.hasItemMeta() || !item.getItemMeta().hasLore() || item.getItemMeta().getLore() == null) sender.sendMessage("Lore: []"); + else item.getItemMeta().getLore().forEach(line -> sender.sendMessage(line.replace('§', '&'))); + return true; + } + + private boolean inspectEnchant(CommandSender sender) { + ItemStack item = hand(sender); + if (item.getEnchantments().isEmpty()) sender.sendMessage("Enchantments: []"); + item.getEnchantments().forEach((enchantment, level) -> sender.sendMessage(enchantment.getKey() + "=" + level)); + return true; + } + + private boolean inspectNbt(CommandSender sender, String[] args) { + if (!(sender instanceof Player player)) + throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + ItemStack item = player.getInventory().getItemInMainHand(); + if (item.getType().isAir()) NBT.get(player, nbt -> { sendNbt(sender, nbt, args); }); + else NBT.get(item, nbt -> { sendNbt(sender, nbt, args); }); + return true; + } + + private void sendNbt(CommandSender sender, ReadableNBT nbt, String[] args) { + if (args.length >= 2) { + String key = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); + if (!nbt.hasTag(key)) sender.sendMessage(itemCodec.color(languages.text("command.nbt_not_found", "&cNBT 不存在: {key}", "key", key))); + else sender.sendMessage(itemCodec.color("&e" + key + " &7(" + nbt.getType(key).getName() + + ") &f= " + nbtValue(nbt, key))); + return; + } + if (nbt.getKeys().isEmpty()) sender.sendMessage("NBT: []"); + else nbt.getKeys().stream().sorted().forEach(key -> sender.sendMessage(itemCodec.color( + "&e" + key + " &7(" + nbt.getType(key).getName() + ") &f= " + nbtValue(nbt, key)))); + } + + private String nbtValue(ReadableNBT nbt, String key) { + NBTType type = nbt.getType(key); + if (type == null) return "null"; + return switch (type) { + case NBTTagByte -> Byte.toString(nbt.getByte(key)); + case NBTTagShort -> Short.toString(nbt.getShort(key)); + case NBTTagInt -> Integer.toString(nbt.getInteger(key)); + case NBTTagLong -> Long.toString(nbt.getLong(key)); + case NBTTagFloat -> Float.toString(nbt.getFloat(key)); + case NBTTagDouble -> Double.toString(nbt.getDouble(key)); + case NBTTagByteArray -> Arrays.toString(nbt.getByteArray(key)); + case NBTTagIntArray -> Arrays.toString(nbt.getIntArray(key)); + case NBTTagLongArray -> Arrays.toString(nbt.getLongArray(key)); + case NBTTagString -> nbt.getString(key); + case NBTTagCompound -> String.valueOf(nbt.getCompound(key)); + case NBTTagList -> "list<" + nbt.getListType(key).getName() + ">"; + default -> type.getName(); + }; + } + + private ItemStack hand(CommandSender sender) { + if (!(sender instanceof Player player)) throw new IllegalArgumentException(languages.text("command.player_only", "&c该命令只能由玩家执行")); + ItemStack item = player.getInventory().getItemInMainHand(); + if (item.getType().isAir()) throw new IllegalArgumentException(languages.text("cmagic.command.empty_hand", "&c主手没有物品")); + return item; + } + + private void requireAdmin(CommandSender sender) { + if (!hasPermission(sender, "admin")) throw new IllegalArgumentException(languages.text("command.no_permission", "&c你没有权限")); + } + + private boolean hasPermission(CommandSender sender, String permission) { + return sender.hasPermission("cmagicgem.command." + permission) + || sender.hasPermission("magicgem.command." + permission); + } + + private Player requirePlayer(String name) { + Player player = Bukkit.getPlayerExact(name); + if (player == null) throw new IllegalArgumentException(languages.text("command.player_not_found", "&c玩家不在线: {player}", "player", name)); + return player; + } + + private GemDefinition requireGem(String name) { + return registry.find(name).orElseThrow(() -> new IllegalArgumentException( + languages.text("command.gem_not_found", "&c未知宝石: {gem}", "gem", name))); + } + + private String joinGemName(String[] args, int start, int end) { + return String.join(" ", Arrays.copyOfRange(args, start, end)); + } + + private boolean integer(String value) { + try { Integer.parseInt(value); return true; } catch (NumberFormatException ignored) { return false; } + } + + private EquipmentSlot parseSlot(String value) { + return switch (value.toLowerCase(Locale.ROOT)) { + case "mainhand", "hand" -> EquipmentSlot.HAND; + case "offhand" -> EquipmentSlot.OFF_HAND; + case "head", "helmet" -> EquipmentSlot.HEAD; + case "chest", "chestplate" -> EquipmentSlot.CHEST; + case "legs", "leggings" -> EquipmentSlot.LEGS; + case "feet", "boots" -> EquipmentSlot.FEET; + default -> null; + }; + } + + private ItemStack getSlot(PlayerInventory inventory, EquipmentSlot slot) { + return switch (slot) { + case HAND -> inventory.getItemInMainHand(); + case OFF_HAND -> inventory.getItemInOffHand(); + case HEAD -> inventory.getHelmet(); + case CHEST -> inventory.getChestplate(); + case LEGS -> inventory.getLeggings(); + case FEET -> inventory.getBoots(); + default -> inventory.getItemInMainHand(); + }; + } + + private void setSlot(PlayerInventory inventory, EquipmentSlot slot, ItemStack item) { + switch (slot) { + case HAND -> inventory.setItemInMainHand(item); + case OFF_HAND -> inventory.setItemInOffHand(item); + case HEAD -> inventory.setHelmet(item); + case CHEST -> inventory.setChestplate(item); + case LEGS -> inventory.setLeggings(item); + case FEET -> inventory.setBoots(item); + default -> inventory.setItemInMainHand(item); + } + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (args.length == 1) return filter(List.of("help", "give", "apply", "remove", "list", "get", "view", "embed", "addblock", "delblock", "signal", "material", "lore", "enchant", "nbt", "reload"), args[0]); + if (args.length == 2 && Set.of("give", "apply", "remove", "signal").contains(args[0].toLowerCase(Locale.ROOT))) { + List players = new ArrayList<>(Bukkit.getOnlinePlayers().stream().map(Player::getName).toList()); + if (args[0].equalsIgnoreCase("signal")) players.add("@all"); + return filter(players, args[1]); + } + if (args.length >= 3 && Set.of("give", "apply", "remove").contains(args[0].toLowerCase(Locale.ROOT))) + return filter(registry.names(), String.join(" ", Arrays.copyOfRange(args, 2, args.length))); + if (args.length == 2 && args[0].equalsIgnoreCase("embed")) { + List values = new ArrayList<>(guiManager.names()); + values.add("addblock"); + values.add("delblock"); + return filter(values, args[1]); + } + if (args.length == 2 && args[0].equalsIgnoreCase("reload")) + return filter(List.of("gem", "skill"), args[1]); + if (args.length >= 3 && args[0].equalsIgnoreCase("embed") && args[1].equalsIgnoreCase("addblock")) + return filter(new ArrayList<>(guiManager.names()), String.join(" ", Arrays.copyOfRange(args, 2, args.length))); + if (args.length >= 2 && args[0].equalsIgnoreCase("addblock")) + return filter(new ArrayList<>(guiManager.names()), String.join(" ", Arrays.copyOfRange(args, 1, args.length))); + if (args.length == 2 && sender instanceof Player player) { + ItemStack item = player.getInventory().getItemInMainHand(); + return switch (args[0].toLowerCase(Locale.ROOT)) { + case "material" -> filter(List.of(item.getType().name()), args[1]); + case "lore" -> item.hasItemMeta() && item.getItemMeta().hasLore() && item.getItemMeta().getLore() != null + ? filter(item.getItemMeta().getLore().stream().map(line -> line.replace('§', '&')).toList(), args[1]) + : List.of(); + case "enchant" -> filter(item.getEnchantments().keySet().stream() + .map(enchantment -> enchantment.getKey().toString()).toList(), args[1]); + case "nbt" -> item.getType().isAir() + ? NBT.get(player, nbt -> { return filter(nbt.getKeys().stream().toList(), args[1]); }) + : NBT.get(item, nbt -> { return filter(nbt.getKeys().stream().toList(), args[1]); }); + default -> List.of(); + }; + } + return List.of(); + } + + private List filter(List values, String prefix) { + String lower = prefix.toLowerCase(Locale.ROOT); + return values.stream().filter(value -> value.toLowerCase(Locale.ROOT).startsWith(lower)).limit(100).toList(); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/condition/ConditionEngine.java b/src/main/java/cn/infstar/cMagicGem/condition/ConditionEngine.java new file mode 100644 index 0000000..6a7fe9f --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/condition/ConditionEngine.java @@ -0,0 +1,63 @@ +package cn.infstar.cmagicgem.condition; + +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.expression.DynamicExpression; +import cn.infstar.cmagicgem.item.ItemMatcher; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public final class ConditionEngine { + private final DynamicExpression expressions; + private final ItemMatcher itemMatcher; + private final LanguageManager languages; + + public ConditionEngine(DynamicExpression expressions, ItemMatcher itemMatcher, LanguageManager languages) { + this.expressions = expressions; + this.itemMatcher = itemMatcher; + this.languages = languages; + } + + public CheckResult check(List conditions, Player player, ItemStack item) { + for (Directive condition : conditions) { + CheckResult result = check(condition, player, item); + if (!result.success()) return result; + } + return CheckResult.PASS; + } + + public CheckResult check(Directive condition, Player player, ItemStack item) { + String type = condition.type().toLowerCase(Locale.ROOT).replace("_", ""); + boolean passed; + try { + passed = switch (type) { + case "papi", "expression" -> expressions.test(condition.argument("false", "p", "expression", "condition", "c"), player, item, Map.of()); + case "permission" -> player.hasPermission(condition.argument("", "p", "permission")) == condition.booleanArgument(true, "has"); + case "invempty" -> emptySlots(player) >= condition.intArgument(1, "amount"); + case "item", "require" -> itemMatcher.matches(item, condition.argument("", "type", "require", "r")); + default -> false; + }; + } catch (RuntimeException exception) { + return new CheckResult(false, languages.text("cmagic.condition_parse", "&c条件解析失败: {condition}", + "condition", condition.source())); + } + return passed ? CheckResult.PASS : new CheckResult(false, + condition.argument(languages.text("cmagic.condition_failed", "&c条件不满足: {condition}", + "condition", condition.type()), "tip", "message", "m")); + } + + private int emptySlots(Player player) { + int count = 0; + for (ItemStack content : player.getInventory().getStorageContents()) + if (content == null || content.getType().isAir()) count++; + return count; + } + + public record CheckResult(boolean success, String message) { + public static final CheckResult PASS = new CheckResult(true, ""); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/config/LanguageManager.java b/src/main/java/cn/infstar/cMagicGem/config/LanguageManager.java new file mode 100644 index 0000000..876aec6 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/config/LanguageManager.java @@ -0,0 +1,86 @@ +package cn.infstar.cmagicgem.config; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public final class LanguageManager { + private final JavaPlugin plugin; + private volatile YamlConfiguration messages = new YamlConfiguration(); + private volatile String language = "zh_CN"; + + public LanguageManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + public void reload() { + String configured = plugin.getConfig().getString("language", + plugin.getConfig().getString("lang", "zh_CN")); + language = safeName(configured); + File file = new File(plugin.getDataFolder(), "languages/" + language + ".yml"); + YamlConfiguration loaded = YamlConfiguration.loadConfiguration(file); + try (InputStream input = plugin.getResource("languages/" + language + ".yml")) { + if (input != null) { + YamlConfiguration bundled = YamlConfiguration.loadConfiguration( + new InputStreamReader(input, StandardCharsets.UTF_8)); + loaded.setDefaults(bundled); + } + } catch (Exception exception) { + plugin.getLogger().warning("无法读取内置语言 " + language + ": " + exception.getMessage()); + } + messages = loaded; + } + + public String language() { + return language; + } + + public String text(String path, String fallback, Object... replacements) { + String value = messages.getString(path, fallback); + if (value == null) value = fallback == null ? "" : fallback; + value = commandName(value); + for (int index = 0; index + 1 < replacements.length; index += 2) { + String key = String.valueOf(replacements[index]); + String replacement = String.valueOf(replacements[index + 1]); + value = value.replace("{" + key + "}", replacement).replace("%" + key + "%", replacement); + } + return value; + } + + public String format(String path, String fallback, Object... arguments) { + String pattern = text(path, fallback); + try { + return String.format(Locale.ROOT, pattern, arguments); + } catch (RuntimeException exception) { + return pattern; + } + } + + public List section(String path) { + ConfigurationSection section = messages.getConfigurationSection(path); + if (section == null) return List.of(); + List result = new ArrayList<>(); + for (String key : section.getKeys(false)) { + String value = section.getString(key); + if (value != null) result.add(commandName(value)); + } + return List.copyOf(result); + } + + private String commandName(String value) { + return value.replace("/mgem", "/cmgem").replace("/magicgem", "/cmagicgem"); + } + + private String safeName(String configured) { + if (configured == null || !configured.matches("[A-Za-z0-9_-]+")) return "zh_CN"; + return configured; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java b/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java new file mode 100644 index 0000000..2104b96 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/config/PluginSettings.java @@ -0,0 +1,13 @@ +package cn.infstar.cmagicgem.config; + +import org.bukkit.configuration.file.FileConfiguration; + +public record PluginSettings(boolean workbench, boolean backpack, boolean gemLore, boolean gemCraft, + boolean skillEngine, boolean skillEngineLoreColor, boolean legacyMigration) { + public static PluginSettings load(FileConfiguration config) { + return new PluginSettings(config.getBoolean("workbench", true), config.getBoolean("backpack", false), + config.getBoolean("gem-lore", false), config.getBoolean("gem-craft", false), + config.getBoolean("skill-engine", true), config.getBoolean("skill-engine-lore-color", false), + config.getBoolean("legacy-migration", true)); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/dsl/Directive.java b/src/main/java/cn/infstar/cMagicGem/dsl/Directive.java new file mode 100644 index 0000000..8ee643d --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/dsl/Directive.java @@ -0,0 +1,34 @@ +package cn.infstar.cmagicgem.dsl; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public record Directive(String type, Map arguments, List positional, + boolean ignorable, Phase phase, String source) { + public enum Phase { + ALWAYS, SUCCESS, FAIL, REMOVE + } + + public String argument(String fallback, String... names) { + for (String name : names) { + String value = arguments.get(name.toLowerCase(Locale.ROOT)); + if (value != null) { + return value; + } + } + return fallback; + } + + public boolean booleanArgument(boolean fallback, String... names) { + return Boolean.parseBoolean(argument(Boolean.toString(fallback), names)); + } + + public int intArgument(int fallback, String... names) { + try { + return Integer.parseInt(argument(Integer.toString(fallback), names)); + } catch (NumberFormatException ignored) { + return fallback; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/dsl/DirectiveParser.java b/src/main/java/cn/infstar/cMagicGem/dsl/DirectiveParser.java new file mode 100644 index 0000000..2e1838c --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/dsl/DirectiveParser.java @@ -0,0 +1,116 @@ +package cn.infstar.cmagicgem.dsl; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public final class DirectiveParser { + public Directive parse(String input) { + String source = input == null ? "" : input.trim(); + boolean ignorable = containsFlag(source, "$ignorable"); + Directive.Phase phase = containsFlag(source, "$onSuccess") ? Directive.Phase.SUCCESS + : containsFlag(source, "$onFail") ? Directive.Phase.FAIL + : containsFlag(source, "$onRemove") ? Directive.Phase.REMOVE : Directive.Phase.ALWAYS; + String clean = source.replaceAll("(?i)\\s+\\$(ignorable|onSuccess|onFail|onRemove)\\b", "").trim(); + int brace = clean.indexOf('{'); + String type = (brace < 0 ? clean : clean.substring(0, brace)).trim(); + String body = brace < 0 ? "" : extractBody(clean, brace); + Map arguments = new LinkedHashMap<>(); + List positional = new ArrayList<>(); + for (String token : splitTopLevel(body, ';')) { + String trimmed = token.trim(); + if (trimmed.isEmpty()) { + continue; + } + int equals = indexOfTopLevel(trimmed, '='); + if (equals < 0) { + positional.add(unquote(trimmed)); + } else { + arguments.put(trimmed.substring(0, equals).trim().toLowerCase(Locale.ROOT), + unquote(trimmed.substring(equals + 1).trim())); + } + } + return new Directive(type, Map.copyOf(arguments), List.copyOf(positional), ignorable, phase, source); + } + + public List splitTopLevel(String value, char separator) { + List parts = new ArrayList<>(); + int depth = 0; + int start = 0; + boolean quoted = false; + for (int index = 0; index < value.length(); index++) { + char current = value.charAt(index); + if (current == '"' && (index == 0 || value.charAt(index - 1) != '\\')) { + quoted = !quoted; + } else if (!quoted && current == '{') { + depth++; + } else if (!quoted && current == '}') { + depth--; + } else if (!quoted && depth == 0 && current == separator) { + parts.add(value.substring(start, index)); + start = index + 1; + } + } + parts.add(value.substring(start)); + return parts; + } + + public List delimitedArgument(Directive directive, Set names, Set stopKeys) { + String source = directive.source(); + int open = source.indexOf('{'); + int close = source.lastIndexOf('}'); + if (open < 0 || close <= open) return List.of(); + Set normalizedNames = names.stream().map(value -> value.toLowerCase(Locale.ROOT)).collect(java.util.stream.Collectors.toSet()); + Set normalizedStops = stopKeys.stream().map(value -> value.toLowerCase(Locale.ROOT)).collect(java.util.stream.Collectors.toSet()); + List values = new ArrayList<>(); + boolean collecting = false; + for (String token : splitTopLevel(source.substring(open + 1, close), ';')) { + String trimmed = token.trim(); + int equals = indexOfTopLevel(trimmed, '='); + String key = equals < 0 ? "" : trimmed.substring(0, equals).trim().toLowerCase(Locale.ROOT); + if (normalizedNames.contains(key)) { + collecting = true; + values.add(unquote(trimmed.substring(equals + 1).trim())); + } else if (collecting && normalizedStops.contains(key)) { + break; + } else if (collecting) { + values.add(unquote(trimmed)); + } + } + return List.copyOf(values); + } + + private int indexOfTopLevel(String value, char target) { + int depth = 0; + for (int index = 0; index < value.length(); index++) { + char current = value.charAt(index); + if (current == '{') depth++; + if (current == '}') depth--; + if (current == target && depth == 0) return index; + } + return -1; + } + + private String extractBody(String value, int brace) { + int end = value.lastIndexOf('}'); + if (end < brace) { + throw new IllegalArgumentException("缺少右花括号: " + value); + } + return value.substring(brace + 1, end); + } + + private boolean containsFlag(String value, String flag) { + return value.toLowerCase(Locale.ROOT).contains(flag.toLowerCase(Locale.ROOT)); + } + + private String unquote(String value) { + if (value.length() < 2) return value; + char quote = value.charAt(0); + if ((quote != '"' && quote != '\'') || value.charAt(value.length() - 1) != quote) return value; + return value.substring(1, value.length() - 1) + .replace("\\" + quote, Character.toString(quote)); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/expression/DynamicExpression.java b/src/main/java/cn/infstar/cMagicGem/expression/DynamicExpression.java new file mode 100644 index 0000000..c3bb912 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/expression/DynamicExpression.java @@ -0,0 +1,146 @@ +package cn.infstar.cmagicgem.expression; + +import cn.infstar.cmagicgem.integration.PlaceholderBridge; +import cn.infstar.cmagicgem.util.EnchantmentResolver; +import de.tr7zw.changeme.nbtapi.NBT; +import org.bukkit.ChatColor; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class DynamicExpression { + private static final Pattern ITEM_VARIABLE = Pattern.compile("\\$(LORE|WORD|NBT|ENCHANT):([^?$]+?)(?:\\?([^$]*))?\\$", Pattern.CASE_INSENSITIVE); + private static final Pattern NUMBER = Pattern.compile("[-+]?\\d+(?:\\.\\d+)?"); + private final Expression expression; + private final PlaceholderBridge placeholders; + + public DynamicExpression(Expression expression, PlaceholderBridge placeholders) { + this.expression = expression; + this.placeholders = placeholders; + } + + public double evaluate(String source, Player player, ItemStack item, Map variables) { + return evaluate(source, player, item, variables, false); + } + + public double evaluateRoman(String source, Player player, ItemStack item, Map variables) { + return evaluate(source, player, item, variables, true); + } + + private double evaluate(String source, Player player, ItemStack item, Map variables, boolean roman) { + String resolved = placeholders.resolve(player, source == null ? "0" : source); + Matcher matcher = ITEM_VARIABLE.matcher(resolved); + StringBuffer buffer = new StringBuffer(); + while (matcher.find()) { + double fallback = parseNumber(matcher.group(3), 0); + double value = resolveItem(matcher.group(1), matcher.group(2), item, fallback, roman); + matcher.appendReplacement(buffer, Matcher.quoteReplacement(Double.toString(value))); + } + matcher.appendTail(buffer); + Map normalized = new HashMap<>(); + variables.forEach((key, value) -> { + normalized.put(key, value); + normalized.put(key.toLowerCase(Locale.ROOT), value); + }); + return expression.evaluate(buffer.toString(), normalized); + } + + public boolean test(String source, Player player, ItemStack item, Map variables) { + return Math.abs(evaluate(source, player, item, variables)) > 1.0E-9; + } + + private double resolveItem(String type, String key, ItemStack item, double fallback, boolean roman) { + if (item == null || item.getType().isAir()) return fallback; + return switch (type.toUpperCase(Locale.ROOT)) { + case "LORE" -> lore(item, key, fallback, roman); + case "WORD" -> word(item, key, fallback); + case "NBT" -> nbt(item, key, fallback); + case "ENCHANT" -> enchant(item, key, fallback); + default -> fallback; + }; + } + + private double word(ItemStack item, String expected, double value) { + if (!item.hasItemMeta()) return 0; + String cleanExpected = strip(expected); + if (item.getItemMeta().hasDisplayName() && strip(item.getItemMeta().getDisplayName()).contains(cleanExpected)) return value; + if (item.getItemMeta().hasLore() && item.getItemMeta().getLore() != null) { + for (String line : item.getItemMeta().getLore()) if (strip(line).contains(cleanExpected)) return value; + } + return 0; + } + + private double lore(ItemStack item, String marker, double fallback, boolean roman) { + if (!item.hasItemMeta() || !item.getItemMeta().hasLore() || item.getItemMeta().getLore() == null) return fallback; + String cleanMarker = strip(marker); + for (String line : item.getItemMeta().getLore()) { + String clean = strip(line); + int index = clean.indexOf(cleanMarker); + if (index < 0) continue; + Matcher number = NUMBER.matcher(clean.substring(index + cleanMarker.length())); + if (number.find()) return parseNumber(number.group(), fallback); + if (roman) { + Matcher numeral = Pattern.compile("(?i)\\b[IVXLCDM]+\\b") + .matcher(clean.substring(index + cleanMarker.length())); + if (numeral.find()) return parseRoman(numeral.group(), fallback); + } + } + return fallback; + } + + private double nbt(ItemStack item, String key, double fallback) { + try { + return NBT.get(item, nbt -> { + if (!nbt.hasTag(key)) return fallback; + return switch (nbt.getType(key)) { + case NBTTagByte -> nbt.getByte(key).doubleValue(); + case NBTTagShort -> nbt.getShort(key).doubleValue(); + case NBTTagInt -> nbt.getInteger(key).doubleValue(); + case NBTTagLong -> nbt.getLong(key).doubleValue(); + case NBTTagFloat -> nbt.getFloat(key).doubleValue(); + case NBTTagDouble -> nbt.getDouble(key); + default -> parseNumber(nbt.getString(key), fallback); + }; + }); + } catch (RuntimeException exception) { + return fallback; + } + } + + private double enchant(ItemStack item, String key, double fallback) { + Enchantment enchantment = EnchantmentResolver.resolve(key); + return enchantment == null ? fallback : item.getEnchantmentLevel(enchantment); + } + + private String strip(String value) { + return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', value == null ? "" : value)); + } + + private double parseNumber(String value, double fallback) { + try { return value == null || value.isBlank() ? fallback : Double.parseDouble(value); } + catch (NumberFormatException ignored) { return fallback; } + } + + private double parseRoman(String value, double fallback) { + if (value == null || value.isBlank()) return fallback; + int total = 0; + int previous = 0; + String normalized = value.toUpperCase(Locale.ROOT); + for (int index = normalized.length() - 1; index >= 0; index--) { + int current = switch (normalized.charAt(index)) { + case 'I' -> 1; case 'V' -> 5; case 'X' -> 10; case 'L' -> 50; + case 'C' -> 100; case 'D' -> 500; case 'M' -> 1000; default -> 0; + }; + if (current == 0) return fallback; + total += current < previous ? -current : current; + previous = current; + } + return total; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/expression/Expression.java b/src/main/java/cn/infstar/cMagicGem/expression/Expression.java new file mode 100644 index 0000000..c190b2f --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/expression/Expression.java @@ -0,0 +1,194 @@ +package cn.infstar.cmagicgem.expression; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +public final class Expression { + public double evaluate(String source, Map variables) { + if (source == null || source.isBlank()) { + return 0; + } + Parser parser = new Parser(source, variables); + double result = parser.parseConditional(); + parser.skipWhitespace(); + if (!parser.finished()) { + throw new IllegalArgumentException("无法解析表达式位置 " + parser.index + ": " + source); + } + return result; + } + + public boolean test(String source, Map variables) { + return Math.abs(evaluate(source, variables)) > 1.0E-9; + } + + private static final class Parser { + private final String source; + private final Map variables; + private int index; + + private Parser(String source, Map variables) { + this.source = source; + this.variables = variables; + } + + private double parseOr() { + double left = parseAnd(); + while (match("||") || match("|")) { + double right = parseAnd(); + left = truth(left) || truth(right) ? 1 : 0; + } + return left; + } + + private double parseConditional() { + double condition = parseOr(); + if (!match("?")) return condition; + double whenTrue = parseConditional(); + require(":"); + double whenFalse = parseConditional(); + return truth(condition) ? whenTrue : whenFalse; + } + + private double parseAnd() { + double left = parseEquality(); + while (match("&&") || match("&")) { + double right = parseEquality(); + left = truth(left) && truth(right) ? 1 : 0; + } + return left; + } + + private double parseEquality() { + double left = parseComparison(); + while (true) { + if (match("==")) left = close(left, parseComparison()) ? 1 : 0; + else if (match("!=")) left = close(left, parseComparison()) ? 0 : 1; + else return left; + } + } + + private double parseComparison() { + double left = parseTerm(); + while (true) { + if (match(">=")) left = left >= parseTerm() ? 1 : 0; + else if (match("<=")) left = left <= parseTerm() ? 1 : 0; + else if (match(">")) left = left > parseTerm() ? 1 : 0; + else if (match("<")) left = left < parseTerm() ? 1 : 0; + else return left; + } + } + + private double parseTerm() { + double left = parseFactor(); + while (true) { + if (match("+")) left += parseFactor(); + else if (match("-")) left -= parseFactor(); + else return left; + } + } + + private double parseFactor() { + double left = parsePower(); + while (true) { + if (match("*")) left *= parsePower(); + else if (match("/")) left /= parsePower(); + else if (match("%")) left %= parsePower(); + else return left; + } + } + + private double parsePower() { + double left = parseUnary(); + if (match("^")) return Math.pow(left, parsePower()); + return left; + } + + private double parseUnary() { + if (match("!")) return truth(parseUnary()) ? 0 : 1; + if (match("-")) return -parseUnary(); + if (match("+")) return parseUnary(); + return parsePrimary(); + } + + private double parsePrimary() { + if (match("(")) { + double value = parseConditional(); + require(")"); + return value; + } + skipWhitespace(); + if (index < source.length() && (Character.isDigit(source.charAt(index)) || source.charAt(index) == '.')) { + int start = index++; + while (index < source.length() && (Character.isDigit(source.charAt(index)) || source.charAt(index) == '.')) index++; + return Double.parseDouble(source.substring(start, index)); + } + match("$"); + String name = readName(); + if (name.isEmpty()) throw new IllegalArgumentException("表达式缺少值: " + source); + if (match("(")) { + double first = match(")") ? Double.NaN : parseConditional(); + double second = Double.NaN; + if (match(",")) second = parseConditional(); + if (!Double.isNaN(first)) require(")"); + return function(name, first, second); + } + if (name.equalsIgnoreCase("true")) return 1; + if (name.equalsIgnoreCase("false")) return 0; + return variables.getOrDefault(name, variables.getOrDefault(name.toLowerCase(Locale.ROOT), 0.0)); + } + + private double function(String name, double first, double second) { + return switch (name.toUpperCase(Locale.ROOT)) { + case "RANDOM" -> Double.isNaN(first) ? ThreadLocalRandom.current().nextDouble() + : Double.isNaN(second) ? ThreadLocalRandom.current().nextDouble(first) + : ThreadLocalRandom.current().nextDouble(first, second); + case "RANDINT" -> ThreadLocalRandom.current().nextInt((int) first, (int) second); + case "ABS" -> Math.abs(first); + case "FLOOR" -> Math.floor(first); + case "LOG" -> Math.log(first); + case "POW" -> Math.pow(first, second); + case "CEIL" -> Math.ceil(first); + case "ROUND" -> Math.rint(first); + case "SQRT" -> Math.sqrt(first); + case "MIN" -> Math.min(first, second); + case "MAX" -> Math.max(first, second); + default -> throw new IllegalArgumentException("未知函数: " + name); + }; + } + + private String readName() { + skipWhitespace(); + int start = index; + while (index < source.length() && (Character.isLetterOrDigit(source.charAt(index)) || source.charAt(index) == '_')) index++; + return source.substring(start, index); + } + + private boolean match(String token) { + skipWhitespace(); + if (!source.startsWith(token, index)) return false; + index += token.length(); + return true; + } + + private void require(String token) { + if (!match(token)) throw new IllegalArgumentException("缺少 " + token + ": " + source); + } + + private void skipWhitespace() { + while (index < source.length() && Character.isWhitespace(source.charAt(index))) index++; + } + + private boolean finished() { + return index >= source.length(); + } + + private boolean truth(double value) { + return Math.abs(value) > 1.0E-9; + } + + private boolean close(double left, double right) { + return Math.abs(left - right) < 1.0E-9; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/GemDefinition.java b/src/main/java/cn/infstar/cMagicGem/gem/GemDefinition.java new file mode 100644 index 0000000..47262c0 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/GemDefinition.java @@ -0,0 +1,21 @@ +package cn.infstar.cmagicgem.gem; + +import cn.infstar.cmagicgem.dsl.Directive; +import org.bukkit.Material; + +import java.util.List; +import java.util.Set; + +public record GemDefinition(String id, String name, GemType type, Material material, String display, + List tips, String texture, Integer color, String effects, + Integer customModelData, boolean glow, + Set requirements, List allowedGuis, int embedLimit, + String successExpression, String downgradeExpression, String breakExpression, + String fastUse, boolean eat, boolean shift, double cooldownSeconds, String cooldownTip, + boolean giveRandomGem, List randomGems, + List rewards, List conditions, + String successTip, String failTip, String downgradeTip, + String removeTip, String breakTip, String loreIdentity) { + public record WeightedGem(String name, double weight) { + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/GemRegistry.java b/src/main/java/cn/infstar/cMagicGem/gem/GemRegistry.java new file mode 100644 index 0000000..37cf80a --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/GemRegistry.java @@ -0,0 +1,169 @@ +package cn.infstar.cmagicgem.gem; + +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.util.MaterialResolver; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; + +public final class GemRegistry { + private final JavaPlugin plugin; + private final DirectiveParser parser; + private volatile Map gems = Map.of(); + private volatile Map> groups = Map.of(); + + public GemRegistry(JavaPlugin plugin, DirectiveParser parser) { + this.plugin = plugin; + this.parser = parser; + } + + public int reload() { + File directory = new File(plugin.getDataFolder(), "gems"); + if (!directory.exists() && !directory.mkdirs()) { + throw new IllegalStateException("无法创建 gems 目录"); + } + File[] files = directory.listFiles((ignored, name) -> name.toLowerCase(Locale.ROOT).endsWith(".yml")); + Map loaded = new LinkedHashMap<>(); + Map> loadedGroups = new LinkedHashMap<>(); + if (files != null) { + java.util.Arrays.sort(files, java.util.Comparator.comparing(File::getName, String.CASE_INSENSITIVE_ORDER)); + for (File file : files) { + List group = new ArrayList<>(); + loadFile(file, loaded, group); + if (!group.isEmpty()) loadedGroups.put(file.getName(), List.copyOf(group)); + } + } + Map randomErrors = RandomGemValidator.validate(loaded); + for (Map.Entry error : randomErrors.entrySet()) { + GemDefinition invalid = loaded.remove(error.getKey()); + if (invalid != null) plugin.getLogger().severe("无法加载随机宝石 " + invalid.name() + ": " + error.getValue()); + } + if (!randomErrors.isEmpty()) loadedGroups.replaceAll((name, values) -> values.stream() + .filter(gem -> loaded.containsKey(gem.name().toLowerCase(Locale.ROOT))).toList()); + loadedGroups.entrySet().removeIf(entry -> entry.getValue().isEmpty()); + gems = Collections.unmodifiableMap(loaded); + groups = Collections.unmodifiableMap(loadedGroups); + return loaded.size(); + } + + private void loadFile(File file, Map target, List group) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); + for (String key : yaml.getKeys(false)) { + ConfigurationSection section = yaml.getConfigurationSection(key); + if (section == null) continue; + try { + GemDefinition gem = parseGem(key, section); + String normalized = gem.name().toLowerCase(Locale.ROOT); + if (target.containsKey(normalized)) throw new IllegalArgumentException("宝石名称重复: " + gem.name()); + target.put(normalized, gem); + group.add(gem); + } catch (RuntimeException exception) { + plugin.getLogger().log(Level.SEVERE, "无法加载宝石 " + file.getName() + "/" + key + ": " + exception.getMessage()); + } + } + } + + private GemDefinition parseGem(String id, ConfigurationSection section) { + String name = section.getString("Name", id); + GemType type = GemType.parse(section.getString("Type", "ITEM_GEM")); + Material material = MaterialResolver.resolve(section.getString("Material", "PLAYER_HEAD")); + if (material == null) throw new IllegalArgumentException("未知材料 " + section.getString("Material")); + List rewards = parseDirectives(section.getStringList("Rewards")); + List conditions = parseDirectives(section.getStringList("Conditions")); + List random = new ArrayList<>(); + ConfigurationSection randomSection = section.getConfigurationSection("Gems"); + if (randomSection != null) { + for (String randomName : randomSection.getKeys(false)) { + random.add(new GemDefinition.WeightedGem(randomName, randomSection.getDouble(randomName, 1))); + } + } else if (type == GemType.RANDOM_GEM) { + for (String entry : section.getStringList("Random")) { + int split = entry.lastIndexOf(':'); + random.add(new GemDefinition.WeightedGem(split < 0 ? entry : entry.substring(0, split), + split < 0 ? 1 : Double.parseDouble(entry.substring(split + 1)))); + } + } + return new GemDefinition(id, name, type, material, section.getString("Display", name), + List.copyOf(section.getStringList("Tips")), section.getString("Texture"), color(section.get("Color")), + section.getString("Effect"), + section.contains("CustomModelData") ? section.getInt("CustomModelData") : null, + section.getBoolean("Glow", false), upperSet(stringList(section, "Require")), + List.copyOf(stringList(section, "Gui")), section.getInt("Embed", 0), + section.getString("Success", "100"), section.getString("Downgrade", "0"), + section.getString("Break", "0"), fastUse(section), + section.getBoolean("Eat", false), section.getBoolean("Shift", false), + section.getDouble("Cooldown", 0), section.getString("CooldownTip"), + section.getBoolean("Give", false), List.copyOf(random), + rewards, conditions, section.getString("SuccessTip"), section.getString("FailTip"), + section.getString("DowngradeTip"), section.getString("RemoveTip"), + section.getString("BreakTip"), section.getString("Lore")); + } + + private Integer color(Object value) { + return ItemCodec.parseColor(value); + } + + private String fastUse(ConfigurationSection section) { + Object value = section.get("FastUse"); + if (value instanceof Boolean enabled) return enabled ? "offhand" : null; + if (value == null) return null; + String slot = value.toString().trim(); + return slot.isEmpty() || slot.equalsIgnoreCase("false") ? null : slot; + } + + private List parseDirectives(List values) { + List directives = new ArrayList<>(); + for (String value : values) directives.add(parser.parse(value)); + return List.copyOf(directives); + } + + private Set upperSet(List values) { + Set result = new LinkedHashSet<>(); + for (String value : values) result.add(value.toUpperCase(Locale.ROOT)); + return Set.copyOf(result); + } + + private List stringList(ConfigurationSection section, String path) { + Object value = section.get(path); + if (value instanceof String text) return text.isBlank() ? List.of() : List.of(text); + if (value instanceof Collection collection) { + List result = new ArrayList<>(); + for (Object entry : collection) if (entry != null && !entry.toString().isBlank()) result.add(entry.toString()); + return List.copyOf(result); + } + return List.of(); + } + + public Optional find(String name) { + if (name == null) return Optional.empty(); + return Optional.ofNullable(gems.get(name.toLowerCase(Locale.ROOT))); + } + + public Collection all() { + return gems.values(); + } + + public List names() { + return gems.values().stream().map(GemDefinition::name).sorted().toList(); + } + + public Map> groups() { + return groups; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/GemService.java b/src/main/java/cn/infstar/cMagicGem/gem/GemService.java new file mode 100644 index 0000000..8a98816 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/GemService.java @@ -0,0 +1,252 @@ +package cn.infstar.cmagicgem.gem; + +import cn.infstar.cmagicgem.api.event.CMagicGemUseEvent; +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.condition.ConditionEngine; +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.expression.DynamicExpression; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.item.ItemMatcher; +import cn.infstar.cmagicgem.reward.RewardContext; +import cn.infstar.cmagicgem.reward.RewardEngine; +import cn.infstar.cmagicgem.reward.RewardException; +import org.bukkit.entity.Player; +import org.bukkit.Bukkit; +import org.bukkit.inventory.ItemStack; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public final class GemService { + private final GemRegistry registry; + private final ItemCodec itemCodec; + private final ItemMatcher matcher; + private final RewardEngine rewards; + private final DynamicExpression expression; + private final ConditionEngine conditions; + private final LanguageManager languages; + private final Map> cooldowns = new ConcurrentHashMap<>(); + + public GemService(GemRegistry registry, ItemCodec itemCodec, ItemMatcher matcher, + RewardEngine rewards, DynamicExpression expression, ConditionEngine conditions, + LanguageManager languages) { + this.registry = registry; + this.itemCodec = itemCodec; + this.matcher = matcher; + this.rewards = rewards; + this.expression = expression; + this.conditions = conditions; + this.languages = languages; + } + + public GemUseResult use(Player player, GemDefinition requested, ItemStack target) { + CMagicGemUseEvent event = new CMagicGemUseEvent(player, requested, true); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return GemUseResult.rejected(target, + languages.text("cmagic.use_cancelled", "&c宝石使用已被其他插件取消")); + GemDefinition gem = resolveRandom(requested); + if (gem == null) return GemUseResult.rejected(target, + languages.text("cmagic.random_invalid", "&c随机宝石没有有效目标")); + if (requested.type() == GemType.RANDOM_GEM && requested.giveRandomGem()) { + give(player, gem, 1); + return new GemUseResult(true, true, target, requested.successTip()); + } + GemUseResult result = switch (gem.type()) { + case ITEM_GEM -> useItem(player, requested, gem, target); + case PLAYER_GEM -> usePlayer(player, requested, gem, target); + case RANDOM_GEM -> GemUseResult.rejected(target, + languages.text("cmagic.random_cycle", "&c随机宝石不能循环引用")); + }; + if (requested.type() != GemType.RANDOM_GEM) return result; + return new GemUseResult(true, result.successful(), result.target(), result.message()); + } + + public GemType effectiveType(GemDefinition gem) { + return effectiveType(gem, new HashSet<>()); + } + + private GemType effectiveType(GemDefinition gem, Set visiting) { + if (gem.type() != GemType.RANDOM_GEM) return gem.type(); + if (!visiting.add(gem.name().toLowerCase(java.util.Locale.ROOT))) return GemType.RANDOM_GEM; + GemType resolved = null; + for (GemDefinition.WeightedGem weighted : gem.randomGems()) { + GemDefinition nested = registry.find(weighted.name()).orElse(null); + if (nested == null) continue; + GemType type = effectiveType(nested, new HashSet<>(visiting)); + if (type == GemType.RANDOM_GEM || resolved != null && resolved != type) return GemType.RANDOM_GEM; + resolved = type; + } + return resolved == null ? GemType.RANDOM_GEM : resolved; + } + + private GemUseResult useItem(Player player, GemDefinition consumedGem, GemDefinition gem, ItemStack target) { + if (!matcher.matches(target, gem.requirements())) return GemUseResult.rejected(target, + languages.text("gem.tip.wrong_type", "&c目标装备不符合宝石要求")); + ConditionEngine.CheckResult condition = conditions.check(gem.conditions(), player, target); + if (!condition.success()) return GemUseResult.rejected(target, condition.message()); + Map embedded = new LinkedHashMap<>(itemCodec.embedded(target)); + if (gem.embedLimit() > 0 && embedded.getOrDefault(gem.name(), 0) >= gem.embedLimit()) + return GemUseResult.rejected(target, languages.text("gem.tip.embed_limit", "&c已达到该宝石的镶嵌上限")); + boolean successful = roll(gem.successExpression(), player, target); + RewardContext context = new RewardContext(player, gem, target, false); + if (!successful) { + try { rewards.applyPlayerRewards(context, gem.rewards(), Directive.Phase.FAIL); } + catch (RewardException exception) { return GemUseResult.rejected(target, exception.getMessage()); } + if (embedded.getOrDefault(gem.name(), 0) > 0 && roll(gem.downgradeExpression(), player, target)) { + RewardContext downgrade = new RewardContext(player, gem, target, true); + try { + ItemStack changed = rewards.applyItemRewards(downgrade, gem.rewards(), Directive.Phase.REMOVE); + embedded.compute(gem.name(), (ignored, value) -> value == null || value <= 1 ? null : value - 1); + changed = itemCodec.setEmbedded(changed, embedded); + downgrade.item(changed); + return new GemUseResult(true, false, changed, + defaultMessage(gem.downgradeTip(), languages.text("gem.tip.downgrade", "&c强化失败,装备已降级"))); + } catch (RewardException exception) { + return GemUseResult.rejected(target, exception.getMessage()); + } + } + return new GemUseResult(true, false, target, + defaultMessage(gem.failTip(), languages.text("gem.tip.fail", "&c宝石使用失败"))); + } + try { + ItemStack changed = rewards.applyItemRewards(context, gem.rewards(), Directive.Phase.SUCCESS); + context.item(changed); + if (gem.embedLimit() > 0) { + embedded.merge(gem.name(), 1, Integer::sum); + changed = itemCodec.setEmbedded(changed, embedded); + context.item(changed); + } + rewards.applyPlayerRewards(context, gem.rewards(), Directive.Phase.SUCCESS); + return new GemUseResult(true, true, context.item(), + defaultMessage(gem.successTip(), languages.text("gem.tip.success", "&a宝石使用成功"))); + } catch (RewardException | RuntimeException exception) { + return GemUseResult.rejected(target, exception.getMessage()); + } + } + + private GemUseResult usePlayer(Player player, GemDefinition consumedGem, GemDefinition gem, ItemStack target) { + ConditionEngine.CheckResult condition = conditions.check(gem.conditions(), player, target); + if (!condition.success()) return GemUseResult.rejected(target, condition.message()); + long now = System.currentTimeMillis(); + Map playerCooldowns = cooldowns.computeIfAbsent(player.getUniqueId(), ignored -> new ConcurrentHashMap<>()); + long ready = playerCooldowns.getOrDefault(gem.name(), 0L); + if (ready > now) return GemUseResult.rejected(target, + cooldownTip(gem.cooldownTip(), (ready - now) / 1000.0, gem.cooldownSeconds())); + boolean successful = roll(gem.successExpression(), player, target); + RewardContext context = new RewardContext(player, gem, target, false); + try { + rewards.applyPlayerRewards(context, gem.rewards(), successful ? Directive.Phase.SUCCESS : Directive.Phase.FAIL); + if (gem.cooldownSeconds() > 0) playerCooldowns.put(gem.name(), now + (long) (gem.cooldownSeconds() * 1000)); + return new GemUseResult(true, successful, target, defaultMessage(successful ? gem.successTip() : gem.failTip(), + languages.text(successful ? "gem.tip.success" : "gem.tip.fail", + successful ? "&a宝石使用成功" : "&c宝石使用失败"))); + } catch (RewardException exception) { + return GemUseResult.rejected(target, exception.getMessage()); + } + } + + public GemUseResult remove(Player player, String gemName, ItemStack target) { + Optional found = registry.find(gemName); + if (found.isEmpty()) return GemUseResult.rejected(target, + languages.text("cmagic.gem_not_found", "&c未知宝石: {gem}", "gem", gemName)); + GemDefinition gem = found.get(); + CMagicGemUseEvent event = new CMagicGemUseEvent(player, gem, false); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return GemUseResult.rejected(target, + languages.text("cmagic.remove_cancelled", "&c宝石拆卸已被其他插件取消")); + Map embedded = new LinkedHashMap<>(itemCodec.embedded(target)); + int count = embedded.getOrDefault(gem.name(), 0); + if (count <= 0) return GemUseResult.rejected(target, + languages.text("cmagic.not_embedded", "&c装备未镶嵌该宝石")); + boolean broken = roll(gem.breakExpression(), player, target); + RewardContext context = new RewardContext(player, gem, target, true); + try { + ItemStack changed = rewards.applyItemRewards(context, gem.rewards(), Directive.Phase.REMOVE); + embedded.compute(gem.name(), (ignored, value) -> value == null || value <= 1 ? null : value - 1); + changed = itemCodec.setEmbedded(changed, embedded); + context.item(changed); + RewardContext onRemove = new RewardContext(player, gem, changed, false); + rewards.applyPlayerRewards(onRemove, gem.rewards(), Directive.Phase.REMOVE); + if (!broken) give(player, gem, 1); + return new GemUseResult(false, !broken, changed, broken + ? defaultMessage(gem.breakTip(), languages.text("gem.tip.break", "&c宝石在拆卸时破碎")) + : defaultMessage(gem.removeTip(), languages.text("gem.embed.success", "&a宝石拆卸成功"))); + } catch (RewardException exception) { + return GemUseResult.rejected(target, exception.getMessage()); + } + } + + public void give(Player player, GemDefinition gem, int amount) { + int remaining = Math.max(1, amount); + while (remaining > 0) { + int stack = Math.min(remaining, gem.material().getMaxStackSize()); + ItemStack item = itemCodec.createGem(gem, stack); + player.getInventory().addItem(item).values().forEach(left -> player.getWorld().dropItemNaturally(player.getLocation(), left)); + remaining -= stack; + } + } + + private GemDefinition resolveRandom(GemDefinition gem) { + return resolveRandom(gem, new HashSet<>()); + } + + private GemDefinition resolveRandom(GemDefinition gem, Set visiting) { + if (gem.type() != GemType.RANDOM_GEM) return gem; + if (!visiting.add(gem.name().toLowerCase(java.util.Locale.ROOT))) return null; + List candidates = gem.randomGems(); + double total = candidates.stream().filter(value -> registry.find(value.name()).isPresent()) + .mapToDouble(value -> Math.max(0, value.weight())).sum(); + if (total <= 0) return null; + double roll = ThreadLocalRandom.current().nextDouble(total); + for (GemDefinition.WeightedGem candidate : candidates) { + GemDefinition selected = registry.find(candidate.name()).orElse(null); + if (selected == null || !Double.isFinite(candidate.weight()) || candidate.weight() <= 0) continue; + roll -= candidate.weight(); + if (roll <= 0) return resolveRandom(selected, visiting); + } + return null; + } + + private boolean roll(String source, Player player, ItemStack item) { + double chance; + try { chance = expression.evaluate(source, player, item, Map.of()); } + catch (RuntimeException ignored) { chance = 0; } + return ThreadLocalRandom.current().nextDouble(100) < chance; + } + + private String defaultMessage(String configured, String fallback) { + return configured == null ? fallback : configured; + } + + private String cooldownTip(String configured, double remaining, double total) { + if (configured == null || configured.isBlank()) + return "&e冷却中,剩余 " + String.format(java.util.Locale.ROOT, "%.2f", remaining) + " 秒"; + int open = configured.indexOf('{'); + int close = configured.lastIndexOf('}'); + if (open < 0 || close < open) return configured; + Map arguments = new LinkedHashMap<>(); + for (String token : configured.substring(open + 1, close).split(";")) { + int equals = token.indexOf('='); + if (equals > 0) arguments.put(token.substring(0, equals).trim().toLowerCase(java.util.Locale.ROOT), + token.substring(equals + 1)); + } + int length; + try { length = Math.max(0, Integer.parseInt(arguments.getOrDefault("length", "0"))); } + catch (NumberFormatException ignored) { length = 0; } + int full = total <= 0 ? 0 : (int) Math.round(length * Math.max(0, Math.min(1, 1 - remaining / total))); + String suffix; + try { suffix = String.format(java.util.Locale.ROOT, arguments.getOrDefault("s", "%.2fs"), remaining); } + catch (RuntimeException ignored) { suffix = String.format(java.util.Locale.ROOT, "%.2fs", remaining); } + String message = arguments.getOrDefault("p", "") + + arguments.getOrDefault("full", "|").repeat(full) + + arguments.getOrDefault("empty", "|").repeat(Math.max(0, length - full)) + suffix; + return configured.substring(0, open) + "{m=" + message + '}'; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/GemType.java b/src/main/java/cn/infstar/cMagicGem/gem/GemType.java new file mode 100644 index 0000000..e1100ed --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/GemType.java @@ -0,0 +1,21 @@ +package cn.infstar.cmagicgem.gem; + +import java.util.Locale; + +public enum GemType { + ITEM_GEM, PLAYER_GEM, RANDOM_GEM; + + public static GemType parse(String value) { + if (value == null || value.isBlank()) return ITEM_GEM; + String normalized = value.trim().toUpperCase(Locale.ROOT) + .replace("_", "") + .replace("-", "") + .replace(" ", ""); + return switch (normalized) { + case "ITEMGEM", "ITEM" -> ITEM_GEM; + case "PLAYERGEM", "PLAYER" -> PLAYER_GEM; + case "RANDOMGEM", "RANDOM" -> RANDOM_GEM; + default -> valueOf(value.trim().toUpperCase(Locale.ROOT)); + }; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/GemUseResult.java b/src/main/java/cn/infstar/cMagicGem/gem/GemUseResult.java new file mode 100644 index 0000000..4a9329c --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/GemUseResult.java @@ -0,0 +1,9 @@ +package cn.infstar.cmagicgem.gem; + +import org.bukkit.inventory.ItemStack; + +public record GemUseResult(boolean consumed, boolean successful, ItemStack target, String message) { + public static GemUseResult rejected(ItemStack target, String message) { + return new GemUseResult(false, false, target, message); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gem/RandomGemValidator.java b/src/main/java/cn/infstar/cMagicGem/gem/RandomGemValidator.java new file mode 100644 index 0000000..e52666a --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gem/RandomGemValidator.java @@ -0,0 +1,58 @@ +package cn.infstar.cmagicgem.gem; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public final class RandomGemValidator { + private RandomGemValidator() { + } + + public static Map validate(Map gems) { + Map errors = new LinkedHashMap<>(); + Map> resolved = new LinkedHashMap<>(); + for (Map.Entry entry : gems.entrySet()) { + if (entry.getValue().type() != GemType.RANDOM_GEM) continue; + try { + resolve(entry.getKey(), gems, resolved, new ArrayDeque<>()); + } catch (IllegalArgumentException exception) { + errors.put(entry.getKey(), exception.getMessage()); + } + } + return Map.copyOf(errors); + } + + private static Set resolve(String name, Map gems, + Map> resolved, Deque path) { + String normalized = name.toLowerCase(Locale.ROOT); + Set cached = resolved.get(normalized); + if (cached != null) return cached; + if (path.contains(normalized)) { + String cycle = String.join(" -> ", path) + " -> " + normalized; + throw new IllegalArgumentException("随机宝石循环引用: " + cycle); + } + GemDefinition gem = gems.get(normalized); + if (gem == null) throw new IllegalArgumentException("随机宝石引用不存在: " + name); + if (gem.type() != GemType.RANDOM_GEM) return Set.of(gem.type()); + path.addLast(normalized); + EnumSet types = EnumSet.noneOf(GemType.class); + boolean candidateFound = false; + for (GemDefinition.WeightedGem weighted : gem.randomGems()) { + if (!Double.isFinite(weighted.weight()) || weighted.weight() <= 0) continue; + candidateFound = true; + String target = weighted.name().toLowerCase(Locale.ROOT); + if (!gems.containsKey(target)) throw new IllegalArgumentException("随机宝石引用不存在: " + weighted.name()); + types.addAll(resolve(target, gems, resolved, path)); + } + path.removeLast(); + if (!candidateFound || types.isEmpty()) throw new IllegalArgumentException("随机宝石没有正权重目标: " + gem.name()); + if (types.size() > 1) throw new IllegalArgumentException("随机宝石同时包含物品和玩家宝石: " + gem.name()); + Set result = Set.copyOf(types); + resolved.put(normalized, result); + return result; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gui/GemBrowser.java b/src/main/java/cn/infstar/cMagicGem/gui/GemBrowser.java new file mode 100644 index 0000000..40bff52 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gui/GemBrowser.java @@ -0,0 +1,185 @@ +package cn.infstar.cmagicgem.gui; + +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.item.ItemCodec; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class GemBrowser implements Listener { + private static final int PAGE_SIZE = 45; + private final GemRegistry registry; + private final ItemCodec codec; + private final LanguageManager languages; + + public GemBrowser(GemRegistry registry, ItemCodec codec, LanguageManager languages) { + this.registry = registry; + this.codec = codec; + this.languages = languages; + } + + public void open(Player player, boolean allowTake, int requestedPage) { + openGroups(player, allowTake, requestedPage); + } + + private void openGroups(Player player, boolean allowTake, int requestedPage) { + List>> groups = new ArrayList<>(registry.groups().entrySet()); + Page page = page(requestedPage, groups.size()); + BrowserHolder holder = new BrowserHolder(allowTake, page.index(), null); + Inventory inventory = Bukkit.createInventory(holder, 54, codec.color( + languages.text(allowTake ? "gem.gui.admin" : "gem.gui.player", + allowTake ? "&8&l管理员-宝石列表" : "&8&l宝石预览"))); + holder.inventory = inventory; + Map entries = new LinkedHashMap<>(); + int start = page.index() * PAGE_SIZE; + for (int slot = 0; slot < PAGE_SIZE && start + slot < groups.size(); slot++) { + Map.Entry> group = groups.get(start + slot); + entries.put(slot, group.getKey()); + inventory.setItem(slot, groupIcon(group.getKey(), group.getValue())); + } + holder.entries = Map.copyOf(entries); + controls(inventory, page); + player.openInventory(inventory); + } + + private void openGroup(Player player, boolean allowTake, String groupName, int requestedPage) { + List gems = registry.groups().get(groupName); + if (gems == null) { openGroups(player, allowTake, 0); return; } + Page page = page(requestedPage, gems.size()); + BrowserHolder holder = new BrowserHolder(allowTake, page.index(), groupName); + Inventory inventory = Bukkit.createInventory(holder, 54, + codec.color("&8" + groupName + " &7" + (page.index() + 1) + '/' + page.count())); + holder.inventory = inventory; + Map entries = new LinkedHashMap<>(); + int start = page.index() * PAGE_SIZE; + for (int slot = 0; slot < PAGE_SIZE && start + slot < gems.size(); slot++) { + GemDefinition gem = gems.get(start + slot); + entries.put(slot, gem.name()); + inventory.setItem(slot, gemIcon(gem, allowTake)); + } + holder.entries = Map.copyOf(entries); + controls(inventory, page); + inventory.setItem(49, button(Material.BARRIER, + languages.text("cmagic.gui.back", "&c返回配置文件列表"))); + player.openInventory(inventory); + } + + private Page page(int requested, int size) { + int pages = Math.max(1, (size + PAGE_SIZE - 1) / PAGE_SIZE); + return new Page(Math.max(0, Math.min(requested, pages - 1)), pages); + } + + private void controls(Inventory inventory, Page page) { + if (page.index() > 0) inventory.setItem(45, button(Material.ARROW, + languages.text("gem.gui.prev", "&e上一页"))); + inventory.setItem(49, button(Material.PAPER, languages.text("cmagic.gui.page", "&f第 {page} / {pages} 页", + "page", page.index() + 1, "pages", page.count()))); + if (page.index() + 1 < page.count()) inventory.setItem(53, button(Material.ARROW, + languages.text("gem.gui.next", "&e下一页"))); + } + + @EventHandler + public void onClick(InventoryClickEvent event) { + if (!(event.getView().getTopInventory().getHolder() instanceof BrowserHolder holder)) return; + event.setCancelled(true); + if (!(event.getWhoClicked() instanceof Player player) + || event.getClickedInventory() != event.getView().getTopInventory()) return; + int slot = event.getRawSlot(); + if (slot == 45) navigate(player, holder, holder.page - 1); + else if (slot == 53) navigate(player, holder, holder.page + 1); + else if (slot == 49 && holder.group != null) openGroups(player, holder.allowTake, 0); + else if (slot < PAGE_SIZE && holder.entries.containsKey(slot)) { + if (holder.group == null) openGroup(player, holder.allowTake, holder.entries.get(slot), 0); + else if (holder.allowTake) registry.find(holder.entries.get(slot)).ifPresent(gem -> { + int amount = Math.min(64, gem.material().getMaxStackSize()); + event.setCursor(codec.createGem(gem, amount)); + }); + } + } + + private void navigate(Player player, BrowserHolder holder, int page) { + if (holder.group == null) openGroups(player, holder.allowTake, page); + else openGroup(player, holder.allowTake, holder.group, page); + } + + private ItemStack groupIcon(String groupName, List gems) { + ItemStack item = codec.createGem(gems.getFirst(), Math.min(gems.size(), gems.getFirst().material().getMaxStackSize())); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(codec.color("&e" + groupName)); + List lore = meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + lore.add(codec.color(languages.text("cmagic.gui.gem_count", "&7宝石数量: &f{amount}", + "amount", gems.size()))); + lore.add(codec.color(languages.text("cmagic.gui.browse", "&a点击浏览"))); + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } + + private ItemStack gemIcon(GemDefinition gem, boolean internalInfo) { + ItemStack item = codec.createGem(gem, 1); + if (!internalInfo) return item; + ItemMeta meta = item.getItemMeta(); + List lore = meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + lore.add(codec.color("&8&m----------------")); + lore.add(codec.color(languages.text("gem.info.info", "&b宝石内部信息"))); + lore.add(codec.color("&7" + languages.text("gem.info.name", "名称") + ": &f" + gem.name())); + lore.add(codec.color("&7" + languages.text("gem.info.type", "类型") + ": &f" + gem.type())); + lore.add(codec.color("&7" + languages.text("gem.info.success", "成功率") + ": &f" + gem.successExpression())); + lore.add(codec.color("&7" + languages.text("common.upper", "上限") + ": &f" + gem.embedLimit())); + if (!gem.requirements().isEmpty()) lore.add(codec.color("&7" + languages.text("gem.info.types", "可强化物品列表") + + ": &f" + String.join(", ", gem.requirements()))); + if (!gem.allowedGuis().isEmpty()) lore.add(codec.color("&7" + languages.text("gem.info.guis", "可用界面") + + ": &f" + String.join(", ", gem.allowedGuis()))); + for (var reward : gem.rewards()) lore.add(codec.color("&7" + languages.text("gem.info.reward", "强化奖励") + + ": &f" + reward.source())); + for (var condition : gem.conditions()) lore.add(codec.color("&7" + languages.text("gem.info.condition", "强化条件") + + ": &f" + condition.source())); + lore.add(codec.color(languages.text("gem.info.get", "&a单击获得宝石实物"))); + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } + + private ItemStack button(Material material, String name) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(codec.color(name)); + item.setItemMeta(meta); + return item; + } + + private record Page(int index, int count) { } + + private static final class BrowserHolder implements InventoryHolder { + private final boolean allowTake; + private final int page; + private final String group; + private Map entries = Map.of(); + private Inventory inventory; + + private BrowserHolder(boolean allowTake, int page, String group) { + this.allowTake = allowTake; + this.page = page; + this.group = group; + } + + @Override + public Inventory getInventory() { + return inventory; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gui/GemGuiManager.java b/src/main/java/cn/infstar/cMagicGem/gui/GemGuiManager.java new file mode 100644 index 0000000..e0170bb --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gui/GemGuiManager.java @@ -0,0 +1,484 @@ +package cn.infstar.cmagicgem.gui; + +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.gem.GemService; +import cn.infstar.cmagicgem.gem.GemType; +import cn.infstar.cmagicgem.gem.GemUseResult; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.item.ItemMatcher; +import cn.infstar.cmagicgem.util.MessageDispatcher; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; + +public final class GemGuiManager implements Listener { + private final JavaPlugin plugin; + private final GemRegistry gems; + private final GemService service; + private final ItemCodec codec; + private final ItemMatcher matcher; + private final MessageDispatcher messages; + private final LanguageManager languages; + private volatile Map definitions = Map.of(); + private volatile Map locations = Map.of(); + + public GemGuiManager(JavaPlugin plugin, GemRegistry gems, GemService service, ItemCodec codec, ItemMatcher matcher, + MessageDispatcher messages, LanguageManager languages) { + this.plugin = plugin; + this.gems = gems; + this.service = service; + this.codec = codec; + this.matcher = matcher; + this.messages = messages; + this.languages = languages; + } + + public int reload() { + closeAll(); + File directory = new File(plugin.getDataFolder(), "gui"); + if (!directory.exists() && !directory.mkdirs()) throw new IllegalStateException("无法创建 gui 目录"); + Map loaded = new LinkedHashMap<>(); + Map blocks = new HashMap<>(); + File[] files = directory.listFiles((ignored, name) -> name.toLowerCase(Locale.ROOT).endsWith(".yml")); + if (files != null) for (File file : files) loadFile(file, loaded, blocks); + definitions = Collections.unmodifiableMap(loaded); + locations = Collections.unmodifiableMap(blocks); + return loaded.size(); + } + + private void loadFile(File file, Map loaded, Map blocks) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); + for (String name : yaml.getKeys(false)) { + ConfigurationSection section = yaml.getConfigurationSection(name); + if (section == null) continue; + try { + GuiDefinition definition = parse(file, name, section); + loaded.put(name.toLowerCase(Locale.ROOT), definition); + for (String location : section.getStringList("Locations")) { + String normalized = normalizeLocation(location); + if (normalized != null) blocks.put(normalized, definition.name()); + } + } catch (RuntimeException exception) { + plugin.getLogger().log(Level.SEVERE, "无法加载镶嵌台 " + file.getName() + '/' + name, exception); + } + } + } + + private GuiDefinition parse(File file, String name, ConfigurationSection section) { + List lines = section.getStringList("Slots"); + if (lines.isEmpty()) lines = List.of("XXXXXXXXX", "XEGGGGGGX", "XXXXXXXXX"); + int rows = Math.max(1, Math.min(6, lines.size())); + StringBuilder layout = new StringBuilder(rows * 9); + for (int row = 0; row < rows; row++) { + String line = lines.get(row); + layout.append(line, 0, Math.min(9, line.length())); + layout.append(" ".repeat(Math.max(0, 9 - line.length()))); + } + Map icons = new LinkedHashMap<>(); + ConfigurationSection items = section.getConfigurationSection("Items"); + if (items != null) for (String key : items.getKeys(false)) { + if (key.isEmpty()) continue; + ConfigurationSection icon = items.getConfigurationSection(key); + if (icon == null) continue; + icons.put(key.charAt(0), new GuiIcon(parseType(icon.getString("Type", "TIP")), + icon.getString("Material", "GRAY_STAINED_GLASS_PANE"), icon.getString("Display", " "), + icon.getStringList("Lore").isEmpty() ? icon.getStringList("Tips") : icon.getStringList("Lore"), + icon.getString("Texture"), ItemCodec.parseColor(icon.get("Color")), icon.getString("Effect"), + icon.getBoolean("Glow", false), icon.contains("CustomModelData") ? icon.getInt("CustomModelData") : null, + Math.max(1, icon.getInt("Amount", 1)), stringSet(icon, "Require"), + icon.getString("Gem"), icon.getString("Gui"))); + } + return new GuiDefinition(name, file, codec.color(section.getString("Title", "&8" + name)), + Math.max(1, section.getInt("Page", 1)), rows * 9, layout.toString(), Map.copyOf(icons)); + } + + private Set stringSet(ConfigurationSection section, String path) { + Object value = section.get(path); + if (value instanceof String text) return text.isBlank() ? Set.of() : Set.of(text); + if (value instanceof Collection collection) { + Set result = new LinkedHashSet<>(); + for (Object entry : collection) if (entry != null && !entry.toString().isBlank()) result.add(entry.toString()); + return Set.copyOf(result); + } + return Set.of(); + } + + private IconType parseType(String input) { + try { return IconType.valueOf(input.toUpperCase(Locale.ROOT)); } + catch (IllegalArgumentException exception) { return IconType.TIP; } + } + + public Collection names() { + return definitions.values().stream().map(GuiDefinition::name).toList(); + } + + public boolean open(Player player, String name) { + GuiDefinition definition = name == null || name.isBlank() ? defaultGui() : definitions.get(name.toLowerCase(Locale.ROOT)); + if (definition == null) return false; + open(player, definition, 0, null); + return true; + } + + private GuiDefinition defaultGui() { + GuiDefinition named = definitions.get("default"); + return named != null ? named : definitions.values().stream().findFirst().orElse(null); + } + + private void open(Player player, GuiDefinition definition, int page, ItemStack equipment) { + int selectedPage = Math.max(0, Math.min(page, definition.pages() - 1)); + WorkbenchHolder holder = new WorkbenchHolder(definition, selectedPage); + Inventory inventory = Bukkit.createInventory(holder, definition.size(), + definition.title().replace("%page%", Integer.toString(selectedPage + 1))); + holder.inventory = inventory; + render(holder, equipment); + player.openInventory(inventory); + } + + private void render(WorkbenchHolder holder, ItemStack equipment) { + holder.inventory.clear(); + holder.equipSlots.clear(); + holder.gemSlots.clear(); + holder.visibleGems.clear(); + Map> candidates = new HashMap<>(); + Map gemIndexes = new HashMap<>(); + boolean equipmentPlaced = false; + for (int slot = 0; slot < holder.definition.size(); slot++) { + char symbol = holder.definition.layout().charAt(slot); + GuiIcon icon = holder.definition.icons().get(symbol); + if (icon == null || icon.type() == IconType.EMPTY) continue; + switch (icon.type()) { + case EQUIP_SLOT -> { + holder.equipSlots.add(slot); + if (!equipmentPlaced && equipment != null && !equipment.getType().isAir()) { + holder.inventory.setItem(slot, equipment); + equipmentPlaced = true; + } else holder.inventory.setItem(slot, iconItem(icon)); + } + case GEM_SLOT -> { + holder.gemSlots.add(slot); + List matching = candidates.computeIfAbsent(symbol, + ignored -> eligibleGems(holder.definition, equipment, icon)); + int gemIndex = gemIndexes.computeIfAbsent(symbol, + ignored -> holder.page * countSymbol(holder.definition, symbol)); + gemIndexes.put(symbol, gemIndex + 1); + GemDefinition gem = gemIndex < matching.size() ? matching.get(gemIndex) : null; + if (gem != null) { + holder.visibleGems.put(slot, gem.name()); + holder.inventory.setItem(slot, codec.createGem(gem, icon.amount())); + } else holder.inventory.setItem(slot, iconItem(icon)); + } + case USE_GEM -> holder.inventory.setItem(slot, icon.gem() == null ? iconItem(icon) + : gems.find(icon.gem()).map(gem -> codec.createGem(gem, 1)).orElseGet(() -> iconItem(icon))); + case PAGE_TIP -> holder.inventory.setItem(slot, pageTip(icon, holder.page + 1, holder.definition.pages())); + default -> holder.inventory.setItem(slot, iconItem(icon)); + } + } + } + + private List eligibleGems(GuiDefinition definition, ItemStack equipment, GuiIcon icon) { + if (equipment == null || equipment.getType().isAir()) return List.of(); + return gems.all().stream() + .filter(gem -> !gem.giveRandomGem() && service.effectiveType(gem) == GemType.ITEM_GEM) + .filter(gem -> matcher.matches(equipment, gem.requirements())) + .filter(gem -> gem.allowedGuis().isEmpty() || gem.allowedGuis().stream() + .anyMatch(gui -> gui.equalsIgnoreCase(definition.name()))) + .filter(gem -> icon.require().isEmpty() || icon.require().stream() + .anyMatch(prefix -> gem.name().startsWith(prefix))) + .toList(); + } + + private int countSymbol(GuiDefinition definition, char symbol) { + int count = 0; + for (int slot = 0; slot < definition.size(); slot++) + if (definition.layout().charAt(slot) == symbol) count++; + return Math.max(1, count); + } + + private ItemStack iconItem(GuiIcon icon) { + try { + return codec.createItem(new ItemCodec.ItemAppearance(icon.material(), icon.amount(), icon.display(), icon.lore(), + icon.texture(), icon.color(), icon.effects(), icon.customModelData(), icon.glow())); + } catch (IllegalArgumentException ignored) { + return codec.createItem(new ItemCodec.ItemAppearance("GRAY_STAINED_GLASS_PANE", icon.amount(), + icon.display(), icon.lore(), null, null, null, icon.customModelData(), icon.glow())); + } + } + + private ItemStack pageTip(GuiIcon icon, int page, int pages) { + ItemStack item = iconItem(icon); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(codec.color(icon.display().replace("%page%", Integer.toString(page)) + .replace("%pages%", Integer.toString(pages))) + " " + page + '/' + pages); + item.setItemMeta(meta); + return item; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false) + public void onClick(InventoryClickEvent event) { + if (!(event.getView().getTopInventory().getHolder() instanceof WorkbenchHolder holder)) return; + if (!(event.getWhoClicked() instanceof Player player)) return; + if (event.getRawSlot() >= holder.inventory.getSize()) return; + event.setCancelled(true); + int slot = event.getRawSlot(); + GuiIcon icon = holder.definition.icons().get(holder.definition.layout().charAt(slot)); + if (icon == null) return; + if (icon.type() == IconType.EQUIP_SLOT) { + ItemStack current = equipment(holder); + ItemStack cursor = event.getCursor(); + if (cursor != null && !cursor.getType().isAir() && !icon.require().isEmpty() + && icon.require().stream().noneMatch(require -> matcher.matches(cursor, require))) { + player.sendMessage(codec.color(languages.text("cmagic.gui.slot_rejected", "&c该物品不能放入这个装备槽。"))); + return; + } + for (int equipSlot : holder.equipSlots) holder.inventory.setItem(equipSlot, null); + holder.inventory.setItem(slot, cursor == null || cursor.getType().isAir() ? null : cursor.clone()); + event.setCursor(current == null || isPlaceholder(holder, current) ? null : current); + refresh(holder); + return; + } + switch (icon.type()) { + case GEM_SLOT -> gemSlot(player, holder, slot, event); + case USE_GEM -> useGem(player, holder, icon); + case PAGE_PREV -> navigate(player, holder, holder.definition, holder.page - 1); + case PAGE_NEXT -> navigate(player, holder, holder.definition, holder.page + 1); + case PAGE_JUMP -> { + GuiDefinition target = icon.gui() == null ? null : definitions.get(icon.gui().toLowerCase(Locale.ROOT)); + if (target != null) navigate(player, holder, target, 0); + } + default -> { } + } + } + + private void gemSlot(Player player, WorkbenchHolder holder, int slot, InventoryClickEvent event) { + ItemStack equipment = equipment(holder); + if (equipment == null || equipment.getType().isAir()) { + player.sendMessage(codec.color(languages.text("cmagic.gui.equipment_required", "&c请先放入装备。"))); + return; + } + if (event.isRightClick()) { + String gemName = holder.visibleGems.get(slot); + if (gemName == null || codec.embedded(equipment).getOrDefault(gemName, 0) <= 0) return; + GemUseResult result = service.remove(player, gemName, equipment); + if (result.successful() || result.target() != equipment) setEquipment(holder, result.target()); + messages.send(player, result.message()); + refresh(holder); + return; + } + ItemStack cursor = event.getCursor(); + GemDefinition gem = codec.readGem(cursor, true).orElse(null); + if (gem == null || gem.giveRandomGem() || service.effectiveType(gem) != GemType.ITEM_GEM) return; + String visible = holder.visibleGems.get(slot); + if (visible != null && !visible.equalsIgnoreCase(gem.name())) return; + GemUseResult result = service.use(player, gem, equipment); + if (result.consumed()) consumeCursor(event); + if (result.target() != null) setEquipment(holder, result.target()); + messages.send(player, result.message()); + refresh(holder); + } + + private void useGem(Player player, WorkbenchHolder holder, GuiIcon icon) { + GemDefinition gem = gems.find(icon.gem()).orElse(null); + if (gem == null) return; + boolean itemGem = !gem.giveRandomGem() && service.effectiveType(gem) == GemType.ITEM_GEM; + ItemStack target = itemGem ? equipment(holder) : null; + if (itemGem && (target == null || target.getType().isAir())) { + player.sendMessage(codec.color(languages.text("cmagic.gui.equipment_required", "&c请先放入装备。"))); + return; + } + GemUseResult result = service.use(player, gem, target); + if (result.target() != null && itemGem) setEquipment(holder, result.target()); + messages.send(player, result.message()); + refresh(holder); + } + + private void consumeCursor(InventoryClickEvent event) { + ItemStack cursor = event.getCursor(); + if (cursor.getAmount() <= 1) event.setCursor(null); + else { + ItemStack changed = cursor.clone(); + changed.setAmount(cursor.getAmount() - 1); + event.setCursor(changed); + } + } + + private void refresh(WorkbenchHolder holder) { + ItemStack equipment = equipment(holder); + render(holder, equipment == null || isPlaceholder(holder, equipment) ? null : equipment); + } + + private ItemStack equipment(WorkbenchHolder holder) { + for (int slot : holder.equipSlots) { + ItemStack item = holder.inventory.getItem(slot); + if (item != null && !item.getType().isAir() && !isPlaceholder(holder, item)) return item; + } + return null; + } + + private boolean isPlaceholder(WorkbenchHolder holder, ItemStack item) { + for (int slot : holder.equipSlots) { + GuiIcon icon = holder.definition.icons().get(holder.definition.layout().charAt(slot)); + if (icon != null && item.isSimilar(iconItem(icon))) return true; + } + return false; + } + + private void setEquipment(WorkbenchHolder holder, ItemStack item) { + if (holder.equipSlots.isEmpty()) return; + holder.inventory.setItem(holder.equipSlots.iterator().next(), item); + } + + private void navigate(Player player, WorkbenchHolder holder, GuiDefinition target, int page) { + ItemStack equipment = equipment(holder); + holder.returned = true; + for (int slot : holder.equipSlots) holder.inventory.setItem(slot, null); + open(player, target, Math.max(0, Math.min(page, target.pages() - 1)), equipment); + } + + @EventHandler(ignoreCancelled = false) + public void onDrag(InventoryDragEvent event) { + if (!(event.getView().getTopInventory().getHolder() instanceof WorkbenchHolder holder)) return; + for (int slot : event.getRawSlots()) if (slot < holder.inventory.getSize()) { + event.setCancelled(true); + return; + } + } + + @EventHandler + public void onClose(InventoryCloseEvent event) { + if (!(event.getInventory().getHolder() instanceof WorkbenchHolder holder) || holder.returned) return; + holder.returned = true; + ItemStack equipment = equipment(holder); + if (equipment == null) return; + for (int slot : holder.equipSlots) holder.inventory.setItem(slot, null); + Player player = (Player) event.getPlayer(); + player.getInventory().addItem(equipment).values() + .forEach(left -> player.getWorld().dropItemNaturally(player.getLocation(), left)); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockUse(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock() == null) return; + String gui = locations.get(locationKey(event.getClickedBlock().getLocation())); + if (gui == null) return; + event.setCancelled(true); + open(event.getPlayer(), gui); + } + + @EventHandler(ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + if (locations.containsKey(locationKey(event.getBlock().getLocation()))) event.setCancelled(true); + } + + public boolean addBlock(String guiName, Location location) { + GuiDefinition definition = definitions.get(guiName.toLowerCase(Locale.ROOT)); + if (definition == null || location.getBlock().getType().isAir() + || locations.containsKey(locationKey(location))) return false; + return updateLocation(definition, location, true); + } + + public boolean deleteBlock(Location location) { + String name = locations.get(locationKey(location)); + if (name == null) return false; + GuiDefinition definition = definitions.get(name.toLowerCase(Locale.ROOT)); + return definition != null && updateLocation(definition, location, false); + } + + private boolean updateLocation(GuiDefinition definition, Location location, boolean add) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(definition.file()); + String path = definition.name() + ".Locations"; + List values = new ArrayList<>(yaml.getStringList(path)); + String encoded = locationKey(location); + values.removeIf(value -> encoded.equals(normalizeLocation(value))); + if (add) values.add(encoded); + yaml.set(path, values); + try { + yaml.save(definition.file()); + reload(); + return true; + } catch (IOException exception) { + plugin.getLogger().log(Level.SEVERE, "保存镶嵌台位置失败", exception); + return false; + } + } + + private String normalizeLocation(String value) { + if (value == null) return null; + String[] parts = value.trim().split("[,;:]"); + if (parts.length < 4) return null; + try { return parts[0] + ',' + Integer.parseInt(parts[1]) + ',' + Integer.parseInt(parts[2]) + ',' + Integer.parseInt(parts[3]); } + catch (NumberFormatException exception) { return null; } + } + + private String locationKey(Location location) { + return location.getWorld().getName() + ',' + location.getBlockX() + ',' + location.getBlockY() + ',' + location.getBlockZ(); + } + + private void closeAll() { + for (Player player : Bukkit.getOnlinePlayers()) + if (player.getOpenInventory().getTopInventory().getHolder() instanceof WorkbenchHolder) player.closeInventory(); + } + + private enum IconType { TIP, EMPTY, EQUIP_SLOT, GEM_SLOT, USE_GEM, PAGE_JUMP, PAGE_PREV, PAGE_NEXT, PAGE_TIP } + + private record GuiIcon(IconType type, String material, String display, List lore, + String texture, Integer color, String effects, boolean glow, + Integer customModelData, int amount, Set require, String gem, String gui) { } + + private record GuiDefinition(String name, File file, String title, int pages, int size, String layout, + Map icons) { } + + private static final class WorkbenchHolder implements GemWorkbenchHolder { + private final GuiDefinition definition; + private final int page; + private final Set equipSlots = new LinkedHashSet<>(); + private final Set gemSlots = new LinkedHashSet<>(); + private final Map visibleGems = new HashMap<>(); + private Inventory inventory; + private boolean returned; + + private WorkbenchHolder(GuiDefinition definition, int page) { + this.definition = definition; + this.page = page; + } + + @Override + public Inventory getInventory() { + return inventory; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/gui/GemWorkbenchHolder.java b/src/main/java/cn/infstar/cMagicGem/gui/GemWorkbenchHolder.java new file mode 100644 index 0000000..a1dad14 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/gui/GemWorkbenchHolder.java @@ -0,0 +1,6 @@ +package cn.infstar.cmagicgem.gui; + +import org.bukkit.inventory.InventoryHolder; + +public interface GemWorkbenchHolder extends InventoryHolder { +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/EconomyBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/EconomyBridge.java new file mode 100644 index 0000000..2d6c9aa --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/EconomyBridge.java @@ -0,0 +1,60 @@ +package cn.infstar.cmagicgem.integration; + +import org.bukkit.OfflinePlayer; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Method; + +public final class EconomyBridge { + private final JavaPlugin plugin; + private Object provider; + private Method getBalance; + private Method deposit; + private Method withdraw; + + public EconomyBridge(JavaPlugin plugin) { + this.plugin = plugin; + refresh(); + } + + public void refresh() { + provider = null; + try { + Class economy = Class.forName("net.milkbowl.vault.economy.Economy"); + RegisteredServiceProvider registration = plugin.getServer().getServicesManager().getRegistration(economy); + if (registration == null) return; + provider = registration.getProvider(); + getBalance = economy.getMethod("getBalance", OfflinePlayer.class); + deposit = economy.getMethod("depositPlayer", OfflinePlayer.class, double.class); + withdraw = economy.getMethod("withdrawPlayer", OfflinePlayer.class, double.class); + } catch (ReflectiveOperationException ignored) { + } + } + + public boolean available() { + return provider != null; + } + + public String status() { + return available() ? provider.getClass().getName() : "unavailable"; + } + + public double balance(OfflinePlayer player) { + if (!available()) return 0; + try { return ((Number) getBalance.invoke(provider, player)).doubleValue(); } + catch (ReflectiveOperationException exception) { return 0; } + } + + public boolean change(OfflinePlayer player, double amount) { + if (!available()) return false; + if (amount < 0 && balance(player) + amount < 0) return false; + try { + Object response = (amount >= 0 ? deposit : withdraw).invoke(provider, player, Math.abs(amount)); + Method successful = response.getClass().getMethod("transactionSuccess"); + return (boolean) successful.invoke(response); + } catch (ReflectiveOperationException exception) { + return false; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/MMOItemsBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/MMOItemsBridge.java new file mode 100644 index 0000000..b645d18 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/MMOItemsBridge.java @@ -0,0 +1,62 @@ +package cn.infstar.cmagicgem.integration; + +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Locale; + +public final class MMOItemsBridge { + private final JavaPlugin plugin; + + public MMOItemsBridge(JavaPlugin plugin) { + this.plugin = plugin; + } + + public ItemStack rebuildIfMMOItem(ItemStack source) { + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("MMOItems"); + if (dependency == null || !dependency.isEnabled()) return source; + try { + ClassLoader loader = dependency.getClass().getClassLoader(); + Class mmoItems = Class.forName("net.Indyuce.mmoitems.MMOItems", true, loader); + Method getId = mmoItems.getMethod("getID", ItemStack.class); + Object id = getId.invoke(null, source); + if (!(id instanceof String value) || value.isBlank()) return source; + + ReadWriteNBT customData = cmagicData(source); + ItemMeta oldMeta = source.getItemMeta(); + Class liveType = Class.forName("net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem", true, loader); + Constructor constructor = liveType.getConstructor(ItemStack.class); + Object liveItem = constructor.newInstance(source); + Object builder = liveType.getMethod("newBuilder").invoke(liveItem); + ItemStack rebuilt = (ItemStack) builder.getClass().getMethod("build").invoke(builder); + rebuilt.setAmount(source.getAmount()); + + ItemMeta rebuiltMeta = rebuilt.getItemMeta(); + oldMeta.getPersistentDataContainer().copyTo(rebuiltMeta.getPersistentDataContainer(), true); + rebuilt.setItemMeta(rebuiltMeta); + NBT.modify(rebuilt, nbt -> { nbt.mergeCompound(customData); }); + return rebuilt; + } catch (ReflectiveOperationException | LinkageError | RuntimeException exception) { + throw new IllegalStateException("MMOItems 物品重建失败: " + exception.getMessage(), exception); + } + } + + private ReadWriteNBT cmagicData(ItemStack source) { + return NBT.get(source, nbt -> { + ReadWriteNBT copy = NBT.createNBTObject(); + copy.mergeCompound(nbt); + for (String key : new ArrayList<>(copy.getKeys())) { + String normalized = key.toUpperCase(Locale.ROOT); + if (!normalized.startsWith("CMAGICGEM_") && !normalized.equals("MYTHIC_TYPE")) copy.removeKey(key); + } + return copy; + }); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/MythicBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/MythicBridge.java new file mode 100644 index 0000000..726a460 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/MythicBridge.java @@ -0,0 +1,185 @@ +package cn.infstar.cmagicgem.integration; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.event.Event; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.Location; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.item.ItemCodec; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public final class MythicBridge { + private final JavaPlugin plugin; + private Object itemManager; + private Method getItemStack; + private Object apiHelper; + private Method castSkill; + private InlineMechanicExecutor inlineExecutor; + private boolean extensionsEnabled; + + public MythicBridge(JavaPlugin plugin) { + this.plugin = plugin; + refresh(); + } + + public void refresh() { + itemManager = null; + getItemStack = null; + apiHelper = null; + castSkill = null; + inlineExecutor = null; + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("MythicMobs"); + if (dependency == null || !dependency.isEnabled()) return; + for (String apiName : new String[]{"io.lumine.mythic.bukkit.MythicBukkit", "io.lumine.xikage.mythicmobs.MythicMobs"}) { + try { + Class apiClass = Class.forName(apiName, true, dependency.getClass().getClassLoader()); + Object api = apiClass.getMethod("inst").invoke(null); + itemManager = api.getClass().getMethod("getItemManager").invoke(api); + getItemStack = itemManager.getClass().getMethod("getItemStack", String.class); + try { + apiHelper = api.getClass().getMethod("getAPIHelper").invoke(api); + for (Method method : apiHelper.getClass().getMethods()) { + if (!method.getName().equals("castSkill") || method.getParameterCount() < 2) continue; + Class[] parameters = method.getParameterTypes(); + if (Entity.class.isAssignableFrom(parameters[0]) && parameters[1] == String.class) { + if (isBetterCastMethod(method, castSkill)) + castSkill = method; + } + } + } catch (ReflectiveOperationException ignored) { + } + return; + } catch (ReflectiveOperationException ignored) { + } + } + plugin.getLogger().warning("MythicMobs 已安装,但无法连接物品 API"); + } + + public boolean available() { + return itemManager != null && getItemStack != null; + } + + public boolean skillApiAvailable() { + return apiHelper != null && castSkill != null; + } + + public String status() { + if (!available()) return "unavailable"; + return "items=" + itemManager.getClass().getName() + ", skillApi=" + skillApiAvailable(); + } + + public Optional item(String name) { + if (!available()) return Optional.empty(); + try { + Object value = getItemStack.invoke(itemManager, name); + if (value instanceof Optional optional) value = optional.orElse(null); + return value instanceof ItemStack item ? Optional.of(item.clone()) : Optional.empty(); + } catch (ReflectiveOperationException exception) { + return Optional.empty(); + } + } + + public boolean cast(Player caster, String skill, Entity target) { + return cast(caster, skill, target, target == null ? caster.getLocation() : target.getLocation()); + } + + public boolean cast(Player caster, String skill, Entity target, Location location) { + if (apiHelper == null || castSkill == null) return false; + try { + Object[] arguments = new Object[castSkill.getParameterCount()]; + Class[] types = castSkill.getParameterTypes(); + arguments[0] = caster; + arguments[1] = skill; + int collectionIndex = 0; + for (int index = 2; index < arguments.length; index++) { + if (Entity.class.isAssignableFrom(types[index])) arguments[index] = target == null ? caster : target; + else if (Location.class.isAssignableFrom(types[index])) arguments[index] = location; + else if (Collection.class.isAssignableFrom(types[index])) { + arguments[index] = collectionIndex++ == 0 + ? (target == null ? List.of() : List.of(target)) + : (location == null ? List.of() : List.of(location)); + } + else if (types[index] == float.class || types[index] == Float.class) arguments[index] = 1.0F; + else if (types[index] == double.class || types[index] == Double.class) arguments[index] = 1.0D; + else if (types[index] == boolean.class || types[index] == Boolean.class) arguments[index] = false; + else arguments[index] = null; + } + Object result = castSkill.invoke(apiHelper, arguments); + return !(result instanceof Boolean success) || success; + } catch (ReflectiveOperationException | IllegalArgumentException exception) { + return false; + } + } + + public boolean castInline(Player caster, String mechanicLine, Entity target, Location location) { + return inlineExecutor != null && inlineExecutor.castInline(caster, mechanicLine, target, location); + } + + public void enableExtensions(GemRegistry gems, ItemCodec items, ProtectionBridge protection, + MythicDeathHandler deathHandler) { + if (extensionsEnabled) return; + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("MythicMobs"); + if (dependency == null || !dependency.isEnabled()) return; + String adapter = hasClass(dependency, "io.lumine.mythic.bukkit.MythicBukkit") + ? "cn.infstar.cmagicgem.integration.mythic.Mythic5Adapter" + : "cn.infstar.cmagicgem.integration.mythic.Mythic4Adapter"; + try { + Class type = Class.forName(adapter, true, plugin.getClass().getClassLoader()); + Object instance = type.getConstructor(JavaPlugin.class, GemRegistry.class, ItemCodec.class, + ProtectionBridge.class, MythicDeathHandler.class) + .newInstance(plugin, gems, items, protection, deathHandler); + type.getMethod("register").invoke(instance); + if (instance instanceof InlineMechanicExecutor executor) inlineExecutor = executor; + extensionsEnabled = true; + if (plugin.getConfig().getBoolean("integrations.mythicmobs.reload-on-enable", true)) { + plugin.getServer().getScheduler().runTask(plugin, () -> { + if (!plugin.getServer().getPluginManager().isPluginEnabled("MythicMobs")) return; + boolean dispatched = plugin.getServer().dispatchCommand( + plugin.getServer().getConsoleSender(), "mm reload"); + if (!dispatched) plugin.getLogger().warning("无法自动重新加载 MythicMobs 扩展配置,请手动执行 /mm reload"); + }); + } + } catch (ReflectiveOperationException | LinkageError exception) { + plugin.getLogger().warning("无法加载 MythicMobs 扩展适配器: " + exception.getMessage()); + } + } + + private boolean hasClass(Plugin dependency, String name) { + try { + Class.forName(name, false, dependency.getClass().getClassLoader()); + return true; + } catch (ClassNotFoundException ignored) { + return false; + } + } + + private boolean isBetterCastMethod(Method candidate, Method current) { + if (current == null) return true; + boolean candidateHasConsumer = hasConsumer(candidate); + boolean currentHasConsumer = hasConsumer(current); + if (candidateHasConsumer != currentHasConsumer) return !candidateHasConsumer; + return candidate.getParameterCount() > current.getParameterCount(); + } + + private boolean hasConsumer(Method method) { + for (Class type : method.getParameterTypes()) + if (java.util.function.Consumer.class.isAssignableFrom(type)) return true; + return false; + } + + @FunctionalInterface + public interface MythicDeathHandler { + void onDeath(Player killer, String mobId, Entity mob, Event event); + } + + public interface InlineMechanicExecutor { + boolean castInline(Player caster, String mechanicLine, Entity target, Location location); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/PlaceholderBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/PlaceholderBridge.java new file mode 100644 index 0000000..5b18f7e --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/PlaceholderBridge.java @@ -0,0 +1,46 @@ +package cn.infstar.cmagicgem.integration; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Method; + +public final class PlaceholderBridge { + private final JavaPlugin plugin; + private Method setPlaceholders; + + public PlaceholderBridge(JavaPlugin plugin) { + this.plugin = plugin; + refresh(); + } + + public void refresh() { + setPlaceholders = null; + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("PlaceholderAPI"); + if (dependency == null || !dependency.isEnabled()) return; + try { + Class api = Class.forName("me.clip.placeholderapi.PlaceholderAPI", true, dependency.getClass().getClassLoader()); + setPlaceholders = api.getMethod("setPlaceholders", Player.class, String.class); + } catch (ReflectiveOperationException exception) { + plugin.getLogger().warning("PlaceholderAPI 存在但接口不可用: " + exception.getMessage()); + } + } + + public String resolve(Player player, String input) { + if (setPlaceholders == null || player == null || input == null) return input; + try { + return (String) setPlaceholders.invoke(null, player, input); + } catch (ReflectiveOperationException exception) { + return input; + } + } + + public boolean available() { + return setPlaceholders != null; + } + + public String status() { + return available() ? setPlaceholders.toGenericString() : "unavailable"; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/PlayerPointsBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/PlayerPointsBridge.java new file mode 100644 index 0000000..5950405 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/PlayerPointsBridge.java @@ -0,0 +1,65 @@ +package cn.infstar.cmagicgem.integration; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Method; +import java.util.UUID; + +public final class PlayerPointsBridge { + private final JavaPlugin plugin; + private Object api; + private Method look; + private Method give; + private Method take; + + public PlayerPointsBridge(JavaPlugin plugin) { + this.plugin = plugin; + refresh(); + } + + public void refresh() { + api = null; + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("PlayerPoints"); + if (dependency == null || !dependency.isEnabled()) return; + try { + api = dependency.getClass().getMethod("getAPI").invoke(dependency); + look = api.getClass().getMethod("look", UUID.class); + give = api.getClass().getMethod("give", UUID.class, int.class); + take = api.getClass().getMethod("take", UUID.class, int.class); + } catch (ReflectiveOperationException exception) { + api = null; + plugin.getLogger().warning("PlayerPoints 存在但接口不可用: " + exception.getMessage()); + } + } + + public boolean available() { + return api != null; + } + + public String status() { + return available() ? api.getClass().getName() : "unavailable"; + } + + public boolean change(Player player, int amount) { + if (!available()) return false; + try { + int balance = ((Number) look.invoke(api, player.getUniqueId())).intValue(); + if (amount < 0 && balance + amount < 0) return false; + Object result = (amount >= 0 ? give : take).invoke(api, player.getUniqueId(), Math.abs(amount)); + return !(result instanceof Boolean value) || value; + } catch (ReflectiveOperationException exception) { + return false; + } + } + + public int balance(Player player) { + if (!available()) return 0; + try { + return ((Number) look.invoke(api, player.getUniqueId())).intValue(); + } catch (ReflectiveOperationException exception) { + return 0; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/ProtectionBridge.java b/src/main/java/cn/infstar/cMagicGem/integration/ProtectionBridge.java new file mode 100644 index 0000000..fa437ad --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/ProtectionBridge.java @@ -0,0 +1,100 @@ +package cn.infstar.cmagicgem.integration; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.lang.reflect.Method; + +public final class ProtectionBridge { + private final JavaPlugin plugin; + + public ProtectionBridge(JavaPlugin plugin) { + this.plugin = plugin; + } + + public boolean pvpEnabled(Player player) { + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("PvPManager"); + if (dependency == null || !dependency.isEnabled()) return false; + try { + Object source = dependency; + for (String accessor : new String[]{"getPlayerHandler", "getPvPPlayerManager", "getPlayerManager"}) { + Method method = find(source.getClass(), accessor); + if (method != null) { source = method.invoke(source); break; } + } + Object pvpPlayer = source; + for (String accessor : new String[]{"get", "getPlayer", "getPvPPlayer"}) { + Method method = find(source.getClass(), accessor, Player.class); + if (method != null) { pvpPlayer = method.invoke(source, player); break; } + } + for (String name : new String[]{"hasPvPEnabled", "isPvPEnabled", "isPvpEnabled", "hasPvpEnabled"}) { + Method method = find(pvpPlayer.getClass(), name); + if (method != null) return (boolean) method.invoke(pvpPlayer); + } + } catch (ReflectiveOperationException | RuntimeException ignored) { + } + return false; + } + + public boolean residenceFlag(Player player, Location location, String flag) { + return residenceFlag((Entity) player, location, flag); + } + + public boolean residenceFlag(Entity entity, Location location, String flag) { + Plugin dependency = plugin.getServer().getPluginManager().getPlugin("Residence"); + if (dependency == null || !dependency.isEnabled()) return false; + try { + Method disabledWorld = find(dependency.getClass(), "isDisabledWorldListener", org.bukkit.World.class); + if (disabledWorld != null && (boolean) disabledWorld.invoke(dependency, location.getWorld())) return true; + Object directPermissions = residencePermissions(dependency, entity, location); + Boolean directResult = permissionValue(directPermissions, entity instanceof Player player ? player : null, flag); + if (directResult != null) return directResult; + Object manager = dependency.getClass().getMethod("getResidenceManager").invoke(dependency); + Object residence = manager.getClass().getMethod("getByLoc", Location.class).invoke(manager, location); + if (residence == null) return true; + Object permissions = residence.getClass().getMethod("getPermissions").invoke(residence); + Boolean result = permissionValue(permissions, entity instanceof Player player ? player : null, flag); + if (result != null) return result; + } catch (ReflectiveOperationException | RuntimeException ignored) { + } + return false; + } + + private Object residencePermissions(Plugin dependency, Entity entity, Location location) + throws ReflectiveOperationException { + if (entity instanceof Player player) { + Method method = find(dependency.getClass(), "getPermsByLocForPlayer", Location.class, Player.class); + if (method != null) return method.invoke(dependency, location, player); + } + Method method = find(dependency.getClass(), "getPermsByLoc", Location.class); + return method == null ? null : method.invoke(dependency, location); + } + + private Boolean permissionValue(Object permissions, Player player, String flag) throws ReflectiveOperationException { + if (permissions == null) return null; + for (Method method : permissions.getClass().getMethods()) { + if (player == null && !method.getName().equals("has")) continue; + if (player != null && !method.getName().equals("playerHas") && !method.getName().equals("has")) continue; + Object[] arguments = new Object[method.getParameterCount()]; + Class[] types = method.getParameterTypes(); + boolean compatible = true; + for (int index = 0; index < arguments.length; index++) { + if (Player.class.isAssignableFrom(types[index]) && player != null) arguments[index] = player; + else if (types[index] == String.class) arguments[index] = flag; + else if (types[index] == boolean.class || types[index] == Boolean.class) arguments[index] = true; + else { compatible = false; break; } + } + if (!compatible) continue; + Object result = method.invoke(permissions, arguments); + if (result instanceof Boolean allowed) return allowed; + } + return null; + } + + private Method find(Class type, String name, Class... parameters) { + try { return type.getMethod(name, parameters); } + catch (NoSuchMethodException ignored) { return null; } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic4Adapter.java b/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic4Adapter.java new file mode 100644 index 0000000..003480a --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic4Adapter.java @@ -0,0 +1,189 @@ +package cn.infstar.cmagicgem.integration.mythic; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.integration.MythicBridge.MythicDeathHandler; +import cn.infstar.cmagicgem.integration.MythicBridge.InlineMechanicExecutor; +import cn.infstar.cmagicgem.integration.ProtectionBridge; +import cn.infstar.cmagicgem.item.ItemCodec; +import io.lumine.xikage.mythicmobs.adapters.AbstractEntity; +import io.lumine.xikage.mythicmobs.adapters.AbstractItemStack; +import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitAdapter; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicConditionLoadEvent; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicDropLoadEvent; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicMechanicLoadEvent; +import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicMobDeathEvent; +import io.lumine.xikage.mythicmobs.drops.Drop; +import io.lumine.xikage.mythicmobs.drops.DropMetadata; +import io.lumine.xikage.mythicmobs.drops.IItemDrop; +import io.lumine.xikage.mythicmobs.io.MythicLineConfig; +import io.lumine.xikage.mythicmobs.skills.ITargetedEntitySkill; +import io.lumine.xikage.mythicmobs.skills.Skill; +import io.lumine.xikage.mythicmobs.skills.SkillCaster; +import io.lumine.xikage.mythicmobs.skills.SkillCondition; +import io.lumine.xikage.mythicmobs.skills.SkillMechanic; +import io.lumine.xikage.mythicmobs.skills.SkillMetadata; +import io.lumine.xikage.mythicmobs.skills.SkillTrigger; +import io.lumine.xikage.mythicmobs.skills.conditions.IEntityCondition; +import io.lumine.xikage.mythicmobs.skills.conditions.ICasterCondition; +import io.lumine.xikage.mythicmobs.mobs.GenericCaster; +import org.bukkit.Location; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public final class Mythic4Adapter implements Listener, InlineMechanicExecutor { + private final JavaPlugin plugin; + private final GemRegistry gems; + private final ItemCodec items; + private final ProtectionBridge protection; + private final MythicDeathHandler deathHandler; + private final Map inlineSkills = new ConcurrentHashMap<>(); + + public Mythic4Adapter(JavaPlugin plugin, GemRegistry gems, ItemCodec items, ProtectionBridge protection, + MythicDeathHandler deathHandler) { + this.plugin = plugin; + this.gems = gems; + this.items = items; + this.protection = protection; + this.deathHandler = deathHandler; + } + + public void register() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean castInline(Player caster, String mechanicLine, Entity target, Location location) { + if (mechanicLine == null || mechanicLine.isBlank()) return false; + try { + Skill skill = inlineSkills.computeIfAbsent(mechanicLine, line -> new Skill(List.of(line))); + AbstractEntity casterEntity = BukkitAdapter.adapt(caster); + AbstractEntity trigger = BukkitAdapter.adapt(target == null ? caster : target); + SkillCaster skillCaster = new GenericCaster(casterEntity); + HashSet entities = new HashSet<>(); + if (target != null) entities.add(BukkitAdapter.adapt(target)); + HashSet locations = new HashSet<>(); + Location origin = location == null ? caster.getLocation() : location; + if (location != null) locations.add(BukkitAdapter.adapt(location)); + SkillMetadata metadata = new SkillMetadata(SkillTrigger.API, skillCaster, trigger, + BukkitAdapter.adapt(origin), entities, locations, 1.0F); + if (!skill.usable(metadata, SkillTrigger.API)) return false; + skill.execute(metadata); + return true; + } catch (RuntimeException | LinkageError exception) { + plugin.getLogger().warning("无法执行 MythicMobs 4 内联机制 " + mechanicLine + ": " + exception.getMessage()); + return false; + } + } + + @EventHandler + public void onDropLoad(MythicDropLoadEvent event) { + if (!event.getDropName().equalsIgnoreCase("gem")) return; + MythicLineConfig config = event.getConfig(); + String name = config.getString(new String[]{"name", "n"}, ""); + GemDefinition gem = gems.find(name).orElse(null); + if (gem != null) event.register(new GemDrop(config.getLine(), config, gem, items)); + } + + @EventHandler + public void onMechanicLoad(MythicMechanicLoadEvent event) { + String name = normalize(event.getMechanicName()); + if (name.equals("shielddamage") || name.equals("sheilddamage")) + event.register(new ShieldDamage(event.getConfig().getLine(), event.getConfig())); + } + + @EventHandler + public void onConditionLoad(MythicConditionLoadEvent event) { + String name = normalize(event.getConditionName()); + switch (name) { + case "noarmorstand" -> event.register(new NoArmorStand(event.getConfig().getLine())); + case "pvpmanager" -> event.register(new Pvp(event.getConfig().getLine(), protection)); + case "residenceflag", "redidenseflag" -> event.register(new Residence(event.getConfig().getLine(), event.getConfig(), protection)); + default -> { } + } + } + + @EventHandler + public void onMobDeath(MythicMobDeathEvent event) { + if (event.getKiller() instanceof Player player) + deathHandler.onDeath(player, event.getMobType().getInternalName(), event.getEntity(), event); + } + + private static String normalize(String input) { + return input.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + } + + private static final class GemDrop extends Drop implements IItemDrop { + private final GemDefinition gem; + private final ItemCodec items; + + private GemDrop(String line, MythicLineConfig config, GemDefinition gem, ItemCodec items) { + super(line, config); + this.gem = gem; + this.items = items; + } + + @Override + public AbstractItemStack getDrop(DropMetadata metadata) { + return BukkitAdapter.adapt(items.createGem(gem, Math.max(1, (int) Math.round(getAmount())))); + } + } + + private static final class ShieldDamage extends SkillMechanic implements ITargetedEntitySkill { + private final double damage; + + private ShieldDamage(String line, MythicLineConfig config) { + super(line, config); + damage = config.getDouble(new String[]{"damage", "d", "amount"}, 0); + } + + @Override + public boolean castAtEntity(SkillMetadata metadata, AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + if (!(entity instanceof LivingEntity living)) return false; + living.setAbsorptionAmount(Math.max(0, living.getAbsorptionAmount() - Math.max(0, damage))); + return true; + } + } + + private static final class NoArmorStand extends SkillCondition implements IEntityCondition { + private NoArmorStand(String line) { super(line); } + @Override public boolean check(AbstractEntity target) { return !(BukkitAdapter.adapt(target) instanceof ArmorStand); } + } + + private static final class Pvp extends SkillCondition implements IEntityCondition, ICasterCondition { + private final ProtectionBridge protection; + private Pvp(String line, ProtectionBridge protection) { super(line); this.protection = protection; } + @Override public boolean check(AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + return entity instanceof Player player && protection.pvpEnabled(player); + } + @Override public boolean check(SkillCaster caster) { return check(caster.getEntity()); } + } + + private static final class Residence extends SkillCondition implements IEntityCondition, ICasterCondition { + private final ProtectionBridge protection; + private final String flag; + private Residence(String line, MythicLineConfig config, ProtectionBridge protection) { + super(line); + this.protection = protection; + flag = config.getString(new String[]{"flag", "f"}, ""); + } + @Override public boolean check(AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + return protection.residenceFlag(entity, entity.getLocation(), flag); + } + @Override public boolean check(SkillCaster caster) { return check(caster.getEntity()); } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic5Adapter.java b/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic5Adapter.java new file mode 100644 index 0000000..8964bf3 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/integration/mythic/Mythic5Adapter.java @@ -0,0 +1,177 @@ +package cn.infstar.cmagicgem.integration.mythic; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.integration.MythicBridge.MythicDeathHandler; +import cn.infstar.cmagicgem.integration.MythicBridge.InlineMechanicExecutor; +import cn.infstar.cmagicgem.integration.ProtectionBridge; +import cn.infstar.cmagicgem.item.ItemCodec; +import io.lumine.mythic.api.adapters.AbstractEntity; +import io.lumine.mythic.api.adapters.AbstractItemStack; +import io.lumine.mythic.api.config.MythicLineConfig; +import io.lumine.mythic.api.drops.DropMetadata; +import io.lumine.mythic.api.drops.IItemDrop; +import io.lumine.mythic.api.skills.ITargetedEntitySkill; +import io.lumine.mythic.api.skills.Skill; +import io.lumine.mythic.api.skills.SkillCaster; +import io.lumine.mythic.api.skills.SkillMetadata; +import io.lumine.mythic.api.skills.SkillResult; +import io.lumine.mythic.api.skills.conditions.IEntityCondition; +import io.lumine.mythic.api.skills.conditions.ICasterCondition; +import io.lumine.mythic.bukkit.BukkitAdapter; +import io.lumine.mythic.bukkit.MythicBukkit; +import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent; +import io.lumine.mythic.bukkit.events.MythicDropLoadEvent; +import io.lumine.mythic.bukkit.events.MythicMechanicLoadEvent; +import io.lumine.mythic.bukkit.events.MythicMobDeathEvent; +import io.lumine.mythic.core.skills.SkillMetadataImpl; +import io.lumine.mythic.core.skills.SkillTriggers; +import org.bukkit.Location; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public final class Mythic5Adapter implements Listener, InlineMechanicExecutor { + private final JavaPlugin plugin; + private final GemRegistry gems; + private final ItemCodec items; + private final ProtectionBridge protection; + private final MythicDeathHandler deathHandler; + private final Map inlineSkills = new ConcurrentHashMap<>(); + + public Mythic5Adapter(JavaPlugin plugin, GemRegistry gems, ItemCodec items, ProtectionBridge protection, + MythicDeathHandler deathHandler) { + this.plugin = plugin; + this.gems = gems; + this.items = items; + this.protection = protection; + this.deathHandler = deathHandler; + } + + public void register() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public boolean castInline(Player caster, String mechanicLine, Entity target, Location location) { + if (mechanicLine == null || mechanicLine.isBlank()) return false; + try { + var manager = MythicBukkit.inst().getSkillManager(); + Skill skill = inlineSkills.computeIfAbsent(mechanicLine, + line -> manager.getSkill(null, List.of(line)).orElse(null)); + if (skill == null) return false; + AbstractEntity casterEntity = BukkitAdapter.adapt(caster); + AbstractEntity trigger = BukkitAdapter.adapt(target == null ? caster : target); + SkillCaster skillCaster = manager.getCaster(casterEntity); + List entities = new ArrayList<>(); + if (target != null) entities.add(BukkitAdapter.adapt(target)); + List locations = new ArrayList<>(); + Location origin = location == null ? caster.getLocation() : location; + if (location != null) locations.add(BukkitAdapter.adapt(location)); + SkillMetadata metadata = new SkillMetadataImpl(SkillTriggers.API, skillCaster, trigger, + BukkitAdapter.adapt(origin), entities, locations, 1.0F); + if (!skill.isUsable(metadata, SkillTriggers.API)) return false; + skill.execute(metadata); + return true; + } catch (RuntimeException | LinkageError exception) { + plugin.getLogger().warning("无法执行 MythicMobs 5 内联机制 " + mechanicLine + ": " + exception.getMessage()); + return false; + } + } + + @EventHandler + public void onDropLoad(MythicDropLoadEvent event) { + if (!event.getDropName().equalsIgnoreCase("gem")) return; + String name = event.getConfig().getString(new String[]{"name", "n"}, ""); + GemDefinition gem = gems.find(name).orElse(null); + if (gem != null) event.register(new GemDrop(gem, items)); + } + + @EventHandler + public void onMechanicLoad(MythicMechanicLoadEvent event) { + String name = normalize(event.getMechanicName()); + if (!name.equals("shielddamage") && !name.equals("sheilddamage")) return; + double damage = event.getConfig().getDouble(new String[]{"damage", "d", "amount"}, 0); + event.register(new ShieldDamage(damage)); + } + + @EventHandler + public void onConditionLoad(MythicConditionLoadEvent event) { + String name = normalize(event.getConditionName()); + switch (name) { + case "noarmorstand" -> event.register((IEntityCondition) target -> + !(BukkitAdapter.adapt(target) instanceof ArmorStand)); + case "pvpmanager" -> event.register(new Pvp(protection)); + case "residenceflag", "redidenseflag" -> { + String flag = event.getConfig().getString(new String[]{"flag", "f"}, ""); + event.register(new Residence(protection, flag)); + } + default -> { } + } + } + + @EventHandler + public void onMobDeath(MythicMobDeathEvent event) { + if (event.getKiller() instanceof Player player) + deathHandler.onDeath(player, event.getMobType().getInternalName(), event.getEntity(), event); + } + + private static String normalize(String input) { + return input.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + } + + private record GemDrop(GemDefinition gem, ItemCodec items) implements IItemDrop { + @Override + public AbstractItemStack getDrop(DropMetadata metadata, double amount) { + ItemStack item = items.createGem(gem, Math.max(1, (int) Math.round(amount))); + return BukkitAdapter.adapt(item); + } + } + + private record ShieldDamage(double damage) implements ITargetedEntitySkill { + @Override + public SkillResult castAtEntity(SkillMetadata metadata, AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + if (!(entity instanceof LivingEntity living)) return SkillResult.INVALID_TARGET; + living.setAbsorptionAmount(Math.max(0, living.getAbsorptionAmount() - Math.max(0, damage))); + return SkillResult.SUCCESS; + } + } + + private record Pvp(ProtectionBridge protection) implements IEntityCondition, ICasterCondition { + @Override + public boolean check(AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + return entity instanceof Player player && protection.pvpEnabled(player); + } + + @Override + public boolean check(SkillCaster caster) { + return check(caster.getEntity()); + } + } + + private record Residence(ProtectionBridge protection, String flag) implements IEntityCondition, ICasterCondition { + @Override + public boolean check(AbstractEntity target) { + Entity entity = BukkitAdapter.adapt(target); + return protection.residenceFlag(entity, entity.getLocation(), flag); + } + + @Override + public boolean check(SkillCaster caster) { + return check(caster.getEntity()); + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java b/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java new file mode 100644 index 0000000..bf7f44a --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/item/ItemCodec.java @@ -0,0 +1,230 @@ +package cn.infstar.cmagicgem.item; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.migration.LegacyItemMigrator; +import cn.infstar.cmagicgem.util.PotionTypes; +import cn.infstar.cmagicgem.util.MaterialResolver; +import org.bukkit.ChatColor; +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import com.destroystokyo.paper.profile.ProfileProperty; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public final class ItemCodec { + private final GemRegistry registry; + private final NamespacedKey gemNameKey; + private final NamespacedKey dataVersionKey; + private final NamespacedKey embeddedKey; + private final NamespacedKey skillsKey; + private final LegacyItemMigrator legacyMigrator; + + public ItemCodec(JavaPlugin plugin, GemRegistry registry) { + this.registry = registry; + this.gemNameKey = new NamespacedKey(plugin, "gem_name"); + this.dataVersionKey = new NamespacedKey(plugin, "data_version"); + this.embeddedKey = new NamespacedKey(plugin, "embedded_gems"); + this.skillsKey = new NamespacedKey(plugin, "skills"); + this.legacyMigrator = new LegacyItemMigrator(registry); + } + + public ItemStack createGem(GemDefinition gem, int amount) { + ItemStack item = createItem(new ItemAppearance(gem.material().name(), amount, gem.display(), gem.tips(), + gem.texture(), gem.color(), gem.effects(), gem.customModelData(), gem.glow())); + ItemMeta meta = item.getItemMeta(); + meta.getPersistentDataContainer().set(gemNameKey, PersistentDataType.STRING, gem.name()); + meta.getPersistentDataContainer().set(dataVersionKey, PersistentDataType.INTEGER, 1); + item.setItemMeta(meta); + return item; + } + + public ItemStack createItem(ItemAppearance appearance) { + String materialName = appearance.material() == null || appearance.material().isBlank() + ? appearance.texture() == null || appearance.texture().isBlank() ? "STONE" : "PLAYER_HEAD" + : appearance.material(); + Material material = MaterialResolver.resolve(materialName); + if (material == null) throw new IllegalArgumentException("未知材料: " + materialName); + int amount = Math.max(1, Math.min(appearance.amount(), material.getMaxStackSize())); + ItemStack item = new ItemStack(material, amount); + ItemMeta meta = item.getItemMeta(); + if (appearance.display() != null && !appearance.display().isBlank()) + meta.setDisplayName(color(appearance.display())); + if (appearance.lore() != null && !appearance.lore().isEmpty()) + meta.setLore(appearance.lore().stream().map(this::color).toList()); + if (appearance.customModelData() != null) meta.setCustomModelData(appearance.customModelData()); + if (meta instanceof SkullMeta skull && appearance.texture() != null && !appearance.texture().isBlank()) { + var profile = Bukkit.createProfile(UUID.randomUUID()); + profile.setProperty(new ProfileProperty("textures", appearance.texture())); + skull.setPlayerProfile(profile); + } + if (meta instanceof PotionMeta potion) { + if (appearance.color() != null) potion.setColor(Color.fromRGB(appearance.color() & 0xFFFFFF)); + addPotionEffects(potion, appearance.effects()); + } + if (appearance.glow()) { + meta.addEnchant(Enchantment.UNBREAKING, 1, true); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + } + item.setItemMeta(meta); + return item; + } + + private void addPotionEffects(PotionMeta potion, String effects) { + if (effects == null || effects.isBlank()) return; + for (String encoded : effects.split(";")) { + String[] parts = encoded.trim().split(":"); + if (parts.length == 0) continue; + PotionEffectType type = PotionTypes.resolve(parts[0]); + if (type == null) continue; + int duration = parts.length > 1 ? parseInt(parts[1], 1) * 20 : 20; + int level = parts.length > 2 ? parseInt(parts[2], 1) : 1; + potion.addCustomEffect(new PotionEffect(type, duration, Math.max(0, level - 1)), true); + } + } + + public static Integer parseColor(Object value) { + if (value instanceof Number number) return number.intValue(); + if (value == null || value.toString().isBlank()) return null; + return switch (value.toString().toUpperCase()) { + case "WHITE" -> 0xFFFFFF; case "SILVER" -> 0xC0C0C0; case "GRAY" -> 0x808080; + case "BLACK" -> 0x000000; case "RED" -> 0xFF0000; case "MAROON" -> 0x800000; + case "YELLOW" -> 0xFFFF00; case "OLIVE" -> 0x808000; case "LIME" -> 0x00FF00; + case "GREEN" -> 0x008000; case "AQUA" -> 0x00FFFF; case "TEAL" -> 0x008080; + case "BLUE" -> 0x0000FF; case "NAVY" -> 0x000080; case "FUCHSIA" -> 0xFF00FF; + case "PURPLE" -> 0x800080; case "ORANGE" -> 0xFFA500; case "GOLD" -> 0xFFD700; + default -> { + try { yield Integer.decode(value.toString()); } catch (NumberFormatException ignored) { yield null; } + } + }; + } + + public record ItemAppearance(String material, int amount, String display, List lore, + String texture, Integer color, String effects, + Integer customModelData, boolean glow) { } + + private int parseInt(String value, int fallback) { + try { return Integer.parseInt(value); } catch (NumberFormatException ignored) { return fallback; } + } + + public Optional readGem(ItemStack item, boolean allowLore) { + if (item == null || item.getType().isAir() || !item.hasItemMeta()) return Optional.empty(); + ItemMeta meta = item.getItemMeta(); + String name = meta.getPersistentDataContainer().get(gemNameKey, PersistentDataType.STRING); + Optional direct = registry.find(name); + if (direct.isPresent()) return direct; + LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item); + if (legacy.identity() != null) { + Optional migrated = registry.find(legacy.identity()); + if (migrated.isPresent()) { + meta.getPersistentDataContainer().set(gemNameKey, PersistentDataType.STRING, migrated.get().name()); + meta.getPersistentDataContainer().set(dataVersionKey, PersistentDataType.INTEGER, 1); + item.setItemMeta(meta); + return migrated; + } + } + if (!allowLore || !meta.hasLore()) return Optional.empty(); + List lore = meta.getLore(); + if (lore == null) return Optional.empty(); + for (GemDefinition gem : registry.all()) { + if (gem.loreIdentity() == null) continue; + String expected = strip(gem.loreIdentity()); + if (lore.stream().map(this::strip).anyMatch(line -> line.contains(expected))) return Optional.of(gem); + } + return Optional.empty(); + } + + public Map embedded(ItemStack item) { + if (item == null || !item.hasItemMeta()) return Map.of(); + String encoded = item.getItemMeta().getPersistentDataContainer().get(embeddedKey, PersistentDataType.STRING); + Map result = new LinkedHashMap<>(); + if (encoded == null || encoded.isBlank()) { + LegacyItemMigrator.LegacyData legacy = legacyMigrator.inspect(item); + if (!legacy.embedded().isEmpty()) { + writeEmbedded(item, legacy.embedded()); + return new LinkedHashMap<>(legacy.embedded()); + } + return result; + } + for (String line : encoded.split("\\n")) { + int separator = line.lastIndexOf('='); + if (separator < 0) continue; + try { + result.put(URLDecoder.decode(line.substring(0, separator), StandardCharsets.UTF_8), + Integer.parseInt(line.substring(separator + 1))); + } catch (IllegalArgumentException ignored) { + } + } + return result; + } + + public ItemStack setEmbedded(ItemStack item, Map gems) { + ItemStack result = item.clone(); + writeEmbedded(result, gems); + return result; + } + + private void writeEmbedded(ItemStack item, Map gems) { + ItemMeta meta = item.getItemMeta(); + StringBuilder encoded = new StringBuilder(); + gems.forEach((name, count) -> encoded.append(URLEncoder.encode(name, StandardCharsets.UTF_8)) + .append('=').append(count).append('\n')); + meta.getPersistentDataContainer().set(embeddedKey, PersistentDataType.STRING, encoded.toString()); + meta.getPersistentDataContainer().set(dataVersionKey, PersistentDataType.INTEGER, 1); + item.setItemMeta(meta); + } + + public List skills(ItemStack item) { + if (item == null || !item.hasItemMeta()) return new ArrayList<>(); + String value = item.getItemMeta().getPersistentDataContainer().get(skillsKey, PersistentDataType.STRING); + if (value == null || value.isBlank()) { + List legacy = new ArrayList<>(legacyMigrator.inspect(item).skills()); + if (!legacy.isEmpty()) writeSkills(item, legacy); + return legacy; + } + return new ArrayList<>(List.of(value.split("\\n"))); + } + + public ItemStack setSkills(ItemStack item, List skills) { + ItemStack result = item.clone(); + writeSkills(result, skills); + return result; + } + + private void writeSkills(ItemStack item, List skills) { + ItemMeta meta = item.getItemMeta(); + PersistentDataContainer data = meta.getPersistentDataContainer(); + data.set(skillsKey, PersistentDataType.STRING, String.join("\n", skills)); + item.setItemMeta(meta); + } + + public String color(String value) { + return ChatColor.translateAlternateColorCodes('&', value == null ? "" : value); + } + + public String strip(String value) { + return ChatColor.stripColor(color(value)); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/item/ItemMatcher.java b/src/main/java/cn/infstar/cMagicGem/item/ItemMatcher.java new file mode 100644 index 0000000..b00cabb --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/item/ItemMatcher.java @@ -0,0 +1,81 @@ +package cn.infstar.cmagicgem.item; + +import de.tr7zw.changeme.nbtapi.NBT; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Locale; +import java.util.Set; + +public final class ItemMatcher { + private final ItemCodec codec; + + public ItemMatcher(ItemCodec codec) { + this.codec = codec; + } + + public boolean matches(ItemStack item, Set requirements) { + if (item == null || item.getType().isAir()) return false; + if (requirements.isEmpty()) return true; + for (String requirement : requirements) { + if (matches(item, requirement)) return true; + } + return false; + } + + public boolean matches(ItemStack item, String rawRequirement) { + if (item == null || item.getType().isAir()) return false; + String requirement = rawRequirement.toUpperCase(Locale.ROOT); + Material material = item.getType(); + if (requirement.equals("NOTHING")) return false; + if (requirement.equals("ALL") || requirement.equals("ANY")) return true; + if (requirement.startsWith("LORE:")) return loreContains(item, rawRequirement.substring(5)); + if (requirement.startsWith("NAME:")) { + ItemMeta meta = item.getItemMeta(); + return meta.hasDisplayName() && strip(meta.getDisplayName()).contains(strip(rawRequirement.substring(5))); + } + if (requirement.startsWith("GEM:")) { + String expected = rawRequirement.substring(4); + return codec.embedded(item).entrySet().stream().anyMatch(entry -> entry.getValue() > 0 + && (expected.equalsIgnoreCase("ALL") || expected.equalsIgnoreCase("ANY") + || entry.getKey().equalsIgnoreCase(expected))); + } + if (requirement.startsWith("NBT:")) { + String[] parts = rawRequirement.substring(4).split(":", 2); + return NBT.get(item, nbt -> { + if (!nbt.hasTag(parts[0])) return false; + return parts.length == 1 || nbt.getString(parts[0]).equals(parts[1]); + }); + } + if (material.name().equals(requirement) || material.name().endsWith('_' + requirement)) return true; + return switch (requirement) { + case "ARMOR" -> material.name().endsWith("_HELMET") || material.name().endsWith("_CHESTPLATE") + || material.name().endsWith("_LEGGINGS") || material.name().endsWith("_BOOTS") + || material == Material.ELYTRA; + case "WEAPON" -> material.name().endsWith("_SWORD") || material.name().endsWith("_AXE") + || material == Material.BOW || material == Material.CROSSBOW || material == Material.TRIDENT + || material == Material.MACE; + case "TOOL" -> material.name().endsWith("_PICKAXE") || material.name().endsWith("_AXE") + || material.name().endsWith("_SHOVEL") || material.name().endsWith("_HOE") + || material == Material.SHEARS || material == Material.FISHING_ROD; + case "HELMET", "HEAD" -> material.name().endsWith("_HELMET"); + case "CHEST", "CHESTPLATE" -> material.name().endsWith("_CHESTPLATE") || material == Material.ELYTRA; + case "LEGS", "LEGGINGS" -> material.name().endsWith("_LEGGINGS"); + case "FEET", "BOOTS" -> material.name().endsWith("_BOOTS"); + default -> false; + }; + } + + private boolean loreContains(ItemStack item, String expected) { + ItemMeta meta = item.getItemMeta(); + if (!meta.hasLore() || meta.getLore() == null) return false; + String clean = strip(expected); + return meta.getLore().stream().map(this::strip).anyMatch(line -> line.contains(clean)); + } + + private String strip(String value) { + return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', value == null ? "" : value)); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/listener/GemUseListener.java b/src/main/java/cn/infstar/cMagicGem/listener/GemUseListener.java new file mode 100644 index 0000000..90ea0d8 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/listener/GemUseListener.java @@ -0,0 +1,154 @@ +package cn.infstar.cmagicgem.listener; + +import cn.infstar.cmagicgem.config.PluginSettings; +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemService; +import cn.infstar.cmagicgem.gem.GemType; +import cn.infstar.cmagicgem.gem.GemUseResult; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.gui.GemWorkbenchHolder; +import cn.infstar.cmagicgem.util.MessageDispatcher; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.FurnaceBurnEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.ItemStack; + +import java.util.function.Supplier; + +public final class GemUseListener implements Listener { + private final GemService gemService; + private final ItemCodec itemCodec; + private final Supplier settings; + private final MessageDispatcher messages; + + public GemUseListener(GemService gemService, ItemCodec itemCodec, MessageDispatcher messages, + Supplier settings) { + this.gemService = gemService; + this.itemCodec = itemCodec; + this.settings = settings; + this.messages = messages; + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent event) { + if (event.getView().getTopInventory().getHolder() instanceof GemWorkbenchHolder) return; + boolean backpack = event.getClickedInventory() == event.getWhoClicked().getInventory(); + boolean workbench = !backpack && event.getView().getTopInventory().getType() == InventoryType.WORKBENCH; + if (backpack && !settings.get().backpack() || workbench && !settings.get().workbench() || !backpack && !workbench) return; + ItemStack cursor = event.getCursor(); + ItemStack target = event.getCurrentItem(); + GemDefinition gem = itemCodec.readGem(cursor, settings.get().gemLore()).orElse(null); + if (gem == null || gem.giveRandomGem() || gemService.effectiveType(gem) != GemType.ITEM_GEM + || target == null || target.getType().isAir()) return; + String gui = backpack ? "backpack" : "workbench"; + if (!gem.allowedGuis().isEmpty() && gem.allowedGuis().stream().noneMatch(value -> value.equalsIgnoreCase(gui))) return; + event.setCancelled(true); + GemUseResult result = gemService.use((org.bukkit.entity.Player) event.getWhoClicked(), gem, target); + if (result.consumed()) consumeOne(cursor, event); + if (result.target() != null) event.setCurrentItem(result.target()); + messages.send(event.getWhoClicked(), result.message()); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + ItemStack used = event.getItem(); + GemDefinition gem = itemCodec.readGem(used, settings.get().gemLore()).orElse(null); + if (gem == null || gem.eat() && gem.type() == GemType.PLAYER_GEM) return; + GemType effectiveType = gemService.effectiveType(gem); + boolean giveaway = gem.type() == GemType.RANDOM_GEM && gem.giveRandomGem(); + if (!giveaway && effectiveType == GemType.ITEM_GEM && gem.fastUse() == null) { + event.setCancelled(true); + return; + } + if (gem.shift() && !event.getPlayer().isSneaking()) return; + event.setCancelled(true); + EquipmentSlot targetSlot = !giveaway && effectiveType == GemType.ITEM_GEM ? slot(gem.fastUse()) : null; + ItemStack target = targetSlot == null ? null : get(event.getPlayer().getInventory(), targetSlot); + GemUseResult result = gemService.use(event.getPlayer(), gem, target); + if (result.consumed() && used != null) { + used.setAmount(used.getAmount() - 1); + if (result.target() != null && targetSlot != null) set(event.getPlayer().getInventory(), targetSlot, result.target()); + } + messages.send(event.getPlayer(), result.message()); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onConsume(PlayerItemConsumeEvent event) { + GemDefinition gem = itemCodec.readGem(event.getItem(), settings.get().gemLore()).orElse(null); + if (gem == null || gem.type() != GemType.PLAYER_GEM || !gem.eat()) return; + if (gem.shift() && !event.getPlayer().isSneaking()) { + event.setCancelled(true); + return; + } + event.setCancelled(true); + GemUseResult result = gemService.use(event.getPlayer(), gem, null); + if (result.consumed()) { + ItemStack held = event.getPlayer().getInventory().getItem(event.getHand()); + held.setAmount(held.getAmount() - 1); + } + messages.send(event.getPlayer(), result.message()); + } + + private EquipmentSlot slot(String value) { + if (value == null) return EquipmentSlot.OFF_HAND; + return switch (value.toLowerCase(java.util.Locale.ROOT)) { + case "head", "helmet" -> EquipmentSlot.HEAD; + case "chest", "chestplate" -> EquipmentSlot.CHEST; + case "legs", "leggings" -> EquipmentSlot.LEGS; + case "feet", "boots" -> EquipmentSlot.FEET; + case "mainhand", "hand" -> EquipmentSlot.HAND; + default -> EquipmentSlot.OFF_HAND; + }; + } + + private ItemStack get(PlayerInventory inventory, EquipmentSlot slot) { + return inventory.getItem(slot); + } + + private void set(PlayerInventory inventory, EquipmentSlot slot, ItemStack item) { + inventory.setItem(slot, item); + } + + @EventHandler + public void onCraft(PrepareItemCraftEvent event) { + if (settings.get().gemCraft()) return; + for (ItemStack item : event.getInventory().getMatrix()) { + if (itemCodec.readGem(item, settings.get().gemLore()).isPresent()) { + event.getInventory().setResult(new ItemStack(Material.AIR)); + return; + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onDrop(PlayerDropItemEvent event) { + if (itemCodec.readGem(event.getItemDrop().getItemStack(), settings.get().gemLore()).isPresent()) + event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = true) + public void onFuel(FurnaceBurnEvent event) { + if (itemCodec.readGem(event.getFuel(), settings.get().gemLore()).isPresent()) event.setCancelled(true); + } + + private void consumeOne(ItemStack cursor, InventoryClickEvent event) { + if (cursor.getAmount() <= 1) event.setCursor(null); + else { + ItemStack reduced = cursor.clone(); + reduced.setAmount(cursor.getAmount() - 1); + event.setCursor(reduced); + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java b/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java new file mode 100644 index 0000000..05b1dbb --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/migration/LegacyDirectoryImporter.java @@ -0,0 +1,82 @@ +package cn.infstar.cmagicgem.migration; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.logging.Level; + +public final class LegacyDirectoryImporter { + private static final List DATA_DIRECTORIES = List.of("gems", "skills", "gui", "languages"); + private final JavaPlugin plugin; + + public LegacyDirectoryImporter(JavaPlugin plugin) { + this.plugin = plugin; + } + + public ImportResult importIfPresent() { + Path pluginsDirectory = plugin.getDataFolder().toPath().toAbsolutePath().getParent(); + if (pluginsDirectory == null) return new ImportResult(0, false); + Path legacy = pluginsDirectory.resolve("MagicGem"); + Path target = plugin.getDataFolder().toPath(); + if (!Files.isDirectory(legacy) || legacy.equals(target)) return new ImportResult(0, false); + int copied = 0; + try { + Files.createDirectories(target); + for (String directory : DATA_DIRECTORIES) copied += copyDirectory(legacy.resolve(directory), target.resolve(directory)); + boolean configImported = importConfig(legacy.resolve("config.yml"), target.resolve("config.yml")); + plugin.getLogger().info("发现旧 MagicGem 数据目录,安全导入 " + copied + " 个文件。原目录保持不变。"); + return new ImportResult(copied, configImported); + } catch (IOException exception) { + plugin.getLogger().log(Level.SEVERE, "导入旧 MagicGem 数据失败,未删除任何旧文件", exception); + return new ImportResult(copied, false); + } + } + + private int copyDirectory(Path source, Path target) throws IOException { + if (!Files.isDirectory(source)) return 0; + int copied = 0; + try (var paths = Files.walk(source)) { + for (Path path : paths.toList()) { + Path relative = source.relativize(path); + Path destination = target.resolve(relative).normalize(); + if (!destination.startsWith(target.normalize())) continue; + if (Files.isDirectory(path)) Files.createDirectories(destination); + else if (!Files.exists(destination)) { + Files.createDirectories(destination.getParent()); + Files.copy(path, destination, StandardCopyOption.COPY_ATTRIBUTES); + copied++; + } + } + } + return copied; + } + + private boolean importConfig(Path source, Path destination) throws IOException { + if (!Files.isRegularFile(source)) return false; + YamlConfiguration oldConfig = YamlConfiguration.loadConfiguration(source.toFile()); + YamlConfiguration newConfig = YamlConfiguration.loadConfiguration(destination.toFile()); + boolean changed = false; + for (String key : List.of("lang", "workbench", "backpack", "gem-lore", "gem-craft", "skill-engine", + "skill-engine-lore-color", "potion-global-limit")) { + if (oldConfig.contains(key) && !newConfig.contains(key)) { + newConfig.set(key, oldConfig.get(key)); + changed = true; + } + } + if (oldConfig.contains("lang") && !newConfig.contains("language")) { + newConfig.set("language", oldConfig.getString("lang", "zh_CN")); + changed = true; + } + if (changed) newConfig.save(destination.toFile()); + return changed; + } + + public record ImportResult(int copiedFiles, boolean configImported) { + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/migration/LegacyItemMigrator.java b/src/main/java/cn/infstar/cMagicGem/migration/LegacyItemMigrator.java new file mode 100644 index 0000000..741a35c --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/migration/LegacyItemMigrator.java @@ -0,0 +1,136 @@ +package cn.infstar.cmagicgem.migration; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.NBTType; +import de.tr7zw.changeme.nbtapi.iface.ReadableNBT; +import org.bukkit.inventory.ItemStack; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class LegacyItemMigrator { + public static final String GEM_NAME_KEY = "MAGICGEM_NAME"; + public static final String EMBEDDED_GEMS_KEY = "MAGICGEM_EMBED"; + public static final String SKILLS_KEY = "MAGICGEM_SKILL"; + private final GemRegistry registry; + + public LegacyItemMigrator(GemRegistry registry) { + this.registry = registry; + } + + public LegacyData inspect(ItemStack item) { + if (item == null || item.getType().isAir()) return LegacyData.EMPTY; + try { + return NBT.get(item, nbt -> { + Collector collector = new Collector(); + readExact(nbt, collector); + scan(nbt, collector, 0); + return collector.result(); + }); + } catch (RuntimeException ignored) { + return LegacyData.EMPTY; + } + } + + private void readExact(ReadableNBT nbt, Collector collector) { + if (nbt.hasTag(GEM_NAME_KEY) && nbt.getType(GEM_NAME_KEY) == NBTType.NBTTagString) { + registry.find(nbt.getString(GEM_NAME_KEY)).ifPresent(gem -> collector.identity = gem.name()); + } + if (nbt.hasTag(EMBEDDED_GEMS_KEY) && nbt.getType(EMBEDDED_GEMS_KEY) == NBTType.NBTTagCompound) { + ReadableNBT embedded = nbt.getCompound(EMBEDDED_GEMS_KEY); + if (embedded != null) for (String name : embedded.getKeys()) { + NBTType type = embedded.getType(name); + if (!numeric(type)) continue; + registry.find(name).ifPresent(gem -> collector.embedded.put(gem.name(), number(embedded, name, type))); + } + } + if (nbt.hasTag(SKILLS_KEY) && nbt.getType(SKILLS_KEY) == NBTType.NBTTagList) { + var skills = nbt.getStringList(SKILLS_KEY); + for (int index = 0; index < skills.size(); index++) collector.skills.add(skills.get(index)); + } + } + + private void scan(ReadableNBT nbt, Collector collector, int depth) { + if (nbt == null || depth > 12) return; + for (String key : nbt.getKeys()) { + if (key.equalsIgnoreCase(GEM_NAME_KEY) || key.equalsIgnoreCase(EMBEDDED_GEMS_KEY) + || key.equalsIgnoreCase(SKILLS_KEY)) continue; + NBTType type = nbt.getType(key); + try { + if (type == NBTType.NBTTagString) { + String value = nbt.getString(key); + registry.find(value).ifPresent(gem -> { + if (isIdentityKey(key)) collector.identity = gem.name(); + else collector.embedded.merge(gem.name(), 1, Integer::sum); + }); + if (isSkillKey(key) && !value.isBlank()) { + for (String skill : value.split("[,;\\n]")) if (!skill.isBlank()) collector.skills.add(skill.trim()); + } + } else if (type == NBTType.NBTTagCompound) { + ReadableNBT child = nbt.getCompound(key); + registry.find(key).ifPresent(gem -> collector.embedded.merge(gem.name(), numericCount(child), Integer::sum)); + scan(child, collector, depth + 1); + } else if (type == NBTType.NBTTagList && isSkillKey(key)) { + var list = nbt.getStringList(key); + for (int index = 0; index < list.size(); index++) collector.skills.add(list.get(index)); + } else if (numeric(type)) { + registry.find(key).ifPresent(gem -> collector.embedded.merge(gem.name(), number(nbt, key, type), Integer::sum)); + } + } catch (RuntimeException ignored) { + } + } + } + + private int numericCount(ReadableNBT nbt) { + if (nbt == null) return 1; + for (String name : Set.of("amount", "count", "level", "value")) { + if (nbt.hasTag(name)) return Math.max(1, nbt.getInteger(name)); + } + return 1; + } + + private int number(ReadableNBT nbt, String key, NBTType type) { + if (type == NBTType.NBTTagByte) return Math.max(1, nbt.getByte(key)); + if (type == NBTType.NBTTagShort) return Math.max(1, nbt.getShort(key)); + if (type == NBTType.NBTTagLong) return (int) Math.max(1, nbt.getLong(key)); + return Math.max(1, nbt.getInteger(key)); + } + + private boolean numeric(NBTType type) { + return type == NBTType.NBTTagByte || type == NBTType.NBTTagShort || type == NBTType.NBTTagInt || type == NBTType.NBTTagLong; + } + + private boolean isIdentityKey(String key) { + String normalized = key.toLowerCase(Locale.ROOT); + return key.equalsIgnoreCase("MAGICGEM_NAME") + || normalized.contains("gem") && (normalized.contains("name") || normalized.contains("id") || normalized.equals("gem")); + } + + private boolean isSkillKey(String key) { + return key.toLowerCase(Locale.ROOT).contains("skill"); + } + + private final class Collector { + private String identity; + private final Map embedded = new LinkedHashMap<>(); + private final Set skills = new LinkedHashSet<>(); + + private LegacyData result() { + return new LegacyData(identity, Map.copyOf(embedded), List.copyOf(skills)); + } + } + + public record LegacyData(String identity, Map embedded, List skills) { + public static final LegacyData EMPTY = new LegacyData(null, Map.of(), List.of()); + + public boolean found() { + return identity != null || !embedded.isEmpty() || !skills.isEmpty(); + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/reward/RewardContext.java b/src/main/java/cn/infstar/cMagicGem/reward/RewardContext.java new file mode 100644 index 0000000..0d9d8a5 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/reward/RewardContext.java @@ -0,0 +1,48 @@ +package cn.infstar.cmagicgem.reward; + +import cn.infstar.cmagicgem.gem.GemDefinition; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.DoubleSupplier; + +public final class RewardContext { + private final Player player; + private final GemDefinition gem; + private ItemStack item; + private final boolean removal; + private final Map evaluatedNumbers = new IdentityHashMap<>(); + + public RewardContext(Player player, GemDefinition gem, ItemStack item, boolean removal) { + this.player = player; + this.gem = gem; + this.item = item; + this.removal = removal; + } + + public Player player() { + return player; + } + + public GemDefinition gem() { + return gem; + } + + public ItemStack item() { + return item; + } + + public void item(ItemStack item) { + this.item = item; + } + + public boolean removal() { + return removal; + } + + public double evaluatedNumber(Object key, DoubleSupplier supplier) { + return evaluatedNumbers.computeIfAbsent(key, ignored -> supplier.getAsDouble()); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/reward/RewardEngine.java b/src/main/java/cn/infstar/cMagicGem/reward/RewardEngine.java new file mode 100644 index 0000000..3966b0c --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/reward/RewardEngine.java @@ -0,0 +1,1186 @@ +package cn.infstar.cmagicgem.reward; + +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.expression.DynamicExpression; +import cn.infstar.cmagicgem.gem.GemDefinition; +import cn.infstar.cmagicgem.gem.GemRegistry; +import cn.infstar.cmagicgem.integration.EconomyBridge; +import cn.infstar.cmagicgem.integration.MythicBridge; +import cn.infstar.cmagicgem.integration.MMOItemsBridge; +import cn.infstar.cmagicgem.integration.PlaceholderBridge; +import cn.infstar.cmagicgem.integration.PlayerPointsBridge; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.item.ItemMatcher; +import cn.infstar.cmagicgem.skill.SkillDefinition; +import cn.infstar.cmagicgem.skill.SkillRegistry; +import cn.infstar.cmagicgem.util.PotionTypes; +import cn.infstar.cmagicgem.util.MaterialResolver; +import cn.infstar.cmagicgem.util.EnchantmentResolver; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.tr7zw.changeme.nbtapi.NBT; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class RewardEngine { + private static final Pattern NUMBER = Pattern.compile("[-+]?\\d+(?:\\.\\d+)?"); + private static final Set ITEM_REWARDS = Set.of("empty", "conditional", "list", "custommodeldata", + "itemdestroy", "durability", "enchant", "enchantset", "itemflag", "itemset", "loreadd", + "loredel", "lorereplace", "loremultireplace", "lorevar", "material", "name", "namevar", + "skilladd", "skilldel", "skillreplace", "skilltonbt", "unbreakable", "attribute", "nbtdouble", + "nbtstring", "mythicitem", "mmoability", "gemremove"); + private static final Set PLAYER_REWARDS = Set.of("actionbar", "chat", "title", "command", "explevel", + "heal", "itemedit", "itemgive", "itemtake", "maxhealth", "money", "point", "mythicitemgive", + "potion", "skill"); + private static final Set CACHED_PLAYER_REWARDS = Set.of("explevel", "heal", "itemtake", "maxhealth", + "money", "point"); + private final JavaPlugin plugin; + private final ItemCodec itemCodec; + private final ItemMatcher itemMatcher; + private final GemRegistry gemRegistry; + private final SkillRegistry skillRegistry; + private final DirectiveParser parser; + private final DynamicExpression expression; + private final PlaceholderBridge placeholders; + private final EconomyBridge economy; + private final PlayerPointsBridge points; + private final MythicBridge mythic; + private final MMOItemsBridge mmoItems; + private BiConsumer skillInvoker; + + public RewardEngine(JavaPlugin plugin, ItemCodec itemCodec, ItemMatcher itemMatcher, GemRegistry gemRegistry, + SkillRegistry skillRegistry, DirectiveParser parser, DynamicExpression expression, PlaceholderBridge placeholders, + EconomyBridge economy, PlayerPointsBridge points, MythicBridge mythic) { + this.plugin = plugin; + this.itemCodec = itemCodec; + this.itemMatcher = itemMatcher; + this.gemRegistry = gemRegistry; + this.skillRegistry = skillRegistry; + this.parser = parser; + this.expression = expression; + this.placeholders = placeholders; + this.economy = economy; + this.points = points; + this.mythic = mythic; + this.mmoItems = new MMOItemsBridge(plugin); + } + + public void skillInvoker(BiConsumer skillInvoker) { + this.skillInvoker = skillInvoker; + } + + public static boolean supports(String type) { + String normalized = normalize(type); + return ITEM_REWARDS.contains(normalized) || PLAYER_REWARDS.contains(normalized); + } + + public void applyDirective(RewardContext context, Directive directive) throws RewardException { + if (isPlayerReward(directive)) applyPlayer(context, directive); else applyItem(context, directive); + } + + public ItemStack applyItemRewards(RewardContext context, List directives, Directive.Phase phase) throws RewardException { + context.item(context.item().clone()); + List ordered = context.removal() ? directives.reversed() : directives; + for (Directive directive : ordered) { + if (!runsInItemPhase(directive, phase) || isPlayerReward(directive)) continue; + try { + applyItem(context, directive); + } catch (RewardException exception) { + if (!directive.ignorable()) throw exception; + } + } + return context.item(); + } + + public void applyPlayerRewards(RewardContext context, List directives, Directive.Phase phase) throws RewardException { + List ordered = context.removal() ? directives.reversed() : directives; + List selected = ordered.stream() + .filter(directive -> runsInPlayerPhase(directive, phase) && isPlayerReward(directive)).toList(); + validatePlayerRewards(context, selected); + applyPlayerRewardPass(context, selected, true); + applyPlayerRewardPass(context, selected, false); + } + + private void applyPlayerRewardPass(RewardContext context, List directives, boolean cached) + throws RewardException { + for (Directive directive : directives) { + if (CACHED_PLAYER_REWARDS.contains(normalize(directive.type())) != cached) continue; + try { + applyPlayer(context, directive); + } catch (RewardException exception) { + if (!directive.ignorable()) throw exception; + } + } + } + + private void validatePlayerRewards(RewardContext context, List directives) throws RewardException { + Player player = context.player(); + double moneyBalance = economy.available() ? economy.balance(player) : 0; + int pointBalance = points.available() ? points.balance(player) : 0; + int levels = player.getLevel(); + var maxHealthAttribute = player.getAttribute(Attribute.GENERIC_MAX_HEALTH); + double maxHealthValue = maxHealthAttribute == null ? player.getMaxHealth() : maxHealthAttribute.getBaseValue(); + ItemStack[] inventory = cloneItems(player.getInventory().getStorageContents()); + + for (Directive directive : directives) { + if (directive.ignorable()) continue; + switch (normalize(directive.type())) { + case "explevel" -> { + levels += (int) Math.round(amount(context, directive)); + if (levels < 0) throw new RewardException("经验等级不足"); + } + case "maxhealth" -> { + if (maxHealthAttribute == null) throw new RewardException("无法读取生命上限"); + maxHealthValue += amount(context, directive); + double limit = number(directive.argument("0", "limit")); + if (maxHealthValue < 1) throw new RewardException("生命上限不能低于 1"); + if (limit > 0 && maxHealthValue > limit) throw new RewardException("生命上限超过限制"); + } + case "money" -> { + if (!economy.available()) throw new RewardException("Vault 经济服务不可用"); + moneyBalance += amount(context, directive); + if (moneyBalance < 0) throw new RewardException("金币不足"); + } + case "point" -> { + if (!points.available()) throw new RewardException("PlayerPoints 不可用"); + pointBalance += (int) Math.round(amount(context, directive)); + if (pointBalance < 0) throw new RewardException("点券不足"); + } + case "itemtake" -> simulateTake(inventory, directive); + case "itemgive" -> validateGivenItem(directive); + case "mythicitemgive" -> { + String name = directive.argument("", "name", "id", "item"); + if (mythic.item(name).isEmpty()) throw new RewardException("未知 MythicMobs 物品: " + name); + } + case "potion" -> { + String name = directive.argument("", "name", "type"); + if (PotionTypes.resolve(name) == null) throw new RewardException("未知药水效果: " + name); + } + default -> { } + } + } + } + + private ItemStack[] cloneItems(ItemStack[] contents) { + ItemStack[] copy = new ItemStack[contents.length]; + for (int index = 0; index < contents.length; index++) + copy[index] = contents[index] == null ? null : contents[index].clone(); + return copy; + } + + private void simulateTake(ItemStack[] contents, Directive directive) throws RewardException { + Map required = new LinkedHashMap<>(); + try { + directive.arguments().forEach((name, value) -> required.put(name, Integer.parseInt(value))); + } catch (NumberFormatException exception) { + throw new RewardException("ItemTake 数量无效", exception); + } + for (Map.Entry entry : required.entrySet()) { + int remaining = entry.getValue(); + if (remaining < 0) throw new RewardException("ItemTake 数量不能为负数"); + for (int index = 0; index < contents.length && remaining > 0; index++) { + ItemStack item = contents[index]; + if (!itemMatcher.matches(item, entry.getKey())) continue; + int removed = Math.min(remaining, item.getAmount()); + item.setAmount(item.getAmount() - removed); + if (item.getAmount() <= 0) contents[index] = null; + remaining -= removed; + } + if (remaining > 0) throw new RewardException("物品不足: " + entry.getKey()); + } + } + + private void validateGivenItem(Directive directive) throws RewardException { + String gem = directive.argument("", "gem"); + if (!gem.isBlank()) { + if (gemRegistry.find(gem).isEmpty()) throw new RewardException("未知宝石: " + gem); + return; + } + configuredItem(directive, Math.max(1, directive.intArgument(1, "amount"))); + } + + private boolean runsInItemPhase(Directive directive, Directive.Phase phase) { + return directive.phase() == Directive.Phase.ALWAYS || directive.phase() == phase; + } + + private boolean runsInPlayerPhase(Directive directive, Directive.Phase phase) { + if (phase == Directive.Phase.REMOVE) return directive.phase() == Directive.Phase.REMOVE; + return directive.phase() == Directive.Phase.ALWAYS || directive.phase() == phase; + } + + private boolean isPlayerReward(Directive directive) { + return PLAYER_REWARDS.contains(normalize(directive.type())); + } + + private void applyItem(RewardContext context, Directive directive) throws RewardException { + String type = normalize(directive.type()); + switch (type) { + case "empty" -> { } + case "conditional" -> applyConditional(context, directive); + case "list" -> applyList(context, directive); + case "custommodeldata" -> customModelData(context, directive); + case "itemdestroy" -> { + if (context.removal()) throw new RewardException("ItemDestroy 不可拆卸"); + context.item(new ItemStack(Material.AIR)); + } + case "durability" -> durability(context, directive); + case "enchant", "enchantset" -> enchant(context, directive, type.equals("enchantset")); + case "itemflag" -> itemFlag(context, directive); + case "itemset" -> itemSet(context, directive); + case "loreadd" -> loreAdd(context, directive); + case "loredel" -> loreDelete(context, directive); + case "lorereplace", "loremultireplace" -> loreReplace(context, directive, type.equals("loremultireplace")); + case "lorevar" -> textVariable(context, directive, false); + case "material" -> material(context, directive); + case "name" -> name(context, directive); + case "namevar" -> textVariable(context, directive, true); + case "skilladd", "skilldel", "skillreplace" -> skill(context, directive, type); + case "skilltonbt" -> skillToNbt(context, directive); + case "unbreakable" -> unbreakable(context); + case "attribute" -> attribute(context, directive); + case "nbtdouble" -> nbtDouble(context, directive); + case "nbtstring" -> nbtString(context, directive); + case "mythicitem" -> mythicItem(context, directive); + case "mmoability" -> mmoAbility(context, directive); + case "gemremove" -> gemRemove(context, directive); + default -> throw new RewardException("未知物品奖励: " + directive.type()); + } + } + + private void applyConditional(RewardContext context, Directive directive) throws RewardException { + String condition = directive.argument("false", "condition", "c"); + boolean passed = directive.booleanArgument(false, "roman") + ? Math.abs(expression.evaluateRoman(condition, context.player(), context.item(), Map.of("v", 0.0))) > 1.0E-9 + : expression.test(condition, context.player(), context.item(), Map.of("v", 0.0)); + if (!passed) + throw new RewardException("条件不满足: " + condition); + Directive nested = parser.parse(directive.argument("", "reward", "r")); + if (isPlayerReward(nested)) applyPlayer(context, nested); else applyItem(context, nested); + } + + private void applyList(RewardContext context, Directive directive) throws RewardException { + String source = directive.source(); + int start = source.indexOf('{'); + int end = source.lastIndexOf('}'); + if (start < 0 || end <= start) return; + for (String part : parser.splitTopLevel(source.substring(start + 1, end), ';')) { + int equals = part.indexOf('='); + String value = equals < 0 ? part : part.substring(equals + 1); + if (!value.isBlank()) { + Directive nested = parser.parse(value); + if (isPlayerReward(nested)) applyPlayer(context, nested); else applyItem(context, nested); + } + } + } + + private void customModelData(RewardContext context, Directive directive) throws RewardException { + ItemMeta meta = context.item().getItemMeta(); + if (context.removal()) meta.setCustomModelData(null); + else { + if (!directive.booleanArgument(true, "force", "f") && meta.hasCustomModelData()) throw new RewardException("物品已有 CustomModelData"); + meta.setCustomModelData(directive.intArgument(0, "id")); + } + context.item().setItemMeta(meta); + } + + private void durability(RewardContext context, Directive directive) throws RewardException { + ItemMeta meta = context.item().getItemMeta(); + if (!(meta instanceof Damageable damageable)) throw new RewardException("物品没有耐久度"); + if (!context.removal()) damageable.setDamage(Math.max(0, damageable.getDamage() - directive.intArgument(0, "amount"))); + context.item().setItemMeta(meta); + } + + private void enchant(RewardContext context, Directive directive, boolean set) throws RewardException { + String name = directive.argument("", "name"); + Enchantment enchantment = enchantment(name); + if (enchantment == null) throw new RewardException("未知附魔: " + name); + ItemMeta meta = context.item().getItemMeta(); + int current = meta.getEnchantLevel(enchantment); + int level = directive.intArgument(1, "level", "l"); + int result = context.removal() ? Math.max(0, current - level) : set ? level : current + level; + int limit = directive.intArgument(0, "limit"); + if (!context.removal() && limit > 0 && result > limit) throw new RewardException("附魔超过限制: " + name); + if (result == 0) meta.removeEnchant(enchantment); else meta.addEnchant(enchantment, result, true); + context.item().setItemMeta(meta); + } + + private void itemFlag(RewardContext context, Directive directive) throws RewardException { + try { + String configured = directive.argument(directive.positional().isEmpty() + ? "" : directive.positional().getFirst(), "flag", "f"); + ItemFlag flag = ItemFlag.valueOf(configured.toUpperCase(Locale.ROOT)); + ItemMeta meta = context.item().getItemMeta(); + if (context.removal()) meta.removeItemFlags(flag); else meta.addItemFlags(flag); + context.item().setItemMeta(meta); + } catch (IllegalArgumentException exception) { + throw new RewardException("未知物品标志", exception); + } + } + + private void itemSet(RewardContext context, Directive directive) throws RewardException { + if (context.removal()) throw new RewardException("ItemSet 不可拆卸"); + String gemName = directive.argument("", "gem"); + if (!gemName.isBlank()) { + GemDefinition gem = gemRegistry.find(gemName).orElseThrow(() -> new RewardException("未知宝石: " + gemName)); + int amount = Math.max(1, directive.intArgument(1, "amount")); + context.item(itemCodec.createGem(gem, Math.min(amount, gem.material().getMaxStackSize()))); + int remaining = amount - context.item().getAmount(); + while (remaining > 0) { + int stack = Math.min(remaining, gem.material().getMaxStackSize()); + giveToInventory(context.player(), itemCodec.createGem(gem, stack)); + remaining -= stack; + } + return; + } + int amount = Math.max(1, directive.intArgument(1, "amount")); + ItemStack item = configuredItem(directive, amount); + context.item(item); + int remaining = amount - item.getAmount(); + while (remaining > 0) { + ItemStack extra = configuredItem(directive, remaining); + giveToInventory(context.player(), extra); + remaining -= extra.getAmount(); + } + } + + private void loreAdd(RewardContext context, Directive directive) throws RewardException { + rewardLimit(context, directive); + ItemMeta meta = context.item().getItemMeta(); + List lore = meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + String value = itemCodec.color(directive.argument("", "lore")); + if (context.removal()) { + if (!lore.remove(value)) throw new RewardException("找不到要拆卸的 Lore"); + } else insertLore(lore, value, directive); + meta.setLore(lore); + context.item().setItemMeta(meta); + } + + private void insertLore(List lore, String value, Directive directive) throws RewardException { + String mode = directive.argument("last", "mode").toLowerCase(Locale.ROOT); + String locator = itemCodec.strip(directive.argument("", "locator")); + int found = -1; + for (int index = 0; index < lore.size(); index++) if (itemCodec.strip(lore.get(index)).contains(locator)) { found = index; break; } + switch (mode) { + case "first" -> lore.addFirst(value); + case "last" -> lore.add(value); + case "before" -> lore.add(found < 0 ? 0 : found, value); + case "after" -> lore.add(found < 0 ? lore.size() : found + 1, value); + case "line" -> { + if (found < 0 && !directive.booleanArgument(true, "force", "f")) throw new RewardException("找不到 Lore 定位器"); + if (found < 0) lore.add(itemCodec.color(directive.argument("", "locator")) + value); + else lore.set(found, lore.get(found) + value); + } + default -> throw new RewardException("未知 Lore 模式: " + mode); + } + } + + private void loreDelete(RewardContext context, Directive directive) throws RewardException { + ItemMeta meta = context.item().getItemMeta(); + List lore = meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + String expected = itemCodec.color(directive.argument("", "lore")); + if (context.removal()) { + lore.add(directive.argument("last", "mode").equalsIgnoreCase("first") ? 0 : lore.size(), expected); + } else { + List matches = new ArrayList<>(); + for (int index = 0; index < lore.size(); index++) if (lore.get(index).contains(expected)) matches.add(index); + if (matches.isEmpty()) throw new RewardException("找不到要删除的 Lore"); + String mode = directive.argument("first", "mode"); + if (mode.equalsIgnoreCase("all")) matches.reversed().forEach(index -> lore.remove((int) index)); + else lore.remove((int) (mode.equalsIgnoreCase("last") ? matches.getLast() : matches.getFirst())); + } + meta.setLore(lore); + context.item().setItemMeta(meta); + } + + private void loreReplace(RewardContext context, Directive directive, boolean multiple) throws RewardException { + rewardLimit(context, directive); + ItemMeta meta = context.item().getItemMeta(); + List lore = meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + String configuredOld = multiple ? String.join(";", parser.delimitedArgument(directive, Set.of("old"), Set.of("lore", "new", "limit"))) + : directive.argument("", "old"); + String configuredNew = multiple ? String.join(";", parser.delimitedArgument(directive, Set.of("lore"), Set.of("limit"))) + : directive.argument("", "lore", "new"); + if (multiple && configuredNew.isEmpty()) + configuredNew = String.join(";", parser.delimitedArgument(directive, Set.of("new"), Set.of("limit"))); + String old = itemCodec.color(context.removal() ? configuredNew : configuredOld); + String replacement = itemCodec.color(context.removal() ? configuredOld : configuredNew); + if (multiple) { + List oldLines = List.of(old.split(";", -1)); + List newLines = List.of(replacement.split(";", -1)); + int start = sequenceIndex(lore, oldLines); + if (start < 0) throw new RewardException("找不到多行 Lore"); + for (int index = 0; index < oldLines.size(); index++) lore.remove(start); + lore.addAll(start, newLines); + } else { + String mode = directive.argument("first", "mode").toLowerCase(Locale.ROOT); + boolean replaced = false; + if (mode.equals("line")) { + String locator = itemCodec.strip(directive.argument("", "locator")); + for (int index = 0; index < lore.size(); index++) { + if (!itemCodec.strip(lore.get(index)).contains(locator) || !lore.get(index).contains(old)) continue; + lore.set(index, lore.get(index).replace(old, replacement)); + replaced = true; + break; + } + } else if (mode.equals("last")) { + for (int index = lore.size() - 1; index >= 0; index--) if (lore.get(index).contains(old)) { + lore.set(index, lore.get(index).replace(old, replacement)); + replaced = true; + break; + } + } else { + for (int index = 0; index < lore.size(); index++) if (lore.get(index).contains(old)) { + lore.set(index, lore.get(index).replace(old, replacement)); + replaced = true; + if (!mode.equals("all")) break; + } + } + if (!replaced) throw new RewardException("找不到要替换的 Lore"); + } + meta.setLore(lore); + context.item().setItemMeta(meta); + } + + private void rewardLimit(RewardContext context, Directive directive) throws RewardException { + int limit = directive.intArgument(0, "limit"); + if (limit <= 0) return; + String key = "CMAGICGEM_LIMIT_" + Integer.toUnsignedString(directive.source().hashCode(), 36); + int current = NBT.get(context.item(), nbt -> { return nbt.hasTag(key) ? nbt.getInteger(key) : 0; }); + if (!context.removal() && current >= limit) throw new RewardException("该奖励已达到次数上限"); + int changedCount = context.removal() ? Math.max(0, current - 1) : current + 1; + ItemStack changed = context.item().clone(); + NBT.modify(changed, nbt -> { + if (changedCount == 0) nbt.removeKey(key); else nbt.setInteger(key, changedCount); + }); + context.item(changed); + } + + private int sequenceIndex(List source, List sequence) { + outer: for (int start = 0; start + sequence.size() <= source.size(); start++) { + for (int index = 0; index < sequence.size(); index++) if (!source.get(start + index).contains(sequence.get(index))) continue outer; + return start; + } + return -1; + } + + private void textVariable(RewardContext context, Directive directive, boolean displayName) throws RewardException { + ItemMeta meta = context.item().getItemMeta(); + List texts = displayName ? new ArrayList<>(List.of(meta.hasDisplayName() ? meta.getDisplayName() : "")) + : meta.hasLore() && meta.getLore() != null ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + String marker = itemCodec.strip(directive.argument("", displayName ? "name" : "lore")); + int selected = -1; + Matcher matcher = null; + for (int index = 0; index < texts.size(); index++) { + String clean = itemCodec.strip(texts.get(index)); + int markerIndex = clean.indexOf(marker); + if (markerIndex < 0) continue; + Matcher candidate = NUMBER.matcher(clean.substring(markerIndex + marker.length())); + if (candidate.find()) { selected = index; matcher = candidate; break; } + } + if (selected < 0 || matcher == null) { + if (context.removal()) throw new RewardException("找不到数字变量: " + marker); + boolean mayCreate = displayName ? directive.booleanArgument(false, "force", "f") + : directive.arguments().containsKey("mode"); + if (!mayCreate) throw new RewardException("找不到数字变量: " + marker); + String formula = directive.argument("v", "var"); + double created = expression.evaluate(formula, context.player(), context.item(), Map.of("v", 0.0)); + String formatted = format(created, directive.argument("%.0f", "format")); + String label = itemCodec.color(directive.argument("", displayName ? "name" : "lore")); + String newText = directive.booleanArgument(false, "prefix") ? formatted + label : label + formatted; + if (displayName) { + meta.setDisplayName(newText); + } else { + insertLore(texts, newText, directive); + meta.setLore(texts); + } + context.item().setItemMeta(meta); + return; + } + double current = Double.parseDouble(matcher.group()); + String formula = directive.argument("v", context.removal() ? "inv" : "var"); + if (context.removal() && !directive.arguments().containsKey("inv")) throw new RewardException("奖励没有拆卸公式"); + double result = expression.evaluate(formula, context.player(), context.item(), Map.of("v", current)); + String limit = directive.argument("", "limit"); + if (!context.removal() && !limit.isBlank() + && !expression.test(limit, context.player(), context.item(), Map.of("v", result))) + throw new RewardException("变量超过限制"); + String formatted = format(result, directive.argument("%.0f", "format")); + String original = texts.get(selected); + String clean = itemCodec.strip(original); + int visibleMarker = clean.indexOf(marker); + int visibleNumber = visibleMarker + marker.length() + matcher.start(); + int rawStart = rawIndexForVisible(original, visibleNumber); + Matcher rawMatcher = NUMBER.matcher(original); + if (!rawMatcher.find(rawStart)) throw new RewardException("无法定位彩色文本中的数字"); + texts.set(selected, original.substring(0, rawMatcher.start()) + formatted + original.substring(rawMatcher.end())); + if (displayName) meta.setDisplayName(texts.getFirst()); else meta.setLore(texts); + context.item().setItemMeta(meta); + } + + private String format(double value, String format) { + if (format.equalsIgnoreCase("ROMAN")) return roman((int) Math.round(value)); + try { return String.format(Locale.ROOT, format, value); } + catch (RuntimeException ignored) { return Double.toString(value); } + } + + private int rawIndexForVisible(String text, int visibleIndex) { + int visible = 0; + for (int raw = 0; raw < text.length(); raw++) { + if (text.charAt(raw) == ChatColor.COLOR_CHAR && raw + 1 < text.length()) { + raw++; + continue; + } + if (visible == visibleIndex) return raw; + visible++; + } + return text.length(); + } + + private String roman(int number) { + if (number <= 0) return Integer.toString(number); + int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; + String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + StringBuilder result = new StringBuilder(); + for (int index = 0; index < values.length; index++) while (number >= values[index]) { result.append(symbols[index]); number -= values[index]; } + return result.toString(); + } + + private void material(RewardContext context, Directive directive) throws RewardException { + if (context.removal()) return; + Material material = MaterialResolver.resolve(directive.argument("", "material", "m")); + if (material == null) throw new RewardException("未知材料"); + context.item().setType(material); + } + + private void name(RewardContext context, Directive directive) { + if (context.removal()) return; + ItemMeta meta = context.item().getItemMeta(); + meta.setDisplayName(itemCodec.color(directive.argument("", "name", "n"))); + context.item().setItemMeta(meta); + } + + private void skill(RewardContext context, Directive directive, String type) throws RewardException { + List skills = itemCodec.skills(context.item()); + String name = directive.argument("", "name", "skill", "s", "new"); + String old = directive.argument("", "old"); + boolean effectiveRemoval = context.removal(); + switch (type) { + case "skilladd" -> { if (effectiveRemoval) skills.remove(name); else if (!skills.contains(name)) skills.add(name); else throw new RewardException("技能已经存在"); } + case "skilldel" -> { String target = effectiveRemoval ? name : name; if (!skills.remove(target) && !effectiveRemoval) throw new RewardException("技能不存在"); if (effectiveRemoval && !skills.contains(target)) skills.add(target); } + case "skillreplace" -> { String from = effectiveRemoval ? name : old; String to = effectiveRemoval ? old : name; int index = skills.indexOf(from); if (index < 0) throw new RewardException("技能不存在: " + from); skills.set(index, to); } + case "skilltonbt" -> { + if (effectiveRemoval) skills.remove(name); + else if (!skills.contains(name)) skills.add(name); + } + default -> { } + } + context.item(itemCodec.setSkills(context.item(), skills)); + } + + private void skillToNbt(RewardContext context, Directive directive) throws RewardException { + ItemStack changed = context.item().clone(); + ItemMeta meta = changed.getItemMeta(); + List lore = meta.hasLore() && meta.getLore() != null + ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); + List nbtSkills = itemCodec.skills(changed); + String requested = directive.argument("", "name", "skill", "s"); + String snapshotKey = "CMAGICGEM_HIDDEN_SKILL_LORE"; + JsonObject snapshots; + try { + String encoded = NBT.get(changed, nbt -> nbt.hasTag(snapshotKey) ? nbt.getString(snapshotKey) : "{}"); + snapshots = JsonParser.parseString(encoded).getAsJsonObject(); + } catch (RuntimeException exception) { + snapshots = new JsonObject(); + } + + boolean changedAny = false; + if (context.removal()) { + List names = requested.isBlank() ? new ArrayList<>(snapshots.keySet()) : List.of(requested); + for (String name : names) { + JsonElement saved = snapshots.get(name); + if (saved == null || !saved.isJsonArray()) continue; + nbtSkills.remove(name); + for (JsonElement line : saved.getAsJsonArray()) { + String value = line.getAsString(); + if (!lore.contains(value)) lore.add(value); + } + snapshots.remove(name); + changedAny = true; + } + } else { + Collection candidates = requested.isBlank() + ? skillRegistry.all() + : List.of(skillRegistry.find(requested) + .orElseThrow(() -> new RewardException("未知技能: " + requested))); + for (SkillDefinition skill : candidates) { + if (skill.lore() == null || skill.lore().isBlank()) continue; + String prefix = itemCodec.strip(skill.lore()); + JsonArray removed = new JsonArray(); + for (int index = lore.size() - 1; index >= 0; index--) { + if (!itemCodec.strip(lore.get(index)).startsWith(prefix)) continue; + removed.add(lore.remove(index)); + } + if (removed.isEmpty()) continue; + if (!nbtSkills.contains(skill.name())) nbtSkills.add(skill.name()); + snapshots.add(skill.name(), removed); + changedAny = true; + } + } + if (!changedAny) throw new RewardException(context.removal() + ? "没有可恢复的隐藏技能" : "装备上没有可隐藏的技能 Lore"); + meta.setLore(lore); + changed.setItemMeta(meta); + changed = itemCodec.setSkills(changed, nbtSkills); + JsonObject savedSnapshots = snapshots; + NBT.modify(changed, nbt -> { + if (savedSnapshots.isEmpty()) nbt.removeKey(snapshotKey); + else nbt.setString(snapshotKey, savedSnapshots.toString()); + }); + context.item(changed); + } + + private void unbreakable(RewardContext context) { + ItemMeta meta = context.item().getItemMeta(); + meta.setUnbreakable(!context.removal()); + context.item().setItemMeta(meta); + } + + private void attribute(RewardContext context, Directive directive) throws RewardException { + String name = directive.argument("", "name").toLowerCase(Locale.ROOT); + Attribute attribute = attribute(name); + if (attribute == null) throw new RewardException("未知属性: " + name); + int operationIndex = directive.intArgument(0, "operation", "op"); + AttributeModifier.Operation operation = AttributeModifier.Operation.values()[Math.max(0, Math.min(2, operationIndex))]; + EquipmentSlotGroup slot = slotGroup(directive.argument("auto", "slot"), context.item().getType()); + NamespacedKey key = new NamespacedKey(plugin, "gem_" + name + '_' + operationIndex + '_' + slot.toString().toLowerCase(Locale.ROOT)); + ItemMeta meta = context.item().getItemMeta(); + Collection modifiers = meta.getAttributeModifiers(attribute); + double current = 0; + if (modifiers != null) for (AttributeModifier modifier : List.copyOf(modifiers)) if (modifier.getKey().equals(key)) { current = modifier.getAmount(); meta.removeAttributeModifier(attribute, modifier); } + String formula = directive.argument("v", context.removal() ? "inv" : "var"); + if (context.removal() && !directive.arguments().containsKey("inv")) throw new RewardException("属性奖励没有拆卸公式"); + double result = expression.evaluate(formula, context.player(), context.item(), Map.of("v", current)); + double limit = number(directive.argument("0", "limit")); + if (!context.removal() && limit > 0 && result > limit) throw new RewardException("属性超过限制"); + if (Math.abs(result) > 1.0E-9) meta.addAttributeModifier(attribute, new AttributeModifier(key, result, operation, slot)); + context.item().setItemMeta(meta); + } + + private void nbtDouble(RewardContext context, Directive directive) throws RewardException { + String key = directive.argument("", "name", "key"); + if (key.isBlank()) throw new RewardException("NBTDouble 缺少 name"); + double current; + try { + current = NBT.get(context.item(), nbt -> nbt.hasTag(key) ? nbt.getDouble(key) : 0.0); + } catch (RuntimeException exception) { + throw new RewardException("无法读取 NBT: " + key, exception); + } + String formula = directive.argument("v", context.removal() ? "inv" : "var"); + if (context.removal() && !directive.arguments().containsKey("inv")) + throw new RewardException("NBTDouble 奖励没有拆卸公式"); + double result; + try { + result = expression.evaluate(formula, context.player(), context.item(), Map.of("v", current)); + } catch (RuntimeException exception) { + throw new RewardException("NBTDouble 表达式无效", exception); + } + String limit = directive.argument("", "limit"); + if (!context.removal() && !limit.isBlank() + && !expression.test(limit, context.player(), context.item(), Map.of("v", result))) + throw new RewardException("NBTDouble 超过限制"); + ItemStack changed = context.item().clone(); + NBT.modify(changed, nbt -> { nbt.setDouble(key, result); }); + context.item(changed); + } + + private void nbtString(RewardContext context, Directive directive) throws RewardException { + String key = directive.argument("", "name", "key"); + if (key.isBlank()) throw new RewardException("NBTString 缺少 name"); + ItemStack changed = context.item().clone(); + if (context.removal()) { + if (!directive.arguments().containsKey("var") && !directive.arguments().containsKey("value")) + throw new RewardException("删除 NBTString 的奖励不可拆卸"); + NBT.modify(changed, nbt -> { nbt.removeKey(key); }); + context.item(changed); + return; + } + String value = directive.argument("", "var", "value"); + boolean exists = NBT.get(changed, nbt -> { return nbt.hasTag(key); }); + if (exists && !directive.booleanArgument(false, "force", "f")) + throw new RewardException("NBTString 已存在: " + key); + NBT.modify(changed, nbt -> { + if (value.isEmpty()) nbt.removeKey(key); else nbt.setString(key, value); + }); + context.item(changed); + } + + private void mythicItem(RewardContext context, Directive directive) throws RewardException { + String name = directive.argument("", "name", "id", "type"); + if (name.isBlank()) throw new RewardException("MythicItem 缺少 name"); + ItemStack changed = context.item().clone(); + if (context.removal()) { + NBT.modify(changed, nbt -> { nbt.removeKey("MYTHIC_TYPE"); }); + if (plugin.getServer().getPluginManager().isPluginEnabled("MythicArtifacts")) { + ItemMeta meta = changed.getItemMeta(); + meta.setDisplayName(null); + changed.setItemMeta(meta); + } + context.item(changed); + return; + } + ItemStack template = mythic.item(name) + .orElseThrow(() -> new RewardException("未知 MythicMobs 物品: " + name)); + boolean force = directive.booleanArgument(false, "force", "f"); + boolean exists = NBT.get(changed, nbt -> { return nbt.hasTag("MYTHIC_TYPE"); }); + if (exists && !force) throw new RewardException("物品已有 MYTHIC_TYPE"); + NBT.modify(changed, nbt -> { nbt.setString("MYTHIC_TYPE", name); }); + if (plugin.getServer().getPluginManager().isPluginEnabled("MythicArtifacts")) { + ItemMeta current = changed.getItemMeta(); + ItemMeta source = template.getItemMeta(); + if (current.hasDisplayName() && !force) throw new RewardException("物品已有显示名"); + changed.setType(template.getType()); + current = changed.getItemMeta(); + if (source.hasDisplayName()) current.setDisplayName(source.getDisplayName()); + changed.setItemMeta(current); + } + context.item(changed); + } + + private void mmoAbility(RewardContext context, Directive directive) throws RewardException { + String id = directive.argument("", "id", "name", "type"); + String mode = directive.argument("RIGHT_CLICK", "mode"); + if (id.isBlank()) throw new RewardException("MMOAbility 缺少 id"); + String abilityKey = "MMOITEMS_ABILITY"; + String marker = "CMAGICGEM_MMO_" + Integer.toUnsignedString(directive.source().hashCode(), 36); + String snapshotKey = marker + "_SNAPSHOT"; + ItemStack changed = context.item().clone(); + String encoded = NBT.get(changed, nbt -> { return nbt.hasTag(abilityKey) ? nbt.getString(abilityKey) : "[]"; }); + JsonArray abilities; + try { abilities = JsonParser.parseString(encoded).getAsJsonArray(); } + catch (RuntimeException exception) { throw new RewardException("MMOItems 技能 NBT 无效", exception); } + JsonObject selected = null; + int selectedIndex = -1; + for (int index = 0; index < abilities.size(); index++) { + JsonElement element = abilities.get(index); + if (!element.isJsonObject()) continue; + JsonObject object = element.getAsJsonObject(); + if (jsonString(object, "Id").equalsIgnoreCase(id) + && jsonString(object, "CastMode").equalsIgnoreCase(mode)) { + selected = object; + selectedIndex = index; + break; + } + } + int applications = NBT.get(changed, nbt -> { return nbt.hasTag(marker) ? nbt.getInteger(marker) : 0; }); + if (context.removal()) { + if (applications <= 0) throw new RewardException("MMOAbility 没有可拆卸记录"); + if (applications == 1) { + String snapshot = NBT.get(changed, nbt -> { return nbt.hasTag(snapshotKey) ? nbt.getString(snapshotKey) : ""; }); + if (selectedIndex >= 0) abilities.remove(selectedIndex); + if (!snapshot.isBlank()) abilities.add(JsonParser.parseString(snapshot)); + NBT.modify(changed, nbt -> { + nbt.removeKey(marker); + nbt.removeKey(snapshotKey); + nbt.setString(abilityKey, abilities.toString()); + }); + context.item(rebuildMMOItem(changed)); + return; + } + } else if (applications == 0) { + String snapshot = selected == null ? "" : selected.toString(); + NBT.modify(changed, nbt -> { nbt.setString(snapshotKey, snapshot); }); + } + if (selected == null) { + selected = new JsonObject(); + selected.addProperty("Id", id.toUpperCase(Locale.ROOT)); + selected.addProperty("CastMode", mode.toUpperCase(Locale.ROOT)); + selected.addProperty("Hide", false); + selected.add("Modifiers", new JsonObject()); + abilities.add(selected); + } + JsonObject modifiers = selected.has("Modifiers") && selected.get("Modifiers").isJsonObject() + ? selected.getAsJsonObject("Modifiers") : new JsonObject(); + selected.add("Modifiers", modifiers); + for (Map.Entry entry : directive.arguments().entrySet()) { + if (Set.of("id", "name", "mode").contains(entry.getKey())) continue; + AbilityModifier modifier = abilityModifier(entry.getValue()); + double current = modifiers.has(entry.getKey()) ? modifiers.get(entry.getKey()).getAsDouble() : modifier.initial(); + String formula = context.removal() ? modifier.inverse() : modifier.formula(); + if (context.removal() && formula == null) throw new RewardException("MMOAbility 属性没有拆卸公式: " + entry.getKey()); + double result = expression.evaluate(formula == null ? "v" : formula, + context.player(), context.item(), Map.of("v", current)); + if (!context.removal() && modifier.limit() != null + && !expression.test(modifier.limit(), context.player(), context.item(), Map.of("v", result))) + throw new RewardException("MMOAbility 属性超过限制: " + entry.getKey()); + modifiers.addProperty(entry.getKey(), result); + } + int count = context.removal() ? applications - 1 : applications + 1; + NBT.modify(changed, nbt -> { + nbt.setString(abilityKey, abilities.toString()); + if (count <= 0) nbt.removeKey(marker); else nbt.setInteger(marker, count); + }); + context.item(rebuildMMOItem(changed)); + } + + private ItemStack rebuildMMOItem(ItemStack item) throws RewardException { + try { + return mmoItems.rebuildIfMMOItem(item); + } catch (IllegalStateException exception) { + throw new RewardException(exception.getMessage(), exception); + } + } + + private String jsonString(JsonObject object, String key) { + return object.has(key) ? object.get(key).getAsString() : ""; + } + + private AbilityModifier abilityModifier(String source) throws RewardException { + int open = source.indexOf('{'); + double initial = number(open < 0 ? source : source.substring(0, open)); + if (open < 0) return new AbilityModifier(initial, "v", "v", null); + int close = source.lastIndexOf('}'); + if (close < open) throw new RewardException("MMOAbility 属性格式错误: " + source); + Directive nested = parser.parse("Modifier" + source.substring(open, close + 1)); + return new AbilityModifier(initial, nested.argument("v", "var"), + nested.arguments().containsKey("inv") ? nested.argument("", "inv") : null, + nested.arguments().containsKey("limit") ? nested.argument("", "limit") : null); + } + + private record AbilityModifier(double initial, String formula, String inverse, String limit) { } + + private void gemRemove(RewardContext context, Directive directive) throws RewardException { + String name = directive.argument("", "name", "gem"); + GemDefinition removedGem = gemRegistry.find(name).orElseThrow(() -> new RewardException("未知宝石: " + name)); + Map embedded = new LinkedHashMap<>(itemCodec.embedded(context.item())); + int count = embedded.getOrDefault(removedGem.name(), 0); + if (count <= 0) throw new RewardException("装备未镶嵌宝石: " + removedGem.name()); + if (directive.booleanArgument(true, "clear", "undo")) { + RewardContext removal = new RewardContext(context.player(), removedGem, context.item(), true); + removal.item(applyItemRewards(removal, removedGem.rewards(), Directive.Phase.REMOVE)); + context.item(removal.item()); + } + embedded.compute(removedGem.name(), (ignored, value) -> value == null || value <= 1 ? null : value - 1); + context.item(itemCodec.setEmbedded(context.item(), embedded)); + } + + private void applyPlayer(RewardContext context, Directive directive) throws RewardException { + Player player = context.player(); + switch (normalize(directive.type())) { + case "chat" -> sendMessages(context, directive, false); + case "actionbar" -> sendMessages(context, directive, true); + case "title" -> title(context, directive); + case "command" -> command(player, directive); + case "explevel" -> player.giveExpLevels((int) Math.round(amount(context, directive))); + case "heal" -> player.setHealth(Math.max(0, Math.min(player.getHealth() + amount(context, directive), player.getMaxHealth()))); + case "itemedit" -> editItem(context, directive); + case "itemgive" -> giveItem(context, directive); + case "itemtake" -> takeItems(player, directive); + case "maxhealth" -> maxHealth(context, directive); + case "money" -> money(context, directive); + case "point" -> point(context, directive); + case "mythicitemgive" -> giveMythicItem(context, directive); + case "potion" -> potion(context, directive); + case "skill" -> invokeSkill(context, directive); + default -> throw new RewardException("未知玩家奖励: " + directive.type()); + } + } + + private List messages(Directive directive) { + List values = parser.delimitedArgument(directive, Set.of("messages", "message", "m"), + Set.of("interval", "fadein", "fadeout", "stay")); + return values.isEmpty() ? directive.positional() : values; + } + + private void sendMessages(RewardContext context, Directive directive, boolean actionBar) { + List messages = messages(directive); + int interval = Math.max(0, directive.intArgument(0, "interval")); + for (int index = 0; index < messages.size(); index++) { + String message = placeholders.resolve(context.player(), messages.get(index)); + Runnable send = () -> { + if (!context.player().isOnline()) return; + if (actionBar) context.player().sendActionBar(component(message)); + else context.player().sendMessage(itemCodec.color(message)); + }; + if (index == 0 || interval == 0) send.run(); + else Bukkit.getScheduler().runTaskLater(plugin, send, (long) interval * index); + } + } + + private void title(RewardContext context, Directive directive) { + List messages = messages(directive); + int interval = Math.max(0, directive.intArgument(0, "interval")); + for (int index = 0; index < messages.size(); index += 2) { + String mainText = placeholders.resolve(context.player(), messages.get(index)); + String subtitleText = placeholders.resolve(context.player(), index + 1 < messages.size() ? messages.get(index + 1) : ""); + Runnable send = () -> context.player().showTitle(Title.title(component(mainText), component(subtitleText), Title.Times.times( + Duration.ofMillis(directive.intArgument(10, "fadein") * 50L), + Duration.ofMillis(directive.intArgument(70, "stay") * 50L), + Duration.ofMillis(directive.intArgument(20, "fadeout") * 50L)))); + if (index == 0 || interval == 0) send.run(); + else Bukkit.getScheduler().runTaskLater(plugin, send, (long) interval * (index / 2)); + } + } + + private void command(Player player, Directive directive) { + String command = placeholders.resolve(player, directive.argument("", "commands", "command", "c")) + .replace("%player%", player.getName()); + if (directive.booleanArgument(true, "console")) { + ConsoleCommandSender console = Bukkit.getConsoleSender(); + Bukkit.dispatchCommand(console, command); + return; + } + boolean temporaryOp = directive.booleanArgument(false, "op") && !player.isOp(); + try { if (temporaryOp) player.setOp(true); Bukkit.dispatchCommand(player, command); } + finally { if (temporaryOp) player.setOp(false); } + } + + private void giveItem(RewardContext context, Directive directive) throws RewardException { + Player player = context.player(); + String gem = directive.argument("", "gem"); + if (!gem.isBlank()) { + GemDefinition definition = gemRegistry.find(gem).orElseThrow(() -> new RewardException("未知宝石: " + gem)); + giveToInventory(player, itemCodec.createGem(definition, Math.max(1, directive.intArgument(1, "amount")))); + return; + } + int amount = Math.max(1, directive.intArgument(1, "amount")); + while (amount > 0) { + ItemStack item = configuredItem(directive, amount); + giveToInventory(player, item); + amount -= item.getAmount(); + } + } + + private ItemStack configuredItem(Directive directive, int amount) throws RewardException { + String material = directive.argument(directive.positional().isEmpty() ? "" : directive.positional().getFirst(), + "material", "m"); + String lore = directive.argument("", "lore", "tips"); + List loreLines = lore.isBlank() ? List.of() : parser.splitTopLevel(lore, '|'); + Integer customModelData = directive.arguments().containsKey("custommodeldata") + ? directive.intArgument(0, "custommodeldata") : null; + ItemCodec.ItemAppearance appearance = new ItemCodec.ItemAppearance(material, amount, + directive.argument("", "display", "name"), loreLines, + directive.argument("", "texture"), ItemCodec.parseColor(directive.argument("", "color")), + directive.argument("", "effect", "effects"), customModelData, + directive.booleanArgument(false, "glow")); + try { + return itemCodec.createItem(appearance); + } catch (IllegalArgumentException exception) { + throw new RewardException(exception.getMessage(), exception); + } + } + + private void giveToInventory(Player player, ItemStack item) { + player.getInventory().addItem(item).values() + .forEach(left -> player.getWorld().dropItemNaturally(player.getLocation(), left)); + } + + private void editItem(RewardContext context, Directive directive) throws RewardException { + String slot = directive.argument("mainhand", "slot").toLowerCase(Locale.ROOT); + ItemStack equipped = equipmentItem(context.player(), slot); + if (equipped == null || equipped.getType().isAir()) throw new RewardException("指定槽位没有物品: " + slot); + Directive nested = parser.parse(directive.argument("", "reward", "r")); + if (isPlayerReward(nested)) throw new RewardException("ItemEdit 只能执行物品奖励"); + RewardContext nestedContext = new RewardContext(context.player(), context.gem(), equipped, context.removal()); + applyItem(nestedContext, nested); + setEquipmentItem(context.player(), slot, nestedContext.item()); + } + + private ItemStack equipmentItem(Player player, String slot) throws RewardException { + return switch (slot) { + case "hand", "mainhand", "main_hand" -> player.getInventory().getItemInMainHand(); + case "offhand", "off_hand" -> player.getInventory().getItemInOffHand(); + case "helmet", "head" -> player.getInventory().getHelmet(); + case "chest", "chestplate" -> player.getInventory().getChestplate(); + case "legs", "leggings" -> player.getInventory().getLeggings(); + case "feet", "boots" -> player.getInventory().getBoots(); + default -> { + try { yield player.getInventory().getItem(Integer.parseInt(slot)); } + catch (NumberFormatException exception) { throw new RewardException("未知装备槽位: " + slot); } + } + }; + } + + private void setEquipmentItem(Player player, String slot, ItemStack item) throws RewardException { + switch (slot) { + case "hand", "mainhand", "main_hand" -> player.getInventory().setItemInMainHand(item); + case "offhand", "off_hand" -> player.getInventory().setItemInOffHand(item); + case "helmet", "head" -> player.getInventory().setHelmet(item); + case "chest", "chestplate" -> player.getInventory().setChestplate(item); + case "legs", "leggings" -> player.getInventory().setLeggings(item); + case "feet", "boots" -> player.getInventory().setBoots(item); + default -> { + try { player.getInventory().setItem(Integer.parseInt(slot), item); } + catch (NumberFormatException exception) { throw new RewardException("未知装备槽位: " + slot); } + } + } + } + + private void takeItems(Player player, Directive directive) throws RewardException { + Map required = new LinkedHashMap<>(); + try { directive.arguments().forEach((name, value) -> required.put(name, Integer.parseInt(value))); } + catch (NumberFormatException exception) { throw new RewardException("ItemTake 数量无效", exception); } + for (Map.Entry entry : required.entrySet()) { + int found = 0; + for (ItemStack item : player.getInventory().getStorageContents()) + if (itemMatcher.matches(item, entry.getKey())) found += item.getAmount(); + if (found < entry.getValue()) throw new RewardException("物品不足: " + entry.getKey()); + } + for (Map.Entry entry : required.entrySet()) { + int remaining = entry.getValue(); + ItemStack[] contents = player.getInventory().getStorageContents(); + for (int index = 0; index < contents.length && remaining > 0; index++) { + ItemStack item = contents[index]; + if (!itemMatcher.matches(item, entry.getKey())) continue; + int removed = Math.min(remaining, item.getAmount()); + item.setAmount(item.getAmount() - removed); + if (item.getAmount() <= 0) contents[index] = null; + remaining -= removed; + } + player.getInventory().setStorageContents(contents); + } + } + + private void maxHealth(RewardContext context, Directive directive) throws RewardException { + Player player = context.player(); + var instance = player.getAttribute(Attribute.GENERIC_MAX_HEALTH); + if (instance == null) throw new RewardException("无法读取生命上限"); + double result = instance.getBaseValue() + amount(context, directive); + double limit = number(directive.argument("0", "limit")); + if (limit > 0 && result > limit) throw new RewardException("生命上限超过限制"); + instance.setBaseValue(Math.max(1, result)); + } + + private void money(RewardContext context, Directive directive) throws RewardException { + double amount = amount(context, directive); + if (!economy.available()) throw new RewardException("Vault 经济服务不可用"); + if (!economy.change(context.player(), amount)) throw new RewardException("金币不足或经济操作失败"); + } + + private void point(RewardContext context, Directive directive) throws RewardException { + int amount = (int) Math.round(amount(context, directive)); + if (!points.available()) throw new RewardException("PlayerPoints 不可用"); + if (!points.change(context.player(), amount)) throw new RewardException("点券不足或操作失败"); + } + + private void giveMythicItem(RewardContext context, Directive directive) throws RewardException { + String name = directive.argument("", "name", "id", "item"); + ItemStack item = mythic.item(name).orElseThrow(() -> new RewardException("未知 MythicMobs 物品: " + name)); + item.setAmount(Math.max(1, directive.intArgument(1, "amount"))); + giveToInventory(context.player(), item); + } + + private void invokeSkill(RewardContext context, Directive directive) throws RewardException { + String name = directive.argument("", "name", "skill", "s"); + if (skillInvoker == null) throw new RewardException("技能引擎不可用"); + if (skillRegistry.find(name).isPresent()) skillInvoker.accept(context.player(), name); + else mythic.cast(context.player(), name, null); + } + + private void potion(RewardContext context, Directive directive) throws RewardException { + Player player = context.player(); + String name = directive.argument("", "name", "type").toLowerCase(Locale.ROOT); + PotionEffectType type = PotionTypes.resolve(name); + if (type == null) throw new RewardException("未知药水效果: " + name); + if (context.removal()) { + player.removePotionEffect(type); + return; + } + int level = Math.max(1, directive.intArgument(1, "level", "l")); + int duration = Math.max(1, directive.intArgument(1, "duration", "d")); + player.addPotionEffect(new PotionEffect(type, duration * 20, level - 1, true, true, true)); + } + + private double amount(RewardContext context, Directive directive) throws RewardException { + try { + double amount = context.evaluatedNumber(directive, () -> expression.evaluate( + directive.argument("0", "amount", "level", "l"), context.player(), context.item(), Map.of())); + if (!Double.isFinite(amount)) throw new IllegalArgumentException("数值不是有限数"); + return context.removal() ? -amount : amount; + } + catch (RuntimeException exception) { throw new RewardException("无效数值表达式", exception); } + } + + private Component component(String message) { + return LegacyComponentSerializer.legacyAmpersand().deserialize(message == null ? "" : message); + } + + private Enchantment enchantment(String input) { + return EnchantmentResolver.resolve(input); + } + + private Attribute attribute(String name) { + String key = switch (name) { + case "health" -> "generic.max_health"; + case "move" -> "generic.movement_speed"; + case "attack_speed" -> "generic.attack_speed"; + case "attack_knockback" -> "generic.attack_knockback"; + case "damage" -> "generic.attack_damage"; + case "luck" -> "generic.luck"; + case "armor" -> "generic.armor"; + case "armor_toughness" -> "generic.armor_toughness"; + case "knockback_resistance" -> "generic.knockback_resistance"; + default -> name; + }; + return Registry.ATTRIBUTE.get(NamespacedKey.minecraft(key)); + } + + private EquipmentSlotGroup slotGroup(String input, Material material) { + String slot = input.toLowerCase(Locale.ROOT); + if (slot.equals("auto")) { + if (material.name().endsWith("_HELMET")) slot = "head"; + else if (material.name().endsWith("_CHESTPLATE") || material == Material.ELYTRA) slot = "chest"; + else if (material.name().endsWith("_LEGGINGS")) slot = "legs"; + else if (material.name().endsWith("_BOOTS")) slot = "feet"; + else slot = "mainhand"; + } + EquipmentSlotGroup group = EquipmentSlotGroup.getByName(slot); + return Objects.requireNonNullElse(group, EquipmentSlotGroup.ANY); + } + + private double number(String value) throws RewardException { + try { return Double.parseDouble(value); } + catch (NumberFormatException exception) { throw new RewardException("无效数字: " + value, exception); } + } + + private static String normalize(String type) { + return type.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/reward/RewardException.java b/src/main/java/cn/infstar/cMagicGem/reward/RewardException.java new file mode 100644 index 0000000..277e8aa --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/reward/RewardException.java @@ -0,0 +1,11 @@ +package cn.infstar.cmagicgem.reward; + +public final class RewardException extends Exception { + public RewardException(String message) { + super(message); + } + + public RewardException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/skill/SkillAction.java b/src/main/java/cn/infstar/cMagicGem/skill/SkillAction.java new file mode 100644 index 0000000..6b3d6ba --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/skill/SkillAction.java @@ -0,0 +1,10 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.dsl.Directive; + +import java.util.List; +import java.util.Map; + +public record SkillAction(Directive mechanic, String mechanicLine, TriggerType trigger, List triggerFilters, String target, + Map worldLimits) { +} diff --git a/src/main/java/cn/infstar/cMagicGem/skill/SkillDefinition.java b/src/main/java/cn/infstar/cMagicGem/skill/SkillDefinition.java new file mode 100644 index 0000000..c890d77 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/skill/SkillDefinition.java @@ -0,0 +1,13 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.dsl.Directive; + +import java.util.List; +import java.util.Set; + +public record SkillDefinition(String name, String lore, String itemName, String mythicType, Set slots, + String success, double cooldown, double duration, String cooldownTip, + String successTip, String failTip, + List actions, List conditions, + List triggerConditions, List targetConditions) { +} diff --git a/src/main/java/cn/infstar/cMagicGem/skill/SkillEngine.java b/src/main/java/cn/infstar/cMagicGem/skill/SkillEngine.java new file mode 100644 index 0000000..5b53db8 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/skill/SkillEngine.java @@ -0,0 +1,868 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.api.event.CMagicEquipmentChangeEvent; +import cn.infstar.cmagicgem.api.event.CMagicJumpEvent; +import cn.infstar.cmagicgem.api.event.CMagicPotionBuffEvent; +import cn.infstar.cmagicgem.api.event.CMagicSkillUseEvent; +import cn.infstar.cmagicgem.condition.ConditionEngine; +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.expression.DynamicExpression; +import cn.infstar.cmagicgem.item.ItemCodec; +import cn.infstar.cmagicgem.integration.MythicBridge; +import cn.infstar.cmagicgem.integration.ProtectionBridge; +import cn.infstar.cmagicgem.reward.RewardContext; +import cn.infstar.cmagicgem.reward.RewardEngine; +import cn.infstar.cmagicgem.reward.RewardException; +import cn.infstar.cmagicgem.util.MessageDispatcher; +import cn.infstar.cmagicgem.util.PotionTypes; +import de.tr7zw.changeme.nbtapi.NBT; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.EntityTameEvent; +import org.bukkit.event.entity.EntityTeleportEvent; +import org.bukkit.event.entity.LingeringPotionSplashEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; +import com.destroystokyo.paper.event.player.PlayerJumpEvent; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerAnimationType; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerItemBreakEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public final class SkillEngine implements Listener { + private final JavaPlugin plugin; + private final SkillRegistry registry; + private final ItemCodec itemCodec; + private final DirectiveParser parser; + private final DynamicExpression expressions; + private final ConditionEngine conditions; + private final RewardEngine rewards; + private final MythicBridge mythic; + private final ProtectionBridge protection; + private final MessageDispatcher messages; + private final Map> cooldowns = new ConcurrentHashMap<>(); + private final Map> durations = new ConcurrentHashMap<>(); + private final Map debuffs = new ConcurrentHashMap<>(); + private final Map counters = new ConcurrentHashMap<>(); + private long ticks; + + public SkillEngine(JavaPlugin plugin, SkillRegistry registry, ItemCodec itemCodec, DirectiveParser parser, + DynamicExpression expressions, ConditionEngine conditions, RewardEngine rewards, + MythicBridge mythic, ProtectionBridge protection, MessageDispatcher messages) { + this.plugin = plugin; + this.registry = registry; + this.itemCodec = itemCodec; + this.parser = parser; + this.expressions = expressions; + this.conditions = conditions; + this.rewards = rewards; + this.mythic = mythic; + this.protection = protection; + this.messages = messages; + Bukkit.getScheduler().runTaskTimer(plugin, this::timer, 1, 1); + } + + public void invoke(Player player, String skillName) { + SkillDefinition skill = registry.find(skillName).orElse(null); + if (skill == null) return; + ItemStack item = player.getInventory().getItemInMainHand(); + execute(player, item, "mainhand", skill, TriggerType.DEFAULT, List.of(), null, + new EventContext(null, player.getLocation())); + } + + public void signal(Player player, String signal) { + trigger(player, TriggerType.SIGNAL, List.of(signal), null, null, null); + } + + public void mythicKill(Player player, String mobId, Entity mob, Event event) { + trigger(player, TriggerType.KILL_MYTHICMOBS, List.of(mobId), mob, mob.getLocation(), event); + } + + private void timer() { + ticks++; + if (ticks % 1200 == 0) { + long now = System.currentTimeMillis(); + debuffs.entrySet().removeIf(entry -> entry.getValue() <= now); + counters.entrySet().removeIf(entry -> now - entry.getValue().lastUsed() > 1_800_000L); + } + for (Player player : Bukkit.getOnlinePlayers()) + trigger(player, TriggerType.TIMER, List.of(Long.toString(ticks)), null, null, null); + } + + private void trigger(Player player, TriggerType trigger, List information, + Entity target, Location targetLocation, Event event) { + if (debuffed(player, "SKILL")) return; + for (EquippedItem equipped : equipment(player)) { + for (SkillDefinition skill : skills(equipped.item(), equipped.slot())) + execute(player, equipped.item(), equipped.slot(), skill, trigger, information, target, + new EventContext(event, targetLocation)); + } + } + + private void triggerItem(Player player, ItemStack item, String slot, TriggerType trigger, + List information, Entity target, Location targetLocation, Event event) { + if (debuffed(player, "SKILL")) return; + for (SkillDefinition skill : skills(item, slot)) + execute(player, item, slot, skill, trigger, information, target, new EventContext(event, targetLocation)); + } + + private void execute(Player player, ItemStack item, String slot, SkillDefinition skill, TriggerType trigger, + List information, Entity target, EventContext eventContext) { + List actions = skill.actions().stream() + .filter(action -> action.trigger() == trigger) + .filter(action -> filter(action, information, trigger)) + .toList(); + if (actions.isEmpty()) return; + if (!checkConditions(skill.conditions(), player, player, item)) return; + if (!checkConditions(skill.triggerConditions(), player, player, item)) return; + if (!checkConditions(skill.targetConditions(), player, target, item)) return; + long now = System.currentTimeMillis(); + String stateKey = skill.name().toLowerCase(Locale.ROOT); + boolean timed = skill.duration() > 0; + boolean active = durations.computeIfAbsent(player.getUniqueId(), ignored -> new ConcurrentHashMap<>()) + .getOrDefault(stateKey, 0L) > now; + long ready = cooldowns.computeIfAbsent(player.getUniqueId(), ignored -> new ConcurrentHashMap<>()) + .getOrDefault(stateKey, 0L); + if (!active && ready > now) { + messages.cooldown(player, skill.cooldownTip(), (ready - now) / 1000.0, skill.cooldown()); + return; + } + if (!roll(skill.success(), player, item)) { + tip(player, skill.failTip()); + return; + } + CMagicSkillUseEvent useEvent = new CMagicSkillUseEvent(player, skill, item); + Bukkit.getPluginManager().callEvent(useEvent); + if (useEvent.isCancelled()) return; + boolean executed = false; + for (SkillAction action : actions) { + String type = normalize(action.mechanic().type()); + if (timed && !active && !type.equals("timestart")) continue; + if (timed && active && type.equals("timestart")) continue; + try { + mechanic(player, item, slot, skill, action, target, eventContext); + executed = true; + } catch (RewardException | RuntimeException exception) { + plugin.getLogger().warning("技能 " + skill.name() + " 执行失败: " + exception.getMessage()); + } + } + if (executed && !active && skill.cooldown() > 0) + cooldowns.get(player.getUniqueId()).put(stateKey, now + (long) (skill.cooldown() * 1000)); + if (executed) tip(player, skill.successTip()); + } + + private void mechanic(Player player, ItemStack item, String slot, SkillDefinition skill, SkillAction action, + Entity target, EventContext context) throws RewardException { + Directive mechanic = action.mechanic(); + switch (normalize(mechanic.type())) { + case "reward" -> { + ItemStack changed = applyReward(player, item, mechanic, false); + if (changed != item) replaceMatchingItem(player, item, changed); + } + case "rewardswitch" -> { } + case "switch" -> switchSkill(player, item, skill, mechanic); + case "potionbuff" -> potionBuff(player, item, mechanic, action.target(), target, action.worldLimits()); + case "blink" -> blink(player, item, mechanic, action.target(), target, context.location()); + case "timestart" -> timeStart(player, item, skill, mechanic); + case "timeadd" -> timeAdd(player, item, skill, mechanic); + case "debuff" -> debuff(player, item, mechanic, action.target(), target); + case "bloodsuck", "bl​​oodsuck", "blooksuck" -> bloodSuck(player, item, mechanic, context.event()); + case "shielddamage" -> shieldDamage(player, item, mechanic, action.target(), target); + case "damage" -> damage(player, item, mechanic, action.target(), target); + case "counter" -> counter(player, item, slot, mechanic, target, context); + case "cancelevent" -> { if (context.event() instanceof Cancellable cancellable) cancellable.setCancelled(true); } + default -> { + Location location = context.location() != null ? context.location() + : target == null ? player.getLocation() : target.getLocation(); + if (!mythic.castInline(player, action.mechanicLine(), target, location) + && !mythic.cast(player, mechanic.type(), target, location)) + plugin.getLogger().warning("未知技能机制或 MythicMobs 技能: " + mechanic.type()); + } + } + } + + private ItemStack applyReward(Player player, ItemStack item, Directive mechanic, boolean removal) + throws RewardException { + String nestedSource = mechanic.positional().isEmpty() ? mechanic.argument("", "reward", "r") + : mechanic.positional().getFirst(); + Directive nested = parser.parse(nestedSource); + RewardContext rewardContext = new RewardContext(player, null, item, removal); + rewards.applyDirective(rewardContext, nested); + return rewardContext.item(); + } + + private void switchSkill(Player player, ItemStack item, SkillDefinition source, Directive mechanic) + throws RewardException { + List cycle = new ArrayList<>(); + String first = mechanic.argument("", "skills", "list", "s"); + if (!first.isBlank()) cycle.addAll(parser.splitTopLevel(first, ';')); + cycle.addAll(mechanic.positional()); + if (cycle.isEmpty()) return; + List current = new ArrayList<>(itemCodec.skills(item)); + int selected = -1; + for (int index = 0; index < cycle.size(); index++) if (current.contains(cycle.get(index))) { selected = index; break; } + String previous = selected < 0 ? null : cycle.get(selected); + String next = cycle.get((selected + 1) % cycle.size()); + ItemStack changed = item; + if (previous != null) { + SkillDefinition oldSkill = registry.find(previous).orElse(null); + if (oldSkill != null) for (SkillAction action : oldSkill.actions()) + if (normalize(action.mechanic().type()).equals("rewardswitch")) + changed = applyReward(player, changed, action.mechanic(), true); + current.remove(previous); + } + if (!current.contains(next)) current.add(next); + changed = itemCodec.setSkills(changed, current); + SkillDefinition newSkill = registry.find(next).orElse(null); + if (newSkill != null) for (SkillAction action : newSkill.actions()) + if (normalize(action.mechanic().type()).equals("rewardswitch")) + changed = applyReward(player, changed, action.mechanic(), false); + replaceMatchingItem(player, item, changed); + } + + private void potionBuff(Player player, ItemStack item, Directive mechanic, String selector, Entity target, + Map worldLimits) + throws RewardException { + LivingEntity living = selectedLiving(player, selector, target); + if (living == null) return; + if (debuffed(living, "POTIONBUFF")) return; + String name = mechanic.argument("", "type", "potion", "p", "name").toLowerCase(Locale.ROOT); + PotionEffectType type = PotionTypes.resolve(name); + if (type == null) throw new RewardException("未知药水效果: " + name); + double levelValue = mechanic.booleanArgument(false, "roman") + ? expressions.evaluateRoman(mechanic.argument("1", "level", "l"), player, item, Map.of()) + : value(mechanic.argument("1", "level", "l"), player, item); + int level = Math.max(1, (int) Math.round(levelValue)); + String world = living.getWorld().getName().toLowerCase(Locale.ROOT); + Integer actionLimit = worldLimits.get(world); + String globalPath = "potion-global-limit." + living.getWorld().getName(); + Integer globalLimit = plugin.getConfig().contains(globalPath) ? plugin.getConfig().getInt(globalPath) : null; + if (actionLimit != null) level = Math.min(level, Math.max(0, actionLimit)); + if (globalLimit != null) level = Math.min(level, Math.max(0, globalLimit)); + if (level <= 0) return; + int duration = Math.max(1, (int) Math.round(value(mechanic.argument("1", "duration", "d"), player, item) * 20)); + int refresh = Math.max(0, (int) Math.round(value(mechanic.argument("0", "refresh"), player, item) * 20)); + PotionEffect old = living.getPotionEffect(type); + if (old != null && (old.getAmplifier() > level - 1 || old.getDuration() > refresh)) return; + CMagicPotionBuffEvent event = new CMagicPotionBuffEvent(player, living, type, level - 1, duration); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) living.addPotionEffect(new PotionEffect(type, event.getDurationTicks(), + event.getAmplifier(), true, true, true), true); + } + + private void blink(Player player, ItemStack item, Directive mechanic, String selector, Entity target, + Location eventLocation) { + double distance = Math.max(0, value(mechanic.argument("5", "distance", "d"), player, item)); + Location destination = switch (selector.toLowerCase(Locale.ROOT)) { + case "entity", "target", "targetentity" -> target == null ? null : target.getLocation(); + case "location" -> eventLocation; + case "bed" -> player.getBedSpawnLocation(); + case "up" -> player.getLocation().add(0, distance, 0); + case "down" -> player.getLocation().add(0, -distance, 0); + case "left" -> directional(player, distance, -90); + case "right" -> directional(player, distance, 90); + case "backward", "back" -> directional(player, distance, 180); + default -> directional(player, distance, 0); + }; + if (destination == null) return; + Location safe = safeDestination(player.getLocation(), destination, distance); + if (safe != null) player.teleport(safe); + } + + private Location directional(Player player, double distance, double yawOffset) { + Location origin = player.getLocation(); + Location facing = origin.clone(); + facing.setYaw(facing.getYaw() + (float) yawOffset); + Vector direction = facing.getDirection().normalize(); + return origin.clone().add(direction.multiply(distance)); + } + + private Location safeDestination(Location origin, Location requested, double limit) { + Vector delta = requested.toVector().subtract(origin.toVector()); + if (limit > 0 && delta.length() > limit) requested = origin.clone().add(delta.normalize().multiply(limit)); + for (int step = 0; step < 4; step++) { + Location candidate = requested.clone().add(0, step, 0); + if (candidate.getBlock().isPassable() && candidate.clone().add(0, 1, 0).getBlock().isPassable()) return candidate; + } + return null; + } + + private void timeStart(Player player, ItemStack item, SkillDefinition skill, Directive mechanic) { + durations.computeIfAbsent(player.getUniqueId(), ignored -> new ConcurrentHashMap<>()) + .put(skill.name().toLowerCase(Locale.ROOT), System.currentTimeMillis() + (long) (skill.duration() * 1000)); + tip(player, mechanic.argument("", "tip")); + } + + private void timeAdd(Player player, ItemStack item, SkillDefinition skill, Directive mechanic) { + long addition = (long) (value(mechanic.argument("0", "time", "duration"), player, item) * 1000); + durations.computeIfAbsent(player.getUniqueId(), ignored -> new ConcurrentHashMap<>()) + .merge(skill.name().toLowerCase(Locale.ROOT), addition, Long::sum); + tip(player, mechanic.argument("", "tip")); + } + + private void debuff(Player player, ItemStack item, Directive mechanic, String selector, Entity target) { + Entity selected = selector.equalsIgnoreCase("self") ? player : target; + if (selected == null) return; + String type = mechanic.argument("SKILL", "type").toUpperCase(Locale.ROOT); + long end = System.currentTimeMillis() + (long) (value(mechanic.argument("1", "duration", "d"), player, item) * 1000); + debuffs.put(selected.getUniqueId() + ":" + type, end); + } + + private void bloodSuck(Player player, ItemStack item, Directive mechanic, Event event) { + if (!(event instanceof EntityDamageEvent damage)) return; + double factor = value(mechanic.argument("0", "factor", "f", "k"), player, item); + player.setHealth(Math.min(player.getMaxHealth(), player.getHealth() + Math.max(0, damage.getFinalDamage() * factor))); + } + + private void shieldDamage(Player player, ItemStack item, Directive mechanic, String selector, Entity target) { + LivingEntity living = selectedLiving(player, selector, target); + if (living == null) return; + double damage = Math.max(0, value(mechanic.argument("0", "damage", "amount", "d"), player, item)); + living.setAbsorptionAmount(Math.max(0, living.getAbsorptionAmount() - damage)); + } + + private void damage(Player player, ItemStack item, Directive mechanic, String selector, Entity target) { + LivingEntity living = selectedLiving(player, selector, target); + if (living == null) return; + living.damage(Math.max(0, value(mechanic.argument("0", "damage", "amount", "d"), player, item)), player); + } + + private void counter(Player player, ItemStack item, String slot, Directive mechanic, Entity target, + EventContext context) { + String tag = mechanic.argument("default", "tag"); + long count; + if (mechanic.booleanArgument(false, "persist", "p")) { + String key = "cmagicgem_counter_" + Integer.toHexString(tag.hashCode()); + count = NBT.get(item, nbt -> { return nbt.hasTag(key) ? nbt.getLong(key) + 1 : 1L; }); + long stored = count; + NBT.modify(item, nbt -> { nbt.setLong(key, stored); }); + } else { + String itemId = NBT.get(item, nbt -> nbt.hasTag("CMAGICGEM_RUNTIME_ID") + ? nbt.getString("CMAGICGEM_RUNTIME_ID") : ""); + if (itemId.isBlank()) { + itemId = UUID.randomUUID().toString(); + String generated = itemId; + NBT.modify(item, nbt -> { nbt.setString("CMAGICGEM_RUNTIME_ID", generated); }); + } + String key = player.getUniqueId() + ":" + itemId + ':' + slot + ':' + + Integer.toUnsignedString(mechanic.source().hashCode(), 36) + ':' + tag; + long now = System.currentTimeMillis(); + CounterState state = counters.compute(key, (ignored, current) -> + new CounterState(current == null ? 1 : current.count() + 1, now)); + count = state.count(); + } + double dynamic = value(mechanic.argument("0", "dynamic", "d"), player, item); + if (!expressions.test(mechanic.argument("true", "condition", "c"), player, item, + Map.of("v", (double) count, "d", dynamic))) return; + List information = List.of(tag); + if (mechanic.booleanArgument(false, "allslot")) + trigger(player, TriggerType.COUNTER, information, target, context.location(), context.event()); + else triggerItem(player, item, slot, TriggerType.COUNTER, information, target, context.location(), context.event()); + } + + private boolean filter(SkillAction action, List information, TriggerType trigger) { + if (action.triggerFilters().isEmpty()) return true; + if (trigger == TriggerType.TIMER) { + try { + long interval = Math.max(1, Long.parseLong(action.triggerFilters().getFirst())); + return ticks % interval == 0; + } catch (NumberFormatException ignored) { return false; } + } + for (String expected : action.triggerFilters()) + for (String actual : information) + if (actual.equalsIgnoreCase(expected)) return true; + return false; + } + + private boolean checkConditions(List checks, Player caster, Entity subject, ItemStack item) { + for (Directive check : checks) { + String type = normalize(check.type()); + boolean passed = switch (type) { + case "noarmorstand" -> subject == null || subject.getType() != org.bukkit.entity.EntityType.ARMOR_STAND; + case "pvpmanager" -> subject instanceof Player player && protection.pvpEnabled(player); + case "residenceflag", "redidenseflag" -> protection.residenceFlag(caster, + subject == null ? caster.getLocation() : subject.getLocation(), check.argument("", "flag", "f")); + default -> conditions.check(check, subject instanceof Player player ? player : caster, item).success(); + }; + if (!passed) return false; + } + return true; + } + + private Collection skills(ItemStack item, String slot) { + if (item == null || item.getType().isAir()) return List.of(); + Set names = new LinkedHashSet<>(itemCodec.skills(item)); + String display = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? strip(item.getItemMeta().getDisplayName()) : ""; + List lore = item.hasItemMeta() && item.getItemMeta().hasLore() && item.getItemMeta().getLore() != null + ? item.getItemMeta().getLore().stream().map(this::strip).toList() : List.of(); + String mythicType = NBT.get(item, nbt -> { return nbt.hasTag("MYTHIC_TYPE") ? nbt.getString("MYTHIC_TYPE") : ""; }); + for (SkillDefinition skill : registry.all()) { + if (skill.lore() != null && lore.stream().anyMatch(line -> line.startsWith(strip(skill.lore())))) names.add(skill.name()); + if (skill.itemName() != null && display.equals(strip(skill.itemName()))) names.add(skill.name()); + if (skill.mythicType() != null && skill.mythicType().equalsIgnoreCase(mythicType)) names.add(skill.name()); + } + List found = new ArrayList<>(); + for (String name : names) registry.find(name).ifPresent(skill -> { + if (skill.slots().isEmpty() || skill.slots().stream().anyMatch(value -> slotMatches(value, slot))) found.add(skill); + }); + return found; + } + + private List equipment(Player player) { + return List.of(new EquippedItem(player.getInventory().getItemInMainHand(), "mainhand"), + new EquippedItem(player.getInventory().getItemInOffHand(), "offhand"), + new EquippedItem(player.getInventory().getHelmet(), "head"), + new EquippedItem(player.getInventory().getChestplate(), "chest"), + new EquippedItem(player.getInventory().getLeggings(), "legs"), + new EquippedItem(player.getInventory().getBoots(), "feet")); + } + + private boolean slotMatches(String configured, String actual) { + String value = configured.toLowerCase(Locale.ROOT); + return value.equals(actual) || value.equals("all") || value.equals("any") + || value.equals("hand") && actual.equals("mainhand") + || value.equals("helmet") && actual.equals("head") + || value.equals("chestplate") && actual.equals("chest") + || value.equals("leggings") && actual.equals("legs") + || value.equals("boots") && actual.equals("feet"); + } + + private LivingEntity selectedLiving(Player player, String selector, Entity target) { + Entity selected = selector.equalsIgnoreCase("self") ? player : target; + return selected instanceof LivingEntity living ? living : null; + } + + private double value(String source, Player player, ItemStack item) { + try { return expressions.evaluate(source, player, item, Map.of()); } + catch (RuntimeException ignored) { return 0; } + } + + private boolean roll(String source, Player player, ItemStack item) { + return ThreadLocalRandom.current().nextDouble(100) < value(source, player, item); + } + + private boolean debuffed(Entity entity, String type) { + String key = entity.getUniqueId() + ":" + type.toUpperCase(Locale.ROOT); + long until = debuffs.getOrDefault(key, 0L); + if (until <= System.currentTimeMillis()) { + debuffs.remove(key); + return false; + } + return true; + } + + private void tip(Player player, String message) { + messages.send(player, message); + } + + private String strip(String value) { + String colored = ChatColor.translateAlternateColorCodes('&', value == null ? "" : value); + return plugin.getConfig().getBoolean("skill-engine-lore-color", false) + ? colored : ChatColor.stripColor(colored); + } + + private String normalize(String value) { + return value.toLowerCase(Locale.ROOT).replace("_", "").replace("-", ""); + } + + private void replaceMatchingItem(Player player, ItemStack original, ItemStack changed) { + for (EquipmentSlot slot : EquipmentSlot.values()) { + ItemStack equipped = player.getInventory().getItem(slot); + if (equipped == original) { + player.getInventory().setItem(slot, changed); + return; + } + } + } + + private Player attackingPlayer(Entity damager) { + if (damager instanceof Player player) return player; + if (damager instanceof Projectile projectile && projectile.getShooter() instanceof Player player) return player; + return null; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false) + public void onDamage(EntityDamageEvent event) { + if (debuffed(event.getEntity(), "HEAL")) { } + if (event.getEntity() instanceof Player victim) { + List info = new ArrayList<>(); + info.add(event.getCause().name()); + Entity combatTarget = event instanceof EntityDamageByEntityEvent by ? by.getDamager() : null; + if (event instanceof EntityDamageByEntityEvent by) { + Entity damager = by.getDamager() instanceof Projectile projectile && projectile.getShooter() instanceof Entity shooter + ? shooter : by.getDamager(); + info.add(damager.getType().name()); + trigger(victim, TriggerType.DAMAGED_ENTITY, List.of(damager.getType().name()), damager, victim.getLocation(), event); + if (by.getDamager() instanceof Projectile projectile) + trigger(victim, TriggerType.DAMAGED_PROJECTILE, List.of(damager.getType().name()), damager, victim.getLocation(), event); + } else trigger(victim, TriggerType.DAMAGED_BLOCK, List.of(event.getCause().name()), null, victim.getLocation(), event); + trigger(victim, TriggerType.DAMAGED, info, combatTarget, victim.getLocation(), event); + trigger(victim, TriggerType.COMBAT, info, combatTarget, victim.getLocation(), event); + } + if (event instanceof EntityDamageByEntityEvent by) { + Player attacker = attackingPlayer(by.getDamager()); + if (attacker != null) { + if (debuffed(attacker, "ATTACK")) { event.setCancelled(true); return; } + Entity target = by.getEntity(); + trigger(attacker, TriggerType.ATTACK, List.of(target.getType().name()), target, target.getLocation(), event); + trigger(attacker, TriggerType.COMBAT, List.of(target.getType().name()), target, target.getLocation(), event); + if (by.getDamager() instanceof Projectile projectile) { + TriggerType projectileTrigger = switch (projectile.getType()) { + case ARROW, SPECTRAL_ARROW -> TriggerType.BOW_HIT; + case TRIDENT -> TriggerType.TRIDENT_ATTACK; + case SNOWBALL -> TriggerType.SNOWBALL_ATTACK; + case FIREBALL, SMALL_FIREBALL -> TriggerType.FIREBALL_ATTACK; + default -> TriggerType.PROJECTILE_ATTACK; + }; + trigger(attacker, projectileTrigger, List.of(target.getType().name()), target, target.getLocation(), event); + } + } + Entity directDamager = by.getDamager() instanceof Projectile projectile + && projectile.getShooter() instanceof Entity shooter ? shooter : by.getDamager(); + if (attacker == null && debuffed(directDamager, "ATTACK")) event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onDeath(EntityDeathEvent event) { + Player killer = event.getEntity().getKiller(); + if (killer != null) { + trigger(killer, TriggerType.KILL, List.of(event.getEntityType().name()), event.getEntity(), event.getEntity().getLocation(), event); + trigger(killer, TriggerType.KILL_ENTITY, List.of(event.getEntityType().name()), event.getEntity(), event.getEntity().getLocation(), event); + if (event.getEntity() instanceof Player) + trigger(killer, TriggerType.KILL_PLAYER, List.of(), event.getEntity(), event.getEntity().getLocation(), event); + } + if (event instanceof PlayerDeathEvent playerDeath) { + trigger(playerDeath.getEntity(), TriggerType.DEATH, List.of(), playerDeath.getEntity(), playerDeath.getEntity().getLocation(), event); + trigger(playerDeath.getEntity(), TriggerType.DESPAWN, List.of(), playerDeath.getEntity(), playerDeath.getEntity().getLocation(), event); + } + } + + @EventHandler(ignoreCancelled = false) + public void onInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + Action action = event.getAction(); + boolean right = action.isRightClick(); + if (!right) return; + if (debuffed(player, "USE")) { event.setCancelled(true); return; } + trigger(player, player.isSneaking() ? TriggerType.SHIFT_USE : TriggerType.USE, + List.of(), null, event.getClickedBlock() == null ? null : event.getClickedBlock().getLocation(), event); + if (event.getClickedBlock() == null) + trigger(player, player.isSneaking() ? TriggerType.SHIFT_RIGHTCLICK : TriggerType.RIGHT_CLICK, + List.of(), null, player.getLocation(), event); + } + + @EventHandler + public void onAnimation(PlayerAnimationEvent event) { + if (event.getAnimationType() != PlayerAnimationType.ARM_SWING) return; + Player player = event.getPlayer(); + trigger(player, player.isSneaking() ? TriggerType.SHIFT_SWING : TriggerType.SWING, + List.of(), null, player.getLocation(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + if (debuffed(player, "MOVE") && event.getTo() != null + && (event.getFrom().getX() != event.getTo().getX() || event.getFrom().getZ() != event.getTo().getZ())) { + event.setCancelled(true); + return; + } + } + + @EventHandler(ignoreCancelled = false) + public void onJump(PlayerJumpEvent event) { + Player player = event.getPlayer(); + if (debuffed(player, "JUMP")) { event.setCancelled(true); return; } + CMagicJumpEvent jump = new CMagicJumpEvent(player, event.getFrom(), event.getTo()); + Bukkit.getPluginManager().callEvent(jump); + if (jump.isCancelled()) { event.setCancelled(true); return; } + trigger(player, TriggerType.JUMP, List.of(), null, jump.getTo(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onTeleport(PlayerTeleportEvent event) { + if (debuffed(event.getPlayer(), "TELEPORT")) { event.setCancelled(true); return; } + trigger(event.getPlayer(), TriggerType.TELEPORT, List.of(), null, event.getTo(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onPlace(BlockPlaceEvent event) { + if (debuffed(event.getPlayer(), "PLACE")) { event.setCancelled(true); return; } + trigger(event.getPlayer(), TriggerType.BLOCK_PLACE, List.of(event.getBlockPlaced().getType().name()), null, event.getBlockPlaced().getLocation(), event); + trigger(event.getPlayer(), TriggerType.BLOCK, List.of(event.getBlockPlaced().getType().name()), null, event.getBlockPlaced().getLocation(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onBreak(BlockBreakEvent event) { + if (debuffed(event.getPlayer(), "BREAK")) { event.setCancelled(true); return; } + trigger(event.getPlayer(), TriggerType.BLOCK_BREAK, List.of(event.getBlock().getType().name()), null, event.getBlock().getLocation(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onConsume(PlayerItemConsumeEvent event) { + if (debuffed(event.getPlayer(), "CONSUME")) { event.setCancelled(true); return; } + trigger(event.getPlayer(), TriggerType.CONSUME, List.of(event.getItem().getType().name()), null, event.getPlayer().getLocation(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onShoot(EntityShootBowEvent event) { + if (!(event.getEntity() instanceof Player player)) return; + if (debuffed(player, "SHOOT")) { event.setCancelled(true); return; } + trigger(player, event.getProjectile().getType().name().equals("TRIDENT") ? TriggerType.TRIDENT_THROW : TriggerType.SHOOT, + List.of(), event.getProjectile(), event.getProjectile().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onProjectileLaunch(ProjectileLaunchEvent event) { + ProjectileSource source = event.getEntity().getShooter(); + if (source instanceof Entity entity && debuffed(entity, "SHOOT")) { + event.setCancelled(true); + return; + } + if (!(source instanceof Player player)) return; + if (event.getEntity().getType() == org.bukkit.entity.EntityType.TRIDENT) + trigger(player, TriggerType.TRIDENT_THROW, List.of(), event.getEntity(), event.getEntity().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onPotionSplash(PotionSplashEvent event) { + if (event.getEntity().getShooter() instanceof Player player) + trigger(player, TriggerType.SPLASH_POTION, List.of(), event.getEntity(), event.getEntity().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onLingeringPotionSplash(LingeringPotionSplashEvent event) { + if (event.getEntity().getShooter() instanceof Player player) + trigger(player, TriggerType.SPLASH_POTION, List.of(), event.getEntity(), event.getEntity().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onProjectileHit(ProjectileHitEvent event) { + if (!(event.getEntity().getShooter() instanceof Player player)) return; + if (event.getHitBlock() == null) return; + Location location = event.getHitBlock().getLocation(); + TriggerType type = event.getEntity().getType().name().equals("TRIDENT") ? TriggerType.TRIDENT_HIT : TriggerType.HIT; + trigger(player, type, List.of(event.getEntityType().name()), null, location, event); + } + + @EventHandler(ignoreCancelled = true) + public void onDrop(PlayerDropItemEvent event) { + trigger(event.getPlayer(), TriggerType.DROP, List.of(event.getItemDrop().getItemStack().getType().name()), + event.getItemDrop(), event.getItemDrop().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onTame(EntityTameEvent event) { + if (event.getOwner() instanceof Player player) + trigger(player, TriggerType.TAME, List.of(event.getEntityType().name()), event.getEntity(), event.getEntity().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onEntityInteract(PlayerInteractAtEntityEvent event) { + trigger(event.getPlayer(), TriggerType.INTERACT, List.of(event.getRightClicked().getType().name()), + event.getRightClicked(), event.getRightClicked().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onFish(PlayerFishEvent event) { + String information = event.getCaught() == null ? event.getState().name() : event.getCaught().getType().name(); + trigger(event.getPlayer(), TriggerType.FISH, List.of(information), event.getCaught(), event.getHook().getLocation(), event); + TriggerType type = switch (event.getState()) { + case BITE -> TriggerType.FISH_BITE; + case CAUGHT_FISH -> TriggerType.FISH_CATCH_FISH; + case CAUGHT_ENTITY -> TriggerType.FISH_CATCH_ENTITY; + case IN_GROUND -> TriggerType.FISH_GROUND; + case REEL_IN -> TriggerType.FISH_REEL; + case FAILED_ATTEMPT -> TriggerType.FISH_FAIL; + default -> null; + }; + if (type != null) trigger(event.getPlayer(), type, List.of(information), event.getCaught(), event.getHook().getLocation(), event); + } + + @EventHandler(ignoreCancelled = true) + public void onSneak(PlayerToggleSneakEvent event) { + trigger(event.getPlayer(), event.isSneaking() ? TriggerType.CROUCH : TriggerType.UNCROUCH, + List.of(), null, event.getPlayer().getLocation(), event); + } + + @EventHandler(ignoreCancelled = false) + public void onRegain(EntityRegainHealthEvent event) { + if (debuffed(event.getEntity(), "HEAL")) event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = false) + public void onEntityTeleport(EntityTeleportEvent event) { + if (debuffed(event.getEntity(), "TELEPORT")) event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = false) + public void onFlight(PlayerToggleFlightEvent event) { + if (event.isFlying() && debuffed(event.getPlayer(), "FLY")) event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = true) + public void onArmorChange(PlayerArmorChangeEvent event) { + equipmentChange(event.getPlayer(), event.getSlotType().name().toLowerCase(Locale.ROOT), + event.getOldItem(), event.getNewItem(), CMagicEquipmentChangeEvent.Cause.ARMOR, event); + } + + @EventHandler(ignoreCancelled = true) + public void onHeld(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + equipmentChange(player, "mainhand", player.getInventory().getItem(event.getPreviousSlot()), + player.getInventory().getItem(event.getNewSlot()), CMagicEquipmentChangeEvent.Cause.HELD, event); + } + + @EventHandler(ignoreCancelled = true) + public void onSwap(PlayerSwapHandItemsEvent event) { + Player player = event.getPlayer(); + equipmentChange(player, "mainhand", player.getInventory().getItemInMainHand(), event.getMainHandItem(), + CMagicEquipmentChangeEvent.Cause.SWAP, event); + equipmentChange(player, "offhand", player.getInventory().getItemInOffHand(), event.getOffHandItem(), + CMagicEquipmentChangeEvent.Cause.SWAP, event); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + ItemStack mainHand = snapshot(player.getInventory().getItemInMainHand()); + ItemStack offHand = snapshot(player.getInventory().getItemInOffHand()); + Bukkit.getScheduler().runTask(plugin, () -> { + ItemStack currentMainHand = snapshot(player.getInventory().getItemInMainHand()); + ItemStack currentOffHand = snapshot(player.getInventory().getItemInOffHand()); + if (!sameItem(mainHand, currentMainHand)) + equipmentChange(player, "mainhand", mainHand, currentMainHand, + CMagicEquipmentChangeEvent.Cause.INVENTORY, event); + if (!sameItem(offHand, currentOffHand)) + equipmentChange(player, "offhand", offHand, currentOffHand, + CMagicEquipmentChangeEvent.Cause.INVENTORY, event); + }); + } + + @EventHandler + public void onBreak(PlayerItemBreakEvent event) { + if (isArmor(event.getBrokenItem().getType())) return; + equipmentChange(event.getPlayer(), slotFor(event.getPlayer(), event.getBrokenItem()), event.getBrokenItem(), + null, CMagicEquipmentChangeEvent.Cause.BREAK, event); + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + Bukkit.getScheduler().runTask(plugin, () -> trigger(event.getPlayer(), TriggerType.SPAWN, List.of(), null, event.getPlayer().getLocation(), event)); + } + + @EventHandler + public void onRespawn(PlayerRespawnEvent event) { + Bukkit.getScheduler().runTask(plugin, () -> trigger(event.getPlayer(), TriggerType.SPAWN, List.of(), null, event.getRespawnLocation(), event)); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + trigger(event.getPlayer(), TriggerType.DESPAWN, List.of(), null, event.getPlayer().getLocation(), event); + cooldowns.remove(event.getPlayer().getUniqueId()); + durations.remove(event.getPlayer().getUniqueId()); + String prefix = event.getPlayer().getUniqueId() + ":"; + counters.keySet().removeIf(key -> key.startsWith(prefix)); + debuffs.keySet().removeIf(key -> key.startsWith(prefix)); + } + + private void equipmentChange(Player player, String slot, ItemStack oldItem, ItemStack newItem, + CMagicEquipmentChangeEvent.Cause cause, Event source) { + CMagicEquipmentChangeEvent event = new CMagicEquipmentChangeEvent(player, slot, oldItem, newItem, cause); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + if (oldItem != null && !oldItem.getType().isAir()) + triggerItem(player, oldItem, slot, TriggerType.ARMOR_UNEQUIP, List.of(), null, player.getLocation(), source); + if (newItem != null && !newItem.getType().isAir()) + triggerItem(player, newItem, slot, TriggerType.ARMOR_EQUIP, List.of(), null, player.getLocation(), source); + } + + private ItemStack snapshot(ItemStack item) { + return item == null || item.getType().isAir() ? null : item.clone(); + } + + private boolean sameItem(ItemStack first, ItemStack second) { + return first == null ? second == null : first.equals(second); + } + + private String slotFor(Player player, ItemStack item) { + for (EquipmentSlot slot : EquipmentSlot.values()) + if (player.getInventory().getItem(slot) == item + || player.getInventory().getItem(slot) != null + && player.getInventory().getItem(slot).isSimilar(item)) + return slot.name().toLowerCase(Locale.ROOT); + return "mainhand"; + } + + private boolean isArmor(Material material) { + return material.name().endsWith("_HELMET") || material.name().endsWith("_CHESTPLATE") + || material.name().endsWith("_LEGGINGS") || material.name().endsWith("_BOOTS") + || material == Material.ELYTRA; + } + + private record EquippedItem(ItemStack item, String slot) { } + private record EventContext(Event event, Location location) { } + + private record CounterState(long count, long lastUsed) { } +} diff --git a/src/main/java/cn/infstar/cMagicGem/skill/SkillRegistry.java b/src/main/java/cn/infstar/cMagicGem/skill/SkillRegistry.java new file mode 100644 index 0000000..5e647a0 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/skill/SkillRegistry.java @@ -0,0 +1,150 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.api.event.CMagicSkillLoadEvent; +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; + +public final class SkillRegistry { + private final JavaPlugin plugin; + private final DirectiveParser parser; + private volatile Map skills = Map.of(); + + public SkillRegistry(JavaPlugin plugin, DirectiveParser parser) { + this.plugin = plugin; + this.parser = parser; + } + + public int reload() { + File directory = new File(plugin.getDataFolder(), "skills"); + if (!directory.exists() && !directory.mkdirs()) throw new IllegalStateException("无法创建 skills 目录"); + Map loaded = new LinkedHashMap<>(); + File[] files = directory.listFiles((ignored, name) -> name.toLowerCase(Locale.ROOT).endsWith(".yml")); + if (files != null) for (File file : files) loadFile(file, loaded); + skills = Collections.unmodifiableMap(loaded); + return loaded.size(); + } + + public void clear() { + skills = Map.of(); + } + + private void loadFile(File file, Map loaded) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); + String dependency = yaml.getString("depend"); + if (dependency != null && plugin.getServer().getPluginManager().getPlugin(dependency) == null) return; + for (String key : yaml.getKeys(false)) { + if (key.equalsIgnoreCase("depend")) continue; + ConfigurationSection section = yaml.getConfigurationSection(key); + if (section == null) continue; + try { + CMagicSkillLoadEvent event = new CMagicSkillLoadEvent(key, section); + plugin.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) loaded.put(key.toLowerCase(Locale.ROOT), parse(key, event.getConfig())); + } + catch (RuntimeException exception) { plugin.getLogger().log(Level.SEVERE, "无法加载技能 " + file.getName() + "/" + key + ": " + exception.getMessage()); } + } + } + + private SkillDefinition parse(String name, ConfigurationSection section) { + List actions = new ArrayList<>(); + for (String line : section.getStringList("Skills")) actions.add(parseAction(line)); + Set slots = new LinkedHashSet<>(); + Object slotValue = section.get("Slot"); + if (slotValue instanceof String value) slots.add(value.toLowerCase(Locale.ROOT)); + else for (String value : section.getStringList("Slot")) slots.add(value.toLowerCase(Locale.ROOT)); + return new SkillDefinition(name, section.getString("Lore"), section.getString("Name"), section.getString("MythicType"), + Set.copyOf(slots), section.getString("Success", "100"), section.getDouble("Cooldown", 0), + section.getDouble("Duration", 0), section.getString("CooldownTip"), + section.getString("SuccessTip"), section.getString("FailTip"), List.copyOf(actions), + parseList(section.getStringList("Conditions")), parseList(section.getStringList("TriggerConditions")), + parseList(section.getStringList("TargetConditions"))); + } + + SkillAction parseAction(String line) { + List tokens = topLevelTokens(line == null ? "" : line.trim()); + String mechanicText = tokens.isEmpty() ? "" : tokens.getFirst(); + TriggerType trigger = TriggerType.DEFAULT; + List filters = List.of(); + String target = "self"; + Map worldLimits = new LinkedHashMap<>(); + List mechanicTokens = new ArrayList<>(); + for (String token : tokens) { + if (token.toLowerCase(Locale.ROOT).startsWith("~on")) { + int separator = token.indexOf(':'); + trigger = TriggerType.parse(separator < 0 ? token : token.substring(0, separator)); + filters = separator < 0 ? List.of() : List.of(token.substring(separator + 1).split(";")); + } else if (worldLimit(token, worldLimits)) { + continue; + } else { + mechanicTokens.add(token); + if (token.startsWith("@")) { + int brace = token.indexOf('{'); + target = token.substring(1, brace < 0 ? token.length() : brace).toLowerCase(Locale.ROOT); + } + } + } + String mechanicLine = String.join(" ", mechanicTokens); + return new SkillAction(parser.parse(mechanicText), mechanicLine, trigger, filters, target, + Map.copyOf(worldLimits)); + } + + private boolean worldLimit(String token, Map worldLimits) { + if (!token.startsWith("$") || token.lastIndexOf(':') <= 1) return false; + int separator = token.lastIndexOf(':'); + try { + worldLimits.put(token.substring(1, separator).toLowerCase(Locale.ROOT), + Integer.parseInt(token.substring(separator + 1))); + return true; + } catch (NumberFormatException ignored) { + return false; + } + } + + private List topLevelTokens(String line) { + List tokens = new ArrayList<>(); + int depth = 0; + int start = -1; + boolean quoted = false; + for (int index = 0; index < line.length(); index++) { + char current = line.charAt(index); + if (start < 0 && !Character.isWhitespace(current)) start = index; + if (current == '"' && (index == 0 || line.charAt(index - 1) != '\\')) quoted = !quoted; + else if (!quoted && current == '{') depth++; + else if (!quoted && current == '}') depth = Math.max(0, depth - 1); + else if (!quoted && depth == 0 && Character.isWhitespace(current) && start >= 0) { + tokens.add(line.substring(start, index)); + start = -1; + } + } + if (start >= 0) tokens.add(line.substring(start)); + return tokens; + } + + private List parseList(List lines) { + return lines.stream().map(parser::parse).toList(); + } + + public Optional find(String name) { + return name == null ? Optional.empty() : Optional.ofNullable(skills.get(name.toLowerCase(Locale.ROOT))); + } + + public Collection all() { + return skills.values(); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/skill/TriggerType.java b/src/main/java/cn/infstar/cMagicGem/skill/TriggerType.java new file mode 100644 index 0000000..57ff2f1 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/skill/TriggerType.java @@ -0,0 +1,24 @@ +package cn.infstar.cmagicgem.skill; + +import java.util.Locale; + +public enum TriggerType { + API, CUSTOM, JUMP, SIGNAL, COUNTER, SHIFT_SWING, SHIFT_USE, SHIFT_RIGHTCLICK, + KILL_ENTITY, KILL_MYTHICMOBS, DAMAGED_PROJECTILE, DAMAGED_BLOCK, DAMAGED_ENTITY, + DROP, TRIDENT_HIT, HIT, BOW_HIT, TRIDENT_ATTACK, SNOWBALL_ATTACK, FIREBALL_ATTACK, + PROJECTILE_ATTACK, COMBAT, TELEPORT, BLOCK, DEFAULT, TRIDENT_THROW, DESPAWN, TAME, + ATTACK, SHOOT, BLOCK_PLACE, BLOCK_BREAK, CONSUME, CROUCH, UNCROUCH, DAMAGED, DEATH, + INTERACT, KILL, KILL_PLAYER, SPAWN, SPLASH_POTION, SWING, TIMER, USE, RIGHT_CLICK, + FISH, FISH_BITE, FISH_CATCH_FISH, FISH_CATCH_ENTITY, FISH_GROUND, FISH_REEL, + FISH_FAIL, ARMOR_EQUIP, ARMOR_UNEQUIP; + + public static TriggerType parse(String input) { + String normalized = input.replaceFirst("(?i)^~on", "") + .replaceAll("([a-z])([A-Z])", "$1_$2") + .replace('-', '_').toUpperCase(Locale.ROOT); + if (normalized.equals("RIGHTCLICK")) normalized = "RIGHT_CLICK"; + if (normalized.equals("KILLPLAYER")) normalized = "KILL_PLAYER"; + if (normalized.equals("DESPAWNED")) normalized = "DESPAWN"; + return valueOf(normalized); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/util/EnchantmentResolver.java b/src/main/java/cn/infstar/cMagicGem/util/EnchantmentResolver.java new file mode 100644 index 0000000..98da14d --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/util/EnchantmentResolver.java @@ -0,0 +1,45 @@ +package cn.infstar.cmagicgem.util; + +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.enchantments.Enchantment; + +import java.util.Locale; +import java.util.Map; + +public final class EnchantmentResolver { + private static final Map LEGACY_NAMES = Map.ofEntries( + Map.entry("protection_environmental", "protection"), + Map.entry("protection_fire", "fire_protection"), + Map.entry("protection_fall", "feather_falling"), + Map.entry("protection_explosions", "blast_protection"), + Map.entry("protection_projectile", "projectile_protection"), + Map.entry("oxygen", "respiration"), Map.entry("water_worker", "aqua_affinity"), + Map.entry("damage_all", "sharpness"), Map.entry("damage_undead", "smite"), + Map.entry("damage_arthropods", "bane_of_arthropods"), Map.entry("dig_speed", "efficiency"), + Map.entry("durability", "unbreaking"), Map.entry("loot_bonus_blocks", "fortune"), + Map.entry("loot_bonus_mobs", "looting"), Map.entry("arrow_damage", "power"), + Map.entry("arrow_knockback", "punch"), Map.entry("arrow_fire", "flame"), + Map.entry("arrow_infinite", "infinity"), Map.entry("luck", "luck_of_the_sea")); + + private EnchantmentResolver() { + } + + public static Enchantment resolve(String input) { + if (input == null || input.isBlank()) return null; + String normalized = input.toLowerCase(Locale.ROOT); + NamespacedKey direct = NamespacedKey.fromString(normalized); + if (direct != null) { + Enchantment found = Registry.ENCHANTMENT.get(direct); + if (found != null) return found; + } + String key = LEGACY_NAMES.getOrDefault(normalized.replace("minecraft:", ""), + normalized.replace("minecraft:", "")); + Enchantment vanilla = Registry.ENCHANTMENT.get(NamespacedKey.minecraft(key)); + if (vanilla != null) return vanilla; + for (Enchantment enchantment : Registry.ENCHANTMENT) + if (enchantment.getKey().getKey().equalsIgnoreCase(key) + || enchantment.getKey().toString().equalsIgnoreCase(input)) return enchantment; + return null; + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/util/MaterialResolver.java b/src/main/java/cn/infstar/cMagicGem/util/MaterialResolver.java new file mode 100644 index 0000000..5cf3eda --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/util/MaterialResolver.java @@ -0,0 +1,40 @@ +package cn.infstar.cmagicgem.util; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.material.MaterialData; + +import java.util.Locale; + +public final class MaterialResolver { + private MaterialResolver() { + } + + public static Material resolve(String input) { + if (input == null || input.isBlank()) return null; + String value = input.trim(); + Material direct = Material.matchMaterial(value); + if (direct != null && !direct.isLegacy()) return direct; + + int data = 0; + int separator = value.lastIndexOf(':'); + if (separator > 0 && !value.regionMatches(true, 0, "minecraft:", 0, 10)) { + try { + data = Integer.parseInt(value.substring(separator + 1)); + value = value.substring(0, separator); + } catch (NumberFormatException ignored) { + } + } + + Material modern = Material.matchMaterial(value); + if (modern != null && !modern.isLegacy()) return modern; + Material legacy = Material.matchMaterial(value.toUpperCase(Locale.ROOT), true); + if (legacy == null || !legacy.isLegacy()) return legacy; + try { + Material converted = Bukkit.getUnsafe().fromLegacy(new MaterialData(legacy, (byte) data), true); + return converted == Material.AIR && legacy != Material.LEGACY_AIR ? null : converted; + } catch (RuntimeException exception) { + return null; + } + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/util/MessageDispatcher.java b/src/main/java/cn/infstar/cMagicGem/util/MessageDispatcher.java new file mode 100644 index 0000000..bd8d7e1 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/util/MessageDispatcher.java @@ -0,0 +1,109 @@ +package cn.infstar.cmagicgem.util; + +import cn.infstar.cmagicgem.config.LanguageManager; +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.integration.PlaceholderBridge; +import cn.infstar.cmagicgem.item.ItemCodec; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.time.Duration; +import java.util.List; +import java.util.Locale; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class MessageDispatcher { + private final JavaPlugin plugin; + private final ItemCodec codec; + private final DirectiveParser parser; + private final PlaceholderBridge placeholders; + private final LanguageManager languages; + + public MessageDispatcher(JavaPlugin plugin, ItemCodec codec, DirectiveParser parser, PlaceholderBridge placeholders, + LanguageManager languages) { + this.plugin = plugin; + this.codec = codec; + this.parser = parser; + this.placeholders = placeholders; + this.languages = languages; + } + + public void send(CommandSender receiver, String source) { + if (source == null || source.isBlank() || source.equalsIgnoreCase("none")) return; + if (!(receiver instanceof Player player) || !source.contains("{")) { + receiver.sendMessage(codec.color(source)); + return; + } + Directive directive; + try { directive = parser.parse(source); } + catch (RuntimeException ignored) { player.sendMessage(codec.color(source)); return; } + String type = directive.type().toLowerCase(Locale.ROOT); + List messages = parser.splitTopLevel(directive.argument("", "messages", "message", "m"), ';'); + int interval = Math.max(0, directive.intArgument(0, "interval")); + if (type.equals("title")) { + for (int index = 0; index < messages.size(); index += 2) { + String title = resolve(player, messages.get(index)); + String subtitle = resolve(player, index + 1 < messages.size() ? messages.get(index + 1) : ""); + int delay = interval * (index / 2); + later(delay, () -> player.showTitle(Title.title(component(title), component(subtitle), Title.Times.times( + Duration.ofMillis(directive.intArgument(10, "fadein") * 50L), + Duration.ofMillis(directive.intArgument(70, "stay") * 50L), + Duration.ofMillis(directive.intArgument(20, "fadeout") * 50L))))); + } + return; + } + for (int index = 0; index < messages.size(); index++) { + String message = resolve(player, messages.get(index)); + int delay = interval * index; + later(delay, () -> { + if (type.equals("actionbar")) player.sendActionBar(component(message)); + else player.sendMessage(codec.color(message)); + }); + } + } + + public void cooldown(Player player, String configured, double remaining, double total) { + if (configured == null || configured.isBlank()) { + send(player, languages.text("cmagic.cooldown", "&e冷却中,剩余 {seconds} 秒", + "seconds", String.format(Locale.ROOT, "%.2f", remaining))); + return; + } + int open = configured.indexOf('{'); + int close = configured.lastIndexOf('}'); + if (open < 0 || close < open) { send(player, configured); return; } + Map arguments = new LinkedHashMap<>(); + for (String token : configured.substring(open + 1, close).split(";")) { + int equals = token.indexOf('='); + if (equals > 0) arguments.put(token.substring(0, equals).trim().toLowerCase(Locale.ROOT), token.substring(equals + 1)); + } + int length; + try { length = Math.max(0, Integer.parseInt(arguments.getOrDefault("length", "0"))); } + catch (NumberFormatException ignored) { length = 0; } + int full = total <= 0 ? 0 : (int) Math.round(length * Math.max(0, Math.min(1, 1 - remaining / total))); + String suffix; + try { suffix = String.format(Locale.ROOT, arguments.getOrDefault("s", "%.2fs"), remaining); } + catch (RuntimeException ignored) { suffix = String.format(Locale.ROOT, "%.2fs", remaining); } + String message = arguments.getOrDefault("p", "") + arguments.getOrDefault("full", "|").repeat(full) + + arguments.getOrDefault("empty", "|").repeat(Math.max(0, length - full)) + suffix; + send(player, configured.substring(0, open) + "{m=" + message + '}'); + } + + private String resolve(Player player, String message) { + return placeholders.resolve(player, message); + } + + private void later(int delay, Runnable action) { + if (delay <= 0) action.run(); else Bukkit.getScheduler().runTaskLater(plugin, action, delay); + } + + private Component component(String message) { + return LegacyComponentSerializer.legacyAmpersand().deserialize(message == null ? "" : message); + } +} diff --git a/src/main/java/cn/infstar/cMagicGem/util/PotionTypes.java b/src/main/java/cn/infstar/cMagicGem/util/PotionTypes.java new file mode 100644 index 0000000..0312a86 --- /dev/null +++ b/src/main/java/cn/infstar/cMagicGem/util/PotionTypes.java @@ -0,0 +1,55 @@ +package cn.infstar.cmagicgem.util; + +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.potion.PotionEffectType; + +import java.util.Locale; +import java.util.Map; + +public final class PotionTypes { + private static final Map ALIASES = Map.ofEntries( + Map.entry("absorb", "absorption"), Map.entry("omen_bad", "bad_omen"), + Map.entry("pillager", "bad_omen"), Map.entry("blind", "blindness"), + Map.entry("confusion", "nausea"), Map.entry("sickness", "nausea"), + Map.entry("damage_resistance", "resistance"), Map.entry("armor", "resistance"), + Map.entry("dmg_resist", "resistance"), Map.entry("dmg_resistance", "resistance"), + Map.entry("conduit", "conduit_power"), Map.entry("power_conduit", "conduit_power"), + Map.entry("dolphin", "dolphins_grace"), Map.entry("grace", "dolphins_grace"), + Map.entry("fast_digging", "haste"), Map.entry("dig_speed", "haste"), + Map.entry("quick_mine", "haste"), Map.entry("super_pick", "haste"), Map.entry("digfast", "haste"), + Map.entry("fire_resist", "fire_resistance"), Map.entry("resist_fire", "fire_resistance"), + Map.entry("glow", "glowing"), Map.entry("shine", "glowing"), Map.entry("shiny", "glowing"), + Map.entry("harm", "instant_damage"), Map.entry("damage", "instant_damage"), + Map.entry("injure", "instant_damage"), Map.entry("harming", "instant_damage"), + Map.entry("inflict", "instant_damage"), Map.entry("heal", "instant_health"), + Map.entry("health", "instant_health"), Map.entry("insta_heal", "instant_health"), + Map.entry("instant_heal", "instant_health"), Map.entry("insta_health", "instant_health"), + Map.entry("boost_health", "health_boost"), Map.entry("boost", "health_boost"), + Map.entry("hp", "health_boost"), Map.entry("hero", "hero_of_the_village"), + Map.entry("village_hero", "hero_of_the_village"), Map.entry("starve", "hunger"), + Map.entry("hungry", "hunger"), Map.entry("increase_damage", "strength"), + Map.entry("bull", "strength"), Map.entry("strong", "strength"), Map.entry("attack", "strength"), + Map.entry("invisible", "invisibility"), Map.entry("hide", "invisibility"), + Map.entry("vanish", "invisibility"), Map.entry("invis", "invisibility"), + Map.entry("disappear", "invisibility"), + Map.entry("jump", "jump_boost"), Map.entry("leap", "jump_boost"), + Map.entry("levitate", "levitation"), Map.entry("lucky", "luck"), + Map.entry("vision", "night_vision"), Map.entry("vision_night", "night_vision"), + Map.entry("venom", "poison"), Map.entry("regen", "regeneration"), Map.entry("food", "saturation"), + Map.entry("slow", "slowness"), Map.entry("sluggish", "slowness"), + Map.entry("slow_digging", "mining_fatigue"), Map.entry("dull", "mining_fatigue"), + Map.entry("fatigue", "mining_fatigue"), Map.entry("dig_slow", "mining_fatigue"), + Map.entry("digging", "mining_fatigue"), Map.entry("slow_dig", "mining_fatigue"), + Map.entry("slow_fall", "slow_falling"), Map.entry("fall_slow", "slow_falling"), + Map.entry("sprint", "speed"), Map.entry("run_fast", "speed"), + Map.entry("swift", "speed"), Map.entry("fast", "speed")); + + private PotionTypes() { } + + public static PotionEffectType resolve(String input) { + if (input == null || input.isBlank()) return null; + String name = input.toLowerCase(Locale.ROOT).replace("minecraft:", ""); + return Registry.EFFECT.get(NamespacedKey.minecraft(ALIASES.getOrDefault(name, name))); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..3ad0b97 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,12 @@ +language: zh_CN +workbench: true +backpack: false +gem-lore: false +gem-craft: false +skill-engine: true +skill-engine-lore-color: false +legacy-migration: true +potion-global-limit: {} +integrations: + mythicmobs: + reload-on-enable: true diff --git a/src/main/resources/gems/AttributeGem.yml b/src/main/resources/gems/AttributeGem.yml new file mode 100644 index 0000000..b8d02fc --- /dev/null +++ b/src/main/resources/gems/AttributeGem.yml @@ -0,0 +1,232 @@ +【Version: 1.05 + +内置-生命宝石: + Name: 生命宝石 + Require: + - ARMOR + Display: "&8&l&k||&c&l生命宝石&8&l&k||" + Tips: + - "&c生命宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其最大生命上限+1(上限5)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDFhNmE0MTFiODAyYjIyZTUzMjQ3OTVjZTM0ZTNjYWU0YTgzNzk2YTE4ZDkyMzMyNjY2Y2JmMjE0ODhjMCJ9fX0= + SuccessTip: "&f你使用了&c&l生命宝石&f, 装备生命上限&c&l+1" + Rewards: + - Attribute{name=health;operation=0;slot=auto;var=v+1;limit=5} + +内置-真生命宝石: + Name: 真·生命宝石 + Require: + - ARMOR + Display: "&8&l&k||&c&l真·生命宝石&8&l&k||" + Tips: + - "&c&l真生命宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其最大生命上限+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDFhNmE0MTFiODAyYjIyZTUzMjQ3OTVjZTM0ZTNjYWU0YTgzNzk2YTE4ZDkyMzMyNjY2Y2JmMjE0ODhjMCJ9fX0= + SuccessTip: "&f你使用了&c&l真·生命宝石&f, 装备生命上限&c&l+1" + Rewards: + - Attribute{name=health;operation=0;slot=auto;var=v+1;limit=10} + +内置-神生命宝石: + Name: 神·生命宝石 + Require: + - ARMOR + Display: "&8&l&k||&c&l神·生命宝石&8&l&k||" + Success: 50 + Tips: + - "&c&l神·生命宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其最大生命上限+2(上限20)" + - "&7成功率: &a50%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDFhNmE0MTFiODAyYjIyZTUzMjQ3OTVjZTM0ZTNjYWU0YTgzNzk2YTE4ZDkyMzMyNjY2Y2JmMjE0ODhjMCJ9fX0= + SuccessTip: "&f你使用了&c&l神·生命宝石&f, 装备生命上限&c&l+2" + Rewards: + - Attribute{name=health;operation=0;slot=auto;var=v+2;limit=20} + +内置-速度宝石: + Name: 速度宝石 + Require: + - BOOTS + Display: "&8&l&k||&a&l速度宝石&8&l&k||" + Tips: + - "&a速度宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其速度属性+1%(上限6%)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzA1OGQwODBiZDYyMTU2ZTVmNmU1ZThkZmZhYTI4OTI2YmQ4MzM5MWRjMjZmZTc4MDY3M2VmNTJmMjZlYjA4YiJ9fX0= + SuccessTip: "&f你使用了&a&l速度宝石&f, 装备速度属性&a&l+1%" + Rewards: + - Attribute{name=move;operation=1;slot=auto;var=v+0.01;limit=0.06} + +内置-真速度宝石: + Name: 真·速度宝石 + Require: + - BOOTS + Display: "&8&l&k||&a&l真·速度宝石&8&l&k||" + Tips: + - "&a&l真速度宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其速度属性+1%(上限15%)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzA1OGQwODBiZDYyMTU2ZTVmNmU1ZThkZmZhYTI4OTI2YmQ4MzM5MWRjMjZmZTc4MDY3M2VmNTJmMjZlYjA4YiJ9fX0= + SuccessTip: "&f你使用了&a&l真·速度宝石&f, 装备速度属性&a&l+1%" + Rewards: + - Attribute{name=move;operation=1;slot=auto;var=v+0.01;limit=0.15} + +内置-神速度宝石: + Name: 神·速度宝石 + Require: + - BOOTS + Display: "&8&l&k||&a&l神·速度宝石&8&l&k||" + Success: 50 + Tips: + - "&a&l神·速度宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其速度属性+2%(上限30%)" + - "&7成功率: &a50%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzA1OGQwODBiZDYyMTU2ZTVmNmU1ZThkZmZhYTI4OTI2YmQ4MzM5MWRjMjZmZTc4MDY3M2VmNTJmMjZlYjA4YiJ9fX0= + SuccessTip: "&f你使用了&a&l神·速度宝石&f, 装备速度属性&a&l+2%" + Rewards: + - Attribute{name=move;operation=1;slot=auto;var=v+0.02;limit=0.3} + +内置-攻击伤害宝石: + Name: 攻击伤害宝石 + Require: + - WEAPON + Display: "&8&l&k||&c&l攻击伤害宝石&8&l&k||" + Tips: + - "&c攻击伤害宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其攻击伤害属性+1(上限20)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzEzODc0ZTgyYmM0NjZhOWM5Zjc5YWFmYmE3MTUxN2UyZWNjMjk0MWM2YmM5Njc5MDYyYTk3YjkxYjJkYWUzOSJ9fX0= + SuccessTip: "&f你使用了&c&l攻击伤害宝石&f, 装备攻击伤害属性&c&l+1%" + Rewards: + - Attribute{name=damage;operation=0;slot=auto;var=v+1;limit=20} + +内置-真攻击伤害宝石: + Name: 真·攻击伤害宝石 + Require: + - WEAPON + Display: "&8&l&k||&c&l真·攻击伤害宝石&8&l&k||" + Tips: + - "&c&l真·攻击伤害宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其攻击伤害属性+1(上限80)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzEzODc0ZTgyYmM0NjZhOWM5Zjc5YWFmYmE3MTUxN2UyZWNjMjk0MWM2YmM5Njc5MDYyYTk3YjkxYjJkYWUzOSJ9fX0= + SuccessTip: "&f你使用了&c&l真·攻击伤害宝石&f, 装备攻击伤害属性&c&l+1" + Rewards: + - Attribute{name=damage;operation=0;slot=auto;var=v+1;limit=80} + +内置-攻速宝石: + Name: 攻速宝石 + Require: + - WEAPON + Display: "&8&l&k||&c&l攻速宝石&8&l&k||" + Tips: + - "&c攻速宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其攻击速度属性+5%(上限80%)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg1YzE5YmVhMWUwYWExYjNmOGY5ODUzYzc1NGI1MzQ2NzcxOGNlMGY5MGRlODg2ZmQ0ZWYyYjQyMDk5NTVjNSJ9fX0= + SuccessTip: "&f你使用了&c&l攻速宝石&f, 装备攻击速度属性&c&l+5%" + Rewards: + - Attribute{name=attack_speed;operation=1;slot=auto;var=v+0.05;limit=0.8} + +内置-真攻速宝石: + Name: 真·攻速宝石 + Require: + - WEAPON + Display: "&8&l&k||&c&l真·攻速宝石&8&l&k||" + Tips: + - "&c&l真·攻速宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其攻击速度属性+10%(上限300%)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg1YzE5YmVhMWUwYWExYjNmOGY5ODUzYzc1NGI1MzQ2NzcxOGNlMGY5MGRlODg2ZmQ0ZWYyYjQyMDk5NTVjNSJ9fX0= + SuccessTip: "&f你使用了&c&l真·攻速宝石&f, 装备攻击速度属性&c&l+10%" + Rewards: + - Attribute{name=attack_speed;operation=1;slot=auto;var=v+0.1;limit=3} + +内置-攻击击退宝石: + Name: 攻击击退宝石 + Require: + - WEAPON + Display: "&8&l&k||&8&l攻击击退宝石&8&l&k||" + Tips: + - "&8攻击击退宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其击退属性+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDk5ZjI4MzMyYmNjMzQ5ZjQyMDIzYzI5ZTZlNjQxZjRiMTBhNmIxZTQ4NzE4Y2FlNTU3NDY2ZDUxZWI5MjIifX19 + SuccessTip: "&f你使用了&8&l攻击击退宝石&f, 武器击退属性&8&l+1" + Rewards: + - Attribute{name=attack_knockback;operation=0;slot=auto;var=v+1;limit=10} + +内置-幸运宝石: + Name: 幸运宝石 + Require: + - WEAPON + - ARMOR + - TOOL + Display: "&8&l&k||&2&l幸运宝石&8&l&k||" + Tips: + - "&2幸运宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的装备" + - "&7即可为其幸运属性+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWEwZWYyMDNhM2MzODM3ZmQ0YTEwZTFjM2YxOWNjNDBlNjQ5N2NiOWI5NzgwNzRkNDcyYzY1ZDQ4MmY2YjY3MyJ9fX0= + SuccessTip: "&f你使用了&2&l幸运宝石&f, 装备幸运属性&2&l+1" + Rewards: + - Attribute{name=luck;operation=0;slot=auto;var=v+1;limit=10} + +内置-护甲宝石: + Name: 护甲宝石 + Require: + - ARMOR + Display: "&8&l&k||&7&l护甲宝石&8&l&k||" + Tips: + - "&7护甲宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其护甲属性+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJkMzFhZDFlOTNmMTY2Zjk3ODU5Y2Q5ZjJiODBkYTUyYTgyOTQ0MDA5N2Y1ZDY3YThjMjEwZDEyMmI5ZDVlNSJ9fX0= + SuccessTip: "&f你使用了&7&l护甲宝石&f, 盔甲护甲属性&7&l+1" + Rewards: + - Attribute{name=armor;operation=0;slot=auto;var=v+1;limit=10} + +内置-盔甲韧性宝石: + Name: 盔甲韧性宝石 + Require: + - ARMOR + Display: "&8&l&k||&7&l盔甲韧性宝石&8&l&k||" + Tips: + - "&7盔甲韧性宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其盔甲韧性属性+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2I5MjRiMzdmZTMyOTQyNzNhNzQzODZjODc4Y2EyMTBmYzg5ZjQ3ODcwMjk0M2EwMjcyZTcxMzk1NjMwYmVkYSJ9fX0= + SuccessTip: "&f你使用了&7&l盔甲韧性宝石&f, 盔甲盔甲韧性属性&7&l+1" + Rewards: + - Attribute{name=armor_toughness;operation=0;slot=auto;var=v+1;limit=10} + +内置-击退抗性宝石: + Name: 击退抗性宝石 + Require: + - ARMOR + Display: "&8&l&k||&8&l击退抗性宝石&8&l&k||" + Tips: + - "&8击退抗性宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其击退抗性属性+1(上限10)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWNiNzdhOWNlYmUxYzE5NjIxMDUyN2EwNjk4OTNiOGY2Mzk1NGIzNDc1NzIwZjgwNDBmZTEyMzNhYWY4OWYwZiJ9fX0= + SuccessTip: "&f你使用了&8&l击退抗性宝石&f, 盔甲击退抗性属性&8&l+1" + Rewards: + - Attribute{name=knockback_resistance;operation=0;slot=auto;var=v+0.1;limit=1} diff --git a/src/main/resources/gems/ButtonGem.yml b/src/main/resources/gems/ButtonGem.yml new file mode 100644 index 0000000..3756ddb --- /dev/null +++ b/src/main/resources/gems/ButtonGem.yml @@ -0,0 +1,1337 @@ +# 本文件中的宝石全是符文台GUI里的按钮 +# 一般情况下无需拿出来用 + +内置-符文碎片: + Name: 符文碎片 + Display: "&8&l&k||&a&l符文碎片&8&l&k||" + Tips: + - "&7破碎符文留下的残片" + - "&7可在&6符文合成台&7重铸" + Require: + - NOTHING + Material: EMPTY_MAP + Glow: true + Rewards: + - EMPTY + +# (分解台按钮用) +内置-符文分解券: + Name: 符文分解券 + Require: + - LORE:符文等级 + Display: "&f符文分解券" + Material: PAPER + Glow: true + Rewards: + - Conditional{condition="$LORE:符文等级:$==1";roman=true;reward=ItemSet{Gem=符文碎片;Amount=10}} $ignorable + - Conditional{condition="$LORE:符文等级:$==2";roman=true;reward=ItemSet{Gem=符文碎片;Amount=20}} $ignorable + - Conditional{condition="$LORE:符文等级:$==3";roman=true;reward=ItemSet{Gem=符文碎片;Amount=50}} $ignorable + - Conditional{condition="$LORE:符文等级:$==4";roman=true;reward=ItemSet{Gem=符文碎片;Amount=200}} $ignorable + - Conditional{condition="$LORE:符文等级:$==5";roman=true;reward=ItemSet{Gem=符文碎片;Amount=800}} $ignorable + SuccessTip: "none" + +内置-合成红色符文强攻1: + Name: 合成红色符文强攻1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l强攻 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文强攻1}" + +内置-合成红色符文强攻2: + Name: 合成红色符文强攻2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l强攻 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文强攻2}" + +内置-合成红色符文强攻3: + Name: 合成红色符文强攻3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l强攻 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文强攻3}" + +内置-合成红色符文强攻4: + Name: 合成红色符文强攻4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l强攻 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文强攻4}" + +内置-合成红色符文强攻5: + Name: 合成红色符文强攻5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l强攻 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文强攻5}" + + +内置-合成红色符文致命1: + Name: 合成红色符文致命1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l致命 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文致命1}" + +内置-合成红色符文致命2: + Name: 合成红色符文致命2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l致命 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文致命2}" + +内置-合成红色符文致命3: + Name: 合成红色符文致命3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l致命 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文致命3}" + +内置-合成红色符文致命4: + Name: 合成红色符文致命4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l致命 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文致命4}" + +内置-合成红色符文致命5: + Name: 合成红色符文致命5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l致命 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文致命5}" + +内置-合成红色符文灾祸1: + Name: 合成红色符文灾祸1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l灾祸 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文灾祸1}" + +内置-合成红色符文灾祸2: + Name: 合成红色符文灾祸2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l灾祸 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文灾祸2}" + +内置-合成红色符文灾祸3: + Name: 合成红色符文灾祸3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l灾祸 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文灾祸3}" + +内置-合成红色符文灾祸4: + Name: 合成红色符文灾祸4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l灾祸 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文灾祸4}" + +内置-合成红色符文灾祸5: + Name: 合成红色符文灾祸5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l灾祸 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文灾祸5}" + +内置-合成红色符文流光1: + Name: 合成红色符文流光1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l流光 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文流光1}" + +内置-合成红色符文流光2: + Name: 合成红色符文流光2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l流光 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文流光2}" + +内置-合成红色符文流光3: + Name: 合成红色符文流光3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l流光 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文流光3}" + +内置-合成红色符文流光4: + Name: 合成红色符文流光4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l流光 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文流光4}" + +内置-合成红色符文流光5: + Name: 合成红色符文流光5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l流光 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文流光5}" + +内置-合成红色符文狂暴1: + Name: 合成红色符文狂暴1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l狂暴 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文狂暴1}" + +内置-合成红色符文狂暴2: + Name: 合成红色符文狂暴2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l狂暴 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文狂暴2}" + +内置-合成红色符文狂暴3: + Name: 合成红色符文狂暴3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l狂暴 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文狂暴3}" + +内置-合成红色符文狂暴4: + Name: 合成红色符文狂暴4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l狂暴 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文狂暴4}" + +内置-合成红色符文狂暴5: + Name: 合成红色符文狂暴5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l狂暴 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文狂暴5}" + +内置-合成红色符文烈焰1: + Name: 合成红色符文烈焰1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l烈焰 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文烈焰1}" + +内置-合成红色符文烈焰2: + Name: 合成红色符文烈焰2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l烈焰 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文烈焰2}" + +内置-合成红色符文烈焰3: + Name: 合成红色符文烈焰3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l烈焰 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文烈焰3}" + +内置-合成红色符文烈焰4: + Name: 合成红色符文烈焰4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l烈焰 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文烈焰4}" + +内置-合成红色符文烈焰5: + Name: 合成红色符文烈焰5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l烈焰 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文烈焰5}" + +内置-合成红色符文坚守1: + Name: 合成红色符文坚守1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l坚守 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文坚守1}" + +内置-合成红色符文坚守2: + Name: 合成红色符文坚守2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l坚守 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文坚守2}" + +内置-合成红色符文坚守3: + Name: 合成红色符文坚守3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l坚守 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文坚守3}" + +内置-合成红色符文坚守4: + Name: 合成红色符文坚守4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l坚守 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文坚守4}" + +内置-合成红色符文坚守5: + Name: 合成红色符文坚守5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l坚守 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文坚守5}" + +内置-合成红色符文重盾1: + Name: 合成红色符文重盾1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l重盾 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文重盾1}" + +内置-合成红色符文重盾2: + Name: 合成红色符文重盾2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l重盾 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文重盾2}" + +内置-合成红色符文重盾3: + Name: 合成红色符文重盾3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l重盾 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文重盾3}" + +内置-合成红色符文重盾4: + Name: 合成红色符文重盾4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l重盾 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文重盾4}" + +内置-合成红色符文重盾5: + Name: 合成红色符文重盾5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l重盾 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文重盾5}" + +内置-合成红色符文稳健1: + Name: 合成红色符文稳健1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l稳健 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文稳健1}" + +内置-合成红色符文稳健2: + Name: 合成红色符文稳健2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l稳健 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文稳健2}" + +内置-合成红色符文稳健3: + Name: 合成红色符文稳健3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l稳健 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文稳健3}" + +内置-合成红色符文稳健4: + Name: 合成红色符文稳健4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l稳健 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文稳健4}" + +内置-合成红色符文稳健5: + Name: 合成红色符文稳健5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l稳健 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文稳健5}" + +内置-合成红色符文神圣1: + Name: 合成红色符文神圣1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l神圣 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=红色符文神圣1}" + +内置-合成红色符文神圣2: + Name: 合成红色符文神圣2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l神圣 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=红色符文神圣2}" + +内置-合成红色符文神圣3: + Name: 合成红色符文神圣3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l神圣 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=红色符文神圣3}" + +内置-合成红色符文神圣4: + Name: 合成红色符文神圣4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l神圣 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=红色符文神圣4}" + +内置-合成红色符文神圣5: + Name: 合成红色符文神圣5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&c&l神圣 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=红色符文神圣5}" + +内置-合成绿色符文锐利1: + Name: 合成绿色符文锐利1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l锐利 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文锐利1}" + +内置-合成绿色符文锐利2: + Name: 合成绿色符文锐利2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l锐利 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文锐利2}" + +内置-合成绿色符文锐利3: + Name: 合成绿色符文锐利3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l锐利 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文锐利3}" + +内置-合成绿色符文锐利4: + Name: 合成绿色符文锐利4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l锐利 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文锐利4}" + +内置-合成绿色符文锐利5: + Name: 合成绿色符文锐利5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l锐利 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文锐利5}" + +内置-合成绿色符文看破1: + Name: 合成绿色符文看破1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l看破 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文看破1}" + +内置-合成绿色符文看破2: + Name: 合成绿色符文看破2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l看破 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文看破2}" + +内置-合成绿色符文看破3: + Name: 合成绿色符文看破3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l看破 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文看破3}" + +内置-合成绿色符文看破4: + Name: 合成绿色符文看破4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l看破 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文看破4}" + +内置-合成绿色符文看破5: + Name: 合成绿色符文看破5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l看破 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文看破5}" + +内置-合成绿色符文重挫1: + Name: 合成绿色符文重挫1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重挫 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文重挫1}" + +内置-合成绿色符文重挫2: + Name: 合成绿色符文重挫2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重挫 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文重挫2}" + +内置-合成绿色符文重挫3: + Name: 合成绿色符文重挫3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重挫 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文重挫3}" + +内置-合成绿色符文重挫4: + Name: 合成绿色符文重挫4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重挫 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文重挫4}" + +内置-合成绿色符文重挫5: + Name: 合成绿色符文重挫5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重挫 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文重挫5}" + +内置-合成绿色符文精准1: + Name: 合成绿色符文精准1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l精准 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文精准1}" + +内置-合成绿色符文精准2: + Name: 合成绿色符文精准2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l精准 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文精准2}" + +内置-合成绿色符文精准3: + Name: 合成绿色符文精准3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l精准 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文精准3}" + +内置-合成绿色符文精准4: + Name: 合成绿色符文精准4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l精准 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文精准4}" + +内置-合成绿色符文精准5: + Name: 合成绿色符文精准5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l精准 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文精准5}" + +内置-合成绿色符文重矢1: + Name: 合成绿色符文重矢1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重矢 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文重矢1}" + +内置-合成绿色符文重矢2: + Name: 合成绿色符文重矢2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重矢 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文重矢2}" + +内置-合成绿色符文重矢3: + Name: 合成绿色符文重矢3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重矢 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文重矢3}" + +内置-合成绿色符文重矢4: + Name: 合成绿色符文重矢4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重矢 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文重矢4}" + +内置-合成绿色符文重矢5: + Name: 合成绿色符文重矢5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l重矢 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文重矢5}" + +内置-合成绿色符文聪慧1: + Name: 合成绿色符文聪慧1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l聪慧 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文聪慧1}" + +内置-合成绿色符文聪慧2: + Name: 合成绿色符文聪慧2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l聪慧 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文聪慧2}" + +内置-合成绿色符文聪慧3: + Name: 合成绿色符文聪慧3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l聪慧 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文聪慧3}" + +内置-合成绿色符文聪慧4: + Name: 合成绿色符文聪慧4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l聪慧 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文聪慧4}" + +内置-合成绿色符文聪慧5: + Name: 合成绿色符文聪慧5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l聪慧 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文聪慧5}" + +内置-合成绿色符文长生1: + Name: 合成绿色符文长生1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l长生 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文长生1}" + +内置-合成绿色符文长生2: + Name: 合成绿色符文长生2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l长生 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文长生2}" + +内置-合成绿色符文长生3: + Name: 合成绿色符文长生3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l长生 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文长生3}" + +内置-合成绿色符文长生4: + Name: 合成绿色符文长生4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l长生 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文长生4}" + +内置-合成绿色符文长生5: + Name: 合成绿色符文长生5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l长生 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文长生5}" + +内置-合成绿色符文强健1: + Name: 合成绿色符文强健1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l强健 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文强健1}" + +内置-合成绿色符文强健2: + Name: 合成绿色符文强健2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l强健 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文强健2}" + +内置-合成绿色符文强健3: + Name: 合成绿色符文强健3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l强健 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文强健3}" + +内置-合成绿色符文强健4: + Name: 合成绿色符文强健4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l强健 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文强健4}" + +内置-合成绿色符文强健5: + Name: 合成绿色符文强健5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l强健 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文强健5}" + +内置-合成绿色符文圣泉1: + Name: 合成绿色符文圣泉1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l圣泉 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=绿色符文圣泉1}" + +内置-合成绿色符文圣泉2: + Name: 合成绿色符文圣泉2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l圣泉 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=绿色符文圣泉2}" + +内置-合成绿色符文圣泉3: + Name: 合成绿色符文圣泉3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l圣泉 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=绿色符文圣泉3}" + +内置-合成绿色符文圣泉4: + Name: 合成绿色符文圣泉4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l圣泉 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=绿色符文圣泉4}" + +内置-合成绿色符文圣泉5: + Name: 合成绿色符文圣泉5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&a&l圣泉 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=绿色符文圣泉5}" + +内置-合成蓝色符文迅捷1: + Name: 合成蓝色符文迅捷1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l迅捷 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文迅捷1}" + +内置-合成蓝色符文迅捷2: + Name: 合成蓝色符文迅捷2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l迅捷 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文迅捷2}" + +内置-合成蓝色符文迅捷3: + Name: 合成蓝色符文迅捷3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l迅捷 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文迅捷3}" + +内置-合成蓝色符文迅捷4: + Name: 合成蓝色符文迅捷4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l迅捷 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文迅捷4}" + +内置-合成蓝色符文迅捷5: + Name: 合成蓝色符文迅捷5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l迅捷 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文迅捷5}" + +内置-合成蓝色符文奔腾1: + Name: 合成蓝色符文奔腾1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l奔腾 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文奔腾1}" + +内置-合成蓝色符文奔腾2: + Name: 合成蓝色符文奔腾2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l奔腾 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文奔腾2}" + +内置-合成蓝色符文奔腾3: + Name: 合成蓝色符文奔腾3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l奔腾 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文奔腾3}" + +内置-合成蓝色符文奔腾4: + Name: 合成蓝色符文奔腾4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l奔腾 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文奔腾4}" + +内置-合成蓝色符文奔腾5: + Name: 合成蓝色符文奔腾5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l奔腾 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文奔腾5}" + +内置-合成蓝色符文极寒1: + Name: 合成蓝色符文极寒1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l极寒 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文极寒1}" + +内置-合成蓝色符文极寒2: + Name: 合成蓝色符文极寒2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l极寒 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文极寒2}" + +内置-合成蓝色符文极寒3: + Name: 合成蓝色符文极寒3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l极寒 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文极寒3}" + +内置-合成蓝色符文极寒4: + Name: 合成蓝色符文极寒4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l极寒 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文极寒4}" + +内置-合成蓝色符文极寒5: + Name: 合成蓝色符文极寒5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l极寒 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文极寒5}" + +内置-合成蓝色符文雷霆1: + Name: 合成蓝色符文雷霆1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l雷霆 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文雷霆1}" + +内置-合成蓝色符文雷霆2: + Name: 合成蓝色符文雷霆2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l雷霆 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文雷霆2}" + +内置-合成蓝色符文雷霆3: + Name: 合成蓝色符文雷霆3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l雷霆 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文雷霆3}" + +内置-合成蓝色符文雷霆4: + Name: 合成蓝色符文雷霆4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l雷霆 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文雷霆4}" + +内置-合成蓝色符文雷霆5: + Name: 合成蓝色符文雷霆5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l雷霆 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文雷霆5}" + +内置-合成蓝色符文汲取1: + Name: 合成蓝色符文汲取1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l汲取 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文汲取1}" + +内置-合成蓝色符文汲取2: + Name: 合成蓝色符文汲取2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l汲取 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文汲取2}" + +内置-合成蓝色符文汲取3: + Name: 合成蓝色符文汲取3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l汲取 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文汲取3}" + +内置-合成蓝色符文汲取4: + Name: 合成蓝色符文汲取4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l汲取 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文汲取4}" + +内置-合成蓝色符文汲取5: + Name: 合成蓝色符文汲取5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l汲取 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文汲取5}" + +内置-合成蓝色符文穿杨1: + Name: 合成蓝色符文穿杨1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l穿杨 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文穿杨1}" + +内置-合成蓝色符文穿杨2: + Name: 合成蓝色符文穿杨2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l穿杨 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文穿杨2}" + +内置-合成蓝色符文穿杨3: + Name: 合成蓝色符文穿杨3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l穿杨 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文穿杨3}" + +内置-合成蓝色符文穿杨4: + Name: 合成蓝色符文穿杨4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l穿杨 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文穿杨4}" + +内置-合成蓝色符文穿杨5: + Name: 合成蓝色符文穿杨5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l穿杨 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文穿杨5}" + +内置-合成蓝色符文毒刺1: + Name: 合成蓝色符文毒刺1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l毒刺 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文毒刺1}" + +内置-合成蓝色符文毒刺2: + Name: 合成蓝色符文毒刺2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l毒刺 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文毒刺2}" + +内置-合成蓝色符文毒刺3: + Name: 合成蓝色符文毒刺3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l毒刺 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文毒刺3}" + +内置-合成蓝色符文毒刺4: + Name: 合成蓝色符文毒刺4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l毒刺 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文毒刺4}" + +内置-合成蓝色符文毒刺5: + Name: 合成蓝色符文毒刺5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l毒刺 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文毒刺5}" + +内置-合成蓝色符文幻影1: + Name: 合成蓝色符文幻影1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l幻影 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文幻影1}" + +内置-合成蓝色符文幻影2: + Name: 合成蓝色符文幻影2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l幻影 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文幻影2}" + +内置-合成蓝色符文幻影3: + Name: 合成蓝色符文幻影3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l幻影 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文幻影3}" + +内置-合成蓝色符文幻影4: + Name: 合成蓝色符文幻影4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l幻影 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文幻影4}" + +内置-合成蓝色符文幻影5: + Name: 合成蓝色符文幻影5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l幻影 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文幻影5}" + +内置-合成蓝色符文格挡1: + Name: 合成蓝色符文格挡1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l格挡 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文格挡1}" + +内置-合成蓝色符文格挡2: + Name: 合成蓝色符文格挡2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l格挡 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文格挡2}" + +内置-合成蓝色符文格挡3: + Name: 合成蓝色符文格挡3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l格挡 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文格挡3}" + +内置-合成蓝色符文格挡4: + Name: 合成蓝色符文格挡4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l格挡 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文格挡4}" + +内置-合成蓝色符文格挡5: + Name: 合成蓝色符文格挡5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l格挡 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文格挡5}" + +内置-合成蓝色符文盾山1: + Name: 合成蓝色符文盾山1 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l盾山 I&f" + Rewards: + - "ItemTake{GEM:符文碎片=15}" + - "ItemGive{gem=蓝色符文盾山1}" + +内置-合成蓝色符文盾山2: + Name: 合成蓝色符文盾山2 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l盾山 II&f" + Rewards: + - "ItemTake{GEM:符文碎片=30}" + - "ItemGive{gem=蓝色符文盾山2}" + +内置-合成蓝色符文盾山3: + Name: 合成蓝色符文盾山3 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l盾山 III&f" + Rewards: + - "ItemTake{GEM:符文碎片=80}" + - "ItemGive{gem=蓝色符文盾山3}" + +内置-合成蓝色符文盾山4: + Name: 合成蓝色符文盾山4 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l盾山 IV&f" + Rewards: + - "ItemTake{GEM:符文碎片=300}" + - "ItemGive{gem=蓝色符文盾山4}" + +内置-合成蓝色符文盾山5: + Name: 合成蓝色符文盾山5 + Type: PLAYER_GEM + Material: STONE + SuccessTip: "&f你合成了&9&l盾山 V&f" + Rewards: + - "ItemTake{GEM:符文碎片=1200}" + - "ItemGive{gem=蓝色符文盾山5}" diff --git a/src/main/resources/gems/EmbedGem.yml b/src/main/resources/gems/EmbedGem.yml new file mode 100644 index 0000000..498089c --- /dev/null +++ b/src/main/resources/gems/EmbedGem.yml @@ -0,0 +1,131 @@ +# 下面是可镶嵌宝石 +# 需要镶嵌槽位的宝石 应如下配置: +Version: 1.05 + +内置-单行槽位开槽粉尘: + Name: 单行槽位开槽粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + Display: "&8&l&k||&9&l单行槽位开槽粉尘&8&l&k||" + Tips: + - "&9&l单行槽位开槽粉尘 &7品质: &a&l传说" + - "&7在工作台内点击此粉尘, 再点击您的装备" + - "&7即可为装备开启&9蓝宝石&f槽(仅限一次)" + Material: CYAN_DYE + Glow: true + SuccessTip: "&f您为装备开启了&9&l蓝宝石&f槽" + Rewards: + - LoreAdd{lore=&f&l[&9蓝宝石&f&l]&7&l⬛&7&l⬛&7&l⬛;mode=after;locator="宝石槽";limit=1} + +内置-单格槽位开槽粉尘: + Name: 单格槽位开槽粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + Display: "&8&l&k||&9&l单格槽位开槽粉尘&8&l&k||" + Tips: + - "&9&l单格槽位开槽粉尘 &7品质: &a&l传说" + - "&7需要槽位&9&l蓝宝石槽" + - "&7在工作台内点击此粉尘, 再点击您的装备" + - "&7即可为其附加一个蓝宝石格 (上限7个)" + Material: CYAN_DYE + Glow: true + SuccessTip: "&f您为装备开启了一个&9&l蓝宝石槽" + Rewards: + - LoreAdd{lore=&7&l⬛;mode=line;locator=&f&l[&9蓝宝石&f&l];limit=7;force=false} + +内置-行内暴击几率蓝宝石: + Name: 行内暴击几率蓝宝石 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l行内暴击几率蓝宝石&8&l&k||" + Tips: + - "&9&l行内暴击几率蓝石 &7品质: &f普通" + - "&7需要槽位&9蓝宝石槽" + - "&7在镶嵌台内镶嵌此宝石" + - "&7即可使装备暴击几率属性+10%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2Y3M2EzZGQ5NGE4OTk5NDc1N2RmOWJlNjMzMGNjM2U4NDgzOTIzMGRlOTFkNDViZmRhYzE5MjcyNzJmYyJ9fX0= + SuccessTip: "&f你使用了&9&l暴击几率蓝宝石&f, 装备暴击几率&9&l+10%" + Embed: 8 + Rewards: + - LoreReplace{old=&7&l⬛;lore=&9&l⬛;mode=line;locator=&f&l[&9蓝宝石&f&l]} + - "LoreVar{lore=&7&l■ 暴击几率: +&a&l;locator=宝石属性;mode=after;var=v+10;inv=v-10;format=%.2f%%}" + +内置-行内物理伤害蓝宝石: + Name: 行内物理伤害蓝宝石 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l行内物理伤害蓝宝石&8&l&k||" + Tips: + - "&9&l行内物理伤害蓝石 &7品质: &f普通" + - "&7需要槽位&9蓝宝石槽" + - "&7在镶嵌台内镶嵌此宝石" + - "&7即可使装备物理伤害属性+10" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2Y3M2EzZGQ5NGE4OTk5NDc1N2RmOWJlNjMzMGNjM2U4NDgzOTIzMGRlOTFkNDViZmRhYzE5MjcyNzJmYyJ9fX0= + SuccessTip: "&f你使用了&9&l物理伤害蓝宝石&f, 装备物理伤害&9&l+10" + Embed: 8 + Rewards: + - LoreReplace{old=&7&l⬛;lore=&9&l⬛;mode=line;locator=&f&l[&9蓝宝石&f&l]} + - "LoreVar{lore=&7&l■ 物理伤害: +&a&l;locator=宝石属性;mode=after;var=v+10;inv=v-10;format=%.2f}" + +内置-多行槽位开槽粉尘: + Name: 多行槽位开槽粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + Display: "&8&l&k||&c&l多行槽位开槽粉尘&8&l&k||" + Tips: + - "&c&l多行槽位开槽粉尘 &7品质: &a&l传说" + - "&7在工作台内点击此粉尘" + - "&7再点击您的装备" + - "&7即可为装备添加一个红宝石槽(上限三个)" + Material: REDSTONE + Glow: true + SuccessTip: "&f您为装备开启了一个&c&l红宝石槽" + Rewards: + - LoreAdd{lore=&c<----红宝石槽---->;locator=宝石槽;mode=after;limit=3} + +内置-槽内物理伤害红宝石: + Name: 槽内物理伤害红宝石 + Require: + - SWORD + - _AXE + Display: "&8&l&k||&c&l槽内物理伤害宝石&8&l&k||" + Tips: + - "&c&l槽内物理伤害宝石 &7品质: &a&l史诗" + - "&7需要槽位&c红宝石槽" + - "&7在镶嵌台内镶嵌此宝石" + - "&7即可为武器物理伤害+10" + - "&7拆卸需花费1000点券" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTcwZjZjNDBjYzdiNTZhNWU3MDFjZmNjZDMxYzE0MzVhYmFiZmM0NWViMzNkODE1YTViNDEyMDNmMjExNWEwYiJ9fX0= + SuccessTip: "&f你使用了&c&l槽内物理伤害宝石&f, 武器物理伤害&c&l+10" + Embed: 5 + Rewards: + - "LoreVar{lore=&c<-&7&l■ 物理伤害: +&a&l;locator=&c<----红宝石槽---->;mode=line;var=v+10;inv=v-10;format=%.2f&c->}" + +内置-槽外物理伤害宝石: + Name: 槽外物理伤害红宝石 + Require: + - SWORD + - _AXE + Display: "&8&l&k||&c&l槽外物理伤害宝石&8&l&k||" + Tips: + - "&c&l槽外物理伤害宝石 &7品质: &a&l史诗" + - "&7需要槽位&c红宝石槽" + - "&7在镶嵌台内镶嵌此宝石" + - "&7即可为武器物理伤害+10" + - "&7拆卸需花费1000点券" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTcwZjZjNDBjYzdiNTZhNWU3MDFjZmNjZDMxYzE0MzVhYmFiZmM0NWViMzNkODE1YTViNDEyMDNmMjExNWEwYiJ9fX0= + SuccessTip: "&f你使用了&c&l槽外物理伤害宝石&f, 武器物理伤害&c&l+10" + Embed: 5 + Rewards: + - LoreReplace{old=&c<----红宝石槽---->;new=&c<&8&l&k||&c&l物理伤害宝石&8&l&k||&c>} + - "LoreVar{lore=&7&l■ 物理伤害: +&a&l;var=v+10;inv=v-10;format=%.2f;mode=last}" diff --git a/src/main/resources/gems/EnchantGem.yml b/src/main/resources/gems/EnchantGem.yml new file mode 100644 index 0000000..6adc29f --- /dev/null +++ b/src/main/resources/gems/EnchantGem.yml @@ -0,0 +1,751 @@ +# +# 以下是升级原版附魔的宝石 +# 你可以在游戏内用mgem enchant查看附魔的名称 +# 支持GoldenEnchant/EcoEnchant的特殊附魔. 你可以自行添加 +Version: 1.05 + +内置-耐久宝石: + Name: 耐久宝石 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + - FLINT_AND_STEEL + Display: "&8&l&k||&b&l耐久宝石&8&l&k||" + Tips: + - "&b耐久宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的装备" + - "&7即可为其耐久等级+1(上限7级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTExNWRjODhlMzIxNGMzODI0M2Q3ODJkNjNlZGIwYTZlMDYyOTFlYjZkYThlNjAwYzdlMmVhMzZlN2Y2MWIzMSJ9fX0= + SuccessTip: "&f你使用了&b&l耐久宝石&f, 物品耐久等级&b&l+1" + Rewards: + - Enchant{name=DURABILITY;limit=7} + +内置-真耐久宝石: + Name: 真·耐久宝石 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + - FLINT_AND_STEEL + Display: "&8&l&k||&b&l真·耐久宝石&8&l&k||" + Tips: + - "&b&l真·耐久宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的装备" + - "&7即可为其耐久等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTExNWRjODhlMzIxNGMzODI0M2Q3ODJkNjNlZGIwYTZlMDYyOTFlYjZkYThlNjAwYzdlMmVhMzZlN2Y2MWIzMSJ9fX0= + SuccessTip: "&f你使用了&b&l真·耐久宝石&f, 物品耐久等级&b&l+1" + Rewards: + - Enchant{name=DURABILITY;limit=15} + +内置-时运宝石: + Name: 时运宝石 + Require: + - TOOL + Display: "&8&l&k||&d&l时运宝石&8&l&k||" + Tips: + - "&d时运宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的工具" + - "&7即可为其时运等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGI2OTc1YWY3MDcyNGQ2YTQ0ZmQ1OTQ2ZTYwYjI3MTc3MzdkZmRiNTQ1YjRkYWIxODkzMzUxYTljOWRkMTgzYyJ9fX0= + SuccessTip: "&f你使用了&d&l时运宝石&f, 工具时运等级&d&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_BLOCKS;limit=15} + +内置-真时运宝石: + Name: 真·时运宝石 + Require: + - TOOL + Display: "&8&l&k||&d&l真·时运宝石&8&l&k||" + Tips: + - "&d&l真·时运宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的工具" + - "&7即可为其时运等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGI2OTc1YWY3MDcyNGQ2YTQ0ZmQ1OTQ2ZTYwYjI3MTc3MzdkZmRiNTQ1YjRkYWIxODkzMzUxYTljOWRkMTgzYyJ9fX0= + SuccessTip: "&f你使用了&d&l真·时运宝石&f, 工具时运等级&d&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_BLOCKS;limit=40} + +内置-神时运宝石: + Name: 神·时运宝石 + Require: + - TOOL + Display: "&8&l&k||&d&l神·时运宝石&8&l&k||" + Success: 80 + Tips: + - "&d&l神·时运宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的工具" + - "&7即可为其时运等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGI2OTc1YWY3MDcyNGQ2YTQ0ZmQ1OTQ2ZTYwYjI3MTc3MzdkZmRiNTQ1YjRkYWIxODkzMzUxYTljOWRkMTgzYyJ9fX0= + SuccessTip: "&f你使用了&d&l神·时运宝石&f, 工具时运等级&d&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_BLOCKS;limit=512} + +内置-锋利宝石: + Name: 锋利宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&c&l锋利宝石&8&l&k||" + Tips: + - "&c锋利宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其锋利等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmIwNTVjODEwYmRkZmQxNjI2NGVjOGQ0MzljNDMyODNlMzViY2E3MWE1MDk4M2UxNWUzNjRjZDhhYjdjNjY4ZiJ9fX0= + SuccessTip: "&f你使用了&c&l锋利宝石&f, 剑/斧锋利等级&c&l+1" + Rewards: + - Enchant{name=DAMAGE_ALL;limit=15} + +内置-真锋利宝石: + Name: 真·锋利宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&c&l真·锋利宝石&8&l&k||" + Tips: + - "&c&l真·锋利宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其锋利等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmIwNTVjODEwYmRkZmQxNjI2NGVjOGQ0MzljNDMyODNlMzViY2E3MWE1MDk4M2UxNWUzNjRjZDhhYjdjNjY4ZiJ9fX0= + SuccessTip: "&f你使用了&c&l真·锋利宝石&f, 剑/斧锋利等级&c&l+1" + Rewards: + - Enchant{name=DAMAGE_ALL;limit=40} + +内置-神锋利宝石: + Name: 神·锋利宝石 + Require: + - _AXE + - SWORD + Success: 80 + Display: "&8&l&k||&c&l神·锋利宝石&8&l&k||" + Tips: + - "&c&l神·锋利宝石 &7品质: &b史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其锋利等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmIwNTVjODEwYmRkZmQxNjI2NGVjOGQ0MzljNDMyODNlMzViY2E3MWE1MDk4M2UxNWUzNjRjZDhhYjdjNjY4ZiJ9fX0= + SuccessTip: "&f你使用了&c&l神·锋利宝石&f, 剑/斧锋利等级&c&l+1" + Rewards: + - Enchant{name=DAMAGE_ALL} + +内置-抢夺宝石: + Name: 抢夺宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&8&l抢夺宝石&8&l&k||" + Tips: + - "&8抢夺宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其抢夺等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjI0MDcwYzliNjY1OWVkMjViMmNhMTI2OTE1ZjRkODgyMGZhZmNlNDMyNGVkOWE4ZjRiOGE1MDYzNDUzMDdmIn19fQ== + SuccessTip: "&f你使用了&8&l抢夺宝石&f, 剑/斧抢夺等级&8&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_MOBS;limit=15} + +内置-真抢夺宝石: + Name: 真·抢夺宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&8&l真·抢夺宝石&8&l&k||" + Tips: + - "&8&l真·抢夺宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其抢夺等级+1(上限30级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjI0MDcwYzliNjY1OWVkMjViMmNhMTI2OTE1ZjRkODgyMGZhZmNlNDMyNGVkOWE4ZjRiOGE1MDYzNDUzMDdmIn19fQ== + SuccessTip: "&f你使用了&8&l真·抢夺宝石&f, 武器抢夺等级&8&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_MOBS;limit=30} + +内置-神抢夺宝石: + Name: 神·抢夺宝石 + Require: + - _AXE + - SWORD + Success: 80 + Display: "&8&l&k||&8&l神·抢夺宝石&8&l&k||" + Tips: + - "&8&l神·抢夺宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其抢夺等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjI0MDcwYzliNjY1OWVkMjViMmNhMTI2OTE1ZjRkODgyMGZhZmNlNDMyNGVkOWE4ZjRiOGE1MDYzNDUzMDdmIn19fQ== + SuccessTip: "&f你使用了&8&l神·抢夺宝石&f, 武器抢夺等级&8&l+1" + Rewards: + - Enchant{name=LOOT_BONUS_MOBS} + +内置-力量宝石: + Name: 力量宝石 + Require: + - BOW + Display: "&8&l&k||&e&l力量宝石&8&l&k||" + Tips: + - "&e力量宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的弓" + - "&7即可为其力量等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTIwNzE2MjAzZGEwMzljYWZjYTI0YmJkOWYzZTliZDVjNjk4NWMzYzI1NTI3YmNkNTA2ZDM4OGU5OWJiN2FmZSJ9fX0= + SuccessTip: "&f你使用了&e&l力量宝石&f, 弓力量等级&e&l+1" + Rewards: + - Enchant{name=ARROW_DAMAGE;limit=15} + +内置-真力量宝石: + Name: 真·力量宝石 + Require: + - BOW + Display: "&8&l&k||&e&l真·&l力量宝石&8&l&k||" + Tips: + - "&e&l真·力量宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的弓" + - "&7即可为其力量等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTIwNzE2MjAzZGEwMzljYWZjYTI0YmJkOWYzZTliZDVjNjk4NWMzYzI1NTI3YmNkNTA2ZDM4OGU5OWJiN2FmZSJ9fX0= + SuccessTip: "&f你使用了&e&l真·力量宝石&f, 弓力量等级&e&l+1" + Rewards: + - Enchant{name=ARROW_DAMAGE;limit=40} + +内置-神力量宝石: + Name: 神·力量宝石 + Require: + - BOW + Success: 80 + Display: "&8&l&k||&e&l神·力量宝石&8&l&k||" + Tips: + - "&e&l神·力量宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的弓" + - "&7即可为其力量等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTIwNzE2MjAzZGEwMzljYWZjYTI0YmJkOWYzZTliZDVjNjk4NWMzYzI1NTI3YmNkNTA2ZDM4OGU5OWJiN2FmZSJ9fX0= + SuccessTip: "&f你使用了&e&l神·力量宝石&f, 弓力量等级&e&l+1" + Rewards: + - Enchant{name=ARROW_DAMAGE} + +内置-荆棘宝石: + Name: 荆棘宝石 + Require: + - ARMOR + Display: "&8&l&k||&2&l荆棘宝石&8&l&k||" + Tips: + - "&2荆棘宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其荆棘等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjc1N2NmYzdjMTFiMWU2ZjEwMjhiODA0NjJkMWU0MzAyZGNhNWFiODU5MjIyNWVhOGM4Yjc2MzljNzBlNjFmOSJ9fX0= + SuccessTip: "&f你使用了&2&l荆棘宝石&f, 盔甲荆棘等级&2&l+1" + Rewards: + - Enchant{name=THORNS;limit=10} + +内置-真荆棘宝石: + Name: 真·荆棘宝石 + Require: + - ARMOR + Display: "&8&l&k||&2&l真·荆棘宝石&8&l&k||" + Tips: + - "&2&l真·荆棘宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其荆棘等级+1(上限30级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjc1N2NmYzdjMTFiMWU2ZjEwMjhiODA0NjJkMWU0MzAyZGNhNWFiODU5MjIyNWVhOGM4Yjc2MzljNzBlNjFmOSJ9fX0= + SuccessTip: "&f你使用了&2&l真·荆棘宝石&f, 盔甲荆棘等级&2&l+1" + Rewards: + - Enchant{name=THORNS;limit=30} + +内置-神荆棘宝石: + Name: 神·荆棘宝石 + Require: + - ARMOR + Success: 80 + Display: "&8&l&k||&2&l神·荆棘宝石&8&l&k||" + Tips: + - "&2&l神·荆棘宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其荆棘等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjc1N2NmYzdjMTFiMWU2ZjEwMjhiODA0NjJkMWU0MzAyZGNhNWFiODU5MjIyNWVhOGM4Yjc2MzljNzBlNjFmOSJ9fX0= + SuccessTip: "&f你使用了&2&l神·荆棘宝石&f, 盔甲荆棘等级&2&l+1" + Rewards: + - Enchant{name=THORNS} + +内置-激流宝石: + Name: 激流宝石 + Require: + - TRIDENT + Display: "&8&l&k||&9&l激流宝石&8&l&k||" + Tips: + - "&9激流宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的三叉戟" + - "&7即可为其激流等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTA5NjcxOTAzZDcwZDhhMWQ1ZDliY2Q1ODI0MWQ1ZGMxYTIzZjEzN2QyNTNjMjgyNTYxOTc2YTY4NTEyODMifX19 + SuccessTip: "&f你使用了&9&l激流宝石&f, 三叉戟激流等级&9&l+1" + Rewards: + - Enchant{name=RIPTIDE;limit=15} + +内置-真激流宝石: + Name: 真·激流宝石 + Require: + - TRIDENT + Display: "&8&l&k||&9&l真·激流宝石&8&l&k||" + Tips: + - "&9&l真·激流宝石 &7品质: &a&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的三叉戟" + - "&7即可为其激流等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTA5NjcxOTAzZDcwZDhhMWQ1ZDliY2Q1ODI0MWQ1ZGMxYTIzZjEzN2QyNTNjMjgyNTYxOTc2YTY4NTEyODMifX19 + SuccessTip: "&f你使用了&9&l真·激流宝石&f, 三叉戟激流等级&9&l+1" + Rewards: + - Enchant{name=RIPTIDE;limit=40} + +内置-保护宝石: + Name: 保护宝石 + Require: + - ARMOR + Display: "&8&l&k||&3&l保护宝石&8&l&k||" + Tips: + - "&3保护宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其保护等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzI0ZDIzZjBiYjNlNzUwYWU0ZmUxMGEyYTg0OTBlOGEwMjg4MDZiOGE5NTRjNDU3YmUzNzlkMDJiN2Q0NjUwMiJ9fX0= + SuccessTip: "&f你使用了&3&l保护宝石&f, 盔甲保护等级&3&l+1" + Rewards: + - Enchant{name=PROTECTION_ENVIRONMENTAL;limit=10} + +内置-真保护宝石: + Name: 真·保护宝石 + Require: + - ARMOR + Display: "&8&l&k||&3&l真·保护宝石&8&l&k||" + Tips: + - "&3&l真·保护宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的盔甲" + - "&7即可为其保护等级+1(上限20级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzI0ZDIzZjBiYjNlNzUwYWU0ZmUxMGEyYTg0OTBlOGEwMjg4MDZiOGE5NTRjNDU3YmUzNzlkMDJiN2Q0NjUwMiJ9fX0= + SuccessTip: "&f你使用了&3&l真·保护宝石&f, 盔甲保护等级&3&l+1" + Rewards: + - Enchant{name=PROTECTION_ENVIRONMENTAL;limit=20} + +内置-冰霜行者宝石: + Name: 冰霜行者宝石 + Require: + - BOOTS + Display: "&8&l&k||&9&l冰霜行者宝石&8&l&k||" + Tips: + - "&9&l冰霜行者宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其冰霜行者等级+1(上限7级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzY5ZTBhYmEzNWQ2ODUzZWU1NGM4Njc5OWY3NTRhZTE2NjlkZDM5YTBlMmYzZDY5NzMxZjMzMWFkZWRlIn19fQ== + SuccessTip: "&f你使用了&9&l冰霜行者宝石&f, 靴子冰霜行者等级&9&l+1" + Rewards: + - Enchant{name=FROST_WALKER;limit=7} +内置-真冰霜行者宝石: + Name: 真·冰霜行者宝石 + Require: + - BOOTS + Display: "&8&l&k||&9&l真·冰霜行者宝石&8&l&k||" + Tips: + - "&9&l真·冰霜行者宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其冰霜行者等级+1(上限14级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzY5ZTBhYmEzNWQ2ODUzZWU1NGM4Njc5OWY3NTRhZTE2NjlkZDM5YTBlMmYzZDY5NzMxZjMzMWFkZWRlIn19fQ== + SuccessTip: "&f你使用了&9&l真·冰霜行者宝石&f, 靴子冰霜行者等级&9&l+1" + Rewards: + - Enchant{name=FROST_WALKER;limit=14} + +内置-海之眷顾宝石: + Name: 海之眷顾宝石 + Require: + - FISHING_ROD + Display: "&8&l&k||&9&l海之眷顾宝石&8&l&k||" + Tips: + - "&9海之眷顾宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的钓鱼竿" + - "&7即可为其海之眷顾等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWY5ZjMwMTZjN2U1NzAzOThkNWIwODVhY2Y2YmMxYjJjMTA5NWVmZTBmNTFhN2I2MDVjODFmOTIwOWZiMzc2NyJ9fX0= + SuccessTip: "&f你使用了&9&l海之眷顾宝石&f, 钓竿海之眷顾等级&9&l+1" + Rewards: + - Enchant{name=LUCK;limit=10} + +内置-真海之眷顾宝石: + Name: 真·海之眷顾宝石 + Require: + - FISHING_ROD + Display: "&8&l&k||&9&l真·海之眷顾宝石&8&l&k||" + Tips: + - "&9&l真·海之眷顾宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的钓鱼竿" + - "&7即可为其海之眷顾等级+1(上限20级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWY5ZjMwMTZjN2U1NzAzOThkNWIwODVhY2Y2YmMxYjJjMTA5NWVmZTBmNTFhN2I2MDVjODFmOTIwOWZiMzc2NyJ9fX0= + SuccessTip: "&f你使用了&9&l真·海之眷顾宝石&f, 钓竿海之眷顾等级&9&l+1" + Rewards: + - Enchant{name=LUCK;limit=24} + +内置-效率宝石: + Name: 效率宝石 + Require: + - AXE + - SHOVEL + Display: "&8&l&k||&a&l效率宝石&8&l&k||" + Tips: + - "&a&l效率宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的镐/斧/铲" + - "&7即可为其效率等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjg1YmNmN2Y4MmQzNGRiODlhOTVhZGRmOGU1MzI1M2UyZDk1NTRjNmZkMmYyZTM5ZTI0MzYyZDI0M2EwY2NmNyJ9fX0= + SuccessTip: "&f你使用了&a&l效率宝石&f, 工具效率等级&a&l+1" + Rewards: + - Enchant{name=DIG_SPEED;limit=10} + +内置-真效率宝石: + Name: 真·效率宝石 + Require: + - AXE + - SHOVEL + Display: "&8&l&k||&a&l真·效率宝石&8&l&k||" + Tips: + - "&a&l真·效率宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的镐/斧/铲" + - "&7即可为其效率等级+1(上限33级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjg1YmNmN2Y4MmQzNGRiODlhOTVhZGRmOGU1MzI1M2UyZDk1NTRjNmZkMmYyZTM5ZTI0MzYyZDI0M2EwY2NmNyJ9fX0= + SuccessTip: "&f你使用了&a&l真·效率宝石&f, 工具效率等级&a&l+1" + Rewards: + - Enchant{name=DIG_SPEED;limit=33} + +内置-火焰附加宝石: + Name: 火焰附加宝石 + Require: + - SWORD + Display: "&8&l&k||&6&l火焰附加宝石&8&l&k||" + Tips: + - "&6火焰附加宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑" + - "&7即可为其火焰附加等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzBhNDRkNTFlY2M3OWY2OTRjZmQ2MDIyOGM4ODQyODg0OGNhNjE4ZTM2YTY1OWE0MTZlOTI0NmQ4NDFhZWM4In19fQ== + SuccessTip: "&f你使用了&6&l火焰附加宝石&f, 剑火焰附加等级&6&l+1" + Rewards: + - Enchant{name=FIRE_ASPECT;limit=10} + +内置-真火焰附加宝石: + Name: 真·火焰附加宝石 + Require: + - SWORD + Display: "&8&l&k||&6&l真·火焰附加宝石&8&l&k||" + Tips: + - "&6&l真·火焰附加宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑" + - "&7即可为其火焰附加等级+1(上限25级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzBhNDRkNTFlY2M3OWY2OTRjZmQ2MDIyOGM4ODQyODg0OGNhNjE4ZTM2YTY1OWE0MTZlOTI0NmQ4NDFhZWM4In19fQ== + SuccessTip: "&f你使用了&6&l真·火焰附加宝石&f, 剑火焰附加等级&6&l+1" + Rewards: + - Enchant{name=FIRE_ASPECT;limit=25} + +内置-神火焰附加宝石: + Name: 神·火焰附加宝石 + Require: + - SWORD + Success: 80 + Display: "&8&l&k||&6&l神·火焰附加宝石&8&l&k||" + Tips: + - "&6&l神·火焰附加宝石 &7品质: &b史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑" + - "&7即可为其火焰附加等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzBhNDRkNTFlY2M3OWY2OTRjZmQ2MDIyOGM4ODQyODg0OGNhNjE4ZTM2YTY1OWE0MTZlOTI0NmQ4NDFhZWM4In19fQ== + SuccessTip: "&f你使用了&6&l神·火焰附加宝石&f, 剑火焰附加等级&6&l+1" + Rewards: + - Enchant{name=FIRE_ASPECT} + +内置-穿刺宝石: + Name: 穿刺宝石 + Require: + - TRIDENT + Display: "&8&l&k||&3&l穿刺宝石&8&l&k||" + Tips: + - "&3穿刺宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的三叉戟" + - "&7即可为其穿刺等级+1(上限20级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWVjZDk3MjdlOWQ5ZjJjMDczOTkzNGVmMWU2M2QxZDg5YzkyMzY5NDIxY2FmYmFlYTczM2NjZTM1YzNiNjc2OCJ9fX0= + SuccessTip: "&f你使用了&3&l穿刺宝石&f, 三叉戟穿刺等级&3&l+1" + Rewards: + - Enchant{name=CHANNELING;limit=20} + +内置-忠诚宝石: + Name: 忠诚宝石 + Require: + - TRIDENT + Display: "&8&l&k||&3&l忠诚宝石&8&l&k||" + Tips: + - "&3忠诚宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的三叉戟" + - "&7即可为其忠诚等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjc2OGVkYzI4ODUzYzQyNDRkYmM2ZWViNjNiZDQ5ZWQ1NjhjYTIyYTg1MmEwYTU3OGIyZjJmOWZhYmU3MCJ9fX0= + SuccessTip: "&f你使用了&3&l忠诚宝石&f, 三叉戟忠诚等级&3&l+1" + Rewards: + - Enchant{name=LOYALTY;limit=15} + +内置-击退宝石: + Name: 击退宝石 + Require: + - SWORD + - _AXE + Display: "&8&l&k||&8&l击退宝石&8&l&k||" + Tips: + - "&8&l击退宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其击退等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDk5ZjI4MzMyYmNjMzQ5ZjQyMDIzYzI5ZTZlNjQxZjRiMTBhNmIxZTQ4NzE4Y2FlNTU3NDY2ZDUxZWI5MjIifX19 + SuccessTip: "&f你使用了&8&l击退宝石&f, 剑/斧击退等级&8&l+1" + Rewards: + - Enchant{name=KNOCKBACK;limit=15} + +内置-真击退宝石: + Name: 真·击退宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&8&l真·击退宝石&8&l&k||" + Tips: + - "&8&l真·击退宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑/斧" + - "&7即可为其击退等级+1(上限30级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDk5ZjI4MzMyYmNjMzQ5ZjQyMDIzYzI5ZTZlNjQxZjRiMTBhNmIxZTQ4NzE4Y2FlNTU3NDY2ZDUxZWI5MjIifX19 + SuccessTip: "&f你使用了&8&l真·击退宝石&f, 剑/斧击退等级&8&l+1" + Rewards: + - Enchant{name=KNOCKBACK;limit=30} + +内置-冲击宝石: + Name: 冲击宝石 + Require: + - BOW + Display: "&8&l&k||&8&l冲击宝石&8&l&k||" + Tips: + - "&8&l冲击宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的弓" + - "&7即可为其冲击等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzU0Nzk1MTA0MjJiMWM1ZGNjNzdmNzVmZGMzMzQ2ZWQ0ZDlkYmJjYzFlODg1YjRhMjk5MmEyNzM3MzM2NDZhOSJ9fX0= + SuccessTip: "&f你使用了&8&l冲击宝石&f, 弓冲击等级&8&l+1" + Rewards: + - Enchant{name=ARROW_KNOCKBACK;limit=15} + +内置-真冲击宝石: + Name: 真·冲击宝石 + Require: + - BOW + Display: "&8&l&k||&8&l真·冲击宝石&8&l&k||" + Tips: + - "&8&l真·冲击宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的弓" + - "&7即可为其冲击等级+1(上限30级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzU0Nzk1MTA0MjJiMWM1ZGNjNzdmNzVmZGMzMzQ2ZWQ0ZDlkYmJjYzFlODg1YjRhMjk5MmEyNzM3MzM2NDZhOSJ9fX0= + SuccessTip: "&f你使用了&8&l真·冲击宝石&f, 弓冲击等级&8&l+1" + Rewards: + - Enchant{name=ARROW_KNOCKBACK;limit=30} + +内置-水下呼吸宝石: + Name: 水下呼吸宝石 + Require: + - HELMET + Display: "&8&l&k||&b&l水下呼吸宝石&8&l&k||" + Tips: + - "&b&l水下呼吸宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的头盔" + - "&7即可为其水下呼吸等级+1(上限6级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2QwZjFkMGE3NDU5OTFlNjYxNGZmZDdkYzlmODBlMzc0NTU5NmU4MTg5Nzc5NmYyODAyNjJmMTJmMTVlYjRhZCJ9fX0= + SuccessTip: "&f你使用了&b&l水下呼吸宝石&f, 头盔水下呼吸等级&b&l+1" + Rewards: + - Enchant{name=OXYGEN;limit=6} + +内置-真水下呼吸宝石: + Name: 真·水下呼吸宝石 + Require: + - HELMET + Display: "&8&l&k||&b&l真·水下呼吸宝石&8&l&k||" + Tips: + - "&b&l真·水下呼吸宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的头盔" + - "&7即可为其水下呼吸等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2QwZjFkMGE3NDU5OTFlNjYxNGZmZDdkYzlmODBlMzc0NTU5NmU4MTg5Nzc5NmYyODAyNjJmMTJmMTVlYjRhZCJ9fX0= + SuccessTip: "&f你使用了&b&l真·水下呼吸宝石&f, 头盔水下呼吸等级&b&l+1" + Rewards: + - Enchant{name=OXYGEN;limit=15} + +内置-亡灵杀手宝石: + Name: 亡灵杀手宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&7&l亡灵杀手宝石&8&l&k||" + Tips: + - "&7亡灵杀手宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其亡灵杀手等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjRmODVkNmFlNzdlMTJlNTFlZTQzYWRlZjQzYzNjMzI0ZTRlMzc4NDg5OWI5MDM3NjE4MmZlN2Y0N2E1ZSJ9fX0= + SuccessTip: "&f你使用了&7&l亡灵杀手宝石&f, 武器亡灵杀手等级&7&l+1" + Rewards: + - Enchant{name=DAMAGE_UNDEAD;limit=15} + +内置-真亡灵杀手宝石: + Name: 真·亡灵杀手宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&7&l真·亡灵杀手宝石&8&l&k||" + Tips: + - "&7&l真·亡灵杀手宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其亡灵杀手等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjRmODVkNmFlNzdlMTJlNTFlZTQzYWRlZjQzYzNjMzI0ZTRlMzc4NDg5OWI5MDM3NjE4MmZlN2Y0N2E1ZSJ9fX0= + SuccessTip: "&f你使用了&7&l真·亡灵杀手宝石&f, 武器亡灵杀手等级&7&l+1" + Rewards: + - Enchant{name=DAMAGE_UNDEAD;limit=40} + +内置-神亡灵杀手宝石: + Name: 神·亡灵杀手宝石 + Require: + - _AXE + - SWORD + Success: 80 + Display: "&8&l&k||&7&l神·亡灵杀手宝石&8&l&k||" + Tips: + - "&7&l神·亡灵杀手宝石 &7品质: &b史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其亡灵杀手等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjRmODVkNmFlNzdlMTJlNTFlZTQzYWRlZjQzYzNjMzI0ZTRlMzc4NDg5OWI5MDM3NjE4MmZlN2Y0N2E1ZSJ9fX0= + SuccessTip: "&f你使用了&7&l神·亡灵杀手宝石&f, 武器亡灵杀手等级&7&l+1" + Rewards: + - Enchant{name=DAMAGE_UNDEAD} + +内置-节肢杀手宝石: + Name: 节肢杀手宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&8&l节肢杀手宝石&8&l&k||" + Tips: + - "&8节肢杀手宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其节肢杀手等级+1(上限15级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODhkYWY1NGFjOGMwNjFhODMzMjY5N2ZkMDM4YjlmM2FmOGVjMzhkMTY4NDllNTI3MTI1ZDI5NzNkZTYyZTliIn19fQ== + SuccessTip: "&f你使用了&8&l节肢杀手宝石&f, 武器节肢杀手等级&8&l+1" + Rewards: + - Enchant{name=DAMAGE_ARTHROPODS;limit=15} + +内置-真节肢杀手宝石: + Name: 真·节肢杀手宝石 + Require: + - _AXE + - SWORD + Display: "&8&l&k||&8&l真·节肢杀手宝石&8&l&k||" + Tips: + - "&8&l真·节肢杀手宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其节肢杀手等级+1(上限40级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODhkYWY1NGFjOGMwNjFhODMzMjY5N2ZkMDM4YjlmM2FmOGVjMzhkMTY4NDllNTI3MTI1ZDI5NzNkZTYyZTliIn19fQ== + SuccessTip: "&f你使用了&8&l真·节肢杀手宝石&f, 武器节肢杀手等级&8&l+1" + Rewards: + - Enchant{name=DAMAGE_ARTHROPODS;limit=40} + +内置-神节肢杀手宝石: + Name: 神·节肢杀手宝石 + Require: + - _AXE + - SWORD + Success: 80 + Display: "&8&l&k||&8&l神·节肢杀手宝石&8&l&k||" + Tips: + - "&8&l神·节肢杀手宝石 &7品质: &b史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的武器" + - "&7即可为其节肢杀手等级+1(无上限)" + - "&7成功率: &a80%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODhkYWY1NGFjOGMwNjFhODMzMjY5N2ZkMDM4YjlmM2FmOGVjMzhkMTY4NDllNTI3MTI1ZDI5NzNkZTYyZTliIn19fQ== + SuccessTip: "&f你使用了&8&l神·节肢杀手宝石&f, 武器节肢杀手等级&8&l+1" + Rewards: + - Enchant{name=DAMAGE_ARTHROPODS} + +内置-横扫之刃宝石: + Name: 横扫之刃宝石 + Require: + - SWORD + Display: "&8&l&k||&c&l横扫之刃宝石&8&l&k||" + Tips: + - "&c横扫之刃宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石" + - "&7再点击您的剑" + - "&7即可为其横扫之刃等级+1(上限10级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTVkNzZkOTBiMzc4MDgzZDE0Nzc1NjgwNTA1ZGRiMWU2YzJjNmRjZjRkZGU3ZjliMWY1ODgwOWJlYzZjNjVjOCJ9fX0= + SuccessTip: "&f你使用了&c&l横扫之刃宝石&f, 武器横扫之刃等级&c&l+1" + Rewards: + - Enchant{name=SWEEPING_EDGE;limit=10} + +内置-灵魂疾行宝石: + Name: 灵魂疾行宝石 + Require: + - BOOTS + Display: "&8&l&k||&3&l灵魂疾行宝石&8&l&k||" + Tips: + - "&3&l灵魂疾行宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其灵魂疾行等级+1(上限7级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7ImlkIjoiOTM4NDM1M2E0YWUwNGZlNDkzOTcxYjU4N2RmNzdmMTIiLCJ0eXBlIjoiU0tJTiIsInVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTU4MGU1Y2Q5NzBjMWRlZDk3M2QwYzEzOWJlNDE4YjA3NTVlMDcxMzM0NWQ3MTZlZDU5NDI0NGE1M2U2ZTEzZCIsInByb2ZpbGVJZCI6IjgwMThhYjAwYjJhZTQ0Y2FhYzliZjYwZWY5MGY0NWU1IiwidGV4dHVyZUlkIjoiOTU4MGU1Y2Q5NzBjMWRlZDk3M2QwYzEzOWJlNDE4YjA3NTVlMDcxMzM0NWQ3MTZlZDU5NDI0NGE1M2U2ZTEzZCJ9fSwiY2FwZSI6bnVsbCwic2tpbiI6eyJpZCI6IjkzODQzNTNhNGFlMDRmZTQ5Mzk3MWI1ODdkZjc3ZjEyIiwidHlwZSI6IlNLSU4iLCJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzk1ODBlNWNkOTcwYzFkZWQ5NzNkMGMxMzliZTQxOGIwNzU1ZTA3MTMzNDVkNzE2ZWQ1OTQyNDRhNTNlNmUxM2QiLCJwcm9maWxlSWQiOiI4MDE4YWIwMGIyYWU0NGNhYWM5YmY2MGVmOTBmNDVlNSIsInRleHR1cmVJZCI6Ijk1ODBlNWNkOTcwYzFkZWQ5NzNkMGMxMzliZTQxOGIwNzU1ZTA3MTMzNDVkNzE2ZWQ1OTQyNDRhNTNlNmUxM2QifX0= + SuccessTip: "&f你使用了&3&l灵魂疾行宝石&f, 靴子灵魂疾行等级&3&l+1" + Rewards: + - Enchant{name=SOUL_SPEED;limit=10} + +内置-真灵魂疾行宝石: + Name: 真·灵魂疾行宝石 + Require: + - BOOTS + Display: "&8&l&k||&3&l真·灵魂疾行宝石&8&l&k||" + Tips: + - "&3&l真·灵魂疾行宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石" + - "&7再点击您的靴子" + - "&7即可为其灵魂疾行等级+1(上限30级)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7ImlkIjoiOTM4NDM1M2E0YWUwNGZlNDkzOTcxYjU4N2RmNzdmMTIiLCJ0eXBlIjoiU0tJTiIsInVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTU4MGU1Y2Q5NzBjMWRlZDk3M2QwYzEzOWJlNDE4YjA3NTVlMDcxMzM0NWQ3MTZlZDU5NDI0NGE1M2U2ZTEzZCIsInByb2ZpbGVJZCI6IjgwMThhYjAwYjJhZTQ0Y2FhYzliZjYwZWY5MGY0NWU1IiwidGV4dHVyZUlkIjoiOTU4MGU1Y2Q5NzBjMWRlZDk3M2QwYzEzOWJlNDE4YjA3NTVlMDcxMzM0NWQ3MTZlZDU5NDI0NGE1M2U2ZTEzZCJ9fSwiY2FwZSI6bnVsbCwic2tpbiI6eyJpZCI6IjkzODQzNTNhNGFlMDRmZTQ5Mzk3MWI1ODdkZjc3ZjEyIiwidHlwZSI6IlNLSU4iLCJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzk1ODBlNWNkOTcwYzFkZWQ5NzNkMGMxMzliZTQxOGIwNzU1ZTA3MTMzNDVkNzE2ZWQ1OTQyNDRhNTNlNmUxM2QiLCJwcm9maWxlSWQiOiI4MDE4YWIwMGIyYWU0NGNhYWM5YmY2MGVmOTBmNDVlNSIsInRleHR1cmVJZCI6Ijk1ODBlNWNkOTcwYzFkZWQ5NzNkMGMxMzliZTQxOGIwNzU1ZTA3MTMzNDVkNzE2ZWQ1OTQyNDRhNTNlNmUxM2QifX0= + SuccessTip: "&f你使用了&3&l真·灵魂疾行宝石&f, 靴子灵魂疾行等级&3&l+1" + Rewards: + - Enchant{name=SOUL_SPEED;limit=30} diff --git a/src/main/resources/gems/ExampleGem.yml b/src/main/resources/gems/ExampleGem.yml new file mode 100644 index 0000000..9116b9f --- /dev/null +++ b/src/main/resources/gems/ExampleGem.yml @@ -0,0 +1,12 @@ +示例生命宝石: + Name: 示例生命宝石 + Type: ITEM_GEM + Material: EMERALD + Display: "&a示例生命宝石" + Tips: + - "&7用于验证 CMagicGem 基础功能" + Require: + - ARMOR + Success: 100 + Rewards: + - "Attribute{name=health;operation=0;slot=auto;var=v+1;inv=v-1;limit=10}" diff --git a/src/main/resources/gems/FunctionGem.yml b/src/main/resources/gems/FunctionGem.yml new file mode 100644 index 0000000..7843955 --- /dev/null +++ b/src/main/resources/gems/FunctionGem.yml @@ -0,0 +1,169 @@ +# +# 以下宝石演示了内置功能 +Version: 1.05 + +内置-无限宝石: + Name: 无限宝石 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + - FLINT_AND_STEEL + Display: "&8&l&k||&9&l无限宝石&8&l&k||" + Tips: + - "&9&l无限宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的装备" + - "&7即可为您的装备添加无限耐久" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTExNWRjODhlMzIxNGMzODI0M2Q3ODJkNjNlZGIwYTZlMDYyOTFlYjZkYThlNjAwYzdlMmVhMzZlN2Y2MWIzMSJ9fX0= + SuccessTip: "&f你使用了&9&l无限宝石&f, 装备已被设置为&9&l无法破坏" + Rewards: + - Unbreakable + +内置-原版耐久修复石: + Name: 修复宝石 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + - FLINT_AND_STEEL + Display: "&8&l&k||&a&l修复宝石&8&l&k||" + Tips: + - "&c&l修复宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石," + - "&7再点击您的装备" + - "&7即可使装备耐久恢复1000点" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTg5MDFmNzE0MzRkNTM5MjA3NDc2OTRmNjgyZjVlNTNiOGY3NDQ4M2YyNjljMzg0YzY5MzZiN2Q4NjU4MiJ9fX0= + SuccessTip: "&f你使用了&a&l修复宝石&f, 装备耐久&a&l+1000" + Rewards: + - Durability{amount=1000} + +内置-失败宝石: + Name: 败者宝石 + Success: 1 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + - FLINT_AND_STEEL + Display: "&8&l&k||&c&l败者宝石&8&l&k||" + Tips: + - "&c&l败者宝石 &7品质: &a&l励志" + - "&7在工作台内点击此宝石," + - "&7再点击您的装备" + - "&7即可以99%的概率感受强化失败的滋味" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTc3NWQzZWM4MmJhNjE5NjQ5YTIzYjg1MDg3NTI3NWY1NTliNjRlNTNjYjgxNmM0NDhiZWVjNzkyYjQ5YzAifX19 + SuccessTip: "&a你竟然成功了&f, 肯定是服务器出了bug(狗头)" + Rewards: + - Name{name=&8&l&k||&a&l天命之子&8&l&k||} + +内置-精准时运切换宝石: + Name: 精准时运切换宝石 + Require: + - AXE + - SHOVEL + Display: "&8&l&k||&a&l精准/时运切换宝石&8&l&k||" + Tips: + - "&a&l精准时运切换宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的镐子/斧头/铲子" + - "&7即可为其加上精准/时运切换功能" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzEyZDJmOTMxMDQ0NDFjYTU2NmY3YjcxOGMzZGQwMzgyY2IzOGY5Njc4YjVjYjE4MWVhMjQ4N2EyZjdmODI2YiJ9fX0= + SuccessTip: "&f你使用了&a&l精准/时运切换宝石&f, 工具添加&a&l精准/时运切换功能" + Rewards: + - LoreAdd{lore=&2Shift+右键切换模式(精准/时运);limit=1} + +内置-忠诚激流切换宝石: + Name: 忠诚激流切换宝石 + Require: + - TRIDENT + Display: "&8&l&k||&a&l忠诚/激流切换宝石&8&l&k||" + Tips: + - "&a&l忠诚/激流切换宝石 &7品质: &b&l史诗" + - "&7在工作台内点击此宝石" + - "&7再点击您的三叉戟" + - "&7即可为其加上忠诚/激流切换功能" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzEyZDJmOTMxMDQ0NDFjYTU2NmY3YjcxOGMzZGQwMzgyY2IzOGY5Njc4YjVjYjE4MWVhMjQ4N2EyZjdmODI2YiJ9fX0= + SuccessTip: "&f你使用了&a&l忠诚/激流切换宝石&f, 三叉戟添加&a&l忠诚/激流切换功能" + Rewards: + - LoreAdd{lore=&2Shift+左键切换模式(忠诚/激流);limit=1} + +内置-巨无霸汉堡: + Name: 巨无霸汉堡 + Require: + - 啥也不能打 + Display: "&8&l&k||&6&l巨无霸汉堡&8&l&k||" + Tips: + - "&6&l巨无霸汉堡 &7品质: &a&l传说" + - "&7拿在手中或戴在头上" + - "&7即可获得饱和效果" + - "&6&l[&c&lBUFF&6&l] &b&l饱和" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQxMWNiNTRhYWExZTNkYjU1NWM3ODVmNmI3NWJlMWJmOGU2OGIyOGU1Y2ZjNTljOWE4NzY4OTRmNjFjZGMxNyJ9fX0= + Rewards: + - Empty + +内置-附魔隐藏粉尘: + Name: 附魔隐藏粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + Display: "&8&l&k||&7&l附魔隐藏粉尘&8&l&k||" + Tips: + - "&7在镶嵌台上使用/取下此粉尘" + - "&7即可隐藏/显示装备的附魔" + SuccessTip: "&f你使用了&7&l附魔隐藏粉尘&f, 装备附魔已隐藏" + RemoveTip: "&f你取下了&7&l附魔隐藏粉尘&f, 装备附魔已显示" + Material: GUNPOWDER + Glow: true + Embed: 1 + Rewards: + - ItemFlag{HIDE_ENCHANTS} + +内置-属性隐藏粉尘: + Name: 属性隐藏粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + Display: "&8&l&k||&9&l属性隐藏粉尘&8&l&k||" + Tips: + - "&7在镶嵌台上使用/取下此粉尘" + - "&7即可隐藏/显示装备的蓝字/绿字属性" + SuccessTip: "&f你使用了&9&l属性隐藏粉尘&f, 装备属性已隐藏" + RemoveTip: "&f你取下了&9&l属性隐藏粉尘&f, 装备属性已显示" + Material: SUGAR + Glow: true + Embed: 1 + Rewards: + - ItemFlag{HIDE_ATTRIBUTES} + +内置-技能隐藏粉尘: + Name: 技能隐藏粉尘 + Require: + - WEAPON + - ARMOR + - TOOL + - SHIELD + - FISHING_ROD + Display: "&8&l&k||&6&l技能隐藏粉尘&8&l&k||" + Tips: + - "&7在镶嵌台上使用/取下此技能" + - "&7即可隐藏/显示装备的药水或技能" + SuccessTip: "&f你使用了&6&l技能隐藏粉尘&f, 装备技能已隐藏" + RemoveTip: "&f你取下了&6&l技能隐藏粉尘&f, 装备技能已显示" + Material: GLOWSTONE_DUST + Glow: true + Embed: 1 + Rewards: + - SkillToNBT diff --git a/src/main/resources/gems/LoreGem.yml b/src/main/resources/gems/LoreGem.yml new file mode 100644 index 0000000..5c07b23 --- /dev/null +++ b/src/main/resources/gems/LoreGem.yml @@ -0,0 +1,2836 @@ +Version: 1.05 +# 以下宝石根据一些手游和AttributePlus的部分属性而设计 + +内置-红色符文强攻1: + Name: 红色符文强攻1 + Require: + - WEAPON + Display: "&8&l&k||&c&l强攻 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c强攻 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l强攻 I&f, 武器物理伤害&c&l+2.0" + RemoveTip: "&7你拆下了&c&l强攻 I" + Rewards: + - "LoreVar{lore=&7■ 物理伤害: &c&l;format=%.1f;var=v+2;inv=v-2;mode=last}" + +内置-红色符文强攻2: + Name: 红色符文强攻2 + Require: + - WEAPON + Display: "&8&l&k||&c&l强攻 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c强攻 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+3.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l强攻 II&f, 武器物理伤害&c&l+3.4" + RemoveTip: "&7你拆下了&c&l强攻 II" + Rewards: + - "LoreVar{lore=&7■ 物理伤害: &c&l;format=%.1f;var=v+3.4;inv=v-3.4;mode=last}" + +内置-红色符文强攻3: + Name: 红色符文强攻3 + Require: + - WEAPON + Display: "&8&l&k||&c&l强攻 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c强攻 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l强攻 III&f, 武器物理伤害&c&l+4.5" + RemoveTip: "&7你拆下了&c&l强攻 III" + Rewards: + - "LoreVar{lore=&7■ 物理伤害: &c&l;format=%.1f;var=v+4.5;inv=v-4.5;mode=last}" + +内置-红色符文强攻4: + Name: 红色符文强攻4 + Require: + - WEAPON + Display: "&8&l&k||&c&l强攻 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c强攻 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+5.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l强攻 IV&f, 武器物理伤害&c&l+5.4" + RemoveTip: "&7你拆下了&c&l强攻 IV" + Rewards: + - "LoreVar{lore=&7■ 物理伤害: &c&l;format=%.1f;var=v+5.4;inv=v-5.4;mode=last}" + +内置-红色符文强攻5: + Name: 红色符文强攻5 + Require: + - WEAPON + Display: "&8&l&k||&c&l强攻 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c强攻 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+6.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l强攻 V&f, 武器物理伤害&c&l+6.0" + RemoveTip: "&7你拆下了&c&l强攻 V" + Rewards: + - "LoreVar{lore=&7■ 物理伤害: &c&l;format=%.1f;var=v+6;inv=v-6;mode=last}" + +内置-红色符文致命1: + Name: 红色符文致命1 + Require: + - WEAPON + Display: "&8&l&k||&c&l致命 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c致命 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l致命 I&f, 武器真实伤害&c&l+2.0" + RemoveTip: "&7你拆下了&c&l致命 I" + Rewards: + - "LoreVar{lore=&7■ 真实伤害: &c&l;format=%.1f;var=v+2;inv=v-2;mode=last}" + +内置-红色符文致命2: + Name: 红色符文致命2 + Require: + - WEAPON + Display: "&8&l&k||&c&l致命 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c致命 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+2.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l致命 II&f, 武器真实伤害&c&l+2.8" + RemoveTip: "&7你拆下了&c&l致命 II" + Rewards: + - "LoreVar{lore=&7■ 真实伤害: &c&l;format=%.1f;var=v+2.8;inv=v-2.8;mode=last}" + +内置-红色符文致命3: + Name: 红色符文致命3 + Require: + - WEAPON + Display: "&8&l&k||&c&l致命 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c致命 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+3.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l致命 III&f, 武器真实伤害&c&l+3.4" + RemoveTip: "&7你拆下了&c&l致命 III" + Rewards: + - "LoreVar{lore=&7■ 真实伤害: &c&l;format=%.1f;var=v+3.4;inv=v-3.4;mode=last}" + +内置-红色符文致命4: + Name: 红色符文致命4 + Require: + - WEAPON + Display: "&8&l&k||&c&l致命 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c致命 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+3.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l致命 IV&f, 武器真实伤害&c&l+3.8" + RemoveTip: "&7你拆下了&c&l致命 IV" + Rewards: + - "LoreVar{lore=&7■ 真实伤害: &c&l;format=%.1f;var=v+3.8;inv=v-3.8;mode=last}" + +内置-红色符文致命5: + Name: 红色符文致命5 + Require: + - WEAPON + Display: "&8&l&k||&c&l致命 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c致命 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+4.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l致命 V&f, 武器真实伤害&c&l+4.0" + RemoveTip: "&7你拆下了&c&l致命 V" + Rewards: + - "LoreVar{lore=&7■ 真实伤害: &c&l;format=%.1f;var=v+4;inv=v-4;mode=last}" + +内置-红色符文灾祸1: + Name: 红色符文灾祸1 + Require: + - WEAPON + Display: "&8&l&k||&c&l灾祸 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c灾祸 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l灾祸 I&f, 武器暴击几率&c&l+1.0&f%" + RemoveTip: "&7你拆下了&c&l灾祸 I" + Rewards: + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+1;inv=v-1;mode=last}" + +内置-红色符文灾祸2: + Name: 红色符文灾祸2 + Require: + - WEAPON + Display: "&8&l&k||&c&l灾祸 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c灾祸 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l灾祸 II&f, 武器暴击几率&c&l+1.7&f%" + RemoveTip: "&7你拆下了&c&l灾祸 II" + Rewards: + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+1.7;inv=v-1.7;mode=last}" + +内置-红色符文灾祸3: + Name: 红色符文灾祸3 + Require: + - WEAPON + Display: "&8&l&k||&c&l灾祸 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c灾祸 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+2.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l灾祸 III&f, 武器暴击几率&c&l+2.2&f%" + RemoveTip: "&7你拆下了&c&l灾祸 III" + Rewards: + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+2.2;inv=v-2.2;mode=last}" + +内置-红色符文灾祸4: + Name: 红色符文灾祸4 + Require: + - WEAPON + Display: "&8&l&k||&c&l灾祸 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c灾祸 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l灾祸 IV&f, 武器暴击几率&c&l+2.7&f%" + RemoveTip: "&7你拆下了&c&l灾祸 IV" + Rewards: + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+2.7;inv=v-2.7;mode=last}" + +内置-红色符文灾祸5: + Name: 红色符文灾祸5 + Require: + - WEAPON + Display: "&8&l&k||&c&l灾祸 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c灾祸 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+3.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l灾祸 V&f, 武器暴击几率&c&l+3.0&f%" + RemoveTip: "&7你拆下了&c&l灾祸 V" + Rewards: + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+3;inv=v-3;mode=last}" + +内置-红色符文流光1: + Name: 红色符文流光1 + Require: + - BOW + Display: "&8&l&k||&c&l流光 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c流光 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l流光 I&f, 弓的箭矢速度&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l流光 I" + Rewards: + - "LoreVar{lore=&7■ 箭矢速度: &c&l;format=%.1f&f%%;var=v+2;inv=v-2;mode=last}" + +内置-红色符文流光2: + Name: 红色符文流光2 + Require: + - BOW + Display: "&8&l&k||&c&l流光 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c流光 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+3.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l流光 II&f, 弓的箭矢速度&c&l+3.4&f%" + RemoveTip: "&7你拆下了&c&l流光 II" + Rewards: + - "LoreVar{lore=&7■ 箭矢速度: &c&l;format=%.1f&f%%;var=v+3.4;inv=v-3.4;mode=last}" + +内置-红色符文流光3: + Name: 红色符文流光3 + Require: + - BOW + Display: "&8&l&k||&c&l流光 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c流光 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l流光 III&f, 弓的箭矢速度&c&l+4.5&f%" + RemoveTip: "&7你拆下了&c&l流光 III" + Rewards: + - "LoreVar{lore=&7■ 箭矢速度: &c&l;format=%.1f&f%%;var=v+4.5;inv=v-4.5;mode=last}" + +内置-红色符文流光4: + Name: 红色符文流光4 + Require: + - BOW + Display: "&8&l&k||&c&l流光 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c流光 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l流光 IV&f, 弓的箭矢速度&c&l+5.4&f%" + RemoveTip: "&7你拆下了&c&l流光 IV" + Rewards: + - "LoreVar{lore=&7■ 箭矢速度: &c&l;format=%.1f&f%%;var=v+5.4;inv=v-5.4;mode=last}" + +内置-红色符文流光5: + Name: 红色符文流光5 + Require: + - BOW + Display: "&8&l&k||&c&l流光 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c流光 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l流光 V&f, 弓的箭矢速度&c&l+6.0&f%" + RemoveTip: "&7你拆下了&c&l流光 V" + Rewards: + - "LoreVar{lore=&7■ 箭矢速度: &c&l;format=%.1f&f%%;var=v+6;inv=v-6;mode=last}" + +内置-红色符文狂暴1: + Name: 红色符文狂暴1 + Require: + - WEAPON + Display: "&8&l&k||&c&l狂暴 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c狂暴 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+2.0&f%" + - "&7 - 暴击几率 &c+0.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l狂暴 I&f, 武器暴击倍率&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l狂暴 I" + Rewards: + - "LoreVar{lore=&7■ 暴击倍率: &c&l;format=%.1f&f%%;var=v+2;inv=v-2;mode=last}" + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+0.2;inv=v-0.2;mode=last}" + +内置-红色符文狂暴2: + Name: 红色符文狂暴2 + Require: + - WEAPON + Display: "&8&l&k||&c&l狂暴 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c狂暴 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+3.5&f%" + - "&7 - 暴击几率 &c+0.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l狂暴 II&f, 武器暴击倍率&c&l+3.5&f%" + RemoveTip: "&7你拆下了&c&l狂暴 II" + Rewards: + - "LoreVar{lore=&7■ 暴击倍率: &c&l;format=%.1f&f%%;var=v+3.5;inv=v-3.5;mode=last}" + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+0.4;inv=v-0.4;mode=last}" + +内置-红色符文狂暴3: + Name: 红色符文狂暴3 + Require: + - WEAPON + Display: "&8&l&k||&c&l狂暴 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c狂暴 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+4.2&f%" + - "&7 - 暴击几率 &c+0.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l狂暴 III&f, 武器暴击倍率&c&l+4.2&f%" + RemoveTip: "&7你拆下了&c&l狂暴 III" + Rewards: + - "LoreVar{lore=&7■ 暴击倍率: &c&l;format=%.1f&f%%;var=v+4.2;inv=v-4.2;mode=last}" + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+0.6;inv=v-0.6;mode=last}" + +内置-红色符文狂暴4: + Name: 红色符文狂暴4 + Require: + - WEAPON + Display: "&8&l&k||&c&l狂暴 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c狂暴 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+4.7&f%" + - "&7 - 暴击几率 &c+0.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l狂暴 IV&f, 武器暴击倍率&c&l+4.7&f%" + RemoveTip: "&7你拆下了&c&l狂暴 IV" + Rewards: + - "LoreVar{lore=&7■ 暴击倍率: &c&l;format=%.1f&f%%;var=v+4.7;inv=v-4.7;mode=last}" + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+0.7;inv=v-0.7;mode=last}" + +内置-红色符文狂暴5: + Name: 红色符文狂暴5 + Require: + - WEAPON + Display: "&8&l&k||&c&l狂暴 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c狂暴 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+5.0&f%" + - "&7 - 暴击几率 &c+0.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l狂暴 IV&f, 武器暴击倍率&c&l+5.0&f%" + RemoveTip: "&7你拆下了&c&l狂暴 V" + Rewards: + - "LoreVar{lore=&7■ 暴击倍率: &c&l;format=%.1f&f%%;var=v+5;inv=v-5;mode=last}" + - "LoreVar{lore=&7■ 暴击几率: &c&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + +内置-红色符文烈焰1: + Name: 红色符文烈焰1 + Require: + - WEAPON + Display: "&8&l&k||&c&l烈焰 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c烈焰 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+0.8&f%" + - "&7 - 燃烧伤害 &c+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l烈焰 I&f, 武器燃烧几率&c&l+0.8&f%" + RemoveTip: "&7你拆下了&c&l烈焰 I" + Rewards: + - "LoreVar{lore=&7■ 燃烧几率: &c&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + - "LoreVar{lore=&7■ 燃烧伤害: &c&l;format=%.1f;var=v+0.5;inv=v-0.5;mode=last}" + +内置-红色符文烈焰2: + Name: 红色符文烈焰2 + Require: + - WEAPON + Display: "&8&l&k||&c&l烈焰 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c烈焰 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+2.0&f%" + - "&7 - 燃烧伤害 &c+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l烈焰 II&f, 武器燃烧几率&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l烈焰 II" + Rewards: + - "LoreVar{lore=&7■ 燃烧几率: &c&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + - "LoreVar{lore=&7■ 燃烧伤害: &c&l;format=%.1f;var=v+1.5;inv=v-1.5;mode=last}" + +内置-红色符文烈焰3: + Name: 红色符文烈焰3 + Require: + - WEAPON + Display: "&8&l&k||&c&l烈焰 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c烈焰 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+3.0&f%" + - "&7 - 燃烧伤害 &c+2.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l烈焰 III&f, 武器燃烧几率&c&l+3.0&f%" + RemoveTip: "&7你拆下了&c&l烈焰 III" + Rewards: + - "LoreVar{lore=&7■ 燃烧几率: &c&l;format=%.1f&f%%;var=v+3.0;inv=v-3.0;mode=last}" + - "LoreVar{lore=&7■ 燃烧伤害: &c&l;format=%.1f;var=v+2.6;inv=v-2.6;mode=last}" + +内置-红色符文烈焰4: + Name: 红色符文烈焰4 + Require: + - WEAPON + Display: "&8&l&k||&c&l烈焰 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c烈焰 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+3.6&f%" + - "&7 - 燃烧伤害 &c+3.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l烈焰 IV&f, 武器燃烧几率&c&l+3.6&f%" + RemoveTip: "&7你拆下了&c&l烈焰 IV" + Rewards: + - "LoreVar{lore=&7■ 燃烧几率: &c&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + - "LoreVar{lore=&7■ 燃烧伤害: &c&l;format=%.1f;var=v+3.6;inv=v-3.6;mode=last}" + +内置-红色符文烈焰5: + Name: 红色符文烈焰5 + Require: + - WEAPON + Display: "&8&l&k||&c&l烈焰 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c烈焰 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+4.0&f%" + - "&7 - 燃烧伤害 &c+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l烈焰 V&f, 武器燃烧几率&c&l+4.0&f%" + RemoveTip: "&7你拆下了&c&l烈焰 V" + Rewards: + - "LoreVar{lore=&7■ 燃烧几率: &c&l;format=%.1f&f%%;var=v+4.0;inv=v-4.0;mode=last}" + - "LoreVar{lore=&7■ 燃烧伤害: &c&l;format=%.1f;var=v+4.5;inv=v-4.5;mode=last}" + +内置-红色符文坚守1: + Name: 红色符文坚守1 + Require: + - ARMOR + Display: "&8&l&k||&c&l坚守 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c坚守 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+0.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l坚守 I&f, 盔甲物理防御&c&l+0.8" + RemoveTip: "&7你拆下了&c&l坚守 I" + Rewards: + - "LoreVar{lore=&7■ 物理防御: &c&l;format=%.1f;var=v+0.8;inv=v-0.8;mode=last}" + +内置-红色符文坚守2: + Name: 红色符文坚守2 + Require: + - ARMOR + Display: "&8&l&k||&c&l坚守 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c坚守 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+1.3" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l坚守 II&f, 盔甲物理防御&c&l+1.3" + RemoveTip: "&7你拆下了&c&l坚守 II" + Rewards: + - "LoreVar{lore=&7■ 物理防御: &c&l;format=%.1f;var=v+1.3;inv=v-1.3;mode=last}" + +内置-红色符文坚守3: + Name: 红色符文坚守3 + Require: + - ARMOR + Display: "&8&l&k||&c&l坚守 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c坚守 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l坚守 III&f, 盔甲物理防御&c&l+1.8" + RemoveTip: "&7你拆下了&c&l坚守 III" + Rewards: + - "LoreVar{lore=&7■ 物理防御: &c&l;format=%.1f;var=v+1.8;inv=v-1.8;mode=last}" + +内置-红色符文坚守4: + Name: 红色符文坚守4 + Require: + - ARMOR + Display: "&8&l&k||&c&l坚守 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c坚守 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+2.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l坚守 IV&f, 盔甲物理防御&c&l+2.2" + RemoveTip: "&7你拆下了&c&l坚守 IV" + Rewards: + - "LoreVar{lore=&7■ 物理防御: &c&l;format=%.1f;var=v+2.2;inv=v-2.2;mode=last}" + +内置-红色符文坚守5: + Name: 红色符文坚守5 + Require: + - ARMOR + Display: "&8&l&k||&c&l坚守 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c坚守 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+2.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l坚守 V&f, 盔甲物理防御&c&l+2.5" + RemoveTip: "&7你拆下了&c&l坚守 V" + Rewards: + - "LoreVar{lore=&7■ 物理防御: &c&l;format=%.1f;var=v+2.5;inv=v-2.5;mode=last}" + +内置-红色符文重盾1: + Name: 红色符文重盾1 + Require: + - ARMOR + Display: "&8&l&k||&c&l重盾 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c重盾 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l重盾 I&f, 盔甲盔甲值&c&l+0.5&f%" + RemoveTip: "&7你拆下了&c&l重盾 I" + Rewards: + - "LoreVar{lore=&7■ 盔甲值: &c&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-红色符文重盾2: + Name: 红色符文重盾2 + Require: + - ARMOR + Display: "&8&l&k||&c&l重盾 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c重盾 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l重盾 II&f, 盔甲盔甲值&c&l+0.9&f%" + RemoveTip: "&7你拆下了&c&l重盾 II" + Rewards: + - "LoreVar{lore=&7■ 盔甲值: &c&l;format=%.1f&f%%;var=v+0.9;inv=v-0.9;mode=last}" + +内置-红色符文重盾3: + Name: 红色符文重盾3 + Require: + - ARMOR + Display: "&8&l&k||&c&l重盾 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c重盾 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l重盾 III&f, 盔甲盔甲值&c&l+1.3&f%" + RemoveTip: "&7你拆下了&c&l重盾 III" + Rewards: + - "LoreVar{lore=&7■ 盔甲值: &c&l;format=%.1f&f%%;var=v+1.3;inv=v-1.3;mode=last}" + +内置-红色符文重盾4: + Name: 红色符文重盾4 + Require: + - ARMOR + Display: "&8&l&k||&c&l重盾 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c重盾 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l重盾 IV&f, 盔甲盔甲值&c&l+1.7&f%" + RemoveTip: "&7你拆下了&c&l重盾 IV" + Rewards: + - "LoreVar{lore=&7■ 盔甲值: &c&l;format=%.1f&f%%;var=v+1.7;inv=v-1.7;mode=last}" + +内置-红色符文重盾5: + Name: 红色符文重盾5 + Require: + - ARMOR + Display: "&8&l&k||&c&l重盾 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c重盾 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l重盾 V&f, 盔甲盔甲值&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l重盾 V" + Rewards: + - "LoreVar{lore=&7■ 盔甲值: &c&l;format=%.1f&f%%;var=v+2;inv=v-2;mode=last}" + +内置-红色符文稳健1: + Name: 红色符文稳健1 + Require: + - ARMOR + Display: "&8&l&k||&c&l稳健 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c稳健 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l稳健 I&f, 盔甲暴击抵抗&c&l+0.5&f%" + RemoveTip: "&7你拆下了&c&l稳健 I" + Rewards: + - "LoreVar{lore=&7■ 暴击抵抗: &c&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-红色符文稳健2: + Name: 红色符文稳健2 + Require: + - ARMOR + Display: "&8&l&k||&c&l稳健 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c稳健 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l稳健 II&f, 盔甲暴击抵抗&c&l+0.9&f%" + RemoveTip: "&7你拆下了&c&l稳健 II" + Rewards: + - "LoreVar{lore=&7■ 暴击抵抗: &c&l;format=%.1f&f%%;var=v+0.9;inv=v-0.9;mode=last}" + +内置-红色符文稳健3: + Name: 红色符文稳健3 + Require: + - ARMOR + Display: "&8&l&k||&c&l稳健 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c稳健 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l稳健 III&f, 盔甲暴击抵抗&c&l+1.3&f%" + RemoveTip: "&7你拆下了&c&l稳健 III" + Rewards: + - "LoreVar{lore=&7■ 暴击抵抗: &c&l;format=%.1f&f%%;var=v+1.3;inv=v-1.3;mode=last}" + +内置-红色符文稳健4: + Name: 红色符文稳健4 + Require: + - ARMOR + Display: "&8&l&k||&c&l稳健 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c稳健 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l稳健 IV&f, 盔甲暴击抵抗&c&l+1.7&f%" + RemoveTip: "&7你拆下了&c&l稳健 IV" + Rewards: + - "LoreVar{lore=&7■ 暴击抵抗: &c&l;format=%.1f&f%%;var=v+1.7;inv=v-1.7;mode=last}" + +内置-红色符文稳健5: + Name: 红色符文稳健5 + Require: + - ARMOR + Display: "&8&l&k||&c&l稳健 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c稳健 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l稳健 V&f, 盔甲暴击抵抗&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l稳健 V" + Rewards: + - "LoreVar{lore=&7■ 暴击抵抗: &c&l;format=%.1f&f%%;var=v+2;inv=v-2;mode=last}" + +内置-红色符文神圣1: + Name: 红色符文神圣1 + Require: + - ARMOR + Display: "&8&l&k||&c&l神圣 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c神圣 &7符文等级: &cI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l神圣 I&f, 盔甲吸血抗性&c&l+0.5&f%" + RemoveTip: "&7你拆下了&c&l神圣 I" + Rewards: + - "LoreVar{lore=&7■ 吸血抗性: &c&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-红色符文神圣2: + Name: 红色符文神圣2 + Require: + - ARMOR + Display: "&8&l&k||&c&l神圣 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c神圣 &7符文等级: &cII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l神圣 II&f, 盔甲吸血抗性&c&l+0.9&f%" + RemoveTip: "&7你拆下了&c&l神圣 II" + Rewards: + - "LoreVar{lore=&7■ 吸血抗性: &c&l;format=%.1f&f%%;var=v+0.9;inv=v-0.9;mode=last}" + +内置-红色符文神圣3: + Name: 红色符文神圣3 + Require: + - ARMOR + Display: "&8&l&k||&c&l神圣 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c神圣 &7符文等级: &cIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l神圣 III&f, 盔甲吸血抗性&c&l+1.3&f%" + RemoveTip: "&7你拆下了&c&l神圣 III" + Rewards: + - "LoreVar{lore=&7■ 吸血抗性: &c&l;format=%.1f&f%%;var=v+1.3;inv=v-1.3;mode=last}" + +内置-红色符文神圣4: + Name: 红色符文神圣4 + Require: + - ARMOR + Display: "&8&l&k||&c&l神圣 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c神圣 &7符文等级: &cIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l神圣 IV&f, 盔甲吸血抗性&c&l+1.7&f%" + RemoveTip: "&7你拆下了&c&l神圣 IV" + Rewards: + - "LoreVar{lore=&7■ 吸血抗性: &c&l;format=%.1f&f%%;var=v+1.7;inv=v-1.7;mode=last}" + +内置-红色符文神圣5: + Name: 红色符文神圣5 + Require: + - ARMOR + Display: "&8&l&k||&c&l神圣 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&c神圣 &7符文等级: &c&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + SuccessTip: "&f你镶嵌了&c&l神圣 V&f, 盔甲吸血抗性&c&l+2.0&f%" + RemoveTip: "&7你拆下了&c&l神圣 V" + Rewards: + - "LoreVar{lore=&7■ 吸血抗性: &c&l;format=%.1f&f%%;var=v+2;inv=v-2;mode=last}" + +内置-绿色符文锐利1: + Name: 绿色符文锐利1 + Require: + - WEAPON + Display: "&8&l&k||&a&l锐利 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a锐利 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l锐利 I&f, 武器护甲穿透&a&l+1&f%" + RemoveTip: "&7你拆下了&a&l锐利 I" + Rewards: + - "LoreVar{lore=&7■ 护甲穿透: &a&l;format=%.1f&f%%;var=v+1;inv=v-1;mode=last}" + +内置-绿色符文锐利2: + Name: 绿色符文锐利2 + Require: + - WEAPON + Display: "&8&l&k||&a&l锐利 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a锐利 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+1.7&f%" + - "&7 - 物理伤害 &c+1.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l锐利 II&f, 武器护甲穿透&a&l+1.7&f%" + RemoveTip: "&7你拆下了&a&l锐利 II" + Rewards: + - "LoreVar{lore=&7■ 护甲穿透: &a&l;var=v+1.7;format=%.1f&f%%;inv=v-1.7;mode=last}" + - "LoreVar{lore=&7■ 物理伤害: &c&l;var=v+1;inv=v-1;mode=last}" + +内置-绿色符文锐利3: + Name: 绿色符文锐利3 + Require: + - WEAPON + Display: "&8&l&k||&a&l锐利 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a锐利 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+2.2&f%" + - "&7 - 物理伤害 &c+1.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l锐利 III&f, 武器护甲穿透&a&l+2.2&f%" + RemoveTip: "&7你拆下了&a&l锐利 III" + Rewards: + - "LoreVar{lore=&7■ 护甲穿透: &a&l;format=%.1f&f%%;var=v+2.2;inv=v-2.2;mode=last}" + - "LoreVar{lore=&7■ 物理伤害: &c&l;var=v+1.4;inv=v-1.4;mode=last}" + +内置-绿色符文锐利4: + Name: 绿色符文锐利4 + Require: + - WEAPON + Display: "&8&l&k||&a&l锐利 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a锐利 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+2.7&f%" + - "&7 - 物理伤害 &c+1.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l锐利 IV&f, 武器护甲穿透&a&l+2.7&f%" + RemoveTip: "&7你拆下了&a&l锐利 IV" + Rewards: + - "LoreVar{lore=&7■ 护甲穿透: &a&l;format=%.1f&f%%;var=v+2.7;inv=v-2.7;mode=last}" + - "LoreVar{lore=&7■ 物理伤害: &c&l;var=v+1.6;inv=v-1.6;mode=last}" + +内置-绿色符文锐利5: + Name: 绿色符文锐利5 + Require: + - WEAPON + Display: "&8&l&k||&a&l锐利 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a锐利 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+3.0&f%" + - "&7 - 物理伤害 &c+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l锐利 V&f, 武器护甲穿透&a&l+3&f%" + RemoveTip: "&7你拆下了&a&l锐利 V" + Rewards: + - "LoreVar{lore=&7■ 护甲穿透: &a&l;format=%.1f&f%%;var=v+3.0;inv=v-3.0;mode=last}" + - "LoreVar{lore=&7■ 物理伤害: &c&l;var=v+1.8;inv=v-1.8;mode=last}" + +内置-绿色符文看破1: + Name: 绿色符文看破1 + Require: + - WEAPON + Display: "&8&l&k||&a&l看破 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a看破 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l看破 I&f, 武器破甲几率&a&l+1.5&f%" + RemoveTip: "&7你拆下了&a&l看破 I" + Rewards: + - "LoreVar{lore=&7■ 破甲几率: &a&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-绿色符文看破2: + Name: 绿色符文看破2 + Require: + - WEAPON + Display: "&8&l&k||&a&l看破 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a看破 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l看破 II&f, 武器破甲几率&a&l+2.7&f%" + RemoveTip: "&7你拆下了&a&l看破 II" + Rewards: + - "LoreVar{lore=&7■ 破甲几率: &a&l;var=v+2.7;format=%.1f&f%%;inv=v-2.7;mode=last}" + +内置-绿色符文看破3: + Name: 绿色符文看破3 + Require: + - WEAPON + Display: "&8&l&k||&a&l看破 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a看破 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l看破 III&f, 武器破甲几率&a&l+3.6&f%" + RemoveTip: "&7你拆下了&a&l看破 III" + Rewards: + - "LoreVar{lore=&7■ 破甲几率: &a&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-绿色符文看破4: + Name: 绿色符文看破4 + Require: + - WEAPON + Display: "&8&l&k||&a&l看破 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a看破 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l看破 IV&f, 武器破甲几率&a&l+4.3&f%" + RemoveTip: "&7你拆下了&a&l看破 IV" + Rewards: + - "LoreVar{lore=&7■ 破甲几率: &a&l;format=%.1f&f%%;var=v+4.3;inv=v-4.3;mode=last}" + +内置-绿色符文看破5: + Name: 绿色符文看破5 + Require: + - WEAPON + Display: "&8&l&k||&a&l看破 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a看破 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l看破 V&f, 武器破甲几率&a&l+5.0&f%" + RemoveTip: "&7你拆下了&a&l看破 V" + Rewards: + - "LoreVar{lore=&7■ 破甲几率: &a&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + +内置-绿色符文重挫1: + Name: 绿色符文重挫1 + Require: + - WEAPON + Display: "&8&l&k||&a&l重挫 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重挫 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重挫 I&f, 武器破盾几率&a&l+1.5&f%" + RemoveTip: "&7你拆下了&a&l重挫 I" + Rewards: + - "LoreVar{lore=&7■ 破盾几率: &a&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-绿色符文重挫2: + Name: 绿色符文重挫2 + Require: + - WEAPON + Display: "&8&l&k||&a&l重挫 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重挫 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重挫 II&f, 武器破盾几率&a&l+2.7&f%" + RemoveTip: "&7你拆下了&a&l重挫 II" + Rewards: + - "LoreVar{lore=&7■ 破盾几率: &a&l;var=v+2.7;format=%.1f&f%%;inv=v-2.7;mode=last}" + +内置-绿色符文重挫3: + Name: 绿色符文重挫3 + Require: + - WEAPON + Display: "&8&l&k||&a&l重挫 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重挫 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重挫 III&f, 武器破盾几率&a&l+3.6&f%" + RemoveTip: "&7你拆下了&a&l重挫 III" + Rewards: + - "LoreVar{lore=&7■ 破盾几率: &a&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-绿色符文重挫4: + Name: 绿色符文重挫4 + Require: + - WEAPON + Display: "&8&l&k||&a&l重挫 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重挫 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重挫 IV&f, 武器破盾几率&a&l+4.3&f%" + RemoveTip: "&7你拆下了&a&l重挫 IV" + Rewards: + - "LoreVar{lore=&7■ 破盾几率: &a&l;format=%.1f&f%%;var=v+4.3;inv=v-4.3;mode=last}" + +内置-绿色符文重挫5: + Name: 绿色符文重挫5 + Require: + - WEAPON + Display: "&8&l&k||&a&l重挫 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重挫 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重挫 V&f, 武器破盾几率&a&l+5.0&f%" + RemoveTip: "&7你拆下了&a&l重挫 V" + Rewards: + - "LoreVar{lore=&7■ 破盾几率: &a&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + +内置-绿色符文精准1: + Name: 绿色符文精准1 + Require: + - WEAPON + Display: "&8&l&k||&a&l精准 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a精准 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l精准 I&f, 武器命中几率&a&l+1.5&f%" + RemoveTip: "&7你拆下了&a&l精准 I" + Rewards: + - "LoreVar{lore=&7■ 命中几率: &a&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-绿色符文精准2: + Name: 绿色符文精准2 + Require: + - WEAPON + Display: "&8&l&k||&a&l精准 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a精准 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l精准 II&f, 武器命中几率&a&l+2.7&f%" + RemoveTip: "&7你拆下了&a&l精准 II" + Rewards: + - "LoreVar{lore=&7■ 命中几率: &a&l;var=v+2.7;format=%.1f&f%%;inv=v-2.7;mode=last}" + +内置-绿色符文精准3: + Name: 绿色符文精准3 + Require: + - WEAPON + Display: "&8&l&k||&a&l精准 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a精准 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l精准 III&f, 武器命中几率&a&l+3.6&f%" + RemoveTip: "&7你拆下了&a&l精准 III" + Rewards: + - "LoreVar{lore=&7■ 命中几率: &a&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-绿色符文精准4: + Name: 绿色符文精准4 + Require: + - WEAPON + Display: "&8&l&k||&a&l精准 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a精准 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l精准 IV&f, 武器命中几率&a&l+4.3&f%" + RemoveTip: "&7你拆下了&a&l精准 IV" + Rewards: + - "LoreVar{lore=&7■ 命中几率: &a&l;format=%.1f&f%%;var=v+4.3;inv=v-4.3;mode=last}" + +内置-绿色符文精准5: + Name: 绿色符文精准5 + Require: + - WEAPON + Display: "&8&l&k||&a&l精准 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a精准 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l精准 V&f, 武器命中几率&a&l+5.0&f%" + RemoveTip: "&7你拆下了&a&l精准 V" + Rewards: + - "LoreVar{lore=&7■ 命中几率: &a&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + +内置-绿色符文重矢1: + Name: 绿色符文重矢1 + Require: + - BOW + Display: "&8&l&k||&a&l重矢 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重矢 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重矢 I&f, 弓的箭矢穿透率&a&l+1.5&f%" + RemoveTip: "&7你拆下了&a&l重矢 I" + Rewards: + - "LoreVar{lore=&7■ 箭矢穿透率: &a&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-绿色符文重矢2: + Name: 绿色符文重矢2 + Require: + - BOW + Display: "&8&l&k||&a&l重矢 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重矢 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重矢 II&f, 弓的箭矢穿透率&a&l+2.7&f%" + RemoveTip: "&7你拆下了&a&l重矢 II" + Rewards: + - "LoreVar{lore=&7■ 箭矢穿透率: &a&l;var=v+2.7;format=%.1f&f%%;inv=v-2.7;mode=last}" + +内置-绿色符文重矢3: + Name: 绿色符文重矢3 + Require: + - BOW + Display: "&8&l&k||&a&l重矢 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重矢 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重矢 III&f, 弓的箭矢穿透率&a&l+3.6&f%" + RemoveTip: "&7你拆下了&a&l重矢 III" + Rewards: + - "LoreVar{lore=&7■ 箭矢穿透率: &a&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-绿色符文重矢4: + Name: 绿色符文重矢4 + Require: + - BOW + Display: "&8&l&k||&a&l重矢 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重矢 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重矢 IV&f, 弓的箭矢穿透率&a&l+4.3&f%" + RemoveTip: "&7你拆下了&a&l重矢 IV" + Rewards: + - "LoreVar{lore=&7■ 箭矢穿透率: &a&l;format=%.1f&f%%;var=v+4.3;inv=v-4.3;mode=last}" + +内置-绿色符文重矢5: + Name: 绿色符文重矢5 + Require: + - BOW + Display: "&8&l&k||&a&l重矢 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a重矢 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l重矢 V&f, 弓的箭矢穿透率&a&l+5.0&f%" + RemoveTip: "&7你拆下了&a&l重矢 V" + Rewards: + - "LoreVar{lore=&7■ 箭矢穿透率: &a&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + +内置-绿色符文聪慧1: + Name: 绿色符文聪慧1 + Require: + - WEAPON + Display: "&8&l&k||&a&l聪慧 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a聪慧 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+3.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l聪慧 I&f, 武器经验加成&a&l+3.0&f%" + RemoveTip: "&7你拆下了&a&l聪慧 I" + Rewards: + - "LoreVar{lore=&7■ 经验加成: &a&l;format=%.1f&f%%;var=v+3.0;inv=v-3.0;mode=last}" + +内置-绿色符文聪慧2: + Name: 绿色符文聪慧2 + Require: + - WEAPON + Display: "&8&l&k||&a&l聪慧 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a聪慧 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l聪慧 II&f, 武器经验加成&a&l+5.4&f%" + RemoveTip: "&7你拆下了&a&l聪慧 II" + Rewards: + - "LoreVar{lore=&7■ 经验加成: &a&l;var=v+5.4;format=%.1f&f%%;inv=v-5.4;mode=last}" + +内置-绿色符文聪慧3: + Name: 绿色符文聪慧3 + Require: + - WEAPON + Display: "&8&l&k||&a&l聪慧 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a聪慧 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+7.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l聪慧 III&f, 武器经验加成&a&l+7.2&f%" + RemoveTip: "&7你拆下了&a&l聪慧 III" + Rewards: + - "LoreVar{lore=&7■ 经验加成: &a&l;format=%.1f&f%%;var=v+7.2;inv=v-7.2;mode=last}" + +内置-绿色符文聪慧4: + Name: 绿色符文聪慧4 + Require: + - WEAPON + Display: "&8&l&k||&a&l聪慧 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a聪慧 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+8.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l聪慧 IV&f, 武器经验加成&a&l+8.6&f%" + RemoveTip: "&7你拆下了&a&l聪慧 IV" + Rewards: + - "LoreVar{lore=&7■ 经验加成: &a&l;format=%.1f&f%%;var=v+8.6;inv=v-8.6;mode=last}" + +内置-绿色符文聪慧5: + Name: 绿色符文聪慧5 + Require: + - WEAPON + Display: "&8&l&k||&a&l聪慧 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a聪慧 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+10.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l聪慧 V&f, 武器经验加成&a&l+5.0&f%" + RemoveTip: "&7你拆下了&a&l聪慧 V" + Rewards: + - "LoreVar{lore=&7■ 经验加成: &a&l;format=%.1f&f%%;var=v+10.0;inv=v-10.0;mode=last}" + +内置-绿色符文长生1: + Name: 绿色符文长生1 + Require: + - ARMOR + Display: "&8&l&k||&a&l长生 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a长生 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+0.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l长生 I&f, 盔甲生命力&a&l+0.8" + RemoveTip: "&7你拆下了&a&l长生 I" + Rewards: + - "LoreVar{lore=&7■ 生命力: &a&l;format=%.1f;var=v+0.8;inv=v-0.8;mode=last}" + +内置-绿色符文长生2: + Name: 绿色符文长生2 + Require: + - ARMOR + Display: "&8&l&k||&a&l长生 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a长生 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l长生 II&f, 盔甲生命力&a&l+1.2" + RemoveTip: "&7你拆下了&a&l长生 II" + Rewards: + - "LoreVar{lore=&7■ 生命力: &a&l;format=%.1f;var=v+1.2;inv=v-1.2;mode=last}" + +内置-绿色符文长生3: + Name: 绿色符文长生3 + Require: + - ARMOR + Display: "&8&l&k||&a&l长生 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a长生 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l长生 III&f, 盔甲生命力&a&l+1.5" + RemoveTip: "&7你拆下了&a&l长生 III" + Rewards: + - "LoreVar{lore=&7■ 生命力: &a&l;format=%.1f;var=v+1.5;inv=v-1.5;mode=last}" + +内置-绿色符文长生4: + Name: 绿色符文长生4 + Require: + - ARMOR + Display: "&8&l&k||&a&l长生 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a长生 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l长生 IV&f, 盔甲生命力&a&l+1.8" + RemoveTip: "&7你拆下了&a&l长生 IV" + Rewards: + - "LoreVar{lore=&7■ 生命力: &a&l;format=%.1f;var=v+1.8;inv=v-1.8;mode=last}" + +内置-绿色符文长生5: + Name: 绿色符文长生5 + Require: + - ARMOR + Display: "&8&l&k||&a&l长生 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a长生 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l长生 V&f, 盔甲生命力&a&l+2.0" + RemoveTip: "&7你拆下了&a&l长生 V" + Rewards: + - "LoreVar{lore=&7■ 生命力: &a&l;format=%.1f;var=v+2.0;inv=v-2.0;mode=last}" + +内置-绿色符文强健1: + Name: 绿色符文强健1 + Require: + - ARMOR + Display: "&8&l&k||&a&l强健 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a强健 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.1" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l强健 I&f, 盔甲生命恢复&a&l+0.1" + RemoveTip: "&7你拆下了&a&l强健 I" + Rewards: + - "LoreVar{lore=&7■ 生命恢复: &a&l;format=%.1f;var=v+0.1;inv=v-0.1;mode=last}" + +内置-绿色符文强健2: + Name: 绿色符文强健2 + Require: + - ARMOR + Display: "&8&l&k||&a&l强健 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a强健 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l强健 II&f, 盔甲生命恢复&a&l+0.2" + RemoveTip: "&7你拆下了&a&l强健 II" + Rewards: + - "LoreVar{lore=&7■ 生命恢复: &a&l;format=%.1f;var=v+0.2;inv=v-0.2;mode=last}" + +内置-绿色符文强健3: + Name: 绿色符文强健3 + Require: + - ARMOR + Display: "&8&l&k||&a&l强健 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a强健 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.3" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l强健 III&f, 盔甲生命恢复&a&l+0.3" + RemoveTip: "&7你拆下了&a&l强健 III" + Rewards: + - "LoreVar{lore=&7■ 生命恢复: &a&l;format=%.1f;var=v+0.3;inv=v-0.3;mode=last}" + +内置-绿色符文强健4: + Name: 绿色符文强健4 + Require: + - ARMOR + Display: "&8&l&k||&a&l强健 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a强健 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l强健 IV&f, 盔甲生命恢复&a&l+0.4" + RemoveTip: "&7你拆下了&a&l强健 IV" + Rewards: + - "LoreVar{lore=&7■ 生命恢复: &a&l;format=%.1f;var=v+0.4;inv=v-0.4;mode=last}" + +内置-绿色符文强健5: + Name: 绿色符文强健5 + Require: + - ARMOR + Display: "&8&l&k||&a&l强健 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a强健 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l强健 V&f, 盔甲生命恢复&a&l+0.5" + RemoveTip: "&7你拆下了&a&l强健 V" + Rewards: + - "LoreVar{lore=&7■ 生命恢复: &a&l;format=%.1f;var=v+0.5;inv=v-0.5;mode=last}" + +内置-绿色符文圣泉1: + Name: 绿色符文圣泉1 + Require: + - ARMOR + Display: "&8&l&k||&a&l圣泉 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a圣泉 &7符文等级: &aI" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.1&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l圣泉 I&f, 盔甲百分比回复&a&l+0.1&f%" + RemoveTip: "&7你拆下了&a&l圣泉 I" + Rewards: + - "LoreVar{lore=&7■ 百分比回复: &a&l;format=%.1f&f%%;var=v+0.1;inv=v-0.1;mode=last}" + +内置-绿色符文圣泉2: + Name: 绿色符文圣泉2 + Require: + - ARMOR + Display: "&8&l&k||&a&l圣泉 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a圣泉 &7符文等级: &aII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.15&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l圣泉 II&f, 盔甲百分比回复&a&l+0.15&f%" + RemoveTip: "&7你拆下了&a&l圣泉 II" + Rewards: + - "LoreVar{lore=&7■ 百分比回复: &a&l;format=%.1f&f%%;var=v+0.15;inv=v-0.15;mode=last}" + +内置-绿色符文圣泉3: + Name: 绿色符文圣泉3 + Require: + - ARMOR + Display: "&8&l&k||&a&l圣泉 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a圣泉 &7符文等级: &aIII" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l圣泉 III&f, 盔甲百分比回复&a&l+0.2&f%" + RemoveTip: "&7你拆下了&a&l圣泉 III" + Rewards: + - "LoreVar{lore=&7■ 百分比回复: &a&l;format=%.1f&f%%;var=v+0.2;inv=v-0.2;mode=last}" + +内置-绿色符文圣泉4: + Name: 绿色符文圣泉4 + Require: + - ARMOR + Display: "&8&l&k||&a&l圣泉 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a圣泉 &7符文等级: &aIV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.25&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l圣泉 IV&f, 盔甲百分比回复&a&l+0.25&f%" + RemoveTip: "&7你拆下了&a&l圣泉 IV" + Rewards: + - "LoreVar{lore=&7■ 百分比回复: &a&l;format=%.1f&f%%;var=v+0.25;inv=v-0.25;mode=last}" + +内置-绿色符文圣泉5: + Name: 绿色符文圣泉5 + Require: + - ARMOR + Display: "&8&l&k||&a&l圣泉 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&a圣泉 &7符文等级: &a&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + SuccessTip: "&f你镶嵌了&a&l圣泉 V&f, 盔甲百分比回复&a&l+0.3&f%" + RemoveTip: "&7你拆下了&a&l圣泉 V" + Rewards: + - "LoreVar{lore=&7■ 百分比回复: &a&l;format=%.1f&f%%;var=v+0.3;inv=v-0.3;mode=last}" + +内置-蓝色符文迅捷1: + Name: 蓝色符文迅捷1 + Require: + - WEAPON + Display: "&8&l&k||&9&l迅捷 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9迅捷 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l迅捷 I&f, 武器移速加成&9&l+0.5&f%" + RemoveTip: "&7你拆下了&9&l迅捷 I" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文迅捷2: + Name: 蓝色符文迅捷2 + Require: + - WEAPON + Display: "&8&l&k||&9&l迅捷 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9迅捷 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+0.9&f%" + - "&7 - 闪避几率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l迅捷 II&f, 武器移速加成&9&l+0.9&f%" + RemoveTip: "&7你拆下了&9&l迅捷 II" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+0.9;inv=v-0.9;mode=last}" + - "LoreVar{lore=&7■ 闪避几率: &a&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文迅捷3: + Name: 蓝色符文迅捷3 + Require: + - WEAPON + Display: "&8&l&k||&9&l迅捷 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9迅捷 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.2&f%" + - "&7 - 闪避几率 &9+0.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l迅捷 III&f, 武器移速加成&9&l+1.2&f%" + RemoveTip: "&7你拆下了&9&l迅捷 III" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+1.2;inv=v-1.2;mode=last}" + - "LoreVar{lore=&7■ 闪避几率: &a&l;format=%.1f&f%%;var=v+0.6;inv=v-0.6;mode=last}" + +内置-蓝色符文迅捷4: + Name: 蓝色符文迅捷4 + Require: + - WEAPON + Display: "&8&l&k||&9&l迅捷 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9迅捷 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.4&f%" + - "&7 - 闪避几率 &9+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l迅捷 IV&f, 武器移速加成&9&l+1.4&f%" + RemoveTip: "&7你拆下了&9&l迅捷 IV" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+1.4;inv=v-1.4;mode=last}" + - "LoreVar{lore=&7■ 闪避几率: &a&l;format=%.1f&f%%;var=v+0.9;inv=v-0.9;mode=last}" + +内置-蓝色符文迅捷5: + Name: 蓝色符文迅捷5 + Require: + - WEAPON + Display: "&8&l&k||&9&l迅捷 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9迅捷 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.6&f%" + - "&7 - 闪避几率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l迅捷 V&f, 武器移速加成&9&l+1.6&f%" + RemoveTip: "&7你拆下了&9&l迅捷 V" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+1.6;inv=v-1.6;mode=last}" + - "LoreVar{lore=&7■ 闪避几率: &a&l;format=%.1f&f%%;var=v+1.0;inv=v-1.0;mode=last}" + +内置-蓝色符文奔腾1: + Name: 蓝色符文奔腾1 + Require: + - WEAPON + - BOOTS + Display: "&8&l&k||&9&l奔腾 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9奔腾 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l奔腾 I&f, 武器移速加成&9&l+1.5&f%" + RemoveTip: "&7你拆下了&9&l奔腾 I" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-蓝色符文奔腾2: + Name: 蓝色符文奔腾2 + Require: + - WEAPON + - BOOTS + Display: "&8&l&k||&9&l奔腾 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9奔腾 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l奔腾 II&f, 武器移速加成&9&l+2.7&f%" + RemoveTip: "&7你拆下了&9&l奔腾 II" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+2.7;inv=v-2.7;mode=last}" + +内置-蓝色符文奔腾3: + Name: 蓝色符文奔腾3 + Require: + - WEAPON + - BOOTS + Display: "&8&l&k||&9&l奔腾 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9奔腾 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l奔腾 III&f, 武器移速加成&9&l+3.6&f%" + RemoveTip: "&7你拆下了&9&l奔腾 III" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-蓝色符文奔腾4: + Name: 蓝色符文奔腾4 + Require: + - WEAPON + - BOOTS + Display: "&8&l&k||&9&l奔腾 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9奔腾 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l奔腾 IV&f, 武器移速加成&9&l+4.5&f%" + RemoveTip: "&7你拆下了&9&l奔腾 IV" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+4.5;inv=v-4.5;mode=last}" + +内置-蓝色符文奔腾5: + Name: 蓝色符文奔腾5 + Require: + - WEAPON + - BOOTS + Display: "&8&l&k||&9&l奔腾 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9奔腾 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l奔腾 V&f, 武器移速加成&9&l+5.0&f%" + RemoveTip: "&7你拆下了&9&l奔腾 V" + Rewards: + - "LoreVar{lore=&7■ 移速加成: &9&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + +内置-蓝色符文极寒1: + Name: 蓝色符文极寒1 + Require: + - WEAPON + Display: "&8&l&k||&9&l极寒 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9极寒 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+0.8&f%" + - "&7 - 冰冻强度 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l极寒 I&f, 武器冰冻几率&9&l+0.8&f%" + RemoveTip: "&7你拆下了&9&l极寒 I" + Rewards: + - "LoreVar{lore=&7■ 冰冻几率: &9&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + - "LoreVar{lore=&7■ 冰冻强度: &9&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-蓝色符文极寒2: + Name: 蓝色符文极寒2 + Require: + - WEAPON + Display: "&8&l&k||&9&l极寒 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9极寒 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+2.0&f%" + - "&7 - 冰冻强度 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l极寒 II&f, 武器冰冻几率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l极寒 II" + Rewards: + - "LoreVar{lore=&7■ 冰冻几率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + - "LoreVar{lore=&7■ 冰冻强度: &9&l;format=%.1f&f%%;var=v+2.5;inv=v-2.5;mode=last}" + +内置-蓝色符文极寒3: + Name: 蓝色符文极寒3 + Require: + - WEAPON + Display: "&8&l&k||&9&l极寒 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9极寒 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+3.2&f%" + - "&7 - 冰冻强度 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l极寒 III&f, 武器冰冻几率&9&l+3.2&f%" + RemoveTip: "&7你拆下了&9&l极寒 III" + Rewards: + - "LoreVar{lore=&7■ 冰冻几率: &9&l;format=%.1f&f%%;var=v+3.2;inv=v-3.2;mode=last}" + - "LoreVar{lore=&7■ 冰冻强度: &9&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-蓝色符文极寒4: + Name: 蓝色符文极寒4 + Require: + - WEAPON + Display: "&8&l&k||&9&l极寒 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9极寒 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+4.2&f%" + - "&7 - 冰冻强度 &9+4.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l极寒 IV&f, 武器冰冻几率&9&l+4.2&f%" + RemoveTip: "&7你拆下了&9&l极寒 IV" + Rewards: + - "LoreVar{lore=&7■ 冰冻几率: &9&l;format=%.1f&f%%;var=v+4.2;inv=v-4.2;mode=last}" + - "LoreVar{lore=&7■ 冰冻强度: &9&l;format=%.1f&f%%;var=v+4.9;inv=v-4.9;mode=last}" + +内置-蓝色符文极寒5: + Name: 蓝色符文极寒5 + Require: + - WEAPON + Display: "&8&l&k||&9&l极寒 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9极寒 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+5.0&f%" + - "&7 - 冰冻强度 &9+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l极寒 V&f, 武器冰冻几率&9&l+5.0&f%" + RemoveTip: "&7你拆下了&9&l极寒 V" + Rewards: + - "LoreVar{lore=&7■ 冰冻几率: &9&l;format=%.1f&f%%;var=v+5.0;inv=v-5.0;mode=last}" + - "LoreVar{lore=&7■ 冰冻强度: &9&l;format=%.1f&f%%;var=v+6.0;inv=v-6.0;mode=last}" + +内置-蓝色符文雷霆1: + Name: 蓝色符文雷霆1 + Require: + - WEAPON + Display: "&8&l&k||&9&l雷霆 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9雷霆 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+0.8&f%" + - "&7 - 雷击伤害 &9+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l雷霆 I&f, 武器雷击几率&9&l+0.8&f%" + RemoveTip: "&7你拆下了&9&l雷霆 I" + Rewards: + - "LoreVar{lore=&7■ 雷击几率: &9&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + - "LoreVar{lore=&7■ 雷击伤害: &9&l;format=%.1f;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文雷霆2: + Name: 蓝色符文雷霆2 + Require: + - WEAPON + Display: "&8&l&k||&9&l雷霆 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9雷霆 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+2.0&f%" + - "&7 - 雷击伤害 &9+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l雷霆 II&f, 武器雷击几率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l雷霆 II" + Rewards: + - "LoreVar{lore=&7■ 雷击几率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + - "LoreVar{lore=&7■ 雷击伤害: &9&l;format=%.1f;var=v+1.5;inv=v-1.5;mode=last}" + +内置-蓝色符文雷霆3: + Name: 蓝色符文雷霆3 + Require: + - WEAPON + Display: "&8&l&k||&9&l雷霆 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9雷霆 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+3.0&f%" + - "&7 - 雷击伤害 &9+2.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l雷霆 III&f, 武器雷击几率&9&l+3.0&f%" + RemoveTip: "&7你拆下了&9&l雷霆 III" + Rewards: + - "LoreVar{lore=&7■ 雷击几率: &9&l;format=%.1f&f%%;var=v+3.0;inv=v-3.0;mode=last}" + - "LoreVar{lore=&7■ 雷击伤害: &9&l;format=%.1f;var=v+2.6;inv=v-2.6;mode=last}" + +内置-蓝色符文雷霆4: + Name: 蓝色符文雷霆4 + Require: + - WEAPON + Display: "&8&l&k||&9&l雷霆 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9雷霆 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+3.6&f%" + - "&7 - 雷击伤害 &9+3.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l雷霆 IV&f, 武器雷击几率&9&l+3.6&f%" + RemoveTip: "&7你拆下了&9&l雷霆 IV" + Rewards: + - "LoreVar{lore=&7■ 雷击几率: &9&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + - "LoreVar{lore=&7■ 雷击伤害: &9&l;format=%.1f;var=v+3.6;inv=v-3.6;mode=last}" + +内置-蓝色符文雷霆5: + Name: 蓝色符文雷霆5 + Require: + - WEAPON + Display: "&8&l&k||&9&l雷霆 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9雷霆 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+4.0&f%" + - "&7 - 雷击伤害 &9+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l雷霆 V&f, 武器雷击几率&9&l+4.0&f%" + RemoveTip: "&7你拆下了&9&l雷霆 V" + Rewards: + - "LoreVar{lore=&7■ 雷击几率: &9&l;format=%.1f&f%%;var=v+4.0;inv=v-4.0;mode=last}" + - "LoreVar{lore=&7■ 雷击伤害: &9&l;format=%.1f;var=v+4.5;inv=v-4.5;mode=last}" + +内置-蓝色符文汲取1: + Name: 蓝色符文汲取1 + Require: + - WEAPON + Display: "&8&l&k||&9&l汲取 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9汲取 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+0.8&f%" + - "&7 - 吸血抵抗 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l汲取 I&f, 武器吸血几率&9&l+0.8&f%" + RemoveTip: "&7你拆下了&9&l汲取 I" + Rewards: + - "LoreVar{lore=&7■ 吸血几率: &9&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + - "LoreVar{lore=&7■ 吸血抵抗: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文汲取2: + Name: 蓝色符文汲取2 + Require: + - WEAPON + Display: "&8&l&k||&9&l汲取 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9汲取 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+2.0&f%" + - "&7 - 吸血抵抗 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l汲取 II&f, 武器吸血几率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l汲取 II" + Rewards: + - "LoreVar{lore=&7■ 吸血几率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + - "LoreVar{lore=&7■ 吸血抵抗: &9&l;format=%.1f&f%%;var=v+1.5;inv=v-1.5;mode=last}" + +内置-蓝色符文汲取3: + Name: 蓝色符文汲取3 + Require: + - WEAPON + Display: "&8&l&k||&9&l汲取 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9汲取 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+3.0&f%" + - "&7 - 吸血抵抗 &9+2.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l汲取 III&f, 武器吸血几率&9&l+3.0&f%" + RemoveTip: "&7你拆下了&9&l汲取 III" + Rewards: + - "LoreVar{lore=&7■ 吸血几率: &9&l;format=%.1f&f%%;var=v+3.0;inv=v-3.0;mode=last}" + - "LoreVar{lore=&7■ 吸血抵抗: &9&l;format=%.1f&f%%;var=v+2.6;inv=v-2.6;mode=last}" + +内置-蓝色符文汲取4: + Name: 蓝色符文汲取4 + Require: + - WEAPON + Display: "&8&l&k||&9&l汲取 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9汲取 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+3.6&f%" + - "&7 - 吸血抵抗 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l汲取 IV&f, 武器吸血几率&9&l+3.6&f%" + RemoveTip: "&7你拆下了&9&l汲取 IV" + Rewards: + - "LoreVar{lore=&7■ 吸血几率: &9&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + - "LoreVar{lore=&7■ 吸血抵抗: &9&l;format=%.1f&f%%;var=v+3.6;inv=v-3.6;mode=last}" + +内置-蓝色符文汲取5: + Name: 蓝色符文汲取5 + Require: + - WEAPON + Display: "&8&l&k||&9&l汲取 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9汲取 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+4.0&f%" + - "&7 - 吸血抵抗 &9+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l汲取 V&f, 武器吸血几率&9&l+4.0&f%" + RemoveTip: "&7你拆下了&9&l汲取 V" + Rewards: + - "LoreVar{lore=&7■ 吸血几率: &9&l;format=%.1f&f%%;var=v+4.0;inv=v-4.0;mode=last}" + - "LoreVar{lore=&7■ 吸血抵抗: &9&l;format=%.1f&f%%;var=v+4.5;inv=v-4.5;mode=last}" + +内置-蓝色符文穿杨1: + Name: 蓝色符文穿杨1 + Require: + - BOW + Display: "&8&l&k||&9&l穿杨 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9穿杨 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l穿杨 I&f, 弓箭术精准&9&l+2.5&f%" + RemoveTip: "&7你拆下了&9&l穿杨 I" + Rewards: + - "LoreVar{lore=&7■ 箭术精准: &9&l;format=%.1f&f%%;var=v+2.5;inv=v-2.5;mode=last}" + +内置-蓝色符文穿杨2: + Name: 蓝色符文穿杨2 + Require: + - BOW + Display: "&8&l&k||&9&l穿杨 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9穿杨 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+3.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l穿杨 II&f, 弓箭术精准&9&l+3.7&f%" + RemoveTip: "&7你拆下了&9&l穿杨 II" + Rewards: + - "LoreVar{lore=&7■ 箭术精准: &9&l;format=%.1f&f%%;var=v+3.7;inv=v-3.7;mode=last}" + +内置-蓝色符文穿杨3: + Name: 蓝色符文穿杨3 + Require: + - BOW + Display: "&8&l&k||&9&l穿杨 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9穿杨 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+4.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l穿杨 III&f, 弓箭术精准&9&l+4.6&f%" + RemoveTip: "&7你拆下了&9&l穿杨 III" + Rewards: + - "LoreVar{lore=&7■ 箭术精准: &9&l;format=%.1f&f%%;var=v+4.6;inv=v-4.6;mode=last}" + +内置-蓝色符文穿杨4: + Name: 蓝色符文穿杨4 + Require: + - BOW + Display: "&8&l&k||&9&l穿杨 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9穿杨 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l穿杨 IV&f, 弓箭术精准&9&l+5.4&f%" + RemoveTip: "&7你拆下了&9&l穿杨 IV" + Rewards: + - "LoreVar{lore=&7■ 箭术精准: &9&l;format=%.1f&f%%;var=v+5.4;inv=v-5.4;mode=last}" + +内置-蓝色符文穿杨5: + Name: 蓝色符文穿杨5 + Require: + - BOW + Display: "&8&l&k||&9&l穿杨 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9穿杨 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l穿杨 V&f, 弓箭术精准&9&l+6.0&f%" + RemoveTip: "&7你拆下了&9&l穿杨 V" + Rewards: + - "LoreVar{lore=&7■ 箭术精准: &9&l;format=%.1f&f%%;var=v+6.0;inv=v-6.0;mode=last}" + +内置-蓝色符文毒刺1: + Name: 蓝色符文毒刺1 + Require: + - ARMOR + Display: "&8&l&k||&9&l毒刺 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9毒刺 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+0.5&f%" + - "&7 - 反弹伤害 &9+0.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l毒刺 I&f, 盔甲反弹几率&9&l+0.5&f%" + RemoveTip: "&7你拆下了&9&l毒刺 I" + Rewards: + - "LoreVar{lore=&7■ 反弹几率: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + - "LoreVar{lore=&7■ 反弹伤害: &9&l;format=%.1f&f%%;var=v+0.8;inv=v-0.8;mode=last}" + +内置-蓝色符文毒刺2: + Name: 蓝色符文毒刺2 + Require: + - ARMOR + Display: "&8&l&k||&9&l毒刺 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9毒刺 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.0&f%" + - "&7 - 反弹伤害 &9+1.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l毒刺 II&f, 盔甲反弹几率&9&l+1.0&f%" + RemoveTip: "&7你拆下了&9&l毒刺 II" + Rewards: + - "LoreVar{lore=&7■ 反弹几率: &9&l;format=%.1f&f%%;var=v+1.0;inv=v-1.0;mode=last}" + - "LoreVar{lore=&7■ 反弹伤害: &9&l;format=%.1f&f%%;var=v+1.2;inv=v-1.2;mode=last}" + +内置-蓝色符文毒刺3: + Name: 蓝色符文毒刺3 + Require: + - ARMOR + Display: "&8&l&k||&9&l毒刺 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9毒刺 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.4&f%" + - "&7 - 反弹伤害 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l毒刺 III&f, 盔甲反弹几率&9&l+1.4&f%" + RemoveTip: "&7你拆下了&9&l毒刺 III" + Rewards: + - "LoreVar{lore=&7■ 反弹几率: &9&l;format=%.1f&f%%;var=v+1.4;inv=v-1.4;mode=last}" + - "LoreVar{lore=&7■ 反弹伤害: &9&l;format=%.1f&f%%;var=v+1.8;inv=v-1.8;mode=last}" + +内置-蓝色符文毒刺4: + Name: 蓝色符文毒刺4 + Require: + - ARMOR + Display: "&8&l&k||&9&l毒刺 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9毒刺 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.8&f%" + - "&7 - 反弹伤害 &9+2.1&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l毒刺 IV&f, 盔甲反弹几率&9&l+1.8&f%" + RemoveTip: "&7你拆下了&9&l毒刺 IV" + Rewards: + - "LoreVar{lore=&7■ 反弹几率: &9&l;format=%.1f&f%%;var=v+1.8;inv=v-1.8;mode=last}" + - "LoreVar{lore=&7■ 反弹伤害: &9&l;format=%.1f&f%%;var=v+2.1;inv=v-2.1;mode=last}" + +内置-蓝色符文毒刺5: + Name: 蓝色符文毒刺5 + Require: + - ARMOR + Display: "&8&l&k||&9&l毒刺 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9毒刺 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+2.0&f%" + - "&7 - 反弹伤害 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l毒刺 V&f, 盔甲反弹几率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l毒刺 V" + Rewards: + - "LoreVar{lore=&7■ 反弹几率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + - "LoreVar{lore=&7■ 反弹伤害: &9&l;format=%.1f&f%%;var=v+2.5;inv=v-2.5;mode=last}" + +内置-蓝色符文幻影1: + Name: 蓝色符文幻影1 + Require: + - ARMOR + Display: "&8&l&k||&9&l幻影 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9幻影 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l幻影 I&f, 盔甲闪避几率&9&l+0.5&f%" + RemoveTip: "&7你拆下了&9&l幻影 I" + Rewards: + - "LoreVar{lore=&7■ 闪避几率: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文幻影2: + Name: 蓝色符文幻影2 + Require: + - ARMOR + Display: "&8&l&k||&9&l幻影 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9幻影 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l幻影 II&f, 盔甲闪避几率&9&l+1.0&f%" + RemoveTip: "&7你拆下了&9&l幻影 II" + Rewards: + - "LoreVar{lore=&7■ 闪避几率: &9&l;format=%.1f&f%%;var=v+1.0;inv=v-1.0;mode=last}" + +内置-蓝色符文幻影3: + Name: 蓝色符文幻影3 + Require: + - ARMOR + Display: "&8&l&k||&9&l幻影 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9幻影 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l幻影 III&f, 盔甲闪避几率&9&l+1.4&f%" + RemoveTip: "&7你拆下了&9&l幻影 III" + Rewards: + - "LoreVar{lore=&7■ 闪避几率: &9&l;format=%.1f&f%%;var=v+1.4;inv=v-1.4;mode=last}" + +内置-蓝色符文幻影4: + Name: 蓝色符文幻影4 + Require: + - ARMOR + Display: "&8&l&k||&9&l幻影 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9幻影 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l幻影 IV&f, 盔甲闪避几率&9&l+1.8&f%" + RemoveTip: "&7你拆下了&9&l幻影 IV" + Rewards: + - "LoreVar{lore=&7■ 闪避几率: &9&l;format=%.1f&f%%;var=v+1.8;inv=v-1.8;mode=last}" + +内置-蓝色符文幻影5: + Name: 蓝色符文幻影5 + Require: + - ARMOR + Display: "&8&l&k||&9&l幻影 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9幻影 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l幻影 V&f, 盔甲闪避几率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l幻影 V" + Rewards: + - "LoreVar{lore=&7■ 闪避几率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + +内置-蓝色符文格挡1: + Name: 蓝色符文格挡1 + Require: + - ARMOR + Display: "&8&l&k||&9&l格挡 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9格挡 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l格挡 I&f, 盔甲盾牌格挡率&9&l+0.5&f%" + RemoveTip: "&7你拆下了&9&l格挡 I" + Rewards: + - "LoreVar{lore=&7■ 盾牌格挡率: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文格挡2: + Name: 蓝色符文格挡2 + Require: + - ARMOR + Display: "&8&l&k||&9&l格挡 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9格挡 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l格挡 II&f, 盔甲盾牌格挡率&9&l+1.0&f%" + RemoveTip: "&7你拆下了&9&l格挡 II" + Rewards: + - "LoreVar{lore=&7■ 盾牌格挡率: &9&l;format=%.1f&f%%;var=v+1.0;inv=v-1.0;mode=last}" + +内置-蓝色符文格挡3: + Name: 蓝色符文格挡3 + Require: + - ARMOR + Display: "&8&l&k||&9&l格挡 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9格挡 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l格挡 III&f, 盔甲盾牌格挡率&9&l+1.4&f%" + RemoveTip: "&7你拆下了&9&l格挡 III" + Rewards: + - "LoreVar{lore=&7■ 盾牌格挡率: &9&l;format=%.1f&f%%;var=v+1.4;inv=v-1.4;mode=last}" + +内置-蓝色符文格挡4: + Name: 蓝色符文格挡4 + Require: + - ARMOR + Display: "&8&l&k||&9&l格挡 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9格挡 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l格挡 IV&f, 盔甲盾牌格挡率&9&l+1.8&f%" + Rewards: + - "LoreVar{lore=&7■ 盾牌格挡率: &9&l;format=%.1f&f%%;var=v+1.8;inv=v-1.8;mode=last}" + +内置-蓝色符文格挡5: + Name: 蓝色符文格挡5 + Require: + - ARMOR + Display: "&8&l&k||&9&l格挡 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9格挡 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l格挡 V&f, 盔甲盾牌格挡率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l格挡 V" + Rewards: + - "LoreVar{lore=&7■ 盾牌格挡率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" + +内置-蓝色符文盾山1: + Name: 蓝色符文盾山1 + Require: + - ARMOR + Display: "&8&l&k||&9&l盾山 I&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9盾山 &7符文等级: &9I" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l盾山 I&f, 盔甲箭伤免疫率&9&l+0.5&f%" + RemoveTip: "&7你拆下了&9&l盾山 I" + Rewards: + - "LoreVar{lore=&7■ 箭伤免疫率: &9&l;format=%.1f&f%%;var=v+0.5;inv=v-0.5;mode=last}" + +内置-蓝色符文盾山2: + Name: 蓝色符文盾山2 + Require: + - ARMOR + Display: "&8&l&k||&9&l盾山 II&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9盾山 &7符文等级: &9II" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l盾山 II&f, 盔甲箭伤免疫率&9&l+1.0&f%" + RemoveTip: "&7你拆下了&9&l盾山 II" + Rewards: + - "LoreVar{lore=&7■ 箭伤免疫率: &9&l;format=%.1f&f%%;var=v+1.0;inv=v-1.0;mode=last}" + +内置-蓝色符文盾山3: + Name: 蓝色符文盾山3 + Require: + - ARMOR + Display: "&8&l&k||&9&l盾山 III&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9盾山 &7符文等级: &9III" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l盾山 III&f, 盔甲箭伤免疫率&9&l+1.4&f%" + RemoveTip: "&7你拆下了&9&l盾山 III" + Rewards: + - "LoreVar{lore=&7■ 箭伤免疫率: &9&l;format=%.1f&f%%;var=v+1.4;inv=v-1.4;mode=last}" + +内置-蓝色符文盾山4: + Name: 蓝色符文盾山4 + Require: + - ARMOR + Display: "&8&l&k||&9&l盾山 IV&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9盾山 &7符文等级: &9IV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l盾山 IV&f, 盔甲箭伤免疫率&9&l+1.8&f%" + Rewards: + - "LoreVar{lore=&7■ 箭伤免疫率: &9&l;format=%.1f&f%%;var=v+1.8;inv=v-1.8;mode=last}" + +内置-蓝色符文盾山5: + Name: 蓝色符文盾山5 + Require: + - ARMOR + Display: "&8&l&k||&9&l盾山 V&8&l&k||" + Embed: 10 + Gui: + - 符文镶嵌台 + Tips: + - "&9盾山 &7符文等级: &9&lV" + - "&7在符文镶嵌台内使用" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + SuccessTip: "&f你镶嵌了&9&l盾山 V&f, 盔甲箭伤免疫率&9&l+2.0&f%" + RemoveTip: "&7你拆下了&9&l盾山 V" + Rewards: + - "LoreVar{lore=&7■ 箭伤免疫率: &9&l;format=%.1f&f%%;var=v+2.0;inv=v-2.0;mode=last}" diff --git a/src/main/resources/gems/MythicGem.yml b/src/main/resources/gems/MythicGem.yml new file mode 100644 index 0000000..3a91c39 --- /dev/null +++ b/src/main/resources/gems/MythicGem.yml @@ -0,0 +1,60 @@ +# +# MMOItem/MythicMobs系列宝石 +# 本宝石修改MMOITEMS_ATTACK_DAMAGE. 可以强化MMOItem的攻击伤害属性 +# 仅供演示 不做生产用途 +# 要制作更多的MMOItem属性强化石, 请在游戏内使用mgem nbt和mgem nbt读取属性名 +Version: 1.05 +内置-MMOItems物理伤害强化石: + Name: MMOItems物理伤害宝石 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&lMMOItems物理伤害宝石&8&l&k||" + Tips: + - "&c&lMMOItems物理伤害宝石 &7品质: &a&l传说" + - "&7在工作台内点击此宝石," + - "&7再点击您的装备" + - "&7即可使装备物理伤害属性+2(上限40)" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTcwZjZjNDBjYzdiNTZhNWU3MDFjZmNjZDMxYzE0MzVhYmFiZmM0NWViMzNkODE1YTViNDEyMDNmMjExNWEwYiJ9fX0= + SuccessTip: "&f你使用了&c&lMMOItems物理伤害强化石&f, 装备物理伤害&c&l+2.0" + Rewards: + - NBTDouble{name=MMOITEMS_ATTACK_DAMAGE;var=v+2;limit=v>=0&&v<=40} + - "LoreVar{lore=&7&l■ 物理伤害: &a&l;var=v+2;mode=last}" +内置-MMOItems闪现技能添加强化宝石: + Name: 闪现宝石 + Embed: true + Require: + - WEAPON + - TOOL + Display: "&8&l&k||&e&lMMOItems闪现宝石&8&l&k||" + Tips: + - "&e&lMMOItems闪现宝石 &7品质: &a&l史诗" + - "&7在工作台内点击此宝石," + - "&7再点击您的装备" + - "&7即可使装备闪现技能+1" + - "&7可花费&e1000金币&7拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODJjZGUwNjhlOTlhNGY5OGMzMWY4N2I0Y2MwNmJlMTRiMjI5YWNhNGY3MjgxYTQxNmM3ZTJmNTUzMjIzZGI3NCJ9fX0= + SuccessTip: "&f你使用了&e&lMMOItems闪现宝石&f, 装备闪现技能等级&e&l+1" + Rewards: + - MMOAbility{type=blink;mode=RIGHT_CLICK;cooldown=60{var=v-2;inv=v+2};range=10{var=v+2;inv=v-2}} + - "LoreAdd{lore=&f右键使用技能: &b&l闪现;limit=1}" + - Money{amount=-1000} +内置-MythicMobs默认剑宝石: + Name: MythicMobs默认剑宝石 + Require: + - SWORD + Display: "&8&l&k||&c&lMythicMobs默认剑宝石&8&l&k||" + Tips: + - "&c&lMythicMobs默认剑宝石 &7品质: &f普通" + - "&7在工作台内点击此宝石," + - "&7再点击您的装备" + - "&7即可将装备识别为MythicMobs默认的骷髅剑" + - "&7MythicMob默认的骷髅剑没有技能" + - "&7所以这个宝石没有任何效果" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvM2E3Y2RhOTAwNGZjMTk3ZDY2YWZiYzJiMDAzYTViOWVmMTNjZjQ2MDBiMWZjNzQ5MDA2NzU5MGYwNDcxODFlIn19fQ== + SuccessTip: "&f你使用了&c&lMythicMobs默认剑宝石&f, 装备变成MythicMobs中的&c&l骷髅剑" + Rewards: + - MythicItem{name=SkeletonKingSword} + - LoreAdd{lore=&c=======MythicMobs技能=======} + - LoreAdd{lore=&f骷髅剑没有技能, 所以这把武器废了(滑稽)} diff --git a/src/main/resources/gems/PlayerGem.yml b/src/main/resources/gems/PlayerGem.yml new file mode 100644 index 0000000..cf7407d --- /dev/null +++ b/src/main/resources/gems/PlayerGem.yml @@ -0,0 +1,213 @@ +# 玩家直接使用的宝石 +Version: 1.05 + +内置-点券兑换券-500: + Name: 点券兑换券-500 + Type: PlayerGem + Display: "&8&l&k||&9&l点券兑换券&8&l&k||" + Tips: + - "&7获得&9500&f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Rewards: + - Point{amount=500} $onSuccess + +内置-点券兑换券-1000: + Name: 点券兑换券-1000 + Type: PlayerGem + Display: "&8&l&k||&9&l点券兑换券&8&l&k||" + Tips: + - "&7获得&91000&f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Rewards: + - Point{amount=1000} $onSuccess + +内置-点券兑换券-2000: + Name: 点券兑换券-2000 + Type: PlayerGem + Display: "&8&l&k||&9&l点券兑换券&8&l&k||" + Tips: + - "&7获得&92000&f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Rewards: + - Point{amount=2000} $onSuccess + +内置-点券兑换券-10000: + Name: 点券兑换券-10000 + Type: PlayerGem + Display: "&8&l&k||&9&l点券兑换券&8&l&k||" + Tips: + - "&7获得&910000&f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Rewards: + - Point{amount=10000} $onSuccess + +内置-随机点券兑换券: + Name: 随机点券兑换券-500-10000 + Type: RandomGem + Display: "&8&l&k||&9&l随机点券兑换券&8&l&k||" + Tips: + - "&7使用后按如下概率获得点券" + - "&7 - 1% 获得 &910000 &f点券" + - "&7 - 10% 获得 &92000 &f点券" + - "&7 - 20% 获得 &91000 &f点券" + - "&7 - 69% 获得 &9500 &f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Gems: + 点券兑换券-10000: 1 + 点券兑换券-2000: 10 + 点券兑换券-1000: 20 + 点券兑换券-500: 69 + +内置-随机点券兑换券2: + Name: 随机点券兑换券-100-3000 + Type: PlayerGem + Display: "&8&l&k||&9&l随机点券兑换券&8&l&k||" + Tips: + - "&7随机获得&9100&f-&93000&f点券" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTc4NDRhNDU1NGY5MzYzZDY1OTkwNmQ2MzEyODE4YTExMjJhMWY5YTkyYTIzMjYyYjFlNjc3MDIyMzJiYjcifX19 + Rewards: + - Point{amount=100+2900*$RANDOM()} $onSuccess + +内置-金币兑换券-1000: + Name: 金币兑换券-1000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7获得&e1000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=1000} $onSuccess + +内置-金币兑换券-2000: + Name: 金币兑换券-2000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7获得&e2000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=2000} $onSuccess + +内置-金币兑换券-5000: + Name: 金币兑换券-5000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7获得&e5000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=5000} $onSuccess + +内置-金币兑换券-10000: + Name: 金币兑换券-10000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7获得&e10000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=10000} $onSuccess + +内置-金币兑换券-100000: + Name: 金币兑换券-100000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7获得&e100000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=100000} $onSuccess + +内置-随机金币兑换券: + Name: 随机金币兑换券-1000-100000 + Type: RandomGem + Display: "&8&l&k||&e&l随机金币兑换券&8&l&k||" + Tips: + - "&7使用后按如下概率获得金币" + - "&7 - 1% 获得 &e100000 &f金币" + - "&7 - 10% 获得 &e10000 &f金币" + - "&7 - 30% 获得 &e2000 &f金币" + - "&7 - 59% 获得 &e1000 &f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Gems: + 金币兑换券-100000: 1 + 金币兑换券-10000: 10 + 金币兑换券-2000: 30 + 金币兑换券-1000: 59 + +内置-随机金币兑换券2: + Name: 随机金币兑换券-1000-10000 + Type: PlayerGem + Display: "&8&l&k||&e&l金币兑换券&8&l&k||" + Tips: + - "&7随机获得&e1000&f-&e10000&f金币" + - "&7(&c!&7) 右键使用" + SuccessTip: "none" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjM3Y2FlNWM1MWViMTU1OGVhODI4ZjU4ZTBkZmY4ZTZiN2IwYjFhMTgzZDczN2VlY2Y3MTQ2NjE3NjEifX19 + Rewards: + - Money{amount=1000+9000*$RANDOM()} $onSuccess + +内置-蛇胆: + Name: 蛇胆 + Type: PlayerGem + Display: "&8&l&k||&c&l蛇胆&8&l&k||" + Tips: + - "&7饮用后永久增加&c1&7点生命" + - "&7上限为30点" + Material: "POTION" + Color: "MAROON" + Eat: true + SuccessTip: "&f您饮用了&c&l蛇胆&f, 生命上限永久&c&l+1" + Rewards: + - MaxHealth{amount=1;limit=30} $onSuccess + +内置-龙胆: + Name: 龙胆 + Type: PlayerGem + Display: "&8&l&k||&6&l龙胆&8&l&k||" + Tips: + - "&7饮用后永久增加&62&7点生命" + - "&7上限为60点" + Material: "POTION" + Color: "GOLD" + Eat: true + SuccessTip: "&f您饮用了&6&l龙胆&f, 生命上限永久&6&l+2" + Rewards: + - MaxHealth{amount=2;limit=60} $onSuccess + +内置-经验药水: + Name: 经验药水 + Type: PlayerGem + Display: "&8&l&k||&a&l经验药水&8&l&k||" + Tips: + - "&7饮用后立即增加&a50&7级经验" + Material: "POTION" + Color: "LIME" + Eat: true + SuccessTip: "&f您饮用了&a&l经验药水&f, 经验等级&a&l+50" + Rewards: + - ExpLevel{amount=50} $onSuccess diff --git a/src/main/resources/gems/PotionGem.yml b/src/main/resources/gems/PotionGem.yml new file mode 100644 index 0000000..8841969 --- /dev/null +++ b/src/main/resources/gems/PotionGem.yml @@ -0,0 +1,760 @@ +# 下面是一些内置药水 +Version: 1.05 +内置-发光药水: + Name: 发光药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&e&l发光药水&8&l&k||" + Tips: + - "&e&l发光药水 &7品质: &a&l传说" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加发光BUFF" + - "&7可花费1000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDY3ZGYzNWI3YTgwMjdkZGM4M2M5MTlkNzNmMGEzOTk4YWQ1NGViMGQ5NzNlZjcxNmFlNTA1YzY3NTU4MTBmNyJ9fX0= + SuccessTip: "&f你使用了&e&l发光药水&f, 装备添加&e&l发光&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l发光} + - Point{amount=-1000} $onRemove +内置-缓降药水: + Name: 缓降药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&7&l缓降药水&8&l&k||" + Tips: + - "&7&l缓降药水 &7品质: &a&l传说" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加缓降BUFF" + - "&7可花费500点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjAxYzI5M2RmNGM5ZGIzYjZmNjNlMzQzNDIwMWU2ZjQ1NGRkMmRkZjAxMDkyZDFhMjQ3NjczZDAzMDBiMjJiOCJ9fX0= + SuccessTip: "&f你使用了&7&l缓降药水&f, 装备添加&7&l缓降&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l缓降} + - Point{amount=-500} $onRemove +内置-潮涌能量药水: + Name: 潮涌能量药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l潮涌能量药水&8&l&k||" + Tips: + - "&9&l潮涌能量药水 &7品质: &a&l传说" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加潮涌能量BUFF" + - "&7可花费2000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2FmNmRjNjdjZTc1YjcyMjJhYzM5ZTA1MzZlMDkwNzI0OWY4NTUxNmVmZDEzODViZWM5MGJlMzNkZDUzZGUwNyJ9fX0= + SuccessTip: "&f你使用了&9&l潮涌能量药水&f, 装备添加&9&l潮涌能量&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l潮涌能量} + - Point{amount=-2000} $onRemove +内置-防火药水: + Name: 防火药水 + Embed: 1 + Require: + - ARMOR + Display: "&8&l&k||&6&l防火药水&8&l&k||" + Tips: + - "&6&l防火药水 &7品质: &b&l史诗" + - "&7在工作台内点击此药水," + - "&7再点击您的盔甲" + - "&7即可为装备添加防火BUFF" + - "&7可花费5000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTRiOTNmNDE1MzJmYzI3ZWY3ZGY5MzNjMTgxYWMzMTY2ZDU2MDM3ZDVjNWZmNzVkMmU4NWFmZTM3Y2EyNTdkMyJ9fX0= + SuccessTip: "&f你使用了&6&l防火药水&f, 装备添加&6&l防火&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l防火} + - Point{amount=-5000} $onRemove +内置-水下呼吸药水: + Name: 水下呼吸药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&b&l水下呼吸药水&8&l&k||" + Tips: + - "&b&l水下呼吸药水 &7品质: &b&l史诗" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加水下呼吸BUFF" + - "&7可花费2000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWRjOWZmMTg0YWU3NjdkM2NiZmQ5YzNhYTJjN2U4OGIxMGY5YjU5MTI5N2ZmNjc2ZGE2MzlmYjQ0NDYyMzhjOCJ9fX0= + SuccessTip: "&f你使用了&b&l水下呼吸药水&f, 装备添加&b&l水下呼吸&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l水下呼吸} + - Point{amount=-2000} $onRemove +内置-夜视药水: + Name: 夜视药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l夜视药水&8&l&k||" + Tips: + - "&9&l夜视药水 &7品质: &b&l史诗" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加夜视BUFF" + - "&7可花费2000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTYyOWEzZDNmNTE5ZGNkMzg1YjU5OTJlZjM0YmZhOGE2OGMzNzQ0NDc1MGI5NGVkZTVjMmY1MjFlMTczNDIifX19 + SuccessTip: "&f你使用了&9&l夜视药水&f, 装备添加&9&l夜视&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l夜视} + - Point{amount=-2000} $onRemove +内置-隐身药水: + Name: 隐身药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&7&l隐身药水&8&l&k||" + Tips: + - "&7隐身药水 &7品质: &f普通" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加隐身BUFF" + - "&7可花费500点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjJmZDQ4MWU2OTNmYzgyNTRmZDYyNWI1ZDE2NjMwYTA2YTZmZGY2MTIwZDA2YjIzNDVlM2U2ZGRiNTE0N2Y5YSJ9fX0= + SuccessTip: "&f你使用了&7&l隐身药水&f, 装备添加&7&l隐身&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l隐身} + - Point{amount=-500} $onRemove +内置-生命恢复药水1: + Name: 生命恢复药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命恢复 I&8&l&k||" + Tips: + - "&c生命恢复药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命恢复BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTFlN2ViMmU0NjFlOTZlNjMxY2JhMGMwY2RhYTU0NDg4MDYzMDJlZGFlOTFiNjFkYWZjMjgxYWU1ODRkOCJ9fX0= + SuccessTip: "&f你使用了&c&l生命恢复药水&f, 装备添加&c&l生命恢复&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命恢复 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-生命恢复药水2: + Name: 生命恢复药水2 + Success: 80 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命恢复 II&8&l&k||" + Tips: + - "&c生命恢复药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命恢复BUFF" + - "&7成功率 &a80%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTFlN2ViMmU0NjFlOTZlNjMxY2JhMGMwY2RhYTU0NDg4MDYzMDJlZGFlOTFiNjFkYWZjMjgxYWU1ODRkOCJ9fX0= + SuccessTip: "&f你使用了&c&l生命恢复药水&f, 装备添加&c&l生命恢复II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命恢复 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-生命恢复药水3: + Name: 生命恢复药水3 + Success: 60 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命恢复 III&8&l&k||" + Tips: + - "&c生命恢复药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命恢复BUFF" + - "&7成功率 &e60%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTFlN2ViMmU0NjFlOTZlNjMxY2JhMGMwY2RhYTU0NDg4MDYzMDJlZGFlOTFiNjFkYWZjMjgxYWU1ODRkOCJ9fX0= + SuccessTip: "&f你使用了&c&l生命恢复药水&f, 装备添加&c&l生命恢复III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命恢复 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-生命恢复药水4: + Name: 生命恢复药水4 + Success: 40 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命恢复 IV&8&l&k||" + Tips: + - "&c生命恢复药水 &f等级 &c&l4" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命恢复BUFF" + - "&7成功率 &660%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTFlN2ViMmU0NjFlOTZlNjMxY2JhMGMwY2RhYTU0NDg4MDYzMDJlZGFlOTFiNjFkYWZjMjgxYWU1ODRkOCJ9fX0= + SuccessTip: "&f你使用了&c&l生命恢复药水&f, 装备添加&c&l生命恢复IV&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命恢复 ;var=v<4?4:5;format=%.0f;limit=v<=4;mode=last} +内置-抗性提升药水1: + Name: 抗性提升药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&7&l抗性提升 I&8&l&k||" + Tips: + - "&7抗性提升药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加抗性提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWRhNGU3OWVlZDEzODY1NjcwOGJjMzI1YzQ0MjE5Mjc1MTdjMWMwOGNjOTM5YTc2MDNiOGQxNWE5ZjI0ODU0ZCJ9fX0= + SuccessTip: "&f你使用了&7&l抗性提升药水&f, 装备添加&7&l抗性提升&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l抗性提升 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-抗性提升药水2: + Name: 抗性提升药水2 + Success: 90 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&7&l抗性提升 II&8&l&k||" + Tips: + - "&7抗性提升药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加抗性提升BUFF" + - "&7成功率 &a90%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWRhNGU3OWVlZDEzODY1NjcwOGJjMzI1YzQ0MjE5Mjc1MTdjMWMwOGNjOTM5YTc2MDNiOGQxNWE5ZjI0ODU0ZCJ9fX0= + SuccessTip: "&f你使用了&7&l抗性提升药水&f, 装备添加&7&l抗性提升II&fBUFF" + FailTip: "&c抗性提升II药水使用失败……脸有点黑呀" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l抗性提升 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-抗性提升药水3: + Name: 抗性提升药水3 + Success: 50 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&8&l抗性提升 III&8&l&k||" + Tips: + - "&8抗性提升药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加抗性提升BUFF" + - "&7成功率 &e50%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWRhNGU3OWVlZDEzODY1NjcwOGJjMzI1YzQ0MjE5Mjc1MTdjMWMwOGNjOTM5YTc2MDNiOGQxNWE5ZjI0ODU0ZCJ9fX0= + SuccessTip: "&f你使用了&8&l抗性提升药水&f, 装备添加&8&l抗性提升III&fBUFF" + FailTip: "&c抗性提升药水使用失败, 再接再厉!" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l抗性提升 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-抗性提升药水4: + Name: 抗性提升药水4 + Success: 10 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&8&l抗性提升 IV&8&l&k||" + Tips: + - "&8抗性提升药水 &f等级 &c&l4" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加抗性提升BUFF" + - "&7成功率 &c10%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWRhNGU3OWVlZDEzODY1NjcwOGJjMzI1YzQ0MjE5Mjc1MTdjMWMwOGNjOTM5YTc2MDNiOGQxNWE5ZjI0ODU0ZCJ9fX0= + SuccessTip: "&f你使用了&8&l抗性提升药水&f, 装备添加&8&l抗性提升IV&fBUFF" + FailTip: "&c抗性提升IV使用失败, 请下次再来!" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l抗性提升 ;var=v<4?4:5;format=%.0f;limit=v<=4;mode=last} + - Command{commands=bc 恭喜 %player_name% 成功为装备添加抗性提升IV!;console=true} $onSuccess +内置-英雄的信物1: + Name: 英雄的信物 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l英雄的信物I&8&l&k||" + Tips: + - "&9英雄的信物 &f等级 I" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可使装备村庄英雄BUFF+1(上限3)" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDU0MzVjMzdlZGFmZTE2NWM5MmFlMzczNmUxNWM0NjRhZDYyMWQ2MTIyMDQzNWQxYTM5YWRiNDcwNWZiODg2In19fQ== + SuccessTip: "&f你使用了&8&l英雄的信物&f, 装备&8&l村庄英雄&fBUFF等级&8+1" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l村庄英雄 ;var=v+1;format=%.0f;limit=v<=3;mode=last} +内置-英雄的信物2: + Name: 英雄的信物2 + Success: 70 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l英雄的信物II&8&l&k||" + Tips: + - "&9&l英雄的信物 &f等级 &a&lII" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可使装备村庄英雄BUFF+1(上限5)" + - "&7成功率 &a70%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDU0MzVjMzdlZGFmZTE2NWM5MmFlMzczNmUxNWM0NjRhZDYyMWQ2MTIyMDQzNWQxYTM5YWRiNDcwNWZiODg2In19fQ== + SuccessTip: "&f你使用了&8&l英雄的信物&f, 装备&8&l村庄英雄&fBUFF等级&8+1" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l村庄英雄 ;var=v+1;format=%.0f;limit=v<=5;mode=last} +内置-英雄的信物3: + Name: 英雄的信物3 + Success: 40 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l英雄的信物III&8&l&k||" + Tips: + - "&9&l英雄的信物 &f等级 &b&lIII" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可使装备村庄英雄BUFF+1(上限8)" + - "&7成功率 &640%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDU0MzVjMzdlZGFmZTE2NWM5MmFlMzczNmUxNWM0NjRhZDYyMWQ2MTIyMDQzNWQxYTM5YWRiNDcwNWZiODg2In19fQ== + SuccessTip: "&f你使用了&8&l英雄的信物&f, 装备&8&l村庄英雄&fBUFF等级&8+1" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l村庄英雄 ;var=v+1;format=%.0f;limit=v<=8;mode=last} +内置-英雄的信物4: + Name: 英雄的信物4 + Success: 20 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&9&l英雄的信物IV&8&l&k||" + Tips: + - "&9&l英雄的信物 &f等级 &c&lIV" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可使装备村庄英雄BUFF+1(上限10)" + - "&7成功率 &c20%&7, &c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDU0MzVjMzdlZGFmZTE2NWM5MmFlMzczNmUxNWM0NjRhZDYyMWQ2MTIyMDQzNWQxYTM5YWRiNDcwNWZiODg2In19fQ== + SuccessTip: "&f你使用了&8&l英雄的信物&f, 装备&8&l村庄英雄&fBUFF等级&8+1" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l村庄英雄 ;var=v+1;format=%.0f;limit=v<=10;mode=last} +内置-跳跃提升药水1: + Name: 跳跃提升药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&d&l跳跃提升 I&8&l&k||" + Tips: + - "&d跳跃提升药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加跳跃提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjNkMzU2Yjg2MTE4ZmY4YzQ4YzExOWVmM2QyYWM5MGMxYWQxMzkyODkzMDIyZjgwYjlkYzE2OGMzNmRiMmE1NiJ9fX0= + SuccessTip: "&f你使用了&d&l跳跃提升药水&f, 装备添加&d&l跳跃提升&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l跳跃提升 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-跳跃提升药水2: + Name: 跳跃提升药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&d&l跳跃提升 II&8&l&k||" + Tips: + - "&d跳跃提升药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加跳跃提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjNkMzU2Yjg2MTE4ZmY4YzQ4YzExOWVmM2QyYWM5MGMxYWQxMzkyODkzMDIyZjgwYjlkYzE2OGMzNmRiMmE1NiJ9fX0= + SuccessTip: "&f你使用了&d&l跳跃提升药水&f, 装备添加&d&l跳跃提升II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l跳跃提升 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-跳跃提升药水3: + Name: 跳跃提升药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&d&l跳跃提升 III&8&l&k||" + Tips: + - "&d跳跃提升药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加跳跃提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjNkMzU2Yjg2MTE4ZmY4YzQ4YzExOWVmM2QyYWM5MGMxYWQxMzkyODkzMDIyZjgwYjlkYzE2OGMzNmRiMmE1NiJ9fX0= + SuccessTip: "&f你使用了&d&l跳跃提升药水&f, 装备添加&d&l跳跃提升III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l跳跃提升 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-速度药水1: + Name: 速度药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&a&l速度 I&8&l&k||" + Tips: + - "&d速度药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加速度BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzNmYTkxZGUzMWUyZGRkYTI5Y2E0MTljMDhiMzRhNWEyZDZiNGIxYTFiNTliMjM2Y2RjMTRjYjEzMWRjYjgwNyJ9fX0= + SuccessTip: "&f你使用了&a&l速度药水&f, 装备添加&a&l速度&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l速度 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-速度药水2: + Name: 速度药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&a&l速度 II&8&l&k||" + Tips: + - "&a速度药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加速度BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzNmYTkxZGUzMWUyZGRkYTI5Y2E0MTljMDhiMzRhNWEyZDZiNGIxYTFiNTliMjM2Y2RjMTRjYjEzMWRjYjgwNyJ9fX0= + SuccessTip: "&f你使用了&a&l速度药水&f, 装备添加&a&l速度II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l速度 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-速度药水3: + Name: 速度药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&a&l速度 III&8&l&k||" + Tips: + - "&a速度药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加速度BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzNmYTkxZGUzMWUyZGRkYTI5Y2E0MTljMDhiMzRhNWEyZDZiNGIxYTFiNTliMjM2Y2RjMTRjYjEzMWRjYjgwNyJ9fX0= + SuccessTip: "&f你使用了&a&l速度药水&f, 装备添加&a&l速度III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l速度 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-急迫药水1: + Name: 急迫药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&6&l急迫 I&8&l&k||" + Tips: + - "&6急迫药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加急迫BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBhNjQ0NDdkNGI3N2U1ZTEwNTU2MWM3MjgyMTZlNjAwNjE3ZmI0Zjg1N2YxYTE1MGY1MmM5MTdhODVjYTc0MyJ9fX0= + SuccessTip: "&f你使用了&6&l急迫药水&f, 装备添加&6&l急迫&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l急迫 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-急迫药水2: + Name: 急迫药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&6&l急迫 II&8&l&k||" + Tips: + - "&6急迫药水 &f等级 &6&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加急迫BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBhNjQ0NDdkNGI3N2U1ZTEwNTU2MWM3MjgyMTZlNjAwNjE3ZmI0Zjg1N2YxYTE1MGY1MmM5MTdhODVjYTc0MyJ9fX0= + SuccessTip: "&f你使用了&6&l急迫药水&f, 装备添加&6&l急迫II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l急迫 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-急迫药水3: + Name: 急迫药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&6&l急迫 III&8&l&k||" + Tips: + - "&6急迫药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加急迫BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBhNjQ0NDdkNGI3N2U1ZTEwNTU2MWM3MjgyMTZlNjAwNjE3ZmI0Zjg1N2YxYTE1MGY1MmM5MTdhODVjYTc0MyJ9fX0= + SuccessTip: "&f你使用了&6&l急迫药水&f, 装备添加&6&l急迫III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l急迫 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-力量药水1: + Name: 力量药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l力量 I&8&l&k||" + Tips: + - "&c力量药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加力量BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l力量药水&f, 装备添加&c&l力量&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l力量 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-力量药水2: + Name: 力量药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l力量 II&8&l&k||" + Tips: + - "&c力量药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加力量BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l力量药水&f, 装备添加&c&l力量II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l力量 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-力量药水3: + Name: 力量药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l力量 III&8&l&k||" + Tips: + - "&c力量药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加力量BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l力量药水&f, 装备添加&c&l力量III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l力量 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-生命提升药水1: + Name: 生命提升药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命提升 I&8&l&k||" + Tips: + - "&c生命提升药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l生命提升药水&f, 装备添加&c&l生命提升&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命提升 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-生命提升药水2: + Name: 生命提升药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命提升 II&8&l&k||" + Tips: + - "&c生命提升药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l生命提升药水&f, 装备添加&c&l生命提升II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命提升 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-生命提升药水3: + Name: 生命提升药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&c&l生命提升 III&8&l&k||" + Tips: + - "&c生命提升药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加生命提升BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTBhMTRhOGNmMDkyZDBjZDE2OTMzYmUyZGM5ZDQ0MGZiMzBiNTE1OTIxZmM2YjY4Y2RjYzE5YWJmODY0NmZjZiJ9fX0= + SuccessTip: "&f你使用了&c&l生命提升药水&f, 装备添加&c&l生命提升III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l生命提升 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-伤害吸收药水1: + Name: 伤害吸收药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&e&l伤害吸收 I&8&l&k||" + Tips: + - "&e伤害吸收药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加伤害吸收BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDlmNDBlZjc3ZmYzMzlhN2MzYWNhOGVlYWRmMTVlNjMyYmU2YzI1NzRlNmNiYTJlNzFjMmNjNzBkYzNiYmU2NCJ9fX0= + SuccessTip: "&f你使用了&e&l伤害吸收药水&f, 装备添加&e&l伤害吸收&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l伤害吸收 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-伤害吸收药水2: + Name: 伤害吸收药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&e&l伤害吸收 II&8&l&k||" + Tips: + - "&e伤害吸收药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加伤害吸收BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDlmNDBlZjc3ZmYzMzlhN2MzYWNhOGVlYWRmMTVlNjMyYmU2YzI1NzRlNmNiYTJlNzFjMmNjNzBkYzNiYmU2NCJ9fX0= + SuccessTip: "&f你使用了&e&l伤害吸收药水&f, 装备添加&e&l伤害吸收II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l伤害吸收 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-伤害吸收药水3: + Name: 伤害吸收药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&e&l伤害吸收 III&8&l&k||" + Tips: + - "&e伤害吸收药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加伤害吸收BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDlmNDBlZjc3ZmYzMzlhN2MzYWNhOGVlYWRmMTVlNjMyYmU2YzI1NzRlNmNiYTJlNzFjMmNjNzBkYzNiYmU2NCJ9fX0= + SuccessTip: "&f你使用了&e&l伤害吸收药水&f, 装备添加&e&l伤害吸收III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l伤害吸收 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-幸运药水1: + Name: 幸运药水1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&2&l幸运 I&8&l&k||" + Tips: + - "&2幸运药水 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加幸运BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODlkYWVmN2VmNjExMDU3YWZkOWY5NDA0NDRjMzBmZmJkNWQ1NDY1OGQzYjFjNjczNjJhNTc5NzY1ZTI4NzIzNSJ9fX0= + SuccessTip: "&f你使用了&2&l幸运药水&f, 装备添加&2&l幸运&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l幸运 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-幸运药水2: + Name: 幸运药水2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&2&l幸运 II&8&l&k||" + Tips: + - "&2幸运药水 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加幸运BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODlkYWVmN2VmNjExMDU3YWZkOWY5NDA0NDRjMzBmZmJkNWQ1NDY1OGQzYjFjNjczNjJhNTc5NzY1ZTI4NzIzNSJ9fX0= + SuccessTip: "&f你使用了&2&l幸运药水&f, 装备添加&2&l幸运II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l幸运 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-幸运药水3: + Name: 幸运药水3 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&2&l幸运 III&8&l&k||" + Tips: + - "&2幸运药水 &f等级 &b&l3" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加幸运BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODlkYWVmN2VmNjExMDU3YWZkOWY5NDA0NDRjMzBmZmJkNWQ1NDY1OGQzYjFjNjczNjJhNTc5NzY1ZTI4NzIzNSJ9fX0= + SuccessTip: "&f你使用了&2&l幸运药水&f, 装备添加&2&l幸运III&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l幸运 ;var=v<3?3:4;format=%.0f;limit=v<=3;mode=last} +内置-海豚的恩惠1: + Name: 海豚的恩惠1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&b&l海豚的恩惠 I&8&l&k||" + Tips: + - "&b海豚的恩惠 &f等级 1" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加海豚的恩惠BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWMzODIxZDRmNjFiMTdmODJmMGQ3YThlNTMxMjYwOGZmNTBlZGUyOWIxYjRkYzg5ODQ3YmU5NDI3ZDM2In19fQ== + SuccessTip: "&f你使用了&b&l海豚的恩惠药水&f, 装备添加&b&l海豚的恩惠&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l海豚的恩惠 ;var=v+1;format=%.0f;limit=v<=1;mode=last} +内置-海豚的恩惠2: + Name: 海豚的恩惠2 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&b&l海豚的恩惠 II&8&l&k||" + Tips: + - "&b海豚的恩惠 &f等级 &a&l2" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加海豚的恩惠BUFF" + - "&c不可拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYWMzODIxZDRmNjFiMTdmODJmMGQ3YThlNTMxMjYwOGZmNTBlZGUyOWIxYjRkYzg5ODQ3YmU5NDI3ZDM2In19fQ== + SuccessTip: "&f你使用了&b&l海豚的恩惠药水&f, 装备添加&b&l海豚的恩惠II&fBUFF" + Rewards: + - LoreVar{lore=&6&l[&c&lBUFF&6&l] &b&l海豚的恩惠 ;var=v<2?2:3;format=%.0f;limit=v<=2;mode=last} +内置-饱和药水: + Name: 饱和药水 + Embed: 1 + Require: + - WEAPON + - TOOL + - ARMOR + Display: "&8&l&k||&6&l饱和药水&8&l&k||" + Tips: + - "&6&l饱和药水 &7品质: &a&l传说" + - "&7在工作台内点击此药水," + - "&7再点击您的装备" + - "&7即可为装备添加饱和BUFF" + - "&7可花费2000点券拆卸" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDE1N2Y5MzRkMzM4MTllYTdjYzkxNTJhNWRjNzU4YzM4MmQwZjdmNmRhZTk0YWY4M2ZkNzk2YTJkY2I2MDNhZCJ9fX0= + SuccessTip: "&f你使用了&6&l饱和药水&f, 装备添加&6&l饱和&fBUFF" + Rewards: + - LoreAdd{lore=&6&l[&c&lBUFF&6&l] &b&l饱和} + - Point{amount=-2000} $onRemove diff --git a/src/main/resources/gui/Default.yml b/src/main/resources/gui/Default.yml new file mode 100644 index 0000000..bf67b44 --- /dev/null +++ b/src/main/resources/gui/Default.yml @@ -0,0 +1,38 @@ +default: + Title: "&8CMagicGem 镶嵌台 &7%page%" + Page: 1 + Locations: [] + Slots: + - "XXXXXXXXX" + - "XGGGGGGGX" + - "XGGGEGGGX" + - "XGGGGGGGX" + - "PXXXXXXNX" + Items: + X: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: " " + E: + Type: EQUIP_SLOT + Material: ANVIL + Display: "&e放入装备" + Lore: + - "&7放入需要镶嵌或拆卸宝石的装备" + Require: + - ALL + G: + Type: GEM_SLOT + Material: LIGHT_GRAY_STAINED_GLASS_PANE + Display: "&7可用宝石" + Lore: + - "&7左键使用背包中的宝石" + - "&7右键拆卸已镶嵌的宝石" + P: + Type: PAGE_PREV + Material: ARROW + Display: "&e上一页" + N: + Type: PAGE_NEXT + Material: ARROW + Display: "&e下一页" diff --git a/src/main/resources/gui/gui.yml b/src/main/resources/gui/gui.yml new file mode 100644 index 0000000..8a5c260 --- /dev/null +++ b/src/main/resources/gui/gui.yml @@ -0,0 +1,80 @@ +default: + Locations: + Page: 10000 + Slots: + - TTTTITTTT + - GGGGGGGGG + - GGGGGGGGG + - GGGGGGGGG + - GGGGGGGGG + - PEEEXEEEN + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + I: + Type: EQUIP_SLOT + G: + Type: GEM_SLOT + P: + Type: PAGE_PREV + Material: ARROW + E: + Type: EMPTY + N: + Type: PAGE_NEXT + Material: ARROW + X: + Type: PAGE_TIP + Material: PAPER + +符文镶嵌台: + Locations: + Title: "&8&l&k||&6&l符文镶嵌台&8&l&k||" + Page: 1 + Slots: + - LLBTITRKK + - LBBMGMRRK + - BBMGGGMRR + - BBMGGGMRR + - LBBMGMRRK + - LLBDSDRKK + Items: + L: + Type: TIP + Material: BLUE_STAINED_GLASS_PANE + Display: "&9在左侧镶嵌蓝色符文" + K: + Type: TIP + Material: RED_STAINED_GLASS_PANE + Display: "&c在右侧镶嵌红色符文" + M: + Type: TIP + Material: GREEN_STAINED_GLASS_PANE + Display: "&a在中间镶嵌绿色符文" + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&f&l在中间放置装备" + D: + Type: TIP + Material: YELLOW_STAINED_GLASS_PANE + Display: "&e在中间镶嵌技能符文" + I: + Type: EQUIP_SLOT + R: + Type: GEM_SLOT + Require: 红色符文 + Amount: 1 + G: + Type: GEM_SLOT + Require: 绿色符文 + Amount: 1 + B: + Type: GEM_SLOT + Require: 蓝色符文 + Amount: 1 + S: + Type: GEM_SLOT + Require: 技能符文 + Amount: 1 diff --git a/src/main/resources/gui/rune.yml b/src/main/resources/gui/rune.yml new file mode 100644 index 0000000..8bc7942 --- /dev/null +++ b/src/main/resources/gui/rune.yml @@ -0,0 +1,1578 @@ +符文分解台: + Locations: + Title: "&8&l&k||&c&l符文分解台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - TGTTBTTIT + - TTTTTTTTT + Items: + T: + Type: TIP + Material: RED_STAINED_GLASS_PANE + Display: "&c在左侧放置符文, 点击按钮分解" + I: + Type: TIP + Material: BOOK + Glow: true + Display: "&a分解材料表" + Tips: + - "&f分解 &6I &f级符文, 可获得 &610 &f符文碎片" + - "&f分解 &6II &f级符文, 可获得 &620 &f符文碎片" + - "&f分解 &6III &f级符文, 可获得 &650 &f符文碎片" + - "&f分解 &6IV &f级符文, 可获得 &6200 &f符文碎片" + - "&f分解 &6V &f级符文, 可获得 &6800 &f符文碎片" + B: + Type: USE_GEM + Material: ANVIL + Glow: true + Display: "&c&l点击分解符文" + Gem: 符文分解券 + G: + Type: EQUIP_SLOT + Require: + - GEM:ALL + +符文合成台: + Locations: + Title: "&8&l&k||&9&l符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - TABCDETIT + - TTTTTTTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击查看可合成的符文" + I: + Type: TIP + Material: BOOK + Glow: true + Display: "&a合成材料表" + Tips: + - "&f合成 &6I &f级符文, 需要 &615 &f符文碎片" + - "&f合成 &6II &f级符文, 需要 &630 &f符文碎片" + - "&f合成 &6III &f级符文, 需要 &680 &f符文碎片" + - "&f合成 &6IV &f级符文, 需要 &6300 &f符文碎片" + - "&f合成 &6V &f级符文, 需要 &61200 &f符文碎片" + A: + Type: PAGE_JUMP + Gui: 一级符文合成 + Display: "&9&l一级符文合成" + Material: COAL_BLOCK + Glow: true + Tips: + - "&f需要 &615 &f符文碎片" + B: + Type: PAGE_JUMP + Gui: 二级符文合成 + Display: "&9&l二级符文合成" + Material: REDSTONE_BLOCK + Glow: true + Tips: + - "&f需要 &630 &f符文碎片" + C: + Type: PAGE_JUMP + Gui: 三级符文合成 + Display: "&9&l三级符文合成" + Material: IRON_BLOCK + Glow: true + Tips: + - "&f需要 &680 &f符文碎片" + D: + Type: PAGE_JUMP + Gui: 四级符文合成 + Display: "&9&l四级符文合成" + Material: GOLD_BLOCK + Glow: true + Tips: + - "&f需要 &6300 &f符文碎片" + E: + Type: PAGE_JUMP + Gui: 五级符文合成 + Display: "&9&l五级符文合成" + Material: DIAMOND_BLOCK + Glow: true + Tips: + - "&f需要 &61200 &f符文碎片" + +一级符文合成: + Locations: + Title: "&8&l&k||&9&l一级符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - ABCDEFGHI + - JKLMNOPQR + - SUVWXYZ@$ + - +*??????? + - TTTT=TTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击符文立即合成" + Tips: + - "&f花费 &615 &f符文碎片" + - "&f即可合成指定符文" + A: + Type: USE_GEM + Gem: 合成红色符文强攻1 + Display: "&8&l&k||&c&l强攻 I&8&l&k||" + Tips: + - "&c强攻 &7符文等级: &cI" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + B: + Type: USE_GEM + Gem: 合成红色符文致命1 + Display: "&8&l&k||&c&l致命 I&8&l&k||" + Tips: + - "&c致命 &7符文等级: &cI" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + C: + Type: USE_GEM + Gem: 合成红色符文灾祸1 + Display: "&8&l&k||&c&l灾祸 I&8&l&k||" + Tips: + - "&c灾祸 &7符文等级: &cI" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + D: + Type: USE_GEM + Gem: 合成红色符文流光1 + Display: "&8&l&k||&c&l流光 I&8&l&k||" + Tips: + - "&c流光 &7符文等级: &cI" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + E: + Type: USE_GEM + Gem: 合成红色符文狂暴1 + Display: "&8&l&k||&c&l狂暴 I&8&l&k||" + Tips: + - "&c狂暴 &7符文等级: &cI" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+2.0&f%" + - "&7 - 暴击几率 &c+0.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + F: + Type: USE_GEM + Gem: 合成红色符文烈焰1 + Display: "&8&l&k||&c&l烈焰 I&8&l&k||" + Tips: + - "&c烈焰 &7符文等级: &cI" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+0.8&f%" + - "&7 - 燃烧伤害 &c+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + G: + Type: USE_GEM + Gem: 合成红色符文坚守1 + Display: "&8&l&k||&c&l坚守 I&8&l&k||" + Tips: + - "&c坚守 &7符文等级: &cI" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+0.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + H: + Type: USE_GEM + Gem: 合成红色符文重盾1 + Display: "&8&l&k||&c&l重盾 I&8&l&k||" + Tips: + - "&c重盾 &7符文等级: &cI" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + I: + Type: USE_GEM + Gem: 合成红色符文稳健1 + Display: "&8&l&k||&c&l稳健 I&8&l&k||" + Tips: + - "&c稳健 &7符文等级: &cI" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + J: + Type: USE_GEM + Gem: 合成红色符文神圣1 + Display: "&8&l&k||&c&l神圣 I&8&l&k||" + Tips: + - "&c神圣 &7符文等级: &cI" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + K: + Type: USE_GEM + Gem: 合成绿色符文锐利1 + Display: "&8&l&k||&a&l锐利 I&8&l&k||" + Tips: + - "&a锐利 &7符文等级: &aI" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + L: + Type: USE_GEM + Gem: 合成绿色符文看破1 + Display: "&8&l&k||&a&l看破 I&8&l&k||" + Tips: + - "&a看破 &7符文等级: &aI" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + M: + Type: USE_GEM + Gem: 合成绿色符文重挫1 + Display: "&8&l&k||&a&l重挫 I&8&l&k||" + Tips: + - "&a重挫 &7符文等级: &aI" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + N: + Type: USE_GEM + Gem: 合成绿色符文精准1 + Display: "&8&l&k||&a&l精准 I&8&l&k||" + Tips: + - "&a精准 &7符文等级: &aI" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + O: + Type: USE_GEM + Gem: 合成绿色符文重矢1 + Display: "&8&l&k||&a&l重矢 I&8&l&k||" + Tips: + - "&a重矢 &7符文等级: &aI" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + P: + Type: USE_GEM + Gem: 合成绿色符文聪慧1 + Display: "&8&l&k||&a&l聪慧 I&8&l&k||" + Tips: + - "&a聪慧 &7符文等级: &aI" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+3.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + Q: + Type: USE_GEM + Gem: 合成绿色符文长生1 + Display: "&8&l&k||&a&l长生 I&8&l&k||" + Tips: + - "&a长生 &7符文等级: &aI" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+0.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + R: + Type: USE_GEM + Gem: 合成绿色符文强健1 + Display: "&8&l&k||&a&l强健 I&8&l&k||" + Tips: + - "&a强健 &7符文等级: &aI" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.1" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + S: + Type: USE_GEM + Gem: 合成绿色符文圣泉1 + Display: "&8&l&k||&a&l圣泉 I&8&l&k||" + Tips: + - "&a圣泉 &7符文等级: &aI" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.1&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + U: + Type: USE_GEM + Gem: 合成蓝色符文迅捷1 + Display: "&8&l&k||&9&l迅捷 I&8&l&k||" + Tips: + - "&9迅捷 &7符文等级: &9I" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + V: + Type: USE_GEM + Gem: 合成蓝色符文奔腾1 + Display: "&8&l&k||&9&l奔腾 I&8&l&k||" + Tips: + - "&9奔腾 &7符文等级: &9I" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + W: + Type: USE_GEM + Gem: 合成蓝色符文极寒1 + Display: "&8&l&k||&9&l极寒 I&8&l&k||" + Tips: + - "&9极寒 &7符文等级: &9I" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+0.8&f%" + - "&7 - 冰冻强度 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + X: + Type: USE_GEM + Gem: 合成蓝色符文雷霆1 + Display: "&8&l&k||&9&l雷霆 I&8&l&k||" + Tips: + - "&9雷霆 &7符文等级: &9I" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+0.8&f%" + - "&7 - 雷击伤害 &9+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Y: + Type: USE_GEM + Gem: 合成蓝色符文汲取1 + Display: "&8&l&k||&9&l汲取 I&8&l&k||" + Tips: + - "&9汲取 &7符文等级: &9I" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+0.8&f%" + - "&7 - 吸血抵抗 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Z: + Type: USE_GEM + Gem: 合成蓝色符文穿杨1 + Display: "&8&l&k||&9&l穿杨 I&8&l&k||" + Tips: + - "&9穿杨 &7符文等级: &9I" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '@': + Type: USE_GEM + Gem: 合成蓝色符文毒刺1 + Display: "&8&l&k||&9&l毒刺 I&8&l&k||" + Tips: + - "&9毒刺 &7符文等级: &9I" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+0.5&f%" + - "&7 - 反弹伤害 &9+0.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '$': + Type: USE_GEM + Gem: 合成蓝色符文幻影1 + Display: "&8&l&k||&9&l幻影 I&8&l&k||" + Tips: + - "&9幻影 &7符文等级: &9I" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '+': + Type: USE_GEM + Gem: 合成蓝色符文格挡1 + Display: "&8&l&k||&9&l格挡 I&8&l&k||" + Tips: + - "&9格挡 &7符文等级: &9I" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '*': + Type: USE_GEM + Gem: 合成蓝色符文盾山1 + Display: "&8&l&k||&9&l盾山 I&8&l&k||" + Tips: + - "&9盾山 &7符文等级: &9I" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '=': + Type: PAGE_JUMP + Display: "&c返回" + Material: IRON_DOOR + Gui: 符文合成台 + '?': + Type: EMPTY + +二级符文合成: + Locations: + Title: "&8&l&k||&9&l二级符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - ABCDEFGHI + - JKLMNOPQR + - SUVWXYZ@$ + - +*??????? + - TTTT=TTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击符文立即合成" + Tips: + - "&f花费 &630 &f符文碎片" + - "&f即可合成指定符文" + A: + Type: USE_GEM + Gem: 合成红色符文强攻2 + Display: "&8&l&k||&c&l强攻 II&8&l&k||" + Tips: + - "&c强攻 &7符文等级: &cII" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+3.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + B: + Type: USE_GEM + Gem: 合成红色符文致命2 + Display: "&8&l&k||&c&l致命 II&8&l&k||" + Tips: + - "&c致命 &7符文等级: &cII" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+2.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + C: + Type: USE_GEM + Gem: 合成红色符文灾祸2 + Display: "&8&l&k||&c&l灾祸 II&8&l&k||" + Tips: + - "&c灾祸 &7符文等级: &cII" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + D: + Type: USE_GEM + Gem: 合成红色符文流光2 + Display: "&8&l&k||&c&l流光 II&8&l&k||" + Tips: + - "&c流光 &7符文等级: &cII" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+3.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + E: + Type: USE_GEM + Gem: 合成红色符文狂暴2 + Display: "&8&l&k||&c&l狂暴 II&8&l&k||" + Tips: + - "&c狂暴 &7符文等级: &cII" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+3.5&f%" + - "&7 - 暴击几率 &c+0.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + F: + Type: USE_GEM + Gem: 合成红色符文烈焰2 + Display: "&8&l&k||&c&l烈焰 II&8&l&k||" + Tips: + - "&c烈焰 &7符文等级: &cII" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+2.0&f%" + - "&7 - 燃烧伤害 &c+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + G: + Type: USE_GEM + Gem: 合成红色符文坚守2 + Display: "&8&l&k||&c&l坚守 II&8&l&k||" + Tips: + - "&c坚守 &7符文等级: &cII" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+1.3" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + H: + Type: USE_GEM + Gem: 合成红色符文重盾2 + Display: "&8&l&k||&c&l重盾 II&8&l&k||" + Tips: + - "&c重盾 &7符文等级: &cII" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + I: + Type: USE_GEM + Gem: 合成红色符文稳健2 + Display: "&8&l&k||&c&l稳健 II&8&l&k||" + Tips: + - "&c稳健 &7符文等级: &cII" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + J: + Type: USE_GEM + Gem: 合成红色符文神圣2 + Display: "&8&l&k||&c&l神圣 II&8&l&k||" + Tips: + - "&c神圣 &7符文等级: &cII" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + K: + Type: USE_GEM + Gem: 合成绿色符文锐利2 + Display: "&8&l&k||&a&l锐利 II&8&l&k||" + Tips: + - "&a锐利 &7符文等级: &aII" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+1.7&f%" + - "&7 - 物理伤害 &c+1.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + L: + Type: USE_GEM + Gem: 合成绿色符文看破2 + Display: "&8&l&k||&a&l看破 II&8&l&k||" + Tips: + - "&a看破 &7符文等级: &aII" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + M: + Type: USE_GEM + Gem: 合成绿色符文重挫2 + Display: "&8&l&k||&a&l重挫 II&8&l&k||" + Tips: + - "&a重挫 &7符文等级: &aII" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + N: + Type: USE_GEM + Gem: 合成绿色符文精准2 + Display: "&8&l&k||&a&l精准 II&8&l&k||" + Tips: + - "&a精准 &7符文等级: &aII" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + O: + Type: USE_GEM + Gem: 合成绿色符文重矢2 + Display: "&8&l&k||&a&l重矢 II&8&l&k||" + Tips: + - "&a重矢 &7符文等级: &aII" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + P: + Type: USE_GEM + Gem: 合成绿色符文聪慧2 + Display: "&8&l&k||&a&l聪慧 II&8&l&k||" + Tips: + - "&a聪慧 &7符文等级: &aII" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + Q: + Type: USE_GEM + Gem: 合成绿色符文长生2 + Display: "&8&l&k||&a&l长生 II&8&l&k||" + Tips: + - "&a长生 &7符文等级: &aII" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + R: + Type: USE_GEM + Gem: 合成绿色符文强健2 + Display: "&8&l&k||&a&l强健 II&8&l&k||" + Tips: + - "&a强健 &7符文等级: &aII" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + S: + Type: USE_GEM + Gem: 合成绿色符文圣泉2 + Display: "&8&l&k||&a&l圣泉 II&8&l&k||" + Tips: + - "&a圣泉 &7符文等级: &aII" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.15&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + U: + Type: USE_GEM + Gem: 合成蓝色符文迅捷2 + Display: "&8&l&k||&9&l迅捷 II&8&l&k||" + Tips: + - "&9迅捷 &7符文等级: &9II" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+0.9&f%" + - "&7 - 闪避几率 &9+0.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + V: + Type: USE_GEM + Gem: 合成蓝色符文奔腾2 + Display: "&8&l&k||&9&l奔腾 II&8&l&k||" + Tips: + - "&9奔腾 &7符文等级: &9II" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + W: + Type: USE_GEM + Gem: 合成蓝色符文极寒2 + Display: "&8&l&k||&9&l极寒 II&8&l&k||" + Tips: + - "&9极寒 &7符文等级: &9II" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+2.0&f%" + - "&7 - 冰冻强度 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + X: + Type: USE_GEM + Gem: 合成蓝色符文雷霆2 + Display: "&8&l&k||&9&l雷霆 II&8&l&k||" + Tips: + - "&9雷霆 &7符文等级: &9II" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+2.0&f%" + - "&7 - 雷击伤害 &9+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Y: + Type: USE_GEM + Gem: 合成蓝色符文汲取2 + Display: "&8&l&k||&9&l汲取 II&8&l&k||" + Tips: + - "&9汲取 &7符文等级: &9II" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+2.0&f%" + - "&7 - 吸血抵抗 &9+1.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Z: + Type: USE_GEM + Gem: 合成蓝色符文穿杨2 + Display: "&8&l&k||&9&l穿杨 II&8&l&k||" + Tips: + - "&9穿杨 &7符文等级: &9II" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+3.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '@': + Type: USE_GEM + Gem: 合成蓝色符文毒刺2 + Display: "&8&l&k||&9&l毒刺 II&8&l&k||" + Tips: + - "&9毒刺 &7符文等级: &9II" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.0&f%" + - "&7 - 反弹伤害 &9+1.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '$': + Type: USE_GEM + Gem: 合成蓝色符文幻影2 + Display: "&8&l&k||&9&l幻影 II&8&l&k||" + Tips: + - "&9幻影 &7符文等级: &9II" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '+': + Type: USE_GEM + Gem: 合成蓝色符文格挡2 + Display: "&8&l&k||&9&l格挡 II&8&l&k||" + Tips: + - "&9格挡 &7符文等级: &9II" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '*': + Type: USE_GEM + Gem: 合成蓝色符文盾山2 + Display: "&8&l&k||&9&l盾山 II&8&l&k||" + Tips: + - "&9盾山 &7符文等级: &9II" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '=': + Type: PAGE_JUMP + Display: "&c返回" + Material: IRON_DOOR + Gui: 符文合成台 + '?': + Type: EMPTY + +三级符文合成: + Locations: + Title: "&8&l&k||&9&l三级符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - ABCDEFGHI + - JKLMNOPQR + - SUVWXYZ@$ + - +*??????? + - TTTT=TTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击符文立即合成" + Tips: + - "&f花费 &680 &f符文碎片" + - "&f即可合成指定符文" + A: + Type: USE_GEM + Gem: 合成红色符文强攻3 + Display: "&8&l&k||&c&l强攻 III&8&l&k||" + Tips: + - "&c强攻 &7符文等级: &cIII" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + B: + Type: USE_GEM + Gem: 合成红色符文致命3 + Display: "&8&l&k||&c&l致命 III&8&l&k||" + Tips: + - "&c致命 &7符文等级: &cIII" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+3.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + C: + Type: USE_GEM + Gem: 合成红色符文灾祸3 + Display: "&8&l&k||&c&l灾祸 III&8&l&k||" + Tips: + - "&c灾祸 &7符文等级: &cIII" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+2.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + D: + Type: USE_GEM + Gem: 合成红色符文流光3 + Display: "&8&l&k||&c&l流光 III&8&l&k||" + Tips: + - "&c流光 &7符文等级: &cIII" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + E: + Type: USE_GEM + Gem: 合成红色符文狂暴3 + Display: "&8&l&k||&c&l狂暴 III&8&l&k||" + Tips: + - "&c狂暴 &7符文等级: &cIII" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+4.2&f%" + - "&7 - 暴击几率 &c+0.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + F: + Type: USE_GEM + Gem: 合成红色符文烈焰3 + Display: "&8&l&k||&c&l烈焰 III&8&l&k||" + Tips: + - "&c烈焰 &7符文等级: &cIII" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+3.0&f%" + - "&7 - 燃烧伤害 &c+2.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + G: + Type: USE_GEM + Gem: 合成红色符文坚守3 + Display: "&8&l&k||&c&l坚守 III&8&l&k||" + Tips: + - "&c坚守 &7符文等级: &cIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + H: + Type: USE_GEM + Gem: 合成红色符文重盾3 + Display: "&8&l&k||&c&l重盾 III&8&l&k||" + Tips: + - "&c重盾 &7符文等级: &cIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + I: + Type: USE_GEM + Gem: 合成红色符文稳健3 + Display: "&8&l&k||&c&l稳健 III&8&l&k||" + Tips: + - "&c稳健 &7符文等级: &cIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + J: + Type: USE_GEM + Gem: 合成红色符文神圣3 + Display: "&8&l&k||&c&l神圣 III&8&l&k||" + Tips: + - "&c神圣 &7符文等级: &cIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+1.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + K: + Type: USE_GEM + Gem: 合成绿色符文锐利3 + Display: "&8&l&k||&a&l锐利 III&8&l&k||" + Tips: + - "&a锐利 &7符文等级: &aIII" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+2.2&f%" + - "&7 - 物理伤害 &c+1.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + L: + Type: USE_GEM + Gem: 合成绿色符文看破3 + Display: "&8&l&k||&a&l看破 III&8&l&k||" + Tips: + - "&a看破 &7符文等级: &aIII" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + M: + Type: USE_GEM + Gem: 合成绿色符文重挫3 + Display: "&8&l&k||&a&l重挫 III&8&l&k||" + Tips: + - "&a重挫 &7符文等级: &aIII" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + N: + Type: USE_GEM + Gem: 合成绿色符文精准3 + Display: "&8&l&k||&a&l精准 III&8&l&k||" + Tips: + - "&a精准 &7符文等级: &aIII" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + O: + Type: USE_GEM + Gem: 合成绿色符文重矢3 + Display: "&8&l&k||&a&l重矢 III&8&l&k||" + Tips: + - "&a重矢 &7符文等级: &aIII" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + P: + Type: USE_GEM + Gem: 合成绿色符文聪慧3 + Display: "&8&l&k||&a&l聪慧 III&8&l&k||" + Tips: + - "&a聪慧 &7符文等级: &aIII" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+7.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + Q: + Type: USE_GEM + Gem: 合成绿色符文长生3 + Display: "&8&l&k||&a&l长生 III&8&l&k||" + Tips: + - "&a长生 &7符文等级: &aIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + R: + Type: USE_GEM + Gem: 合成绿色符文强健3 + Display: "&8&l&k||&a&l强健 III&8&l&k||" + Tips: + - "&a强健 &7符文等级: &aIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.3" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + S: + Type: USE_GEM + Gem: 合成绿色符文圣泉3 + Display: "&8&l&k||&a&l圣泉 III&8&l&k||" + Tips: + - "&a圣泉 &7符文等级: &aIII" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.2&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + U: + Type: USE_GEM + Gem: 合成蓝色符文迅捷3 + Display: "&8&l&k||&9&l迅捷 III&8&l&k||" + Tips: + - "&9迅捷 &7符文等级: &9III" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.2&f%" + - "&7 - 闪避几率 &9+0.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + V: + Type: USE_GEM + Gem: 合成蓝色符文奔腾3 + Display: "&8&l&k||&9&l奔腾 III&8&l&k||" + Tips: + - "&9奔腾 &7符文等级: &9III" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + W: + Type: USE_GEM + Gem: 合成蓝色符文极寒3 + Display: "&8&l&k||&9&l极寒 III&8&l&k||" + Tips: + - "&9极寒 &7符文等级: &9III" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+3.2&f%" + - "&7 - 冰冻强度 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + X: + Type: USE_GEM + Gem: 合成蓝色符文雷霆3 + Display: "&8&l&k||&9&l雷霆 III&8&l&k||" + Tips: + - "&9雷霆 &7符文等级: &9III" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+3.0&f%" + - "&7 - 雷击伤害 &9+2.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Y: + Type: USE_GEM + Gem: 合成蓝色符文汲取3 + Display: "&8&l&k||&9&l汲取 III&8&l&k||" + Tips: + - "&9汲取 &7符文等级: &9III" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+3.0&f%" + - "&7 - 吸血抵抗 &9+2.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Z: + Type: USE_GEM + Gem: 合成蓝色符文穿杨3 + Display: "&8&l&k||&9&l穿杨 III&8&l&k||" + Tips: + - "&9穿杨 &7符文等级: &9III" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+4.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '@': + Type: USE_GEM + Gem: 合成蓝色符文毒刺3 + Display: "&8&l&k||&9&l毒刺 III&8&l&k||" + Tips: + - "&9毒刺 &7符文等级: &9III" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.4&f%" + - "&7 - 反弹伤害 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '$': + Type: USE_GEM + Gem: 合成蓝色符文幻影3 + Display: "&8&l&k||&9&l幻影 III&8&l&k||" + Tips: + - "&9幻影 &7符文等级: &9III" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '+': + Type: USE_GEM + Gem: 合成蓝色符文格挡3 + Display: "&8&l&k||&9&l格挡 III&8&l&k||" + Tips: + - "&9格挡 &7符文等级: &9III" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '*': + Type: USE_GEM + Gem: 合成蓝色符文盾山3 + Display: "&8&l&k||&9&l盾山 III&8&l&k||" + Tips: + - "&9盾山 &7符文等级: &9III" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '=': + Type: PAGE_JUMP + Display: "&c返回" + Material: IRON_DOOR + Gui: 符文合成台 + '?': + Type: EMPTY + +四级符文合成: + Locations: + Title: "&8&l&k||&9&l四级符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - ABCDEFGHI + - JKLMNOPQR + - SUVWXYZ@$ + - +*??????? + - TTTT=TTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击符文立即合成" + Tips: + - "&f花费 &6300 &f符文碎片" + - "&f即可合成指定符文" + A: + Type: USE_GEM + Gem: 合成红色符文强攻4 + Display: "&8&l&k||&c&l强攻 IV&8&l&k||" + Tips: + - "&c强攻 &7符文等级: &cIV" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+5.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + B: + Type: USE_GEM + Gem: 合成红色符文致命4 + Display: "&8&l&k||&c&l致命 IV&8&l&k||" + Tips: + - "&c致命 &7符文等级: &cIV" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+3.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + C: + Type: USE_GEM + Gem: 合成红色符文灾祸4 + Display: "&8&l&k||&c&l灾祸 IV&8&l&k||" + Tips: + - "&c灾祸 &7符文等级: &cIV" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+2.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + D: + Type: USE_GEM + Gem: 合成红色符文流光4 + Display: "&8&l&k||&c&l流光 IV&8&l&k||" + Tips: + - "&c流光 &7符文等级: &cIV" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + E: + Type: USE_GEM + Gem: 合成红色符文狂暴4 + Display: "&8&l&k||&c&l狂暴 IV&8&l&k||" + Tips: + - "&c狂暴 &7符文等级: &cIV" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+4.7&f%" + - "&7 - 暴击几率 &c+0.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + F: + Type: USE_GEM + Gem: 合成红色符文烈焰4 + Display: "&8&l&k||&c&l烈焰 IV&8&l&k||" + Tips: + - "&c烈焰 &7符文等级: &cIV" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+3.6&f%" + - "&7 - 燃烧伤害 &c+3.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + G: + Type: USE_GEM + Gem: 合成红色符文坚守4 + Display: "&8&l&k||&c&l坚守 IV&8&l&k||" + Tips: + - "&c坚守 &7符文等级: &cIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+2.2" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + H: + Type: USE_GEM + Gem: 合成红色符文重盾4 + Display: "&8&l&k||&c&l重盾 IV&8&l&k||" + Tips: + - "&c重盾 &7符文等级: &cIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + I: + Type: USE_GEM + Gem: 合成红色符文稳健4 + Display: "&8&l&k||&c&l稳健 IV&8&l&k||" + Tips: + - "&c稳健 &7符文等级: &cIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + J: + Type: USE_GEM + Gem: 合成红色符文神圣4 + Display: "&8&l&k||&c&l神圣 IV&8&l&k||" + Tips: + - "&c神圣 &7符文等级: &cIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+1.7&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + K: + Type: USE_GEM + Gem: 合成绿色符文锐利4 + Display: "&8&l&k||&a&l锐利 IV&8&l&k||" + Tips: + - "&a锐利 &7符文等级: &aIV" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+2.7&f%" + - "&7 - 物理伤害 &c+1.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + L: + Type: USE_GEM + Gem: 合成绿色符文看破4 + Display: "&8&l&k||&a&l看破 IV&8&l&k||" + Tips: + - "&a看破 &7符文等级: &aIV" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + M: + Type: USE_GEM + Gem: 合成绿色符文重挫4 + Display: "&8&l&k||&a&l重挫 IV&8&l&k||" + Tips: + - "&a重挫 &7符文等级: &aIV" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + N: + Type: USE_GEM + Gem: 合成绿色符文精准4 + Display: "&8&l&k||&a&l精准 IV&8&l&k||" + Tips: + - "&a精准 &7符文等级: &aIV" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + O: + Type: USE_GEM + Gem: 合成绿色符文重矢4 + Display: "&8&l&k||&a&l重矢 IV&8&l&k||" + Tips: + - "&a重矢 &7符文等级: &aIV" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+4.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + P: + Type: USE_GEM + Gem: 合成绿色符文聪慧4 + Display: "&8&l&k||&a&l聪慧 IV&8&l&k||" + Tips: + - "&a聪慧 &7符文等级: &aIV" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+8.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + Q: + Type: USE_GEM + Gem: 合成绿色符文长生4 + Display: "&8&l&k||&a&l长生 IV&8&l&k||" + Tips: + - "&a长生 &7符文等级: &aIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + R: + Type: USE_GEM + Gem: 合成绿色符文强健4 + Display: "&8&l&k||&a&l强健 IV&8&l&k||" + Tips: + - "&a强健 &7符文等级: &aIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.4" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + S: + Type: USE_GEM + Gem: 合成绿色符文圣泉4 + Display: "&8&l&k||&a&l圣泉 IV&8&l&k||" + Tips: + - "&a圣泉 &7符文等级: &aIV" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.25&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + U: + Type: USE_GEM + Gem: 合成蓝色符文迅捷4 + Display: "&8&l&k||&9&l迅捷 IV&8&l&k||" + Tips: + - "&9迅捷 &7符文等级: &9IV" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.4&f%" + - "&7 - 闪避几率 &9+0.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + V: + Type: USE_GEM + Gem: 合成蓝色符文奔腾4 + Display: "&8&l&k||&9&l奔腾 IV&8&l&k||" + Tips: + - "&9奔腾 &7符文等级: &9IV" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + W: + Type: USE_GEM + Gem: 合成蓝色符文极寒4 + Display: "&8&l&k||&9&l极寒 IV&8&l&k||" + Tips: + - "&9极寒 &7符文等级: &9IV" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+4.2&f%" + - "&7 - 冰冻强度 &9+4.9&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + X: + Type: USE_GEM + Gem: 合成蓝色符文雷霆4 + Display: "&8&l&k||&9&l雷霆 IV&8&l&k||" + Tips: + - "&9雷霆 &7符文等级: &9IV" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+3.6&f%" + - "&7 - 雷击伤害 &9+3.6" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Y: + Type: USE_GEM + Gem: 合成蓝色符文汲取4 + Display: "&8&l&k||&9&l汲取 IV&8&l&k||" + Tips: + - "&9汲取 &7符文等级: &9IV" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+3.6&f%" + - "&7 - 吸血抵抗 &9+3.6&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Z: + Type: USE_GEM + Gem: 合成蓝色符文穿杨4 + Display: "&8&l&k||&9&l穿杨 IV&8&l&k||" + Tips: + - "&9穿杨 &7符文等级: &9IV" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+5.4&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '@': + Type: USE_GEM + Gem: 合成蓝色符文毒刺4 + Display: "&8&l&k||&9&l毒刺 IV&8&l&k||" + Tips: + - "&9毒刺 &7符文等级: &9IV" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+1.8&f%" + - "&7 - 反弹伤害 &9+2.1&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '$': + Type: USE_GEM + Gem: 合成蓝色符文幻影4 + Display: "&8&l&k||&9&l幻影 IV&8&l&k||" + Tips: + - "&9幻影 &7符文等级: &9IV" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '+': + Type: USE_GEM + Gem: 合成蓝色符文格挡4 + Display: "&8&l&k||&9&l格挡 IV&8&l&k||" + Tips: + - "&9格挡 &7符文等级: &9IV" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '*': + Type: USE_GEM + Gem: 合成蓝色符文盾山4 + Display: "&8&l&k||&9&l盾山 IV&8&l&k||" + Tips: + - "&9盾山 &7符文等级: &9IV" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+1.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '=': + Type: PAGE_JUMP + Display: "&c返回" + Material: IRON_DOOR + Gui: 符文合成台 + '?': + Type: EMPTY + + +五级符文合成: + Locations: + Title: "&8&l&k||&9&l五级符文合成台&8&l&k||" + Page: 1 + Slots: + - TTTTTTTTT + - ABCDEFGHI + - JKLMNOPQR + - SUVWXYZ@$ + - +*??????? + - TTTT=TTTT + Items: + T: + Type: TIP + Material: BLACK_STAINED_GLASS_PANE + Display: "&9点击符文立即合成" + Tips: + - "&f花费 &61200 &f符文碎片" + - "&f即可合成指定符文" + A: + Type: USE_GEM + Gem: 合成红色符文强攻5 + Display: "&8&l&k||&c&l强攻 V&8&l&k||" + Tips: + - "&c强攻 &7符文等级: &c&lV" + - "&7装备: 武器, 属性:" + - "&7 - 物理伤害 &c+6.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + B: + Type: USE_GEM + Gem: 合成红色符文致命5 + Display: "&8&l&k||&c&l致命 V&8&l&k||" + Tips: + - "&c致命 &7符文等级: &c&lV" + - "&7装备: 武器, 属性:" + - "&7 - 真实伤害 &c+4.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + C: + Type: USE_GEM + Gem: 合成红色符文灾祸5 + Display: "&8&l&k||&c&l灾祸 V&8&l&k||" + Tips: + - "&c灾祸 &7符文等级: &c&lV" + - "&7装备: 武器, 属性:" + - "&7 - 暴击几率 &c+3.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + D: + Type: USE_GEM + Gem: 合成红色符文流光5 + Display: "&8&l&k||&c&l流光 V&8&l&k||" + Tips: + - "&c流光 &7符文等级: &c&lV" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢速度 &c+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + E: + Type: USE_GEM + Gem: 合成红色符文狂暴5 + Display: "&8&l&k||&c&l狂暴 V&8&l&k||" + Tips: + - "&c狂暴 &7符文等级: &c&lV" + - "&7装备: 武器, 属性:" + - "&7 - 暴击倍率 &c+5.0&f%" + - "&7 - 暴击几率 &c+0.8&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + F: + Type: USE_GEM + Gem: 合成红色符文烈焰5 + Display: "&8&l&k||&c&l烈焰 V&8&l&k||" + Tips: + - "&c烈焰 &7符文等级: &c&lV" + - "&7装备: 武器, 属性:" + - "&7 - 燃烧几率 &c+4.0&f%" + - "&7 - 燃烧伤害 &c+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + G: + Type: USE_GEM + Gem: 合成红色符文坚守5 + Display: "&8&l&k||&c&l坚守 V&8&l&k||" + Tips: + - "&c坚守 &7符文等级: &c&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 物理防御 &c+2.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + H: + Type: USE_GEM + Gem: 合成红色符文重盾5 + Display: "&8&l&k||&c&l重盾 V&8&l&k||" + Tips: + - "&c重盾 &7符文等级: &c&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 盔甲值 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + I: + Type: USE_GEM + Gem: 合成红色符文稳健5 + Display: "&8&l&k||&c&l稳健 V&8&l&k||" + Tips: + - "&c稳健 &7符文等级: &c&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 暴击抵抗 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + J: + Type: USE_GEM + Gem: 合成红色符文神圣5 + Display: "&8&l&k||&c&l神圣 V&8&l&k||" + Tips: + - "&c神圣 &7符文等级: &c&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 吸血抗性 &c+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU3MTdiNDg1ZDk1YjNiZDE0ZGIxMzUwYzU1YTI5ZGE5OWVlMWQzMTk1YzA1MDYxNzA4OGE5Njc3YWM0MiJ9fX0= + K: + Type: USE_GEM + Gem: 合成绿色符文锐利5 + Display: "&8&l&k||&a&l锐利 V&8&l&k||" + Tips: + - "&a锐利 &7符文等级: &a&lV" + - "&7装备: 武器, 属性:" + - "&7 - 护甲穿透 &a+3.0&f%" + - "&7 - 物理伤害 &c+1.8" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + L: + Type: USE_GEM + Gem: 合成绿色符文看破5 + Display: "&8&l&k||&a&l看破 V&8&l&k||" + Tips: + - "&a看破 &7符文等级: &a&lV" + - "&7装备: 武器, 属性:" + - "&7 - 破甲几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + M: + Type: USE_GEM + Gem: 合成绿色符文重挫5 + Display: "&8&l&k||&a&l重挫 V&8&l&k||" + Tips: + - "&a重挫 &7符文等级: &a&lV" + - "&7装备: 武器, 属性:" + - "&7 - 破盾几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + N: + Type: USE_GEM + Gem: 合成绿色符文精准5 + Display: "&8&l&k||&a&l精准 V&8&l&k||" + Tips: + - "&a精准 &7符文等级: &a&lV" + - "&7装备: 武器, 属性:" + - "&7 - 命中几率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + O: + Type: USE_GEM + Gem: 合成绿色符文重矢5 + Display: "&8&l&k||&a&l重矢 V&8&l&k||" + Tips: + - "&a重矢 &7符文等级: &a&lV" + - "&7装备: 弓, 属性:" + - "&7 - 箭矢穿透率 &a+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + P: + Type: USE_GEM + Gem: 合成绿色符文聪慧5 + Display: "&8&l&k||&a&l聪慧 V&8&l&k||" + Tips: + - "&a聪慧 &7符文等级: &a&lV" + - "&7装备: 武器, 属性:" + - "&7 - 经验加成 &a+10.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + Q: + Type: USE_GEM + Gem: 合成绿色符文长生5 + Display: "&8&l&k||&a&l长生 V&8&l&k||" + Tips: + - "&a长生 &7符文等级: &a&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命力 &a+2.0" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + R: + Type: USE_GEM + Gem: 合成绿色符文强健5 + Display: "&8&l&k||&a&l强健 V&8&l&k||" + Tips: + - "&a强健 &7符文等级: &a&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 生命恢复 &a+0.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + S: + Type: USE_GEM + Gem: 合成绿色符文圣泉5 + Display: "&8&l&k||&a&l圣泉 V&8&l&k||" + Tips: + - "&a圣泉 &7符文等级: &a&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 百分比回复 &a+0.3&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdhOTg1YzMxYWQ0NjFlMzIyYjM5YzQzM2VhZTc5ZmY0NmZiNjQ4MDBjZGVjYWRmMmY2ZDBmOTcyNzI1MiJ9fX0= + U: + Type: USE_GEM + Gem: 合成蓝色符文迅捷5 + Display: "&8&l&k||&9&l迅捷 V&8&l&k||" + Tips: + - "&9迅捷 &7符文等级: &9&lV" + - "&7装备: 武器, 属性:" + - "&7 - 移速加成 &9+1.6&f%" + - "&7 - 闪避几率 &9+1.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + V: + Type: USE_GEM + Gem: 合成蓝色符文奔腾5 + Display: "&8&l&k||&9&l奔腾 V&8&l&k||" + Tips: + - "&9奔腾 &7符文等级: &9&lV" + - "&7装备: 武器/靴子, 属性:" + - "&7 - 移速加成 &9+5.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + W: + Type: USE_GEM + Gem: 合成蓝色符文极寒5 + Display: "&8&l&k||&9&l极寒 V&8&l&k||" + Tips: + - "&9极寒 &7符文等级: &9&lV" + - "&7装备: 武器, 属性:" + - "&7 - 冰冻几率 &9+5.0&f%" + - "&7 - 冰冻强度 &9+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + X: + Type: USE_GEM + Gem: 合成蓝色符文雷霆5 + Display: "&8&l&k||&9&l雷霆 V&8&l&k||" + Tips: + - "&9雷霆 &7符文等级: &9&lV" + - "&7装备: 武器, 属性:" + - "&7 - 雷击几率 &9+4.0&f%" + - "&7 - 雷击伤害 &9+4.5" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Y: + Type: USE_GEM + Gem: 合成蓝色符文汲取5 + Display: "&8&l&k||&9&l汲取 V&8&l&k||" + Tips: + - "&9汲取 &7符文等级: &9&lV" + - "&7装备: 武器, 属性:" + - "&7 - 吸血几率 &9+4.0&f%" + - "&7 - 吸血抵抗 &9+4.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + Z: + Type: USE_GEM + Gem: 合成蓝色符文穿杨5 + Display: "&8&l&k||&9&l穿杨 V&8&l&k||" + Tips: + - "&9穿杨 &7符文等级: &9&lV" + - "&7装备: 弓, 属性:" + - "&7 - 箭术精准 &9+6.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '@': + Type: USE_GEM + Gem: 合成蓝色符文毒刺5 + Display: "&8&l&k||&9&l毒刺 V&8&l&k||" + Tips: + - "&9毒刺 &7符文等级: &9&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 反弹几率 &9+2.0&f%" + - "&7 - 反弹伤害 &9+2.5&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '$': + Type: USE_GEM + Gem: 合成蓝色符文幻影5 + Display: "&8&l&k||&9&l幻影 V&8&l&k||" + Tips: + - "&9幻影 &7符文等级: &9&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 闪避几率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '+': + Type: USE_GEM + Gem: 合成蓝色符文格挡5 + Display: "&8&l&k||&9&l格挡 V&8&l&k||" + Tips: + - "&9格挡 &7符文等级: &9&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 盾牌格挡率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '*': + Type: USE_GEM + Gem: 合成蓝色符文盾山5 + Display: "&8&l&k||&9&l盾山 V&8&l&k||" + Tips: + - "&9盾山 &7符文等级: &9&lV" + - "&7装备: 盔甲, 属性:" + - "&7 - 箭伤免疫率 &9+2.0&f%" + Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmJiZTQxYmYyM2MzY2RmZTNkZGVkNDIyZjQ4NDQ1MzExNDgxM2UxMmY3YjZmODU1OTM1NTJhNTVjMDgwM2MifX19 + '=': + Type: PAGE_JUMP + Display: "&c返回" + Material: IRON_DOOR + Gui: 符文合成台 + '?': + Type: EMPTY diff --git a/src/main/resources/languages/en_US.yml b/src/main/resources/languages/en_US.yml new file mode 100644 index 0000000..3fe3278 --- /dev/null +++ b/src/main/resources/languages/en_US.yml @@ -0,0 +1,291 @@ +version: 1.10 +plugin: + prefix: "&9&l[&e&lMagicGem&9&l]&r" + read_file: "&eReading " + dupe_gem: "&cDuplicated gems find! Name: " + extract: "&eExtracting resource files" + build_error: "&cError: Failed to load class builder" + build_no_dependency: "&cError: A dependency is lacking, some features are disabled." + no_license: "&cThe plugin has not been licensed yet. Please check the console." + connect_failed: "&cLicense server connecting failed" + ip_deny: "&cThe IP address is not owned by you (please also check your username)!" + port_deny: "&cYour port is not licensed. Please buy more ports." + accept: "&cPlugin licensed. Thank you for purchasing!" + busy: "&cThe license server is busy. Will try again later" + wrong_info: "&cLicense request failed, plugin file may be corrupted" + wrong_user: "&cPlease specify correct account in MagicGem/config.yml (For example, user: Bob), and then use /mgem reload." + help: "&aFor support, please contact QQ: 386029724 or email liyi20129@gmail.com" +gem: + tip: + unable: "&cYour equipment doesn't meet certain conditions" + wrong_type: "&cYour equipment type is not acceptable." + embed_limit: "&cYou have added too much gem to the item" + success: "&aSuccessfully added gem!" + fail: "&cYou have failed to add gem to your item" + break: "&cYour gem broke" + downgrade: "&c&lYour item downgraded" + multi_equip: "&cYou cannot add gem to more than 1 item!" + downgrade_disable: "&cWarning: Reward cannot be cancelled, downgrade disabled" + invalid_reward: "&cWarning: Reward loading failed" + empty_reward: "&cWarning: Empty reward" + invalid_embed: "&cWarning: This reward cannot be canceled" + econ_dupe: "&cWarning: Embed gems cannot have positive (in total) economic rewards" + random_conflict: "&cError: Random gems cannot contain both player gem(s) and item gem(s)" + random_no_gem: "&cError: This random gem doesn't contain gems" + info: + name: "Gem Name" + type: "Type" + item: "Gem for items" + player: "Gem for players" + item_random: "Random gem for items" + player_random: "Random gem for players" + embed: "Embed" + consume: "&cPermanent" + use: "Right-click to use" + eat: "&aCan eat" + success: "Success Probability" + break: "Broke Probability" + downgrade: "Downgrade Probability" + types: "Available Types" + guis: "Available GUIs" + reward: "Rewards" + condition: "Conditions" + ignorable: "Ignore when failed" + info: "&bHidden Information" + get: "&aClick to get the real gem" + gui: + admin: "&8&l&k||&b&lAdmin Gem List&8&l&k||" + player: "&8&l&k||&b&lGem Preview&8&l&k||" + prev: "&8Previous Page" + next: "&8Next Page" + embed: + title: "&8&l&k||&b&lEmbed&8&l&k||" + tip: "&b&lPut your equipment in the middle slot, left-click to embed, right-click to undo" + success: "&aGem removed successfully!" + fail: "&cFailed to remove the gem, it has damaged!" + type_error: "&cThis type of gem cannot be used in this embed table!" + slot_error: "&cThis type of gem cannot be used in this slot!" + not_found: "&cThe specified gem embedder doesn't exist" + gem_not_found: "&cWarning: The UseGem button points to an invalid gem" + no_equip_slot: "&cWarning: Embedder doesn' have an equip slot" + multi_equip_slot: "&cWarning: Embedder cannot have multiple equip slots" +common: + type: "Type" + name: "Name" + amount: "Amount" + level: "Level" + upper: "Upper Limit" + range: "Range" + expression: "Expression" + inv_expression: "Inv-Expression" + null_expression: "&cWarning: Null expression" + invalid_range: "&cWarning: Invalid range" + invalid_texture: "&cWarning: Invalid skull texture" + invalid_material: "&cWarning: Invalid material name" + use: "On Use" + undo: "On Cancel" +reward: + common: + override: "Override origin value" + before_success: "Before Success" + after_success: "After Success" + ability: + type: "MMOItems Ability" + attribute: + type: "AttributeModifier" + unknown: "Unknown attribute" + unknown_slot: "&cUnknown Slot" + command: + type: "Command" + console: "Run with console" + player: "Run by player" + empty: "&cWarning: Empty Command" + conditional: + type: "Conditional Reward" + condition: "Condition" + reward: "Reward" + destroy: + type: "&cDestroy target item" + durability: + type: "Durability" + invalid: "&cWarning: Invalid durability amount" + enchant: + type: "Enchantment" + unknown: "&cUnknown Enchantment" + empty: + type: "&cEmpty" + exp: + type_level: "EXP Level" + invalid: "Invalid exp amount" + poor: "&cYou don't have enough EXP" + heal: + type: "Health Value" + item: + give: "Give items" + take: "Consume items" + set: "Set to item" + edit: "Item edit" + edit_slot: "Slot" + material: "Material name" + displayname: "Display name" + poor: "&cYou don't have enough materials" + lore: + add: "Add Lore" + first: "First line" + last: "Last line" + before: "Before" + after: "After" + line: "Single Line Edit" + all: "All" + del: "&cDelete Lore" + replace: "Replace Lore" + var: "Lore Variable Edit" + var_name: "Variable name" + invalid: "&cInvalid lore" + invalid_format: "&cWarning: Invalid format" + format: "Format" + roman: "Roman number" + locator: "Lore locator" + material: + type: "Change Material" + maxhealth: + type: "Max Health" + poor: "&cYour max health is over the range" + invalid: "&cInvalid max health amount" + message: + chat: "Chat Message" + title: "Title" + actionbar: "Action Bar" + content: "Contents" + mmitem: + type: "Change to MythicMobs Item" + disable: "&cWarning: Neither of MythicArtifacts or MythicCrucible is found, MMItem reward is disabled" + invalid: "&cWarning: Specified MythicMobs item doesn't exist in MythicMobs item storage" + money: + type: "Money" + disable: "&cWarning: Vault not found, money rewards were disabled" + invalid: "&cInvalid money amount" + poor: "&cYou don't have enough money" + spend: "&cYou paid &e%.2f &cgold!" + get: "&fYou get &e%.2f &fgold!" + name: + type: "New Name" + nbt_double: + type: "NBT Double" + nbt_string: + type: "NBT String" + remove: "&cRemove" + content: "Content" + point: + type: "PlayerPoints" + disable: "&cWarning: PlayerPoints not found, point rewards were disabled" + invalid: "Invalid point amount" + poor: "&cYou don't have enough points" + spend: "&cYou paid &9%d&c points!" + get: "&fYou got &9%d&f points!" + potion: + type: "Potion Effect" + duration: "Duration" + skill: + add: "Add MagicGem Skill" + change: "Change MagicGem Skill" + remove: "Remove MaticGem Skill" + unbreakable: + type: "Unbreakable" + upgrade: + type: "Gem Embed Override" + list: "Override List" +condition: + common: + invalid: "&cWarning: Invalid condition" + invempty: + type: "Required Empty Slot" + poor: "&cYou don't have enough empty slots" + invalid: "&cInvalid empty slot amount" + permission: + type: "Permission Requirement" + poor: "&cYou don't have required permission" + has: "&cYou already have required permission" + papi: + type: "PlaceHolderAPI Check" + disable: "&cWarning: PlaceHolderAPI not found, PAPI checks disabled" +skill: + common: + disable: "&cSkill engine is disabled" + invalid: "&cWarning: Invalid skill" + invalid_target: "&cWarning: Invalid target" + cooldown_tip: "&cSkill Cooldown" + mm_disable: "&cWarning: MythicMobs not found, MythicSkill is not supported" + potion: + invalid: "&cWarning: Invalid potion" + mmskill: + load_error: "&cWarning: MythicMobs skill loading error" + debuff: + invalid: "&cWarning: Invalid de-buff type" +command: + no_permission: "&cYou have no permissions to run the command" + player_only: "&cOnly players can use the command" + player_not_found: "The specified player is not online or doesn't exist" + reload_all: "&aAll config files have been successfully reloaded" + reload_skill: "&aSkill config files have been successfully reloaded" + reload_gem: "&aGem config files have been successfully reloaded" + invalid_args: "&cInvalid argument size" + invalid_amount: "&cThe specified amount is out of range [1, 64]" + gem_not_found: "&cThe specified gem cannot be found" + give_gem: "&fYou received %d %s" + full_inventory: "&cYour inventory has no empty slot. The item is spawned at your location" + nbt_not_found: "The specified NBT tag cannot be found" + nbt_type: "NBT type" + nbt_name: "name" + nbt_value: "value" + no_nbt: "&cThe item has no NBT tags" + no_meta: "&cThe item has no ItemMeta" + no_lore: "&cThe item has no Lore" + no_enchant: "&cThe item has no enchantment" + enchant_list: "&cEnchantment List" + embed: + addblock: "&aAdd Embed GUI successfully" + delblock: "&aRemove Embed GUI successfully" + save_error: "&cError saving data" + add_fail: "&c Add failed! Please check the block position" + del_fail: "&cDelete failed, the specified Embed GUI block doesn't exist" + help: + reload: "&e/mgem reload (gem|skill) - Reload config files" + apply: "&e/mgem apply (slot) - Apply a gem to a player's equip slot (default mainhand)" + give: "&e/mgem give (amount) - Give a player some gems (default 1 gem)" + signal: "&e/mgem signal (player|@all) (signal info) - Trigger signal skill of a player or all players" + get: "&e/mgem get - Open the admin GUI to preview gems with hidden information" + view: "&e/mgem view - Open the player preview GUI" + embed: "&e/mgem embed - Open Embed GUi" + embed_addblock: "&e/mgem embed addblock (guiName) - Add the block under your feet to Embed GUI block" + embed_delblock: "&e/mgem embed delblock - Del the Embed GUI block under your feet" + enchant: "&e/mgem enchant - List the enchants of your main hand item" + lore: "&e/mgem lore - List the lores of your main hand item" + material: "&e/mgem material - show the material (and ID) of your main hand item" + nbt: "&e/mgem nbt - List the NBT tags and types of your main hand item. When empty, it list your player nbt" + nbt_read: "&e/mgem nbt - Read the NBT value of the specified key" + tab_tip: " &ePress TAB, use shift and copy to clone the read values" +cmagic: + cooldown: "&eCooldown: {seconds} seconds remaining" + condition_parse: "&cFailed to parse condition: {condition}" + condition_failed: "&cCondition not met: {condition}" + use_cancelled: "&cGem use was cancelled by another plugin" + remove_cancelled: "&cGem removal was cancelled by another plugin" + random_invalid: "&cThe random gem has no valid target" + random_cycle: "&cRandom gems cannot contain circular references" + gem_not_found: "&cUnknown gem: {gem}" + not_embedded: "&cThat gem is not embedded in this item" + command: + reload: "&aCMagicGem reloaded safely; {amount} gems loaded." + give: "&aGave {player} {amount} {gem}" + list: "&6Loaded gems (&e{amount}&6):" + signal_all: "&aSent the skill signal to every online player." + signal_player: "&aSent the skill signal to {player}." + empty_hand: "&cYour main hand is empty" + gui: + back: "&cBack to configuration files" + page: "&fPage {page} / {pages}" + gem_count: "&7Gems: &f{amount}" + browse: "&aClick to browse" + slot_rejected: "&cThat item cannot be placed in this equipment slot." + equipment_required: "&cPlace equipment in the slot first." diff --git a/src/main/resources/languages/zh_CN.yml b/src/main/resources/languages/zh_CN.yml new file mode 100644 index 0000000..a01e115 --- /dev/null +++ b/src/main/resources/languages/zh_CN.yml @@ -0,0 +1,291 @@ +version: 1.10 +plugin: + prefix: "&9&l[&e&l魔术宝石&9&l]&r" + read_file: "&e读取" + dupe_gem: "&c发现重复的宝石: " + extract: "&e抽取资源文件" + build_error: "&c错误: 构建器加载失败" + build_no_dependency: "&c警告: 您缺少了下列依赖,某些功能将不起效果" + no_license: "&c插件尚未认证,请检查控制台输出" + connect_failed: "&c验证服务器连接失败" + ip_deny: "&c这个IP地址尚未购买, 或不属于您(请注意config.yml中的user是否填对!)" + port_deny: "&c您的端口尚未购买, 请购买端口(享受折扣)" + accept: "&a插件成功认证, 感谢您对作者劳动的支持!" + wrong_info: "&c验证发生未知错误, 插件损坏或不受支持" + wrong_user: "&c请在插件config.yml中配置你的用户名(比如user: ZhangSan), 然后输入/mgem reload" + help: "&a更多支持请联系QQ: 386029724 或 邮件 liyi20129@gmail.com" + busy: "&e验证服务器繁忙, 将稍后再试" +gem: + tip: + unable: "&c强化失败, 未满足强化条件" + wrong_type: "&c物品类型不正确" + embed_limit: "&c宝石镶嵌数量达到上限" + success: "&a强化成功!" + fail: "&c强化失败!" + break: "&c宝石破碎!" + downgrade: "&c装备降级!" + multi_equip: "&c你不能同时对多个物品使用宝石!" + downgrade_disable: "&c警告: 奖励不能取消, 宝石降级率强制归零" + invalid_reward: "&c警告: 奖励未能读取成功" + empty_reward: "&c警告: 宝石奖励为空" + invalid_embed: "&c警告:奖励不可拆卸, 宝石设为不可拆卸" + econ_dupe: "&c警告: 可拆卸宝石不能有总价值为正的奖励" + random_conflict: "&c错误: 随机宝石不能同时包含物品和玩家宝石" + random_no_gem: "&c错误: 该随机宝石没有包含任何宝石" + info: + name: "名称" + type: "类型" + item: "物品强化" + player: "玩家强化" + item_random: "随机物品强化" + player_random: "随机玩家强化" + embed: "&a可镶嵌" + consume: "&c一次性" + use: "右键使用" + eat: "&a可食用" + success: "成功率" + downgrade: "降级率" + break: "破碎率" + types: "可强化物品列表" + guis: "可使用的镶嵌台" + reward: "强化奖励" + condition: "强化条件" + ignorable: "允许失败" + info: "&b宝石内部信息" + get: "&a单击获得宝石实物" + gui: + admin: "&8&l&k||&b&l管理员-宝石列表&8&l&k||" + player: "&8&l&k||&b&l宝石预览&8&l&k||" + prev: "&8上一页" + next: "&8下一页" + embed: + title: "&8&l&k||&b&l宝石镶嵌台&8&l&k||" + tip: "&b&l在中央放入装备, 左键镶嵌, 右键拆除" + success: "&a宝石成功拆除!" + fail: "&c宝石拆除失败, 该宝石已经失效!" + type_error: "&c该类型的宝石不能在本台上使用!" + slot_error: "&c该类型的宝石不能在该格子中使用!" + not_found: "&c指定的镶嵌台不存在" + gem_not_found: "&c警告: 使用宝石的按钮未能找到指定的宝石" + no_equip_slot: "&c警告: 镶嵌台不能没有装备格子" + multi_equip_slot: "&c警告: 镶嵌台不能有两个放装备的格子, 已删去多余格子" +common: + type: "类型" + name: "名称" + amount: "数量" + level: "等级" + upper: "上限" + range: "范围" + expression: "表达式" + inv_expression: "逆表达式" + null_expression: "&c警告: 表达式为空" + invalid_range: "&c警告: 上下限范围不正确" + invalid_texture: "&c警告: 加载头颅贴图时出错" + invalid_material: "&c警告: 加载材料时出错" + use: "使用时" + undo: "拆卸时" +reward: + common: + override: "强制覆盖" + before_success: "成功前" + after_success: "成功后" + ability: + type: "MMOItems技能" + attribute: + type: "原版属性" + unknown: "&c未知属性!" + unknown_slot: "&c未知装备格" + command: + type: "指令" + console: "用控制台执行" + player: "玩家执行" + empty: "&c警告: 指令为空" + conditional: + type: "条件奖励" + condition: "&c条件" + reward: "&a奖励" + destroy: + type: "&c破坏该物品" + durability: + type: "耐久度恢复" + invalid: "&c警告: 耐久为零" + enchant: + type: "附魔" + unknown: "&c未知附魔" + empty: + type: "空奖励" + exp: + type_level: "经验等级" + invalid: "无效经验数目" + poor: "&c你没有足够的经验" + heal: + type: "生命值" + item: + give: "获取物品" + take: "消耗物品" + edit: "物品编辑" + edit_slot: "槽位" + set: "设置为物品" + material: "材料名" + displayname: "显示名" + poor: "&c您没有足够的物品可供消耗" + lore: + add: "新增Lore" + first: "第一行" + last: "最后一行" + before: "在前" + after: "在后" + line: "单行操作" + all: "全部" + del: "&c删除Lore" + replace: "替换Lore" + var: "Lore变量编辑" + var_name: "变量名" + invalid: "&c无效Lore条目" + invalid_format: "&c警告: format格式不正确" + format: "格式" + roman: "罗马数字" + locator: "定位Lore" + material: + type: "材料修改" + maxhealth: + type: "最大生命" + poor: "&c你的最大生命值不在允许的范围" + invalid: "&c无效的最大生命点数" + message: + chat: "聊天框信息" + title: "全屏标题" + actionbar: "动作栏信息" + content: "内容" + mmitem: + type: "修改为MythicMobs物品" + disable: "&c警告: 未安装MythicArtifacts/MythicCrucible之一, MMItem奖励已经禁用" + invalid: "&c警告: 指定的MythicMobs物品在MythicMobs武器库中不存在" + money: + type: "金币" + disable: "&c警告: 未检测到Vault插件, 金币相关已禁用" + invalid: "&c无效金币数目" + poor: "&c您没有足够的金币" + spend: "&c您花费了&e%.2f&c金币!" + get: "&f您获得了&e%.2f&f金币!" + name: + type: "新名称" + nbt_double: + type: "NBT浮点数" + nbt_string: + type: "NBT字符串" + remove: "&c移除" + content: "内容" + point: + type: "点券" + disable: "&c警告: 未检测到PlayerPoint插件, 点券相关已禁用" + invalid: "无效点券数目" + poor: "&c您没有足够的点券" + spend: "&c您花费了&9%d&c点券!" + get: "&f您获得了&9%d&f点券!" + potion: + type: "药水效果" + duration: "持续时间" + skill: + add: "添加技能" + change: "修改技能" + remove: "移除技能" + unbreakable: + type: "不可破坏" + upgrade: + type: "宝石镶嵌覆盖" + list: "覆盖列表" +condition: + common: + invalid: "&cWarning: Invalid condition" + invempty: + type: "背包空格数目条件" + poor: "&c所需背包空间不足" + invalid: "&c无效背包空格数目" + papi: + type: "PlaceholderAPI条件" + disable: "&c警告: 未找到PlaceholderAPI, PAPI条件检查已禁用" + permission: + type: "权限节点条件" + poor: "&c您没有所需的权限" + has: "&c您已经拥有所需权限" +skill: + common: + disable: "&c宝石技能引擎处于禁用状态" + invalid: "&c警告: 无效技能" + invalid_target: "&c警告: 目标类型错误" + cooldown_tip: "&c技能冷却进度" + mm_disable: "&c警告: MythicMobs未安装, 技能引擎将忽略MM技能" + potion: + invalid: "&c警告: 无效药水" + mmskill: + load_error: "&c警告: MythicMobs技能加载错误" + debuff: + invalid: "&c警告: 无效DEBUFF类型" +command: + no_permission: "&c你没有使用该命令的权限" + player_only: "&c玩家才能使用本命令" + player_not_found: "指定的玩家不存在或不在线" + reload_all: "&a全部配置已经重载" + reload_skill: "&a技能配置已经重载" + reload_gem: "&a宝石配置已经重载" + invalid_args: "&c参数数量不正确" + invalid_amount: "&c指定的数量不在[1, 64]范围内, 已重置" + gem_not_found: "&c指定的宝石不存在" + give_gem: "&f你收到了%d个%s" + full_inventory: "&c你的背包空间不足, 已将宝石置于脚下" + nbt_not_found: "指定的NBT属性不存在" + nbt_type: "NBT类型" + nbt_name: "名称" + nbt_value: "值" + no_lore: "&c该物品没有Lore" + no_nbt: "&c该物品没有NBT标签" + no_meta: "&c该物品没有Meta" + no_enchant: "&c该物品没有附魔" + enchant_list: "&c附魔列表" + embed: + addblock: "&a成功添加脚下方块为镶嵌台" + delblock: "&a成功删除脚下镶嵌台" + save_error: "&c配置文件存储错误" + add_fail: "&c添加失败, 方块为空或镶嵌台已存在" + del_fail: "&c删除失败, 镶嵌台不存在" + help: + reload: "&e/mgem reload (gem|skill)- 重载配置全部/宝石/技能文件" + apply: "&e/mgem apply <玩家> <宝石名> (槽位) - 给玩家的指定装备位置使用1颗宝石(默认主手),或直接使用玩家宝石" + give: "&e/mgem give <玩家> <宝石名> (数量) - 给玩家指定数量的某种宝石(默认1个)" + signal: "&e/mgem signal (玩家|@all) (信号内容) - 触发全体玩家或某个玩家的信号技能" + get: "&e/mgem get - 打开管理员GUI, 查看说明并获取宝石" + view: "&e/mgem view - 打开宝石预览页面" + embed: "&e/mgem embed - 打开镶嵌台, 镶嵌或拆卸装备宝石" + embed_addblock: "&e/mgem embed addblock (镶嵌台名) - 指定脚下方块为镶嵌台" + embed_delblock: "&e/mgem embed delblock - 删除脚下的镶嵌台方块" + enchant: "&e/mgem enchant - 列出主手中物品的附魔(包括插件附魔)" + lore: "&e/mgem lore - 列出主手中物品的Lore" + material: "&e/mgem material - 显示主手物品的材料名和子ID" + nbt: "&e/mgem nbt - 列出主手中物品的NBT标签和类型 (空手会显示玩家自身nbt)" + nbt_read: "&e/mgem nbt <属性名> - 读取主手物品的指定属性。" + tab_tip: " &e部分功能可以用Tab读到输入栏直接复制(颜色代码会转换成&)" +cmagic: + cooldown: "&e冷却中,剩余 {seconds} 秒" + condition_parse: "&c条件解析失败: {condition}" + condition_failed: "&c条件不满足: {condition}" + use_cancelled: "&c宝石使用已被其他插件取消" + remove_cancelled: "&c宝石拆卸已被其他插件取消" + random_invalid: "&c随机宝石没有有效目标" + random_cycle: "&c随机宝石不能循环引用" + gem_not_found: "&c未知宝石: {gem}" + not_embedded: "&c装备未镶嵌该宝石" + command: + reload: "&aCMagicGem 已安全重载,共加载 {amount} 颗宝石。" + give: "&a已给予 {player} {amount} 个 {gem}" + list: "&6已加载宝石 (&e{amount}&6):" + signal_all: "&a已向所有在线玩家发送技能信号。" + signal_player: "&a已向 {player} 发送技能信号。" + empty_hand: "&c主手没有物品" + gui: + back: "&c返回配置文件列表" + page: "&f第 {page} / {pages} 页" + gem_count: "&7宝石数量: &f{amount}" + browse: "&a点击浏览" + slot_rejected: "&c该物品不能放入这个装备槽。" + equipment_required: "&c请先放入装备。" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..0d9da1a --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,19 @@ +name: CMagicGem +version: '${version}' +description: Paper 1.21.x compatible MagicGem reimplementation +main: cn.infstar.cmagicgem.CMagicGem +api-version: '1.21' +authors: [ Coldsmile_7 ] +softdepend: [PlaceholderAPI, Vault, PlayerPoints, MythicMobs, MMOItems, MythicArtifacts, MythicCrucible, Residence, PvPManager, EcoEnchants, GoldenEnchants] +commands: + cmgem: + description: CMagicGem main command + usage: /cmgem help + aliases: [cmg] +permissions: + cmagicgem.command.admin: + default: op + cmagicgem.command.view: + default: op + cmagicgem.command.embed: + default: op diff --git a/src/main/resources/skills/DefaultBuffs.yml b/src/main/resources/skills/DefaultBuffs.yml new file mode 100644 index 0000000..8e38f95 --- /dev/null +++ b/src/main/resources/skills/DefaultBuffs.yml @@ -0,0 +1,149 @@ +# 不写nbt时, 技能名将作为默认的nbt识别名 +# 如果一个技能同时还有匹配的NBT技能, 则两者能够用宝石互相转换 +发光: + Lore: "&6&l[&c&lBUFF&6&l] &b&l发光" + Skills: + - PotionBuff{type=GLOWING} ~onTimer @Self $example-world:1 + +缓降: + Lore: "&6&l[&c&lBUFF&6&l] &b&l缓降" + Skills: + - PotionBuff{type=SLOW_FALLING} ~onTimer + +潮涌能量: + Lore: "&6&l[&c&lBUFF&6&l] &b&l潮涌能量" + Skills: + - PotionBuff{type=CONDUIT_POWER} ~onTimer +夜视: + Lore: "&6&l[&c&lBUFF&6&l] &b&l夜视" + Skills: + - PotionBuff{type=NIGHT_VISION;duration=30;refresh=15} ~onTimer + +饱和: + Lore: "&6&l[&c&lBUFF&6&l] &b&l饱和" + Skills: + - PotionBuff{type=SATURATION} ~onTimer + +隐身: + Lore: "&6&l[&c&lBUFF&6&l] &b&l隐身" + Skills: + - PotionBuff{type=INVISIBILITY} ~onTimer + +防火: + Lore: "&6&l[&c&lBUFF&6&l] &b&l防火" + Skills: + - PotionBuff{type=FIRE_RESISTANCE} ~onTimer + +不详之兆: + Lore: "&6&l[&c&lBUFF&6&l] &b&l不祥之兆" + Skills: + - PotionBuff{type=BAD_OMEN} ~onTimer + +伤害吸收: + Lore: "&6&l[&c&lBUFF&6&l] &b&l伤害吸收 " + Skills: + - PotionBuff{type=ABSORPTION;duration=60;level=$LORE:[BUFF] 伤害吸收?1$} ~onTimer + +抗性提升: + Lore: "&6&l[&c&lBUFF&6&l] &b&l抗性提升 " + Skills: + - PotionBuff{type=DAMAGE_RESISTANCE;level=$LORE:[BUFF] 抗性提升?1$} ~onTimer + +海豚的恩惠: + Lore: "&6&l[&c&lBUFF&6&l] &b&l海豚的恩惠 " + Skills: + - PotionBuff{type=DOLPHINS_GRACE;level=$LORE:[BUFF] 海豚的恩惠?1$} ~onTimer + +急迫: + Lore: "&6&l[&c&lBUFF&6&l] &b&l急迫 " + Skills: + - PotionBuff{type=FAST_DIGGING;level=$LORE:[BUFF] 急迫?1$} ~onTimer + +生命提升: + Lore: "&6&l[&c&lBUFF&6&l] &b&l生命提升 " + Skills: + - PotionBuff{type=HEALTH_BOOST;level=$LORE:[BUFF] 生命提升?1$;duration=480} ~onTimer + +村庄英雄: + Lore: "&6&l[&c&lBUFF&6&l] &b&l村庄英雄 " + Skills: + - PotionBuff{type=HERO_OF_THE_VILLAGE;level=$LORE:[BUFF] 村庄英雄?1$} ~onTimer + +力量: + Lore: "&6&l[&c&lBUFF&6&l] &b&l力量 " + Skills: + - PotionBuff{type=INCREASE_DAMAGE;level=$LORE:[BUFF] 力量?1$} ~onTimer + +跳跃提升: + Lore: "&6&l[&c&lBUFF&6&l] &b&l跳跃提升 " + Skills: + - PotionBuff{type=JUMP;level=$LORE:[BUFF] 跳跃提升?1$} ~onTimer + +幸运: + Lore: "&6&l[&c&lBUFF&6&l] &b&l幸运 " + Skills: + - PotionBuff{type=LUCK;level=$LORE:[BUFF] 幸运?1$} ~onTimer + + +生命恢复: + Lore: "&6&l[&c&lBUFF&6&l] &b&l生命恢复 " + Skills: + - PotionBuff{type=REGENERATION;level=$LORE:[BUFF] 生命恢复?1$} ~onTimer + +速度: + Lore: "&6&l[&c&lBUFF&6&l] &b&l速度 " + Skills: + - PotionBuff{type=SPEED;level=$LORE:[BUFF] 速度?1$} ~onTimer + +水下呼吸: + Lore: "&6&l[&c&lBUFF&6&l] &b&l水下呼吸 " + Skills: + - PotionBuff{type=WATER_BREATHING;level=$LORE:[BUFF] 水下呼吸?1$} ~onTimer + +NBT伤害吸收: + Skills: + - PotionBuff{type=ABSORPTION;duration=60;level=$NBT:伤害吸收?1$} ~onTimer + +NBT抗性提升: + Skills: + - PotionBuff{type=DAMAGE_RESISTANCE;level=$NBT:抗性提升?1$} ~onTimer + +NBT海豚的恩惠: + Skills: + - PotionBuff{type=DOLPHINS_GRACE;level=$NBT:海豚的恩惠?1$} ~onTimer + +NBT急迫: + Skills: + - PotionBuff{type=FAST_DIGGING;level=$NBT:急迫?1$} ~onTimer + +NBT生命提升: + Skills: + - PotionBuff{type=HEALTH_BOOST;level=$NBT:生命提升?1$;duration=480} ~onTimer + +NBT村庄英雄: + Skills: + - PotionBuff{type=HERO_OF_THE_VILLAGE;level=$NBT:村庄英雄?1$} ~onTimer + +NBT力量: + Skills: + - PotionBuff{type=INCREASE_DAMAGE;level=$NBT:力量?1$} ~onTimer + +NBT跳跃提升: + Skills: + - PotionBuff{type=JUMP;level=$NBT:跳跃提升?1$} ~onTimer + +NBT幸运: + Skills: + - PotionBuff{type=LUCK;level=$NBT:幸运?1$} ~onTimer + +NBT生命恢复: + Skills: + - PotionBuff{type=REGENERATION;level=$NBT:生命恢复?1$} ~onTimer + +NBT速度: + Skills: + - PotionBuff{type=SPEED;level=$NBT:速度?1$} ~onTimer + +NBT水下呼吸: + Skills: + - PotionBuff{type=WATER_BREATHING;level=$NBT:水下呼吸?1$} ~onTimer diff --git a/src/main/resources/skills/DefaultMMSkills.yml b/src/main/resources/skills/DefaultMMSkills.yml new file mode 100644 index 0000000..d4ab390 --- /dev/null +++ b/src/main/resources/skills/DefaultMMSkills.yml @@ -0,0 +1,219 @@ +depend: MythicMobs +刀: + Skills: + - effect:particles{p=witchMagic;a=1} @origin +斩: + Skills: + - effect:sound{s=block.fire.ambient;v=5;p=1} @Self + - damage{amount=70;ia=true;i=false} + - effect:particles{p=largeexplode;a=100} @Target +刀斩: + Cooldown: 5 + Skills: + - missile{ot=刀;oh=斩;v=5;i=0.01;hR=0.5;vR=0.5;} +拔刀: + Skills: + - effect:particles{p=sweepAttack;a=2;hs=0.2;vs=0.2;speed=2} @origin +拔刀2: + Skills: + - effect:particles{particle=dragonbreath;amount=30;hS=0.05;vS=0.05;speed=0.2} @origin + - damage{amount=75;i=false} + - effect:explosion +拔刀斩: + Cooldown: 10 + Skills: + - message{m="&8[&e技能提示&8]&f玩家 &6 &f发动了技能 &c拔刀斩 &f!"} @PlayersInRadius{r=20;targetself=true} + - delay 30 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 +拔刀斩2: + Cooldown: 5 + Skills: + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 + - missile{ot=拔刀;oh=拔刀2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 9 +范围切割斩: + Cooldown: 10 + Skills: + - message{m="&8[&e技能提示&8]&f玩家 &6 &f发动了技能 &c切割斩 &f!"} @PlayersInRadius{r=20;targetself=true} + - effect:particletornado{p=totem;mr=2;h=1;i=1;d=60;rs=1;sh=1;cs=0;ca=1;chs=1;cvs=1;cps=0.3;cyo=0.2} @Self + - message{m="&8[&e技能提示&8]&f你爆发了&b&l特殊能力 &c&n狂热 可以快速回血 &f!"} @Self + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - heal{amount=15} @Self + - delay 1 + - throw{v=15;velocityY=3} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - leap{velocity=430} @target + - jump{velocity=0.5} @Self + - effect:particletornado{p=witchMagic;mr=1;h=1;i=1;d=3;rs=1;sh=1;cs=0;ca=1;chs=1;cvs=1;cps=0.3;cyo=0.2} @LivingEntitiesInRadius{r=10} + - effect:particletornado{p=flame;mr=2;h=1;i=1;d=8;rs=1;sh=1;cs=0;ca=1;chs=1;cvs=1;cps=0.3;cyo=0.2} @LivingEntitiesInRadius{r=10} + - sound{s=block.fire.ambient;v=2;p=1} @Self + - effect:explosion @PlayersInRadius @LivingEntitiesInRadius{r=10} + - effect:particles{p=cloud;a=35;hS=0.5;vS=0.5} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - damage{a=1000} @LivingEntitiesInRadius{r=10} +范围切割斩2: + Cooldown: 30 + Skills: + - effect:particletornado{p=totem;mr=1;h=1;i=1;d=40;rs=1;sh=1;cs=0;ca=1;chs=1;cvs=1;cps=0.3;cyo=0.2} @Self + - message{m="&8[&e技能提示&8]&f你爆发了&b&l特殊能力 &c&n狂热 可以快速回血 &f!"} @Self + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - heal{amount=1500} @Self + - delay 1 + - throw{v=15;velocityY=3} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - leap{velocity=430} @target + - jump{velocity=0.5} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - effect:explosion @PlayersInRadius @LivingEntitiesInRadius{r=10} + - effect:particles{p=cloud;a=35;hS=0.5;vS=0.5} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - damage{a=300000} @LivingEntitiesInRadius{r=10} +剑意: + Skills: + - effect:particles{p=sweepAttack;a=2;hs=0.2;vs=0.2;speed=2} @origin +劈中: + Skills: + - effect:particles{particle=dragonbreath;amount=30;hS=0.05;vS=0.05;speed=0.2} @origin + - damage{amount=1550;i=false} + - effect:explosion +剑意2: + Skills: + - effect:particles{p=sweepAttack;a=2;hs=0.2;vs=0.2;speed=2} @origin +劈中2: + Skills: + - effect:particles{particle=dragonbreath;amount=30;hS=0.05;vS=0.05;speed=0.2} @origin + - damage{amount=1550;i=false} + - effect:explosion +终焉剑意: + Cooldown: 15 + Skills: + - message{m="&8[&e技能提示&8]&f&6 &f发动了技能 &b终焉剑意 &f!"} @PlayersInRadius{r=20;targetself=true} + - delay 30 + - message{m="&8[&e技能提示&8]&b&l斩 &f!"} @Self + - missile{ot=剑意2;oh=劈中2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 5 + - missile{ot=剑意2;oh=劈中2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 5 + - missile{ot=剑意2;oh=劈中2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 5 + - missile{ot=剑意2;oh=劈中2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 5 + - missile{ot=剑意2;oh=劈中2;v=15;i=1;hR=0.5;vR=0.5;} + - delay 5 + - message{m="&8[&e技能提示&8]&b&l划刀斩 &f!"} @Self + - effect:particletornado{p=totem;mr=2;h=1;i=1;d=60;rs=1;sh=1;cs=0;ca=1;chs=1;cvs=1;cps=0.3;cyo=0.2} @Self + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - heal{amount=150} @Self + - delay 1 + - sound{s=block.fire.ambient;v=2;p=1} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - leap{velocity=430} @target + - jump{velocity=0.5} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - effect:explosion @PlayersInRadius @LivingEntitiesInRadius{r=10} + - effect:particles{p=cloud;a=35;hS=0.5;vS=0.5} @Self + - sound{s=block.fire.ambient;v=2;p=1} @Self + - damage{a=10000} @LivingEntitiesInRadius{r=10} + - delay 35 + - message{m="&8[&e技能提示&8]&b&l湮灭斩 &f!"} @Self + - effect:particles{p=largeexplode;a=100} @Target + - damage{a=10000} @LivingEntitiesInRadius{r=10} + - throw{v=15;velocityY=15} @LivingEntitiesInRadius{r=10} + - delay 35 + - sendtitle{title="&8[&7&l>&8&f &6&7&l<&8]";subtitle="&0&l&k2&e&l终焉剑意&8&l·&b圣剑奥义!&0&l&k2";d=50;fi=7;fo=10} @PlayersInRadius{r=50;targetself=true} + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=1500;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + - delay 20 + - damage{amount=15000;i=false} @LivingEntitiesInRadius{r=10} + - effect:particles{p=flame;a=15;hS=0.2;vS=0.2;y=1.0;s=2.5;s=0.5} @LivingEntitiesInRadius{r=10} + - effect:sound{s=entity.generic.explode;v=1.2;p=1.5} @LivingEntitiesInRadius{r=10} + +测试MM技能: + Lore: "[技能] 终焉剑意" + Cooldown: 60.0 + Skills: + - skill{s=终焉剑意} ~onUse diff --git a/src/main/resources/skills/DefaultSkills.yml b/src/main/resources/skills/DefaultSkills.yml new file mode 100644 index 0000000..9413f7d --- /dev/null +++ b/src/main/resources/skills/DefaultSkills.yml @@ -0,0 +1,79 @@ +向前闪现: + Cooldown: 10.0 + Lore: "右键即可闪现" + CooldownTip: ActionBar{p=&f闪现冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Blink{distance=$LORE:闪现距离?32$} ~onUse @Forward + +飞雷神三叉戟: + Cooldown: 15.0 + Lore: "飞雷神三叉戟" + CooldownTip: ActionBar{p=&a技能冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Blink{distance=120} ~onTridentHit @Location + - Reward{Title{m=&e飞雷神之术}} ~onTridentHit + +跳跃之蛋: + Cooldown: 5.0 + Lore: "扔出此蛋, 你将瞬移到落点" + Slot: + - mainhand + CooldownTip: ActionBar{p=&a技能冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Blink{distance=120} ~onHit:EGG @Location + - Reward{Chat{m=&e跳跃之蛋!}} ~onHit:EGG + +旅行者之弓: + Cooldown: 5.0 + Lore: "逐日之弓" + CooldownTip: ActionBar{p=&a技能冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Blink{distance=120} ~onHit:ARROW @Location + +精准时运切换: + Lore: §2Shift+右键切换模式(精准/时运) + Cooldown: 4.0 + CooldownTip: ActionBar{p=&c切换冷却时间;s=&a%.2fs;empty=&f|;full=&c|;length=75} + Skills: + - Switch{s=精准;时运} ~onShiftUse +精准: + Skills: + - RewardSwitch{Enchant{name=SILK_TOUCH;limit=1}} + - RewardSwitch{ActionBar{m=&b&l切换为精准模式}} + +时运: + Skills: + - RewardSwitch{Enchant{name=LOOT_BONUS_BLOCKS;level=0}} + - RewardSwitch{ActionBar{m=&a&l切换为时运模式}} + +忠诚/激流切换: + Lore: §2Shift+左键切换模式(忠诚/激流) + Cooldown: 4.0 + CooldownTip: ActionBar{p=&c切换冷却时间;s=&a%.2fs;empty=&f|;full=&c|;length=75} + Skills: + - Switch{s=忠诚;激流} ~onShiftSwing + +激流: + Skills: + - RewardSwitch{Enchant{name=RIPTIDE;level=0}} + - RewardSwitch{ActionBar{m=&b&l切换为激流模式}} + +忠诚: + Skills: + - RewardSwitch{ActionBar{m=&a&l切换为时忠诚模式}} + +禁飞DEBUFF: + Cooldown: 5.0 + Lore: 禁飞 + CooldownTip: ActionBar{p=&a技能冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Debuff{type=fly;duration=5} ~onAttack @Entity + - Reward{Chat{m=&c禁飞!}} ~onAttack + +定身DEBUFF: + Cooldown: 5.0 + Lore: 定身 + CooldownTip: ActionBar{p=&a技能冷却时间;s=&a%.2fs;empty=&f|;full=&a|;length=75} + Skills: + - Debuff{type=move;duration=2} ~onAttack @Entity + - Reward{Chat{m=&c定身!}} ~onAttack diff --git a/src/main/resources/skills/ExampleSkills.yml b/src/main/resources/skills/ExampleSkills.yml new file mode 100644 index 0000000..7a7fda3 --- /dev/null +++ b/src/main/resources/skills/ExampleSkills.yml @@ -0,0 +1,5 @@ +示例回复: + Lore: "[技能] 示例回复" + Cooldown: 5 + Skills: + - "Reward{Heal{amount=2}} ~onUse @Self" diff --git a/src/test/java/cn/infstar/cmagicgem/dsl/DirectiveParserTest.java b/src/test/java/cn/infstar/cmagicgem/dsl/DirectiveParserTest.java new file mode 100644 index 0000000..340acf5 --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/dsl/DirectiveParserTest.java @@ -0,0 +1,65 @@ +package cn.infstar.cmagicgem.dsl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; + +class DirectiveParserTest { + @Test + void keepsNestedRewardTogether() { + Directive directive = new DirectiveParser().parse("Conditional{condition=v>1;reward=Enchant{name=sharpness;level=1}} $ignorable"); + assertEquals("Conditional", directive.type()); + assertEquals("Enchant{name=sharpness;level=1}", directive.argument("", "reward")); + assertTrue(directive.ignorable()); + } + + @Test + void parsesLegacyRewardPhases() { + DirectiveParser parser = new DirectiveParser(); + assertEquals(Directive.Phase.SUCCESS, parser.parse("Money{amount=1} $onSuccess").phase()); + assertEquals(Directive.Phase.FAIL, parser.parse("Chat{m=失败} $onFail").phase()); + assertEquals(Directive.Phase.REMOVE, parser.parse("MaxHealth{amount=-1} $onRemove").phase()); + } + + @Test + void removesBalancedArgumentQuotes() { + Directive directive = new DirectiveParser().parse( + "Conditional{condition=\"v>=2&&v<=8\";reward='Chat{m=ok}'}"); + + assertEquals("v>=2&&v<=8", directive.argument("", "condition")); + assertEquals("Chat{m=ok}", directive.argument("", "reward")); + } + + @Test + void retainsLegacyDelimitedMessageSegments() { + DirectiveParser parser = new DirectiveParser(); + Directive directive = parser.parse("Title{messages=主标题;副标题;第二页;第二页副标题;interval=20;stay=60}"); + + assertEquals(List.of("主标题", "副标题", "第二页", "第二页副标题"), + parser.delimitedArgument(directive, Set.of("messages", "message", "m"), + Set.of("interval", "fadein", "fadeout", "stay"))); + } + + @Test + void retainsSwitchCycleAfterNamedFirstEntry() { + Directive directive = new DirectiveParser().parse("Switch{s=精准;时运}"); + + assertEquals("精准", directive.argument("", "s")); + assertEquals(List.of("时运"), directive.positional()); + } + + @Test + void splitsLegacyMultiLineLoreGroups() { + DirectiveParser parser = new DirectiveParser(); + Directive directive = parser.parse("LoreMultiReplace{old=第一行;第二行;new=新一行;新二行;limit=v<5}"); + + assertEquals(List.of("第一行", "第二行"), + parser.delimitedArgument(directive, Set.of("old"), Set.of("lore", "new", "limit"))); + assertEquals(List.of("新一行", "新二行"), + parser.delimitedArgument(directive, Set.of("new"), Set.of("limit"))); + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/expression/ExpressionTest.java b/src/test/java/cn/infstar/cmagicgem/expression/ExpressionTest.java new file mode 100644 index 0000000..89dc2cd --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/expression/ExpressionTest.java @@ -0,0 +1,35 @@ +package cn.infstar.cmagicgem.expression; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ExpressionTest { + private final Expression expression = new Expression(); + + @Test + void evaluatesArithmeticAndVariables() { + assertEquals(7, expression.evaluate("v*2+1", Map.of("v", 3.0))); + assertEquals(9, expression.evaluate("(v+1)^2", Map.of("v", 2.0))); + } + + @Test + void evaluatesConditions() { + assertTrue(expression.test("v>=2&&v<=8", Map.of("v", 5.0))); + assertFalse(expression.test("v%3==0", Map.of("v", 5.0))); + assertFalse(expression.test("false&&v>0", Map.of("v", 5.0))); + assertTrue(expression.test("true||v<0", Map.of("v", 5.0))); + } + + @Test + void evaluatesLegacyFunctionsAndTernary() { + assertEquals(8, expression.evaluate("true?$POW(2,3):0", Map.of())); + assertEquals(2, expression.evaluate("false?1:$FLOOR(2.9)", Map.of())); + double random = expression.evaluate("$RANDOM()", Map.of()); + assertTrue(random >= 0 && random < 1); + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/gem/GemTypeTest.java b/src/test/java/cn/infstar/cmagicgem/gem/GemTypeTest.java new file mode 100644 index 0000000..169ea7c --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/gem/GemTypeTest.java @@ -0,0 +1,29 @@ +package cn.infstar.cmagicgem.gem; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class GemTypeTest { + @Test + void acceptsLegacyAndEnumSpellings() { + assertEquals(GemType.ITEM_GEM, GemType.parse("ItemGem")); + assertEquals(GemType.PLAYER_GEM, GemType.parse("PlayerGem")); + assertEquals(GemType.RANDOM_GEM, GemType.parse("RandomGem")); + assertEquals(GemType.PLAYER_GEM, GemType.parse("PLAYER_GEM")); + assertEquals(GemType.RANDOM_GEM, GemType.parse("random-gem")); + assertEquals(GemType.ITEM_GEM, GemType.parse("item gem")); + } + + @Test + void defaultsBlankValuesToItemGems() { + assertEquals(GemType.ITEM_GEM, GemType.parse(null)); + assertEquals(GemType.ITEM_GEM, GemType.parse(" ")); + } + + @Test + void rejectsUnknownValues() { + assertThrows(IllegalArgumentException.class, () -> GemType.parse("not-a-gem")); + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/gem/RandomGemValidatorTest.java b/src/test/java/cn/infstar/cmagicgem/gem/RandomGemValidatorTest.java new file mode 100644 index 0000000..441602f --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/gem/RandomGemValidatorTest.java @@ -0,0 +1,57 @@ +package cn.infstar.cmagicgem.gem; + +import org.bukkit.Material; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RandomGemValidatorTest { + @Test + void acceptsNestedRandomGemsWithOneTerminalType() { + Map gems = gems( + gem("item", GemType.ITEM_GEM), + random("nested", new GemDefinition.WeightedGem("item", 1)), + random("root", new GemDefinition.WeightedGem("nested", 1))); + + assertTrue(RandomGemValidator.validate(gems).isEmpty()); + } + + @Test + void rejectsCyclesUnknownReferencesAndMixedTypes() { + Map gems = gems( + gem("item", GemType.ITEM_GEM), gem("player", GemType.PLAYER_GEM), + random("mixed", new GemDefinition.WeightedGem("item", 1), new GemDefinition.WeightedGem("player", 1)), + random("unknown", new GemDefinition.WeightedGem("missing", 1)), + random("cycle-a", new GemDefinition.WeightedGem("cycle-b", 1)), + random("cycle-b", new GemDefinition.WeightedGem("cycle-a", 1))); + + Map errors = RandomGemValidator.validate(gems); + assertEquals(Set.of("mixed", "unknown", "cycle-a", "cycle-b"), errors.keySet()); + } + + private Map gems(GemDefinition... definitions) { + Map result = new LinkedHashMap<>(); + for (GemDefinition definition : definitions) result.put(definition.name(), definition); + return result; + } + + private GemDefinition random(String name, GemDefinition.WeightedGem... targets) { + return gem(name, GemType.RANDOM_GEM, List.of(targets)); + } + + private GemDefinition gem(String name, GemType type) { + return gem(name, type, List.of()); + } + + private GemDefinition gem(String name, GemType type, List targets) { + return new GemDefinition(name, name, type, Material.STONE, name, List.of(), null, null, null, + null, false, Set.of(), List.of(), 0, "100", "0", "0", null, false, + false, 0, null, false, targets, List.of(), List.of(), null, null, null, null, null, null); + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/reward/RewardContextTest.java b/src/test/java/cn/infstar/cmagicgem/reward/RewardContextTest.java new file mode 100644 index 0000000..f2440f7 --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/reward/RewardContextTest.java @@ -0,0 +1,23 @@ +package cn.infstar.cmagicgem.reward; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class RewardContextTest { + @Test + void evaluatesEachDynamicRewardValueOnce() { + RewardContext context = new RewardContext(null, null, null, false); + Object reward = new Object(); + AtomicInteger evaluations = new AtomicInteger(); + + assertEquals(7.5, context.evaluatedNumber(reward, () -> { + evaluations.incrementAndGet(); + return 7.5; + })); + assertEquals(7.5, context.evaluatedNumber(reward, () -> 99)); + assertEquals(1, evaluations.get()); + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java b/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java new file mode 100644 index 0000000..e252716 --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/skill/BundledConfigurationTest.java @@ -0,0 +1,93 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.dsl.Directive; +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import cn.infstar.cmagicgem.reward.RewardEngine; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BundledConfigurationTest { + private final DirectiveParser parser = new DirectiveParser(); + + @Test + void pluginDescriptorOnlyRegistersNewCommandNames() { + YamlConfiguration descriptor = YamlConfiguration.loadConfiguration(new File("src/main/resources/plugin.yml")); + + assertNotNull(descriptor.getConfigurationSection("commands.cmgem")); + assertTrue(descriptor.getStringList("commands.cmgem.aliases").contains("cmg")); + assertNull(descriptor.getConfigurationSection("commands.mgem")); + assertNull(descriptor.getConfigurationSection("commands.magicgem")); + } + + @Test + void everyBundledGemRewardAndReferenceLoads() throws Exception { + Path directory = Path.of("src/main/resources/gems"); + Set names = new LinkedHashSet<>(); + try (var files = Files.list(directory)) { + for (Path file : files.filter(path -> path.toString().endsWith(".yml")).toList()) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file.toFile()); + for (String key : yaml.getKeys(false)) { + ConfigurationSection section = yaml.getConfigurationSection(key); + if (section == null) continue; + names.add(section.getString("Name", key).toLowerCase(Locale.ROOT)); + for (String source : section.getStringList("Rewards")) { + Directive reward = parser.parse(source); + assertTrue(RewardEngine.supports(reward.type()), file + "/" + key + ": " + source); + } + for (String source : section.getStringList("Conditions")) assertFalse(parser.parse(source).type().isBlank()); + } + } + } + try (var files = Files.list(directory)) { + for (Path file : files.filter(path -> path.toString().endsWith(".yml")).toList()) { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file.toFile()); + for (String key : yaml.getKeys(false)) { + ConfigurationSection random = yaml.getConfigurationSection(key + ".Gems"); + if (random == null) continue; + for (String target : random.getKeys(false)) + assertTrue(names.contains(target.toLowerCase(Locale.ROOT)), file + "/" + key + " -> " + target); + } + } + } + } + + @Test + void everyBundledSkillActionParses() throws Exception { + SkillRegistry registry = new SkillRegistry(null, parser); + try (var files = Files.list(Path.of("src/main/resources/skills"))) { + for (Path path : files.filter(file -> file.toString().endsWith(".yml")).toList()) { + File file = path.toFile(); + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file); + for (String key : yaml.getKeys(false)) { + if (key.equalsIgnoreCase("depend")) continue; + ConfigurationSection section = yaml.getConfigurationSection(key); + assertNotNull(section, file + "/" + key); + for (String source : section.getStringList("Skills")) { + SkillAction action = registry.parseAction(source); + assertFalse(action.mechanic().type().isBlank(), file + "/" + key + ": " + source); + if (action.mechanic().type().equalsIgnoreCase("Reward") + || action.mechanic().type().equalsIgnoreCase("RewardSwitch")) { + String nested = action.mechanic().positional().isEmpty() + ? action.mechanic().argument("", "reward", "r") + : action.mechanic().positional().getFirst(); + assertTrue(RewardEngine.supports(parser.parse(nested).type()), file + "/" + key + ": " + source); + } + } + } + } + } + } +} diff --git a/src/test/java/cn/infstar/cmagicgem/skill/SkillRegistryTest.java b/src/test/java/cn/infstar/cmagicgem/skill/SkillRegistryTest.java new file mode 100644 index 0000000..0b02feb --- /dev/null +++ b/src/test/java/cn/infstar/cmagicgem/skill/SkillRegistryTest.java @@ -0,0 +1,35 @@ +package cn.infstar.cmagicgem.skill; + +import cn.infstar.cmagicgem.dsl.DirectiveParser; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SkillRegistryTest { + private final SkillRegistry registry = new SkillRegistry(null, new DirectiveParser()); + + @Test + void preservesMythicMechanicAndTargeterWithSpaces() { + String line = "message{m=\"Player used a skill\"} @PlayersInRadius{r=20;targetself=true} ~onUse"; + + SkillAction action = registry.parseAction(line); + + assertEquals("message", action.mechanic().type()); + assertEquals("Player used a skill", action.mechanic().argument("", "m").replace("\"", "")); + assertEquals("message{m=\"Player used a skill\"} @PlayersInRadius{r=20;targetself=true}", + action.mechanicLine()); + assertEquals(TriggerType.USE, action.trigger()); + assertEquals("playersinradius", action.target()); + } + + @Test + void separatesTriggerFiltersAndWorldLimits() { + SkillAction action = registry.parseAction( + "PotionBuff{type=SPEED;level=$LORE:Speed:1$} ~onHit:ARROW;TRIDENT @Self $world:3"); + + assertEquals("PotionBuff{type=SPEED;level=$LORE:Speed:1$} @Self", action.mechanicLine()); + assertEquals(TriggerType.HIT, action.trigger()); + assertEquals(java.util.List.of("ARROW", "TRIDENT"), action.triggerFilters()); + assertEquals(java.util.Map.of("world", 3), action.worldLimits()); + } +}