package net.mcft.copy.backpacks.client.gui.config.custom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.resources.I18n; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.fml.client.config.GuiUtils; import net.minecraftforge.fml.common.registry.EntityEntry; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.mcft.copy.backpacks.WearableBackpacks; import net.mcft.copy.backpacks.api.BackpackRegistry.BackpackEntityEntry; import net.mcft.copy.backpacks.api.BackpackRegistry.BackpackEntry; import net.mcft.copy.backpacks.api.BackpackRegistry.ColorRange; import net.mcft.copy.backpacks.api.BackpackRegistry.RenderOptions; import net.mcft.copy.backpacks.client.gui.Direction; import net.mcft.copy.backpacks.client.gui.GuiElementBase; import net.mcft.copy.backpacks.client.gui.GuiLabel; import net.mcft.copy.backpacks.client.gui.GuiLayout; import net.mcft.copy.backpacks.client.gui.GuiLabel.TextAlign; import net.mcft.copy.backpacks.client.gui.config.BaseConfigScreen; import net.mcft.copy.backpacks.client.gui.config.BaseEntry; import net.mcft.copy.backpacks.client.gui.config.BaseEntryList; import net.mcft.copy.backpacks.client.gui.config.EntryCategory; import net.mcft.copy.backpacks.client.gui.config.EntryValueField; import net.mcft.copy.backpacks.client.gui.config.IConfigEntry; import net.mcft.copy.backpacks.client.gui.config.IConfigValue; import net.mcft.copy.backpacks.client.gui.config.BaseEntryList.Entry.MoveButton; import net.mcft.copy.backpacks.client.gui.control.GuiButton; import net.mcft.copy.backpacks.client.gui.control.GuiButtonIcon; import net.mcft.copy.backpacks.client.gui.control.GuiField; import net.mcft.copy.backpacks.client.gui.control.GuiButtonIcon.Icon; import net.mcft.copy.backpacks.config.Status; import net.mcft.copy.backpacks.config.Setting.ChangeRequiredAction; import net.mcft.copy.backpacks.config.Status.Severity; import net.mcft.copy.backpacks.item.ItemBackpack; import net.mcft.copy.backpacks.misc.util.NbtUtils; @SideOnly(Side.CLIENT) public class ScreenEntityEntry extends BaseConfigScreen { public static final long UPDATE_TIMESPAN = 1600; private final EntryListEntities _owningList; private final Optional<EntryListEntities.Entry> _entry; public final EntryEntityID entryEntityID; public final EntryButtonRenderOptions entryRenderOptions; public final EntryListBackpack listBackpack; public final GuiButton buttonCancel; public final boolean isDefault; public ScreenEntityEntry(EntryListEntities owningList, Optional<EntryListEntities.Entry> entry) { super(GuiElementBase.getCurrentScreen(), Stream.concat( ((BaseConfigScreen)GuiElementBase.getCurrentScreen()).getTitleLines().stream().skip(1), Stream.of("< this is where the entity name will go >") ).toArray(String[]::new)); _owningList = owningList; _entry = entry; Optional<BackpackEntityEntry> backpackEntry = entry.map(EntryListEntities.Entry::getValue); isDefault = backpackEntry.map(e -> e.isDefault).orElse(false); List<BackpackEntry> entries = backpackEntry.map(BackpackEntityEntry::getEntries).orElseGet(Collections::emptyList); List<BackpackEntry> defaults = entries.stream().filter(e -> e.isDefault).collect(Collectors.toList()); // Content entryEntityID = new EntryEntityID(this); entryRenderOptions = new EntryButtonRenderOptions(entryEntityID, _entry.map(e -> e.getValue().renderOptions), this::getBackpackEntries); entryRenderOptions.setLabelAndTooltip("entity.renderOptions"); listBackpack = new EntryListBackpack(defaults, entries); listEntries.addFixed(entryEntityID); listEntries.addFixed(entryRenderOptions); listEntries.addFixed(listBackpack); if (isDefault) { entryEntityID.setEnabled(false); entryRenderOptions.setEnabled(false); } // Buttons buttonCancel = new GuiButton(I18n.format("config." + WearableBackpacks.MOD_ID + ".gui.cancel")); if (buttonCancel.getWidth() < 100) buttonCancel.setWidth(100); buttonCancel.setAction(this::cancelClicked); layoutButtons.addFixed(buttonDone); // If editing an existing entry ... if (entry.isPresent()) { layoutButtons.addFixed(buttonUndo); // Add "Undo Changes" button. // If defaults are available, add "Set to Default" button. if (!defaults.isEmpty()) layoutButtons.addFixed(buttonReset); // ... otherwise add just "Cancel" button. } else layoutButtons.addFixed(buttonCancel); } private List<BackpackEntry> getBackpackEntries() { List<BackpackEntry> backpacks = listBackpack.getEntries().map(e -> e.getValue()) .filter(e -> (Item.getByNameOrId(e.backpack) != null)) .collect(Collectors.toCollection(ArrayList::new)); if (backpacks.isEmpty()) backpacks.add(BackpackEntry.DEFAULT); return backpacks; } @Override protected void doneClicked() { BackpackEntityEntry value = new BackpackEntityEntry( entryEntityID.getValue().get(), entryRenderOptions.getValue().get(), listBackpack.getValue(), isDefault); _entry.orElseGet(() -> (EntryListEntities.Entry)_owningList.addEntry()).setValue(value); GuiElementBase.display(parentScreen); } protected void cancelClicked() { GuiElementBase.display(parentScreen); } @Override public void drawScreen(int mouseX, int mouseY, float partialTicks) { buttonDone.setEnabled(listEntries.getEntries().allMatch(IConfigEntry::isValid)); buttonUndo.setEnabled(listEntries.getEntries().anyMatch(IConfigEntry::isChanged)); buttonReset.setEnabled(!listEntries.getEntries().allMatch(IConfigEntry::isDefault)); super.drawScreen(mouseX, mouseY, partialTicks); } public static class EntryEntityID extends BaseEntry.Value<String> { private final ScreenEntityEntry _owningScreen; public Optional<EntityEntry> entityEntry; public EntryEntityID(ScreenEntityEntry owningScreen) { super(new EntryValueField.Text(), Optional.empty(), owningScreen._entry.map(e -> e.getValue().entityID)); setLabelAndTooltip("entity.entityID"); ((EntryValueField.Text)control).setChangedAction(this::onChanged); _owningScreen = owningScreen; onChanged(); } @Override public List<Status> getStatus() { return entityEntry.isPresent() ? Collections.emptyList() : getValue().get().isEmpty() ? Arrays.asList(Status.EMPTY) : Arrays.asList(EntryListEntities.STATUS_NOT_FOUND); } private void onChanged() { String entityID = getValue().get(); entityEntry = EntryListEntities.getEntityEntry(entityID); _owningScreen.setTitleLine(_owningScreen.getTitleLineCount() - 1, EntryListEntities.getEntityEntryName(entityEntry, entityID)); } } @SuppressWarnings("unchecked") public static class EntryButtonRenderOptions extends BaseEntry implements IConfigValue<RenderOptions> { private final Optional<RenderOptions> _previousValue; private RenderOptions _value; public EntryButtonRenderOptions(EntryEntityID entityID, Optional<RenderOptions> value, Supplier<List<BackpackEntry>> backpacks) { _previousValue = value; _value = value.orElse(RenderOptions.DEFAULT); GuiButton button = new GuiButton(EntryCategory.BUTTON_WIDTH); String languageKey = "config." + WearableBackpacks.MOD_ID + ".entity.renderOptions"; button.setText(I18n.format(languageKey)); button.setTooltip(formatTooltip(languageKey, languageKey + ".tooltip", null, null)); button.setAction(() -> display(new ScreenRenderOptions(this, backpacks.get(), entityID.entityEntry.map(e -> (Class<? extends EntityLivingBase>)e.getEntityClass()).orElse(null)))); setSpacing(6); addWeighted(button); addFixed(buttonUndo); } @Override public List<Status> getStatus() { List<Status> status = new ArrayList<Status>(); if (!getValue().isPresent()) status.add(Status.INVALID); return status; } // IConfigValue implementation @Override public Optional<RenderOptions> getValue() { return Optional.of(_value); } @Override public void setValue(RenderOptions value) { _value = value; } // IConfigEntry implementation @Override public boolean isChanged() { return !_previousValue.equals(getValue()); } @Override public boolean isDefault() { return false; } @Override public void undoChanges() { _previousValue.ifPresent(this::setValue); } @Override public void setToDefault() { } @Override public ChangeRequiredAction applyChanges() { return ChangeRequiredAction.None; } } public static class EntryListBackpack extends BaseEntryList<BackpackEntry> { public static final int CHANCE_WIDTH = 42; public EntryListBackpack(List<BackpackEntry> defaultValue, List<BackpackEntry> previousValue) { super(300, defaultValue, previousValue); GuiLayout entryHeader = new GuiLayout(Direction.HORIZONTAL); entryHeader.setFillHorizontal(); entryHeader.setHeight(DEFAULT_ENTRY_HEIGHT); entryHeader.setPaddingHorizontal(MoveButton.WIDTH - 8, (DEFAULT_ENTRY_HEIGHT + 2) * 2); entryHeader.setSpacing(9, 2); entryHeader.addFixed(createLabel("entity.chance"), CHANCE_WIDTH + 20); entryHeader.addWeighted(createLabel("entity.backpack")); entryHeader.addWeighted(createLabel("entity.lootTable")); insertFixed(0, entryHeader); } private static GuiLabel createLabel(String key) { key = "config." + WearableBackpacks.MOD_ID + "." + key; GuiLabel label = new GuiLabel(I18n.format(key), TextAlign.CENTER); label.setTooltip(Arrays.asList(I18n.format(key + ".tooltip").split("\\n"))); label.setBottom(2); return label; } @Override protected BaseEntryList.Entry<BackpackEntry> createListEntry() { return new Entry(this); } @Override public ChangeRequiredAction applyChanges() { return ChangeRequiredAction.None; } public static class Entry extends BaseEntryList.Entry<BackpackEntry> { private static final String HEX_DIGITS = "0123456789ABCDEF"; private static final Icon ICON_COLOR_ON = new Icon(BaseEntry.TEXTURE_CONFIG_ICONS, 0, 16, 16, 16); private static final Icon ICON_COLOR_OFF = new Icon(BaseEntry.TEXTURE_CONFIG_ICONS, 16, 16, 16, 16); private static final Icon ICON_COLOR_PICK = new Icon(BaseEntry.TEXTURE_CONFIG_ICONS, 32, 16, 16, 16); public String id; private boolean _isDefault; private long _lastUpdateTime = Long.MIN_VALUE; private ItemStack _backpack; public final GuiField fieldChance; public final GuiItem itemBackpack; public final GuiField fieldBackpack; public final GuiField fieldLootTable; public final GuiButtonIcon switchColorPick; public final GuiButtonIcon switchColorOn; public final GuiLabel labelColor; public final GuiField fieldColorMin; public final GuiLabel labelColorCenter; public final GuiField fieldColorMax; public Entry(EntryListBackpack owningList) { super(owningList); int h = DEFAULT_ENTRY_HEIGHT; fieldChance = new GuiField(CHANCE_WIDTH, h); fieldChance.setMaxLength(5); fieldChance.setCharValidator(Character::isDigit); itemBackpack = new GuiItem(h, h); fieldBackpack = new GuiField(0, h); fieldBackpack.setChangedAction(this::updateBackpackItem); fieldLootTable = new GuiField(0, h); switchColorPick = new GuiButtonIcon(h, h, ICON_COLOR_PICK); switchColorPick.setSwitch(); switchColorPick.setAction(this::onColorPickPressed); switchColorOn = new GuiButtonIcon(h, h, ICON_COLOR_ON); switchColorOn.setSwitch(); switchColorOn.setAction(this::onColorOnPressed); labelColor = new GuiLabel(" Min/max:"); labelColor.setCenteredVertical(); fieldColorMin = new GuiField(50, h); fieldColorMin.setCharValidator(c -> (HEX_DIGITS.indexOf(Character.toUpperCase(c.charValue())) != -1)); labelColorCenter = new GuiLabel("/"); labelColorCenter.setCenteredVertical(); fieldColorMax = new GuiField(50, h); fieldColorMax.setCharValidator(c -> (HEX_DIGITS.indexOf(Character.toUpperCase(c.charValue())) != -1)); setSpacing(2, 2, -1, 2); addFixed(buttonMove); addFixed(fieldChance); addFixed(itemBackpack); addWeighted(fieldBackpack); addWeighted(fieldLootTable); addFixed(switchColorPick); addFixed(buttonRemove); setValue(BackpackEntry.DEFAULT); } @Override public BackpackEntry getValue() { ColorRange colorRange = switchColorOn.isSwitchOn() ? new ColorRange( !fieldColorMin.getText().isEmpty() ? Integer.parseInt(fieldColorMin.getText(), 16) : 0, !fieldColorMax.getText().isEmpty() ? Integer.parseInt(fieldColorMax.getText(), 16) : 0) : null; return new BackpackEntry(id, fieldBackpack.getText(), !fieldChance.getText().isEmpty() ? Integer.parseInt(fieldChance.getText()) : 0, fieldLootTable.getText(), colorRange, _isDefault); } @Override public void setValue(BackpackEntry value) { id = value.id; _isDefault = value.isDefault; fieldChance.setText(Integer.toString(value.chance)); fieldBackpack.setText(value.backpack); fieldLootTable.setText(value.lootTable); boolean on = (value.colorRange != null); switchColorOn.setSwitch(on); switchColorOn.setIcon(on ? ICON_COLOR_ON : ICON_COLOR_OFF); fieldColorMin.setEnabled(on); fieldColorMax.setEnabled(on); fieldColorMin.setText(String.format("%06X", on ? value.colorRange.min : ColorRange.DEFAULT.min)); fieldColorMax.setText(String.format("%06X", on ? value.colorRange.max : ColorRange.DEFAULT.max)); Arrays.asList(buttonMove, fieldBackpack, fieldLootTable, switchColorPick, buttonRemove) .forEach(e -> e.setEnabled(!_isDefault)); updateBackpackItem(); } private void updateBackpackItem() { Item item = Item.getByNameOrId(fieldBackpack.getText()); _backpack = (item != null) ? new ItemStack(item) : ItemStack.EMPTY; if (switchColorOn.isSwitchOn()) { boolean isValid = (fieldColorMin.getText().length() == 6) && (fieldColorMax.getText().length() == 6); int minColor = isValid ? Integer.parseInt(fieldColorMin.getText(), 16) : 0xFF0000; int maxColor = isValid ? Integer.parseInt(fieldColorMax.getText(), 16) : 0xFF0000; ColorRange range = new ColorRange(minColor, maxColor); int color = range.isValid() ? range.getRandom() : 0xFF0000; NbtUtils.set(_backpack, color, "display", "color"); } itemBackpack.setStack(_backpack); } private void onColorPickPressed() { if (switchColorPick.isSwitchOn()) { remove(fieldBackpack); remove(fieldLootTable); setSpacing(2); insertFixed(3, switchColorOn); insertFixed(4, labelColor); insertWeighted(5, fieldColorMin); insertFixed(6, labelColorCenter); insertWeighted(7, fieldColorMax); } else { remove(switchColorOn); remove(labelColor); remove(fieldColorMin); remove(labelColorCenter); remove(fieldColorMax); setSpacing(2, 2, -1, 2); insertWeighted(3, fieldBackpack); insertWeighted(4, fieldLootTable); } } private void onColorOnPressed() { boolean on = switchColorOn.isSwitchOn(); switchColorOn.setIcon(on ? ICON_COLOR_ON : ICON_COLOR_OFF); fieldColorMin.setEnabled(on); fieldColorMax.setEnabled(on); updateBackpackItem(); } @Override public List<Status> getStatus() { List<Status> status = new ArrayList<Status>(); if (fieldChance.getText().isEmpty()) status.add(Status.EMPTY); if (fieldBackpack.getText().isEmpty()) status.add(Status.EMPTY); else if (itemBackpack.getStack().isEmpty()) status.add(Status.WARN()); else if (!(itemBackpack.getStack().getItem() instanceof ItemBackpack)) status.add(Status.INVALID); else if (switchColorOn.isSwitchOn()) { if ((fieldColorMin.getText().length() != 6) || (fieldColorMax.getText().length() != 6) || !new ColorRange(Integer.parseInt(fieldColorMin.getText(), 16), Integer.parseInt(fieldColorMax.getText(), 16)).isValid()) status.add(Status.INVALID); } return status; } @Override public void draw(int mouseX, int mouseY, float partialTicks) { long currentTime = Minecraft.getSystemTime(); if (currentTime > _lastUpdateTime + UPDATE_TIMESPAN) { updateBackpackItem(); _lastUpdateTime = currentTime; } setTextAndBorderColorIf(fieldChance.getText().isEmpty(), fieldChance, Severity.ERROR.foregroundColor); int backpackColor = fieldBackpack.getText().isEmpty() ? Severity.ERROR.foregroundColor : itemBackpack.getStack().isEmpty() ? Severity.WARN.foregroundColor : !(itemBackpack.getStack().getItem() instanceof ItemBackpack) ? Severity.ERROR.foregroundColor : -1; setTextAndBorderColorIf((backpackColor != -1), fieldBackpack, backpackColor); if (backpackColor != -1) itemBackpack.setBorderColor(backpackColor); else itemBackpack.resetBorderColor(); boolean colorOn = switchColorOn.isSwitchOn(); boolean valid = !colorOn || (fieldColorMin.getText().length() == 6) && (fieldColorMax.getText().length() == 6) && new ColorRange(Integer.parseInt(fieldColorMin.getText(), 16), Integer.parseInt(fieldColorMax.getText(), 16)).isValid(); setTextAndBorderColorIf(!valid, fieldColorMin, Severity.ERROR.foregroundColor); setTextAndBorderColorIf(!valid, fieldColorMax, Severity.ERROR.foregroundColor); int color = !colorOn ? GuiUtils.getColorCode('8', true) : valid ? GuiUtils.getColorCode('7', true) : Severity.ERROR.foregroundColor; labelColor.setColor(color); labelColorCenter.setColor(color); super.draw(mouseX, mouseY, partialTicks); } private static void setTextAndBorderColorIf(boolean condition, GuiField field, int color) { if (condition) { field.setTextColor(color); field.setBorderColor(color); } else { field.resetTextColor(); field.resetBorderColor(); } } } public static class GuiItem extends GuiElementBase { private ItemStack _stack = ItemStack.EMPTY; private int _colorBackground = 0xFF333333; private int _colorBorder = GuiField.COLOR_BORDER_DEFAULT; public GuiItem(int width, int height) { this(width, height, ItemStack.EMPTY); } public GuiItem(int width, int height, ItemStack stack) { this(0, 0, width, height, stack); } public GuiItem(int x, int y, int width, int height, ItemStack stack) { setPosition(x, y); setSize(width, height); setStack(stack); setTooltip(0, Collections.emptyList()); // Just set delay. } public ItemStack getStack() { return _stack; } public void setStack(ItemStack value) { _stack = value; } @Override public List<String> getTooltip() { return !getStack().isEmpty() ? getContext().getScreen().getItemToolTip(getStack()) : Collections.emptyList(); } public void setBackgroundColor(int value) { _colorBackground = value; } public void resetBackgroundColor() { setBackgroundColor(0xFF333333); } public void setBorderColor(int value) { _colorBorder = value; } public void resetBorderColor() { setBorderColor(GuiField.COLOR_BORDER_DEFAULT); } @Override public void draw(int mouseX, int mouseY, float partialTicks) { int w = getWidth(); int h = getHeight(); enableBlendAlphaStuffs(); setRenderColorARGB(_colorBackground); drawRect(1, 1, w - 2, h - 2); setRenderColorARGB(_colorBorder); drawOutline(0, 0, w, h); disableBlendAlphaStuffs(); ItemStack stack = getStack(); if (stack.isEmpty()) return; GlStateManager.enableDepth(); GlStateManager.enableRescaleNormal(); RenderHelper.enableGUIStandardItemLighting(); getMC().getRenderItem().renderItemIntoGUI(stack, w / 2 - 8, h / 2 - 8); RenderHelper.disableStandardItemLighting(); GlStateManager.disableRescaleNormal(); GlStateManager.disableDepth(); } } } }