This commit is contained in:
BuildTools 2023-11-28 18:49:21 +05:00
parent daa3e40c65
commit 9d7c98cddb
9 changed files with 156 additions and 112 deletions

View File

@ -108,6 +108,7 @@ public class ExcellentEnchants extends NexPlugin<ExcellentEnchants> {
public void loadLang() { public void loadLang() {
this.getLangManager().loadMissing(Lang.class); this.getLangManager().loadMissing(Lang.class);
this.getLangManager().loadEnum(FitItemType.class); this.getLangManager().loadEnum(FitItemType.class);
this.getLangManager().loadEnum(ObtainType.class);
this.getLang().saveChanges(); this.getLang().saveChanges();
} }

View File

@ -8,6 +8,7 @@ public class Placeholders extends su.nexmedia.engine.utils.Placeholders {
public static final String URL_ENGINE_ITEMS = "https://github.com/nulli0n/NexEngine-spigot/wiki/Configuration-Tips#item-sections"; public static final String URL_ENGINE_ITEMS = "https://github.com/nulli0n/NexEngine-spigot/wiki/Configuration-Tips#item-sections";
public static final String GENERIC_TYPE = "%type%"; public static final String GENERIC_TYPE = "%type%";
public static final String GENERIC_NAME = "%name%";
public static final String GENERIC_ITEM = "%item%"; public static final String GENERIC_ITEM = "%item%";
public static final String GENERIC_LEVEL = "%level%"; public static final String GENERIC_LEVEL = "%level%";
public static final String GENERIC_AMOUNT = "%amount%"; public static final String GENERIC_AMOUNT = "%amount%";

View File

@ -9,7 +9,6 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.command.AbstractCommand; import su.nexmedia.engine.api.command.AbstractCommand;
import su.nexmedia.engine.api.command.CommandResult; import su.nexmedia.engine.api.command.CommandResult;
import su.nexmedia.engine.lang.LangManager;
import su.nexmedia.engine.utils.*; import su.nexmedia.engine.utils.*;
import su.nexmedia.engine.utils.random.Rnd; import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants; import su.nightexpress.excellentenchants.ExcellentEnchants;
@ -90,7 +89,7 @@ public class EnchantCommand extends AbstractCommand<ExcellentEnchants> {
plugin.getMessage(sender == player ? Lang.COMMAND_ENCHANT_DONE_SELF : Lang.COMMAND_ENCHANT_DONE_OTHERS) plugin.getMessage(sender == player ? Lang.COMMAND_ENCHANT_DONE_SELF : Lang.COMMAND_ENCHANT_DONE_OTHERS)
.replace(Placeholders.forPlayer(player)) .replace(Placeholders.forPlayer(player))
.replace(Placeholders.GENERIC_ITEM, ItemUtil.getItemName(item)) .replace(Placeholders.GENERIC_ITEM, ItemUtil.getItemName(item))
.replace(Placeholders.GENERIC_ENCHANT, LangManager.getEnchantment(enchantment)) .replace(Placeholders.GENERIC_ENCHANT, EnchantUtils.getLocalized(enchantment))
.replace(Placeholders.GENERIC_LEVEL, NumberUtil.toRoman(level)) .replace(Placeholders.GENERIC_LEVEL, NumberUtil.toRoman(level))
.send(sender); .send(sender);
} }

View File

@ -19,6 +19,8 @@ import java.util.stream.Stream;
public class Config { public class Config {
public static final String DIR_MENU = "/menu/";
public static final JOption<Long> TASKS_ARROW_TRAIL_TICKS_INTERVAL = JOption.create("Tasks.Arrow_Trail.Tick_Interval", public static final JOption<Long> TASKS_ARROW_TRAIL_TICKS_INTERVAL = JOption.create("Tasks.Arrow_Trail.Tick_Interval",
1L, 1L,
"Sets how often (in ticks) arrow trail particle effects will be spawned behind the arrow." "Sets how often (in ticks) arrow trail particle effects will be spawned behind the arrow."

View File

@ -69,10 +69,7 @@ public class EnchantPopulator {
Set<ExcellentEnchant> enchants = EnchantRegistry.getOfTier(tier); Set<ExcellentEnchant> enchants = EnchantRegistry.getOfTier(tier);
enchants.removeIf(enchant -> { enchants.removeIf(enchant -> {
if (enchant.getObtainChance(this.getObtainType()) <= 0D) return true; return !enchant.isObtainable(this.getObtainType()) || !enchant.canEnchantItem(this.getItem());
if (!enchant.canEnchantItem(this.getItem())) return true;
return this.getObtainType() == ObtainType.ENCHANTING && (enchant.isTreasure() || enchant.isCursed());
}); });
this.candidates.put(tier, enchants); this.candidates.put(tier, enchants);

View File

@ -195,6 +195,13 @@ public abstract class ExcellentEnchant extends Enchantment implements IEnchantme
return description; return description;
} }
@NotNull
public List<String> formatDescription() {
return new ArrayList<>(this.getDescription().stream()
.map(line -> Config.ENCHANTMENTS_DESCRIPTION_FORMAT.get().replace(Placeholders.GENERIC_DESCRIPTION, line))
.toList());
}
@NotNull @NotNull
public List<String> formatDescription(int level) { public List<String> formatDescription(int level) {
return new ArrayList<>(this.getDescription(level).stream() return new ArrayList<>(this.getDescription(level).stream()
@ -202,6 +209,10 @@ public abstract class ExcellentEnchant extends Enchantment implements IEnchantme
.toList()); .toList());
} }
public boolean hasConflicts() {
return !this.getConflicts().isEmpty();
}
@NotNull @NotNull
public Set<String> getConflicts() { public Set<String> getConflicts() {
return this.getDefaults().getConflicts(); return this.getDefaults().getConflicts();
@ -234,6 +245,12 @@ public abstract class ExcellentEnchant extends Enchantment implements IEnchantme
return get != 0 ? this.fineLevel(get, ObtainType.ENCHANTING) : 0; return get != 0 ? this.fineLevel(get, ObtainType.ENCHANTING) : 0;
} }
public boolean isObtainable(@NotNull ObtainType obtainType) {
if (obtainType == ObtainType.ENCHANTING && (this.isTreasure() || this.isCursed())) return false;
return this.getObtainChance(obtainType) > 0D;
}
public double getObtainChance(@NotNull ObtainType obtainType) { public double getObtainChance(@NotNull ObtainType obtainType) {
return this.getDefaults().getObtainChance().getOrDefault(obtainType, 0D); return this.getDefaults().getObtainChance().getOrDefault(obtainType, 0D);
} }

View File

@ -1,9 +1,12 @@
package su.nightexpress.excellentenchants.enchantment.menu; package su.nightexpress.excellentenchants.enchantment.menu;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.config.JYML; import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.api.menu.AutoPaged; import su.nexmedia.engine.api.menu.AutoPaged;
import su.nexmedia.engine.api.menu.MenuItemType; import su.nexmedia.engine.api.menu.MenuItemType;
@ -12,49 +15,50 @@ import su.nexmedia.engine.api.menu.click.ItemClick;
import su.nexmedia.engine.api.menu.impl.ConfigMenu; import su.nexmedia.engine.api.menu.impl.ConfigMenu;
import su.nexmedia.engine.api.menu.impl.MenuOptions; import su.nexmedia.engine.api.menu.impl.MenuOptions;
import su.nexmedia.engine.api.menu.impl.MenuViewer; import su.nexmedia.engine.api.menu.impl.MenuViewer;
import su.nexmedia.engine.api.menu.item.MenuItem;
import su.nexmedia.engine.utils.Colorizer; import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.ItemReplacer;
import su.nexmedia.engine.utils.ItemUtil; import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.PDCUtil; import su.nexmedia.engine.utils.PDCUtil;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants; import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders; import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant; import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.registry.EnchantRegistry; import su.nightexpress.excellentenchants.enchantment.registry.EnchantRegistry;
import su.nightexpress.excellentenchants.enchantment.type.ObtainType;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils; import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.IntStream;
import static su.nexmedia.engine.utils.Colors2.*;
import static su.nightexpress.excellentenchants.Placeholders.*;
public class EnchantmentsListMenu extends ConfigMenu<ExcellentEnchants> implements AutoPaged<ExcellentEnchant> { public class EnchantmentsListMenu extends ConfigMenu<ExcellentEnchants> implements AutoPaged<ExcellentEnchant> {
private static final String PATH = "/menu/enchants_list.yml"; private static final String FILE = "enchants.yml";
private static final String PLACEHOLDER_CONFLICTS = "%conflicts%"; private static final String PLACEHOLDER_CONFLICTS = "%conflicts%";
private static final String PLACEHOLDER_CHARGES = "%charges%"; private static final String PLACEHOLDER_CHARGES = "%charges%";
private static final String PLACEHOLDER_OBTAINING = "%obtaining%"; private static final String PLACEHOLDER_OBTAINING = "%obtaining%";
private final ItemStack enchantIcon;
private final List<String> enchantLoreConflicts;
private final List<String> enchantLoreCharges;
private final List<String> enchantLoreObtaining;
private final int[] enchantSlots;
private final NamespacedKey keyLevel; private final NamespacedKey keyLevel;
private final Map<String, Map<Integer, ItemStack>> iconCache; private final Map<String, Map<Integer, ItemStack>> iconCache;
private String enchantName;
private List<String> enchantLoreMain;
private List<String> enchantLoreConflicts;
private List<String> enchantLoreCharges;
private List<String> enchantLoreObtaining;
private int[] enchantSlots;
public EnchantmentsListMenu(@NotNull ExcellentEnchants plugin) { public EnchantmentsListMenu(@NotNull ExcellentEnchants plugin) {
super(plugin, JYML.loadOrExtract(plugin, PATH)); super(plugin, new JYML(plugin.getDataFolder() + Config.DIR_MENU, FILE));
this.keyLevel = new NamespacedKey(plugin, "list_display_level"); this.keyLevel = new NamespacedKey(plugin, "list_display_level");
this.iconCache = new HashMap<>(); this.iconCache = new HashMap<>();
this.enchantIcon = cfg.getItem("Enchantments.Icon");
this.enchantLoreConflicts = Colorizer.apply(cfg.getStringList("Enchantments.Lore.Conflicts"));
this.enchantLoreCharges = Colorizer.apply(cfg.getStringList("Enchantments.Lore.Charges"));
this.enchantLoreObtaining = Colorizer.apply(cfg.getStringList("Enchantments.Lore.Obtaining"));
this.enchantSlots = cfg.getIntArray("Enchantments.Slots");
this.registerHandler(MenuItemType.class) this.registerHandler(MenuItemType.class)
.addClick(MenuItemType.CLOSE, (viewer, event) -> plugin.runTask(task -> viewer.getPlayer().closeInventory())) .addClick(MenuItemType.CLOSE, ClickHandler.forClose(this))
.addClick(MenuItemType.PAGE_NEXT, ClickHandler.forNextPage(this)) .addClick(MenuItemType.PAGE_NEXT, ClickHandler.forNextPage(this))
.addClick(MenuItemType.PAGE_PREVIOUS, ClickHandler.forPreviousPage(this)); .addClick(MenuItemType.PAGE_PREVIOUS, ClickHandler.forPreviousPage(this));
@ -67,6 +71,85 @@ public class EnchantmentsListMenu extends ConfigMenu<ExcellentEnchants> implemen
this.iconCache.clear(); this.iconCache.clear();
} }
// ----------
@Override
public boolean isCodeCreation() {
return true;
}
@Override
protected void loadAdditional() {
this.enchantName = JOption.create("Enchantment.Name", ENCHANTMENT_NAME_FORMATTED).read(cfg);
this.enchantLoreMain = JOption.create("Enchantment.Lore.Main",
Arrays.asList(
ENCHANTMENT_DESCRIPTION,
DARK_GRAY + "(click to switch level)",
"",
LIGHT_YELLOW + BOLD + "Info:",
LIGHT_YELLOW + "" + LIGHT_GRAY + "Tier: " + LIGHT_YELLOW + ENCHANTMENT_TIER,
LIGHT_YELLOW + "" + LIGHT_GRAY + "Applies to: " + LIGHT_YELLOW + ENCHANTMENT_FIT_ITEM_TYPES,
LIGHT_YELLOW + "" + LIGHT_GRAY + "Levels: " + LIGHT_YELLOW + ENCHANTMENT_LEVEL_MIN + GRAY + " - " + LIGHT_YELLOW + ENCHANTMENT_LEVEL_MAX,
PLACEHOLDER_CHARGES,
PLACEHOLDER_CONFLICTS,
PLACEHOLDER_OBTAINING
)).read(cfg);
this.enchantLoreConflicts = JOption.create("Enchantment.Lore.Conflicts",
Arrays.asList(
"",
LIGHT_RED + BOLD + "Conflicts:",
LIGHT_RED + "" + LIGHT_GRAY + GENERIC_NAME
)).read(cfg);
this.enchantLoreCharges = JOption.create("Enchantment.Lore.Charges",
Arrays.asList(
LIGHT_YELLOW + "" + LIGHT_GRAY + "Charges: " + LIGHT_YELLOW + ENCHANTMENT_CHARGES_MAX_AMOUNT + "" + LIGHT_GRAY + " (" + WHITE + ENCHANTMENT_CHARGES_FUEL_ITEM + LIGHT_GRAY + ")"
)).read(cfg);
this.enchantLoreObtaining = JOption.create("Enchantment.Lore.Obtaining",
Arrays.asList(
"",
LIGHT_GREEN + BOLD + "Obtaining:",
LIGHT_GREEN + "" + LIGHT_GRAY + GENERIC_TYPE
)).read(cfg);
this.enchantSlots = new JOption<int[]>("Enchantment.Slots",
(cfg, path, def) -> cfg.getIntArray(path),
() -> IntStream.range(0, 27).toArray()
).setWriter(JYML::setIntArray).read(cfg);
}
@Override
@NotNull
protected MenuOptions createDefaultOptions() {
return new MenuOptions(DARK_GRAY + BOLD + "Custom Enchants", 36, InventoryType.CHEST);
}
@Override
@NotNull
protected List<MenuItem> createDefaultItems() {
List<MenuItem> list = new ArrayList<>();
ItemStack nextPageStack = ItemUtil.createCustomHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMyY2E2NjA1NmI3Mjg2M2U5OGY3ZjMyYmQ3ZDk0YzdhMGQ3OTZhZjY5MWM5YWMzYTkxMzYzMzEzNTIyODhmOSJ9fX0=");
ItemUtil.mapMeta(nextPageStack, meta -> {
meta.setDisplayName(WHITE + "Next Page" + LIGHT_GRAY + " (→)");
});
ItemStack prevPageStack = ItemUtil.createCustomHead("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODY5NzFkZDg4MWRiYWY0ZmQ2YmNhYTkzNjE0NDkzYzYxMmY4Njk2NDFlZDU5ZDFjOTM2M2EzNjY2YTVmYTYifX19");
ItemUtil.mapMeta(prevPageStack, meta -> {
meta.setDisplayName(LIGHT_GRAY + "(←) " + WHITE + "Previous Page");
});
list.add(new MenuItem(nextPageStack).setSlots(35).setType(MenuItemType.PAGE_NEXT).setPriority(5));
list.add(new MenuItem(prevPageStack).setSlots(27).setType(MenuItemType.PAGE_PREVIOUS).setPriority(5));
return list;
}
// -----------
@Override @Override
public void onPrepare(@NotNull MenuViewer viewer, @NotNull MenuOptions options) { public void onPrepare(@NotNull MenuViewer viewer, @NotNull MenuOptions options) {
super.onPrepare(viewer, options); super.onPrepare(viewer, options);
@ -118,26 +201,42 @@ public class EnchantmentsListMenu extends ConfigMenu<ExcellentEnchants> implemen
@NotNull @NotNull
private ItemStack buildEnchantIcon(@NotNull ExcellentEnchant enchant, int level) { private ItemStack buildEnchantIcon(@NotNull ExcellentEnchant enchant, int level) {
ItemStack icon = new ItemStack(this.enchantIcon); ItemStack icon = new ItemStack(Material.ENCHANTED_BOOK);
ItemUtil.mapMeta(icon, meta -> {
List<String> lore = meta.getLore();
if (lore == null) lore = new ArrayList<>();
List<String> conflicts = enchant.getConflicts().isEmpty() ? Collections.emptyList() : new ArrayList<>(this.enchantLoreConflicts); List<String> conflicts = new ArrayList<>();
List<String> conflictNames = enchant.getConflicts().stream().map(EnchantUtils::getLocalized).filter(Objects::nonNull).toList(); if (enchant.hasConflicts()) {
conflicts = StringUtil.replace(conflicts, Placeholders.ENCHANTMENT_NAME, true, conflictNames); for (String line : this.enchantLoreConflicts) {
if (line.contains(GENERIC_NAME)) {
List<String> charges = enchant.isChargesEnabled() ? new ArrayList<>(this.enchantLoreCharges) : Collections.emptyList(); enchant.getConflicts().stream().map(EnchantUtils::getLocalized).filter(Objects::nonNull).forEach(conf -> {
List<String> obtaining = new ArrayList<>(this.enchantLoreObtaining); conflicts.add(line.replace(GENERIC_NAME, conf));
lore = StringUtil.replaceInList(lore, PLACEHOLDER_CONFLICTS, conflicts);
lore = StringUtil.replaceInList(lore, PLACEHOLDER_CHARGES, charges);
lore = StringUtil.replaceInList(lore, PLACEHOLDER_OBTAINING, obtaining);
lore = StringUtil.replace(lore, Placeholders.ENCHANTMENT_DESCRIPTION, true, enchant.getDescription());
meta.setLore(lore);
ItemUtil.replace(meta, enchant.getPlaceholders(level).replacer());
}); });
}
else conflicts.add(line);
}
}
List<String> obtaining = new ArrayList<>();
for (String line : this.enchantLoreObtaining) {
if (line.contains(GENERIC_TYPE)) {
for (ObtainType obtainType : ObtainType.values()) {
if (enchant.isObtainable(obtainType)) {
obtaining.add(line.replace(GENERIC_TYPE, plugin.getLangManager().getEnum(obtainType)));
}
}
}
else obtaining.add(line);
}
ItemReplacer.create(icon).hideFlags().trimmed()
.setDisplayName(this.enchantName)
.setLore(this.enchantLoreMain)
.replaceLoreExact(PLACEHOLDER_CHARGES, enchant.isChargesEnabled() ? new ArrayList<>(this.enchantLoreCharges) : Collections.emptyList())
.replaceLoreExact(PLACEHOLDER_CONFLICTS, conflicts)
.replaceLoreExact(PLACEHOLDER_OBTAINING, obtaining)
.replaceLoreExact(ENCHANTMENT_DESCRIPTION, enchant.formatDescription())
.replace(enchant.getPlaceholders(level))
.replace(Colorizer::apply)
.writeMeta();
return icon; return icon;
} }

View File

@ -1,72 +0,0 @@
Title: ' #a267f3&lCustom Enchants'
Size: 36
Inventory_Type: CHEST
Use_Mini_Message: false
Enchantments:
Icon:
Material: ENCHANTED_BOOK
Name: '%enchantment_name_formatted%'
Lore:
- '#aeb6bf%enchantment_description%'
- ''
- '#bcff9a&lInfo:'
- '#bcff9a▪ #ddeceeTier: #bcff9a%enchantment_tier%'
- '#bcff9a▪ #ddeceeApplies to: #bcff9a%enchantment_fit_item_types%'
- '#bcff9a▪ #ddeceeLevels: #bcff9a%enchantment_level_min% #ddecee- #bcff9a%enchantment_level_max%'
- '%conflicts%'
- '%charges%'
- '%obtaining%'
- ''
- '#a5ff9a&lActions:'
- '#a5ff9a▪ #ddeceeLeft-Click: #a5ff9aSwitch Level'
Lore:
Conflicts:
- ''
- '#ff9a9a[!] #ddeceeCan not be used together with:'
- '#ff9a9a▸ %enchantment_name%'
Charges:
- ''
- '#d39aff&lCharges:'
- '#d39aff◈ #ddeceeMaximum: #d39aff%enchantment_charges_max_amount%⚡'
- '#d39aff◈ #ddeceePer Use: #d39aff-%enchantment_charges_consume_amount%⚡'
- '#d39aff◈ #ddeceePer Recharge: #d39aff+%enchantment_charges_recharge_amount%⚡'
- '#d39aff◈ #ddeceeFuel Item: #d39aff%enchantment_charges_fuel_item%'
Obtaining:
- ''
- '#9af7ff&lObtain Chance:'
- '#9af7ff┃ #ddeceeEnchanting Table: #9af7ff%enchantment_obtain_chance_enchanting%%'
- '#9af7ff┃ #ddeceeVillager Trade: #9af7ff%enchantment_obtain_chance_villager%%'
- '#9af7ff┃ #ddeceeLoot Generation: #9af7ff%enchantment_obtain_chance_loot_generation%%'
- '#9af7ff┃ #ddeceeFishing: #9af7ff%enchantment_obtain_chance_fishing%%'
- '#9af7ff┃ #ddeceeMob Spawning: #9af7ff%enchantment_obtain_chance_mob_spawning%%'
Slots: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
Content:
return:
Priority: 5
Item:
Material: PLAYER_HEAD
Head_Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmU5YWU3YTRiZTY1ZmNiYWVlNjUxODEzODlhMmY3ZDQ3ZTJlMzI2ZGI1OWVhM2ViNzg5YTkyYzg1ZWE0NiJ9fX0=
Name: '#ffee9a(↓) &fClose Menu'
Lore: []
Slots: 31
Type: CLOSE
page_next:
Slots: 35
Type: PAGE_NEXT
Item:
Material: PLAYER_HEAD
Head_Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMyY2E2NjA1NmI3Mjg2M2U5OGY3ZjMyYmQ3ZDk0YzdhMGQ3OTZhZjY5MWM5YWMzYTkxMzYzMzEzNTIyODhmOSJ9fX0=
Name: '#ffee9a(→) &fNext Page'
Priority: 5
page_previous:
Slots: 27
Type: PAGE_PREVIOUS
Item:
Material: PLAYER_HEAD
Head_Texture: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODY5NzFkZDg4MWRiYWY0ZmQ2YmNhYTkzNjE0NDkzYzYxMmY4Njk2NDFlZDU5ZDFjOTM2M2EzNjY2YTVmYTYifX19
Name: '#ffee9a(←) &fPrevious Page'
Priority: 5