mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-27 06:44:00 +00:00
Compare commits
29
Commits
2.8.152-GTNH
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5faed21218 | ||
|
|
aa9da2e0a0 | ||
|
|
e9a3a494d5 | ||
|
|
ebe83f970f | ||
|
|
ad9beb7f90 | ||
|
|
68bdb51c6b | ||
|
|
e4e1964dec | ||
|
|
560618024f | ||
|
|
e2a2d2a9df | ||
|
|
7163630cb1 | ||
|
|
ab168ad6ba | ||
|
|
e13e3483f0 | ||
|
|
de26547b64 | ||
|
|
c49f8808fd | ||
|
|
1c6cc8b693 | ||
|
|
43ae1b613a | ||
|
|
402c4325a6 | ||
|
|
550e85c456 | ||
|
|
1673079cbb | ||
|
|
e6174c446a | ||
|
|
869b4571b0 | ||
|
|
35f5400ecb | ||
|
|
1e2801c891 | ||
|
|
1b3b495fec | ||
|
|
2844ea35bc | ||
|
|
c020ecbf1f | ||
|
|
a105a60a1a | ||
|
|
3a4a6734dc | ||
|
|
11f9194de9 |
No files matched your search
@@ -33,12 +33,10 @@ body:
|
||||
description: What Java version are you using? It's worth mentioning that if you play on Java9+ you should try update to latest minor release (e.g. prefer Java 17.0.6 over 17.0.2) of that version.
|
||||
options:
|
||||
- Java 8
|
||||
- Java 9
|
||||
- Java 11
|
||||
- Java 17
|
||||
- Java 19
|
||||
- Java 20
|
||||
- Java 21
|
||||
- java 25
|
||||
- java 26
|
||||
- Other (Please Specify)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
+4
-4
@@ -5,8 +5,8 @@ configurations.create('gtnhlibEmbed') {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.21:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.49:dev')
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.22:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.54:dev')
|
||||
|
||||
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
|
||||
|
||||
@@ -23,8 +23,8 @@ dependencies {
|
||||
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
|
||||
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
|
||||
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.54") { transitive = false }
|
||||
}
|
||||
|
||||
def embedArchiveOperations = gtnhGradle.archiveOperations
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.31'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.34'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -572,9 +572,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
|
||||
return;
|
||||
} else if (this.sortableGroup != null) {
|
||||
final int overRowIndex = grid.getRowIndex(mousey);
|
||||
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
|
||||
|
||||
if (overRowIndex >= 0 && this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
|
||||
if (overRowIndex >= 0 && !isOverNamespaceControls(mousex, mousey)
|
||||
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
|
||||
this.grid.moveGroup(this.sortableGroup, overRowIndex);
|
||||
}
|
||||
|
||||
@@ -1022,14 +1023,18 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
this.mouseDownSlot = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int shift, int mousex, int mousey) {
|
||||
|
||||
if ((!inEditingState() || this.sortableGroup != null) && new Rectangle4i(
|
||||
private boolean isOverNamespaceControls(int mousex, int mousey) {
|
||||
return new Rectangle4i(
|
||||
namespacePrev.x,
|
||||
namespacePrev.y,
|
||||
namespaceNext.x + namespaceNext.w - namespacePrev.x,
|
||||
namespacePrev.h).contains(mousex, mousey)) {
|
||||
namespacePrev.h).contains(mousex, mousey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int shift, int mousex, int mousey) {
|
||||
|
||||
if ((!inEditingState() || this.sortableGroup != null) && isOverNamespaceControls(mousex, mousey)) {
|
||||
|
||||
if (shift > 0) {
|
||||
this.storage.prevNamespace();
|
||||
@@ -1105,10 +1110,10 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
}
|
||||
|
||||
if (super.onMouseWheel(shift, mousex, mousey)) {
|
||||
final int overRowIndex = grid.getRowIndex(mousey);
|
||||
final int overRowIndex = Math.min(grid.getRowIndex(mousey), grid.getRows() - 1);
|
||||
|
||||
if (this.sortableGroup != null && overRowIndex >= 0
|
||||
&& this.sortableGroup.groupId != this.grid.getRowGroupId(overRowIndex)) {
|
||||
&& !this.sortableGroup.matches(this.grid, this.grid.getRowGroupId(overRowIndex))) {
|
||||
this.grid.moveGroup(this.sortableGroup, overRowIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,40 +7,36 @@ import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
|
||||
public class GuiNEIButton extends GuiButton {
|
||||
|
||||
protected static final ResourceLocation guiTex = new ResourceLocation("textures/gui/widgets.png");
|
||||
private static final DrawableResource[] BACKGROUNDS = new DrawableResource[3];
|
||||
|
||||
static {
|
||||
for (int state = 0; state < BACKGROUNDS.length; state++) {
|
||||
BACKGROUNDS[state] = new DrawableBuilder(guiTex, 0, 46 + state * 20, 200, 20).setSlices(2, 2, 2, 2).build();
|
||||
}
|
||||
}
|
||||
|
||||
public GuiNEIButton(int i, int j, int k, int l, int i1, String s) {
|
||||
super(i, j, k, l, i1, s);
|
||||
}
|
||||
|
||||
protected DrawableResource getBackground(int mouseX, int mouseY, boolean mouseOver) {
|
||||
return BACKGROUNDS[getHoverState(mouseOver)];
|
||||
}
|
||||
|
||||
public void drawButton(Minecraft minecraft, int x, int y) {
|
||||
if (!visible) return;
|
||||
|
||||
minecraft.renderEngine.bindTexture(guiTex);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
boolean mouseOver = x >= xPosition && y >= yPosition && x < xPosition + width && y < yPosition + height;
|
||||
int k = getHoverState(mouseOver);
|
||||
drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height / 2); // top left
|
||||
drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height / 2); // top
|
||||
// right
|
||||
drawTexturedModalRect(
|
||||
xPosition,
|
||||
yPosition + height / 2,
|
||||
0,
|
||||
46 + k * 20 + 20 - height / 2,
|
||||
width / 2,
|
||||
height / 2); // bottom left
|
||||
drawTexturedModalRect(
|
||||
xPosition + width / 2,
|
||||
yPosition + height / 2,
|
||||
200 - width / 2,
|
||||
46 + k * 20 + 20 - height / 2,
|
||||
width / 2,
|
||||
height / 2); // bottom right
|
||||
mouseDragged(minecraft, x, y);
|
||||
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
getBackground(x, y, mouseOver).draw(xPosition, yPosition, width, height);
|
||||
mouseDragged(minecraft, x, y);
|
||||
drawContent(minecraft, x, y, mouseOver);
|
||||
}
|
||||
|
||||
@@ -55,7 +51,7 @@ public class GuiNEIButton extends GuiButton {
|
||||
return ColorUtils.buttonLabelNormal.getColor();
|
||||
}
|
||||
|
||||
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
|
||||
protected void drawContent(Minecraft minecraft, int x, int y, boolean mouseOver) {
|
||||
FontRenderer fontrenderer = minecraft.fontRenderer;
|
||||
int color = getTextColour(mouseOver);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.regex.Pattern;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
@@ -103,7 +104,7 @@ public class ItemList {
|
||||
}
|
||||
|
||||
for (String alias : ItemInfo.getAliases(item)) {
|
||||
if (this.pattern.matcher(alias).find()) {
|
||||
if (this.pattern.matcher(StatCollector.translateToLocal(alias)).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,13 +75,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
|
||||
}
|
||||
|
||||
public static void updateItemList(ArrayList<ItemStack> newItems) {
|
||||
final ItemsPanelGrid grid = ItemPanels.itemPanel.getGrid();
|
||||
|
||||
if (grid.rawItems.size() != newItems.size()) {
|
||||
grid.setPage(0);
|
||||
}
|
||||
|
||||
grid.setItems(newItems);
|
||||
ItemPanels.itemPanel.getGrid().setItems(newItems);
|
||||
ItemPanels.itemPanel.realItems = newItems;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,8 +111,14 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
this.xPosition = (availableAreaWidth - this.scale * SLOT_SIZE) / 2;
|
||||
this.availableAreaWidth = (int) (availableAreaWidth / screenScale);
|
||||
|
||||
if (availableAreaWidth / screenScale >= mouse.x) {
|
||||
this.xPosition += gui.width * screenScale - availableAreaWidth;
|
||||
final float rightSideShift = gui.width * screenScale - availableAreaWidth;
|
||||
final boolean leftSideFree = availableAreaWidth / screenScale < mouse.x;
|
||||
final boolean rightSideFree = mouse.x < gui.width - availableAreaWidth / screenScale;
|
||||
|
||||
final boolean showOnRight = leftSideFree && rightSideFree ? !preferLeftSide() : !leftSideFree;
|
||||
|
||||
if (showOnRight) {
|
||||
this.xPosition += rightSideShift;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -128,6 +134,22 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preferLeftSide() {
|
||||
switch (NEIClientConfig.getIntSetting("inventory.itemzoom.preferredSide")) {
|
||||
case 1:
|
||||
return true;
|
||||
case 2:
|
||||
return false;
|
||||
}
|
||||
|
||||
final int leftCount = NEIClientConfig.isBookmarkPanelHidden()
|
||||
|| ItemPanels.bookmarkPanel.getGrid().getPerPage() == 0 ? 0 : ItemPanels.bookmarkPanel.getGrid().size();
|
||||
final int rightCount = ItemPanels.itemPanel.getGrid().getPerPage() == 0 ? 0
|
||||
: ItemPanels.itemPanel.getGrid().size();
|
||||
|
||||
return leftCount <= rightCount;
|
||||
}
|
||||
|
||||
private float getPointSize(GuiContainer gui) {
|
||||
final float screenScale = 1.0f / getScreenScale();
|
||||
final float availableAreaWidth = (gui.width - (gui.xSize + gui.width) / 2) * screenScale;
|
||||
@@ -197,6 +219,11 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
|
||||
if (KeyManager.isHashDown("itemzoom.toggle", NEIClientUtils.SHIFT_HASH)) {
|
||||
NEIClientConfig.getSetting("inventory.itemzoom.enabled")
|
||||
|
||||
@@ -89,6 +89,10 @@ public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot,
|
||||
public void refresh(GuiContainer gui) {
|
||||
|
||||
if (this.newItems != null) {
|
||||
if (this.rawItems.size() != this.newItems.size()) {
|
||||
this.page = 0;
|
||||
}
|
||||
|
||||
this.groupItems.clear();
|
||||
this.itemToGroup.clear();
|
||||
this.forceExpand = false;
|
||||
|
||||
@@ -167,7 +167,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
protected int activeStackIndex = -1;
|
||||
protected Dimension size = new Dimension();
|
||||
|
||||
protected final boolean showNames;
|
||||
protected int maxNamedRows = Integer.MAX_VALUE;
|
||||
|
||||
protected int length = 0;
|
||||
protected int rows = 0;
|
||||
@@ -175,29 +175,25 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
protected int gridColumns = 0;
|
||||
protected int gridCount = 0;
|
||||
|
||||
public static ItemsTooltipLineHandler grid(String label, List<ItemStack> items, int maxRows) {
|
||||
return new ItemsTooltipLineHandler(label, items, true, maxRows, false);
|
||||
}
|
||||
|
||||
public static ItemsTooltipLineHandler list(String label, List<ItemStack> items, int maxRows) {
|
||||
return new ItemsTooltipLineHandler(label, items, true, maxRows, true);
|
||||
public static ItemsTooltipLineHandler of(String label, List<ItemStack> items, int maxRows, int maxNamedRows) {
|
||||
return new ItemsTooltipLineHandler(label, items, true, maxRows, maxNamedRows);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
|
||||
this(label, items, true, DEFAULT_MAX_ROWS, false);
|
||||
this(label, items, true, DEFAULT_MAX_ROWS, 0);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
|
||||
this(label, items, saveStackSize, maxRows, false);
|
||||
this(label, items, saveStackSize, maxRows, 0);
|
||||
}
|
||||
|
||||
protected ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows,
|
||||
boolean showNames) {
|
||||
int maxNamedRows) {
|
||||
this.label = label;
|
||||
this.items = groupingItemStacks(items);
|
||||
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
|
||||
this.length = this.items.size();
|
||||
this.showNames = showNames;
|
||||
this.maxNamedRows = maxNamedRows;
|
||||
|
||||
setMaxRows(maxRows);
|
||||
}
|
||||
@@ -214,7 +210,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
this.names.clear();
|
||||
this.nameRows = 0;
|
||||
|
||||
if (!this.showNames || !layoutWithNames(maxRows)) {
|
||||
if (!layoutWithNames(maxRows)) {
|
||||
layoutAsGrid(maxRows);
|
||||
}
|
||||
|
||||
@@ -228,7 +224,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
}
|
||||
|
||||
protected boolean layoutWithNames(int maxRows) {
|
||||
final int maxNames = this.length <= maxRows ? this.length : maxRows - 1;
|
||||
final int maxNames = Math.min(this.maxNamedRows, this.length <= maxRows ? this.length : maxRows - 1);
|
||||
final int[] lineWidth = new int[maxNames + 1];
|
||||
lineWidth[0] = labelWidth();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
@@ -29,6 +30,7 @@ public class KeyManager {
|
||||
private static final Map<String, KeyBinding> keyBindings = new HashMap<>();
|
||||
private static final Map<String, String> keyAliases = new HashMap<>();
|
||||
private static volatile boolean pendingOptionsReload = false;
|
||||
private static int clickedButton = -1;
|
||||
|
||||
static {
|
||||
// Aliases for legacy keybinds
|
||||
@@ -73,7 +75,8 @@ public class KeyManager {
|
||||
final int keyCode = binding.getKeyCode();
|
||||
|
||||
if (keyCode < 0) {
|
||||
return Mouse.isButtonDown(keyCode + 100);
|
||||
final int button = keyCode + 100;
|
||||
return Mouse.isButtonDown(button) || button == clickedButton;
|
||||
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
|
||||
return Keyboard.isKeyDown(keyCode);
|
||||
}
|
||||
@@ -82,6 +85,40 @@ public class KeyManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any NEI key binding is assigned to the given mouse button.
|
||||
*/
|
||||
public static boolean isMouseBound(int button) {
|
||||
for (KeyBinding binding : keyBindings.values()) {
|
||||
if (binding.getKeyCode() == button - 100) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a hotkey handler for a click of the given mouse button if the button is bound to any NEI key binding. The
|
||||
* button is reported as down by {@link #isKeyDown} for the duration of the call, because the LWJGL button state can
|
||||
* already be released when a quick click is processed.
|
||||
*
|
||||
* @return true if the click was consumed by the handler
|
||||
*/
|
||||
public static boolean handleMouseHotkey(int button, BooleanSupplier handler) {
|
||||
if (!isMouseBound(button)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int previous = clickedButton;
|
||||
clickedButton = button;
|
||||
|
||||
try {
|
||||
return handler.getAsBoolean();
|
||||
} finally {
|
||||
clickedButton = previous;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isHashDown(String ident) {
|
||||
return isHashDown(ident, 0);
|
||||
}
|
||||
@@ -107,18 +144,23 @@ public class KeyManager {
|
||||
|
||||
public static String getKeyName(String ident, int meta) {
|
||||
final int keyCode = getKeyCode(ident);
|
||||
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode + meta);
|
||||
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode, meta);
|
||||
}
|
||||
|
||||
public static String getKeyName(String ident, int meta, int mouseBind) {
|
||||
final int keyCode = getKeyCode(ident);
|
||||
return keyCode == Keyboard.KEY_NONE && mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? null
|
||||
: getKeyName(keyCode + meta, mouseBind);
|
||||
: getKeyName(keyCode, meta, mouseBind);
|
||||
}
|
||||
|
||||
public static String getKeyName(int keyBind, int mouseBind) {
|
||||
final int keyCode = NEIKeyboardUtils.unhash(keyBind);
|
||||
return getKeyName(keyCode, keyBind - keyCode, mouseBind);
|
||||
}
|
||||
|
||||
private static String getKeyName(int keyCode, int hash, int mouseBind) {
|
||||
StringJoiner keyText = new StringJoiner(" + ");
|
||||
String keyHash = keyBind == Keyboard.KEY_NONE ? "" : NEIKeyboardUtils.getKeyName(keyBind);
|
||||
String keyHash = keyCode == Keyboard.KEY_NONE && hash == 0 ? "" : NEIKeyboardUtils.getKeyName(keyCode, hash);
|
||||
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
|
||||
|
||||
if (!keyHash.isEmpty()) {
|
||||
|
||||
@@ -220,6 +220,20 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
|
||||
if (isEnabled() && !isHidden()) {
|
||||
for (Widget widget : controlWidgets) {
|
||||
if (inputFocused == null) {
|
||||
widget.lastKeyTyped(keyID, keyChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
|
||||
if (KeyManager.isKeyDown("gui.hide")) {
|
||||
toggleBooleanSetting("inventory.hidden");
|
||||
return true;
|
||||
@@ -234,8 +248,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isEnabled() && !isHidden()) {
|
||||
for (Widget widget : controlWidgets) if (inputFocused == null) widget.lastKeyTyped(keyID, keyChar);
|
||||
if (isEnabled() && !isHidden() && inputFocused == null) {
|
||||
for (Widget widget : controlWidgets) {
|
||||
if (widget.onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -61,6 +61,9 @@ public class NEICPH implements IClientPacketHandler {
|
||||
case S2C.CACHE_BOOKMARK_DATA:
|
||||
handleCacheBookmarkData(packet);
|
||||
break;
|
||||
case S2C.SYNC:
|
||||
ServerSync.onReply(packet.readInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import codechicken.lib.config.ConfigTag;
|
||||
import codechicken.lib.config.ConfigTagParent;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.api.NEIInfo;
|
||||
import codechicken.nei.config.ConfigSet;
|
||||
@@ -200,6 +199,11 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.maxVerticalTooltipRows")
|
||||
.setComment("Max rows in vertical crafting chain tooltip lists (0 - disable, always use grid)")
|
||||
.getIntValue(12);
|
||||
API.addOption(new OptionIntegerField("inventory.bookmarks.maxVerticalTooltipRows", 0, 50));
|
||||
|
||||
tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker")
|
||||
.getHexValue(0xADADAD);
|
||||
API.addOption(
|
||||
@@ -267,6 +271,11 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.scrollPages", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.freezeCycleOnHover")
|
||||
.setComment("Stop cycling the recipe while hovering over an ingredient or a button")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.freezeCycleOnHover", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
|
||||
|
||||
@@ -372,6 +381,11 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.itemzoom.onlySolid").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.itemzoom.onlySolid", true));
|
||||
|
||||
tag.getTag("inventory.itemzoom.preferredSide")
|
||||
.setComment("Which side to prefer showing the zoomed item on, when both sides are free.")
|
||||
.getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.itemzoom.preferredSide", 3, true));
|
||||
|
||||
tag.getTag("inventory.itemzoom.helpText")
|
||||
.setComment("Display name \"Item Zoom\" and the hotkey to toggle this mod below the zoomed item.")
|
||||
.getBooleanValue(true);
|
||||
@@ -1015,16 +1029,7 @@ public class NEIClientConfig {
|
||||
? NEIClientConfig.pluginsList.parallelStream()
|
||||
: NEIClientConfig.pluginsList.stream();
|
||||
|
||||
stream.forEach(clazz -> {
|
||||
try {
|
||||
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
|
||||
config.loadConfig();
|
||||
NEIModContainer.plugins.add(config);
|
||||
logger.debug("Loaded {}", clazz.getName());
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to Load {}", clazz.getName(), e);
|
||||
}
|
||||
});
|
||||
PluginLoader.load(stream, NEIModContainer.plugins, logger);
|
||||
|
||||
KeyManager.requestOptionsReload();
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
@@ -1106,6 +1111,10 @@ public class NEIClientConfig {
|
||||
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
|
||||
}
|
||||
|
||||
public static int getMaxVerticalTooltipRows() {
|
||||
return getIntSetting("inventory.bookmarks.maxVerticalTooltipRows");
|
||||
}
|
||||
|
||||
public static boolean showItemQuantityWidget() {
|
||||
return getBooleanSetting("inventory.showItemQuantityWidget");
|
||||
}
|
||||
@@ -1166,6 +1175,10 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
|
||||
}
|
||||
|
||||
public static boolean freezeCycleOnHover() {
|
||||
return getBooleanSetting("inventory.guirecipe.freezeCycleOnHover");
|
||||
}
|
||||
|
||||
public static boolean requireShiftForOverlayRecipe() {
|
||||
return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe");
|
||||
}
|
||||
|
||||
@@ -92,9 +92,17 @@ public class NEISPH implements IServerPacketHandler {
|
||||
case C2S.SEND_CHAT_ITEM_LINK:
|
||||
handleSendChatItemLink(sender, packet);
|
||||
break;
|
||||
case C2S.SYNC:
|
||||
handleSync(sender, packet.readInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSync(EntityPlayerMP sender, int id) {
|
||||
sender.sendContainerAndContentsToPlayer(sender.openContainer, sender.openContainer.getInventory());
|
||||
new PacketCustom(channel, S2C.SYNC).writeInt(id).sendToPlayer(sender);
|
||||
}
|
||||
|
||||
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
NEIServerUtils.sendChatItemLink(sender, payloadTag);
|
||||
|
||||
@@ -24,6 +24,7 @@ public final class PacketIDs {
|
||||
public static final int REQUEST_POTION_GUI = 24;
|
||||
public static final int SET_DUMMY_SLOT = 25;
|
||||
public static final int SEND_CHAT_ITEM_LINK = 26;
|
||||
public static final int SYNC = 27;
|
||||
}
|
||||
|
||||
public static final class S2C {
|
||||
@@ -38,5 +39,6 @@ public final class PacketIDs {
|
||||
public static final int SET_CREATIVE_PLUS_MODE = 23;
|
||||
public static final int OPEN_POTION_GUI = 24;
|
||||
public static final int CACHE_BOOKMARK_DATA = 25;
|
||||
public static final int SYNC = 26;
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,7 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
if (!contains(mouse.x, mouse.y)) return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
|
||||
final class PluginLoader {
|
||||
|
||||
private PluginLoader() {}
|
||||
|
||||
static void load(Stream<Class<?>> plugins, Collection<IConfigureNEI> loadedPlugins, Logger logger) {
|
||||
final ClassLoader classLoader = IConfigureNEI.class.getClassLoader();
|
||||
|
||||
plugins.forEach(clazz -> {
|
||||
final Thread thread = Thread.currentThread();
|
||||
final ClassLoader previousClassLoader = thread.getContextClassLoader();
|
||||
|
||||
// Common-pool workers may use the system loader, which cannot see FML-only classes. Keep FML's loader
|
||||
// through error logging too: old Log4j can otherwise throw while resolving a plugin's stack frames.
|
||||
try {
|
||||
thread.setContextClassLoader(classLoader);
|
||||
try {
|
||||
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
|
||||
config.loadConfig();
|
||||
loadedPlugins.add(config);
|
||||
logger.debug("Loaded {}", clazz.getName());
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to Load {}", clazz.getName(), e);
|
||||
}
|
||||
} finally {
|
||||
thread.setContextClassLoader(previousClassLoader);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ public class PopupInputHandler implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
|
||||
if (KeyManager.isHashDown("gui.enchant") && canPerformAction("enchant")) {
|
||||
NEICPH.sendOpenEnchantmentWindow();
|
||||
|
||||
@@ -361,7 +361,7 @@ public class PositionedStack implements Cloneable {
|
||||
return;
|
||||
}
|
||||
|
||||
FluidDrawer.drawFluid(
|
||||
FluidDrawer.drawTank(
|
||||
this.relx,
|
||||
this.rely,
|
||||
this.width,
|
||||
|
||||
@@ -48,15 +48,19 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -227,18 +227,24 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (isVisible() && KeyManager.isHashDown("gui.search")) {
|
||||
setFocus(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -445,7 +445,7 @@ public class SearchTokenParser {
|
||||
final char ch = input.charAt(index);
|
||||
|
||||
if (!insideQuotes && delimiters.indexOf(ch) >= 0
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
|
||||
|
||||
if (lastEnd == index) {
|
||||
token += String.valueOf(ch);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.nei.PacketIDs.C2S;
|
||||
|
||||
/**
|
||||
* Waits until the server has processed everything the client sent before. The server handles slot clicks and NEI
|
||||
* packets in the order they came and answers after it has sent the slot updates, so once the answer is here the client
|
||||
* container holds what the server computed.
|
||||
*/
|
||||
public final class ServerSync {
|
||||
|
||||
private static int lastId;
|
||||
private static volatile int awaiting;
|
||||
|
||||
private ServerSync() {}
|
||||
|
||||
public static void request() {
|
||||
|
||||
if (!NEIClientConfig.hasSMPCounterPart()) {
|
||||
awaiting = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
awaiting = ++lastId;
|
||||
new PacketCustom(NEICPH.channel, C2S.SYNC).writeInt(awaiting).sendToServer();
|
||||
}
|
||||
|
||||
public static boolean pending() {
|
||||
return awaiting != 0;
|
||||
}
|
||||
|
||||
static void onReply(int id) {
|
||||
if (id == awaiting) awaiting = 0;
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
// 1 - partitial hidden
|
||||
// 2 - enabled
|
||||
public int state = 2;
|
||||
public int calculatedWidth;
|
||||
public volatile int calculatedWidth = -1;
|
||||
public final List<ItemStack> items;
|
||||
public final List<SubsetTag> children = new ArrayList<>();
|
||||
protected final SubsetSlot slot = new SubsetSlot();
|
||||
@@ -206,7 +206,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
this.state = 2;
|
||||
this.items.clear();
|
||||
this.children.clear();
|
||||
this.calculatedWidth = 0;
|
||||
this.calculatedWidth = -1;
|
||||
}
|
||||
|
||||
public void updateVisiblity(int mx, int my) {
|
||||
@@ -261,6 +261,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public void resize(Rectangle4i screen, Rectangle4i parent, boolean dropRight) {
|
||||
if (calculatedWidth < 0) {
|
||||
calculatedWidth = this.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
|
||||
}
|
||||
|
||||
int height = Math.min(slot.contentHeight(), screen.h);
|
||||
int width = Math.max(calculatedWidth + 2, this.items.isEmpty() ? 0 : 16 + MARGIN * 2);
|
||||
int scrollbarWidth = slot.scrollbarDim().width;
|
||||
@@ -686,12 +690,12 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
|
||||
for (SubsetTag tag : list) {
|
||||
tag.children.sort(Comparator.comparing(SubsetTag::displayName));
|
||||
tag.calculatedWidth = tag.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
|
||||
tag.calculatedWidth = -1;
|
||||
}
|
||||
|
||||
root.children.removeIf(child -> child.children.isEmpty() && child.items.isEmpty());
|
||||
root.children.sort(Comparator.comparing(SubsetTag::displayName));
|
||||
root.calculatedWidth = root.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
|
||||
root.calculatedWidth = -1;
|
||||
|
||||
calculateVisibility(root);
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -29,6 +29,10 @@ public abstract class Widget {
|
||||
|
||||
public void lastKeyTyped(int keyID, char keyChar) {}
|
||||
|
||||
public boolean onHotkeyPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean handleClickExt(int mx, int my, int button) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,19 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return this.isFocused;
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@@ -33,6 +34,7 @@ import codechicken.nei.SubsetWidget.SubsetTag;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.config.Option;
|
||||
import codechicken.nei.item.FluidDrawer;
|
||||
import codechicken.nei.recipe.CatalystInfo;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
@@ -178,6 +180,8 @@ public class API {
|
||||
|
||||
/**
|
||||
* Add an alias name that this item can be found by in the search field, in addition to its display name.
|
||||
*
|
||||
* @param aliases translation keys, localized at search time
|
||||
*/
|
||||
public static void setAliases(ItemStack item, String... aliases) {
|
||||
final List<String> list = Arrays.asList(aliases).stream().filter(Objects::nonNull).map(String::trim)
|
||||
@@ -375,6 +379,16 @@ public class API {
|
||||
StackInfo.stackStringifyHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the default rendering of a fluid in NEI fluid display slots and fluid tanks.
|
||||
*
|
||||
* @param fluid the fluid to render
|
||||
* @param renderer the renderer, or null to restore the default rendering
|
||||
*/
|
||||
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
|
||||
FluidDrawer.registerFluidRenderer(fluid, renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
|
||||
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
/**
|
||||
* Custom renderer for a fluid, used by NEI fluid display slots and fluid tanks in recipes.
|
||||
* <p>
|
||||
* Register with {@link API#registerFluidRenderer(Fluid, IFluidRenderer)}.
|
||||
*/
|
||||
public interface IFluidRenderer {
|
||||
|
||||
/**
|
||||
* Renders one 16x16 tile of the fluid. Partial tiles of a tank are clipped by NEI. The amount overlay is drawn by
|
||||
* NEI.
|
||||
*/
|
||||
void renderFluid(Fluid fluid, int x, int y);
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import codechicken.nei.BookmarkContainerInfo;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemQuantityField;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.LayoutManager;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
@@ -38,6 +37,7 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiFavoriteButton;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.GuiUsageRecipe;
|
||||
import codechicken.nei.recipe.HoveredRecipe;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
@@ -415,41 +415,37 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
private static Recipe getFocusedRecipe(ItemStack stackover, int mousex, int mousey, boolean useFavorites) {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
Recipe recipe = null;
|
||||
|
||||
if (useFavorites) {
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
final RecipeId recipeId = itemSlot.getRecipeId();
|
||||
|
||||
if (recipeId != null && (recipe = Recipe.of(recipeId)) != null) {
|
||||
|
||||
recipe.getIngredients().stream().forEach(ingr -> {
|
||||
final List<ItemStack> permutations = ingr.getPermutations();
|
||||
for (int index = 0; index < permutations.size(); index++) {
|
||||
if (FavoriteRecipes.contains(permutations.get(index))) {
|
||||
ingr.setActiveIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return focusedRecipe;
|
||||
}
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
recipe = guiRecipe.getFocusedRecipe();
|
||||
if (!useFavorites) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
|
||||
|
||||
if (hovered == null || hovered.source != HoveredRecipe.Source.ITEM_PANEL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Recipe recipe = Recipe.of(hovered.recipeId);
|
||||
|
||||
if (recipe != null) {
|
||||
recipe.getIngredients().stream().forEach(ingr -> {
|
||||
final List<ItemStack> permutations = ingr.getPermutations();
|
||||
for (int index = 0; index < permutations.size(); index++) {
|
||||
if (FavoriteRecipes.contains(permutations.get(index))) {
|
||||
ingr.setActiveIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return recipe;
|
||||
@@ -529,7 +525,9 @@ public abstract class ShortcutInputHandler {
|
||||
}
|
||||
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
|
||||
final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot);
|
||||
final HoveredRecipe hovered = HoveredRecipe.of(gui, stack);
|
||||
final RecipeId recipeId = hovered == null || hovered.source == HoveredRecipe.Source.OTHER
|
||||
|| hovered.source == HoveredRecipe.Source.BOOKMARK && hovered.favorite ? null : hovered.recipeId;
|
||||
|
||||
if (slot != null) {
|
||||
final BookmarkGroup group = slot.getGroup();
|
||||
@@ -729,36 +727,4 @@ public abstract class ShortcutInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static RecipeId getHotkeyRecipeId(GuiContainer gui, int mousex, int mousey, ItemStack stack,
|
||||
BookmarksGridSlot slot) {
|
||||
|
||||
if (slot != null) {
|
||||
return slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.getRecipeId();
|
||||
}
|
||||
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
return itemSlot.getRecipeId();
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return focusedRecipe.getRecipeId();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import static org.objectweb.asm.Opcodes.ILOAD;
|
||||
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
||||
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
||||
import static org.objectweb.asm.Opcodes.IRETURN;
|
||||
import static org.objectweb.asm.Opcodes.NEW;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -22,11 +23,14 @@ import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
|
||||
import codechicken.lib.asm.ASMBlock;
|
||||
@@ -98,6 +102,48 @@ public class NEITransformer implements IClassTransformer {
|
||||
}
|
||||
});
|
||||
|
||||
if (Boolean.getBoolean("nei.recipeOrigins")) {
|
||||
// Replace the plain ArrayList CraftingManager's constructor builds its recipe list with, so every recipe
|
||||
// added anywhere (including vanilla's own, added in that same constructor) is observed for
|
||||
// codechicken.nei.recipe.debug.RecipeOrigins. Must be done at classload time, since by the time any mod
|
||||
// code runs vanilla has already populated the list. Capturing a stack trace per recipe is slow, so this is
|
||||
// opt-in via -Dnei.recipeOrigins=true.
|
||||
transformer.add(
|
||||
new MethodTransformer(
|
||||
new ObfMapping("net/minecraft/item/crafting/CraftingManager", "<init>", "()V")) {
|
||||
|
||||
@Override
|
||||
public void transform(MethodNode mv) {
|
||||
ASMHelper.logger.debug("NEI: Hooking CraftingManager recipe list for origin tracking");
|
||||
|
||||
TypeInsnNode newInsn = null;
|
||||
MethodInsnNode initInsn = null;
|
||||
|
||||
// only the first ArrayList construction, the recipe list field
|
||||
for (AbstractInsnNode insn : mv.instructions.toArray()) {
|
||||
if (newInsn == null) {
|
||||
if (insn.getOpcode() == NEW
|
||||
&& "java/util/ArrayList".equals(((TypeInsnNode) insn).desc)) {
|
||||
newInsn = (TypeInsnNode) insn;
|
||||
}
|
||||
} else if (insn.getOpcode() == INVOKESPECIAL
|
||||
&& "java/util/ArrayList".equals(((MethodInsnNode) insn).owner)
|
||||
&& "<init>".equals(((MethodInsnNode) insn).name)) {
|
||||
initInsn = (MethodInsnNode) insn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newInsn == null || initInsn == null) {
|
||||
ASMHelper.logger.warn("NEI: CraftingManager recipe list not found, origins disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
newInsn.desc = initInsn.owner = "codechicken/nei/recipe/debug/RecipeOrigins$TrackingList";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// put glint alpha into the buffer correctly for exporting
|
||||
transformer.add(
|
||||
new MethodReplacer(
|
||||
|
||||
@@ -452,8 +452,9 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
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;
|
||||
&& ItemPanels.bookmarkPanel.sortableGroup.grid == this
|
||||
? ItemPanels.bookmarkPanel.sortableGroup.groupId
|
||||
: -3;
|
||||
final int rowShift = this.page * this.rows;
|
||||
Map<Integer, Integer> groups = this.gridGenerator.rowToGroupId;
|
||||
|
||||
@@ -795,10 +796,10 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
this.bookmarkItems.add(item);
|
||||
}
|
||||
|
||||
sortableGroup.grid = this;
|
||||
sortableGroup.groupId = groupId;
|
||||
sortableGroup.update(this, groupId);
|
||||
this.onItemsChanged();
|
||||
this.getMask();// force generate grid
|
||||
overGroupId = getRowGroupId(overRowIndex);
|
||||
}
|
||||
|
||||
if (sortableGroup.groupId == overGroupId) {
|
||||
@@ -1284,7 +1285,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
for (BookmarksGridSlot slot : getMask()) {
|
||||
Rectangle4i rect = getSlotRect(slot.slotIndex);
|
||||
|
||||
if (sortableGroup != null && slot.getGroupId() == sortableGroup.groupId) {
|
||||
if (sortableGroup != null && sortableGroup.matches(this, slot.getGroupId())) {
|
||||
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);
|
||||
@@ -1313,7 +1314,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
final SortableItem sortableItem = LayoutManager.bookmarkPanel.sortableItem;
|
||||
|
||||
for (BookmarksGridSlot slot : getMask()) {
|
||||
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
|
||||
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
|
||||
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
|
||||
slot.afterDraw(getSlotRect(slot.slotIndex), mouseContext);
|
||||
}
|
||||
@@ -1330,7 +1331,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
|
||||
for (BookmarksGridSlot slot : getMask()) {
|
||||
|
||||
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
|
||||
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
|
||||
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
|
||||
final BookmarkItem item = slot.getBookmarkItem();
|
||||
|
||||
|
||||
@@ -340,7 +340,8 @@ public class BookmarkGridGenerator {
|
||||
0,
|
||||
chainItem.getItem(),
|
||||
null,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
} else {
|
||||
return new BookmarksGridSlot(
|
||||
slotIndex,
|
||||
@@ -350,7 +351,8 @@ public class BookmarkGridGenerator {
|
||||
chainItem.getCalculatedMultiplier(),
|
||||
chainItem.getItem(),
|
||||
chainItem.calculatedType,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ public class BookmarkItem {
|
||||
private static synchronized String getItemGUID(ItemStack stack) {
|
||||
final FluidStack fluidStack = StackInfo.getFluid(stack);
|
||||
|
||||
if (fluidStack != null) {
|
||||
if (fluidStack != null && (fluidStack.amount > 0 || !StackInfo.isFluidContainer(stack))) {
|
||||
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
|
||||
} else {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
@@ -79,10 +80,21 @@ public class BookmarkStorage {
|
||||
|
||||
public int getNamespaceSize() {
|
||||
|
||||
if (this.namespaces.get(this.namespaces.size() - 1).size() > 0) {
|
||||
this.namespaces.add(new BookmarkGrid());
|
||||
} else if (this.activeNamespaceIndex == this.namespaces.size() - 2 && getActiveGrid().size() == 0) {
|
||||
this.namespaces.remove(this.namespaces.size() - 1);
|
||||
final int lastIndex = this.namespaces.size() - 1;
|
||||
final BookmarkGrid lastGrid = this.namespaces.get(lastIndex);
|
||||
final SortableGroup sortableGroup = ItemPanels.bookmarkPanel.sortableGroup;
|
||||
|
||||
if (lastGrid.size() > 0) {
|
||||
if (sortableGroup == null || sortableGroup.grid != lastGrid || !sortableGroup.aloneInGrid) {
|
||||
this.namespaces.add(new BookmarkGrid());
|
||||
}
|
||||
} else if (lastIndex > 0 && this.activeNamespaceIndex != lastIndex) {
|
||||
final BookmarkGrid prevGrid = this.namespaces.get(lastIndex - 1);
|
||||
|
||||
if (sortableGroup != null && sortableGroup.grid == prevGrid && sortableGroup.aloneInGrid
|
||||
|| prevGrid == getActiveGrid() && prevGrid.size() == 0) {
|
||||
this.namespaces.remove(lastIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return this.namespaces.size();
|
||||
@@ -198,6 +210,11 @@ public class BookmarkStorage {
|
||||
final BookmarkItem item = BookmarkItem.of(parser.parse(itemStr).getAsJsonObject());
|
||||
|
||||
if (item != null) {
|
||||
|
||||
if (!grid.groups.containsKey(item.groupId)) {
|
||||
item.groupId = BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
}
|
||||
|
||||
grid.addItem(item, false);
|
||||
} else {
|
||||
wasError = true;
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.ItemsGrid.MouseContext;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
@@ -65,7 +64,8 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
protected final int rowIndex;
|
||||
|
||||
public BookmarksGridSlot(int slotIndex, int itemIndex, long realMultiplier, long shiftAmount,
|
||||
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
|
||||
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group,
|
||||
BookmarkGrid grid) {
|
||||
super(
|
||||
slotIndex,
|
||||
itemIndex,
|
||||
@@ -96,7 +96,6 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
: CalculatedType.RESULT;
|
||||
}
|
||||
|
||||
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
|
||||
final BookmarkItem prevItem = grid.getCalculatedItem(
|
||||
grid.gridGenerator.slotToItem.getOrDefault(grid.gridGenerator.itemToSlot.get(this.itemIndex) - 1, -1));
|
||||
this.isFirstOutput = this.itemIndex >= 0 && this.bookmarkItem.type == BookmarkItemType.RESULT
|
||||
|
||||
@@ -13,13 +13,23 @@ public class SortableGroup {
|
||||
|
||||
public int groupId;
|
||||
public BookmarkGrid grid;
|
||||
public boolean aloneInGrid;
|
||||
|
||||
public int shiftX = 0;
|
||||
public int shiftY = 0;
|
||||
|
||||
public SortableGroup(BookmarkGrid grid, int groupId) {
|
||||
update(grid, groupId);
|
||||
}
|
||||
|
||||
public void update(BookmarkGrid grid, int groupId) {
|
||||
this.grid = grid;
|
||||
this.groupId = groupId;
|
||||
this.aloneInGrid = grid.bookmarkItems.stream().allMatch(item -> item.groupId == groupId);
|
||||
}
|
||||
|
||||
public boolean matches(BookmarkGrid grid, int groupId) {
|
||||
return this.grid == grid && this.groupId == groupId;
|
||||
}
|
||||
|
||||
public void postDraw(int mousex, int mousey) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
class Camera {
|
||||
|
||||
private static final float ZOOM_MIN = 0.1f;
|
||||
@@ -39,23 +41,34 @@ class Camera {
|
||||
&& y < this.widgetY + this.widgetH;
|
||||
}
|
||||
|
||||
public void startDrag(int mouseX, int mouseY) {
|
||||
public void startDrag(Point2D mouse) {
|
||||
this.dragging = true;
|
||||
this.lastDragX = mouseX;
|
||||
this.lastDragY = mouseY;
|
||||
this.lastDragX = (float) mouse.getX();
|
||||
this.lastDragY = (float) mouse.getY();
|
||||
}
|
||||
|
||||
public void drag(int mouseX, int mouseY) {
|
||||
public void drag(Point2D mouse) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float mouseX = (float) mouse.getX();
|
||||
final float mouseY = (float) mouse.getY();
|
||||
|
||||
this.scrollX -= (mouseX - this.lastDragX) / this.zoomLevel;
|
||||
this.scrollY -= (mouseY - this.lastDragY) / this.zoomLevel;
|
||||
this.lastDragX = mouseX;
|
||||
this.lastDragY = mouseY;
|
||||
}
|
||||
|
||||
public void update(Point2D mouse, int treeWidth, int treeHeight) {
|
||||
if (this.dragging && Mouse.isButtonDown(0)) {
|
||||
drag(mouse);
|
||||
}
|
||||
|
||||
clampToBounds(treeWidth, treeHeight);
|
||||
}
|
||||
|
||||
public void endDrag() {
|
||||
this.dragging = false;
|
||||
}
|
||||
@@ -79,7 +92,6 @@ class Camera {
|
||||
} else {
|
||||
this.scrollY -= direction * this.widgetH / (SCROLL_STEP_DIVISOR * this.zoomLevel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void centerOn(float worldX, float worldY) {
|
||||
|
||||
@@ -169,12 +169,12 @@ class CraftingTreeCanvas {
|
||||
: -FIT_MARGIN;
|
||||
}
|
||||
|
||||
public void startDrag(int mouseX, int mouseY) {
|
||||
this.camera.startDrag(mouseX, mouseY);
|
||||
public void startDrag(Point2D mouse) {
|
||||
this.camera.startDrag(mouse);
|
||||
}
|
||||
|
||||
public void drag(int mouseX, int mouseY) {
|
||||
this.camera.drag(mouseX, mouseY);
|
||||
public void drag(Point2D mouse) {
|
||||
this.camera.drag(mouse);
|
||||
}
|
||||
|
||||
public void endDrag() {
|
||||
@@ -259,7 +259,7 @@ class CraftingTreeCanvas {
|
||||
|
||||
public void draw(Point2D mouse, Set<BookmarkItem> collapsedItems, SearchResult searchResult,
|
||||
boolean useInventorySnapshot, ItemTreeSlot hoveredNode, ItemTreeSlot deleteHoverNode) {
|
||||
this.camera.clampToBounds(this.treeWidth, this.treeHeight);
|
||||
this.camera.update(mouse, this.treeWidth, this.treeHeight);
|
||||
|
||||
final float zScrollX = this.camera.scrollX * this.camera.zoomLevel;
|
||||
final float zScrollY = this.camera.scrollY * this.camera.zoomLevel;
|
||||
|
||||
@@ -58,6 +58,7 @@ import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.guihook.IGuiClientSide;
|
||||
import codechicken.nei.guihook.IGuiHandleMouseWheel;
|
||||
import codechicken.nei.guihook.IGuiHotkeyListener;
|
||||
import codechicken.nei.recipe.AcceptsFollowingTooltipLineHandler;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
@@ -77,7 +78,7 @@ import codechicken.nei.util.SlotInaccessible;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGuiClientSide, IGuiHandleMouseWheel,
|
||||
IContainerTooltipHandler, IGuiContainerOverlay {
|
||||
IGuiHotkeyListener, IContainerTooltipHandler, IGuiContainerOverlay {
|
||||
|
||||
public static class EventHandler {
|
||||
|
||||
@@ -102,20 +103,20 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MARGIN = 4;
|
||||
private static final int PADDING = 9;
|
||||
private static final int PADDING_TOP = 22;
|
||||
|
||||
private static final DrawableResource BG_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
0,
|
||||
0,
|
||||
64,
|
||||
64).build();
|
||||
64).setSlices(PADDING, PADDING, PADDING_TOP, PADDING).build();
|
||||
|
||||
private static final DateTimeFormatter SCREENSHOT_DATE_FORMAT = DateTimeFormatter
|
||||
.ofPattern("yyyy-MM-dd_HH.mm.ss", Locale.ROOT);
|
||||
|
||||
private static final int MARGIN = 4;
|
||||
private static final int PADDING = 9;
|
||||
private static final int PADDING_TOP = 22;
|
||||
|
||||
private final LRUCache<String, String> handlerTitleCache = new LRUCache<>(100);
|
||||
|
||||
private GuiScreen prevGui;
|
||||
@@ -162,7 +163,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
final ItemTreeSlot replaceNode = this.uiState.replaceRecipeNode;
|
||||
this.uiState.replaceRecipeNode = null;
|
||||
|
||||
if (recipe != null) {
|
||||
if (recipe != null && this.grid.getGroup(this.groupId) != null) {
|
||||
final RecipeId recipeId = recipe.getRecipeId();
|
||||
boolean changed = false;
|
||||
|
||||
@@ -190,6 +191,13 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
|
||||
// the group can be removed from the bookmark panel while a recipe screen opened from this tree is shown
|
||||
if (this.grid.getGroup(this.groupId) == null) {
|
||||
this.mc.displayGuiScreen(this.prevGui);
|
||||
return;
|
||||
}
|
||||
|
||||
this.xSize = this.width;
|
||||
this.ySize = this.height;
|
||||
|
||||
@@ -331,15 +339,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
this.hoveredNode = canHover && hoveredBadgeHandler == null ? this.canvas.getNodeMouseOver(getMousePosition())
|
||||
: null;
|
||||
|
||||
BG_TEXTURE.draw(
|
||||
MARGIN,
|
||||
MARGIN,
|
||||
this.width - MARGIN * 2,
|
||||
this.height - MARGIN * 2,
|
||||
PADDING,
|
||||
PADDING,
|
||||
PADDING_TOP,
|
||||
PADDING);
|
||||
BG_TEXTURE.draw(MARGIN, MARGIN, this.width - MARGIN * 2, this.height - MARGIN * 2);
|
||||
|
||||
if (this.math.outputRecipes.isEmpty()) {
|
||||
drawCenteredString(
|
||||
@@ -758,7 +758,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
return true;
|
||||
}
|
||||
|
||||
this.canvas.startDrag(mouseX, mouseY);
|
||||
this.canvas.startDrag(getMousePosition());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -779,7 +779,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
}
|
||||
|
||||
this.canvas.drag(mouseX, mouseY);
|
||||
this.canvas.drag(getMousePosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -912,6 +912,12 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
return;
|
||||
}
|
||||
|
||||
super.keyTyped(typedChar, keyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (this.statsPanelVisible && KeyManager.isKeyDown("recipe.recipe")) {
|
||||
final Point2D mouse = getMousePosition();
|
||||
final Widget widget = this.statsPanel.getWidgetUnderMouse((int) mouse.getX(), (int) mouse.getY());
|
||||
@@ -919,11 +925,11 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
if (widget != null && widget instanceof StatsPanel.ItemStatsSlot itemStatsSlot
|
||||
&& itemStatsSlot.recipeId != null) {
|
||||
GuiCraftingRecipe.openRecipeGui("recipeId", itemStatsSlot.recipeId.getResult(), itemStatsSlot.recipeId);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
super.keyTyped(typedChar, keyCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getHandlerTitle(String handlerName) {
|
||||
|
||||
@@ -242,7 +242,7 @@ class StatsPanel extends ScrollContainer {
|
||||
26,
|
||||
84,
|
||||
46,
|
||||
16).build();
|
||||
16).setSlices(0, 0, 2, 0).build();
|
||||
|
||||
private static final DrawableResource SLOT_DEFAULT = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
@@ -290,8 +290,8 @@ class StatsPanel extends ScrollContainer {
|
||||
|
||||
public StatsPanel() {
|
||||
final ScrollBar scrollbar = new ScrollBar().setTrackWidth(4)
|
||||
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build(), 0, 0)
|
||||
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build(), 0, 0)
|
||||
.setThumbTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 100, 24, 4).build())
|
||||
.setTrackTexture(new DrawableBuilder("nei:textures/gui/craftingtree.png", 26, 104, 24, 4).build())
|
||||
.setTrackPadding(1, -5, 1, 0).setOverflowType(ScrollBar.OverflowType.AUTO);
|
||||
|
||||
setHorizontalScroll(scrollbar);
|
||||
@@ -589,7 +589,7 @@ class StatsPanel extends ScrollContainer {
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
BG_TEXTURE.draw(this.x, this.y, this.w, this.h, 0, 0, 2, 0);
|
||||
BG_TEXTURE.draw(this.x, this.y, this.w, this.h);
|
||||
|
||||
super.draw(mouseX, mouseY);
|
||||
|
||||
|
||||
@@ -74,7 +74,11 @@ public class IMCHandler {
|
||||
tag.getInteger("imageX"),
|
||||
tag.getInteger("imageY"),
|
||||
tag.getInteger("imageWidth"),
|
||||
tag.getInteger("imageHeight"));
|
||||
tag.getInteger("imageHeight"),
|
||||
tag.hasKey("imageTextureWidth") ? tag.getInteger("imageTextureWidth")
|
||||
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE,
|
||||
tag.hasKey("imageTextureHeight") ? tag.getInteger("imageTextureHeight")
|
||||
: HandlerInfo.DEFAULT_IMAGE_TEXTURE_SIZE);
|
||||
}
|
||||
if (!info.hasImageOrItem()) {
|
||||
final String itemName = tag.getString("itemName");
|
||||
|
||||
@@ -79,19 +79,24 @@ public class RightPanel extends GuiWidget {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (!focused() && KeyManager.isHashDown("gui.search")) {
|
||||
setFocus(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,13 +15,21 @@ public class DrawableBuilder {
|
||||
private int paddingBottom = 0;
|
||||
private int paddingLeft = 0;
|
||||
private int paddingRight = 0;
|
||||
private int sliceLeft = 0;
|
||||
private int sliceRight = 0;
|
||||
private int sliceTop = 0;
|
||||
private int sliceBottom = 0;
|
||||
|
||||
public DrawableBuilder(String resourceLocation, int u, int v, int width, int height) {
|
||||
this(new ResourceLocation(resourceLocation), u, v, width, height);
|
||||
}
|
||||
|
||||
public DrawableBuilder(ResourceLocation resource, int u, int v, int width, int height) {
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.resourceLocation = new ResourceLocation(resourceLocation);
|
||||
this.resourceLocation = resource;
|
||||
}
|
||||
|
||||
public DrawableBuilder setTextureSize(int width, int height) {
|
||||
@@ -38,6 +46,14 @@ public class DrawableBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DrawableBuilder setSlices(int sliceLeft, int sliceRight, int sliceTop, int sliceBottom) {
|
||||
this.sliceLeft = sliceLeft;
|
||||
this.sliceRight = sliceRight;
|
||||
this.sliceTop = sliceTop;
|
||||
this.sliceBottom = sliceBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DrawableBuilder trim(int trimTop, int trimBottom, int trimLeft, int trimRight) {
|
||||
this.u += trimLeft;
|
||||
this.v += trimTop;
|
||||
@@ -58,6 +74,10 @@ public class DrawableBuilder {
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
textureWidth,
|
||||
textureHeight);
|
||||
textureHeight,
|
||||
sliceLeft,
|
||||
sliceRight,
|
||||
sliceTop,
|
||||
sliceBottom);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package codechicken.nei.drawable;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -19,9 +19,42 @@ public class DrawableResource extends Image {
|
||||
private final int paddingLeft;
|
||||
private final int paddingRight;
|
||||
|
||||
private final int sliceLeft;
|
||||
private final int sliceRight;
|
||||
private final int sliceTop;
|
||||
private final int sliceBottom;
|
||||
|
||||
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
|
||||
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight) {
|
||||
this(
|
||||
resourceLocation,
|
||||
u,
|
||||
v,
|
||||
width,
|
||||
height,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
textureWidth,
|
||||
textureHeight,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
// negative padding lets the texture overflow the box it is drawn into
|
||||
public DrawableResource(ResourceLocation resourceLocation, int u, int v, int width, int height, int paddingTop,
|
||||
int paddingBottom, int paddingLeft, int paddingRight, int textureWidth, int textureHeight, int sliceLeft,
|
||||
int sliceRight, int sliceTop, int sliceBottom) {
|
||||
super(u, v, width, height);
|
||||
|
||||
this.sliceLeft = sliceLeft;
|
||||
this.sliceRight = sliceRight;
|
||||
this.sliceTop = sliceTop;
|
||||
this.sliceBottom = sliceBottom;
|
||||
|
||||
this.resourceLocation = resourceLocation;
|
||||
this.textureWidth = textureWidth;
|
||||
this.textureHeight = textureHeight;
|
||||
@@ -40,6 +73,14 @@ public class DrawableResource extends Image {
|
||||
return height + paddingTop + paddingBottom;
|
||||
}
|
||||
|
||||
public int getTextureWidth() {
|
||||
return this.textureWidth;
|
||||
}
|
||||
|
||||
public int getTextureHeight() {
|
||||
return this.textureHeight;
|
||||
}
|
||||
|
||||
public void bindTexture() {
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(this.resourceLocation);
|
||||
}
|
||||
@@ -55,18 +96,28 @@ public class DrawableResource extends Image {
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
bindTexture();
|
||||
drawModalRectWithCustomSizedTexture(xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
addQuad(tessellator, xOffset + maskLeft, yOffset + maskTop, maskLeft, maskTop, width, height);
|
||||
tessellator.draw();
|
||||
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
public void draw(int xOffset, int yOffset, int width, int height, int sliceLeft, int sliceRight, int sliceTop,
|
||||
int sliceBottom) {
|
||||
final int textureWidth = getWidth();
|
||||
final int textureHeight = getHeight();
|
||||
final int middleWidth = textureWidth - sliceLeft - sliceRight;
|
||||
final int middleHeight = textureHeight - sliceTop - sliceBottom;
|
||||
final int tileWidth = width - sliceLeft - sliceRight;
|
||||
final int tileHeight = height - sliceTop - sliceBottom;
|
||||
public void draw(int xOffset, int yOffset, int width, int height) {
|
||||
|
||||
if (width == getWidth() && height == getHeight()) {
|
||||
draw(xOffset, yOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
final int sliceLeft = this.sliceLeft;
|
||||
final int sliceRight = this.sliceRight;
|
||||
final int sliceTop = this.sliceTop;
|
||||
final int sliceBottom = this.sliceBottom;
|
||||
final int middleWidth = this.width - sliceLeft - sliceRight;
|
||||
final int middleHeight = this.height - sliceTop - sliceBottom;
|
||||
final int tileWidth = width - this.paddingLeft - this.paddingRight - sliceLeft - sliceRight;
|
||||
final int tileHeight = height - this.paddingTop - this.paddingBottom - sliceTop - sliceBottom;
|
||||
|
||||
if (middleWidth > 0 && middleHeight > 0 && tileWidth > 0 && tileHeight > 0) {
|
||||
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
|
||||
@@ -78,14 +129,30 @@ public class DrawableResource extends Image {
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
bindTexture();
|
||||
|
||||
// middle parts
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
|
||||
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
|
||||
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
|
||||
final int x = xOffset + sliceLeft + tileW * middleWidth;
|
||||
|
||||
// border top
|
||||
addQuad(tessellator, x, yOffset, sliceLeft, 0, tileWidthSize, sliceTop);
|
||||
// border bottom
|
||||
addQuad(
|
||||
tessellator,
|
||||
x,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
sliceLeft,
|
||||
sliceTop + middleHeight,
|
||||
tileWidthSize,
|
||||
sliceBottom);
|
||||
|
||||
// middle parts
|
||||
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
|
||||
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
addQuad(
|
||||
tessellator,
|
||||
x,
|
||||
yOffset + sliceTop + tileH * middleHeight,
|
||||
sliceLeft,
|
||||
@@ -95,114 +162,75 @@ public class DrawableResource extends Image {
|
||||
}
|
||||
}
|
||||
|
||||
// border top
|
||||
if (sliceTop > 0) {
|
||||
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
|
||||
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset + sliceLeft + tileW * middleWidth,
|
||||
yOffset,
|
||||
sliceLeft,
|
||||
0,
|
||||
tileWidthSize,
|
||||
sliceTop);
|
||||
}
|
||||
}
|
||||
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
|
||||
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
|
||||
final int y = yOffset + sliceTop + tileH * middleHeight;
|
||||
|
||||
// border bottom
|
||||
if (sliceBottom > 0) {
|
||||
for (int tileW = 0; tileW <= tileWidthCount; tileW++) {
|
||||
final int tileWidthSize = (tileW == tileWidthCount) ? remainderWidth : middleWidth;
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset + sliceLeft + tileW * middleWidth,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
sliceLeft,
|
||||
sliceTop + middleHeight,
|
||||
tileWidthSize,
|
||||
sliceBottom);
|
||||
}
|
||||
}
|
||||
|
||||
// border left
|
||||
if (sliceLeft > 0) {
|
||||
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
|
||||
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset,
|
||||
yOffset + sliceTop + tileH * middleHeight,
|
||||
0,
|
||||
sliceTop,
|
||||
sliceLeft,
|
||||
tileHeightSize);
|
||||
}
|
||||
}
|
||||
|
||||
// border right
|
||||
if (sliceRight > 0) {
|
||||
for (int tileH = 0; tileH <= tileHeightCount; tileH++) {
|
||||
final int tileHeightSize = (tileH == tileHeightCount) ? remainderHeight : middleHeight;
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
yOffset + sliceTop + tileH * middleHeight,
|
||||
sliceLeft + middleWidth,
|
||||
sliceTop,
|
||||
sliceRight,
|
||||
tileHeightSize);
|
||||
}
|
||||
// border left
|
||||
addQuad(tessellator, xOffset, y, 0, sliceTop, sliceLeft, tileHeightSize);
|
||||
// border right
|
||||
addQuad(
|
||||
tessellator,
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
y,
|
||||
sliceLeft + middleWidth,
|
||||
sliceTop,
|
||||
sliceRight,
|
||||
tileHeightSize);
|
||||
}
|
||||
|
||||
// angle left-top
|
||||
if (sliceLeft > 0 && sliceTop > 0) {
|
||||
drawModalRectWithCustomSizedTexture(xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
|
||||
}
|
||||
|
||||
addQuad(tessellator, xOffset, yOffset, 0, 0, sliceLeft, sliceTop);
|
||||
// angle right-top
|
||||
if (sliceRight > 0 && sliceTop > 0) {
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
yOffset,
|
||||
sliceLeft + middleWidth,
|
||||
0,
|
||||
sliceRight,
|
||||
sliceTop);
|
||||
}
|
||||
|
||||
addQuad(
|
||||
tessellator,
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
yOffset,
|
||||
sliceLeft + middleWidth,
|
||||
0,
|
||||
sliceRight,
|
||||
sliceTop);
|
||||
// angle left-bottom
|
||||
if (sliceLeft > 0 && sliceBottom > 0) {
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
0,
|
||||
sliceTop + middleHeight,
|
||||
sliceLeft,
|
||||
sliceBottom);
|
||||
}
|
||||
|
||||
addQuad(
|
||||
tessellator,
|
||||
xOffset,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
0,
|
||||
sliceTop + middleHeight,
|
||||
sliceLeft,
|
||||
sliceBottom);
|
||||
// angle right-bottom
|
||||
if (sliceRight > 0 && sliceBottom > 0) {
|
||||
drawModalRectWithCustomSizedTexture(
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
sliceLeft + middleWidth,
|
||||
sliceTop + middleHeight,
|
||||
sliceRight,
|
||||
sliceBottom);
|
||||
}
|
||||
addQuad(
|
||||
tessellator,
|
||||
xOffset + sliceLeft + tileWidth,
|
||||
yOffset + sliceTop + tileHeight,
|
||||
sliceLeft + middleWidth,
|
||||
sliceTop + middleHeight,
|
||||
sliceRight,
|
||||
sliceBottom);
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawModalRectWithCustomSizedTexture(int xOffset, int yOffset, int u, int v, int width, int height) {
|
||||
Gui.func_146110_a(
|
||||
xOffset + this.paddingLeft,
|
||||
yOffset + this.paddingTop,
|
||||
this.x + u,
|
||||
this.y + v,
|
||||
width,
|
||||
height,
|
||||
this.textureWidth,
|
||||
this.textureHeight);
|
||||
private void addQuad(Tessellator tessellator, int xOffset, int yOffset, int u, int v, int width, int height) {
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
final float scaleU = 1.0F / this.textureWidth;
|
||||
final float scaleV = 1.0F / this.textureHeight;
|
||||
final int x = xOffset + this.paddingLeft;
|
||||
final int y = yOffset + this.paddingTop;
|
||||
final float u0 = (this.x + u) * scaleU;
|
||||
final float u1 = (this.x + u + width) * scaleU;
|
||||
final float v0 = (this.y + v) * scaleV;
|
||||
final float v1 = (this.y + v + height) * scaleV;
|
||||
|
||||
tessellator.addVertexWithUV(x, y + height, 0.0D, u0, v1);
|
||||
tessellator.addVertexWithUV(x + width, y + height, 0.0D, u1, v1);
|
||||
tessellator.addVertexWithUV(x + width, y, 0.0D, u1, v0);
|
||||
tessellator.addVertexWithUV(x, y, 0.0D, u0, v0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -449,6 +449,7 @@ public class GuiContainerManager {
|
||||
}
|
||||
|
||||
private int clickHandled = 0;
|
||||
private int hotkeyHandled = 0;
|
||||
public final List<IContainerTooltipHandler> instanceTooltipHandlers;
|
||||
|
||||
public GuiContainerManager(GuiContainer screen) {
|
||||
@@ -483,6 +484,7 @@ public class GuiContainerManager {
|
||||
|
||||
public void load() {
|
||||
clickHandled = 0;
|
||||
hotkeyHandled = 0;
|
||||
for (IContainerObjectHandler objectHandler : objectHandlers) objectHandler.load(window);
|
||||
}
|
||||
|
||||
@@ -502,6 +504,46 @@ public class GuiContainerManager {
|
||||
for (IContainerInputHandler inputhander : inputHandlers)
|
||||
if (inputhander.lastKeyTyped(window, keyChar, keyID)) return true;
|
||||
|
||||
return onHotkeyPressed();
|
||||
}
|
||||
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (IContainerInputHandler inputhander : inputHandlers) {
|
||||
if (inputhander.onHotkeyPressed(window)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onScreenHotkeyPressed();
|
||||
}
|
||||
|
||||
public boolean handleMouseHotkey() {
|
||||
final int button = Mouse.getEventButton();
|
||||
|
||||
if (button < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Mouse.getEventButtonState()) {
|
||||
// the press has been consumed, so the release must not reach the screen either
|
||||
if ((hotkeyHandled & 1 << button) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hotkeyHandled &= ~(1 << button);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (KeyManager.handleMouseHotkey(button, this::onHotkeyPressed)) {
|
||||
hotkeyHandled |= 1 << button;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,18 @@ public interface IContainerInputHandler {
|
||||
*/
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID);
|
||||
|
||||
/**
|
||||
* Called when a key or a mouse button is pressed and the event has not been handled by the first pass. Use this for
|
||||
* key bindings that work globally and check them with {@link codechicken.nei.KeyManager#isKeyDown}, so they work
|
||||
* the same for keyboard keys and mouse buttons.
|
||||
*
|
||||
* @param gui An instance of the currentscreen
|
||||
* @return true to terminate further processing of this event.
|
||||
*/
|
||||
default boolean onHotkeyPressed(GuiContainer gui) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse is clicked in the gui
|
||||
*
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package codechicken.nei.guihook;
|
||||
|
||||
/**
|
||||
* Interface for GuiContainers to receive NEI compatible hotkey events from both keyboard keys and mouse buttons
|
||||
*/
|
||||
public interface IGuiHotkeyListener {
|
||||
|
||||
/**
|
||||
* Called before the input handlers when a key or a mouse button is pressed, check key bindings with
|
||||
* {@link codechicken.nei.KeyManager#isKeyDown}
|
||||
*
|
||||
* @return true to terminate further processing of this event.
|
||||
*/
|
||||
default boolean onHotkeyPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the input handlers for screen-wide hotkeys that do not depend on the hovered object, so they never
|
||||
* take priority over more specific actions
|
||||
*
|
||||
* @return true to terminate further processing of this event.
|
||||
*/
|
||||
default boolean onScreenHotkeyPressed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,14 @@ package codechicken.nei.item;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.client.IItemRenderer;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.item.FluidDrawer.FillDirection;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
@@ -34,37 +33,13 @@ public class FluidDisplayRenderer implements IItemRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
renderIcon(item, fluidDisplay);
|
||||
renderIcon(item);
|
||||
renderAmountOverlay(item, fluidDisplay);
|
||||
}
|
||||
|
||||
private void renderIcon(ItemStack item, ItemFluidDisplay fluidDisplay) {
|
||||
final IIcon icon = fluidDisplay.getIconIndex(item);
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int color = fluidDisplay.getColorFromItemStack(item, 0);
|
||||
final float red = (color >> 16 & 0xFF) / 255F;
|
||||
final float green = (color >> 8 & 0xFF) / 255F;
|
||||
final float blue = (color & 0xFF) / 255F;
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
|
||||
GL11.glColor4f(red, green, blue, 1F);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
OpenGlHelper
|
||||
.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertexWithUV(0, 16, 0, icon.getMinU(), icon.getMaxV());
|
||||
tessellator.addVertexWithUV(16, 16, 0, icon.getMaxU(), icon.getMaxV());
|
||||
tessellator.addVertexWithUV(16, 0, 0, icon.getMaxU(), icon.getMinV());
|
||||
tessellator.addVertexWithUV(0, 0, 0, icon.getMinU(), icon.getMinV());
|
||||
tessellator.draw();
|
||||
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
private void renderIcon(ItemStack item) {
|
||||
final Fluid fluid = FluidRegistry.getFluid(item.getItemDamage());
|
||||
FluidDrawer.drawFluidArea(fluid != null ? fluid : FluidRegistry.WATER, 0, 0, 16, 16, false, FillDirection.UP);
|
||||
}
|
||||
|
||||
private void renderAmountOverlay(ItemStack item, ItemFluidDisplay fluidDisplay) {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
package codechicken.nei.item;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
import net.minecraftforge.fluids.FluidRegistry;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.api.IFluidRenderer;
|
||||
import codechicken.nei.scroll.GuiHelper;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -27,28 +34,32 @@ public class FluidDrawer {
|
||||
return this == LEFT || this == RIGHT;
|
||||
}
|
||||
|
||||
public FillDirection resolve(FluidStack fluidStack) {
|
||||
public FillDirection resolve(Fluid fluid) {
|
||||
if (this != AUTO) return this;
|
||||
return fluidStack.getFluid().isGaseous(fluidStack) ? DOWN : UP;
|
||||
return fluid.isGaseous() ? DOWN : UP;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Fluid, IFluidRenderer> fluidRenderers = new IdentityHashMap<>();
|
||||
|
||||
private FluidDrawer() {}
|
||||
|
||||
public static void drawFluid(int x, int y, int width, int height, int capacity, FluidStack fluidStack,
|
||||
boolean flowing, FillDirection direction) {
|
||||
final Fluid fluid = fluidStack.getFluid();
|
||||
IIcon icon = flowing ? fluid.getFlowingIcon() : null;
|
||||
|
||||
if (icon == null) {
|
||||
icon = fluid.getIcon(fluidStack);
|
||||
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
|
||||
if (renderer == null) {
|
||||
fluidRenderers.remove(fluid);
|
||||
} else {
|
||||
fluidRenderers.put(fluid, renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (icon == null || fluidStack.amount <= 0) {
|
||||
public static void drawTank(int x, int y, int width, int height, int capacity, FluidStack fluidStack,
|
||||
boolean flowing, FillDirection direction) {
|
||||
if (fluidStack.amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
direction = direction.resolve(fluidStack);
|
||||
final Fluid fluid = fluidStack.getFluid();
|
||||
direction = direction.resolve(fluid);
|
||||
|
||||
final int tankSize = direction.isHorizontal() ? width : height;
|
||||
final int tankCapacity = capacity > 0 ? capacity : fluidStack.amount;
|
||||
@@ -59,38 +70,109 @@ public class FluidDrawer {
|
||||
final int fillHeight = direction.isHorizontal() ? height : Math.min(fillSize, height);
|
||||
final int fillX = direction == FillDirection.LEFT ? x + width - fillWidth : x;
|
||||
final int fillY = direction == FillDirection.DOWN ? y : y + height - fillHeight;
|
||||
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
drawFluidArea(fluid, fillX, fillY, fillWidth, fillHeight, flowing, direction);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the area with fluid tiles. The tiles start from the side the fluid fills from, so a partial tile ends up at
|
||||
* the fluid surface.
|
||||
*/
|
||||
public static void drawFluidArea(Fluid fluid, int x, int y, int width, int height, boolean flowing,
|
||||
FillDirection direction) {
|
||||
direction = direction.resolve(fluid);
|
||||
final boolean anchorRight = direction == FillDirection.LEFT;
|
||||
final boolean anchorBottom = direction != FillDirection.DOWN;
|
||||
final IFluidRenderer renderer = fluidRenderers.get(fluid);
|
||||
|
||||
final int color = fluid.getColor(fluidStack);
|
||||
if (renderer == null) {
|
||||
drawDefaultFluid(fluid, x, y, width, height, flowing, anchorRight, anchorBottom);
|
||||
} else {
|
||||
drawCustomFluid(renderer, fluid, x, y, width, height, anchorRight, anchorBottom);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawCustomFluid(IFluidRenderer renderer, Fluid fluid, int x, int y, int width, int height,
|
||||
boolean anchorRight, boolean anchorBottom) {
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
final boolean clip = width % 16 != 0 || height % 16 != 0;
|
||||
|
||||
if (clip) {
|
||||
GuiHelper.pushScissorFrame(x, y, width, height);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int tx = 0; tx < width; tx += 16) {
|
||||
final int tileX = anchorRight ? x + width - tx - 16 : x + tx;
|
||||
|
||||
for (int ty = 0; ty < height; ty += 16) {
|
||||
final int tileY = anchorBottom ? y + height - ty - 16 : y + ty;
|
||||
renderer.renderFluid(fluid, tileX, tileY);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (clip) {
|
||||
GuiHelper.popScissorFrame();
|
||||
}
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawDefaultFluid(Fluid fluid, int x, int y, int width, int height, boolean flowing,
|
||||
boolean anchorRight, boolean anchorBottom) {
|
||||
IIcon icon = flowing ? fluid.getFlowingIcon() : null;
|
||||
|
||||
if (icon == null) {
|
||||
icon = fluid.getStillIcon();
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
icon = FluidRegistry.WATER.getStillIcon();
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int color = fluid.getColor();
|
||||
final float red = (color >> 16 & 0xFF) / 255F;
|
||||
final float green = (color >> 8 & 0xFF) / 255F;
|
||||
final float blue = (color & 0xFF) / 255F;
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
|
||||
GL11.glColor3f(red, green, blue);
|
||||
GL11.glColor4f(red, green, blue, 1F);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
OpenGlHelper
|
||||
.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
final double iconMinU = icon.getMinU();
|
||||
final double iconMaxU = icon.getMaxU();
|
||||
final double iconMinV = icon.getMinV();
|
||||
final double iconMaxV = icon.getMaxV();
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
for (int tx = 0; tx < fillWidth; tx += 16) {
|
||||
final int tileWidth = Math.min(16, fillWidth - tx);
|
||||
for (int tx = 0; tx < width; tx += 16) {
|
||||
final int tileWidth = Math.min(16, width - tx);
|
||||
// a cropped tile keeps the texture edge that stays seamless against the neighbouring tile
|
||||
final double uSize = (icon.getMaxU() - icon.getMinU()) * tileWidth / 16D;
|
||||
final double uMin = anchorRight ? icon.getMaxU() - uSize : icon.getMinU();
|
||||
final double uMax = anchorRight ? icon.getMaxU() : icon.getMinU() + uSize;
|
||||
final int leftX = anchorRight ? fillX + fillWidth - tx - tileWidth : fillX + tx;
|
||||
final double uSize = (iconMaxU - iconMinU) * tileWidth / 16D;
|
||||
final double uMin = anchorRight ? iconMaxU - uSize : iconMinU;
|
||||
final double uMax = anchorRight ? iconMaxU : iconMinU + uSize;
|
||||
final int leftX = anchorRight ? x + width - tx - tileWidth : x + tx;
|
||||
final int rightX = leftX + tileWidth;
|
||||
|
||||
for (int ty = 0; ty < fillHeight; ty += 16) {
|
||||
final int tileHeight = Math.min(16, fillHeight - ty);
|
||||
final double vSize = (icon.getMaxV() - icon.getMinV()) * tileHeight / 16D;
|
||||
final double vMin = anchorBottom ? icon.getMaxV() - vSize : icon.getMinV();
|
||||
final double vMax = anchorBottom ? icon.getMaxV() : icon.getMinV() + vSize;
|
||||
final int bottomY = anchorBottom ? fillY + fillHeight - ty : fillY + ty + tileHeight;
|
||||
for (int ty = 0; ty < height; ty += 16) {
|
||||
final int tileHeight = Math.min(16, height - ty);
|
||||
final double vSize = (iconMaxV - iconMinV) * tileHeight / 16D;
|
||||
final double vMin = anchorBottom ? iconMaxV - vSize : iconMinV;
|
||||
final double vMax = anchorBottom ? iconMaxV : iconMinV + vSize;
|
||||
final int bottomY = anchorBottom ? y + height - ty : y + ty + tileHeight;
|
||||
final int topY = bottomY - tileHeight;
|
||||
|
||||
tessellator.addVertexWithUV(leftX, bottomY, 0, uMin, vMax);
|
||||
@@ -102,7 +184,6 @@ public class FluidDrawer {
|
||||
tessellator.draw();
|
||||
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class ItemFluidDisplay extends Item implements IFluidContainerItem {
|
||||
@Override
|
||||
public FluidStack getFluid(ItemStack stack) {
|
||||
|
||||
if (stack == null || !(stack.getItem() instanceof ItemFluidDisplay) || !stack.hasTagCompound()) {
|
||||
if (stack == null || !(stack.getItem() instanceof ItemFluidDisplay)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class ItemFluidDisplay extends Item implements IFluidContainerItem {
|
||||
final NBTTagCompound nbTag = stack.getTagCompound();
|
||||
final FluidStack fluidStack = new FluidStack(
|
||||
fluid,
|
||||
(int) Math.min(nbTag.getLong("neiFluidDisplayAmount"), Integer.MAX_VALUE));
|
||||
(int) Math.min(nbTag != null ? nbTag.getLong("neiFluidDisplayAmount") : 0, Integer.MAX_VALUE));
|
||||
|
||||
return fluidStack;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,18 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.inventory.SlotCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.FastTransferManager;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIServerUtils;
|
||||
import codechicken.nei.RestartableTask;
|
||||
import codechicken.nei.ServerSync;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.chain.RecipeChainIterator;
|
||||
@@ -26,6 +31,7 @@ public class AutoCraftingManager {
|
||||
@Override
|
||||
public void execute() {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final ItemStack[] layout = snapshotLayout(guiContainer);
|
||||
final List<BookmarkItem> initialItems = prepareInitialItems(math, getInventoryItems(guiContainer));
|
||||
boolean processed = false;
|
||||
boolean changed = false;
|
||||
@@ -69,6 +75,11 @@ public class AutoCraftingManager {
|
||||
|
||||
} while (changed && !interrupted(guiContainer));
|
||||
|
||||
if (!interrupted(guiContainer)) {
|
||||
restoreLayout(guiContainer, layout);
|
||||
ServerSync.request();
|
||||
}
|
||||
|
||||
} finally {
|
||||
StackInfo.pauseItemDamageSound(false);
|
||||
}
|
||||
@@ -134,4 +145,70 @@ public class AutoCraftingManager {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
private static ItemStack[] snapshotLayout(GuiContainer guiContainer) {
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final ItemStack[] layout = new ItemStack[slots.size()];
|
||||
|
||||
for (int index = 0; index < slots.size(); index++) {
|
||||
final Slot slot = slots.get(index);
|
||||
|
||||
if (slot.getHasStack() && !(slot instanceof SlotCrafting)) {
|
||||
layout[index] = slot.getStack().copy();
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static void restoreLayout(GuiContainer guiContainer, ItemStack[] layout) {
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final EntityPlayer player = guiContainer.mc.thePlayer;
|
||||
|
||||
for (int index = 0; index < slots.size() && player.inventory.getItemStack() == null
|
||||
&& guiContainer.mc.currentScreen == guiContainer; index++) {
|
||||
final ItemStack expected = layout[index];
|
||||
final Slot target = slots.get(index);
|
||||
|
||||
if (expected == null || inPlace(slots, layout, index)
|
||||
|| !target.isItemValid(expected)
|
||||
|| target.getHasStack() && !target.canTakeStack(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Slot source = findMisplaced(slots, layout, expected, player);
|
||||
if (source == null) continue;
|
||||
|
||||
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
|
||||
FastTransferManager.clickSlot(guiContainer, target.slotNumber);
|
||||
|
||||
if (player.inventory.getItemStack() != null) {
|
||||
FastTransferManager.clickSlot(guiContainer, source.slotNumber);
|
||||
}
|
||||
}
|
||||
|
||||
FastTransferManager.dropHeldItem(guiContainer);
|
||||
}
|
||||
|
||||
private static boolean inPlace(List<Slot> slots, ItemStack[] layout, int index) {
|
||||
final ItemStack stack = slots.get(index).getStack();
|
||||
return stack != null && layout[index] != null
|
||||
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(layout[index], stack);
|
||||
}
|
||||
|
||||
private static Slot findMisplaced(List<Slot> slots, ItemStack[] layout, ItemStack expected, EntityPlayer player) {
|
||||
|
||||
for (int index = 0; index < slots.size(); index++) {
|
||||
final Slot slot = slots.get(index);
|
||||
|
||||
if (slot.getHasStack() && !(slot instanceof SlotCrafting)
|
||||
&& !inPlace(slots, layout, index)
|
||||
&& slot.canTakeStack(player)
|
||||
&& NEIServerUtils.areStacksSameTypeCraftingWithNBT(expected, slot.getStack())) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -20,10 +21,13 @@ import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.nei.FastTransferManager;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.ServerSync;
|
||||
import codechicken.nei.api.IOverlayHandler;
|
||||
|
||||
public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
|
||||
private static final long SYNC_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
public static class DistributedIngred {
|
||||
|
||||
public DistributedIngred(ItemStack item) {
|
||||
@@ -113,7 +117,8 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
|
||||
boolean craft = false;
|
||||
while (multiplier > 0) {
|
||||
while (multiplier > 0 && firstGui.mc.currentScreen == firstGui
|
||||
&& presenceOverlay(firstGui, handler, recipeIndex).stream().allMatch(s -> s.isPresent())) {
|
||||
final int transfer = transferRecipe(firstGui, handler, recipeIndex, multiplier);
|
||||
|
||||
if (transfer <= 0) {
|
||||
@@ -122,6 +127,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
|
||||
multiplier -= transfer;
|
||||
|
||||
if (!craftingSlot.getHasStack() && !awaitServer(firstGui)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (craftingSlot.getHasStack() && craftingSlot.canTakeStack(firstGui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(firstGui, craftingSlot.slotNumber, 0, 1);
|
||||
|
||||
@@ -139,9 +148,30 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return craft;
|
||||
}
|
||||
|
||||
private boolean awaitServer(GuiContainer gui) {
|
||||
final long deadline = System.currentTimeMillis() + SYNC_TIMEOUT_MILLIS;
|
||||
ServerSync.request();
|
||||
|
||||
while (ServerSync.pending()) {
|
||||
if (gui.mc.currentScreen != gui || System.currentTimeMillis() > deadline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean clearIngredients(GuiContainer gui, IRecipeHandler handler) {
|
||||
|
||||
for (Slot slot : getCraftMatrixSlots(gui, handler)) {
|
||||
if (gui.mc.currentScreen != gui) return false;
|
||||
|
||||
if (slot.canTakeStack(gui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
|
||||
if (slot.getHasStack()) return false;
|
||||
@@ -206,7 +236,9 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
|
||||
final Slot craftingSlot = getCraftingResultSlot(gui);
|
||||
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) {
|
||||
for (Slot slot : getSourceSlots(gui)) {
|
||||
if (gui.mc.currentScreen != gui) return;
|
||||
|
||||
if (slot == craftingSlot || !slot.getHasStack()
|
||||
|| !canMoveFrom(slot, gui)
|
||||
|| !slot.canTakeStack(gui.mc.thePlayer))
|
||||
@@ -240,6 +272,10 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
FastTransferManager.clickSlot(gui, slot.slotNumber);
|
||||
}
|
||||
|
||||
// A right click with an empty or foreign cursor would take half of the dest stack
|
||||
final ItemStack held = gui.mc.thePlayer.inventory.getItemStack();
|
||||
if (held == null || !canStack(distrib.permutation, held)) break;
|
||||
|
||||
FastTransferManager.clickSlot(gui, dest.slotNumber, 1);
|
||||
stackSize--;
|
||||
}
|
||||
@@ -370,7 +406,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
|
||||
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
|
||||
for (Slot slot : getSourceSlots(gui)) /* work out how much we have to go round */ {
|
||||
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
|
||||
final ItemStack pstack = slot.getStack();
|
||||
final DistributedIngred istack = findIngred(ingredStacks, pstack);
|
||||
@@ -408,6 +444,25 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Slot> getSourceSlots(GuiContainer gui) {
|
||||
final List<Slot> slots = new ArrayList<>(gui.inventorySlots.inventorySlots);
|
||||
slots.sort(Comparator.comparingLong(slot -> getRemainingDurability(slot.getStack())));
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static long getRemainingDurability(ItemStack stack) {
|
||||
if (stack == null || stack.getMaxStackSize() != 1) return Long.MAX_VALUE;
|
||||
|
||||
final NBTTagCompound tagCompound = stack.getTagCompound();
|
||||
|
||||
if (tagCompound != null && tagCompound.hasKey("GT.ToolStats")) {
|
||||
final NBTTagCompound toolStats = tagCompound.getCompoundTag("GT.ToolStats");
|
||||
return toolStats.getLong("MaxDamage") - toolStats.getLong("Damage");
|
||||
}
|
||||
|
||||
return stack.isItemStackDamageable() ? stack.getMaxDamage() - stack.getItemDamage() : Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
private int getGTToolDamagePerContainerCraft(ItemStack aStack) {
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -13,14 +12,9 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.bookmark.BookmarksGridSlot;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
|
||||
@@ -138,39 +132,8 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
|
||||
}
|
||||
|
||||
public static RecipeId getRecipeId(GuiScreen gui, ItemStack stackover) {
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return focusedRecipe.getRecipeId();
|
||||
}
|
||||
}
|
||||
|
||||
final Point mouseover = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
|
||||
if (panelSlot != null) {
|
||||
return panelSlot.getType() == BookmarkItemType.INGREDIENT || panelSlot.getRecipeId() == null
|
||||
? FavoriteRecipes.getFavorite(stackover)
|
||||
: panelSlot.getRecipeId();
|
||||
}
|
||||
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
return itemSlot.getRecipeId();
|
||||
}
|
||||
|
||||
return null;
|
||||
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
|
||||
return hovered != null ? hovered.recipeId : null;
|
||||
}
|
||||
|
||||
private GuiCraftingRecipe(ArrayList<ICraftingHandler> handlers) {
|
||||
|
||||
@@ -122,18 +122,25 @@ public class GuiFavoriteButton extends GuiRecipeButton {
|
||||
return Recipe.of(this.handlerRef);
|
||||
}
|
||||
|
||||
public void lastKeyTyped(char keyChar, int keyID) {
|
||||
@Override
|
||||
public void lastKeyTyped(char keyChar, int keyID) {}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
|
||||
saveRecipeInBookmark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (KeyManager.isKeyDown("bookmark.open_crafting_tree") && NEIClientUtils.shiftKey()
|
||||
&& saveRecipeInBookmark()) {
|
||||
final int groupId = ItemPanels.bookmarkPanel.getGrid().getNextGroupId() - 1;
|
||||
GuiCraftingTree.openCraftingTreeGui(ItemPanels.bookmarkPanel.getGrid(), groupId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -177,12 +177,20 @@ public class GuiOverlayButton extends GuiRecipeButton {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(
|
||||
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH,
|
||||
NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
|
||||
}
|
||||
|
||||
} else if (canFillCraftingGrid()) {
|
||||
hotkeys.put(
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("recipe.overlay.fillCraftingGrid"));
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("recipe.overlay.fillCraftingGridOnce"));
|
||||
}
|
||||
|
||||
hotkeys.put(
|
||||
@@ -263,7 +271,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
this.missedMaterialsTooltipLineHandler = ItemsTooltipLineHandler
|
||||
.grid(NEIClientUtils.translate("recipe.overlay.missing"), items, Integer.MAX_VALUE);
|
||||
.of(NEIClientUtils.translate("recipe.overlay.missing"), items, Integer.MAX_VALUE, 0);
|
||||
} else {
|
||||
this.missedMaterialsTooltipLineHandler = null;
|
||||
}
|
||||
@@ -329,7 +337,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
|
||||
|
||||
if (!requireShiftForOverlayRecipe() || shift) {
|
||||
if (this.canFillCraftingGrid) {
|
||||
this.handlerRef.fillCraftingGrid(this.firstGui, 0);
|
||||
this.handlerRef.fillCraftingGrid(this.firstGui, NEIClientUtils.controlKey() ? 1 : 0);
|
||||
}
|
||||
} else if (hasOverlay() && useOverlayRenderer()) {
|
||||
this.handlerRef.useOverlayRenderer(this.firstGui);
|
||||
@@ -338,14 +346,21 @@ public class GuiOverlayButton extends GuiRecipeButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(char keyChar, int keyID) {
|
||||
public void lastKeyTyped(char keyChar, int keyID) {}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
|
||||
saveRecipeInBookmark();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey() && NEIClientUtils.shiftKey()) {
|
||||
sendRecipeChatLink();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void saveRecipeInBookmark() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -51,6 +51,7 @@ import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.guihook.IGuiClientSide;
|
||||
import codechicken.nei.guihook.IGuiHandleMouseWheel;
|
||||
import codechicken.nei.guihook.IGuiHotkeyListener;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.ScrollBar;
|
||||
@@ -62,7 +63,7 @@ import codechicken.nei.util.EmptyContainer;
|
||||
import codechicken.nei.util.SlotInaccessible;
|
||||
|
||||
public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
|
||||
IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler {
|
||||
IGuiClientSide, IGuiHandleMouseWheel, IGuiHotkeyListener, IContainerTooltipHandler, INEIGuiHandler {
|
||||
|
||||
private static final int BORDER_PADDING = 5;
|
||||
private static final int TRANSPARENCY_BORDER = 4;
|
||||
@@ -71,15 +72,23 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
0,
|
||||
0,
|
||||
176 + TRANSPARENCY_BORDER * 2,
|
||||
166 + TRANSPARENCY_BORDER * 2).build();
|
||||
166 + TRANSPARENCY_BORDER * 2)
|
||||
.setSlices(
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER)
|
||||
.build();
|
||||
|
||||
private static final int BUTTON_WIDTH = 13;
|
||||
private static final int BUTTON_HEIGHT = 12;
|
||||
|
||||
protected static final ScrollBar VERTICAL_SCROLLBAR = new ScrollBar().setTrackWidth(14)
|
||||
.setOverflowType(OverflowType.AUTO).setScrollPlace(ScrollPlace.END)
|
||||
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).build(), 9, 9)
|
||||
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
|
||||
.setTrackTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 42, 90, 13, 21).setSlices(0, 0, 9, 9).build())
|
||||
.setThumbTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).setSlices(0, 0, 3, 2).build())
|
||||
.setThumbPadding(1, 6, 0, 6);
|
||||
|
||||
public static final List<IRecipeFilterProvider> recipeFilterers = new LinkedList<>();
|
||||
@@ -295,73 +304,118 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
}
|
||||
|
||||
/**
|
||||
* Many old mods assumed a fixed NEI window height of {@code 166} pixels. Now that this is no longer the case, their
|
||||
* tooltip and click zone handling is broken. This helper class fixes these old mods by hacking the {@link #height}
|
||||
* property's value so that these old mods' calculations return the correct value for the new height.
|
||||
* Many old mods assumed a fixed NEI window size of {@code 176x166} pixels, with exactly one recipe shown at a time,
|
||||
* flush against the top-left corner of the window. Now that none of these assumptions hold (the window has a
|
||||
* flexible size, several recipes can be stacked in the same scrollable list, and the recipe area is inset from the
|
||||
* window's edges), these old mods' tooltip and click zone handling is broken. This helper class fixes these old
|
||||
* mods by hacking the {@link #width}, {@link #height}, {@link #guiLeft} and {@link #guiTop} properties' values so
|
||||
* that these old mods' calculations return the correct value for whichever recipe they are currently being asked
|
||||
* about.
|
||||
*
|
||||
* <p>
|
||||
* New and old mods often assume that the handler is only drawn when {@link GuiRecipe} is the currently active
|
||||
* screen in {@link net.minecraft.client.Minecraft}. This is no longer true since the addition of recipe tooltips
|
||||
* which need to render the recipe in other containers on mouse hover, this class temporarily switches the active
|
||||
* screen to {@code this} while rendering them to avoid ClassCastExceptions and other similar crashes.
|
||||
* Since each recipe handled by a {@link RecipeWidget} can sit at a different real position, this must be
|
||||
* constructed with that widget's top-left edge every time a legacy entry point ({@code mouseClicked},
|
||||
* {@code handleTooltip}, ...) is invoked for it, rather than once per {@link GuiRecipe}.
|
||||
*
|
||||
* <p>
|
||||
* This class is an {@link AutoCloseable} so that it can be used with try-with-resources, which will ensure that
|
||||
* {@link #height} and {@link net.minecraft.client.Minecraft#currentScreen} is returned to the correct value
|
||||
* {@link #width}, {@link #height}, {@link #guiLeft} and {@link #guiTop} are returned to the correct value
|
||||
* afterwards.
|
||||
*/
|
||||
private class CompatibilityHacks implements AutoCloseable {
|
||||
public static final class CompatibilityHacks implements AutoCloseable {
|
||||
|
||||
// The historical NEI recipe screen's fixed size: a single top paging widget 16px tall, no horizontal inset,
|
||||
// and a 176x166 box. Legacy handlers either read guiLeft/guiTop directly (expecting them to point 16px above
|
||||
// their recipe, flush on the left) or recompute them from width/height with these same two constants.
|
||||
private static final int LEGACY_TOP_GAP = 16;
|
||||
private static final int LEGACY_BOX_WIDTH = 176;
|
||||
private static final int LEGACY_BOX_HEIGHT = 166;
|
||||
|
||||
// layoutWindow() places the recipe container at (guiLeft + 3, guiTop + 32); RecipePageManager then places
|
||||
// each left-aligned recipe widget another 2px to the right of that. This is the inverse of both, undoing the
|
||||
// container's own inset so a legacy handler sees its recipe flush with guiLeft as it expects.
|
||||
private static final int CONTAINER_LEFT_INSET = 3 + 2;
|
||||
|
||||
private final GuiRecipe<?> gui;
|
||||
private final int trueWidth;
|
||||
private final int trueHeight;
|
||||
private final int trueGuiLeft;
|
||||
private final int trueGuiTop;
|
||||
|
||||
private CompatibilityHacks() {
|
||||
trueHeight = height;
|
||||
trueGuiTop = guiTop;
|
||||
/**
|
||||
* @param widget The recipe widget a legacy entry point ({@code mouseClicked}, {@code handleTooltip}, ...) is
|
||||
* about to be invoked for.
|
||||
*/
|
||||
public CompatibilityHacks(GuiRecipe<?> gui, RecipeWidget widget) {
|
||||
this.gui = gui;
|
||||
this.trueWidth = gui.width;
|
||||
this.trueHeight = gui.height;
|
||||
this.trueGuiLeft = gui.guiLeft;
|
||||
this.trueGuiTop = gui.guiTop;
|
||||
|
||||
isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
|
||||
.map(pattern -> pattern.matcher(handler.original.getHandlerId())).anyMatch(Matcher::matches);
|
||||
if (isHeightHackApplied) {
|
||||
// guiTop is the top edge of the recipe screen on the y-axis. Old recipe
|
||||
// handlers expect a single paging
|
||||
// widget at the top and at the bottom of the recipe screen with a height of
|
||||
// 16px each, but GTNH NEI
|
||||
// moved the bottom paging widget to the top following JEI's design, increasing
|
||||
// the "forbidden" zone at
|
||||
// the top to 32px. To fix these old recipe handlers we move guiTop down 16px so
|
||||
// that they can keep
|
||||
// working with the old 16px gap.
|
||||
guiTop += 16;
|
||||
final String handlerId = gui.handler.original.getHandlerId();
|
||||
gui.isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream()
|
||||
.anyMatch(pattern -> pattern.matcher(handlerId).matches());
|
||||
|
||||
// The old NEI recipe screen had a fixed width and height and was always
|
||||
// centered on the screen. Legacy
|
||||
// recipe handlers use the calculation ((height - 166) / 2) to compute the
|
||||
// y-value of the top edge of
|
||||
// the NEI window. GTNH NEI changes the layout so that the recipe screen length
|
||||
// now has a flexible
|
||||
// length and is no longer centered vertically. In order for the top-of-screen
|
||||
// calculation to return the
|
||||
// correct result, we have to hack the height field with an inverse of the
|
||||
// calculation using the actual
|
||||
// top of the recipe screen stored in guiTop.
|
||||
height = (2 * guiTop) + 166;
|
||||
|
||||
// For reference, in case we ever need to modify width as well:
|
||||
// the legacy calculation used for width is ((width - 176) / 2), which should
|
||||
// evaluate to be equal to
|
||||
// guiWidth (the x-value of the left edge of the NEI recipe screen). So if we
|
||||
// wanted to override width
|
||||
// as well, we'd do this:
|
||||
// width = (2 * guiWidth) + 176;
|
||||
if (gui.isHeightHackApplied) {
|
||||
gui.guiTop = recipeTop(widget) - LEGACY_TOP_GAP;
|
||||
gui.guiLeft = widget.x - CONTAINER_LEFT_INSET;
|
||||
gui.height = (2 * gui.guiTop) + LEGACY_BOX_HEIGHT;
|
||||
gui.width = (2 * gui.guiLeft) + LEGACY_BOX_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
// recipiesPerPage() == 2 is the IRecipeHandler default: a handler only ends up with this value if it never
|
||||
// overrode the method, or if it overrode it and chose 2 on purpose. We only care about the latter case, so
|
||||
// we require the override to actually come from the handler's own class rather than being inherited as-is.
|
||||
private static final Map<Class<?>, Boolean> overridesRecipiesPerPage = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* The real top edge of this widget's recipe, as a legacy handler following the {@link #LEGACY_TOP_GAP}
|
||||
* convention would expect to find it at guiTop. A few handlers (e.g. ImmersiveEngineering) are left over from
|
||||
* the old {@code recipiesPerPage() == 2} convention, where two recipes shared one physical page: their own
|
||||
* {@code mouseClicked}/{@code handleTooltip} still add a hardcoded "recipe height * (recipe % 2)" on top of
|
||||
* guiTop to find the odd one of the pair. Since every recipe is its own independent widget now, we pre-subtract
|
||||
* that same offset here so their own addition cancels out and lands back on this widget's real top.
|
||||
*
|
||||
* <p>
|
||||
* This only applies when the handler itself overrides {@link IRecipeHandler#recipiesPerPage()} to return
|
||||
* {@code 2}: that method defaults to {@code 2} on the interface, so most height-hacked handlers return it
|
||||
* without ever having implemented this pairing (e.g. BuildCraftCompat never overrides it at all, and only reads
|
||||
* it elsewhere to pick a cosmetic sub-name), and applying this compensation to them would break them. A handler
|
||||
* that went out of its way to declare {@code return 2;} itself is making a deliberate statement about this
|
||||
* legacy convention that a handler which simply never mentions the method isn't.
|
||||
*/
|
||||
private static int recipeTop(RecipeWidget widget) {
|
||||
final RecipeHandlerRef ref = widget.getRecipeHandlerRef();
|
||||
final HandlerInfo handlerInfo = widget.getHandlerInfo();
|
||||
final int top = widget.y + handlerInfo.getYShift();
|
||||
|
||||
if (ref.handler.recipiesPerPage() == 2 && declaresRecipiesPerPage(ref.handler)) {
|
||||
return top - handlerInfo.getHeight() * (ref.recipeIndex % 2);
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
private static boolean declaresRecipiesPerPage(IRecipeHandler handler) {
|
||||
return overridesRecipiesPerPage.computeIfAbsent(handler.getClass(), clazz -> {
|
||||
try {
|
||||
return clazz.getMethod("recipiesPerPage").getDeclaringClass() != IRecipeHandler.class;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
guiTop = trueGuiTop;
|
||||
height = trueHeight;
|
||||
this.gui.guiLeft = this.trueGuiLeft;
|
||||
this.gui.guiTop = this.trueGuiTop;
|
||||
this.gui.width = this.trueWidth;
|
||||
this.gui.height = this.trueHeight;
|
||||
|
||||
isHeightHackApplied = false;
|
||||
this.gui.isHeightHackApplied = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,6 +698,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
|
||||
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.focused()) {
|
||||
GuiRecipe.searchField.lastKeyTyped(i, c);
|
||||
GuiRecipe.searchField.onHotkeyPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -668,7 +723,24 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
if (i == mc.gameSettings.keyBindInventory.getKeyCode()) {
|
||||
this.mc.displayGuiScreen(getFirstScreenGeneral());
|
||||
NEICPH.sendRequestContainer();
|
||||
} else if (KeyManager.isKeyDown("recipe.back")) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.onHotkeyPressed()
|
||||
|| this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.onHotkeyPressed()
|
||||
|| this.container.onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScreenHotkeyPressed() {
|
||||
|
||||
if (KeyManager.isKeyDown("recipe.back")) {
|
||||
this.mc.displayGuiScreen(this.prevGui);
|
||||
} else if (KeyManager.isKeyDown("recipe.prev_machine")) {
|
||||
prevType();
|
||||
@@ -678,7 +750,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
prevPage();
|
||||
} else if (KeyManager.isKeyDown("recipe.next_recipe")) {
|
||||
nextPage();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -698,10 +774,8 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
return;
|
||||
}
|
||||
|
||||
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
||||
if (this.container.handleClick(mousex, mousey, button)) {
|
||||
return;
|
||||
}
|
||||
if (this.container.handleClick(mousex, mousey, button)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.recipeTabs.mouseClicked(mousex, mousey, button)) {
|
||||
@@ -749,11 +823,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
public void mouseScrolled(int scroll) {
|
||||
// Height hacking is not necessary here since mouse scrolling is a new feature,
|
||||
// added in
|
||||
// GTNH NEI. So no old mods will use this. Though not hacking the height here
|
||||
// does mean that
|
||||
// the value of the height field will be different from in other mouseover
|
||||
// methods, which
|
||||
// could be confusing...
|
||||
// GTNH NEI. So no old mods will use this.
|
||||
|
||||
// First, invoke scroll handling over recipe handler tabbar. Makes sure it is
|
||||
// not overwritten by recipe
|
||||
@@ -766,10 +836,8 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
return;
|
||||
}
|
||||
|
||||
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
||||
if (this.container.onMouseWheel(scroll, mouse.x, mouse.y)) {
|
||||
return;
|
||||
}
|
||||
if (this.container.onMouseWheel(scroll, mouse.x, mouse.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If shift is held, try switching to the next recipe handler. Replicates the
|
||||
@@ -821,9 +889,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
currenttip = this.recipeCatalyst.handleTooltip(mousex, mousey, currenttip);
|
||||
}
|
||||
|
||||
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
||||
currenttip = this.container.handleTooltip(mousex, mousey, currenttip);
|
||||
}
|
||||
currenttip = this.container.handleTooltip(mousex, mousey, currenttip);
|
||||
|
||||
this.recipeTabs.handleTooltip(mousex, mousey, currenttip);
|
||||
|
||||
@@ -869,9 +935,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
hotkeys = this.recipeCatalyst.handleHotkeys(mousex, mousey, hotkeys);
|
||||
}
|
||||
|
||||
try (CompatibilityHacks compatibilityHacks = new CompatibilityHacks()) {
|
||||
hotkeys = this.container.handleHotkeys(mousex, mousey, hotkeys);
|
||||
}
|
||||
hotkeys = this.container.handleHotkeys(mousex, mousey, hotkeys);
|
||||
|
||||
return hotkeys;
|
||||
}
|
||||
@@ -925,11 +989,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
this.guiLeft - TRANSPARENCY_BORDER,
|
||||
this.guiTop - TRANSPARENCY_BORDER,
|
||||
this.xSize + TRANSPARENCY_BORDER * 2,
|
||||
this.ySize + TRANSPARENCY_BORDER * 2,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER);
|
||||
this.ySize + TRANSPARENCY_BORDER * 2);
|
||||
|
||||
drawJEITabs(mouseX, mouseY);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ public abstract class GuiRecipeButton extends GuiNEIButton {
|
||||
|
||||
public abstract void lastKeyTyped(char keyChar, int keyID);
|
||||
|
||||
public boolean onHotkeyPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void drawItemOverlay();
|
||||
|
||||
public void update() {
|
||||
|
||||
@@ -55,11 +55,17 @@ public class GuiRecipeCatalyst extends ScrollContainer {
|
||||
0,
|
||||
0,
|
||||
28 + TRANSPARENCY_BORDER * 2,
|
||||
28 + TRANSPARENCY_BORDER * 2).setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
|
||||
28 + TRANSPARENCY_BORDER * 2)
|
||||
.setTextureSize(28 + TRANSPARENCY_BORDER * 2, 28 + TRANSPARENCY_BORDER * 2)
|
||||
.setSlices(
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER)
|
||||
.build();
|
||||
|
||||
private static final DrawableResource FG_TEXTURE = new DrawableBuilder("nei:textures/slot.png", 0, 0, 18, 18)
|
||||
.setTextureSize(18, 18).build();
|
||||
.setTextureSize(18, 18).setSlices(1, 1, 1, 1).build();
|
||||
|
||||
private static final ScrollBar VERTICAL_SCROLLBAR = ScrollBar.defaultVerticalBar()
|
||||
.setOverflowType(ScrollBar.OverflowType.AUTO).setTrackPadding(0, BORDER_PADDING, -13, BORDER_PADDING)
|
||||
@@ -155,20 +161,12 @@ public class GuiRecipeCatalyst extends ScrollContainer {
|
||||
this.x - TRANSPARENCY_BORDER,
|
||||
this.y - TRANSPARENCY_BORDER,
|
||||
this.w + TRANSPARENCY_BORDER * 2,
|
||||
this.h + TRANSPARENCY_BORDER * 2,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER);
|
||||
this.h + TRANSPARENCY_BORDER * 2);
|
||||
FG_TEXTURE.draw(
|
||||
this.x + this.paddingInlineStart - 1,
|
||||
this.y + this.paddingBlockStart - 1,
|
||||
this.w - this.paddingInlineStart - this.paddingInlineEnd + 2,
|
||||
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1);
|
||||
this.h - this.paddingBlockStart - this.paddingBlockEnd + 2);
|
||||
|
||||
super.draw(mx, my);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class HandlerInfo {
|
||||
|
||||
public static int DEFAULT_HEIGHT = 65;
|
||||
public static int DEFAULT_WIDTH = 166;
|
||||
public static final int DEFAULT_IMAGE_TEXTURE_SIZE = 256;
|
||||
|
||||
@Deprecated
|
||||
public static int DEFAULT_MAX_PER_PAGE = 1;
|
||||
@@ -70,9 +71,22 @@ public class HandlerInfo {
|
||||
}
|
||||
|
||||
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight) {
|
||||
return setImage(
|
||||
resourceLocation,
|
||||
imageX,
|
||||
imageY,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
DEFAULT_IMAGE_TEXTURE_SIZE,
|
||||
DEFAULT_IMAGE_TEXTURE_SIZE);
|
||||
}
|
||||
|
||||
public boolean setImage(String resourceLocation, int imageX, int imageY, int imageWidth, int imageHeight,
|
||||
int textureWidth, int textureHeight) {
|
||||
if (hasImageOrItem()) return false;
|
||||
|
||||
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight).build();
|
||||
this.image = new DrawableBuilder(resourceLocation, imageX, imageY, imageWidth, imageHeight)
|
||||
.setTextureSize(textureWidth, textureHeight).build();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -211,6 +225,20 @@ public class HandlerInfo {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDisplayImage(ResourceLocation location, int imageX, int imageY, int imageWidth,
|
||||
int imageHeight, int textureWidth, int textureHeight) {
|
||||
this.info.itemStack = null;
|
||||
this.info.setImage(
|
||||
location.toString(),
|
||||
imageX,
|
||||
imageY,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
textureWidth,
|
||||
textureHeight);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Builder setUseCustomScroll(boolean useCustomScroll) {
|
||||
this.info.setUseCustomScroll(useCustomScroll);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.awt.Point;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.bookmark.BookmarksGridSlot;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
public final class HoveredRecipe {
|
||||
|
||||
public enum Source {
|
||||
RECIPE_GUI,
|
||||
BOOKMARK,
|
||||
ITEM_PANEL,
|
||||
OTHER
|
||||
}
|
||||
|
||||
public final RecipeId recipeId;
|
||||
public final Source source;
|
||||
public final boolean favorite;
|
||||
|
||||
private HoveredRecipe(RecipeId recipeId, Source source, boolean favorite) {
|
||||
this.recipeId = recipeId;
|
||||
this.source = source;
|
||||
this.favorite = favorite;
|
||||
}
|
||||
|
||||
public static HoveredRecipe of(GuiScreen gui, ItemStack stackover) {
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return new HoveredRecipe(focusedRecipe.getRecipeId(), Source.RECIPE_GUI, false);
|
||||
}
|
||||
}
|
||||
|
||||
final Point mouseover = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot bookmarkSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
|
||||
if (bookmarkSlot != null) {
|
||||
|
||||
if (bookmarkSlot.getType() != BookmarkItemType.INGREDIENT && bookmarkSlot.getRecipeId() != null) {
|
||||
return new HoveredRecipe(bookmarkSlot.getRecipeId(), Source.BOOKMARK, false);
|
||||
}
|
||||
|
||||
return favorite(stackover, Source.BOOKMARK);
|
||||
}
|
||||
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
|
||||
if (itemSlot.getRecipeId() != null) {
|
||||
return new HoveredRecipe(itemSlot.getRecipeId(), Source.ITEM_PANEL, false);
|
||||
}
|
||||
|
||||
return favorite(stackover, Source.ITEM_PANEL);
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe && guiRecipe.getSlotAtPosition(mouseover.x, mouseover.y) != null) {
|
||||
return favorite(stackover, Source.RECIPE_GUI);
|
||||
}
|
||||
|
||||
return favorite(stackover, Source.OTHER);
|
||||
}
|
||||
|
||||
private static HoveredRecipe favorite(ItemStack stackover, Source source) {
|
||||
final RecipeId recipeId = FavoriteRecipes.getFavorite(stackover);
|
||||
return recipeId != null ? new HoveredRecipe(recipeId, source, true) : null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -167,6 +168,8 @@ public class RecipeHandlerRef {
|
||||
return transferAlghoritmCache.computeIfAbsent(overlayHandler.getClass(), clazz -> {
|
||||
|
||||
try {
|
||||
final Method transferRecipe = overlayHandler.getClass()
|
||||
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class);
|
||||
|
||||
if (overlayHandler instanceof DefaultOverlayHandler) {
|
||||
final Class<?> overlayRecipeClazz = overlayHandler.getClass()
|
||||
@@ -178,14 +181,11 @@ public class RecipeHandlerRef {
|
||||
boolean.class)
|
||||
.getDeclaringClass();
|
||||
|
||||
if (overlayRecipeClazz.equals(DefaultOverlayHandler.class)) {
|
||||
return true;
|
||||
}
|
||||
return overlayRecipeClazz.equals(DefaultOverlayHandler.class)
|
||||
|| !transferRecipe.getDeclaringClass().equals(DefaultOverlayHandler.class);
|
||||
}
|
||||
|
||||
return !overlayHandler.getClass()
|
||||
.getMethod("transferRecipe", GuiContainer.class, IRecipeHandler.class, int.class, int.class)
|
||||
.isDefault();
|
||||
return !transferRecipe.isDefault();
|
||||
} catch (NoSuchMethodException e) {}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -7,14 +7,10 @@ import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.bookmark.BookmarksGridSlot;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerInputHandler;
|
||||
@@ -33,6 +29,11 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
return ShortcutInputHandler.handleKeyEvent(GuiContainerManager.getStackMouseOver(gui));
|
||||
}
|
||||
|
||||
@@ -58,7 +59,9 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
|
||||
List<String> currenttip) {
|
||||
|
||||
if (itemstack != null) {
|
||||
final RecipeId recipeId = getHoveredRecipeId(gui, itemstack, mousex, mousey);
|
||||
final HoveredRecipe hovered = HoveredRecipe.of(gui, itemstack);
|
||||
final RecipeId recipeId = hovered != null && showRecipeTooltip(hovered, mousex, mousey) ? hovered.recipeId
|
||||
: null;
|
||||
|
||||
if (recipeId == null) {
|
||||
recipeTooltipLineHandler = null;
|
||||
@@ -76,49 +79,21 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
private RecipeId getHoveredRecipeId(GuiContainer gui, ItemStack itemstack, int mousex, int mousey) {
|
||||
private boolean showRecipeTooltip(HoveredRecipe hovered, int mousex, int mousey) {
|
||||
return switch (hovered.source) {
|
||||
case BOOKMARK -> !hovered.favorite && showRecipeTooltipInBookmarks(mousex, mousey);
|
||||
case ITEM_PANEL -> NEIClientConfig.showRecipeTooltipInPanel();
|
||||
case RECIPE_GUI -> hovered.favorite && NEIClientConfig.showRecipeTooltipInGui();
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
if (ItemPanels.bookmarkPanel.contains(mousex, mousey)) {
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (slot != null && slot.getRecipeId() != null
|
||||
&& slot.getType() != BookmarkItemType.INGREDIENT
|
||||
&& NEIClientConfig.getRecipeTooltipsMode() != 0) {
|
||||
final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode();
|
||||
final BookmarkViewMode viewMode = slot.getGroup().viewMode;
|
||||
if (tooltipMode == 3 || viewMode == BookmarkViewMode.DEFAULT && tooltipMode == 1
|
||||
|| viewMode == BookmarkViewMode.TODO_LIST && tooltipMode == 2) {
|
||||
return slot.getRecipeId();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showRecipeTooltipInPanel()) {
|
||||
ItemsGridSlot panelSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (panelSlot == null) {
|
||||
panelSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (panelSlot == null) {
|
||||
panelSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (panelSlot != null) {
|
||||
return panelSlot.getRecipeId();
|
||||
}
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe
|
||||
&& new Rectangle4i(gui.guiLeft, gui.guiTop, gui.xSize, gui.ySize).contains(mousex, mousey)
|
||||
&& NEIClientConfig.showRecipeTooltipInGui()
|
||||
&& guiRecipe.getFocusedRecipe() == null) {
|
||||
return FavoriteRecipes.getFavorite(itemstack);
|
||||
}
|
||||
|
||||
return null;
|
||||
private boolean showRecipeTooltipInBookmarks(int mousex, int mousey) {
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
|
||||
final int tooltipMode = NEIClientConfig.getRecipeTooltipsMode();
|
||||
final BookmarkViewMode viewMode = slot.getGroup().viewMode;
|
||||
return tooltipMode == 3 || viewMode == BookmarkViewMode.DEFAULT && tooltipMode == 1
|
||||
|| viewMode == BookmarkViewMode.TODO_LIST && tooltipMode == 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.ItemsTooltipLineHandler;
|
||||
import codechicken.nei.ItemsTooltipLineHandler.StacksAmountRenderer;
|
||||
import codechicken.nei.ItemsTooltipLineHandler.TotalAmountRenderer;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.api.IGuiContainerOverlay;
|
||||
@@ -163,9 +164,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
|
||||
protected void addResultLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
|
||||
final String label = NEIClientUtils.translate("recipe.items.results");
|
||||
final ItemsTooltipLineHandler line = items.size() == 1
|
||||
? ItemsTooltipLineHandler.list(label, items, maxLineRows())
|
||||
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
|
||||
final int maxRows = maxLineRows();
|
||||
final int maxNamedRows = items.size() == 1 ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
|
||||
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
|
||||
|
||||
if (line.isEmpty()) {
|
||||
return;
|
||||
@@ -188,7 +189,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
|
||||
protected void addRequiredLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
|
||||
final String label = NEIClientUtils.translate("recipe.items.ingredients");
|
||||
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.list(label, items, maxLineRows());
|
||||
final int maxRows = maxLineRows();
|
||||
final int maxNamedRows = NEIClientConfig.getMaxVerticalTooltipRows();
|
||||
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
|
||||
|
||||
if (line.isEmpty()) {
|
||||
return;
|
||||
@@ -211,8 +214,9 @@ public class RecipeItemsTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
|
||||
protected void addNeededLine(List<ItemStack> items, Set<NBTTagCompound> approximate, boolean vertical) {
|
||||
final String label = NEIClientUtils.translate("recipe.items.ingredients_needed");
|
||||
final ItemsTooltipLineHandler line = vertical ? ItemsTooltipLineHandler.list(label, items, maxLineRows())
|
||||
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
|
||||
final int maxRows = maxLineRows();
|
||||
final int maxNamedRows = vertical ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
|
||||
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxRows, maxNamedRows);
|
||||
|
||||
if (line.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -25,7 +25,13 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
|
||||
0,
|
||||
0,
|
||||
176 + TRANSPARENCY_BORDER * 2,
|
||||
166 + TRANSPARENCY_BORDER * 2).build();
|
||||
166 + TRANSPARENCY_BORDER * 2)
|
||||
.setSlices(
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER)
|
||||
.build();
|
||||
|
||||
protected long lastUpdate = System.currentTimeMillis();
|
||||
protected String recipeName = "";
|
||||
@@ -105,11 +111,7 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
|
||||
-TRANSPARENCY_BORDER,
|
||||
-TRANSPARENCY_BORDER,
|
||||
size.width + TRANSPARENCY_BORDER * 2,
|
||||
size.height + TRANSPARENCY_BORDER * 2,
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER,
|
||||
BG_PADDING + TRANSPARENCY_BORDER);
|
||||
size.height + TRANSPARENCY_BORDER * 2);
|
||||
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
|
||||
|
||||
@@ -131,7 +131,14 @@ public class StackInfo {
|
||||
}
|
||||
|
||||
if (isFluidDisplayItem(stackA)) {
|
||||
return isFluidDisplayItem(stackB) && getFluid(stackA).isFluidEqual(getFluid(stackB));
|
||||
|
||||
if (isFluidDisplayItem(stackB)) {
|
||||
final FluidStack fluidA = getFluid(stackA);
|
||||
final FluidStack fluidB = getFluid(stackB);
|
||||
return fluidA != null && fluidA.isFluidEqual(fluidB);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stackA.getItem() != stackB.getItem()) {
|
||||
|
||||
@@ -287,6 +287,11 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
if (!canHandle(gui)) return false;
|
||||
|
||||
if (KeyManager.isKeyDown("recipe.recipe")) {
|
||||
|
||||
@@ -3,11 +3,9 @@ package codechicken.nei.recipe.chain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -23,8 +21,8 @@ import codechicken.nei.ItemSorter;
|
||||
import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.ItemsTooltipLineHandler;
|
||||
import codechicken.nei.ItemsTooltipLineHandler.AmountRenderer;
|
||||
import codechicken.nei.ItemsTooltipLineHandler.StacksAmountRenderer;
|
||||
import codechicken.nei.ItemsTooltipLineHandler.TotalAmountRenderer;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
@@ -38,25 +36,6 @@ import codechicken.nei.recipe.StackInfo;
|
||||
|
||||
public class RecipeChainTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
|
||||
protected static class MissingStacksAmountRenderer extends StacksAmountRenderer {
|
||||
|
||||
protected static final int MISSING_COLOR = 0xFF5555;
|
||||
|
||||
protected final Set<NBTTagCompound> missing;
|
||||
private final Map<ItemStack, Boolean> cache = new IdentityHashMap<>();
|
||||
|
||||
public MissingStacksAmountRenderer(Set<NBTTagCompound> missing) {
|
||||
this.missing = missing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getColor(ItemStack stack) {
|
||||
return this.cache.computeIfAbsent(stack, key -> this.missing.contains(StackInfo.itemStackToNBT(key, false)))
|
||||
? MISSING_COLOR
|
||||
: super.getColor(stack);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class HandlerIconRenderer extends TotalAmountRenderer {
|
||||
|
||||
protected static final int ICON_SIZE = 16;
|
||||
@@ -284,35 +263,15 @@ public class RecipeChainTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
}
|
||||
}
|
||||
|
||||
final Set<NBTTagCompound> missing = new HashSet<>();
|
||||
|
||||
if (this.lastShiftKey) {
|
||||
for (ItemStack stack : inputs) {
|
||||
missing.add(StackInfo.itemStackToNBT(stack, false));
|
||||
}
|
||||
}
|
||||
|
||||
// items with a shortage are already in the inputs list, holding the missing amount instead of the required one
|
||||
final List<ItemStack> covered = new ArrayList<>();
|
||||
|
||||
for (ItemStack stack : available) {
|
||||
if (!missing.contains(StackInfo.itemStackToNBT(stack, false))) {
|
||||
covered.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
final Comparator<ItemStack> comparator = Comparator
|
||||
.comparing((ItemStack stack) -> StackInfo.getFluid(stack) != null)
|
||||
.thenComparingInt(stack -> -1 * stack.stackSize).thenComparing(ItemSorter.instance);
|
||||
|
||||
inputs.sort(comparator);
|
||||
covered.sort(comparator);
|
||||
available.sort(comparator);
|
||||
outputs.sort(comparator);
|
||||
remainder.sort(comparator);
|
||||
|
||||
// missing items stay on top of the list
|
||||
inputs.addAll(covered);
|
||||
|
||||
if (this.lastShiftKey) {
|
||||
outputs.addAll(remainder);
|
||||
}
|
||||
@@ -331,12 +290,21 @@ public class RecipeChainTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
EnumChatFormatting.BLUE,
|
||||
new HandlerIconRenderer(craftingHandlers));
|
||||
|
||||
if (this.lastShiftKey) {
|
||||
addLine(
|
||||
NEIClientUtils.translate("bookmark.crafting_chain.missing"),
|
||||
inputs,
|
||||
true,
|
||||
EnumChatFormatting.RED,
|
||||
new TotalAmountRenderer());
|
||||
}
|
||||
|
||||
addLine(
|
||||
NEIClientUtils.translate("bookmark.crafting_chain.input"),
|
||||
inputs,
|
||||
this.lastShiftKey ? available : inputs,
|
||||
this.lastShiftKey,
|
||||
EnumChatFormatting.YELLOW,
|
||||
this.lastShiftKey ? new MissingStacksAmountRenderer(missing) : new TotalAmountRenderer());
|
||||
new TotalAmountRenderer());
|
||||
}
|
||||
|
||||
protected static void putHandlerInfo(Map<NBTTagCompound, HandlerInfo> handlers, ItemStack stack,
|
||||
@@ -354,8 +322,8 @@ public class RecipeChainTooltipLineHandler extends CompositeTooltipLineHandler {
|
||||
|
||||
protected void addLine(String label, List<ItemStack> items, boolean vertical, EnumChatFormatting labelColor,
|
||||
AmountRenderer amountRenderer) {
|
||||
final ItemsTooltipLineHandler line = vertical ? ItemsTooltipLineHandler.list(label, items, maxLineRows())
|
||||
: ItemsTooltipLineHandler.grid(label, items, maxLineRows());
|
||||
final int maxNamedRows = vertical ? NEIClientConfig.getMaxVerticalTooltipRows() : 0;
|
||||
final ItemsTooltipLineHandler line = ItemsTooltipLineHandler.of(label, items, maxLineRows(), maxNamedRows);
|
||||
|
||||
if (line.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -25,6 +25,7 @@ public class CheckboxWidget extends Button implements IUpdatableWidget {
|
||||
|
||||
@Override
|
||||
public boolean onButtonPress(boolean rightclick) {
|
||||
if (this.state == 2) return false;
|
||||
this.value = !this.value;
|
||||
onChange.accept(this);
|
||||
return true;
|
||||
|
||||
@@ -30,19 +30,24 @@ import codechicken.nei.ClientHandler;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.Widget;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeButton;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.ShapedRecipeHandler;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.ScrollContainer;
|
||||
|
||||
public class DebugHandlerWidget extends Widget {
|
||||
public class DebugHandlerWidget extends Widget implements IContainerTooltipHandler {
|
||||
|
||||
private static class HandlerInfoRecord {
|
||||
|
||||
@@ -152,6 +157,9 @@ public class DebugHandlerWidget extends Widget {
|
||||
private Map<String, HandlerInfoRecord> patches = new HashMap<>();
|
||||
|
||||
private HandlerInfoRecord record;
|
||||
private RecipeWidget hoveredRecipe;
|
||||
private boolean duplicatesOnly = false;
|
||||
private String duplicatesHandlerId;
|
||||
|
||||
private Point dragPoint = null;
|
||||
private boolean showWidget = false;
|
||||
@@ -162,11 +170,19 @@ public class DebugHandlerWidget extends Widget {
|
||||
0,
|
||||
0,
|
||||
176 + TRANSPARENCY_BORDER * 2,
|
||||
166 + TRANSPARENCY_BORDER * 2).build();
|
||||
166 + TRANSPARENCY_BORDER * 2)
|
||||
.setSlices(
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER)
|
||||
.build();
|
||||
private static final int BORDER_PADDING = 6;
|
||||
private static final int TRANSPARENCY_BORDER = 4;
|
||||
|
||||
public DebugHandlerWidget() {
|
||||
protected DebugHandlerWidget() {
|
||||
GuiContainerManager.addTooltipHandler(this);
|
||||
|
||||
this.w = WINDOW_WIDTH;
|
||||
this.x = 10;
|
||||
this.y = 50;
|
||||
@@ -242,6 +258,20 @@ public class DebugHandlerWidget extends Widget {
|
||||
saveHandlerInfoPatch();
|
||||
}, y);
|
||||
|
||||
if (Boolean.getBoolean("nei.recipeOrigins")) {
|
||||
y = addCheckboxWidget("DuplicatesOnly", button -> {
|
||||
this.duplicatesOnly = button.value;
|
||||
|
||||
if (NEIClientUtils.getGuiContainer() instanceof GuiRecipe<?>recipe) {
|
||||
this.duplicatesHandlerId = getHandlerID(recipe.getHandler());
|
||||
recipe.forceRefreshPage();
|
||||
}
|
||||
}, y + LINE_HEIGHT / 2);
|
||||
|
||||
// only the handler the checkbox was toggled on is filtered, so switching handlers does not pay for a rescan
|
||||
API.addRecipeFilter(() -> this.duplicatesOnly ? this::isDuplicate : null);
|
||||
}
|
||||
|
||||
this.h = 10 + LINE_HEIGHT + y;
|
||||
this.container.h = this.h - INLINE_PADDING * 2 - LINE_HEIGHT;
|
||||
}
|
||||
@@ -355,11 +385,7 @@ public class DebugHandlerWidget extends Widget {
|
||||
this.x - TRANSPARENCY_BORDER,
|
||||
this.y - TRANSPARENCY_BORDER,
|
||||
this.w + TRANSPARENCY_BORDER * 2,
|
||||
this.h + TRANSPARENCY_BORDER * 2,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER,
|
||||
BORDER_PADDING + TRANSPARENCY_BORDER);
|
||||
this.h + TRANSPARENCY_BORDER * 2);
|
||||
GuiDraw.drawRect(
|
||||
this.x + INLINE_PADDING,
|
||||
this.y + BORDER_PADDING,
|
||||
@@ -417,6 +443,11 @@ public class DebugHandlerWidget extends Widget {
|
||||
|
||||
updateOverride();
|
||||
|
||||
if (this.values.get("duplicatesonly") instanceof CheckboxWidget duplicates) {
|
||||
duplicates.state = handler instanceof ShapedRecipeHandler ? 0 : 2;
|
||||
resetDuplicatesOnly();
|
||||
}
|
||||
|
||||
this.container.removeIf(
|
||||
widget -> this.values.get("height") == widget || this.values.get("height_readonly") == widget);
|
||||
if (handler.getRecipeHeight(0) > 0) {
|
||||
@@ -430,11 +461,26 @@ public class DebugHandlerWidget extends Widget {
|
||||
} else {
|
||||
this.record = null;
|
||||
this.showWidget = false;
|
||||
resetDuplicatesOnly();
|
||||
}
|
||||
|
||||
this.container.update();
|
||||
}
|
||||
|
||||
private void resetDuplicatesOnly() {
|
||||
if (!this.duplicatesOnly) return;
|
||||
|
||||
this.duplicatesOnly = false;
|
||||
|
||||
if (this.values.get("duplicatesonly") instanceof CheckboxWidget duplicates) {
|
||||
duplicates.updateValue("false");
|
||||
}
|
||||
|
||||
if (NEIClientUtils.getGuiContainer() instanceof GuiRecipe<?>recipe) {
|
||||
recipe.forceRefreshPage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(int mx, int my, List<String> tooltip) {
|
||||
return this.record != null ? this.container.handleTooltip(mx, my, tooltip) : tooltip;
|
||||
@@ -466,6 +512,11 @@ public class DebugHandlerWidget extends Widget {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
return this.record != null && this.container.onHotkeyPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClick(int mx, int my) {
|
||||
if (this.record != null) {
|
||||
@@ -532,9 +583,51 @@ public class DebugHandlerWidget extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> tooltip) {
|
||||
final RecipeWidget widget = this.hoveredRecipe;
|
||||
this.hoveredRecipe = null;
|
||||
|
||||
if (widget == null || !this.showWidget || !tooltip.isEmpty()) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
final RecipeHandlerRef ref = widget.getRecipeHandlerRef();
|
||||
|
||||
for (List<String> origin : RecipeOrigins.findOrigins(ref.handler, ref.recipeIndex)) {
|
||||
addOriginLines(tooltip, origin);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
private static void addOriginLines(List<String> tooltip, List<String> lines) {
|
||||
tooltip.add(
|
||||
EnumChatFormatting.WHITE + NEIClientUtils.translate("debug.RecipeHandler.RegisteredBy", lines.get(0))
|
||||
+ GuiDraw.TOOLTIP_LINESPACE);
|
||||
|
||||
for (int i = 1; i < lines.size(); i++) {
|
||||
tooltip.add(EnumChatFormatting.GRAY + lines.get(i));
|
||||
}
|
||||
|
||||
tooltip.add("");
|
||||
}
|
||||
|
||||
// only the handler the checkbox was toggled on is filtered
|
||||
private boolean isDuplicate(IRecipeHandler handler, int recipeIndex) {
|
||||
return !getHandlerID(handler).equals(this.duplicatesHandlerId)
|
||||
|| RecipeOrigins.hasDuplicates(handler, recipeIndex);
|
||||
}
|
||||
|
||||
public void drawGuiPlaceholder(RecipeWidget widget) {
|
||||
if (!this.showWidget) return;
|
||||
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
if (widget.getRecipeHandlerRef().handler instanceof ShapedRecipeHandler && widget.contains(mouse.x, mouse.y)
|
||||
&& widget.getStackMouseOver(mouse.x, mouse.y) == null) {
|
||||
this.hoveredRecipe = widget;
|
||||
}
|
||||
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
|
||||
// background
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
package codechicken.nei.recipe.debug;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.ShapedRecipes;
|
||||
import net.minecraft.item.crafting.ShapelessRecipes;
|
||||
import net.minecraftforge.oredict.ShapedOreRecipe;
|
||||
import net.minecraftforge.oredict.ShapelessOreRecipe;
|
||||
|
||||
import codechicken.nei.NEIServerUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.ShapedRecipeHandler;
|
||||
import codechicken.nei.recipe.ShapedRecipeHandler.CachedShapedRecipe;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler.CachedRecipe;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
|
||||
/**
|
||||
* Tracks which mod registered each crafting recipe, so the recipe handler debug widget can show it in the recipe
|
||||
* tooltip. {@link TrackingList} is spliced in as the type of {@link CraftingManager}'s internal recipe list by
|
||||
* {@link codechicken.nei.asm.NEITransformer} (see the {@code <init>} transform there) in place of the plain
|
||||
* {@code ArrayList} vanilla constructs it with. Patching the constructor itself, rather than swapping the field later
|
||||
* on, means every single recipe is observed, including vanilla's own, which are added by that same constructor before
|
||||
* any mod code ever runs. Opt-in via {@code -Dnei.recipeOrigins=true}, since capturing a stack trace per recipe slows
|
||||
* down loading.
|
||||
*/
|
||||
public class RecipeOrigins {
|
||||
|
||||
private static final int MAX_FRAMES = 10;
|
||||
|
||||
private static final Map<IRecipe, List<String>> origins = Collections.synchronizedMap(new IdentityHashMap<>());
|
||||
|
||||
private static class OutputIndex {
|
||||
|
||||
final int size;
|
||||
final Map<Item, List<IRecipe>> byItem;
|
||||
|
||||
OutputIndex(int size, Map<Item, List<IRecipe>> byItem) {
|
||||
this.size = size;
|
||||
this.byItem = byItem;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<CachedRecipe, List<List<String>>> cache = new WeakHashMap<>();
|
||||
|
||||
private static volatile OutputIndex outputIndex = new OutputIndex(-1, Collections.emptyMap());
|
||||
|
||||
private RecipeOrigins() {}
|
||||
|
||||
public static List<List<String>> findOrigins(IRecipeHandler handler, int recipeIndex) {
|
||||
if (!(handler instanceof ShapedRecipeHandler shaped)) return Collections.emptyList();
|
||||
|
||||
final CachedRecipe recipe = shaped.arecipes.get(recipeIndex);
|
||||
List<List<String>> found;
|
||||
|
||||
synchronized (cache) {
|
||||
found = cache.get(recipe);
|
||||
}
|
||||
|
||||
if (found == null) {
|
||||
found = findOrigins(shaped, recipe.getResult(), recipe.getIngredients());
|
||||
|
||||
synchronized (cache) {
|
||||
cache.put(recipe, found);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public static boolean hasDuplicates(IRecipeHandler handler, int recipeIndex) {
|
||||
return handler instanceof ShapedRecipeHandler shaped
|
||||
&& hasDuplicateCandidates(shaped.arecipes.get(recipeIndex).getResult())
|
||||
&& findOrigins(handler, recipeIndex).size() > 1;
|
||||
}
|
||||
|
||||
private static boolean hasDuplicateCandidates(PositionedStack result) {
|
||||
if (origins.isEmpty() || result == null || result.item == null) return false;
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (IRecipe recipe : getRecipesByOutput(result.item.getItem())) {
|
||||
if (origins.containsKey(recipe)
|
||||
&& NEIServerUtils.areStacksSameTypeCrafting(recipe.getRecipeOutput(), result.item)
|
||||
&& ++count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<List<String>> findOrigins(ShapedRecipeHandler handler, PositionedStack result,
|
||||
List<PositionedStack> ingredients) {
|
||||
if (origins.isEmpty() || result == null || result.item == null) return Collections.emptyList();
|
||||
final List<List<String>> found = new ArrayList<>();
|
||||
|
||||
for (IRecipe recipe : getRecipesByOutput(result.item.getItem())) {
|
||||
final List<String> origin = origins.get(recipe);
|
||||
|
||||
if (origin != null && NEIServerUtils.areStacksSameTypeCrafting(recipe.getRecipeOutput(), result.item)
|
||||
&& matchesIngredients(handler, recipe, ingredients)) {
|
||||
final List<String> entry = new ArrayList<>(origin);
|
||||
entry.add(recipe.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(recipe)));
|
||||
found.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private static List<IRecipe> getRecipesByOutput(Item item) {
|
||||
final List<IRecipe> recipes = CraftingManager.getInstance().getRecipeList();
|
||||
OutputIndex index = outputIndex;
|
||||
|
||||
if (recipes.size() != index.size) {
|
||||
synchronized (RecipeOrigins.class) {
|
||||
index = outputIndex;
|
||||
|
||||
if (recipes.size() != index.size) {
|
||||
final Map<Item, List<IRecipe>> byItem = new HashMap<>();
|
||||
|
||||
for (IRecipe recipe : recipes) {
|
||||
final ItemStack output = recipe.getRecipeOutput();
|
||||
if (output != null)
|
||||
byItem.computeIfAbsent(output.getItem(), k -> new ArrayList<>()).add(recipe);
|
||||
}
|
||||
|
||||
outputIndex = index = new OutputIndex(recipes.size(), byItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index.byItem.getOrDefault(item, Collections.emptyList());
|
||||
}
|
||||
|
||||
private static boolean matchesIngredients(ShapedRecipeHandler handler, IRecipe recipe,
|
||||
List<PositionedStack> ingredients) {
|
||||
if (recipe instanceof ShapedRecipes shaped) {
|
||||
return matchesGrid(handler.new CachedShapedRecipe(shaped), ingredients);
|
||||
}
|
||||
if (recipe instanceof ShapedOreRecipe shaped)
|
||||
return matchesGrid(handler.forgeShapedRecipe(shaped), ingredients);
|
||||
if (recipe instanceof ShapelessRecipes shapeless) return matchesUnordered(shapeless.recipeItems, ingredients);
|
||||
if (recipe instanceof ShapelessOreRecipe shapeless) return matchesUnordered(shapeless.getInput(), ingredients);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean matchesGrid(CachedShapedRecipe reference, List<PositionedStack> ingredients) {
|
||||
if (reference == null || reference.ingredients.size() != ingredients.size()) return false;
|
||||
|
||||
for (PositionedStack expected : reference.ingredients) {
|
||||
final PositionedStack actual = ingredients.stream()
|
||||
.filter(stack -> stack.relx == expected.relx && stack.rely == expected.rely).findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (actual == null || !matchesItems(expected.items, actual.items)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean matchesItems(ItemStack[] expected, ItemStack[] actual) {
|
||||
for (ItemStack candidate : expected) {
|
||||
for (ItemStack item : actual) {
|
||||
if (NEIServerUtils.areStacksSameTypeCrafting(candidate, item)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean matchesUnordered(List<?> inputs, List<PositionedStack> ingredients) {
|
||||
final List<PositionedStack> remaining = new ArrayList<>(ingredients);
|
||||
|
||||
for (Object input : inputs) {
|
||||
if (input == null) continue;
|
||||
|
||||
final PositionedStack match = remaining.stream().filter(stack -> matchesInput(input, stack)).findFirst()
|
||||
.orElse(null);
|
||||
if (match == null) return false;
|
||||
remaining.remove(match);
|
||||
}
|
||||
|
||||
return remaining.isEmpty();
|
||||
}
|
||||
|
||||
private static boolean matchesInput(Object input, PositionedStack stack) {
|
||||
final List<?> candidates = input instanceof List<?>list ? list : Collections.singletonList(input);
|
||||
|
||||
for (Object candidate : candidates) {
|
||||
if (!(candidate instanceof ItemStack candidateStack)) continue;
|
||||
for (ItemStack item : stack.items) {
|
||||
if (NEIServerUtils.areStacksSameTypeCrafting(candidateStack, item)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class TrackingList extends ArrayList<IRecipe> {
|
||||
|
||||
@Override
|
||||
public boolean add(IRecipe recipe) {
|
||||
origins.put(recipe, captureOrigin());
|
||||
return super.add(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, IRecipe recipe) {
|
||||
origins.put(recipe, captureOrigin());
|
||||
super.add(index, recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRecipe set(int index, IRecipe recipe) {
|
||||
origins.put(recipe, captureOrigin());
|
||||
return super.set(index, recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends IRecipe> recipes) {
|
||||
final List<String> origin = captureOrigin();
|
||||
for (IRecipe recipe : recipes) origins.put(recipe, origin);
|
||||
return super.addAll(recipes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends IRecipe> recipes) {
|
||||
final List<String> origin = captureOrigin();
|
||||
for (IRecipe recipe : recipes) origins.put(recipe, origin);
|
||||
return super.addAll(index, recipes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRecipe remove(int index) {
|
||||
final IRecipe removed = super.remove(index);
|
||||
origins.remove(removed);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object recipe) {
|
||||
final boolean removed = super.remove(recipe);
|
||||
if (removed) origins.remove(recipe);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
origins.clear();
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> captureOrigin() {
|
||||
final List<StackTraceElement> frames = findCallerFrames();
|
||||
|
||||
if (frames.isEmpty()) return Collections.singletonList("Unknown");
|
||||
|
||||
final ModContainer container = Loader.instance().activeModContainer();
|
||||
final List<String> lines = new ArrayList<>(frames.size() + 1);
|
||||
|
||||
lines.add(container != null ? container.getName() : "Unknown");
|
||||
|
||||
for (int i = 0; i < frames.size(); i++) {
|
||||
lines.add(formatFrame(frames.get(i)));
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static String formatFrame(StackTraceElement frame) {
|
||||
return frame.getFileName() != null ? frame.getClassName() + "."
|
||||
+ frame.getMethodName()
|
||||
+ "("
|
||||
+ frame.getFileName()
|
||||
+ ":"
|
||||
+ frame.getLineNumber()
|
||||
+ ")" : frame.getClassName() + "." + frame.getMethodName();
|
||||
}
|
||||
|
||||
private static List<StackTraceElement> findCallerFrames() {
|
||||
final List<StackTraceElement> frames = new ArrayList<>(MAX_FRAMES);
|
||||
|
||||
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
|
||||
final String className = element.getClassName();
|
||||
|
||||
if (className.startsWith("java.") || className.startsWith("net.minecraft.")
|
||||
|| className.startsWith("net.minecraftforge.")
|
||||
|| className.startsWith("cpw.mods.fml.")
|
||||
|| className.startsWith("sun.")
|
||||
|| className.startsWith("jdk.")
|
||||
|| className.startsWith("org.spongepowered.")
|
||||
|| className.startsWith("codechicken.nei.recipe.debug.RecipeOrigins")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
frames.add(element);
|
||||
if (frames.size() >= MAX_FRAMES) break;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,12 @@ public class GTFluidStackStringifyHandler implements IStackStringifyHandler {
|
||||
return (FluidStack) obj;
|
||||
}
|
||||
|
||||
final Fluid fluid = FluidRegistry.getFluid(stack.getItemDamage());
|
||||
|
||||
if (fluid != null) {
|
||||
return new FluidStack(fluid, 0);
|
||||
}
|
||||
|
||||
} else if (item == GameRegistry.findItem("ae2fc", "fluid_packet")) {
|
||||
NBTTagCompound nbtTag = stack.getTagCompound();
|
||||
|
||||
|
||||
@@ -456,25 +456,6 @@ public class RecipeWidget extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyID, char keyChar) {
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled()
|
||||
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final PositionedStack hovered = getCyclingStackMouseOver(mouse.x, mouse.y);
|
||||
|
||||
if (hovered != null && getPermutations(hovered).size() > 1) {
|
||||
FavoriteRecipes.toggleFavoriteItem(hovered.item);
|
||||
setPaused(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
@@ -490,6 +471,27 @@ public class RecipeWidget extends Widget {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled()
|
||||
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
|
||||
final PositionedStack hovered = getCyclingStackMouseOver(mouse.x, mouse.y);
|
||||
|
||||
if (hovered != null && getPermutations(hovered).size() > 1) {
|
||||
FavoriteRecipes.toggleFavoriteItem(hovered.item);
|
||||
setPaused(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return forEachButtons(
|
||||
button -> button.contains(mouse.x, mouse.y) && button.onHotkeyPressed() ? true : null,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mouseX, int mouseY, int button) {
|
||||
|
||||
@@ -526,7 +528,9 @@ public class RecipeWidget extends Widget {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.handler.mouseClicked(guiRecipe, button, getRecipeIndex());
|
||||
try (GuiRecipe.CompatibilityHacks hack = new GuiRecipe.CompatibilityHacks(guiRecipe, this)) {
|
||||
return this.handler.mouseClicked(guiRecipe, button, getRecipeIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -550,7 +554,9 @@ public class RecipeWidget extends Widget {
|
||||
return buttonTooltip;
|
||||
}
|
||||
|
||||
return this.handler.handleTooltip(guiRecipe, tooltip, getRecipeIndex());
|
||||
try (GuiRecipe.CompatibilityHacks hack = new GuiRecipe.CompatibilityHacks(guiRecipe, this)) {
|
||||
return this.handler.handleTooltip(guiRecipe, tooltip, getRecipeIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -741,7 +747,8 @@ public class RecipeWidget extends Widget {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !this.showAsWidget && contains(mouseX, mouseY)
|
||||
return NEIClientConfig.freezeCycleOnHover() && !this.showAsWidget
|
||||
&& contains(mouseX, mouseY)
|
||||
&& (getPositionedStackMouseOver(mouseX, mouseY) != null
|
||||
|| forEachButtons(button -> button.contains(mouseX, mouseY) ? true : null, false));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import cpw.mods.fml.relauncher.SideOnly;
|
||||
public class GuiHelper {
|
||||
|
||||
private static final Stack<int[]> scissorFrameStack = new Stack<>();
|
||||
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
public static void useScissor(int x, int y, int width, int height, Runnable codeBlock) {
|
||||
pushScissorFrame(x, y, width, height);
|
||||
@@ -36,7 +37,7 @@ public class GuiHelper {
|
||||
return currentTopFrame;
|
||||
}
|
||||
|
||||
private static void pushScissorFrame(int x, int y, int width, int height) {
|
||||
public static void pushScissorFrame(int x, int y, int width, int height) {
|
||||
int[] matrix = prepareMatrix(x, y, width, height);
|
||||
int[] parentScissor = peekFirstScissorOrFullScreen();
|
||||
int parentX = parentScissor[0];
|
||||
@@ -110,7 +111,7 @@ public class GuiHelper {
|
||||
|
||||
private static int[] prepareMatrix(int x, int y, int w, int h) {
|
||||
// Get the current translation component to support the Gui being drawn at any position on the screen
|
||||
final FloatBuffer matBuf = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer matBuf = MATRIX_BUFFER;
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matBuf);
|
||||
final float xScale = matBuf.get(0);
|
||||
final float yScale = matBuf.get(5);
|
||||
|
||||
@@ -53,28 +53,26 @@ public class ScrollBar {
|
||||
private int trackPaddingBottom = 0;
|
||||
|
||||
private DrawableResource thumbTexture;
|
||||
private int thumbSliceStart;
|
||||
private int thumbSliceEnd;
|
||||
|
||||
private DrawableResource trackTexture;
|
||||
private int trackSliceStart;
|
||||
private int trackSliceEnd;
|
||||
|
||||
public static ScrollBar defaultScrollBar() {
|
||||
return new ScrollBar();
|
||||
}
|
||||
|
||||
public static ScrollBar defaultVerticalBar() {
|
||||
return new ScrollBar().setTrackWidth(8)
|
||||
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 29, 95, 8, 11).build(), 3, 4)
|
||||
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).build(), 3, 2)
|
||||
return new ScrollBar().setTrackWidth(8).setTrackTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 29, 95, 8, 11).setSlices(0, 0, 3, 4).build())
|
||||
.setThumbTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 22, 96, 6, 9).setSlices(0, 0, 3, 2).build())
|
||||
.setThumbPadding(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public static ScrollBar defaultHorizontalBar() {
|
||||
return new ScrollBar().setTrackWidth(8).setOverflowType(OverflowType.SCROLL)
|
||||
.setTrackTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 0, 97, 11, 8).build(), 3, 4)
|
||||
.setThumbTexture(new DrawableBuilder("nei:textures/nei_sprites.png", 12, 98, 9, 6).build(), 3, 2)
|
||||
.setTrackTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 0, 97, 11, 8).setSlices(3, 4, 0, 0).build())
|
||||
.setThumbTexture(
|
||||
new DrawableBuilder("nei:textures/nei_sprites.png", 12, 98, 9, 6).setSlices(3, 2, 0, 0).build())
|
||||
.setThumbPadding(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
@@ -126,17 +124,13 @@ public class ScrollBar {
|
||||
return getTrackWidth();
|
||||
}
|
||||
|
||||
public ScrollBar setThumbTexture(DrawableResource texture, int sliceStart, int sliceEnd) {
|
||||
public ScrollBar setThumbTexture(DrawableResource texture) {
|
||||
this.thumbTexture = texture;
|
||||
this.thumbSliceStart = sliceStart;
|
||||
this.thumbSliceEnd = sliceEnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScrollBar setTrackTexture(DrawableResource texture, int sliceStart, int sliceEnd) {
|
||||
public ScrollBar setTrackTexture(DrawableResource texture) {
|
||||
this.trackTexture = texture;
|
||||
this.trackSliceStart = sliceStart;
|
||||
this.trackSliceEnd = sliceEnd;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -379,13 +373,13 @@ public class ScrollBar {
|
||||
final boolean disabled = getVisibleSize(container) >= getActualSize(container);
|
||||
|
||||
if (this.trackTexture != null) {
|
||||
drawTexture(this.trackTexture, this.trackSliceStart, this.trackSliceEnd, trackBounds, false, opacity);
|
||||
drawTexture(this.trackTexture, trackBounds, false, opacity);
|
||||
} else {
|
||||
drawTrackRect(container, trackBounds, opacity);
|
||||
}
|
||||
|
||||
if (this.thumbTexture != null) {
|
||||
drawTexture(this.thumbTexture, this.thumbSliceStart, this.thumbSliceEnd, thumbBounds, disabled, opacity);
|
||||
drawTexture(this.thumbTexture, thumbBounds, disabled, opacity);
|
||||
} else {
|
||||
drawThumbRect(container, thumbBounds, selected, opacity);
|
||||
}
|
||||
@@ -417,8 +411,7 @@ public class ScrollBar {
|
||||
NEIClientUtils.drawRect(bounds.x, bounds.y, bounds.w, bounds.h, thumbColor);
|
||||
}
|
||||
|
||||
private void drawTexture(DrawableResource texture, int sliceStart, int sliceEnd, Rectangle4i bounds,
|
||||
boolean disabled, float opacity) {
|
||||
private void drawTexture(DrawableResource texture, Rectangle4i bounds, boolean disabled, float opacity) {
|
||||
final float c = disabled ? 0.65f : 1f;
|
||||
|
||||
GL11.glColor4f(c, c, c, opacity);
|
||||
@@ -426,9 +419,9 @@ public class ScrollBar {
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
if (this.scrollType == ScrollType.HORIZONTAL) {
|
||||
texture.draw(bounds.x, bounds.y, bounds.w, texture.getHeight(), sliceStart, sliceEnd, 0, 0);
|
||||
texture.draw(bounds.x, bounds.y, bounds.w, texture.getHeight());
|
||||
} else {
|
||||
texture.draw(bounds.x, bounds.y, texture.getWidth(), bounds.h, 0, 0, sliceStart, sliceEnd);
|
||||
texture.draw(bounds.x, bounds.y, texture.getWidth(), bounds.h);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
@@ -249,6 +250,8 @@ public class ScrollContainer extends WidgetContainer {
|
||||
final boolean canScrollHorizontal = canScrollHorizontal();
|
||||
|
||||
if (canScrollVertical || canScrollHorizontal) {
|
||||
dragScrollBars(mousex, mousey, canScrollVertical, canScrollHorizontal);
|
||||
|
||||
GuiHelper.useScissor(
|
||||
this.x + this.paddingInlineStart,
|
||||
this.y + this.paddingBlockStart,
|
||||
@@ -273,6 +276,20 @@ public class ScrollContainer extends WidgetContainer {
|
||||
|
||||
}
|
||||
|
||||
private void dragScrollBars(int mousex, int mousey, boolean canScrollVertical, boolean canScrollHorizontal) {
|
||||
if (!Mouse.isButtonDown(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canScrollVertical) {
|
||||
this.verticalScrollBar.onMouseDragged(this, mousex, mousey, 0, 0);
|
||||
}
|
||||
|
||||
if (canScrollHorizontal) {
|
||||
this.horizontalScrollBar.onMouseDragged(this, mousex, mousey, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawContent(int mousex, int mousey) {
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushMatrix();
|
||||
|
||||
@@ -14,6 +14,9 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import codechicken.nei.ItemList.AllMultiItemFilter;
|
||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
||||
import codechicken.nei.ItemList.NegatedItemFilter;
|
||||
@@ -197,9 +200,9 @@ public class ItemStackFilterParser {
|
||||
}
|
||||
|
||||
private static ItemFilter getStrictIdentifierFilter(String rule) {
|
||||
ItemStack item = NEIServerUtils.getModdedItem(rule, null);
|
||||
Supplier<ItemStack> item = Suppliers.memoize(() -> NEIServerUtils.getModdedItem(rule, null));
|
||||
|
||||
return stack -> OreDictionary.itemMatches(item, stack, false);
|
||||
return stack -> OreDictionary.itemMatches(item.get(), stack, false);
|
||||
}
|
||||
|
||||
protected static ItemFilter getStringIdentifierFilter(String rule) {
|
||||
|
||||
@@ -56,18 +56,24 @@ public class NEIKeyboardUtils {
|
||||
return keyText.toString();
|
||||
}
|
||||
|
||||
public static String getKeyName(int keyBind) {
|
||||
keyBind = keyHashMap.getOrDefault(keyBind, keyBind);
|
||||
StringJoiner keyText = new StringJoiner(" + ");
|
||||
String hashText = getHashName(keyBind);
|
||||
int keyID = unhash(keyBind);
|
||||
/**
|
||||
* Mouse buttons are negative key codes, so the hash can not be packed into them and is passed separately.
|
||||
*/
|
||||
public static String getKeyName(int keyCode, int hash) {
|
||||
if (hash == 0 && keyHashMap.containsKey(keyCode)) {
|
||||
hash = keyHashMap.get(keyCode);
|
||||
keyCode = Keyboard.CHAR_NONE;
|
||||
}
|
||||
|
||||
final StringJoiner keyText = new StringJoiner(" + ");
|
||||
final String hashText = getHashName(hash);
|
||||
|
||||
if (!hashText.isEmpty()) {
|
||||
keyText.add(hashText);
|
||||
}
|
||||
|
||||
if (keyID != Keyboard.CHAR_NONE || hashText.isEmpty()) {
|
||||
keyText.add(keyID < 0 ? NEIMouseUtils.getKeyName(keyID + 100) : Keyboard.getKeyName(keyID));
|
||||
if (keyCode != Keyboard.CHAR_NONE || hashText.isEmpty()) {
|
||||
keyText.add(keyCode < 0 ? NEIMouseUtils.getKeyName(keyCode + 100) : Keyboard.getKeyName(keyCode));
|
||||
}
|
||||
|
||||
return keyText.toString();
|
||||
|
||||
@@ -121,6 +121,12 @@ RETURN
|
||||
|
||||
list m_handleMouseInput
|
||||
ALOAD 0
|
||||
GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager;
|
||||
INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleMouseHotkey ()Z
|
||||
IFEQ LCONT
|
||||
RETURN
|
||||
LCONT
|
||||
ALOAD 0
|
||||
INVOKESPECIAL net/minecraft/client/gui/GuiScreen.func_146274_d ()V
|
||||
ALOAD 0
|
||||
GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager;
|
||||
|
||||
@@ -48,6 +48,7 @@ nei.bookmark.recipe.toggle_collapse=Toggle Collapse/Expand
|
||||
nei.bookmark.recipe.chat_link=Send Recipe to Chat
|
||||
nei.bookmark.crafting_chain=Crafting Chain
|
||||
nei.bookmark.crafting_chain.input=Ingredients
|
||||
nei.bookmark.crafting_chain.missing=Missing Items
|
||||
nei.bookmark.crafting_chain.needed=Required Crafts
|
||||
nei.bookmark.tree.title=Crafting Tree
|
||||
nei.bookmark.tree.empty=No crafting chain to display
|
||||
@@ -289,6 +290,7 @@ nei.options.inventory.bookmarks.showRecipeMarkerMode=Show Recipe Marker
|
||||
nei.options.inventory.bookmarks.showRecipeMarkerMode.0=None
|
||||
nei.options.inventory.bookmarks.showRecipeMarkerMode.1=Text
|
||||
nei.options.inventory.bookmarks.showRecipeMarkerMode.2=Background
|
||||
nei.options.inventory.bookmarks.maxVerticalTooltipRows=Max Vertical Tooltip Rows (0 - disable)
|
||||
nei.options.inventory.bookmarks.showRecipeHandlerIcon=Show Handler Icon
|
||||
nei.options.inventory.bookmarks.showRecipeHandlerIcon.true=Yes
|
||||
nei.options.inventory.bookmarks.showRecipeHandlerIcon.false=No
|
||||
@@ -427,6 +429,9 @@ nei.options.inventory.autocrafting.false=Disabled
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe=Require holding shift to move items when overlaying recipe
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe.true=Yes
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe.false=No
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover=Stop cycling the recipe while hovering over an ingredient or a button
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover.true=Yes
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover.false=No
|
||||
nei.options.inventory.widgetsenabled.true=NEI Enabled
|
||||
nei.options.inventory.widgetsenabled.false=NEI Disabled
|
||||
nei.options.inventory.cheatmode.0=Recipe Mode
|
||||
@@ -516,6 +521,13 @@ nei.options.inventory.itemzoom.neiOnly.tip=Zoom items only from the JEI ingredie
|
||||
nei.options.inventory.itemzoom.onlySolid=Show Only Solid Items
|
||||
nei.options.inventory.itemzoom.onlySolid.true=Enabled
|
||||
nei.options.inventory.itemzoom.onlySolid.false=Disabled
|
||||
nei.options.inventory.itemzoom.preferredSide=Preferred Side
|
||||
nei.options.inventory.itemzoom.preferredSide.0=Auto
|
||||
nei.options.inventory.itemzoom.preferredSide.1=Left
|
||||
nei.options.inventory.itemzoom.preferredSide.2=Right
|
||||
nei.options.inventory.itemzoom.preferredSide.0.tip=Prefer whichever side currently has fewer items in its panel.
|
||||
nei.options.inventory.itemzoom.preferredSide.1.tip=Always prefer the left side, unless the cursor is there.
|
||||
nei.options.inventory.itemzoom.preferredSide.2.tip=Always prefer the right side, unless the cursor is there.
|
||||
nei.options.inventory.itemzoom.helpText=Display Help Text
|
||||
nei.options.inventory.itemzoom.helpText.true=Enabled
|
||||
nei.options.inventory.itemzoom.helpText.false=Disabled
|
||||
@@ -617,6 +629,7 @@ nei.debug.RecipeHandler.Width=Width
|
||||
nei.debug.RecipeHandler.ShowFavorites=Show Favorites
|
||||
nei.debug.RecipeHandler.ShowOverlay=Show Overlays
|
||||
nei.debug.RecipeHandler.ShowBadge=Show Badge
|
||||
nei.debug.RecipeHandler.DuplicatesOnly=Duplicates Only
|
||||
nei.debug.RecipeHandler.AllowOverflowY=Allow Overflow Y
|
||||
nei.debug.RecipeHandler.AllowOverflowX=Allow Overflow X
|
||||
nei.debug.RecipeHandler.Override=Override
|
||||
@@ -625,6 +638,7 @@ nei.debug.RecipeHandler.checkbox.off=Off
|
||||
nei.debug.RecipeHandler.yes=Yes
|
||||
nei.debug.RecipeHandler.no=No
|
||||
nei.debug.RecipeHandler.reset=Reset
|
||||
nei.debug.RecipeHandler.RegisteredBy=Registered by: %s
|
||||
|
||||
nei.recipe.page=Page %s/%s
|
||||
nei.recipe.tooltip=Recipes
|
||||
@@ -693,6 +707,7 @@ nei.recipe.overlay.missing=Missing following
|
||||
nei.recipe.overlay.mismatch=Mismatch Crafting Grid
|
||||
nei.recipe.overlay.overlayRecipe=Overlay Recipe
|
||||
nei.recipe.overlay.fillCraftingGrid=Fill Crafting Grid
|
||||
nei.recipe.overlay.fillCraftingGridOnce=Fill Crafting Grid (single recipe)
|
||||
nei.recipe.overlay.bookmarkRecipe=Save with Recipe
|
||||
nei.recipe.overlay.bookmarkRecipeAndCount=Save with Recipe & Count
|
||||
nei.recipe.overlay.sendRecipeToChat=Send Recipe to Chat
|
||||
|
||||
@@ -265,6 +265,9 @@ nei.options.inventory.optimize_gui_overlap_computation.false=Нет
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe=Требовать удержание Shift для перемещения предметов при отображении рецепта
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe.true=Да
|
||||
nei.options.inventory.guirecipe.shiftOverlayRecipe.false=Нет
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover=Останавливать перелистывание рецепта при наведении на предмет или кнопку
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover.true=Да
|
||||
nei.options.inventory.guirecipe.freezeCycleOnHover.false=Нет
|
||||
nei.options.inventory.widgetsenabled.true=NEI включен
|
||||
nei.options.inventory.widgetsenabled.false=NEI выключен
|
||||
nei.options.inventory.cheatmode.0=Режим рецептов
|
||||
@@ -332,6 +335,13 @@ nei.options.inventory.itemzoom.neiOnly.tip=Увеличивать предмет
|
||||
nei.options.inventory.itemzoom.onlySolid=Показывать только твердые предметы
|
||||
nei.options.inventory.itemzoom.onlySolid.true=Включено
|
||||
nei.options.inventory.itemzoom.onlySolid.false=Отключено
|
||||
nei.options.inventory.itemzoom.preferredSide=Предпочитаемая сторона
|
||||
nei.options.inventory.itemzoom.preferredSide.0=Авто
|
||||
nei.options.inventory.itemzoom.preferredSide.1=Слева
|
||||
nei.options.inventory.itemzoom.preferredSide.2=Справа
|
||||
nei.options.inventory.itemzoom.preferredSide.0.tip=Выбирать сторону, где сейчас меньше предметов в панели.
|
||||
nei.options.inventory.itemzoom.preferredSide.1.tip=Всегда предпочитать левую сторону, если там нет курсора.
|
||||
nei.options.inventory.itemzoom.preferredSide.2.tip=Всегда предпочитать правую сторону, если там нет курсора.
|
||||
nei.options.inventory.itemzoom.helpText=Показывать текст помощи
|
||||
nei.options.inventory.itemzoom.helpText.true=Включено
|
||||
nei.options.inventory.itemzoom.helpText.false=Отключено
|
||||
@@ -428,6 +438,7 @@ nei.recipe.overlay=отображение рецепта
|
||||
nei.recipe.overlay.missing=Отсутствует следующее
|
||||
nei.recipe.overlay.mismatch=Несоответствие сетки крафта
|
||||
nei.recipe.overlay.fillCraftingGrid=Заполнить сетку крафта
|
||||
nei.recipe.overlay.fillCraftingGridOnce=Заполнить сетку крафта (один рецепт)
|
||||
nei.recipe.overlay.bookmarkRecipe=Сохранить с рецептом
|
||||
nei.recipe.overlay.sendRecipeToChat=Отправить рецепт в чат
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
|
||||
class PluginLoaderTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { false, true })
|
||||
void loadsPluginsAndLogsFailuresWithTheGameClassLoader(boolean parallel) throws Exception {
|
||||
ClassLoader workerClassLoader = new ClassLoader(null) {};
|
||||
Set<Thread> workers = ConcurrentHashMap.newKeySet();
|
||||
ForkJoinPool pool = new ForkJoinPool(2, owner -> {
|
||||
ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(owner);
|
||||
worker.setContextClassLoader(workerClassLoader);
|
||||
workers.add(worker);
|
||||
return worker;
|
||||
}, null, false);
|
||||
Logger logger = mock(Logger.class);
|
||||
doAnswer(invocation -> {
|
||||
assertSame(IConfigureNEI.class.getClassLoader(), Thread.currentThread().getContextClassLoader());
|
||||
return null;
|
||||
}).when(logger).error(eq("Failed to Load {}"), anyString(), any(Throwable.class));
|
||||
|
||||
List<Class<?>> plugins = new ArrayList<>();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
plugins.add(HealthyPlugin.class);
|
||||
plugins.add(ConstructorFailurePlugin.class);
|
||||
plugins.add(ConfigurationFailurePlugin.class);
|
||||
}
|
||||
List<IConfigureNEI> loaded = new CopyOnWriteArrayList<>();
|
||||
|
||||
try {
|
||||
pool.submit(() -> PluginLoader.load(parallel ? plugins.parallelStream() : plugins.stream(), loaded, logger))
|
||||
.get(10, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals(16, loaded.size());
|
||||
verify(logger, times(32)).error(eq("Failed to Load {}"), anyString(), any(Throwable.class));
|
||||
workers.forEach(worker -> assertSame(workerClassLoader, worker.getContextClassLoader()));
|
||||
} finally {
|
||||
pool.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void restoresTheClassLoaderEvenIfErrorLoggingThrows() {
|
||||
Thread thread = Thread.currentThread();
|
||||
ClassLoader previousClassLoader = thread.getContextClassLoader();
|
||||
ClassLoader workerClassLoader = new ClassLoader(null) {};
|
||||
Logger logger = mock(Logger.class);
|
||||
doThrow(new LinkageError("Logging failed")).when(logger)
|
||||
.error(eq("Failed to Load {}"), anyString(), any(Throwable.class));
|
||||
|
||||
try {
|
||||
thread.setContextClassLoader(workerClassLoader);
|
||||
assertThrows(
|
||||
LinkageError.class,
|
||||
() -> PluginLoader.load(Stream.of(ConfigurationFailurePlugin.class), new ArrayList<>(), logger));
|
||||
assertSame(workerClassLoader, thread.getContextClassLoader());
|
||||
} finally {
|
||||
thread.setContextClassLoader(previousClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HealthyPlugin implements IConfigureNEI {
|
||||
|
||||
public HealthyPlugin() {
|
||||
checkClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadConfig() {
|
||||
checkClassLoader();
|
||||
}
|
||||
|
||||
private static void checkClassLoader() {
|
||||
if (Thread.currentThread().getContextClassLoader() != IConfigureNEI.class.getClassLoader()) {
|
||||
throw new IllegalStateException("Plugin cannot see the game classloader");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Test plugin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "1";
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConstructorFailurePlugin extends HealthyPlugin {
|
||||
|
||||
public ConstructorFailurePlugin() {
|
||||
throw new IllegalStateException("Constructor failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfigurationFailurePlugin extends HealthyPlugin {
|
||||
|
||||
@Override
|
||||
public void loadConfig() {
|
||||
super.loadConfig();
|
||||
throw new IllegalStateException("Configuration failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ public class SearchTokenParserTest {
|
||||
new ModeValue(1, "Extended mode"), new ModeValue(2, "Regex mode"), new ModeValue(3, "Extended+ mode") };
|
||||
private static final ModeValue[] extendedPlusPatternMode = { patternModes[3] };
|
||||
private static final ModeValue[] extendedPatternModes = { patternModes[1], patternModes[3] };
|
||||
private static final ModeValue[] nonGrammarPatternModes = { patternModes[0], patternModes[1], patternModes[2] };
|
||||
|
||||
private static final SearchParserProvider defaultParserProvider = parserProvider(
|
||||
'\0',
|
||||
@@ -119,6 +120,10 @@ public class SearchTokenParserTest {
|
||||
|
||||
private static final List<Arguments> allModesTestProvider = combinationsProvider(spaceModes, patternModes);
|
||||
|
||||
private static final List<Arguments> nonGrammarAllSpaceModesTestProvider = combinationsProvider(
|
||||
spaceModes,
|
||||
nonGrammarPatternModes);
|
||||
|
||||
private static final List<Arguments> extendedPlusAllSpaceModesTestProvider = combinationsProvider(
|
||||
spaceModes,
|
||||
extendedPlusPatternMode);
|
||||
@@ -236,6 +241,36 @@ public class SearchTokenParserTest {
|
||||
assertTrue(matchedItems.contains(itemA));
|
||||
}
|
||||
|
||||
@DisplayName("Negated prefix (dash)")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("allModesTestProvider")
|
||||
public void testNegatedPrefixDash(int spaceMode, int patternMode) {
|
||||
SearchTokenParser searchParser = new SearchTokenParser();
|
||||
searchParser.addProvider(defaultParserProvider);
|
||||
searchParser
|
||||
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
|
||||
|
||||
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "-?A");
|
||||
|
||||
assertEquals(5, matchedItems.size());
|
||||
assertFalse(matchedItems.contains(itemA));
|
||||
}
|
||||
|
||||
@DisplayName("Negated prefix (bang)")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("nonGrammarAllSpaceModesTestProvider")
|
||||
public void testNegatedPrefixBang(int spaceMode, int patternMode) {
|
||||
SearchTokenParser searchParser = new SearchTokenParser();
|
||||
searchParser.addProvider(defaultParserProvider);
|
||||
searchParser
|
||||
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
|
||||
|
||||
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "!?A");
|
||||
|
||||
assertEquals(5, matchedItems.size());
|
||||
assertFalse(matchedItems.contains(itemA));
|
||||
}
|
||||
|
||||
@DisplayName("Space mode")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("spaceIsSpaceAllModesTestProvider")
|
||||
|
||||
Reference in new issue
Block a user