Basic support for array edition in UI

This commit is contained in:
Jules 2025-01-20 21:30:43 +01:00
parent 6c8ed33466
commit fa097127cd
17 changed files with 435 additions and 172 deletions

View File

@ -15,11 +15,15 @@ import net.Indyuce.mmoitems.stat.category.StatCategory;
import net.Indyuce.mmoitems.stat.component.LoreWrapper;
import net.Indyuce.mmoitems.stat.component.model.Model;
import net.Indyuce.mmoitems.stat.component.model.builtin.ArrayModel;
import net.Indyuce.mmoitems.stat.component.model.builtin.MapModel;
import net.Indyuce.mmoitems.stat.component.type.ComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.ArrayComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.MapComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.ObjectComponentType;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.util.Pair;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@ -31,18 +35,17 @@ import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class ItemEdition extends EditionInventory {
private static final int[] SLOTS = {19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 46, 47, 48, 49, 50, 51, 52};
private static final NamespacedKey COMPONENT_KEY = new NamespacedKey(MMOItems.plugin, "component_key");
private static final NamespacedKey NEW_ELEMENT = new NamespacedKey(MMOItems.plugin, "new_element");
private Stack<Entry> explored = new Stack<>();
@ -63,18 +66,33 @@ public class ItemEdition extends EditionInventory {
int downmostPage = 1;
private static class Entry {
@NotNull
final String key;
@Nullable
final StatCategory category;
final ComponentType ctype;
@NotNull
final ComponentType componentType;
@Nullable
final Model<?> model;
final boolean newElement;
int page = 1;
private Entry(String key, StatCategory category, ComponentType<?, ?> ctype, Model<?> model) {
Entry(@NotNull String key, @Nullable StatCategory category, @NotNull ComponentType<?, ?> componentType, @Nullable Model<?> model) {
this.key = key;
this.category = category;
this.ctype = ctype;
this.componentType = componentType;
this.model = model;
this.newElement = false;
}
Entry(@NotNull ComponentType<?, ?> componentType) {
this.key = "newItem";
this.category = null;
this.componentType = componentType;
this.model = null;
this.newElement = true;
}
}
@ -87,26 +105,45 @@ public class ItemEdition extends EditionInventory {
.map(stat -> new Entry(stat.getId(), stat.getCategory(), stat.getComponentType(), template.getModels().get(stat)))
.collect(Collectors.toList());
// TODO abstraction over objects, arrays and dicts
// If topmost is an object, explore its keys
if (topmost.ctype instanceof ObjectComponentType)
return ((ObjectComponentType) topmost.ctype).getChildrenTypes().stream()
if (topmost.componentType instanceof ObjectComponentType)
return ((ObjectComponentType) topmost.componentType).getChildrenTypes().stream()
.map(pair -> new Entry(pair.getKey(), null, pair.getValue(), topmost.model == null ? null : topmost.model.getModel(pair.getKey())))
.collect(Collectors.toList());
// If topmost is an array, explore its elements
if (topmost.ctype instanceof ArrayComponentType) {
AtomicInteger counter = new AtomicInteger();
ComponentType subtype = ((ArrayComponentType<?>) topmost.ctype).getSubType();
return topmost.model == null ? new ArrayList<>() :
((ArrayModel<?>) topmost.model).getKeyedModels().stream()
.map(submodel -> new Entry(String.valueOf(counter.getAndIncrement()), null, subtype, submodel.getValue()))
.collect(Collectors.toList());
// return ((ArrayComponentType) topmost.ctype).get
if (topmost.componentType instanceof MapComponentType) {
ComponentType subtype = ((MapComponentType<?>) topmost.componentType).getSubtype();
List<Entry> entries = new ArrayList<>();
// Collect existing entries
if (topmost.model != null)
for (Map.Entry<String, Model<?>> entry : ((MapModel<?>) topmost.model).getModels().entrySet())
entries.add(new Entry(entry.getKey(), null, subtype, entry.getValue()));
// Add "new element" entry
entries.add(new Entry(subtype));
return entries;
}
throw new TerminalComponentException(topmost.ctype);
// If topmost is an array, explore its elements
if (topmost.componentType instanceof ArrayComponentType) {
ComponentType subtype = ((ArrayComponentType<?>) topmost.componentType).getSubType();
List<Entry> entries = new ArrayList<>();
ArrayModel<?> arrayModel = (ArrayModel<?>) topmost.model;
// Collect existing entries
if (topmost.model != null)
for (Pair<String, Model<?>> entry : arrayModel.getKeyedModels())
entries.add(new Entry(entry.getKey(), null, subtype, entry.getValue()));
// Add "new element" entry
entries.add(new Entry(subtype));
return entries;
}
throw new TerminalComponentException(topmost.componentType);
}
@Override
@ -125,41 +162,51 @@ public class ItemEdition extends EditionInventory {
for (int j = min; j < max; j++) {
Entry entry = entries.get(j);
ItemStack item = entry.ctype.provideIcon(entry.model);
ItemStack item = entry.componentType.provideIcon(entry.model);
ItemMeta meta = item.getItemMeta();
meta.addItemFlags(ItemFlag.values());
VersionUtils.addEmptyAttributeModifier(meta);
meta.setDisplayName(ChatColor.GREEN + entry.ctype.provideName(entry.model));
List<String> lore = new ArrayList<>();
// Display stat category
if (entry.category != null) {
lore.add(ChatColor.BLUE + entry.category.getLoreTag());
}
// Display component type lore
List<String> statDesc = (List<String>) entry.ctype.provideDescription(entry.model);
if (statDesc != null) {
lore.add("");
lore.addAll(MythicLib.plugin.parseColors(statDesc.stream().map(s -> ChatColor.GRAY + s).collect(Collectors.toList())));
}
// Display current value of main stat component
try {
entry.ctype.editionUiDisplay(new LoreWrapper(lore), entry.model);
} catch (Exception ignored) {
// Pass
}
// Lore action tags
lore.add("");
for (String loreActionTag : (List<String>) entry.ctype.getActionLoreTags())
lore.add(ChatColor.YELLOW + AltChar.listDash + " " + loreActionTag);
// Set NBT tag for exploration and edition
meta.addItemFlags(ItemFlag.values());
meta.getPersistentDataContainer().set(COMPONENT_KEY, PersistentDataType.STRING, entry.key);
meta.setLore(lore);
// "Add New" entry
if (entry.newElement) {
meta.setDisplayName(ChatColor.GREEN + "Add New");
meta.getPersistentDataContainer().set(NEW_ELEMENT, PersistentDataType.BOOLEAN, true);
}
// Normal entry
else {
meta.setDisplayName(ChatColor.GREEN + entry.componentType.provideName(entry.model));
List<String> lore = new ArrayList<>();
// Display stat category
if (entry.category != null) {
lore.add(ChatColor.BLUE + entry.category.getLoreTag());
}
// Display component type lore
List<String> statDesc = (List<String>) entry.componentType.provideDescription(entry.model);
if (statDesc != null) {
lore.add("");
lore.addAll(MythicLib.plugin.parseColors(statDesc.stream().map(s -> ChatColor.GRAY + s).collect(Collectors.toList())));
}
// Display current value of main stat component
try {
entry.componentType.editionUiDisplay(new LoreWrapper(lore), entry.model);
} catch (Exception ignored) {
// Pass
}
// Lore action tags
lore.add("");
for (String loreActionTag : (List<String>) entry.componentType.getActionLoreTags())
lore.add(ChatColor.YELLOW + AltChar.listDash + " " + loreActionTag);
meta.setLore(lore);
}
item.setItemMeta(meta);
inventory.setItem(SLOTS[slotc++], item);
}
@ -192,21 +239,21 @@ public class ItemEdition extends EditionInventory {
ItemStack item = event.getCurrentItem();
if (!MMOUtils.isMetaItem(item, false) || event.getInventory().getItem(4) == null) return;
if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + "Next Page")) {
if (Objects.equals(item.getItemMeta().getDisplayName(), ChatColor.GREEN + "Next Page")) {
Entry topmost = explored.isEmpty() ? null : explored.peek();
if (topmost == null) downmostPage++;
else topmost.page++;
refreshInventory();
}
if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + "Previous Page")) {
if (Objects.equals(item.getItemMeta().getDisplayName(), ChatColor.GREEN + "Previous Page")) {
Entry topmost = explored.isEmpty() ? null : explored.peek();
if (topmost == null) downmostPage--;
else topmost.page--;
refreshInventory();
}
if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + "Back")) {
if (Objects.equals(item.getItemMeta().getDisplayName(), ChatColor.GREEN + "Back")) {
// Back to type browser
if (explored.isEmpty()) {
@ -220,14 +267,22 @@ public class ItemEdition extends EditionInventory {
}
}
///////////////////////////////////////////////////////////////////////
//
// Reading Entry
//
///////////////////////////////////////////////////////////////////////
// Check tag
final String tag = item.getItemMeta().getPersistentDataContainer().get(COMPONENT_KEY, PersistentDataType.STRING);
if (tag == null) return;
// Exploration
Entry topmost;
ComponentType<?, ?> ctype;
Model<?> model;
String key;
boolean newElement;
if (explored.isEmpty()) {
ItemStat<?> stat = Objects.requireNonNull(MMOItems.plugin.getStats().get(tag.toUpperCase()), "No stat found with ID '" + tag + "'");
@ -240,60 +295,130 @@ public class ItemEdition extends EditionInventory {
return;
}
topmost = null;
ctype = stat.getComponentType();
model = template.getModels().get(stat);
key = stat.getId().toLowerCase();
newElement = false;
} else {
Entry topmost = explored.peek();
topmost = explored.peek();
ctype = topmost.ctype.getChildType(tag);
ctype = topmost.componentType.getChildType(tag);
model = topmost.model == null ? null : topmost.model.getModel(tag);
key = tag;
key = "newItem";
newElement = item.getItemMeta().getPersistentDataContainer().has(NEW_ELEMENT);
}
Bukkit.broadcastMessage("Clicked " + key + " " + ctype.isTerminal() + " " + ctype + " " + model);
///////////////////////////////////////////////////////////////////////
//
// Player Input and Exploration
//
///////////////////////////////////////////////////////////////////////
// Input handling
if (ctype.isTerminal()) {
Bukkit.broadcastMessage(String.format("Clicked key='%s' ctype_terminal=%b ctype=%s model=%s", key, ctype.isTerminal(), String.valueOf(ctype), String.valueOf(model)));
// Right click basic
if (event.getClick() == ClickType.RIGHT) {
getEditedSection().set(appendComponentPaths(key), null);
registerTemplateEdition();
player.sendMessage(ChatColor.GRAY + String.format("Successfully deleted %s", ((ComponentType) ctype).provideName(model)));
return;
// "Add New" Button
if (newElement) {
// Add new element to map
if (topmost.componentType instanceof MapComponentType) {
// TODO support non-inputable map value types. there are none atm
Validate.isTrue(ctype.isInputable(), "Non-inputable map value types are not supported yet");
playerInputWrapper(input -> {
String[] firstSplit = input.split(" ", 2); // TODO edit splitter
Validate.isTrue(firstSplit.length == 2, "Invalid format");
String newKey = firstSplit[0];
Object fromInput = ctype.inputToConfig(firstSplit[1]); // Read input from player
getEditedSection().set(appendComponentPaths(newKey), fromInput); // Write to config
registerTemplateEdition(); // Register edition
});
}
// Left click basic input
PlayerInputProvider.input(navigator, playerInput -> {
try {
// Add new element to array
else if (topmost.componentType instanceof ArrayComponentType) {
//TODO support list format
// Read input from player
Object fromInput = ctype.inputToConfig(playerInput);
String newKey = topmost.model == null ? "1" : ((ArrayModel) topmost.model).findNewKey();
// Write to config
getEditedSection().set(appendComponentPaths(key), fromInput);
registerTemplateEdition();
// If inputable OR terminal, input and add
if (ctype.isInputable() || ctype.isTerminal()) {
player.sendMessage("{item_edition_input_success}");
return true;
} catch (Exception exception) {
player.sendMessage(ChatColor.RED + exception.getMessage());
return null;
playerInputWrapper(input -> {
Object fromInput = ctype.inputToConfig(input);
getEditedSection().set(appendComponentPaths(newKey), fromInput);
registerTemplateEdition();
});
}
}).enable("{begin_input_edition}");
// Otherwise, create and explore
else {
getEditedSection().createSection(appendComponentPaths(newKey)); // Write to config
registerTemplateEdition();
callExploration(newKey, ctype, null);
}
}
// Not implemented
else throw new RuntimeException("Not implemented");
}
// Remove content
else if (event.getClick() == ClickType.RIGHT) {
getEditedSection().set(appendComponentPaths(key), null);
registerTemplateEdition();
player.sendMessage(ChatColor.GRAY + String.format("Successfully deleted %s", ((ComponentType) ctype).provideName(model)));
}
// Inputable Component
else if (ctype.isInputable()) {
// TODO remove last, input next for maps and dicts
// Shift-left click: explore instead of input non-terminal inputable
if (event.getClick() == ClickType.SHIFT_LEFT && !ctype.isTerminal() && model != null) {
callExploration(key, ctype, model);
}
// Simple player input
else callSimplePlayerInput(key, ctype);
}
// Exploration
else {
explored.push(new Entry(key, null, ctype, model));
refreshInventory();
callExploration(key, ctype, model);
}
}
private void callExploration(String key, ComponentType<?, ?> ctype, Model<?> model) {
explored.push(new Entry(key, null, ctype, model));
refreshInventory();
}
private void callSimplePlayerInput(String key, ComponentType<?, ?> componentType) {
playerInputWrapper(playerInput -> {
Object fromInput = componentType.inputToConfig(playerInput); // Read input from player
getEditedSection().set(appendComponentPaths(key), fromInput); // Write to config
registerTemplateEdition(); // Register edition
});
}
private void playerInputWrapper(Consumer<String> callback) {
PlayerInputProvider.input(navigator, playerInput -> {
try {
callback.accept(playerInput);
player.sendMessage("{item_edition_input_success}");
return true;
} catch (Exception exception) {
player.sendMessage(ChatColor.RED + exception.getMessage());
return false;
}
}).enable("{begin_input_edition}");
}
private String appendComponentPaths(String lastKey) {
StringBuilder fullPath = new StringBuilder();
for (Entry entry : explored) {

View File

@ -25,6 +25,7 @@ public class Lore extends StringListStat {
super("LORE", null);
setComponentType(ArrayComponentType.arrayOf(StringComponentType.init().build())
.fullUiDisplay(true)
.icon(Material.WRITABLE_BOOK)
.name("Lore")
.trimdesc("Give some tooltip lore to your item. You can't change the whole lore format from here.")

View File

@ -1,16 +1,20 @@
package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.behaviour.ItemRestriction;
import net.Indyuce.mmoitems.stat.component.type.builtin.ArrayComponentType;
import net.Indyuce.mmoitems.stat.component.type.builtin.StringComponentType;
import net.Indyuce.mmoitems.stat.component.builtin.ArrayComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import net.Indyuce.mmoitems.stat.type.StringListStat;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
@ -23,17 +27,20 @@ public class RequiredClass extends StringListStat implements ItemRestriction {
"The class you need to profess to use your item.",
new String[]{"!block", "all"});
// TODO fix old NBT syntax:
// this should be self explanatory:
// ret.add(new ItemTag(getNBTPath(), String.join(", ", ((StringListData) data).getList())));
setComponentType(ArrayComponentType.arrayOf(StringComponentType.init().build())
.noExploration(true)
.build());
setGemstoneTransferable(false);
}
@Nullable
@Override
public ArrayComponent<StringComponent> read(ReadMMOItem mmoitem) {
return readStringSeparatedStringArray(mmoitem, ", ");
}
@Override
public void write(ItemStackBuilder builder, @NotNull ArrayComponent<StringComponent> component) {
writeStringSeparatedStringArray(builder, component, ", ");
}
@Override
public boolean canUse(RPGPlayer player, NBTItem item, boolean message) {
String requiredClass = item.getString(getNBTPath());

View File

@ -122,9 +122,9 @@ public class UpgradeStat extends ItemStat<UpgradeComponent> implements Consumabl
builder.getLore().registerPlaceholder("upgrade_level", String.valueOf(component.getLevel()));
// Write to lore
if (component.getMaxUpgrades() > 0)
if (component.getMax() > 0)
builder.getLore().insert(getPath(),
getGeneralStatFormat().replace("{value}", String.valueOf(component.getMaxUpgrades())));
getGeneralStatFormat().replace("{value}", String.valueOf(component.getMax())));
}
@Deprecated

View File

@ -4,6 +4,7 @@ package net.Indyuce.mmoitems.stat.component.builtin;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.type.builtin.BooleanComponentType;
import org.apache.commons.lang.NotImplementedException;
import org.jetbrains.annotations.Nullable;
public class BooleanComponent extends StatComponent {
private boolean value;
@ -45,4 +46,13 @@ public class BooleanComponent extends StatComponent {
throw new NotImplementedException();
}
}
public static boolean getValue(@Nullable BooleanComponent component) {
return component != null && component.value;
}
@Nullable
public static BooleanComponent from(boolean value){
return value ? new BooleanComponent(true) : null;
}
}

View File

@ -2,6 +2,7 @@ package net.Indyuce.mmoitems.stat.component.builtin;
import net.Indyuce.mmoitems.stat.component.type.builtin.NumberComponentType;
import org.apache.commons.lang.NotImplementedException;
import org.jetbrains.annotations.Nullable;
public class DoubleComponent extends NumberComponent {
private double value;
@ -73,4 +74,13 @@ public class DoubleComponent extends NumberComponent {
throw new NotImplementedException();
}
}
public static double getValue(@Nullable DoubleComponent component) {
return component == null ? 0 : component.value;
}
@Nullable
public static DoubleComponent from(double value) {
return value == 0 ? null : new DoubleComponent(value);
}
}

View File

@ -71,7 +71,12 @@ public class IntegerComponent extends NumberComponent {
}
}
public static int asInteger(@Nullable IntegerComponent component) {
public static int getValue(@Nullable IntegerComponent component) {
return component == null ? 0 : component.value;
}
@Nullable
public static IntegerComponent from(int value) {
return value == 0 ? null : new IntegerComponent(value);
}
}

View File

@ -70,6 +70,11 @@ public class StringComponent extends StatComponent {
return component == null ? null : component.value;
}
@Nullable
public static StringComponent fromString(@Nullable String input) {
return input == null || input.isEmpty() ? null : new StringComponent(input);
}
@Nullable
public static UUID asUniqueId(@Nullable StringComponent component) {
return component == null ? null : UUID.fromString(component.toString());

View File

@ -34,7 +34,7 @@ public class ColorComponent extends ObjectComponent {
}
public int getRed() {
return IntegerComponent.asInteger(red);
return IntegerComponent.getValue(red);
}
public void setRed(int red) {
@ -42,7 +42,7 @@ public class ColorComponent extends ObjectComponent {
}
public int getGreen() {
return IntegerComponent.asInteger(green);
return IntegerComponent.getValue(green);
}
public void setGreen(int green) {
@ -50,7 +50,7 @@ public class ColorComponent extends ObjectComponent {
}
public int getBlue() {
return IntegerComponent.asInteger(blue);
return IntegerComponent.getValue(blue);
}
public void setBlue(int blue) {

View File

@ -29,19 +29,35 @@ public class CommandComponent extends ObjectComponent {
@Nullable
public String getCommand() {
return command == null ? null : command.getValue();
return StringComponent.getValue(command);
}
public void setCommand(@Nullable String command) {
this.command = StringComponent.fromString(command);
}
public double getDelay() {
return delay == null ? 0 : delay.getValue();
return DoubleComponent.getValue(delay);
}
public void setDelay(double delay) {
this.delay = DoubleComponent.from(delay);
}
public boolean isConsoleCommand() {
return console != null && console.getValue();
return BooleanComponent.getValue(console);
}
public void setConsole(boolean console) {
this.console = BooleanComponent.from(console);
}
public boolean hasOpPerms() {
return op != null && op.getValue();
return BooleanComponent.getValue(op);
}
public void setOp(boolean op) {
this.op = BooleanComponent.from(op);
}
//endregion

View File

@ -43,7 +43,7 @@ public class SoulboundComponent extends ObjectComponent {
}
public int getLevel() {
return IntegerComponent.asInteger(level);
return IntegerComponent.getValue(level);
}
public void setLevel(int level) {

View File

@ -5,50 +5,85 @@ import net.Indyuce.mmoitems.api.UpgradeTemplate;
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.stat.annotation.InvalidComponentKeyException;
import net.Indyuce.mmoitems.stat.component.StatComponent;
import net.Indyuce.mmoitems.stat.component.builtin.IntegerComponent;
import net.Indyuce.mmoitems.stat.component.builtin.ObjectComponent;
import net.Indyuce.mmoitems.stat.component.builtin.StringComponent;
import net.Indyuce.mmoitems.stat.component.builtin.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
// TODO Finish
@Deprecated
public class UpgradeComponent extends ObjectComponent {
private IntegerComponent level, min, max;
private StringComponent template;
/*
private String reference;
private boolean workbench;
private boolean destroy;
private double success;
*/
private DoubleComponent success;
private BooleanComponent destroyOnFail, workbench;
private StringComponent reference;
public UpgradeComponent() {
// Empty constructor
}
public static final String LEVEL = "Level";
public static final String MIN = "Min";
public static final String MAX = "Max";
public static final String TEMPLATE = "Template";
public static final String SUCCESS = "Success";
public static final String DESTROY_ON_FAIL = "Destroy";
public static final String WORKBENCH = "Workbench";
public static final String REFERENCE = "Reference";
//region API
public double getSuccess() {
return 0;
return DoubleComponent.getValue(success);
}
public int getMaxUpgrades() {
return 0;
public void setSuccess(double success) {
this.success = success <= 0 ? null : new DoubleComponent(Math.min(1, success));
}
public int getMin() {
return IntegerComponent.getValue(min);
}
public void setMin(int min) {
this.min = IntegerComponent.from(min);
}
public int getMax() {
return IntegerComponent.getValue(max);
}
public void setMax(int max) {
this.max = IntegerComponent.from(max);
}
public boolean destroysOnFail() {
return false;
return BooleanComponent.getValue(destroyOnFail);
}
public void setDestroyOnFail(boolean destroyOnFail) {
this.destroyOnFail = BooleanComponent.from(destroyOnFail);
}
public boolean canLevelUp() {
return false;
int max = getMax();
return max == 0 || getLevel() < max;
}
public boolean isWorkbench() {
return false;
return BooleanComponent.getValue(workbench);
}
public void setWorkbench(boolean workbench) {
this.workbench = BooleanComponent.from(workbench);
}
public String getReference() {
return "";
return StringComponent.getValue(reference);
}
public void setReference(@Nullable String reference) {
this.reference = StringComponent.fromString(reference);
}
@Nullable
@ -56,48 +91,16 @@ public class UpgradeComponent extends ObjectComponent {
return template == null ? null : MMOItems.plugin.getUpgrades().getTemplate(template.getValue());
}
/*
public String getTemplateName() {
return template==null ? null
}
*/
public void setTemplate(@Nullable UpgradeTemplate template) {
this.template = template == null ? null : new StringComponent(template.getId());
}
public int getMax() {
return max == null ? 0 : max.getValue();
}
public void setMax(int max) {
this.max = max == 0 ? null : new IntegerComponent(max);
}
public int getMin() {
return min == null ? 0 : min.getValue();
}
public void setMin(int min) {
this.min = min == 0 ? null : new IntegerComponent(min);
}
public int getLevel() {
return level == null ? 0 : level.getValue();
return IntegerComponent.getValue(level);
}
public void setLevel(int level) {
this.level = level == 0 ? null : new IntegerComponent(level);
}
@NotNull
public UpgradeComponent clone() {
UpgradeComponent clone = new UpgradeComponent();
clone.setTemplate(getTemplate());
clone.setLevel(getLevel());
clone.setMax(getMax());
clone.setMin(getMin());
return clone;
this.level = IntegerComponent.from(level);
}
public void upgrade(@NotNull MMOItem mmoitem) {
@ -112,11 +115,51 @@ return template==null ? null
//endregion
@NotNull
public UpgradeComponent clone() {
UpgradeComponent clone = new UpgradeComponent();
clone.level = StatComponent.clone(level);
clone.min = StatComponent.clone(min);
clone.max = StatComponent.clone(max);
clone.template = StatComponent.clone(template);
clone.success = StatComponent.clone(success);
clone.destroyOnFail = StatComponent.clone(destroyOnFail);
clone.workbench = StatComponent.clone(workbench);
clone.reference = StatComponent.clone(reference);
return clone;
}
//region ObjectComponent Interface
@Override
public void set(@NotNull String key, @Nullable StatComponent component) {
switch (key) {
case LEVEL:
level = (IntegerComponent) component;
return;
case MIN:
min = (IntegerComponent) component;
return;
case MAX:
max = (IntegerComponent) component;
return;
case TEMPLATE:
template = (StringComponent) component;
return;
case SUCCESS:
success = (DoubleComponent) component;
return;
case DESTROY_ON_FAIL:
destroyOnFail = (BooleanComponent) component;
return;
case WORKBENCH:
workbench = (BooleanComponent) component;
return;
case REFERENCE:
reference = (StringComponent) component;
return;
default:
throw new InvalidComponentKeyException(this, key);
}
@ -126,6 +169,22 @@ return template==null ? null
@Override
public StatComponent get(@NotNull String key) {
switch (key) {
case LEVEL:
return level;
case MIN:
return min;
case MAX:
return max;
case TEMPLATE:
return template;
case SUCCESS:
return success;
case DESTROY_ON_FAIL:
return destroyOnFail;
case WORKBENCH:
return workbench;
case REFERENCE:
return reference;
default:
throw new InvalidComponentKeyException(this, key);
}
@ -133,7 +192,14 @@ return template==null ? null
@Override
public void forEachComponent(@NotNull BiConsumer<String, StatComponent> action) {
if (level != null) action.accept(LEVEL, level);
if (min != null) action.accept(MIN, min);
if (max != null) action.accept(MAX, max);
if (template != null) action.accept(TEMPLATE, template);
if (success != null) action.accept(SUCCESS, success);
if (destroyOnFail != null) action.accept(DESTROY_ON_FAIL, destroyOnFail);
if (workbench != null) action.accept(WORKBENCH, workbench);
if (reference != null) action.accept(REFERENCE, reference);
}
//endregion

View File

@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class ArrayModel<C extends StatComponent> implements Model<ArrayComponent<C>> {
@ -33,6 +34,14 @@ public class ArrayModel<C extends StatComponent> implements Model<ArrayComponent
return found == null ? null : found.getValue();
}
public String findNewKey() {
AtomicInteger counter = new AtomicInteger(1);
while (models.stream().anyMatch(pair -> pair.getKey().equals(String.valueOf(counter.get())))) {
counter.incrementAndGet();
}
return String.valueOf(counter);
}
public ArrayComponent<C> randomize(ArrayComponentType<C> componentType, MMOItemBuilder builder) {
ArrayComponent<C> randomized = new ArrayComponent<>();

View File

@ -55,12 +55,18 @@ public abstract class ComponentType<M extends Model<C>, C extends StatComponent>
private Function<M, String> name;
private Function<M, List<String>> description;
private List<String> actionLoreTags = Arrays.asList("Left click to change this value.", "Right click to reset this value.");
protected List<String> actionLoreTags = DEFAULT_ACTION_LORE_TAGS;
protected static final List<String> DEFAULT_ACTION_LORE_TAGS = Arrays.asList("Left click to change this value.", "Right click to reset this value.");
public boolean isTerminal() {
return terminal;
}
public boolean isInputable() {
return inputable;
}
@NotNull
public ItemStack provideIcon(@Nullable M model) {
return icon.apply(model);

View File

@ -40,11 +40,6 @@ public class ArrayComponentType<C extends StatComponent> extends ComponentType<A
@BackwardsCompatibility(version = "7.0")
private Predicate<String> keyValidator;
/**
*
*/
private boolean noExploration;
private String listCountFormat = "Element Count: %s";
private String listElementPrefix = "";
private String emptyList = ChatColor.RED + "None";
@ -228,11 +223,6 @@ public class ArrayComponentType<C extends StatComponent> extends ComponentType<A
return this;
}
public Builder noExploration(boolean val) {
ArrayComponentType.this.noExploration = val;
return this;
}
public Builder uiFormat(@Nullable String listCountFormat, @Nullable String listElementPrefix, @Nullable String emptyList) {
if (listCountFormat != null) ArrayComponentType.this.listCountFormat = listCountFormat;
if (listElementPrefix != null) ArrayComponentType.this.listElementPrefix = listElementPrefix;
@ -243,6 +233,12 @@ public class ArrayComponentType<C extends StatComponent> extends ComponentType<A
public ArrayComponentType<C> build() {
Validate.notNull(subtype, "Subtype cannot be null");
// Better action lore tags
if (actionLoreTags.equals(DEFAULT_ACTION_LORE_TAGS) && subtype.isInputable()) {
actionLoreTags("Left click to edit.", "Shift-left click to input one value.", "Right click to remove.", "Shift-right click to remove the last value.");
}
return (ArrayComponentType<C>) super.build();
}
}

View File

@ -120,6 +120,13 @@ public class MapComponentType<C extends StatComponent> extends ComponentType<Map
return comp;
}
@NotNull
@Override
public ComponentType<?, ?> getChildType(String key) {
// No matter the key, subtype is always the same
return subtype;
}
//region Merging
@Override

View File

@ -21,13 +21,13 @@ public class RFGKeepUpgrades implements Listener {
if (!event.getOptions().shouldKeepUpgrades()
|| upgrade == null
|| newOne == null
|| newOne.getMaxUpgrades() <= 0)
|| newOne.getMax() <= 0)
return;
// Clone existing, set level and reset
// TODO unnecessary cloning? Just edit current.
UpgradeComponent processed = newOne.clone();
processed.setLevel(Math.min(upgrade.getLevel(), newOne.getMaxUpgrades()));
processed.setLevel(Math.min(upgrade.getLevel(), newOne.getMax()));
event.getNewMMOItem().setComponent(ItemStats.UPGRADE, processed);
}
}