Enable challenges to have multiple statistic requirements

This enables things like a challenge to kill 10 creepers, 10 zombies,
and 10 skeletons.
This commit is contained in:
tastybento 2025-02-10 02:47:06 -08:00
parent cf4dcaf630
commit c5334acc87
9 changed files with 1075 additions and 580 deletions

View File

@ -2,11 +2,16 @@
// Created by BONNe
// Copyright - 2021
//
// Enhanced by tastybento
package world.bentobox.challenges.database.object.requirements;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.EntityType;
@ -15,8 +20,69 @@ import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.Expose;
/**
* Requirements for statistics based challenges
*/
public class StatisticRequirements extends Requirements
{
/**
* Record for this requirement
* @param Statistic statistic
* @param EntityType entity
* @param Material material
* @param Integer amount
* @param Boolean reduceStatistic
*/
public record StatisticRec(@Expose Statistic statistic, @Expose EntityType entity, @Expose Material material,
@Expose Integer amount, @Expose Boolean reduceStatistic) {
}
/**
* Type of the statistic field.
* @deprecated Shifting to a list
*/
@Expose
@Nullable
private Statistic statistic;
/**
* Type of entity for entity related statistics.
* @deprecated Shifting to a list
*/
@Expose
@Nullable
private EntityType entity;
/**
* Type of material for block and item related statistics.
* @deprecated Shifting to a list
*/
@Expose
@Nullable
private Material material;
/**
* Amount of the stats.
* @deprecated Shifting to a list
*/
@Expose
private Integer amount;
/**
* Indicate that player statistic fields must be adjusted after completing challenges.
* @deprecated Shifting to a list
*/
@Expose
private Boolean reduceStatistic;
/**
* List of statistics that must be done for this challenge
*/
@Expose
@Nullable
private List<StatisticRec> statisticList;
/**
* Constructor Requirements creates a new Requirements instance.
*/
@ -34,12 +100,7 @@ public class StatisticRequirements extends Requirements
public Requirements copy()
{
StatisticRequirements requirements = new StatisticRequirements();
requirements.setStatistic(this.statistic);
requirements.setEntity(this.entity);
requirements.setMaterial(this.material);
requirements.setAmount(this.amount);
requirements.setReduceStatistic(this.reduceStatistic);
requirements.setStatisticList(this.getRequiredStatistics());
return requirements;
}
@ -47,183 +108,33 @@ public class StatisticRequirements extends Requirements
@Override
public boolean isValid()
{
if (!super.isValid())
{
return false;
// TODO - do something here?
return super.isValid();
}
/**
* @return the statisticList
*/
public List<StatisticRec> getRequiredStatistics() {
if (statisticList == null) {
statisticList = new ArrayList<>();
// Convert old single statistic entries to new list of records
if (statistic != null) {
StatisticRec rec = new StatisticRec(this.statistic, this.entity, this.material, this.amount,
this.reduceStatistic);
statisticList.add(rec);
}
}
return statisticList;
}
if (this.statistic == null)
{
return false;
}
return switch (this.statistic.getType())
{
case ITEM -> this.material != null && this.material.isItem();
case BLOCK -> this.material != null && this.material.isBlock();
case ENTITY -> this.entity != null;
case UNTYPED -> true;
};
/**
* @param value the statisticList to set
*/
public void setStatisticList(Collection<StatisticRec> value) {
// If value is null, assign null; otherwise, create a new ArrayList from value.
this.statisticList = (value == null) ? null : new ArrayList<>(value);
}
// ---------------------------------------------------------------------
// Section: Getters and setters
// ---------------------------------------------------------------------
/**
* Gets statistic.
*
* @return the statistic
*/
@Nullable
public Statistic getStatistic()
{
return statistic;
}
/**
* Sets statistic.
*
* @param statistic the statistic
*/
public void setStatistic(@Nullable Statistic statistic)
{
this.statistic = statistic;
}
/**
* Gets entity.
*
* @return the entity
*/
@Nullable
public EntityType getEntity()
{
return entity;
}
/**
* Sets entity.
*
* @param entity the entity
*/
public void setEntity(@Nullable EntityType entity)
{
this.entity = entity;
}
/**
* Gets material.
*
* @return the material
*/
@Nullable
public Material getMaterial()
{
return material;
}
/**
* Sets material.
*
* @param material the material
*/
public void setMaterial(@Nullable Material material)
{
this.material = material;
}
/**
* Gets amount.
*
* @return the amount
*/
public int getAmount()
{
return amount;
}
/**
* Sets amount.
*
* @param amount the amount
*/
public void setAmount(int amount)
{
this.amount = amount;
}
/**
* Is reduce statistic boolean.
*
* @return the boolean
*/
public boolean isReduceStatistic()
{
return reduceStatistic;
}
/**
* Sets reduce statistic.
*
* @param reduceStatistic the reduce statistic
*/
public void setReduceStatistic(boolean reduceStatistic)
{
this.reduceStatistic = reduceStatistic;
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* Type of the statistic field.
*/
@Expose
@Nullable
private Statistic statistic;
/**
* Type of entity for entity related statistics.
*/
@Expose
@Nullable
private EntityType entity;
/**
* Type of material for block and item related statistics.
*/
@Expose
@Nullable
private Material material;
/**
* Amount of the stats.
*/
@Expose
private int amount;
/**
* Indicate that player statistic fields must be adjusted after completing challenges.
*/
@Expose
private boolean reduceStatistic;
}

View File

@ -50,6 +50,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme
import world.bentobox.challenges.database.object.requirements.IslandRequirements;
import world.bentobox.challenges.database.object.requirements.OtherRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -356,13 +357,12 @@ public class ChallengesImportManager
case STATISTIC_TYPE -> {
StatisticRequirements requirements = new StatisticRequirements();
challenge.setRequirements(requirements);
requirements.setAmount(section.getInt("amount", 0));
requirements.setReduceStatistic(section.getBoolean("reduce", false));
requirements.setStatistic(matchStatistic(section.getString("statistic")));
requirements.setEntity(matchEntity(section.getString("entity")));
requirements.setMaterial(matchMaterial(section.getString("material")));
List<StatisticRec> list = new ArrayList<>();
list.add(new StatisticRec(matchStatistic(section.getString("statistic")),
matchEntity(section.getString("entity")), matchMaterial(section.getString("material")),
section.getInt("amount", 0), section.getBoolean("reduce", false)));
// TODO: Add support for multiple stat challenge
requirements.setStatisticList(list);
}
}

View File

@ -13,6 +13,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
@ -29,6 +30,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme
import world.bentobox.challenges.database.object.requirements.IslandRequirements;
import world.bentobox.challenges.database.object.requirements.OtherRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec;
import world.bentobox.challenges.managers.ChallengesManager;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.LevelStatus;
@ -467,7 +469,7 @@ public abstract class CommonPanel {
}
/**
* This method generates lore message for Statistic requirement.
* This method generates lore message for Statistic requirements.
*
* @param requirement Statistic Requirement.
* @return Requirement lore message.
@ -475,48 +477,49 @@ public abstract class CommonPanel {
private String generateStatisticChallenge(StatisticRequirements requirement) {
final String reference = Constants.DESCRIPTIONS + "challenge.requirements.statistic.";
String statistic;
if (requirement.getStatistic() == null) {
if (requirement.getRequiredStatistics().isEmpty()) {
// Challenges by default comes with empty statistic field.
return "";
}
switch (requirement.getStatistic().getType()) {
case UNTYPED -> statistic = this.user.getTranslationOrNothing(reference + "statistic", "[statistic]",
Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]",
String.valueOf(requirement.getAmount()));
StringBuilder statistics = new StringBuilder();
for (StatisticRec s : requirement.getRequiredStatistics()) {
String statistic = switch (s.statistic().getType()) {
case UNTYPED -> this.user.getTranslationOrNothing(reference + "statistic", "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount()));
case ITEM, BLOCK -> {
if (requirement.getAmount() > 1) {
statistic = this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]",
Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]",
String.valueOf(requirement.getAmount()), "[target]",
Utils.prettifyObject(requirement.getMaterial(), this.user));
if (s.amount() > 1) {
yield this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount()),
"[target]", Utils.prettifyObject(s.material(), this.user));
} else {
statistic = this.user.getTranslationOrNothing(reference + "single-target", "[statistic]",
Utils.prettifyObject(requirement.getStatistic(), this.user), "[target]",
Utils.prettifyObject(requirement.getMaterial(), this.user));
yield this.user.getTranslationOrNothing(reference + "single-target", "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[target]",
Utils.prettifyObject(s.material(), this.user));
}
}
case ENTITY -> {
if (requirement.getAmount() > 1) {
statistic = this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]",
Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]",
String.valueOf(requirement.getAmount()), "[target]",
Utils.prettifyObject(requirement.getEntity(), this.user));
if (s.amount() > 1) {
yield this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount()),
"[target]", Utils.prettifyObject(s.entity(), this.user));
} else {
statistic = this.user.getTranslationOrNothing(reference + "single-target", "[statistic]",
Utils.prettifyObject(requirement.getStatistic(), this.user), "[target]",
Utils.prettifyObject(requirement.getEntity(), this.user));
yield this.user.getTranslationOrNothing(reference + "single-target", "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[target]",
Utils.prettifyObject(s.entity(), this.user));
}
}
default -> statistic = "";
default -> "";
};
String warning = s.reduceStatistic() ? this.user.getTranslationOrNothing(reference + "warning")
: "";
statistics.append(this.user.getTranslationOrNothing(reference + "lore", "[statistic]", statistic, "[warning]",
warning));
statistics.append("\n");
}
String warning = requirement.isReduceStatistic() ? this.user.getTranslationOrNothing(reference + "warning")
: "";
return this.user.getTranslationOrNothing(reference + "lore", "[statistic]", statistic, "[warning]", warning);
return statistics.toString();
}
/**

View File

@ -35,9 +35,6 @@ import world.bentobox.challenges.panel.ConversationUtils;
import world.bentobox.challenges.panel.util.EnvironmentSelector;
import world.bentobox.challenges.panel.util.ItemSelector;
import world.bentobox.challenges.panel.util.MultiBlockSelector;
import world.bentobox.challenges.panel.util.SingleBlockSelector;
import world.bentobox.challenges.panel.util.SingleEntitySelector;
import world.bentobox.challenges.panel.util.StatisticSelector;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -229,24 +226,8 @@ public class EditChallengePanel extends CommonPanel {
* @param panelBuilder PanelBuilder where icons must be added.
*/
private void buildStatisticRequirementsPanel(PanelBuilder panelBuilder) {
panelBuilder.item(10, this.createRequirementButton(RequirementButton.STATISTIC));
panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_STATISTIC));
panelBuilder.item(11, this.createRequirementButton(RequirementButton.STATISTIC_AMOUNT));
StatisticRequirements requirements = this.challenge.getRequirements();
if (requirements.getStatistic() != null) {
switch (requirements.getStatistic().getType()) {
case ITEM -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ITEMS));
case BLOCK -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_BLOCKS));
case ENTITY -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ENTITIES));
default -> {
}
}
}
panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS));
// Give user the ability to add or remove statistics
panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_STATISTICS));
}
/**
@ -627,9 +608,9 @@ public class EditChallengePanel extends CommonPanel {
case REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY -> {
return this.createOtherRequirementButton(button);
}
// Buttons for Statistic Requirements
case STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES, STATISTIC_AMOUNT, REMOVE_STATISTIC -> {
return this.createStatisticRequirementButton(button);
// Statistics
case REQUIRED_STATISTICS -> {
return this.createStatisticsRequirementButton(button);
}
// Default behaviour.
default -> {
@ -702,8 +683,8 @@ public class EditChallengePanel extends CommonPanel {
description.add(this.user.getTranslation(reference + "title"));
// Add Material Tags only
requirements.getRequiredMaterialTags()
.forEach((block, count) -> description.add(this.user.getTranslation(reference + "list",
"[tag]", Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count))));
.forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", "[tag]",
Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count))));
}
icon = new ItemStack(Material.STONE_BRICKS);
@ -724,8 +705,8 @@ public class EditChallengePanel extends CommonPanel {
description.add(this.user.getTranslation(reference + "title"));
// Add Material Tags only
requirements.getRequiredEntityTypeTags()
.forEach((block, count) -> description.add(this.user.getTranslation(reference + "list",
"[tag]", Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count))));
.forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", "[tag]",
Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count))));
}
icon = new ItemStack(Material.ZOMBIE_HEAD);
@ -811,6 +792,62 @@ public class EditChallengePanel extends CommonPanel {
.clickHandler(clickHandler).build();
}
/**
* This method creates buttons for inventory requirements menu.
*
* @param button Button which panel item must be created.
* @return PanelItem that represents given button.
*/
private PanelItem createStatisticsRequirementButton(RequirementButton button) {
final String reference = Constants.BUTTON + button.name().toLowerCase() + ".";
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon;
boolean glow;
PanelItem.ClickHandler clickHandler;
final StatisticRequirements requirements = this.challenge.getRequirements();
switch (button) {
// Just one special statistic button right now
case REQUIRED_STATISTICS -> {
// TODO rename getStaticicList() to getRequiredStatistics()
if (requirements.getRequiredStatistics().isEmpty()) {
description.add(this.user.getTranslation(reference + "none"));
} else {
description.add(this.user.getTranslation(reference + "title"));
requirements.getRequiredStatistics().stream()
.sorted(Comparator.comparing(r -> r.statistic().getKey().getKey()))
// Just list the name of the statistic
.forEach(sr -> description.add(this.user.getTranslationOrNothing(reference + "list", "[name]",
Utils.prettifyObject(sr.statistic(), user))));
}
icon = new ItemStack(Material.CHEST);
clickHandler = (panel, user, clickType, slot) -> {
// Deal with adding and removing statistics in the MultiStatisticSelector class
ManageStatisticsPanel.open(this, requirements.getRequiredStatistics());
return true;
};
glow = false;
}
default -> {
// This should never need to be shown. Just for future expansion.
glow = false;
icon = new ItemStack(Material.PAPER);
clickHandler = null;
}
}
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow)
.clickHandler(clickHandler).build();
}
/**
* This method creates buttons for inventory requirements menu.
*
@ -1085,163 +1122,6 @@ public class EditChallengePanel extends CommonPanel {
.clickHandler(clickHandler).build();
}
/**
* Creates a button for statistic requirements.
*
* @param button Button that must be created.
* @return PanelItem button.
*/
private PanelItem createStatisticRequirementButton(RequirementButton button) {
final String reference = Constants.BUTTON + button.name().toLowerCase() + ".";
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon;
boolean glow;
PanelItem.ClickHandler clickHandler;
final StatisticRequirements requirements = this.challenge.getRequirements();
switch (button) {
case STATISTIC -> {
description.add(this.user.getTranslation(reference + "value", "[statistic]",
Utils.prettifyObject(requirements.getStatistic(), this.user)));
icon = new ItemStack(requirements.getStatistic() == null ? Material.BARRIER : Material.PAPER);
clickHandler = (panel, user, clickType, slot) -> {
StatisticSelector.open(this.user, (status, statistic) -> {
if (status) {
requirements.setStatistic(statistic);
requirements.setMaterial(null);
requirements.setEntity(null);
requirements.setAmount(0);
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_AMOUNT -> {
description.add(this.user.getTranslation(reference + "value", Constants.PARAMETER_NUMBER,
String.valueOf(requirements.getAmount())));
icon = new ItemStack(Material.CHEST);
clickHandler = (panel, user, clickType, i) -> {
Consumer<Number> numberConsumer = number -> {
if (number != null) {
requirements.setAmount(number.intValue());
}
// reopen panel
this.build();
};
ConversationUtils.createNumericInput(numberConsumer, this.user,
this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), 0, Integer.MAX_VALUE);
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case REMOVE_STATISTIC -> {
description.add(
this.user.getTranslation(reference + (requirements.isReduceStatistic() ? "enabled" : "disabled")));
icon = new ItemStack(Material.LEVER);
clickHandler = (panel, user, clickType, slot) -> {
requirements.setReduceStatistic(!requirements.isReduceStatistic());
this.build();
return true;
};
glow = requirements.isReduceStatistic();
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle"));
}
case STATISTIC_BLOCKS -> {
description.add(this.user.getTranslation(reference + "value", "[block]",
Utils.prettifyObject(requirements.getMaterial(), this.user)));
icon = requirements.getMaterial() == null ? new ItemStack(Material.BARRIER)
: new ItemStack(requirements.getMaterial());
clickHandler = (panel, user, clickType, slot) -> {
SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.BLOCKS, (status, block) -> {
if (status) {
requirements.setMaterial(block);
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_ITEMS -> {
description.add(this.user.getTranslation(reference + "value", "[item]",
Utils.prettifyObject(requirements.getMaterial(), this.user)));
icon = requirements.getMaterial() == null ? new ItemStack(Material.BARRIER)
: new ItemStack(requirements.getMaterial());
clickHandler = (panel, user, clickType, slot) -> {
SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.ITEMS, (status, block) -> {
if (status) {
requirements.setMaterial(block);
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_ENTITIES -> {
description.add(this.user.getTranslation(reference + "value", "[entity]",
Utils.prettifyObject(requirements.getEntity(), this.user)));
icon = requirements.getEntity() == null ? new ItemStack(Material.BARRIER)
: new ItemStack(PanelUtils.getEntityEgg(requirements.getEntity()));
clickHandler = (panel, user, clickType, slot) -> {
SingleEntitySelector.open(this.user, true, (status, entity) -> {
if (status) {
requirements.setEntity(entity);
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
default -> {
icon = new ItemStack(Material.PAPER);
clickHandler = null;
glow = false;
}
}
return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow)
.clickHandler(clickHandler).build();
}
/**
* This method creates buttons for rewards menu.
*
@ -1791,11 +1671,13 @@ public class EditChallengePanel extends CommonPanel {
/**
* Represents different requirement buttons that are used in menus.
*/
private enum RequirementButton {
public enum RequirementButton {
REQUIRED_ENTITIES, REMOVE_ENTITIES, REQUIRED_BLOCKS, REMOVE_BLOCKS, SEARCH_RADIUS, REQUIRED_PERMISSIONS,
REQUIRED_ITEMS, REMOVE_ITEMS, ADD_IGNORED_META, REMOVE_IGNORED_META, REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE,
REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY, STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES,
STATISTIC_AMOUNT, REMOVE_STATISTIC, REQUIRED_MATERIALTAGS, REQUIRED_ENTITYTAGS,
REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY, STATISTIC_SHOW, STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS,
STATISTIC_ENTITIES, NEW_STATISTIC,
STATISTIC_AMOUNT, REMOVE_STATISTIC, REQUIRED_MATERIALTAGS, REQUIRED_ENTITYTAGS, REQUIRED_STATISTICS,
REMOVE_STATISTICS,
}
// ---------------------------------------------------------------------

View File

@ -0,0 +1,616 @@
package world.bentobox.challenges.panel.admin;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.Nullable;
import lv.id.bonne.panelutils.PanelUtils;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec;
import world.bentobox.challenges.panel.CommonPagedPanel;
import world.bentobox.challenges.panel.CommonPanel;
import world.bentobox.challenges.panel.ConversationUtils;
import world.bentobox.challenges.panel.admin.EditChallengePanel.RequirementButton;
import world.bentobox.challenges.panel.util.SingleBlockSelector;
import world.bentobox.challenges.panel.util.SingleEntitySelector;
import world.bentobox.challenges.panel.util.StatisticSelector;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
/**
* This class allows to edit material that are in required material map.
*/
public class ManageStatisticsPanel extends CommonPagedPanel<StatisticRec>
{
// ---------------------------------------------------------------------
// Section: Enums
// ---------------------------------------------------------------------
/**
* Functional buttons in current GUI.
*/
private enum Button {
ADD_STATISTIC, REMOVE_STATISTIC
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* Contains selected stats.
*/
private final Set<StatisticRec> selectedStats;
/**
* List of required statistics
*/
private final List<StatisticRec> statisticsList;
/**
* Stores filtered items.
*/
private List<StatisticRec> filterElements;
private ManageStatisticsPanel(CommonPanel parentGUI, List<StatisticRec> statisticsList)
{
super(parentGUI);
this.statisticsList = statisticsList;
// Sort tags by their ordinal value.
this.statisticsList.sort(Comparator.comparing(tag -> tag.statistic().getKey().getKey()));
this.selectedStats = new HashSet<>();
// Init without filters applied.
this.filterElements = this.statisticsList;
}
/**
* Open the Challenges Admin GUI.
*/
public static void open(CommonPanel parentGUI, List<StatisticRec> statisticsList) {
new ManageStatisticsPanel(parentGUI, statisticsList).build();
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* This method is called when filter value is updated.
*/
@Override
protected void updateFilters()
{
if (this.searchString == null || this.searchString.isBlank())
{
this.filterElements = this.statisticsList;
}
else
{
this.filterElements = this.statisticsList.stream().
filter(element -> {
// If element name is set and name contains search field, then do not filter out.
return element.statistic().getKey().getKey().toLowerCase(Locale.ENGLISH)
.contains(this.searchString.toLowerCase(Locale.ENGLISH));
}).
distinct().
collect(Collectors.toList());
}
}
/**
* This method builds all necessary elements in GUI panel.
*/
@Override
protected void build()
{
PanelBuilder panelBuilder = new PanelBuilder().user(this.user).
name(this.user.getTranslation(Constants.TITLE + "manage-statistics"));
// Create nice border.
PanelUtils.fillBorder(panelBuilder);
panelBuilder.item(3, this.createButton(Button.ADD_STATISTIC));
panelBuilder.item(5, this.createButton(Button.REMOVE_STATISTIC));
// Fill the box with what is selected
this.populateElements(panelBuilder, this.filterElements);
// Add return button.
panelBuilder.item(44, this.returnButton);
panelBuilder.build();
}
/**
* This method creates PanelItem button of requested type.
* @param button Button which must be created.
* @return new PanelItem with requested functionality.
*/
private PanelItem createButton(Button button)
{
final String reference = Constants.BUTTON + button.name().toLowerCase() + ".";
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon;
PanelItem.ClickHandler clickHandler;
boolean glow;
switch (button)
{
case ADD_STATISTIC -> {
icon = new ItemStack(Material.BUCKET);
clickHandler = (panel, user1, clickType, slot) ->
{
StatisticSelector.open(this.user, (status, statistic) ->
{
if (status)
{
StatisticRec newItem = new StatisticRec(statistic, null, null, 0, false);
this.statisticsList.add(newItem);
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-add"));
}
case REMOVE_STATISTIC -> {
if (!this.selectedStats.isEmpty())
{
description.add(this.user.getTranslation(reference + "title"));
this.selectedStats.forEach(stat ->
description.add(this.user.getTranslation(reference + "statistic_element", "[statistic]",
Utils.prettifyObject(stat.statistic(), this.user))));
}
icon = new ItemStack(Material.LAVA_BUCKET);
clickHandler = (panel, user1, clickType, slot) ->
{
if (!this.selectedStats.isEmpty())
{
this.statisticsList.removeAll(this.selectedStats);
this.selectedStats.clear();
this.build();
}
return true;
};
glow = !this.selectedStats.isEmpty();
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove"));
}
default -> {
icon = new ItemStack(Material.PAPER);
clickHandler = null;
glow = false;
}
}
return new PanelItemBuilder().
icon(icon).
name(name).
description(description).
clickHandler(clickHandler).
glow(glow).
build();
}
/**
* This method creates button for given stat.
* @param rec material which button must be created.
* @return new Button for material.
*/
@Override
protected PanelItem createElementButton(StatisticRec rec)
{
final String reference = Constants.BUTTON + "statistic_element.";
List<String> description = new ArrayList<>();
// Show everything about this statistic
// Type is not shown to the user, just used to decide what to show
switch (rec.statistic().getType()) {
case BLOCK:
description.add(this.user.getTranslation(reference + "block", "[block]",
Utils.prettifyObject(rec.material(), this.user)));
break;
case ENTITY:
description.add(this.user.getTranslation(reference + "entity", "[entity]",
Utils.prettifyObject(rec.entity(), this.user)));
break;
case ITEM:
description.add(this.user.getTranslation(reference + "item", "[item]",
Utils.prettifyObject(rec.material(), this.user)));
break;
default:
break;
}
// Amount
description.add(this.user.getTranslation(reference + "amount", Constants.PARAMETER_NUMBER,
String.valueOf(Objects.requireNonNullElse(rec.amount(), 0))));
// Removal
description.add(this.user.getTranslation(reference + "remove.name", "[value]",
this.user.getTranslation(reference + "remove.value."
+ (Objects.requireNonNullElse(rec.reduceStatistic(), false) ? "enabled" : "disabled"))));
if (this.selectedStats.contains(rec))
{
description.add(this.user.getTranslation(reference + "selected"));
}
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose"));
if (this.selectedStats.contains(rec))
{
description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect"));
}
else
{
description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select"));
}
return new PanelItemBuilder().
name(this.user.getTranslation(reference + "name", "[statistic]",
Utils.prettifyObject(rec.statistic(), this.user)))
.icon(getStatisticIcon(rec.statistic())).
description(description).
clickHandler((panel, user1, clickType, slot) -> {
// On right click change which entities are selected for deletion.
if (clickType.isRightClick())
{
if (!this.selectedStats.add(rec))
{
// Remove material if it is already selected
this.selectedStats.remove(rec);
}
this.build();
}
else
{
// Left click
this.buildStatisticPanel(rec);
}
return true;
}).
glow(this.selectedStats.contains(rec)).
build();
}
/**
* Get an icon for a Statistic. Hand selected!
* @param stat Statistic
* @return ItemStack icon
*/
public static ItemStack getStatisticIcon(Statistic stat) {
return switch (stat) {
case ANIMALS_BRED -> new ItemStack(Material.WHEAT);
case ARMOR_CLEANED -> new ItemStack(Material.LEATHER_CHESTPLATE);
case AVIATE_ONE_CM -> new ItemStack(Material.ELYTRA);
case BANNER_CLEANED -> new ItemStack(Material.RED_BANNER);
case BEACON_INTERACTION -> new ItemStack(Material.BEACON);
case BELL_RING -> new ItemStack(Material.BELL);
case BOAT_ONE_CM -> new ItemStack(Material.OAK_BOAT);
case BREAK_ITEM -> new ItemStack(Material.AMETHYST_SHARD);
case BREWINGSTAND_INTERACTION -> new ItemStack(Material.BREWING_STAND);
case CAKE_SLICES_EATEN -> new ItemStack(Material.CAKE);
case CAULDRON_FILLED -> new ItemStack(Material.CAULDRON);
case CAULDRON_USED -> new ItemStack(Material.CAULDRON);
case CHEST_OPENED -> new ItemStack(Material.CHEST);
case CLEAN_SHULKER_BOX -> new ItemStack(Material.SHULKER_BOX);
case CLIMB_ONE_CM -> new ItemStack(Material.LADDER);
case CRAFTING_TABLE_INTERACTION -> new ItemStack(Material.CRAFTING_TABLE);
case CRAFT_ITEM -> new ItemStack(Material.CRAFTING_TABLE);
case CROUCH_ONE_CM -> new ItemStack(Material.PAPER);
case DAMAGE_ABSORBED -> new ItemStack(Material.IRON_SWORD);
case DAMAGE_BLOCKED_BY_SHIELD -> new ItemStack(Material.SHIELD);
case DAMAGE_DEALT -> new ItemStack(Material.DIAMOND_SWORD);
case DAMAGE_DEALT_ABSORBED -> new ItemStack(Material.GOLDEN_SWORD);
case DAMAGE_DEALT_RESISTED -> new ItemStack(Material.WOODEN_SWORD);
case DAMAGE_RESISTED -> new ItemStack(Material.IRON_HELMET);
case DAMAGE_TAKEN -> new ItemStack(Material.IRON_LEGGINGS);
case DEATHS -> new ItemStack(Material.OBSIDIAN);
case DISPENSER_INSPECTED -> new ItemStack(Material.DISPENSER);
case DROP -> new ItemStack(Material.PAPER);
case DROPPER_INSPECTED -> new ItemStack(Material.DROPPER);
case DROP_COUNT -> new ItemStack(Material.PAPER);
case ENDERCHEST_OPENED -> new ItemStack(Material.ENDER_CHEST);
case ENTITY_KILLED_BY -> new ItemStack(Material.BOW);
case FALL_ONE_CM -> new ItemStack(Material.PAPER);
case FISH_CAUGHT -> new ItemStack(Material.FISHING_ROD);
case FLOWER_POTTED -> new ItemStack(Material.FLOWER_POT);
case FLY_ONE_CM -> new ItemStack(Material.ELYTRA);
case FURNACE_INTERACTION -> new ItemStack(Material.FURNACE);
case HOPPER_INSPECTED -> new ItemStack(Material.HOPPER);
case HORSE_ONE_CM -> new ItemStack(Material.IRON_HORSE_ARMOR);
case INTERACT_WITH_ANVIL -> new ItemStack(Material.ANVIL);
case INTERACT_WITH_BLAST_FURNACE -> new ItemStack(Material.BLAST_FURNACE);
case INTERACT_WITH_CAMPFIRE -> new ItemStack(Material.CAMPFIRE);
case INTERACT_WITH_CARTOGRAPHY_TABLE -> new ItemStack(Material.CARTOGRAPHY_TABLE);
case INTERACT_WITH_GRINDSTONE -> new ItemStack(Material.GRINDSTONE);
case INTERACT_WITH_LECTERN -> new ItemStack(Material.LECTERN);
case INTERACT_WITH_LOOM -> new ItemStack(Material.LOOM);
case INTERACT_WITH_SMITHING_TABLE -> new ItemStack(Material.SMITHING_TABLE);
case INTERACT_WITH_SMOKER -> new ItemStack(Material.SMOKER);
case INTERACT_WITH_STONECUTTER -> new ItemStack(Material.STONECUTTER);
case ITEM_ENCHANTED -> new ItemStack(Material.ENCHANTED_GOLDEN_APPLE);
case JUMP -> new ItemStack(Material.RABBIT_FOOT);
case KILL_ENTITY -> new ItemStack(Material.DIAMOND_SWORD);
case LEAVE_GAME -> new ItemStack(Material.PAPER);
case MINECART_ONE_CM -> new ItemStack(Material.MINECART);
case MINE_BLOCK -> new ItemStack(Material.STONE);
case MOB_KILLS -> new ItemStack(Material.PAPER);
case NOTEBLOCK_PLAYED -> new ItemStack(Material.NOTE_BLOCK);
case NOTEBLOCK_TUNED -> new ItemStack(Material.NOTE_BLOCK);
case OPEN_BARREL -> new ItemStack(Material.BARREL);
case PICKUP -> new ItemStack(Material.PAPER);
case PIG_ONE_CM -> new ItemStack(Material.PIG_SPAWN_EGG);
case PLAYER_KILLS -> new ItemStack(Material.PLAYER_HEAD);
case PLAY_ONE_MINUTE -> new ItemStack(Material.CLOCK);
case RAID_TRIGGER -> new ItemStack(Material.OMINOUS_BOTTLE);
case RAID_WIN -> new ItemStack(Material.GOLD_INGOT);
case RECORD_PLAYED -> new ItemStack(Material.MUSIC_DISC_BLOCKS);
case SHULKER_BOX_OPENED -> new ItemStack(Material.SHULKER_BOX);
case SLEEP_IN_BED -> new ItemStack(Material.RED_BED);
case SNEAK_TIME -> new ItemStack(Material.PAPER);
case SPRINT_ONE_CM -> new ItemStack(Material.PAPER);
case STRIDER_ONE_CM -> new ItemStack(Material.STRIDER_SPAWN_EGG);
case SWIM_ONE_CM -> new ItemStack(Material.WATER_BUCKET);
case TALKED_TO_VILLAGER -> new ItemStack(Material.EMERALD);
case TARGET_HIT -> new ItemStack(Material.TARGET);
case TIME_SINCE_DEATH -> new ItemStack(Material.CLOCK);
case TIME_SINCE_REST -> new ItemStack(Material.CLOCK);
case TOTAL_WORLD_TIME -> new ItemStack(Material.CLOCK);
case TRADED_WITH_VILLAGER -> new ItemStack(Material.EMERALD);
case TRAPPED_CHEST_TRIGGERED -> new ItemStack(Material.TRAPPED_CHEST);
case USE_ITEM -> new ItemStack(Material.STICK);
case WALK_ONE_CM -> new ItemStack(Material.LEATHER_BOOTS);
case WALK_ON_WATER_ONE_CM -> new ItemStack(Material.LILY_PAD);
case WALK_UNDER_WATER_ONE_CM -> new ItemStack(Material.WATER_BUCKET);
default -> new ItemStack(Material.PAPER);
};
}
private Panel buildStatisticPanel(StatisticRec req) {
PanelBuilder panelBuilder = new PanelBuilder().user(this.user);
panelBuilder.name(this.user.getTranslation(Constants.TITLE + "statistic-selector"));
PanelUtils.fillBorder(panelBuilder);
panelBuilder.item(10, this.createStatisticRequirementButton(RequirementButton.STATISTIC, req));
panelBuilder.item(19, this.createStatisticRequirementButton(RequirementButton.REMOVE_STATISTIC, req));
panelBuilder.item(11, this.createStatisticRequirementButton(RequirementButton.STATISTIC_AMOUNT, req));
switch (req.statistic().getType()) {
case BLOCK:
panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_BLOCKS, req));
break;
case ENTITY:
panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_ENTITIES, req));
break;
case ITEM:
panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_ITEMS, req));
break;
case UNTYPED:
break;
default:
break;
}
panelBuilder.item(25, this.createStatisticRequirementButton(RequirementButton.REQUIRED_PERMISSIONS, req));
panelBuilder.item(44, this.returnButton);
return panelBuilder.build();
}
/**
* Creates a button for statistic requirements.
*
* @param button Button that must be created.
* @param req
* @return PanelItem button.
*/
private PanelItem createStatisticRequirementButton(RequirementButton button, StatisticRec req) {
final String reference = Constants.BUTTON + button.name().toLowerCase(Locale.ENGLISH) + ".";
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon;
boolean glow;
PanelItem.ClickHandler clickHandler;
switch (button) {
case STATISTIC -> {
description.add(this.user.getTranslation(reference + "value", "[statistic]",
Utils.prettifyObject(req.statistic(), this.user)));
icon = new ItemStack(req.statistic() == null ? Material.BARRIER : Material.PAPER);
clickHandler = (panel, user, clickType, slot) -> {
StatisticSelector.open(this.user, (status, statistic) -> {
if (status) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(statistic, null, null, 0, false));
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_AMOUNT -> {
description.add(this.user.getTranslation(reference + "value", Constants.PARAMETER_NUMBER,
String.valueOf(req.amount())));
icon = new ItemStack(Material.CHEST);
clickHandler = (panel, user, clickType, i) -> {
Consumer<Number> numberConsumer = number -> {
if (number != null) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(req.statistic(), req.entity(), req.material(),
number.intValue(), req.reduceStatistic()));
}
// reopen panel
this.build();
};
ConversationUtils.createNumericInput(numberConsumer, this.user,
this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), 0, Integer.MAX_VALUE);
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case REMOVE_STATISTIC -> {
description.add(this.user.getTranslation(reference + (req.reduceStatistic() ? "enabled" : "disabled")));
icon = new ItemStack(Material.LEVER);
clickHandler = (panel, user, clickType, slot) -> {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(req.statistic(), req.entity(), req.material(), req.amount(),
!req.reduceStatistic()));
this.build();
return true;
};
glow = req.reduceStatistic();
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle"));
}
case STATISTIC_BLOCKS -> {
description.add(this.user.getTranslation(reference + "value", "[block]",
Utils.prettifyObject(req.material(), this.user)));
icon = req.material() == null ? new ItemStack(Material.BARRIER) : new ItemStack(req.material());
clickHandler = (panel, user, clickType, slot) -> {
SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.BLOCKS, (status, block) -> {
if (status) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(req.statistic(), req.entity(), block, req.amount(),
req.reduceStatistic()));
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_ITEMS -> {
description.add(this.user.getTranslation(reference + "value", "[item]",
Utils.prettifyObject(req.material(), this.user)));
icon = req.material() == null ? new ItemStack(Material.BARRIER) : new ItemStack(req.material());
clickHandler = (panel, user, clickType, slot) -> {
SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.ITEMS, (status, block) -> {
if (status) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(req.statistic(), req.entity(), block, req.amount(),
req.reduceStatistic()));
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
case STATISTIC_ENTITIES -> {
description.add(this.user.getTranslation(reference + "value", "[entity]",
Utils.prettifyObject(req.entity(), this.user)));
icon = req.entity() == null ? new ItemStack(Material.BARRIER)
: new ItemStack(PanelUtils.getEntityEgg(req.entity()));
clickHandler = (panel, user, clickType, slot) -> {
SingleEntitySelector.open(this.user, true, (status, entity) -> {
if (status) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));
statisticsList.add(new StatisticRec(req.statistic(), entity, req.material(), req.amount(),
req.reduceStatistic()));
}
this.build();
});
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-change"));
}
default -> {
icon = new ItemStack(Material.PAPER);
clickHandler = null;
glow = false;
}
}
return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow)
.clickHandler(clickHandler).build();
}
}

View File

@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.challenges.panel.admin.ManageStatisticsPanel;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -132,13 +133,11 @@ public class StatisticSelector extends PagedSelector<Statistic>
return new PanelItemBuilder().
name(this.user.getTranslation(reference + "name", "[statistic]",
Utils.prettifyObject(statistic, this.user))).
icon(Material.PAPER).
description(description).
clickHandler((panel, user1, clickType, slot) -> {
this.consumer.accept(true, statistic);
return true;
}).
build();
icon(ManageStatisticsPanel.getStatisticIcon(statistic)).description(description)
.clickHandler((panel, user1, clickType, slot) -> {
this.consumer.accept(true, statistic);
return true;
}).build();
}

View File

@ -57,6 +57,20 @@ public abstract class UnifiedMultiSelector<T> extends PagedSelector<T> {
this.filterElements = this.elements;
}
protected UnifiedMultiSelector(User user, Mode mode, List<T> elements,
BiConsumer<Boolean, Collection<T>> consumer) {
super(user);
this.mode = mode;
this.consumer = consumer;
this.selectedElements = new HashSet<>();
//If the elements are passed to the subclass in the constructor
this.elements = elements;
// Sort elements using the provided string representation.
this.elements.sort(Comparator.comparing(this::elementToString));
// Start with the full list as the filtered list.
this.filterElements = this.elements;
}
/**
* Subclasses must return the complete list of available elements.
*/

View File

@ -3,6 +3,7 @@ package world.bentobox.challenges.tasks;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -48,6 +49,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme
import world.bentobox.challenges.database.object.requirements.IslandRequirements;
import world.bentobox.challenges.database.object.requirements.OtherRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements;
import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec;
import world.bentobox.challenges.managers.ChallengesManager;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -475,63 +477,58 @@ public class TryToComplete
}
case STATISTIC_TYPE -> {
StatisticRequirements requirements = this.challenge.getRequirements();
for (StatisticRec s : requirements.getRequiredStatistics()) {
if (s.reduceStatistic() && s.statistic() != null) {
int removeAmount = result.getFactor() * s.amount();
if (requirements.isReduceStatistic() && requirements.getStatistic() != null) {
int removeAmount = result.getFactor() * requirements.getAmount();
// Start to remove from player who called the completion.
switch (requirements.getStatistic().getType())
{
case UNTYPED -> {
int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic());
if (removeAmount >= statistic) {
this.user.getPlayer().setStatistic(requirements.getStatistic(), 0);
removeAmount -= statistic;
} else {
this.user.getPlayer().setStatistic(requirements.getStatistic(), statistic - removeAmount);
removeAmount = 0;
}
}
case ITEM, BLOCK -> {
if (requirements.getMaterial() == null) {
// Just a sanity check. Material cannot be null at this point of code.
removeAmount = 0;
} else {
int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(),
requirements.getMaterial());
// Start to remove from player who called the completion.
switch (s.statistic().getType())
{
case UNTYPED -> {
int statistic = this.user.getPlayer().getStatistic(s.statistic());
if (removeAmount >= statistic) {
this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(),
0);
this.user.getPlayer().setStatistic(s.statistic(), 0);
removeAmount -= statistic;
} else {
this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(),
statistic - removeAmount);
this.user.getPlayer().setStatistic(s.statistic(), statistic - removeAmount);
removeAmount = 0;
}
}
}
case ENTITY -> {
if (requirements.getEntity() == null) {
// Just a sanity check. Entity cannot be null at this point of code.
removeAmount = 0;
} else {
int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(),
requirements.getEntity());
if (removeAmount >= statistic) {
this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(),
0);
removeAmount -= statistic;
} else {
this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(),
statistic - removeAmount);
case ITEM, BLOCK -> {
if (s.material() == null) {
// Just a sanity check. Material cannot be null at this point of code.
removeAmount = 0;
} else {
int statistic = this.user.getPlayer().getStatistic(s.statistic(), s.material());
if (removeAmount >= statistic) {
this.user.getPlayer().setStatistic(s.statistic(), s.material(), 0);
removeAmount -= statistic;
} else {
this.user.getPlayer().setStatistic(s.statistic(), s.material(),
statistic - removeAmount);
removeAmount = 0;
}
}
}
}
}
case ENTITY -> {
if (s.entity() == null) {
// Just a sanity check. Entity cannot be null at this point of code.
removeAmount = 0;
} else {
int statistic = this.user.getPlayer().getStatistic(s.statistic(), s.entity());
if (removeAmount >= statistic) {
this.user.getPlayer().setStatistic(s.statistic(), s.entity(), 0);
removeAmount -= statistic;
} else {
this.user.getPlayer().setStatistic(s.statistic(), s.entity(), statistic - removeAmount);
removeAmount = 0;
}
}
}
}
// If challenges are in sync with all island members, then punish others too.
if (this.addon.getChallengesSettings().isStoreAsIslandData())
@ -553,64 +550,62 @@ public class TryToComplete
continue;
}
switch (Objects.requireNonNull(requirements.getStatistic()).getType()) {
switch (Objects.requireNonNull(s.statistic()).getType()) {
case UNTYPED -> {
int statistic = player.getStatistic(requirements.getStatistic());
int statistic = player.getStatistic(s.statistic());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), 0);
player.setStatistic(s.statistic(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(), statistic - removeAmount);
player.setStatistic(s.statistic(), statistic - removeAmount);
removeAmount = 0;
}
}
case ITEM, BLOCK -> {
if (requirements.getMaterial() == null)
if (s.material() == null)
{
// Just a sanity check. Entity cannot be null at this point of code.
removeAmount = 0;
}
else
{
int statistic = player.getStatistic(requirements.getStatistic(),
requirements.getMaterial());
int statistic = player.getStatistic(s.statistic(), s.material());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0);
player.setStatistic(s.statistic(), s.material(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(), requirements.getMaterial(),
player.setStatistic(s.statistic(), s.material(),
statistic - removeAmount);
removeAmount = 0;
}
}
}
case ENTITY -> {
if (requirements.getEntity() == null)
if (s.entity() == null)
{
// Just a sanity check. Entity cannot be null at this point of code.
removeAmount = 0;
}
else
{
int statistic = player.getStatistic(requirements.getStatistic(),
requirements.getEntity());
int statistic = player.getStatistic(s.statistic(), s.entity());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0);
player.setStatistic(s.statistic(), s.entity(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(), requirements.getEntity(),
player.setStatistic(s.statistic(), s.entity(),
statistic - removeAmount);
removeAmount = 0;
}
@ -619,6 +614,7 @@ public class TryToComplete
}
}
}
}
}
}
}
@ -1470,54 +1466,58 @@ public class TryToComplete
int currentValue;
if (requirements.getStatistic() == null) {
if (requirements.getRequiredStatistics().isEmpty()) {
// Sanity check.
return EMPTY_RESULT;
}
List<ChallengeResult> cr = new ArrayList<>();
// Check all requirements
for (StatisticRec s : requirements.getRequiredStatistics()) {
switch (Objects.requireNonNull(requirements.getStatistic()).getType())
{
case UNTYPED ->
currentValue = this.manager.getStatisticData(this.user, this.world, requirements.getStatistic());
case ITEM, BLOCK -> currentValue = this.manager.getStatisticData(this.user, this.world,
requirements.getStatistic(), requirements.getMaterial());
case ENTITY -> currentValue = this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(),
requirements.getEntity());
default -> currentValue = 0;
}
if (currentValue < requirements.getAmount()) {
switch (Objects.requireNonNull(requirements.getStatistic()).getType())
switch (Objects.requireNonNull(s.statistic()).getType())
{
case ITEM, BLOCK -> {
Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-material",
TextVariables.NUMBER, String.valueOf(requirements.getAmount()), "[statistic]",
Utils.prettifyObject(requirements.getStatistic(), this.user), "[material]",
Utils.prettifyObject(requirements.getMaterial(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
case UNTYPED -> currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic());
case ITEM, BLOCK ->
currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic(), s.material());
case ENTITY ->
currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic(), s.entity());
default -> currentValue = 0;
}
case ENTITY -> {
Utils.sendMessage(this.user,
this.world, Constants.ERRORS + "requirement-not-met-entity", TextVariables.NUMBER,
String.valueOf(requirements.getAmount()), "[statistic]",
Utils.prettifyObject(requirements.getStatistic(), this.user), "[entity]",
Utils.prettifyObject(requirements.getEntity(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
}
default -> {
Utils.sendMessage(this.user,
this.world, Constants.ERRORS + "requirement-not-met", TextVariables.NUMBER,
String.valueOf(requirements.getAmount()), "[statistic]",
Utils.prettifyObject(requirements.getStatistic(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
}
}
} else {
factor = requirements.getAmount() == 0 ? factor : Math.min(factor, currentValue / requirements.getAmount());
return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor);
if (currentValue < s.amount()) {
switch (Objects.requireNonNull(s.statistic()).getType()) {
case ITEM, BLOCK -> {
Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-material",
TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[material]",
Utils.prettifyObject(s.material(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
}
case ENTITY -> {
Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-entity",
TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), "[entity]",
Utils.prettifyObject(s.entity(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
}
default -> {
Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met",
TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]",
Utils.prettifyObject(s.statistic(), this.user), Constants.PARAMETER_VALUE,
String.valueOf(currentValue));
}
}
} else {
factor = s.amount() == 0 ? factor : Math.min(factor, currentValue / s.amount());
// Store result
cr.add(new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor));
}
}
// Check results -- all must pass
if (cr.stream().allMatch(result -> result.meetsRequirements)) {
// Return any of them, because they pass
return cr.getFirst();
}
return EMPTY_RESULT;
}

View File

@ -63,6 +63,7 @@ challenges:
manage-block-groups: "&0&l Manage Block Groups"
manage-entity-groups: "&0&l Manage Entity Groups"
manage-entities: "&0&l Manage Entities"
manage-statistics: "&0&l Manage Statistics"
type-selector: "&0&l Challenge Type Selector"
item-selector: "&0&l Item Selector"
block-selector: "&0&l Block Selector"
@ -212,7 +213,8 @@ challenges:
name: "&f&l Deployment"
description: |-
&7 Toggle whether the challenge is
&7 deployed and can be completed by users.
&7 deployed and can be completed
&7 by users.
enabled: "&2 Enabled"
disabled: "&c Disabled"
name:
@ -265,7 +267,8 @@ challenges:
name: "&f&l Required Permissions"
description: |-
&7 Allows you to change the required
&7 permissions for this challenge to be completed.
&7 permissions for this challenge to
&7 be completed.
title: "&7 Permissions: "
permission: " &8 - [permission]"
none: "&7 No permissions are set."
@ -273,15 +276,16 @@ challenges:
name: "&f&l Remove Entities"
description: |-
&7 Toggle whether required entities will
&7 be removed from the world after completing
&7 the challenge.
&7 be removed from the world after
&7 completing the challenge.
enabled: "&2 Enabled"
disabled: "&c Disabled"
required_entities:
name: "&f&l Required Entities"
description: |-
&7 Allows you to change the required
&7 entities for this challenge to be completed.
&7 entities for this challenge to be
&7 completed.
title: "&7 Entities: "
list: " &8 - [number] x [entity]"
none: "&7 No entities have been added."
@ -289,18 +293,28 @@ challenges:
name: "&f&l Remove Blocks"
description: |-
&7 Toggle whether required blocks will
&7 be removed from the world after completing
&7 the challenge.
&7 be removed from the world after
&7 completing the challenge.
enabled: "&2 Enabled"
disabled: "&c Disabled"
required_blocks:
name: "&f&l Required Blocks"
description: |-
&7 Allows you to change the required
&7 blocks for this challenge to be completed.
&7 blocks for this challenge to be
&7 completed.
title: "&7 Blocks: "
list: " &8 - [number] x [block]"
none: "&7 No blocks have been added."
required_statistics:
name: "&f&l Required Statistics"
description: |-
&7 Allows you to change the required
&7 statistics for this challenge to be
&7 completed.
title: "&7 Statistics: "
list: " &8 - [name]"
none: "&7 No statistics have been added."
search_radius:
name: "&f&l Search Radius"
description: |-
@ -312,8 +326,8 @@ challenges:
name: "&f&l Remove Items"
description: |-
&7 Toggle whether required items will
&7 be removed from the inventory after completing
&7 the challenge.
&7 be removed from the inventory after
&7 completing the challenge.
enabled: "&2 Enabled"
disabled: "&c Disabled"
required_items:
@ -327,8 +341,9 @@ challenges:
add_ignored_meta:
name: "&f&l Add Ignore Metadata"
description: |-
&7 Allows you to specify which items should ignore
&7 any metadata assigned to them.
&7 Allows you to specify which items
&7 should ignore any metadata
&7 assigned to them.
title: "&7 Items: "
list: " &8 - [number] x [item]"
none: "&7 No items have been added."
@ -340,28 +355,31 @@ challenges:
remove_experience:
name: "&f&l Remove Experience"
description: |-
&7 Toggle whether the required experience will
&7 be removed from the player after completing
&7 Toggle whether the required
&7 experience will be removed
&7 from the player after completing
&7 the challenge.
enabled: "&2 Enabled"
disabled: "&c Disabled"
required_experience:
name: "&f&l Required Experience"
description: |-
&7 Allows you to change the required experience for
&7 the player.
&7 Allows you to change the required
&7 experience for the player.
value: "&7 Current experience: &e [number]"
required_level:
name: "&f&l Required Island Level"
description: |-
&7 Allows you to change the required island level
&7 Allows you to change the
&7 required island level
&7 for the challenge.
value: "&7 Current level: &e [number]"
required_materialtags:
name: "&f&l Required Block Groups"
description: |-
&7 Allows you to change the required
&7 block groups for this challenge to be completed.
&7 block groups for this challenge to
&7 be completed.
title: "&7 Block group: "
list: " &8 - [number] x [tag]"
none: "&7 No block groups have been added."
@ -369,57 +387,71 @@ challenges:
name: "&f&l Required Entity Groups"
description: |-
&7 Allows you to change the required
&7 entity groups for this challenge to be completed.
&7 entity groups for this challenge to
&7 be completed.
title: "&7 Entity group: "
list: " &8 - [number] x [tag]"
none: "&7 No entity groups have been added."
remove_money:
name: "&f&l Remove Money"
description: |-
&7 Toggle whether the required money will
&7 be removed from the player's account after completing
&7 the challenge.
&7 Toggle whether the required
&7 money will be removed from
&7 the player's account after
&7 completing the challenge.
enabled: "&2 Enabled"
disabled: "&c Disabled"
required_money:
name: "&f&l Required Money"
description: |-
&7 Allows you to change the required money on the player's
&7 Allows you to change the required
&7 money on the player's
&7 account for the challenge.
value: "&7 Current value: &e [number]"
statistic:
name: "&f&l Statistic"
description: |-
&7 Allows you to change the statistic type that is
&7 checked for this challenge.
&7 Allows you to change the statistic
&7 type that is checked for
&7 this challenge.
value: "&7 Current value: &e [statistic]"
statistic_amount:
name: "&f&l Target Value"
description: |-
&7 Allows you to change the target statistic value
&7 Allows you to change the target
&7 statistic value
&7 that must be met.
value: "&7 Current value: &e [number]"
remove_statistic:
name: "&f&l Reduce Statistic"
add_statistic:
name: "&f&l Add Statistic"
description: |-
&7 Toggle whether the statistic value will
&7 be reduced after completing the challenge.
&7 Allows you to add a new
&7 statistic to the list.
remove_statistic:
name: "&f&l Remove Statistic"
description: |-
&7 Allows you to remove
&7 selected statistics
&7 from the list.
enabled: "&2 Enabled"
disabled: "&c Disabled"
statistic_blocks:
name: "&f&l Target Block"
description: |-
&7 Allows you to change the target block for the statistic.
&7 Allows you to change the target
&7 block for the statistic.
value: "&7 Current block: &e [block]"
statistic_items:
name: "&f&l Target Item"
description: |-
&7 Allows you to change the target item for the statistic.
&7 Allows you to change the target
&7 item for the statistic.
value: "&7 Current item: &e [item]"
statistic_entities:
name: "&f&l Target Entity"
description: |-
&7 Allows you to change the target entity for the statistic.
&7 Allows you to change the target
&7 entity for the statistic.
value: "&7 Current entity: &e [entity]"
reward_text:
name: "&f&l Reward Text"
@ -430,7 +462,8 @@ challenges:
repeat_reward_text:
name: "&f&l Repeat Reward Text"
description: |-
&7 Specifies the repeat reward text for the challenge.
&7 Specifies the repeat reward
&7 text for the challenge.
&7 Color codes must be applied.
value: "&7 Current text:"
reward_items:
@ -443,21 +476,22 @@ challenges:
repeat_reward_items:
name: "&f&l Repeat Reward Items"
description: |-
&7 Allows you to change the repeat reward items for this challenge.
&7 Allows you to change the repeat
&7 reward items for this challenge.
title: "&7 Items: "
list: " &8 - [number] x [item]"
none: "&7 No items have been added."
reward_experience:
name: "&f&l Reward Experience"
description: |-
&7 Allows you to change the reward experience for
&7 the player.
&7 Allows you to change the reward
& experience for the player.
value: "&7 Reward experience: &e [number]"
repeat_reward_experience:
name: "&f&l Repeat Reward Experience"
description: |-
&7 Allows you to change the repeat reward experience
&7 for the player.
&7 Allows you to change the repeat
&7 reward experience for the player.
value: "&7 Reward experience: &e [number]"
reward_money:
name: "&f&l Reward Money"
@ -467,8 +501,8 @@ challenges:
repeat_reward_money:
name: "&f&l Repeat Reward Money"
description: |-
&7 Allows you to change the repeat reward money
&7 for the challenge.
&7 Allows you to change the repeat
&7 reward money for the challenge.
value: "&7 Current value: &e [number]"
reward_commands:
name: "&f&l Reward Commands"
@ -511,8 +545,9 @@ challenges:
cool_down:
name: "&f&l Cool Down"
description: |-
&7 Allows you to change the cooldown period (in seconds)
&7 that must elapse between repeatable challenge completions.
&7 Allows you to change the cooldown
&7 period (in seconds) that must elapse
&7 between repeatable challenge completions.
value: "&7 Current value: &e [time]"
challenges:
name: "&f&l Challenges"
@ -522,66 +557,79 @@ challenges:
waiver_amount:
name: "&f&l Waiver Amount"
description: |-
&7 Allows you to set the number of challenges that can
&7 be left uncompleted to unlock the next level.
&7 Allows you to set the number of challenges
&7 that can be left uncompleted to unlock
&7 the next level.
value: "&7 Current value: &e [number]"
add_challenges:
name: "&f&l Add Challenge(-s)"
description: |-
&7 Allows you to select and add challenges to the level.
&7 Allows you to select and add
&7 challenges to the level.
remove_challenges:
name: "&f&l Remove Challenge(-s)"
description: |-
&7 Allows you to select and remove challenges from the level.
&7 Allows you to select and remove
&7 challenges from the level.
reset_on_new:
name: "&f&l Reset On New"
description: |-
&7 Toggle whether challenges should be reset when a user leaves
&7 Toggle whether challenges should
& be reset when a user leaves
&7 their island or creates a new one.
enabled: "&2 Enabled"
disabled: "&c Disabled"
broadcast:
name: "&f&l Broadcast"
description: |-
&7 Broadcasts the first-time completion of a challenge or level to everyone.
&7 Broadcasts the first-time completion
&7 of a challenge or level to everyone.
enabled: "&2 Enabled"
disabled: "&c Disabled"
remove_completed:
name: "&f&l Hide Completed"
description: |-
&7 Hides completed challenges from the menu.
&7 Hides completed challenges
&7 from the menu.
enabled: "&2 Enabled"
disabled: "&c Disabled"
glow_completed:
name: "&f&l Glow Completed"
description: |-
&7 Applies an enchantment glow to completed challenges.
&7 Applies an enchantment glow
&7 to completed challenges.
enabled: "&2 Enabled"
disabled: "&c Disabled"
store_history:
name: "&f&l Store History"
description: |-
&7 Stores internal history each time a challenge is completed.
&7 Currently viewable only in the database.
&7 Stores internal history each time
&7 a challenge is completed.
&7 Currently viewable only in the
&7 database.
enabled: "&2 Enabled"
disabled: "&c Disabled"
data_per_island:
name: "&f&l Store Per Island"
description: |-
&7 Stores completed challenges on a per-island basis.
&7 Progress will be shared with all team members.
&7 Stores completed challenges
&7 on a per-island basis.
&7 Progress will be shared
&7 with all team members.
enabled: "&2 Enabled"
disabled: "&c Disabled"
show_title:
name: "&f&l Show Title"
description: |-
&7 Displays a title when a challenge or level is completed.
&7 Displays a title when a challenge
&7 or level is completed.
enabled: "&2 Enabled"
disabled: "&c Disabled"
gamemode_gui:
name: "&f&l GameMode Selection GUI"
description: |-
&7 Enables a single GUI accessible via the /challenges command.
&7 Enables a single GUI accessible
&7 via the /challenges command.
&7 (Requires server restart.)
enabled: "&2 Enabled"
disabled: "&c Disabled"
@ -593,20 +641,26 @@ challenges:
purge_history:
name: "&f&l History Lifetime"
description: |-
&7 Specifies the number of days that history data is stored
&7 Specifies the number of days
&7 that history data is stored
&7 in user data.
&7 A value of 0 means the data will not be removed.
&7 A value of 0 means the data will
&7 not be removed.
value: "&7 Current value: &e [number]"
title_showtime:
name: "&f&l Title Showtime"
description: |-
&7 Number of ticks the title will be displayed to the player.
&7 Number of ticks the title will
& be displayed to the player.
value: "&7 Current value: &e [number]"
active_world_list:
name: "&f&l Show Only Active World"
description: |-
&7 If the GameMode Selection GUI is enabled, this setting determines
&7 whether the GUI displays the GameMode selection or challenges for the current world.
&7 If the GameMode Selection GUI is
&7 enabled, this setting determines
&7 whether the GUI displays the
&7 GameMode selection or challenges
&7 for the current world.
&7 (Requires server restart.)
enabled: "&2 Enabled"
disabled: "&c Disabled"
@ -622,13 +676,15 @@ challenges:
include_undeployed:
name: "&f&l Include Undeployed Challenges"
description: |-
&7 Indicates whether undeployed challenges should be counted towards level completion.
&7 Indicates whether undeployed challenges
&7 should be counted towards level completion.
enabled: "&2 Enabled"
disabled: "&c Disabled"
download:
name: "&f&l Download Libraries"
description: |-
&7 Manually updates the available challenges libraries.
&7 Manually updates the available
&7 challenges libraries.
enabled: "&2 With cache clear"
disabled: "&c Without cache clear"
player:
@ -728,20 +784,24 @@ challenges:
inventory_type:
name: "&f&l Inventory Type"
description: |-
&7 Challenge that checks items in the player's inventory.
&7 Challenge that checks items
&7 in the player's inventory.
island_type:
name: "&f&l Island Type"
description: |-
&7 Challenge that checks blocks or entities around the player.
&7 Challenge that checks blocks
&7 or entities around the player.
other_type:
name: "&f&l Other Type"
description: |-
&7 Challenge that utilizes plugins or add-on features,
&7 Challenge that utilizes plugins
&7 or add-on features,
&7 such as level and money.
statistic_type:
name: "&f&l Statistic Type"
description: |-
&7 Challenge that checks the player's statistic data.
&7 Challenge that checks the
&7 player's statistic data.
save:
name: "&f&l Save"
description: |-
@ -761,7 +821,17 @@ challenges:
element: "&8 - [element]"
statistic_element:
name: "&f&l [statistic]"
amount: "&7 Target Value: &e [number]"
remove:
name: "&7 Reduce Statistic: [value]"
value:
enabled: "&c Enabled"
disabled: "&2 Disabled"
block: "&7 Target Block: &e [block]"
item: "&7 Target Item: &e [item]"
entity: "&7 Target Entity: &e [entity]"
description: "[description]"
selected: "&2 Selected"
environment_element:
name: "&f&l [environment]"
description: "[description]"