Merge pull request #373 from BentoBoxWorld/better_icons_for_entities

Better icons for entities
This commit is contained in:
tastybento 2025-02-10 05:09:09 -08:00 committed by GitHub
commit 5f8aebadf1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 221 additions and 232 deletions

View File

@ -5,15 +5,16 @@ import java.util.List;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import lv.id.bonne.panelutils.PanelUtils;
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.panel.CommonPanel;
import world.bentobox.challenges.panel.util.MultiEntitySelector;
import world.bentobox.challenges.panel.util.SingleEntitySelector;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -32,17 +33,12 @@ import world.bentobox.challenges.utils.Utils;
public class ManageEntitiesPanel extends AbstractManageEnumPanel<EntityType> {
/**
* Flag to indicate whether entities should be displayed as eggs (true) or mob heads (false).
*/
private boolean asEggs = true;
/**
* Private constructor that initializes the ManageEntitiesPanel with the provided
* entities map.
*
* @param parentGUI The parent panel that spawns this panel.
* @param requiredEntities A map of EntityType objects to their required counts.
*/
* Private constructor that initializes the ManageEntitiesPanel with the provided
* entities map.
*
* @param parentGUI The parent panel that spawns this panel.
* @param requiredEntities A map of EntityType objects to their required counts.
*/
private ManageEntitiesPanel(CommonPanel parentGUI, Map<EntityType, Integer> requiredEntities) {
super(parentGUI, requiredEntities);
}
@ -69,7 +65,12 @@ public class ManageEntitiesPanel extends AbstractManageEnumPanel<EntityType> {
*/
@Override
protected ItemStack getElementIcon(EntityType entity, int count) {
return asEggs ? PanelUtils.getEntityEgg(entity, count) : PanelUtils.getEntityHead(entity, count);
if (Tag.ENTITY_TYPES_CAN_TURN_IN_BOATS.isTagged(entity) && count > 1) {
return new ItemStack(Material.OAK_PLANKS, count); // Boats cannot be stacked
}
ItemStack icon = SingleEntitySelector.getIcon(entity);
icon.setAmount(count);
return icon;
}
/**
@ -113,8 +114,6 @@ public class ManageEntitiesPanel extends AbstractManageEnumPanel<EntityType> {
panelBuilder.item(3, createButton(Button.ADD_ENTITY));
// Position 5: Button for removing selected entities.
panelBuilder.item(5, createButton(Button.REMOVE_ENTITY));
// Position 8: Button to switch between displaying entity eggs and mob heads.
panelBuilder.item(8, createButton(Button.SWITCH_ENTITY));
}
/**
@ -138,7 +137,7 @@ public class ManageEntitiesPanel extends AbstractManageEnumPanel<EntityType> {
icon = new ItemStack(Material.BUCKET);
clickHandler = (panel, user, clickType, slot) -> {
// Open a multi-selection tool to add new entities.
MultiEntitySelector.open(this.user, this.asEggs, MultiEntitySelector.Mode.ALIVE, this.itemsMap.keySet(),
MultiEntitySelector.open(this.user, MultiEntitySelector.Mode.ALIVE, this.itemsMap.keySet(),
(status, entities) -> {
if (status) {
// For each selected entity, add it to the map with a default count.
@ -179,19 +178,6 @@ public class ManageEntitiesPanel extends AbstractManageEnumPanel<EntityType> {
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove"));
}
case SWITCH_ENTITY -> {
// Button to toggle the display mode between entity eggs and mob heads.
icon = new ItemStack(asEggs ? Material.EGG : Material.PLAYER_HEAD);
clickHandler = (panel, user, clickType, slot) -> {
// Toggle the display mode flag and rebuild the panel.
this.asEggs = !this.asEggs;
this.build();
return true;
};
glow = false;
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle"));
}
default -> {
icon = new ItemStack(Material.PAPER);
clickHandler = null;

View File

@ -26,6 +26,7 @@ import world.bentobox.challenges.panel.CommonPagedPanel;
import world.bentobox.challenges.panel.CommonPanel;
import world.bentobox.challenges.panel.ConversationUtils;
import world.bentobox.challenges.panel.util.MultiEntityTypeTagsSelector;
import world.bentobox.challenges.panel.util.SingleEntitySelector;
import world.bentobox.challenges.panel.util.UnifiedMultiSelector.Mode;
import world.bentobox.challenges.utils.Constants;
import world.bentobox.challenges.utils.Utils;
@ -316,18 +317,12 @@ public class ManageEntityGroupsPanel extends CommonPagedPanel<Tag<EntityType>>
if (entityTag.getKey().getKey().contains("boat")) {
return new ItemStack(Material.OAK_PLANKS, quantity); // Boats cannot be stacked
}
EntityType entType = Registry.ENTITY_TYPE.stream().filter(entityTag::isTagged).findAny().orElse(null);
EntityType entType = Registry.ENTITY_TYPE.stream()
.filter(entityTag::isTagged).findAny().orElse(null);
if (entType == null) {
return new ItemStack(Material.PAPER, quantity);
}
String eggName = entType.getKey().getKey().toUpperCase(Locale.ENGLISH) + "_SPAWN_EGG";
Material result;
try {
result = Material.valueOf(eggName);
} catch (Exception e) {
result = Material.PAPER;
}
return new ItemStack(result, quantity);
return SingleEntitySelector.getIcon(entType);
}

View File

@ -14,10 +14,8 @@ 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;
@ -584,7 +582,7 @@ public class ManageStatisticsPanel extends CommonPagedPanel<StatisticRec>
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) -> {
SingleEntitySelector.open(this.user, (status, entity) -> {
if (status) {
// Replace the old with the new
statisticsList.removeIf(sr -> sr.equals(req));

View File

@ -11,7 +11,6 @@ import java.util.stream.Collectors;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import lv.id.bonne.panelutils.PanelUtils;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.challenges.utils.Utils;
@ -22,22 +21,19 @@ import world.bentobox.challenges.utils.Utils;
*/
public class MultiEntitySelector extends UnifiedMultiSelector<EntityType> {
private final boolean asEgg;
private final Set<EntityType> excluded;
/**
* Private constructor.
*
* @param user the user opening the selector
* @param asEgg if true, display entities using their spawn egg icon; otherwise, use the entity head
* @param mode determines whether to show only living entities (ALIVE) or all (ANY)
* @param excluded a set of EntityType values to exclude
* @param consumer the callback to be invoked when the user confirms or cancels
*/
private MultiEntitySelector(User user, boolean asEgg, Mode mode, Set<EntityType> excluded,
private MultiEntitySelector(User user, Mode mode, Set<EntityType> excluded,
java.util.function.BiConsumer<Boolean, Collection<EntityType>> consumer) {
super(user, mode, consumer);
this.asEgg = asEgg;
this.excluded = excluded;
}
@ -45,26 +41,24 @@ public class MultiEntitySelector extends UnifiedMultiSelector<EntityType> {
* Opens the MultiEntitySelector GUI with the specified parameters.
*
* @param user the user who opens the GUI
* @param asEgg if true, show the entity spawn egg icon; otherwise, show the entity head
* @param mode the filtering mode (ALIVE or ANY)
* @param excluded a set of EntityType values to exclude from the list
* @param consumer a callback to receive the result
*/
public static void open(User user, boolean asEgg, Mode mode, Set<EntityType> excluded,
public static void open(User user, Mode mode, Set<EntityType> excluded,
java.util.function.BiConsumer<Boolean, Collection<EntityType>> consumer) {
new MultiEntitySelector(user, asEgg, mode, excluded, consumer).build();
new MultiEntitySelector(user, mode, excluded, consumer).build();
}
/**
* Opens the MultiEntitySelector GUI with default parameters (mode ANY and no exclusions).
*
* @param user the user who opens the GUI
* @param asEgg if true, show the entity spawn egg icon; otherwise, show the entity head
* @param consumer a callback to receive the result
*/
public static void open(User user, boolean asEgg,
public static void open(User user,
java.util.function.BiConsumer<Boolean, Collection<EntityType>> consumer) {
new MultiEntitySelector(user, asEgg, Mode.ANY, new HashSet<>(), consumer).build();
new MultiEntitySelector(user, Mode.ANY, new HashSet<>(), consumer).build();
}
/**
@ -73,7 +67,7 @@ public class MultiEntitySelector extends UnifiedMultiSelector<EntityType> {
@Override
protected List<EntityType> getElements() {
return Arrays.stream(EntityType.values()).filter(entity -> excluded == null || !excluded.contains(entity))
.filter(entity -> mode == Mode.ALIVE ? entity.isAlive() : true)
.filter(entity -> !SingleEntitySelector.NON_ENTITIES.contains(entity))
.sorted(Comparator.comparing(EntityType::name)).collect(Collectors.toList());
}
@ -99,7 +93,7 @@ public class MultiEntitySelector extends UnifiedMultiSelector<EntityType> {
*/
@Override
protected ItemStack getIcon(EntityType element) {
return asEgg ? PanelUtils.getEntityEgg(element) : PanelUtils.getEntityHead(element);
return SingleEntitySelector.getIcon(element);
}
/**

View File

@ -6,6 +6,7 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@ -28,216 +29,231 @@ import world.bentobox.challenges.utils.Utils;
*/
public class SingleEntitySelector extends PagedSelector<EntityType>
{
/**
* Instantiates a new Single entity selector.
*
* @param user the user
* @param asEggs the boolean
* @param mode the mode
* @param excluded the excluded
* @param consumer the consumer
*/
private SingleEntitySelector(User user, boolean asEggs, Mode mode, Set<EntityType> excluded, BiConsumer<Boolean, EntityType> consumer)
{
super(user);
this.consumer = consumer;
this.asEggs = asEggs;
this.elements = Arrays.stream(EntityType.values()).
filter(entity -> !excluded.contains(entity)).
filter(entity -> {
if (mode == Mode.ALIVE)
{
return entity.isAlive();
}
else
{
return true;
}
}).
// Sort by names
sorted(Comparator.comparing(EntityType::name)).
collect(Collectors.toList());
// Init without filters applied.
this.filterElements = this.elements;
}
/**
* Non entities that really cannot be used
*/
public static final List<EntityType> NON_ENTITIES = List.of(EntityType.UNKNOWN, EntityType.BLOCK_DISPLAY,
EntityType.ITEM_DISPLAY,
EntityType.TEXT_DISPLAY, EntityType.FALLING_BLOCK, EntityType.FIREBALL, EntityType.FISHING_BOBBER,
EntityType.GIANT, EntityType.ILLUSIONER, EntityType.INTERACTION, EntityType.LIGHTNING_BOLT,
EntityType.LLAMA_SPIT, EntityType.MARKER, EntityType.SHULKER_BULLET, EntityType.SMALL_FIREBALL,
EntityType.DRAGON_FIREBALL, EntityType.EVOKER_FANGS, EntityType.BREEZE_WIND_CHARGE,
EntityType.AREA_EFFECT_CLOUD);
/**
* Instantiates a new Single entity selector.
*
* @param user the user
* @param asEggs the boolean
* @param mode the mode
* @param excluded the excluded
* @param consumer the consumer
*/
private SingleEntitySelector(User user, Mode mode, Set<EntityType> excluded,
BiConsumer<Boolean, EntityType> consumer)
{
super(user);
this.consumer = consumer;
this.elements = Arrays.stream(EntityType.values()).filter(entity -> !excluded.contains(entity))
.filter(entity -> !NON_ENTITIES.contains(entity))
.filter(entity -> {
if (mode == Mode.ALIVE) {
return entity.isAlive();
} else {
return true;
}
}).
// Sort by names
sorted(Comparator.comparing(EntityType::name)).collect(Collectors.toList());
// Init without filters applied.
this.filterElements = this.elements;
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, boolean asEggs, Mode mode, Set<EntityType> excluded, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, asEggs, mode, excluded, consumer).build();
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, Mode mode, Set<EntityType> excluded, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, mode, excluded, consumer).build();
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, boolean asEggs, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, asEggs, Mode.ANY, new HashSet<>(), consumer).build();
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, Mode.ANY, new HashSet<>(), consumer).build();
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, boolean asEggs, Mode mode, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, asEggs, mode, new HashSet<>(), consumer).build();
}
/**
* This method opens GUI that allows to select challenge type.
*
* @param user User who opens GUI.
* @param consumer Consumer that allows to get clicked type.
*/
public static void open(User user, Mode mode, BiConsumer<Boolean, EntityType> consumer)
{
new SingleEntitySelector(user, mode, new HashSet<>(), consumer).build();
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* This method builds
*/
@Override
protected void build()
{
PanelBuilder panelBuilder = new PanelBuilder().user(this.user);
panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector"));
/**
* This method builds
*/
@Override
protected void build() {
PanelBuilder panelBuilder = new PanelBuilder().user(this.user);
panelBuilder.name(this.user.getTranslation(Constants.TITLE + "entity-selector"));
PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE);
PanelUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE);
this.populateElements(panelBuilder, this.filterElements);
this.populateElements(panelBuilder, this.filterElements);
panelBuilder.item(4, this.createButton());
panelBuilder.item(4, this.createButton());
panelBuilder.build();
}
panelBuilder.build();
}
/**
* This method is called when filter value is updated.
*/
@Override
protected void updateFilters()
{
if (this.searchString == null || this.searchString.isBlank())
{
this.filterElements = this.elements;
}
else
{
this.filterElements = this.elements.stream().
filter(element -> {
// If element name is set and name contains search field, then do not filter out.
return element.name().toLowerCase().contains(this.searchString.toLowerCase());
}).
distinct().
collect(Collectors.toList());
}
}
/**
* This method is called when filter value is updated.
*/
@Override
protected void updateFilters() {
if (this.searchString == null || this.searchString.isBlank()) {
this.filterElements = this.elements;
} else {
this.filterElements = this.elements.stream().filter(element -> {
// If element name is set and name contains search field, then do not filter out.
return element.name().toLowerCase().contains(this.searchString.toLowerCase());
}).distinct().collect(Collectors.toList());
}
}
/**
* This method builds PanelItem for given entity.
* @param entity Entity which PanelItem must be created.
* @return new PanelItem for given Entity.
*/
@Override
protected PanelItem createElementButton(EntityType entity)
{
final String reference = Constants.BUTTON + "entity.";
/**
* This method builds PanelItem for given entity.
* @param entity Entity which PanelItem must be created.
* @return new PanelItem for given Entity.
*/
@Override
protected PanelItem createElementButton(EntityType entity) {
final String reference = Constants.BUTTON + "entity.";
List<String> description = new ArrayList<>();
description.add(this.user.getTranslation(reference + "description",
"[id]", entity.name()));
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose"));
return new PanelItemBuilder().
name(this.user.getTranslation(reference + "name", "[entity]",
Utils.prettifyObject(entity, this.user))).
icon(this.asEggs ? PanelUtils.getEntityEgg(entity) : PanelUtils.getEntityHead(entity)).
description(description).
clickHandler((panel, user1, clickType, slot) -> {
this.consumer.accept(true, entity);
return true;
}).
build();
}
List<String> description = new ArrayList<>();
description.add(this.user.getTranslation(reference + "description", "[id]", entity.name()));
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-choose"));
/**
* This method creates PanelItem button of requested type.
* @return new PanelItem with requested functionality.
*/
private PanelItem createButton()
{
final String reference = Constants.BUTTON + "cancel.";
return new PanelItemBuilder()
.name(this.user.getTranslation(reference + "name", "[entity]", Utils.prettifyObject(entity, this.user)))
.icon(getIcon(entity)).description(description)
.clickHandler((panel, user1, clickType, slot) -> {
this.consumer.accept(true, entity);
return true;
}).build();
}
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
/**
* Get an ItemStack icon for any entity type, or PAPER if it's not really known
* @param et entity type
* @return ItemStack
*/
public static ItemStack getIcon(EntityType et) {
// Check for Materials like boats that are named the same as their entitytype.
Material m = Material.getMaterial(et.getKey().getKey().toUpperCase(Locale.ENGLISH));
if (m != null) {
return new ItemStack(m);
}
// Try to get the spawn egg for the given entity by using the naming convention.
String spawnEggName = et.name() + "_SPAWN_EGG";
try {
Material spawnEgg = Material.valueOf(spawnEggName);
// If found, return an ItemStack of the spawn egg.
return new ItemStack(spawnEgg);
} catch (IllegalArgumentException ex) {
// No spawn egg material exists for this entity type.
}
// Fallback
return switch (et) {
case EYE_OF_ENDER -> new ItemStack(Material.ENDER_EYE);
case LEASH_KNOT -> new ItemStack(Material.LEAD);
case OMINOUS_ITEM_SPAWNER -> new ItemStack(Material.TRIAL_SPAWNER);
case PLAYER -> new ItemStack(Material.PLAYER_HEAD);
case SPAWNER_MINECART -> new ItemStack(Material.MINECART);
case TRADER_LLAMA -> new ItemStack(Material.LLAMA_SPAWN_EGG);
case WITHER_SKULL -> new ItemStack(Material.WITHER_SKELETON_SKULL);
default -> new ItemStack(Material.PAPER);
ItemStack icon = new ItemStack(Material.IRON_DOOR);
PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) ->
{
this.consumer.accept(false, null);
return true;
};
};
}
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel"));
/**
* This method creates PanelItem button of requested type.
* @return new PanelItem with requested functionality.
*/
private PanelItem createButton() {
final String reference = Constants.BUTTON + "cancel.";
return new PanelItemBuilder().
icon(icon).
name(name).
description(description).
clickHandler(clickHandler).
build();
}
final String name = this.user.getTranslation(reference + "name");
final List<String> description = new ArrayList<>(3);
description.add(this.user.getTranslation(reference + "description"));
ItemStack icon = new ItemStack(Material.IRON_DOOR);
PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> {
this.consumer.accept(false, null);
return true;
};
description.add("");
description.add(this.user.getTranslation(Constants.TIPS + "click-to-cancel"));
return new PanelItemBuilder().icon(icon).name(name).description(description).clickHandler(clickHandler).build();
}
// ---------------------------------------------------------------------
// Section: Mode
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Section: Mode
// ---------------------------------------------------------------------
public enum Mode
{
ALIVE,
ANY
}
public enum Mode {
ALIVE, ANY
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* List with elements that will be displayed in current GUI.
*/
private final List<EntityType> elements;
/**
* List with elements that will be displayed in current GUI.
*/
private final List<EntityType> elements;
/**
* Indicates if entities are displayed as eggs or heads.
*/
private final boolean asEggs;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, EntityType> consumer;
/**
* This variable stores consumer.
*/
private final BiConsumer<Boolean, EntityType> consumer;
/**
* Stores filtered items.
*/
private List<EntityType> filterElements;
/**
* Stores filtered items.
*/
private List<EntityType> filterElements;
}