Create Statistic Requirement for Challenges addon.

Statistic requirement is a new type of challenge that is based on Statistic page for clients.
This commit is contained in:
BONNe 2021-08-14 19:25:04 +03:00
parent f75cfa9e7a
commit c63087c5af
10 changed files with 1090 additions and 116 deletions

View File

@ -17,7 +17,10 @@ import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -1287,6 +1290,101 @@ public class ChallengesManager
// ---------------------------------------------------------------------
/**
* Gets statistic data.
*
* @param user the user
* @param world the world
* @param statistic the statistic
* @return the statistic data
*/
public int getStatisticData(User user, World world, Statistic statistic)
{
if (this.settings.isStoreAsIslandData())
{
Island island = this.addon.getIslands().getIsland(world, user);
if (island == null)
{
return 0;
}
return island.getMemberSet().stream().map(Bukkit::getPlayer).
filter(Objects::nonNull).
mapToInt(player -> player.getStatistic(statistic)).
sum();
}
else
{
return user.getPlayer().getStatistic(statistic);
}
}
/**
* Gets statistic data.
*
* @param user the user
* @param world the world
* @param statistic the statistic
* @param material the material
* @return the statistic data
*/
public int getStatisticData(User user, World world, Statistic statistic, Material material)
{
if (this.settings.isStoreAsIslandData())
{
Island island = this.addon.getIslands().getIsland(world, user);
if (island == null)
{
return 0;
}
return island.getMemberSet().stream().map(Bukkit::getPlayer).
filter(Objects::nonNull).
mapToInt(player -> player.getStatistic(statistic, material)).
sum();
}
else
{
return user.getPlayer().getStatistic(statistic, material);
}
}
/**
* Gets statistic data.
*
* @param user the user
* @param world the world
* @param statistic the statistic
* @param entity the entity
* @return the statistic data
*/
public int getStatisticData(User user, World world, Statistic statistic, EntityType entity)
{
if (this.settings.isStoreAsIslandData())
{
Island island = this.addon.getIslands().getIsland(world, user);
if (island == null)
{
return 0;
}
return island.getMemberSet().stream().map(Bukkit::getPlayer).
filter(Objects::nonNull).
mapToInt(player -> player.getStatistic(statistic, entity)).
sum();
}
else
{
return user.getPlayer().getStatistic(statistic, entity);
}
}
/**
* This method returns if given user has completed given challenge in world.
* @param user - User that must be checked.

View File

@ -57,6 +57,11 @@ public class Challenge implements DataObject
* other plugins to be setup before it could work.
*/
OTHER,
/**
* Challenge based on player statistic data.
*/
STATISTIC
}

View File

@ -0,0 +1,223 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.challenges.database.object.requirements;
import com.google.gson.annotations.Expose;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.EntityType;
public class StatisticRequirements extends Requirements
{
/**
* Constructor Requirements creates a new Requirements instance.
*/
public StatisticRequirements()
{
// Empty constructor
}
/**
* This method clones given statistic object.
* @return Clone of this object.
*/
@Override
public Requirements clone()
{
StatisticRequirements requirements = new StatisticRequirements();
requirements.setStatistic(this.statistic);
requirements.setEntity(this.entity);
requirements.setMaterial(this.material);
requirements.setAmount(this.amount);
requirements.setReduceStatistic(this.reduceStatistic);
return requirements;
}
@Override
public boolean isValid()
{
if (!super.isValid())
{
return false;
}
if (this.statistic == null)
{
return false;
}
switch (this.statistic.getType())
{
case ITEM -> {
return this.material != null && this.material.isItem();
}
case BLOCK -> {
return this.material != null && this.material.isBlock();
}
case ENTITY -> {
return this.entity != null;
}
}
return true;
}
// ---------------------------------------------------------------------
// Section: Getters and setters
// ---------------------------------------------------------------------
/**
* Gets statistic.
*
* @return the statistic
*/
public Statistic getStatistic()
{
return statistic;
}
/**
* Sets statistic.
*
* @param statistic the statistic
*/
public void setStatistic(Statistic statistic)
{
this.statistic = statistic;
}
/**
* Gets entity.
*
* @return the entity
*/
public EntityType getEntity()
{
return entity;
}
/**
* Sets entity.
*
* @param entity the entity
*/
public void setEntity(EntityType entity)
{
this.entity = entity;
}
/**
* Gets material.
*
* @return the material
*/
public Material getMaterial()
{
return material;
}
/**
* Sets material.
*
* @param material the material
*/
public void setMaterial(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
private Statistic statistic;
/**
* Type of entity for entity related statistics.
*/
@Expose
private EntityType entity;
/**
* Type of material for block and item related statistics.
*/
@Expose
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

@ -44,6 +44,8 @@ import world.bentobox.challenges.database.object.ChallengeLevel;
import world.bentobox.challenges.database.object.requirements.InventoryRequirements;
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.utils.GuiUtils;
import world.bentobox.challenges.utils.LevelStatus;
import world.bentobox.challenges.utils.Utils;
@ -488,15 +490,18 @@ public abstract class CommonGUI
{
switch (challenge.getChallengeType())
{
case INVENTORY:
result.addAll(this.getInventoryRequirements(challenge.getRequirements()));
break;
case ISLAND:
result.addAll(this.getIslandRequirements(challenge.getRequirements()));
break;
case OTHER:
result.addAll(this.getOtherRequirements(challenge.getRequirements()));
break;
case INVENTORY:
result.addAll(this.getInventoryRequirements(challenge.getRequirements()));
break;
case ISLAND:
result.addAll(this.getIslandRequirements(challenge.getRequirements()));
break;
case OTHER:
result.addAll(this.getOtherRequirements(challenge.getRequirements()));
break;
case STATISTIC:
result.addAll(this.getStatisticRequirements(challenge.getRequirements()));
break;
}
}
@ -727,6 +732,45 @@ public abstract class CommonGUI
}
/**
* This method returns list of strings that contains basic information about requirements.
* @param requirements which requirements message must be created.
* @return list of strings that contains requirements message.
*/
private List<String> getStatisticRequirements(StatisticRequirements requirements)
{
List<String> result = new ArrayList<>();
result.add(this.user.getTranslation("challenges.gui.challenge-description.required-stats",
"[stat]", GuiUtils.sanitizeInput(requirements.getStatistic().name())));
switch (requirements.getStatistic().getType())
{
case ITEM -> {
result.add(this.user.getTranslation("challenges.gui.challenge-description.stat-item",
"[material]", GuiUtils.sanitizeInput(requirements.getMaterial().name()),
"[amount]", String.valueOf(requirements.getAmount())));
}
case BLOCK -> {
result.add(this.user.getTranslation("challenges.gui.challenge-description.stat-block",
"[material]", GuiUtils.sanitizeInput(requirements.getMaterial().name()),
"[amount]", String.valueOf(requirements.getAmount())));
}
case ENTITY -> {
result.add(this.user.getTranslation("challenges.gui.challenge-description.stat-entity",
"[entity]", GuiUtils.sanitizeInput(requirements.getEntity().name()),
"[amount]", String.valueOf(requirements.getAmount())));
}
case UNTYPED -> {
result.add(this.user.getTranslation("challenges.gui.challenge-description.stat-amount",
"[amount]", String.valueOf(requirements.getAmount())));
}
}
return result;
}
/**
* This method returns list of strings that contains required items, entities and blocks from given challenge.
* @param challenge Challenge which requirement items, entities and blocks must be returned.

View File

@ -23,12 +23,9 @@ import world.bentobox.challenges.database.object.Challenge;
import world.bentobox.challenges.database.object.requirements.InventoryRequirements;
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.panel.CommonGUI;
import world.bentobox.challenges.panel.util.ItemSwitchGUI;
import world.bentobox.challenges.panel.util.NumberGUI;
import world.bentobox.challenges.panel.util.SelectBlocksGUI;
import world.bentobox.challenges.panel.util.SelectEnvironmentGUI;
import world.bentobox.challenges.panel.util.StringListGUI;
import world.bentobox.challenges.panel.util.*;
import world.bentobox.challenges.utils.GuiUtils;
import world.bentobox.challenges.utils.Utils;
@ -118,15 +115,10 @@ public class EditChallengeGUI extends CommonGUI
{
switch (this.challenge.getChallengeType())
{
case INVENTORY:
this.buildInventoryRequirementsPanel(panelBuilder);
break;
case ISLAND:
this.buildIslandRequirementsPanel(panelBuilder);
break;
case OTHER:
this.buildOtherRequirementsPanel(panelBuilder);
break;
case INVENTORY -> this.buildInventoryRequirementsPanel(panelBuilder);
case ISLAND -> this.buildIslandRequirementsPanel(panelBuilder);
case OTHER -> this.buildOtherRequirementsPanel(panelBuilder);
case STATISTIC -> this.buildStatisticRequirementsPanel(panelBuilder);
}
}
else if (this.currentMenuType.equals(MenuType.REWARDS))
@ -139,6 +131,8 @@ public class EditChallengeGUI extends CommonGUI
// Every time when this GUI is build, save challenge
// This will ensure that all main things will be always stored
this.addon.getChallengesManager().saveChallenge(this.challenge);
// If for some reason challenge is not loaded, do it.
this.addon.getChallengesManager().loadChallenge(this.challenge, false, null, true);
panelBuilder.build();
}
@ -210,6 +204,33 @@ public class EditChallengeGUI extends CommonGUI
}
/**
* This class populates ChallengesEditGUI with other challenges requirement elements.
* @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));
}
}
panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS));
}
/**
* This class populates ChallengesEditGUI with challenges reward elements.
* @param panelBuilder PanelBuilder where icons must be added.
@ -344,7 +365,16 @@ public class EditChallengeGUI extends CommonGUI
icon = new ItemStack(Material.LEVER);
clickHandler = (panel, user, clickType, slot) -> {
this.challenge.setDeployed(!this.challenge.isDeployed());
if (this.challenge.isValid())
{
this.challenge.setDeployed(!this.challenge.isDeployed());
}
else
{
this.user.sendMessage("challenges.errors.invalid-challenge",
"[challenge]", this.challenge.getFriendlyName());
this.challenge.setDeployed(false);
}
this.build();
return true;
@ -547,24 +577,25 @@ public class EditChallengeGUI extends CommonGUI
switch (button)
{
case REQUIRED_PERMISSIONS:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.required-permissions");
description = new ArrayList<>(this.challenge.getRequirements().getRequiredPermissions().size() + 1);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-permissions"));
case REQUIRED_PERMISSIONS -> {
name = this.user.getTranslation("challenges.gui.buttons.admin.required-permissions");
description = new ArrayList<>(this.challenge.getRequirements().getRequiredPermissions().size() + 1);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-permissions"));
for (String permission : this.challenge.getRequirements().getRequiredPermissions())
{
description.add(this.user.getTranslation("challenges.gui.descriptions.permission",
for (String permission : this.challenge.getRequirements().getRequiredPermissions())
{
description.add(this.user.getTranslation("challenges.gui.descriptions.permission",
"[permission]", permission));
}
}
icon = new ItemStack(Material.REDSTONE_LAMP);
clickHandler = (panel, user, clickType, slot) -> {
new StringListGUI(this.user,
icon = new ItemStack(Material.REDSTONE_LAMP);
clickHandler = (panel, user, clickType, slot) ->
{
new StringListGUI(this.user,
this.challenge.getRequirements().getRequiredPermissions(),
lineLength,
(status, value) -> {
(status, value) ->
{
if (status)
{
this.challenge.getRequirements().setRequiredPermissions(new HashSet<>(value));
@ -573,47 +604,39 @@ public class EditChallengeGUI extends CommonGUI
this.build();
});
return true;
};
glow = false;
break;
}
case REQUIRED_ENTITIES:
case REMOVE_ENTITIES:
case REQUIRED_BLOCKS:
case REMOVE_BLOCKS:
case SEARCH_RADIUS:
{
return this.createIslandRequirementButton(button);
}
case REQUIRED_ITEMS:
case REMOVE_ITEMS:
{
return this.createInventoryRequirementButton(button);
}
case REQUIRED_EXPERIENCE:
case REMOVE_EXPERIENCE:
case REQUIRED_LEVEL:
case REQUIRED_MONEY:
case REMOVE_MONEY:
{
return this.createOtherRequirementButton(button);
}
default:
return null;
return true;
};
glow = false;
}
// Buttons for Island Requirements
case REQUIRED_ENTITIES, REMOVE_ENTITIES, REQUIRED_BLOCKS, REMOVE_BLOCKS, SEARCH_RADIUS -> {
return this.createIslandRequirementButton(button);
}
// Buttons for Inventory Requirements
case REQUIRED_ITEMS, REMOVE_ITEMS -> {
return this.createInventoryRequirementButton(button);
}
// Buttons for Other Requirements
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);
}
// Default behaviour.
default -> {
return null;
}
}
return new PanelItemBuilder().
icon(icon).
name(name).
description(GuiUtils.stringSplit(description, this.lineLength)).
glow(glow).
clickHandler(clickHandler).
build();
icon(icon).
name(name).
description(GuiUtils.stringSplit(description, this.lineLength)).
glow(glow).
clickHandler(clickHandler).
build();
}
@ -1023,6 +1046,198 @@ public class EditChallengeGUI extends CommonGUI
}
/**
* Creates a button for statistic requirements.
* @param button Button that must be created.
* @return PanelItem button.
*/
private PanelItem createStatisticRequirementButton(RequirementButton button)
{
ItemStack icon;
String name;
List<String> description;
boolean glow;
PanelItem.ClickHandler clickHandler;
final StatisticRequirements requirements = this.challenge.getRequirements();
switch (button)
{
case STATISTIC:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.required-statistic");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-statistic"));
description.add(this.user.getTranslation("challenges.gui.descriptions.current-value",
"[value]", String.valueOf(requirements.getStatistic())));
icon = new ItemStack(Material.PAPER);
clickHandler = (panel, user, clickType, slot) -> {
new SelectStatisticGUI(this.user, (status, statistic) -> {
if (status)
{
requirements.setStatistic(statistic);
}
this.build();
});
return true;
};
glow = false;
break;
}
case STATISTIC_AMOUNT:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.required-amount");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.required-amount"));
description.add(this.user.getTranslation("challenges.gui.descriptions.current-value",
"[value]", Integer.toString(requirements.getAmount())));
icon = new ItemStack(Material.CHEST);
clickHandler = (panel, user, clickType, slot) -> {
new NumberGUI(this.user,
requirements.getAmount(),
0,
this.lineLength,
(status, value) -> {
if (status)
{
requirements.setAmount(value);
}
this.build();
});
return true;
};
glow = false;
break;
}
case REMOVE_STATISTIC:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.remove-statistic");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.remove-statistic"));
description.add(this.user.getTranslation("challenges.gui.descriptions.current-value",
"[value]",
requirements.isReduceStatistic() ?
this.user.getTranslation("challenges.gui.descriptions.enabled") :
this.user.getTranslation("challenges.gui.descriptions.disabled")));
icon = new ItemStack(Material.LEVER);
clickHandler = (panel, user, clickType, slot) -> {
requirements.setReduceStatistic(!requirements.isReduceStatistic());
this.build();
return true;
};
glow = requirements.isReduceStatistic();
break;
}
case STATISTIC_BLOCKS:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.statistic-block");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.statistic-block"));
description.add(this.user.getTranslation("challenges.gui.descriptions.statistic-block",
"[material]", String.valueOf(requirements.getMaterial())));
icon = requirements.getMaterial() == null ?
new ItemStack(Material.BARRIER) :
new ItemStack(requirements.getMaterial());
clickHandler = (panel, user, clickType, slot) -> {
new SelectBlocksGUI(this.user,
true,
Collections.emptySet(),
(status, block) -> {
if (status)
{
requirements.setMaterial(block.iterator().next());
}
this.build();
});
return true;
};
glow = false;
break;
}
case STATISTIC_ITEMS:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.statistic-item");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.statistic-item"));
description.add(this.user.getTranslation("challenges.gui.descriptions.statistic-item",
"[material]", String.valueOf(requirements.getMaterial())));
icon = requirements.getMaterial() == null ?
new ItemStack(Material.BARRIER) :
new ItemStack(requirements.getMaterial());
clickHandler = (panel, user, clickType, slot) -> {
new SelectBlocksGUI(this.user,
true,
(status, block) -> {
if (status)
{
requirements.setMaterial(block.iterator().next());
}
this.build();
});
return true;
};
glow = false;
break;
}
case STATISTIC_ENTITIES:
{
name = this.user.getTranslation("challenges.gui.buttons.admin.statistic-entity");
description = new ArrayList<>(2);
description.add(this.user.getTranslation("challenges.gui.descriptions.admin.statistic-entity"));
description.add(this.user.getTranslation("challenges.gui.descriptions.statistic-entity",
"[entity]", String.valueOf(requirements.getEntity())));
icon = requirements.getEntity() == null ?
new ItemStack(Material.BARRIER) :
new ItemStack(GuiUtils.getEntityEgg(requirements.getEntity()));
clickHandler = (panel, user, clickType, slot) -> {
new SelectEntityGUI(this.user, Collections.emptySet(), true, (status, entities) -> {
if (status)
{
requirements.setEntity(entities.iterator().next());
}
this.build();
});
return true;
};
glow = false;
break;
}
default:
return null;
}
return new PanelItemBuilder().
icon(icon).
name(name).
description(GuiUtils.stringSplit(description, this.lineLength)).
glow(glow).
clickHandler(clickHandler).
build();
}
/**
* This method creates buttons for rewards menu.
* @param button Button which panel item must be created.
@ -1506,6 +1721,12 @@ public class EditChallengeGUI extends CommonGUI
REQUIRED_LEVEL,
REQUIRED_MONEY,
REMOVE_MONEY,
STATISTIC,
STATISTIC_BLOCKS,
STATISTIC_ITEMS,
STATISTIC_ENTITIES,
STATISTIC_AMOUNT,
REMOVE_STATISTIC,
}

View File

@ -20,10 +20,7 @@ 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.database.object.Challenge;
import world.bentobox.challenges.database.object.requirements.InventoryRequirements;
import world.bentobox.challenges.database.object.requirements.IslandRequirements;
import world.bentobox.challenges.database.object.requirements.OtherRequirements;
import world.bentobox.challenges.database.object.requirements.Requirements;
import world.bentobox.challenges.database.object.requirements.*;
import world.bentobox.challenges.utils.GuiUtils;
@ -71,6 +68,7 @@ public class ChallengeTypeGUI
panelBuilder.item(0, this.getButton(Challenge.ChallengeType.INVENTORY));
panelBuilder.item(1, this.getButton(Challenge.ChallengeType.ISLAND));
panelBuilder.item(2, this.getButton(Challenge.ChallengeType.OTHER));
panelBuilder.item(3, this.getButton(Challenge.ChallengeType.STATISTIC));
panelBuilder.build();
}
@ -112,6 +110,13 @@ public class ChallengeTypeGUI
return true;
});
break;
case STATISTIC:
icon = new ItemStack(Material.BOOK);
clickHandler = ((panel, user1, clickType, slot) -> {
this.consumer.accept(type, new StatisticRequirements());
return true;
});
break;
default:
return null;
}

View File

@ -0,0 +1,168 @@
package world.bentobox.challenges.panel.util;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.function.BiConsumer;
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.utils.GuiUtils;
/**
* This class contains all necessary things that allows to select single statistic. Selected
* stats will be returned via BiConsumer.
*/
public class SelectStatisticGUI
{
public SelectStatisticGUI(User user, BiConsumer<Boolean, Statistic> consumer)
{
this.consumer = consumer;
this.user = user;
this.elements = new ArrayList<>();
this.elements.addAll(Arrays.asList(Statistic.values()));
this.build(0);
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* This method builds all necessary elements in GUI panel.
*/
public void build(int pageIndex)
{
PanelBuilder panelBuilder = new PanelBuilder().user(this.user).
name(this.user.getTranslation("challenges.gui.title.admin.select-statistic"));
GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE);
final int MAX_ELEMENTS = 21;
final int correctPage;
if (pageIndex < 0)
{
correctPage = this.elements.size() / MAX_ELEMENTS;
}
else if (pageIndex > (this.elements.size() / MAX_ELEMENTS))
{
correctPage = 0;
}
else
{
correctPage = pageIndex;
}
int entitiesIndex = MAX_ELEMENTS * correctPage;
// I want first row to be only for navigation and return button.
int index = 10;
while (entitiesIndex < ((correctPage + 1) * MAX_ELEMENTS) &&
entitiesIndex < this.elements.size())
{
if (!panelBuilder.slotOccupied(index))
{
panelBuilder.item(index, this.createStatisticButton(this.elements.get(entitiesIndex++)));
}
index++;
}
panelBuilder.item(3,
new PanelItemBuilder().
icon(Material.RED_STAINED_GLASS_PANE).
name(this.user.getTranslation("challenges.gui.buttons.admin.cancel")).
clickHandler( (panel, user1, clickType, slot) -> {
this.consumer.accept(false, null);
return true;
}).build());
if (this.elements.size() > MAX_ELEMENTS)
{
// Navigation buttons if necessary
panelBuilder.item(18,
new PanelItemBuilder().
icon(Material.OAK_SIGN).
name(this.user.getTranslation("challenges.gui.buttons.previous")).
clickHandler((panel, user1, clickType, slot) -> {
this.build(correctPage - 1);
return true;
}).build());
panelBuilder.item(26,
new PanelItemBuilder().
icon(Material.OAK_SIGN).
name(this.user.getTranslation("challenges.gui.buttons.next")).
clickHandler((panel, user1, clickType, slot) -> {
this.build(correctPage + 1);
return true;
}).build());
}
panelBuilder.item(44,
new PanelItemBuilder().
icon(Material.OAK_DOOR).
name(this.user.getTranslation("challenges.gui.buttons.return")).
clickHandler( (panel, user1, clickType, slot) -> {
this.consumer.accept(false, null);
return true;
}).build());
panelBuilder.build();
}
/**
* This method creates PanelItem that represents given statistic.
* Some materials is not displayable in Inventory GUI, so they are replaced with "placeholder" items.
* @param statistic Material which icon must be created.
* @return PanelItem that represents given statistic.
*/
private PanelItem createStatisticButton(Statistic statistic)
{
ItemStack itemStack = new ItemStack(Material.PAPER);
return new PanelItemBuilder().
name(WordUtils.capitalize(statistic.name().toLowerCase().replace("_", " "))).
icon(itemStack).
clickHandler((panel, user1, clickType, slot) -> {
this.consumer.accept(true, statistic);
return true;
}).
glow(false).
build();
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* List with elements that will be displayed in current GUI.
*/
private List<Statistic> elements;
/**
* This variable stores consumer.
*/
private BiConsumer<Boolean,Statistic> consumer;
/**
* User who runs GUI.
*/
private User user;
}

View File

@ -2,6 +2,7 @@ package world.bentobox.challenges.tasks;
import com.google.common.collect.UnmodifiableIterator;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
@ -11,6 +12,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
@ -38,6 +40,7 @@ import world.bentobox.challenges.database.object.ChallengeLevel;
import world.bentobox.challenges.database.object.requirements.InventoryRequirements;
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.utils.Utils;
@ -447,63 +450,196 @@ public class TryToComplete
*/
private void fullFillRequirements(ChallengeResult result)
{
if (this.challenge.getChallengeType().equals(ChallengeType.ISLAND))
switch (this.challenge.getChallengeType())
{
IslandRequirements requirements = this.challenge.getRequirements();
case ISLAND -> {
IslandRequirements requirements = this.challenge.getRequirements();
if (result.meetsRequirements &&
if (result.meetsRequirements &&
requirements.isRemoveEntities() &&
!requirements.getRequiredEntities().isEmpty())
{
this.removeEntities(result.entities, result.getFactor());
}
{
this.removeEntities(result.entities, result.getFactor());
}
if (result.meetsRequirements &&
if (result.meetsRequirements &&
requirements.isRemoveBlocks() &&
!requirements.getRequiredBlocks().isEmpty())
{
this.removeBlocks(result.blocks, result.getFactor());
{
this.removeBlocks(result.blocks, result.getFactor());
}
}
}
else if (this.challenge.getChallengeType().equals(ChallengeType.INVENTORY))
{
// If remove items, then remove them
if (this.getInventoryRequirements().isTakeItems())
{
int sumEverything = result.requiredItems.stream().
case INVENTORY -> {
// If remove items, then remove them
if (this.getInventoryRequirements().isTakeItems())
{
int sumEverything = result.requiredItems.stream().
mapToInt(itemStack -> itemStack.getAmount() * result.getFactor()).
sum();
Map<ItemStack, Integer> removedItems =
Map<ItemStack, Integer> removedItems =
this.removeItems(result.requiredItems, result.getFactor());
int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum();
int removedAmount = removedItems.values().stream().mapToInt(num -> num).sum();
// Something is not removed.
if (sumEverything != removedAmount)
{
this.user.sendMessage("challenges.errors.cannot-remove-items");
// Something is not removed.
if (sumEverything != removedAmount)
{
this.user.sendMessage("challenges.errors.cannot-remove-items");
result.removedItems = removedItems;
result.meetsRequirements = false;
result.removedItems = removedItems;
result.meetsRequirements = false;
}
}
}
}
else if (this.challenge.getChallengeType().equals(ChallengeType.OTHER))
{
OtherRequirements requirements = this.challenge.getRequirements();
case OTHER -> {
OtherRequirements requirements = this.challenge.getRequirements();
if (this.addon.isEconomyProvided() && requirements.isTakeMoney())
{
this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney());
}
if (this.addon.isEconomyProvided() && requirements.isTakeMoney())
{
this.addon.getEconomyProvider().withdraw(this.user, requirements.getRequiredMoney());
}
if (requirements.isTakeExperience() &&
if (requirements.isTakeExperience() &&
this.user.getPlayer().getGameMode() != GameMode.CREATIVE)
{
// Cannot take anything from creative game mode.
this.user.getPlayer().setTotalExperience(
{
// Cannot take anything from creative game mode.
this.user.getPlayer().setTotalExperience(
this.user.getPlayer().getTotalExperience() - requirements.getRequiredExperience());
}
}
case STATISTIC -> {
StatisticRequirements requirements = this.challenge.getRequirements();
if (requirements.isReduceStatistic())
{
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 -> {
int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic());
if (removeAmount >= statistic)
{
this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0);
removeAmount -= statistic;
}
else
{
this.user.getPlayer().setStatistic(requirements.getStatistic(),
requirements.getMaterial(),
statistic - removeAmount);
removeAmount = 0;
}
}
case ENTITY -> {
int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic());
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);
removeAmount = 0;
}
}
}
// If challenges are in sync with all island members, then punish others too.
if (this.addon.getChallengesSettings().isStoreAsIslandData())
{
Island island = this.addon.getIslands().getIsland(this.world, this.user);
if (island == null)
{
// hmm
return;
}
for (UnmodifiableIterator<UUID> iterator = island.getMemberSet().iterator();
iterator.hasNext() && removeAmount > 0; )
{
Player player = Bukkit.getPlayer(iterator.next());
if (player == null || player == this.user.getPlayer())
{
// cannot punish null or player who already was punished.
continue;
}
switch (requirements.getStatistic().getType())
{
case UNTYPED -> {
int statistic = player.getStatistic(requirements.getStatistic());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(), statistic - removeAmount);
removeAmount = 0;
}
}
case ITEM, BLOCK -> {
int statistic = player.getStatistic(requirements.getStatistic());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(),
requirements.getMaterial(),
statistic - removeAmount);
removeAmount = 0;
}
}
case ENTITY -> {
int statistic = player.getStatistic(requirements.getStatistic());
if (removeAmount >= statistic)
{
removeAmount -= statistic;
player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0);
}
else
{
player.setStatistic(requirements.getStatistic(),
requirements.getEntity(),
statistic - removeAmount);
removeAmount = 0;
}
}
}
}
}
}
}
}
}
@ -596,6 +732,10 @@ public class TryToComplete
{
result = this.checkOthers(this.getAvailableCompletionTimes(maxTimes));
}
else if (type.equals(ChallengeType.STATISTIC))
{
result = this.checkStatistic(this.getAvailableCompletionTimes(maxTimes));
}
else
{
result = EMPTY_RESULT;
@ -1233,6 +1373,50 @@ public class TryToComplete
}
// ---------------------------------------------------------------------
// Section: Statistic Challenge
// ---------------------------------------------------------------------
/**
* Checks if a statistic challenge can be completed or not
* It returns ChallengeResult.
* @param factor - times that user wanted to complete
*/
private ChallengeResult checkStatistic(int factor)
{
StatisticRequirements requirements = this.challenge.getRequirements();
int currentValue;
switch (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())
{
this.user.sendMessage("challenges.errors.requirement-not-met",
TextVariables.NUMBER, String.valueOf(requirements.getAmount()),
"[value]", String.valueOf(currentValue));
}
else
{
factor = requirements.getAmount() == 0 ? factor : Math.min(factor, currentValue / requirements.getAmount());
return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor);
}
return EMPTY_RESULT;
}
// ---------------------------------------------------------------------
// Section: Title parsings
// ---------------------------------------------------------------------

View File

@ -79,6 +79,7 @@ challenges:
lore-edit: '&a Edit Lore'
type-select: "&a Choose Challenge Type"
select-statistic: "&a Select Statistic Type"
challenges: '&6 Challenges'
game-modes: '&6 Choose GameMode'
@ -176,6 +177,13 @@ challenges:
challenge-wipe: 'Wipe challenges database'
players-wipe: 'Wipe user database'
required-statistic: 'Required Statistic'
required-amount: 'Required amount'
remove-statistic: 'Reduce statistic value'
statistic-block: 'Statistic Block'
statistic-item: 'Statistic Item'
statistic-entity: 'Statistic Entity'
library: 'Web Library'
download: 'Download Libraries'
@ -183,6 +191,7 @@ challenges:
island: '&6 Island Type'
inventory: '&6 Inventory Type'
other: '&6 Other Type'
statistic: '&6 Statistic Type'
next: 'Next'
previous: 'Previous'
return: 'Return'
@ -349,6 +358,13 @@ challenges:
Right click to enable cache clearing.'
download-disabled: 'GitHub data downloader is disabled in BentoBox. Without it, you cannot use Libraries!'
required-statistic: 'Type of statistic that are checked.'
required-amount: 'Amount that need to be reached for the statistic.'
remove-statistic: 'Reduce statistic value from player stats data.'
statistic-block: 'Block which stats must be checked.'
statistic-item: 'Item which stats must be checked.'
statistic-entity: 'Entity which stats must be checked.'
lore:
level: |-
Level string.
@ -432,6 +448,7 @@ challenges:
island: '&a require blocks or mobs around player'
inventory: '&a require items in the player"s inventory'
other: '&a require things from other plugins/addons'
statistic: '&a require statistic data for player'
the-end: '- The End'
nether: '- Nether'
normal: '- Overworld'
@ -453,6 +470,10 @@ challenges:
hidden: "Only Deployed challenges are visible."
toggleable: "Toggle if undeployed challenges should be displayed"
statistic-entity: "Current entity: [entity]"
statistic-item: "Current item: [material]"
statistic-block: "Current block: [material]"
challenge-description:
level: '&f Level: [level]'
completed: '&b Completed'
@ -476,6 +497,11 @@ challenges:
required-items: 'Required Items:'
required-entities: 'Required Entities:'
required-blocks: 'Required Blocks:'
required-stats: 'Statistic: [stat]'
stat-item: 'Item: [amount] x [material]'
stat-block: 'Block: [amount] x [material]'
stat-entity: 'Entity: [amount] x [entity]'
stat-amount: 'Amount: [amount]'
level-description:
completed: '&b Completed'
completed-challenges-of: '&3 You have completed [number] out of [max] challenges in this level.'

View File

@ -1,4 +1,4 @@
name: Challenges
name: Pladdon
main: world.bentobox.challenges.ChallengesPladdon
version: ${version}
api-version: 1.17