Files
NotEnoughItems/src/main/java/codechicken/nei/bookmark/BookmarkGrid.java
T

1345 lines
50 KiB
Java

package codechicken.nei.bookmark;
import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import net.minecraft.item.ItemStack;
import org.lwjgl.opengl.GL11;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemStackSet;
import codechicken.nei.ItemsGrid;
import codechicken.nei.ItemsGrid.MouseContext;
import codechicken.nei.LayoutManager;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.recipe.chain.RecipeChainMath;
public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.BookmarkMouseContext> {
public static class BookmarkMouseContext extends MouseContext {
public final int groupId;
public final RecipeId recipeId;
public final boolean controlKey;
public final boolean shiftKey;
public BookmarkMouseContext(int slotIndex, int rowIndex, int columnIndex) {
super(slotIndex, rowIndex, columnIndex);
this.controlKey = NEIClientUtils.controlKey();
this.shiftKey = NEIClientUtils.shiftKey();
this.groupId = -1;
this.recipeId = null;
}
public BookmarkMouseContext(int slotIndex, int rowIndex, int columnIndex, int groupId, RecipeId recipeId) {
super(slotIndex, rowIndex, columnIndex);
this.controlKey = NEIClientUtils.controlKey();
this.shiftKey = NEIClientUtils.shiftKey();
this.groupId = groupId;
this.recipeId = recipeId;
}
@Override
public int hashCode() {
return Objects
.hash(this.groupId, this.rowIndex, this.slotIndex, this.recipeId, this.controlKey, this.shiftKey);
}
public boolean equals(Object obj) {
if (super.equals(obj) && obj instanceof BookmarkMouseContext hover) {
return this.groupId == hover.groupId && this.controlKey == hover.controlKey
&& this.shiftKey == hover.shiftKey
&& (this.recipeId == hover.recipeId
|| this.recipeId != null && this.recipeId.equals(hover.recipeId));
}
return false;
}
}
protected static final Color DRAG_COLOR = new Color(0x66555555, true);
public static final Color GROUP_CHAIN_COLOR = new Color(0x6645DA75, true);
public static final Color GROUP_NONE_COLOR = new Color(0xff666666, true);
public static final Color RECIPE_COLOR = new Color(0x99A033A0, true);
public static final Color PLACEHOLDER_COLOR = new Color(0x66222222, true);
public static final int DEFAULT_GROUP_ID = 0;
public static final int GROUP_PANEL_WIDTH = 7;
protected static final float SCALE_SPEED = 0.1f;
protected List<BookmarkItem> bookmarkItems = new ArrayList<>();
protected final WeakHashMap<BookmarkItem, Float> animation = new WeakHashMap<>();
protected final Map<Integer, BookmarkGroup> groups = new HashMap<>();
protected final BookmarkGridGenerator gridGenerator;
private boolean isDirty = false;
public BookmarkGrid() {
this.groups.put(DEFAULT_GROUP_ID, new BookmarkGroup(BookmarkViewMode.DEFAULT));
this.gridGenerator = new BookmarkGridGenerator(this);
}
public BookmarkViewMode getViewMode(int groupId) {
return this.groups.get(groupId).viewMode;
}
public void setViewMode(int groupId, BookmarkViewMode mode) {
if (this.groups.get(groupId).viewMode != mode) {
this.groups.get(groupId).viewMode = mode;
onItemsChanged();
}
}
public void toggleViewMode(int groupId) {
this.groups.get(groupId).toggleViewMode();
onItemsChanged();
}
public void toggleCollapsedState(int groupId) {
this.groups.get(groupId).toggleCollapsedState();
onItemsChanged();
}
public void toggleCollapsedRecipe(int groupId, RecipeId recipeId) {
final BookmarkGroup group = getGroup(groupId);
final RecipeId collapsedRecipeId = group.crafting.recipeRelations.entrySet().stream()
.filter(entry -> entry.getValue().contains(recipeId)).map(entry -> entry.getKey()).findAny()
.orElse(recipeId);
group.toggleCollapsedRecipe(collapsedRecipeId);
if (group.crafting.recipeInMiddle.contains(collapsedRecipeId)) {
if (group.collapsedRecipes.contains(collapsedRecipeId)) {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 1) {
item.multiplier = 0;
}
}
} else {
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 0) {
item.multiplier = 1;
}
}
}
}
onItemsChanged();
}
public boolean isCraftingMode(int groupId) {
return this.groups.get(groupId).crafting != null;
}
public void setCraftingMode(int groupId, boolean on) {
this.groups.get(groupId).setCraftingMode(on);
onItemsChanged();
}
public void toggleCraftingMode(int groupId) {
this.groups.get(groupId).toggleCraftingMode();
onItemsChanged();
}
public BookmarkGroup getGroup(int groupId) {
return this.groups.get(groupId);
}
public int addGroup(BookmarkGroup group) {
int groupId = this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1;
this.groups.put(groupId, group);
return groupId;
}
@Override
public int getNumPages() {
if (this.gridGenerator.gridMask == null) {
getMask();
}
return this.gridGenerator.pageCount;
}
@Override
public int size() {
return this.bookmarkItems.size();
}
@Override
public boolean isEmpty() {
return this.bookmarkItems.isEmpty();
}
@Override
public List<BookmarksGridSlot> getMask() {
if (this.isDirty) {
this.isDirty = false;
this.bookmarkItems = new ArrayList<>(new LinkedHashSet<>(this.bookmarkItems));
sortIngredients();
this.realItems.clear();
for (BookmarkItem item : this.bookmarkItems) {
this.realItems.add(item.itemStack);
}
}
if (this.gridGenerator.gridMask != null) {
return this.gridGenerator.gridMask;
}
this.gridGenerator.generate();
this.page = Math.max(0, Math.min(this.page, this.gridGenerator.pageCount - 1));
if (ItemPanels.bookmarkPanel.sortableItem != null) {
ItemPanels.bookmarkPanel.sortableItem.update();
}
return this.gridGenerator.gridMask;
}
@Override
protected BookmarkMouseContext getMouseContext(int mousex, int mousey) {
if (contains(mousex, mousey) && !LayoutManager.bookmarkPanel.inEditingState()) {
return generateMouseContext(mousex, mousey);
}
return null;
}
private BookmarkMouseContext generateMouseContext(int mousex, int mousey) {
final BookmarksGridSlot hovered = getSlotMouseOver(mousex, mousey);
if (hovered != null) {
return new BookmarkMouseContext(
hovered.slotIndex,
hovered.slotIndex / columns,
hovered.slotIndex % columns,
hovered.getGroupId(),
this.gridGenerator.itemToRecipe.getOrDefault(hovered.itemIndex, hovered.getRecipeId()));
}
final int rowIndex = getHoveredRowIndex(false);
if (rowIndex != -1) {
final int groupId = getRowGroupId(rowIndex);
final int rowItemIndex = getRowItemIndex(rowIndex, false);
if (rowItemIndex != Integer.MIN_VALUE) {
int columnIndex = getColumnIndex(mousex);
BookmarkGroup group = getGroup(groupId);
RecipeId recipeId = null;
if (!group.collapsed) {
recipeId = this.gridGenerator.itemToRecipe
.getOrDefault(rowItemIndex, getCalculatedItem(rowItemIndex).recipeId);
}
return new BookmarkMouseContext(
-1,
rowIndex,
columnIndex < 0 || columnIndex > this.columns ? -1 : columnIndex,
groupId,
recipeId);
}
}
return null;
}
public int getRowGroupId(int rowIndex) {
if (this.gridGenerator.gridMask == null) {
getMask();
}
return this.gridGenerator.getRowGroupId(rowIndex);
}
public int getRowItemIndex(int rowIndex, boolean firstItemIndex) {
if (this.gridGenerator.gridMask == null) {
getMask();
}
int absoluteSlotIndex = (this.page * this.rows + rowIndex) * this.columns
+ (firstItemIndex ? 0 : this.columns - 1);
for (int index = 0; index < this.columns; index++) {
if (this.gridGenerator.slotToItem.containsKey(absoluteSlotIndex + index * (firstItemIndex ? 1 : -1))) {
return this.gridGenerator.slotToItem.get(absoluteSlotIndex + index * (firstItemIndex ? 1 : -1));
}
}
return Integer.MIN_VALUE;
}
public int getHoveredRowIndex(boolean groupPanel) {
final Point mouse = GuiDraw.getMousePosition();
final int leftBorder = marginLeft + paddingLeft;
final int r = getRowIndex(mouse.y);
if (this.isEmpty()) {
return -1;
}
if (!(new Rectangle4i(
leftBorder - (groupPanel ? BookmarkGrid.GROUP_PANEL_WIDTH : 0),
marginTop,
columns * SLOT_SIZE,
(getLastRowIndex() + 1) * SLOT_SIZE)).contains(mouse.x, mouse.y)) {
return -1;
}
if (groupPanel && mouse.x >= leftBorder - GROUP_PANEL_WIDTH && mouse.x < leftBorder) {
return r;
}
if (!groupPanel && !isInvalidSlot(columns * r + ((mouse.x - leftBorder) / SLOT_SIZE))) {
return r;
}
return -1;
}
@Override
public void setGridSize(int mleft, int mtop, int w, int h) {
super.setGridSize(mleft + GROUP_PANEL_WIDTH, mtop, w - GROUP_PANEL_WIDTH, h);
}
private void sortIngredients() {
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId != DEFAULT_GROUP_ID) {
final List<BookmarkItem> items = this.bookmarkItems.stream().filter(itm -> itm.groupId == item.groupId)
.collect(Collectors.toList());
this.bookmarkItems.removeAll(items);
this.bookmarkItems.addAll(itemIndex, items);
itemIndex += items.size() - 1;
}
}
for (Entry<Integer, BookmarkGroup> entry : this.groups.entrySet()) {
final Map<RecipeId, Integer> recipeState = new HashMap<>();
final BookmarkGroup group = entry.getValue();
final int groupId = entry.getKey();
if (group.viewMode == BookmarkViewMode.TODO_LIST || group.crafting != null) {
sortItemsInsideGroup(groupId);
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.recipeId == null && item.type != BookmarkItemType.ITEM) {
item.type = BookmarkItemType.ITEM;
} else if (item.groupId == groupId && item.recipeId != null && item.type != BookmarkItemType.ITEM) {
recipeState.put(
item.recipeId,
recipeState.getOrDefault(item.recipeId, 0)
| (item.type == BookmarkItemType.INGREDIENT ? 1 : 2));
}
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == groupId && item.type != BookmarkItemType.ITEM
&& recipeState.getOrDefault(item.recipeId, 0) != 3) {
if (item.type == BookmarkItemType.INGREDIENT) {
item.recipeId = null;
}
recipeState.remove(item.recipeId);
item.type = BookmarkItemType.ITEM;
}
}
group.collapsedRecipes.removeIf(recipeId -> !recipeState.containsKey(recipeId));
if (group.crafting != null) {
group.crafting.refresh(createChainItems(groupId), group.collapsedRecipes);
}
}
}
public void sortItemsInsideGroup(int groupId) {
final int size = this.bookmarkItems.size();
int itemIndex = 0;
while (itemIndex < size) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && item.recipeId != null) {
final Map<Integer, Integer> sortingRank = new HashMap<>();
final List<BookmarkItem> sortedItems = new ArrayList<>();
final List<Integer> items = new ArrayList<>();
for (int index = itemIndex; index < size; index++) {
if (item.equalsRecipe(this.bookmarkItems.get(index))) {
sortingRank
.put(index, this.bookmarkItems.get(index).type == BookmarkItemType.INGREDIENT ? 1 : -1);
items.add(index);
}
}
items.sort((a, b) -> sortingRank.get(a) - sortingRank.get(b));
for (int index : items) {
sortedItems.add(this.bookmarkItems.get(index));
}
this.bookmarkItems.removeAll(sortedItems);
this.bookmarkItems.addAll(itemIndex, sortedItems);
itemIndex += sortedItems.size();
} else {
itemIndex++;
}
}
}
@Override
public void draw(int mousex, int mousey) {
if (getPerPage() == 0) {
return;
}
if (this.gridGenerator.gridMask == null) {
getMask();
}
if (!this.isEmpty() && this.gridGenerator.maxAbsoluteSlotIndex > 0) {
final GroupingItem groupingItem = ItemPanels.bookmarkPanel.groupingItem;
final int sortableGroupId = ItemPanels.bookmarkPanel.sortableGroup != null
? ItemPanels.bookmarkPanel.sortableGroup.groupId
: -3;
final int rowShift = this.page * this.rows;
Map<Integer, Integer> groups = this.gridGenerator.rowToGroupId;
if (groupingItem != null && groupingItem.hasEndRow()) {
groups = groupingItem.prepareGroups(groups);
} else if (sortableGroupId != -3) {
for (int rowIndex = 0; rowIndex < this.rows; rowIndex++) {
if (groups.getOrDefault(rowShift + rowIndex, DEFAULT_GROUP_ID) == sortableGroupId) {
NEIClientUtils.drawRect(
marginLeft + paddingLeft - GROUP_PANEL_WIDTH,
marginTop + rowIndex * SLOT_SIZE,
GROUP_PANEL_WIDTH,
SLOT_SIZE,
DRAG_COLOR);
}
}
} else if (!ItemPanels.bookmarkPanel.inEditingState()) {
final int focusedRowIndex = getHoveredRowIndex(true);
if (focusedRowIndex != -1) {
NEIClientUtils.drawRect(
marginLeft + paddingLeft - GROUP_PANEL_WIDTH,
marginTop + focusedRowIndex * SLOT_SIZE,
GROUP_PANEL_WIDTH,
SLOT_SIZE,
HIGHLIGHT_COLOR);
}
}
int previousPageGroupId = groups.getOrDefault(rowShift - 1, DEFAULT_GROUP_ID);
int nextPageGroupId = groups.getOrDefault(rowShift + this.rows, DEFAULT_GROUP_ID);
int previousGroupId = DEFAULT_GROUP_ID;
int groupStartIndex = -2;
if (previousPageGroupId == groups.get(rowShift)) {
previousGroupId = previousPageGroupId;
groupStartIndex = -1;
}
for (int rowIndex = 0; rowIndex < this.rows && groups.containsKey(rowShift + rowIndex); rowIndex++) {
int groupId = groups.get(rowShift + rowIndex);
if (groupStartIndex != -2 && previousGroupId != groupId) {
if (previousGroupId != DEFAULT_GROUP_ID && previousGroupId != sortableGroupId) {
drawGroup(previousGroupId, groupStartIndex, rowIndex - 1);
} else {
drawShadowGroup(groupStartIndex, rowIndex - 1);
}
groupStartIndex = -2;
}
if (groupStartIndex == -2) {
groupStartIndex = rowIndex;
}
previousGroupId = groupId;
}
if (groupStartIndex != -2) {
final int rowIndex = nextPageGroupId != DEFAULT_GROUP_ID
&& nextPageGroupId == groups.get(rowShift + this.rows - 1) ? this.rows : getLastRowIndex();
if (previousGroupId != DEFAULT_GROUP_ID && previousGroupId != sortableGroupId) {
drawGroup(previousGroupId, groupStartIndex, rowIndex);
} else {
drawShadowGroup(groupStartIndex, rowIndex);
}
}
}
super.draw(mousex, mousey);
}
public void drawGroup(int groupId, int rowIndexStart, int rowIndexEnd) {
final int halfWidth = GROUP_PANEL_WIDTH / 2;
final int heightPadding = SLOT_SIZE / 4;
final int leftPosition = marginLeft + paddingLeft - halfWidth - 1;
final Color color = groupId > DEFAULT_GROUP_ID && this.groups.get(groupId).crafting != null ? GROUP_CHAIN_COLOR
: GROUP_NONE_COLOR;
int height = (Math.min(rowIndexEnd, this.rows - 1) - Math.max(0, rowIndexStart) + 1) * SLOT_SIZE;
int top = marginTop + Math.max(0, rowIndexStart) * SLOT_SIZE;
if (rowIndexStart >= 0) {
NEIClientUtils
.drawRect(leftPosition, marginTop + rowIndexStart * SLOT_SIZE + heightPadding, halfWidth, 1, color);
top += heightPadding + 1;
height -= heightPadding + 1;
}
if (rowIndexEnd < this.rows) {
NEIClientUtils.drawRect(
leftPosition,
marginTop + (rowIndexEnd + 1) * SLOT_SIZE - heightPadding,
halfWidth,
1,
color);
height -= heightPadding;
}
NEIClientUtils.drawRect(leftPosition, top, 1, height, color);
}
private void drawShadowGroup(int rowIndexStart, int rowIndexEnd) {
int top = marginTop + Math.max(0, rowIndexStart) * SLOT_SIZE;
int height = (Math.min(rowIndexEnd, this.rows - 1) - Math.max(0, rowIndexStart) + 1) * SLOT_SIZE;
NEIClientUtils.drawRect(
marginLeft + paddingLeft - GROUP_PANEL_WIDTH / 2 - 1,
top + 2,
1,
height - 4,
PLACEHOLDER_COLOR);
}
@Override
public void onItemsChanged() {
this.isDirty = true;
onGridChanged();
}
@Override
public void onGridChanged() {
this.gridGenerator.gridMask = null;
super.onGridChanged();
}
public Map<Integer, BookmarkItem> createChainItems(int groupId) {
final Map<Integer, BookmarkItem> chainItems = new HashMap<>();
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId) {
chainItems.put(itemIndex, item.copy());
}
}
return chainItems;
}
public RecipeChainMath createRecipeChainMath(int groupId) {
return createRecipeChainMath(groupId, null);
}
public RecipeChainMath createRecipeChainMath(int groupId, RecipeId recipeId) {
final BookmarkGroup group = getGroup(groupId);
final List<BookmarkItem> chainItems = new ArrayList<>();
final Set<RecipeId> collapsedRecipes = group.crafting != null && recipeId == null ? group.collapsedRecipes
: Collections.emptySet();
Set<RecipeId> recipeRelations = null;
if (group.collapsed) {
recipeId = null;
}
if (group.crafting != null && recipeId != null) {
for (Map.Entry<RecipeId, Set<RecipeId>> entry : group.crafting.recipeRelations.entrySet()) {
if (entry.getValue().contains(recipeId)) {
recipeId = entry.getKey();
break;
}
}
}
if (group.crafting != null && group.crafting.recipeRelations.containsKey(recipeId)) {
recipeRelations = group.crafting.recipeRelations.get(recipeId);
} else if (recipeId != null) {
recipeRelations = new HashSet<>(Arrays.asList(recipeId));
}
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && (recipeRelations == null || recipeRelations.contains(item.recipeId))) {
if (recipeId != null && group.crafting != null
&& group.crafting.calculatedItems.containsKey(itemIndex)
&& recipeId.equals(item.recipeId)) {
chainItems.add(
item.copyWithMultiplier(
group.crafting.calculatedItems.get(itemIndex).getCalculatedMultiplier()));
} else {
chainItems.add(item.copy());
}
}
}
if (group.crafting == null) {
for (BookmarkItem item : chainItems) {
item.type = BookmarkItemType.ITEM;
item.recipeId = null;
}
}
return RecipeChainMath.of(chainItems, collapsedRecipes);
}
public void createGroup(GroupingItem groupingItem) {
final Map<Integer, List<BookmarkItem>> rowItems = collectItemsToRows();
final Map<Integer, Integer> newGroups = new HashMap<>();
boolean change = false;
for (Map.Entry<Integer, Integer> entry : groupingItem.prepareGroups(this.gridGenerator.rowToGroupId)
.entrySet()) {
if (this.gridGenerator.rowToGroupId.get(entry.getKey()) != entry.getValue()) {
int groupId = entry.getValue();
if (groupId == Integer.MIN_VALUE) {
groupId = newGroups.computeIfAbsent(
groupId,
grId -> addGroup(new BookmarkGroup(getViewMode(DEFAULT_GROUP_ID))));
} else if (groupId < 0) {
groupId = newGroups
.computeIfAbsent(groupId, grId -> addGroup(new BookmarkGroup(getViewMode(Math.abs(grId)))));
}
for (BookmarkItem item : rowItems.getOrDefault(entry.getKey(), Collections.emptyList())) {
item.groupId = groupId;
}
change = true;
}
}
if (change) {
this.bookmarkItems = rowItems.values().stream().flatMap(items -> items.stream())
.collect(Collectors.toList());
final Set<Integer> usedGroupIds = new HashSet<>();
for (BookmarkItem item : this.bookmarkItems) {
usedGroupIds.add(item.groupId);
}
this.groups.keySet().removeIf(k -> k != DEFAULT_GROUP_ID && !usedGroupIds.contains(k));
onItemsChanged();
}
}
private Map<Integer, List<BookmarkItem>> collectItemsToRows() {
final Map<Integer, List<BookmarkItem>> results = new HashMap<>();
final int totalRows = (int) Math.ceil(this.gridGenerator.maxAbsoluteSlotIndex / (float) this.columns);
final List<BookmarkItem> allItems = new ArrayList<>(this.bookmarkItems);
for (int rowIndex = 0; rowIndex < totalRows; rowIndex++) {
int itemIndex = Integer.MIN_VALUE;
for (int index = 0; index < this.columns; index++) {
if (this.gridGenerator.slotToItem.containsKey(rowIndex * this.columns + index)) {
itemIndex = this.gridGenerator.slotToItem.get(rowIndex * this.columns + index);
break;
}
}
if (itemIndex != Integer.MIN_VALUE) {
final BookmarkItem item = getCalculatedItem(itemIndex);
final BookmarkGroup group = getGroup(item.groupId);
final List<BookmarkItem> items = new ArrayList<>();
if (group.collapsed || group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT) {
for (BookmarkItem itm : allItems) {
if (itm.groupId == item.groupId) {
items.add(itm);
}
}
} else if (item.recipeId != null && group.crafting != null
&& group.crafting.itemToRecipe.containsKey(itemIndex)) {
final Set<RecipeId> recipeRelations = group.crafting.recipeRelations
.getOrDefault(item.recipeId, Collections.singleton(item.recipeId));
for (BookmarkItem itm : allItems) {
if (itm.groupId == item.groupId && recipeRelations.contains(itm.recipeId)) {
items.add(itm);
}
}
} else {
for (int index = 0; index < this.columns; index++) {
if (this.gridGenerator.slotToItem.containsKey(rowIndex * this.columns + index)) {
final BookmarkItem itm = this.bookmarkItems
.get(this.gridGenerator.slotToItem.get(rowIndex * this.columns + index));
if (allItems.contains(itm)) {
items.add(itm);
}
}
}
}
results.put(rowIndex, items);
allItems.removeAll(items);
}
}
results.put(totalRows, allItems);
return results;
}
public void removeGroup(int groupId) {
this.bookmarkItems.removeIf(item -> item.groupId == groupId);
if (groupId != DEFAULT_GROUP_ID) {
this.groups.remove(groupId);
}
onItemsChanged();
}
public void moveGroup(SortableGroup sortableGroup, int overRowIndex) {
int absoluteRowIndex = this.page * this.rows + overRowIndex;
int absoluteSlotIndex = absoluteRowIndex * this.columns;
int overGroupId = getRowGroupId(overRowIndex);
// move group to same grid
if (this != sortableGroup.grid) {
final BookmarkGroup sourceGroup = sortableGroup.grid.getGroup(sortableGroup.groupId);
final List<BookmarkItem> itemsToMove = sortableGroup.getBookmarkItems();
sortableGroup.grid.removeGroup(sortableGroup.groupId);
final int groupId = this.addGroup(sourceGroup);
for (BookmarkItem item : itemsToMove) {
item.groupId = groupId;
this.bookmarkItems.add(item);
}
sortableGroup.grid = this;
sortableGroup.groupId = groupId;
this.onItemsChanged();
this.getMask();// force generate grid
}
if (sortableGroup.groupId == overGroupId) {
return;
}
boolean mouseDown = false;
for (int rowId = 0; rowId < absoluteRowIndex && !mouseDown; rowId++) {
if (this.gridGenerator.rowToGroupId.get(rowId) == sortableGroup.groupId) {
mouseDown = true;
}
}
if (mouseDown) {
absoluteRowIndex += 1;
absoluteSlotIndex += this.columns;
}
if (mouseDown && absoluteSlotIndex >= this.gridGenerator.maxAbsoluteSlotIndex) {
final int itemIndex = this.gridGenerator.slotToItem
.getOrDefault(this.gridGenerator.maxAbsoluteSlotIndex - 1, Integer.MIN_VALUE);
if (itemIndex == Integer.MIN_VALUE || getCalculatedItem(itemIndex).groupId == sortableGroup.groupId) {
return;
}
moveItems(null, sortableGroup.getBookmarkItems());
} else if (this.gridGenerator.rowToGroupId.containsKey(absoluteRowIndex)) {
overGroupId = this.gridGenerator.rowToGroupId.get(absoluteRowIndex);
if (overGroupId != DEFAULT_GROUP_ID && overGroupId
== this.gridGenerator.rowToGroupId.getOrDefault(absoluteRowIndex - 1, DEFAULT_GROUP_ID)) {
return;
}
final int itemIndex = this.gridGenerator.slotToItem.getOrDefault(absoluteSlotIndex, Integer.MIN_VALUE);
BookmarkItem bookmarkItem = getCalculatedItem(itemIndex);
if (bookmarkItem == null) {
return;
}
if (bookmarkItem.groupId != DEFAULT_GROUP_ID) {
for (int i = 0; i < this.bookmarkItems.size(); i++) {
if (this.bookmarkItems.get(i).groupId == bookmarkItem.groupId) {
bookmarkItem = this.bookmarkItems.get(i);
break;
}
}
}
moveItems(bookmarkItem, sortableGroup.getBookmarkItems());
}
}
public int indexOf(int groupId, ItemStack stackA, RecipeId recipeId, boolean ingredient) {
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
if (item.groupId == groupId && ingredient == (item.type == BookmarkItemType.INGREDIENT)
&& (recipeId == item.recipeId || recipeId != null && recipeId.equals(item.recipeId))
&& StackInfo.equalItemAndNBT(stackA, item.itemStack, true)) {
return itemIndex;
}
}
return -1;
}
public BookmarkItem getCalculatedItem(int itemIndex) {
if (this.gridGenerator.caclulatedItems.containsKey(itemIndex)) {
return this.gridGenerator.caclulatedItems.get(itemIndex).getItem();
}
return null;
}
public BookmarkItem getBookmarkItem(int itemIndex) {
if (itemIndex < 0 || itemIndex >= this.bookmarkItems.size()) {
return null;
}
return this.bookmarkItems.get(itemIndex);
}
public int getAbsoluteSlotIndex(int itemIndex) {
return this.gridGenerator.itemToSlot.getOrDefault(itemIndex, -1);
}
public List<Integer> getSortedItems() {
return new ArrayList<>(this.gridGenerator.slotToItem.values());
}
public void addItem(BookmarkItem item, boolean animate) {
this.bookmarkItems.add(item);
if (animate && NEIClientConfig.getGridRenderingCacheMode() == 0 && NEIClientConfig.areBookmarksAnimated()) {
this.animation.put(item, 0f);
}
onItemsChanged();
}
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
final ItemStackSet seenResults = new ItemStackSet();
final ItemStackSet seenIngredients = new ItemStackSet();
final List<ItemStack> results = new ArrayList<>();
final List<ItemStack> ingredients = new ArrayList<>();
for (RecipeIngredient result : recipe.getResults()) {
ItemStack stack = result.getItemStack();
if (!seenResults.contains(stack)) {
seenResults.add(stack);
results.add(stack);
}
}
for (RecipeIngredient ingr : recipe.getIngredients()) {
ItemStack stack = ingr.getItemStack();
if (!seenIngredients.contains(stack)) {
seenIngredients.add(stack);
ingredients.add(stack);
}
}
for (ItemStack stack : results) {
this.addItem(
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT).multiplier(multiplier)
.build(),
true);
}
for (ItemStack stack : ingredients) {
this.addItem(
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier)
.build(),
true);
}
}
public void removeRecipe(int itemIndex, boolean removeFullRecipe) {
final BookmarkItem item = getCalculatedItem(itemIndex);
if (item == null) {
return;
}
if (!removeFullRecipe && item.recipeId != null && item.type == BookmarkItemType.RESULT) {
removeFullRecipe = this.bookmarkItems.stream()
.noneMatch(m -> m.type == BookmarkItemType.RESULT && !m.equals(item) && item.equalsRecipe(m));
}
if (item.recipeId != null && item.type != BookmarkItemType.ITEM && removeFullRecipe) {
removeRecipe(item.recipeId, item.groupId);
} else if (itemIndex >= 0) {
this.bookmarkItems.remove(itemIndex);
onItemsChanged();
}
}
public boolean removeRecipe(RecipeId recipeId, int groupId) {
if (this.bookmarkItems.removeIf(item -> item.equalsRecipe(recipeId, groupId))) {
onItemsChanged();
return true;
}
return false;
}
public boolean existsRecipe(RecipeId recipeId, int groupId) {
return this.bookmarkItems.stream()
.anyMatch(item -> item.type != BookmarkItemType.ITEM && item.equalsRecipe(recipeId, groupId));
}
public RecipeId getRecipeId(int itemIndex) {
final BookmarkItem bookmarkItem = getCalculatedItem(itemIndex);
return bookmarkItem != null ? bookmarkItem.recipeId : null;
}
public void moveItem(SortableItem sortableItem, int overRowIndex, int overGroupId, boolean moveAfter) {
final BookmarkGroup overGroup = getGroup(overGroupId);
int absoluteSlotIndex = (this.page * this.rows + overRowIndex) * this.columns;
if (sortableItem.isCollapsedRecipe && sortableItem.bookmarkItem.groupId != overGroupId || overGroup.collapsed
|| overGroup.viewMode == BookmarkViewMode.DEFAULT) {
return;
}
if (sortableItem.bookmarkItem.groupId != overGroupId) {
for (BookmarkItem item : this.bookmarkItems) {
if (sortableItem.containsItem(item)) {
item.groupId = overGroupId;
}
}
sortableItem.bookmarkItem.groupId = overGroupId;
onItemsChanged();
}
if (moveAfter) {
absoluteSlotIndex += this.columns;
}
if (moveAfter && absoluteSlotIndex >= this.gridGenerator.maxAbsoluteSlotIndex) {
final int itemIndex = this.gridGenerator.slotToItem
.getOrDefault(this.gridGenerator.maxAbsoluteSlotIndex - 1, Integer.MIN_VALUE);
if (itemIndex == Integer.MIN_VALUE || sortableItem.items.contains(itemIndex)) {
return;
}
moveItems(null, sortableItem.getBookmarkItems());
} else if (this.gridGenerator.slotToItem.containsKey(absoluteSlotIndex)) {
final int itemIndex = this.gridGenerator.slotToItem.get(absoluteSlotIndex);
final List<Integer> sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values());
BookmarkItem bookmarkItem = getCalculatedItem(itemIndex);
final BookmarkGroup group = getGroup(bookmarkItem.groupId);
final int sortIndex = sortedIndexes.indexOf(itemIndex);
if (sortIndex == -1 || sortableItem.items.contains(itemIndex)
|| sortIndex > 0 && sortableItem.items.contains(sortedIndexes.get(sortIndex - 1))) {
return;
}
if (group.crafting != null && sortIndex > 0) {
final RecipeId recipeId = group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId);
if (recipeId != null) {
if (recipeId.equals(this.gridGenerator.itemToRecipe.get(sortedIndexes.get(sortIndex - 1)))) {
return;
}
for (int i = 0; i < this.bookmarkItems.size(); i++) {
if (this.bookmarkItems.get(i).groupId == bookmarkItem.groupId
&& recipeId.equals(this.bookmarkItems.get(i).recipeId)) {
bookmarkItem = this.bookmarkItems.get(i);
break;
}
}
}
}
moveItems(bookmarkItem, sortableItem.getBookmarkItems());
}
}
public void moveItem(SortableItem sortableItem, int itemIndex) {
final List<Integer> sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values());
int sortIndex = sortedIndexes.indexOf(itemIndex);
final boolean moveAfter = sortIndex > sortedIndexes.indexOf(sortableItem.getItemIndex());
final BookmarkGroup group = getGroup(sortableItem.bookmarkItem.groupId);
BookmarkItem bookmarkItem = getCalculatedItem(itemIndex);
if (bookmarkItem == null || sortIndex == -1 || sortableItem.items.contains(itemIndex) || group.collapsed) {
return;
}
if (moveAfter) {
sortIndex = findNextBookmarkItem(itemIndex);
if (sortIndex == -1) {
return;
}
if (sortIndex < sortedIndexes.size()) {
bookmarkItem = getCalculatedItem(sortedIndexes.get(sortIndex));
} else {
bookmarkItem = null;
}
}
if (bookmarkItem != null && (group.crafting != null && group.viewMode == BookmarkViewMode.DEFAULT
|| group.viewMode == BookmarkViewMode.TODO_LIST
&& sortableItem.bookmarkItem.type == BookmarkItemType.RESULT)) {
BookmarkItem prevBookmarkItem = sortIndex > 1 ? getCalculatedItem(sortedIndexes.get(sortIndex - 1)) : null;
while (sortIndex > 0 && prevBookmarkItem != null
&& prevBookmarkItem.type != BookmarkItemType.ITEM
&& bookmarkItem.equalsRecipe(prevBookmarkItem)) {
prevBookmarkItem = sortIndex > 1 ? getCalculatedItem(sortedIndexes.get(sortIndex - 1)) : null;
sortIndex--;
}
bookmarkItem = getCalculatedItem(sortedIndexes.get(sortIndex));
}
moveItems(bookmarkItem, sortableItem.getBookmarkItems());
}
private int findNextBookmarkItem(int itemIndex) {
final List<Integer> sortedIndexes = new ArrayList<>(this.gridGenerator.slotToItem.values());
final int sortIndex = sortedIndexes.indexOf(itemIndex);
final BookmarkItem target = getCalculatedItem(itemIndex);
final BookmarkGroup group = getGroup(target.groupId);
int nextSortIndex = sortIndex + 1;
if (group.viewMode == BookmarkViewMode.TODO_LIST) {
final int rowId = this.gridGenerator.itemToSlot.get(itemIndex) / this.columns;
while (nextSortIndex < sortedIndexes.size()
&& (this.gridGenerator.itemToSlot.get(sortedIndexes.get(nextSortIndex)) / this.columns) == rowId) {
nextSortIndex++;
}
}
if (nextSortIndex < sortedIndexes.size()) {
final int nextItemIndex = sortedIndexes.get(nextSortIndex);
if (group.viewMode == BookmarkViewMode.TODO_LIST && target.type == BookmarkItemType.RESULT
&& target.equalsRecipe(getCalculatedItem(nextItemIndex))) {
return -1;
}
if (group.crafting != null) {
final RecipeId recipeId = group.crafting.itemToRecipe.get(itemIndex);
if (recipeId != null && recipeId.equals(this.gridGenerator.itemToRecipe.get(nextItemIndex))) {
return -1;
}
}
return nextSortIndex;
}
return Integer.MAX_VALUE;
}
private void moveItems(BookmarkItem bookmarkItem, List<BookmarkItem> items) {
this.bookmarkItems.removeAll(items);
final int index = bookmarkItem == null ? this.bookmarkItems.size() : this.bookmarkItems.indexOf(bookmarkItem);
this.bookmarkItems.addAll(index, items);
onItemsChanged();
}
public void shiftGroupAmount(int targetGroupId, long shift) {
final BookmarkGroup group = getGroup(targetGroupId);
if (group.crafting == null) {
final List<BookmarkItem> items = new ArrayList<>();
final Set<Long> sizes = new HashSet<>();
for (BookmarkItem item : this.bookmarkItems) {
if (item.groupId == targetGroupId && item.getAmount() > 0) {
sizes.add(Math.max(item.getStackSize(), item.getStackSize(item.getAmount(1))));
items.add(item);
}
}
final long gcd = sizes.stream().reduce(0L, (a, b) -> gcd(a, b));
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : items) {
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
final long customMultiplier = customFactor > 0 ? ((amount + customFactor - 1) / customFactor) : 0;
multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier);
}
for (BookmarkItem item : items) {
final long amount = item.getAmount();
final long customFactor = (amount + gcd - 1) / gcd;
item.multiplier = item.getMultiplierFromAmount(customFactor * multiplier);
}
} else {
final Map<RecipeId, Long> recipeMultipliers = new HashMap<>();
final Set<RecipeId> outputRecipes = group.crafting.outputRecipes;
final List<BookmarkItem> items = new ArrayList<>();
for (BookmarkItem item : this.bookmarkItems) {
if (!item.emptyFactor()
&& outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) {
final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE);
final boolean recipeInMiddle = group.crafting != null
&& group.crafting.recipeInMiddle.contains(item.recipeId);
recipeMultipliers.put(
item.recipeId,
Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier));
items.add(item);
}
}
for (BookmarkItem item : items) {
item.multiplier = recipeMultipliers.getOrDefault(item.recipeId, 0L);
}
}
this.onItemsChanged();
}
public void shiftItemAmount(int targetItemIndex, long shift) {
final BookmarkItem targetItem = getCalculatedItem(targetItemIndex);
final BookmarkGroup group = getGroup(targetItem.groupId);
if (group.collapsed) {
shiftGroupAmount(targetItem.groupId, shift);
} else if (targetItem.recipeId != null && targetItem.type != BookmarkItemType.ITEM) {
final RecipeId recipeId = this.gridGenerator.itemToRecipe
.getOrDefault(targetItemIndex, targetItem.recipeId);
final boolean recipeInMiddle = group.crafting != null && group.crafting.recipeInMiddle.contains(recipeId);
long multiplier = Integer.MAX_VALUE;
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
multiplier = Math.min(
shiftMultiplier(
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
shift,
recipeInMiddle ? 1 : 0),
multiplier);
}
}
for (BookmarkItem item : this.bookmarkItems) {
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
item.multiplier = multiplier;
}
}
} else {
for (BookmarkItem item : this.bookmarkItems) {
if (targetItem.equals(item)) {
item.multiplier = shiftMultiplier(item.multiplier, shift, 0);
break;
}
}
}
this.onItemsChanged();
}
private long shiftMultiplier(long multiplier, long shift, int minMultiplier) {
final long currentMultiplier = ((multiplier + shift) / shift) * shift;
return Math.min(
Integer.MAX_VALUE,
currentMultiplier <= 0 && multiplier > 1 ? 1 : Math.max(minMultiplier, currentMultiplier));
}
private long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
@Override
protected void beforeDrawItems(int mousex, int mousey, BookmarkMouseContext mouseContext) {
if (LayoutManager.bookmarkPanel.inEditingState()) {
final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup;
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
final List<BookmarksGridSlot> borders = new ArrayList<>();
for (BookmarksGridSlot slot : getMask()) {
Rectangle4i rect = getSlotRect(slot.slotIndex);
if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
} else if (sortableItem != null && sortableItem.items.contains(slot.itemIndex)) {
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, DRAG_COLOR);
borders.add(slot);
} else {
borders.add(slot);
}
}
for (BookmarksGridSlot slot : borders) {
slot.drawBorder(getSlotRect(slot.slotIndex));
}
} else {
super.beforeDrawItems(mousex, mousey, mouseContext);
}
}
@Override
protected void afterDrawItems(int mousex, int mousey, BookmarkMouseContext mouseContext) {
if (LayoutManager.bookmarkPanel.inEditingState()) {
final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup;
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext);
}
}
} else {
super.afterDrawItems(mousex, mousey, mouseContext);
}
}
@Override
protected void drawItems() {
final SortableGroup sortableGroup = LayoutManager.bookmarkPanel.sortableGroup;
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
for (BookmarksGridSlot slot : getMask()) {
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
final BookmarkItem item = slot.getBookmarkItem();
if (this.animation.containsKey(item) && this.animation.get(item) < 1) {
final float currentScale = this.animation.get(item) + SCALE_SPEED;
if (currentScale >= 1) {
this.animation.remove(item);
} else {
this.animation.put(item, currentScale);
}
drawPoppingItem(slot, getSlotRect(slot.slotIndex), currentScale);
} else {
slot.drawItem(getSlotRect(slot.slotIndex));
}
}
}
}
protected void drawPoppingItem(BookmarksGridSlot slot, Rectangle4i rect, float currentScale) {
final float inverseScaleFactor = 1f / currentScale;
final float shiftX = (rect.x + (rect.w - rect.w * currentScale) / 2f) * inverseScaleFactor;
final float shiftY = (rect.y + (rect.h - rect.h * currentScale) / 2f) * inverseScaleFactor;
GL11.glScalef(currentScale, currentScale, 1);
GL11.glTranslated(shiftX, shiftY, 0);
slot.drawItem(new Rectangle4i(0, 0, rect.w, rect.h));
GL11.glTranslated(-1 * shiftX, -1 * shiftY, 0);
GL11.glScalef(inverseScaleFactor, inverseScaleFactor, 1);
}
}