mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-15 17:54:50 +00:00
Compare commits
74
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
673ed93907 | ||
|
|
8fa2d8aa59 | ||
|
|
0e2e814051 | ||
|
|
c6acb196f8 | ||
|
|
baaea8484e | ||
|
|
8e6b31de18 | ||
|
|
1a27b02412 | ||
|
|
0185cd64b1 | ||
|
|
fab584b8c1 | ||
|
|
fbbeafcd79 | ||
|
|
9839d1d08f | ||
|
|
0fc657c9d9 | ||
|
|
0e6f01f84d | ||
|
|
1478f21eb4 | ||
|
|
dab939acb7 | ||
|
|
4d63ed13dc | ||
|
|
76b62072ab | ||
|
|
84d42de15f | ||
|
|
d04e1c8e83 | ||
|
|
42c47e5a89 | ||
|
|
8d8f8370fc | ||
|
|
753fc0da3a | ||
|
|
ef8cac9c39 | ||
|
|
aa8f1c8adb | ||
|
|
b29494935d | ||
|
|
d6789c44d4 | ||
|
|
d07059cb5a | ||
|
|
b7dcd0fb3d | ||
|
|
d04718c5c6 | ||
|
|
514af5669c | ||
|
|
41c2b96c88 | ||
|
|
8e1541eab1 | ||
|
|
9411541fd8 | ||
|
|
ae0d6c6108 | ||
|
|
aea4c2866a | ||
|
|
c1a525594c | ||
|
|
afc8a2380b | ||
|
|
a0b2430458 | ||
|
|
4dc6be839b | ||
|
|
030795e3c7 | ||
|
|
897a22ad17 | ||
|
|
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 | ||
|
|
a685efb3b7 | ||
|
|
9f3697bb67 | ||
|
|
18abb880f8 | ||
|
|
5868187410 |
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
|
||||
|
||||
@@ -3,9 +3,9 @@ name: Build and test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main ]
|
||||
branches: [ master, main, release/** ]
|
||||
push:
|
||||
branches: [ master, main ]
|
||||
branches: [ master, main, release/** ]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
|
||||
@@ -75,7 +75,7 @@ All items that match `filter` will show your information page. See `config/NEI/c
|
||||
|
||||
#### Item Information Config File
|
||||
|
||||
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples.
|
||||
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples. The file is not created automatically: use the Export button in the NEI options under Tools > Default Config Files to copy the defaults to `config/NEI`.
|
||||
|
||||
## Other items of note:
|
||||
|
||||
|
||||
+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'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.lwjgl.opengl.GL11;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
@@ -55,12 +56,14 @@ public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSl
|
||||
}
|
||||
|
||||
protected void updateLinePadding() {
|
||||
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
|
||||
final Rectangle4i rect = new Rectangle4i(this.x, this.y - 1, 10, 2);
|
||||
this.linePaddingStart = 0;
|
||||
|
||||
while (GuiInfo
|
||||
.hideItemPanelSlot(guiContainer, new Rectangle4i(this.x + this.linePaddingStart, this.y - 1, 10, 2))) {
|
||||
while (this.linePaddingStart < this.w && GuiInfo.hideItemPanelSlot(guiContainer, rect, handlers)) {
|
||||
this.linePaddingStart += 10;
|
||||
rect.x += 10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ package codechicken.nei;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
@@ -12,7 +10,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -37,8 +36,10 @@ import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import codechicken.core.ClassDiscoverer;
|
||||
@@ -50,8 +51,13 @@ import codechicken.nei.api.IConfigureNEI;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.bookmark.tree.GuiCraftingTree;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiUsageRecipe;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.RecipeItemInputHandler;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.recipe.debug.DebugHandlerWidget;
|
||||
import cpw.mods.fml.client.CustomModLoadingErrorDisplayException;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
@@ -67,6 +73,22 @@ public class ClientHandler {
|
||||
|
||||
private static ClientHandler instance;
|
||||
private final ArrayList<EntityItem> SMPmagneticItems = new ArrayList<>();
|
||||
private static final String HANDLER_ORDERING = "handlerordering.csv";
|
||||
public static final List<String> DEFAULT_CONFIGS = ImmutableList.of(
|
||||
"serialhandlers.cfg",
|
||||
"heighthackhandlers.cfg",
|
||||
"hiddenhandlers.cfg",
|
||||
"enableautofocus.cfg",
|
||||
"guicraftablesblacklist.cfg",
|
||||
"defaultbookmarkcontainers.cfg",
|
||||
"hiddenitems.cfg",
|
||||
"guidfilters.cfg",
|
||||
"informationpages.cfg",
|
||||
"collapsibleitems.cfg",
|
||||
"recipeidblacklist.cfg",
|
||||
"handlers.csv",
|
||||
"catalysts.csv",
|
||||
HANDLER_ORDERING);
|
||||
private World lastworld;
|
||||
|
||||
public void addSMPMagneticItem(int i, World world) {
|
||||
@@ -160,17 +182,30 @@ public class ClientHandler {
|
||||
StackInfo.loadGuidFilters();
|
||||
}
|
||||
|
||||
public static void loadSettingsFile(String resource, Consumer<Stream<String>> callback) {
|
||||
loadSettingsFile(resource, (file, writer) -> {
|
||||
String folder = resource.substring(resource.lastIndexOf(".") + 1);
|
||||
URL defaultResource = ClientHandler.class.getResource("/assets/nei/" + folder + "/" + resource);
|
||||
private static URL getDefaultResource(String resource) {
|
||||
final String folder = resource.substring(resource.lastIndexOf(".") + 1);
|
||||
return ClientHandler.class.getResource("/assets/nei/" + folder + "/" + resource);
|
||||
}
|
||||
|
||||
if (defaultResource != null) {
|
||||
try {
|
||||
IOUtils.copy(defaultResource.openStream(), writer);
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}, callback);
|
||||
public static boolean exportDefaultConfig(String resource) {
|
||||
final File file = new File(NEIClientConfig.configDir, resource);
|
||||
if (file.exists() && !NEIClientUtils.backupFile(file)) return false;
|
||||
|
||||
if (HANDLER_ORDERING.equals(resource)) {
|
||||
return writeDefaultHandlerOrdering();
|
||||
}
|
||||
|
||||
final URL url = getDefaultResource(resource);
|
||||
if (url == null) return false;
|
||||
|
||||
try {
|
||||
FileUtils.copyURLToFile(url, file);
|
||||
NEIClientConfig.logger.info("Exported default '{}' to {}", resource, file);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to export default '{}' to file {}", resource, file, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean loadSettingsResource(String resource, Consumer<Stream<String>> callback) {
|
||||
@@ -196,30 +231,24 @@ public class ClientHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
|
||||
Consumer<Stream<String>> callback) {
|
||||
File file = new File(NEIClientConfig.configDir, resource);
|
||||
public static boolean loadSettingsFile(String resource, Consumer<Stream<String>> callback) {
|
||||
final File file = new File(NEIClientConfig.configDir, resource);
|
||||
final boolean fromJar = !file.exists();
|
||||
|
||||
if (!file.exists() && createDefault != null) {
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
|
||||
createDefault.accept(file, writer);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", resource, file, e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
final URL url = fromJar ? getDefaultResource(resource) : file.toURI().toURL();
|
||||
if (url == null) return false;
|
||||
|
||||
if (file.exists()) {
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
|
||||
try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) {
|
||||
NEIClientConfig.logger.info("Loading '{}' from {}", resource, fromJar ? "JAR" : file);
|
||||
callback.accept(
|
||||
IOUtils.readLines(reader).stream()
|
||||
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
|
||||
.map(String::trim).filter(line -> !line.isEmpty()));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -292,26 +321,46 @@ public class ClientHandler {
|
||||
}
|
||||
|
||||
public static void loadHandlerOrdering() {
|
||||
final String COMMA_DELIMITER = ",";
|
||||
final String[] defaultHandlerOrdering = {
|
||||
loadSettingsFile(
|
||||
HANDLER_ORDERING,
|
||||
lines -> lines.map(line -> line.split(",")).filter(parts -> parts.length == 2).forEach(parts -> {
|
||||
String handlerId = parts[0];
|
||||
int ordering = Integer.parseInt(parts[1]);
|
||||
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
|
||||
}));
|
||||
}
|
||||
|
||||
private static boolean writeDefaultHandlerOrdering() {
|
||||
final File file = new File(NEIClientConfig.configDir, HANDLER_ORDERING);
|
||||
final List<String> lines = Lists.newArrayList(
|
||||
"# Each line in this file should either be a comment (starts with '#') or an ordering.",
|
||||
"# Ordering lines are <handler ID>,<ordering number>.",
|
||||
"# Handlers will be sorted in order of number ascending, so smaller numbers first.",
|
||||
"# Any handlers that are missing from this file will be assigned to 0.", "# Negative numbers are fine.",
|
||||
"# If you delete this file, it will be regenerated with all registered handler IDs.", };
|
||||
"# Any handlers that are missing from this file keep their own order, 0 for most of them.",
|
||||
"# Negative numbers are fine.");
|
||||
final Map<String, Integer> orders = new TreeMap<>();
|
||||
final List<IRecipeHandler> handlers = new ArrayList<>();
|
||||
handlers.addAll(GuiCraftingRecipe.craftinghandlers);
|
||||
handlers.addAll(GuiCraftingRecipe.serialCraftingHandlers);
|
||||
handlers.addAll(GuiUsageRecipe.usagehandlers);
|
||||
handlers.addAll(GuiUsageRecipe.serialUsageHandlers);
|
||||
|
||||
loadSettingsFile("handlerordering.csv", (file, writer) -> {
|
||||
List<String> toWrite = Lists.newArrayList(defaultHandlerOrdering);
|
||||
GuiRecipeTab.handlerMap.keySet().stream().sorted()
|
||||
.forEach(handlerId -> toWrite.add(String.format("%s" + COMMA_DELIMITER + "0", handlerId)));
|
||||
try {
|
||||
IOUtils.writeLines(toWrite, "\n", writer);
|
||||
} catch (IOException e) {}
|
||||
}, lines -> lines.map(line -> line.split(COMMA_DELIMITER)).filter(parts -> parts.length == 2).forEach(parts -> {
|
||||
String handlerId = parts[0];
|
||||
int ordering = Integer.parseInt(parts[1]);
|
||||
NEIClientConfig.handlerOrdering.put(handlerId, ordering);
|
||||
}));
|
||||
for (IRecipeHandler handler : handlers) {
|
||||
orders.putIfAbsent(
|
||||
NEIClientConfig.HANDLER_ID_FUNCTION.apply(handler),
|
||||
NEIClientConfig.getHandlerOrder(handler));
|
||||
}
|
||||
|
||||
orders.forEach((handlerId, order) -> lines.add(handlerId + "," + order));
|
||||
|
||||
try {
|
||||
NEIClientConfig.logger.info("Creating default '{}' {}", HANDLER_ORDERING, file);
|
||||
FileUtils.writeLines(file, StandardCharsets.UTF_8.name(), lines, "\n");
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save default '{}' to file {}", HANDLER_ORDERING, file, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadPluginsList() {
|
||||
@@ -398,6 +447,12 @@ public class ClientHandler {
|
||||
if (event.world == mc.theWorld) {
|
||||
NEIClientConfig.unloadWorld();
|
||||
ItemMobSpawner.clearEntityReferences();
|
||||
WorldOverlayRenderer.onWorldUnload();
|
||||
RecipeItemInputHandler.instance.clearTooltipCache();
|
||||
RecipeHandlerRef.clearCache();
|
||||
ItemPanels.itemPanel.craftablesPanel.clearGuiReferences();
|
||||
DebugHandlerWidget.instance.clearGuiReferences();
|
||||
NEIController.manager = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTBase.NBTPrimitive;
|
||||
@@ -35,6 +38,7 @@ public class CollapsibleItems {
|
||||
public ItemFilter filter;
|
||||
public boolean expanded = false;
|
||||
public String displayName = "";
|
||||
public String unlocalizedName;
|
||||
|
||||
public void setFilter(String filter) {
|
||||
this.filter = ItemStackFilterParser.parse(filter.trim());
|
||||
@@ -46,15 +50,38 @@ public class CollapsibleItems {
|
||||
this.guid = guid;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (this.unlocalizedName != null && !this.unlocalizedName.isEmpty()) {
|
||||
final String translated = StatCollector.translateToLocal(this.unlocalizedName);
|
||||
|
||||
if (!translated.equals(this.unlocalizedName)) {
|
||||
return translated;
|
||||
}
|
||||
}
|
||||
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public boolean matches(ItemStack stack) {
|
||||
return this.filter.matches(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiGroup {
|
||||
|
||||
private String unlocalizedName;
|
||||
|
||||
public ApiGroup setName(String unlocalizedName) {
|
||||
this.unlocalizedName = unlocalizedName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String STATE_KEY = "collapsibleitems";
|
||||
|
||||
protected static File statesFile;
|
||||
protected static final List<GroupItem> groups = new ArrayList<>();
|
||||
protected static final List<Supplier<GroupItem>> apiGroups = new CopyOnWriteArrayList<>();
|
||||
protected static final Map<ItemStack, Integer> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private CollapsibleItems() {}
|
||||
@@ -77,11 +104,57 @@ public class CollapsibleItems {
|
||||
ClientHandler.loadSettingsFile(
|
||||
"collapsibleitems.cfg",
|
||||
lines -> parseFile(lines.collect(Collectors.toCollection(ArrayList::new))));
|
||||
|
||||
for (Supplier<GroupItem> apiGroup : CollapsibleItems.apiGroups) {
|
||||
try {
|
||||
addGroup(apiGroup.get());
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Failed to add API group", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadStates();
|
||||
}
|
||||
|
||||
public static ApiGroup registerGroup(String filter) {
|
||||
final ApiGroup apiGroup = new ApiGroup();
|
||||
|
||||
CollapsibleItems.apiGroups.add(() -> {
|
||||
GroupItem group = new GroupItem();
|
||||
group.setFilter(filter);
|
||||
group.unlocalizedName = apiGroup.unlocalizedName;
|
||||
return group;
|
||||
});
|
||||
|
||||
return apiGroup;
|
||||
}
|
||||
|
||||
public static ApiGroup registerGroup(Iterable<ItemStack> items) {
|
||||
final ApiGroup apiGroup = new ApiGroup();
|
||||
final ItemStackSet filter = new ItemStackSet();
|
||||
|
||||
for (ItemStack stack : items) {
|
||||
if (stack != null) filter.add(stack.copy());
|
||||
}
|
||||
|
||||
if (filter.isEmpty()) return apiGroup;
|
||||
|
||||
final String key = filter.keys().stream()
|
||||
.map(stack -> Item.itemRegistry.getNameForObject(stack.getItem()) + ":" + stack.getItemDamage())
|
||||
.sorted().collect(Collectors.joining(","));
|
||||
final String guid = UUID.nameUUIDFromBytes(key.getBytes()).toString();
|
||||
|
||||
CollapsibleItems.apiGroups.add(() -> {
|
||||
GroupItem group = new GroupItem();
|
||||
group.setFilter(filter, guid);
|
||||
group.unlocalizedName = apiGroup.unlocalizedName;
|
||||
return group;
|
||||
});
|
||||
|
||||
return apiGroup;
|
||||
}
|
||||
|
||||
private static void parseFile(List<String> itemStrings) {
|
||||
final JsonParser parser = new JsonParser();
|
||||
GroupItem group = new GroupItem();
|
||||
@@ -92,17 +165,8 @@ public class CollapsibleItems {
|
||||
if (itemStr.startsWith("; ")) {
|
||||
JsonObject settings = parser.parse(itemStr.substring(2)).getAsJsonObject();
|
||||
|
||||
if (settings.get("displayName") != null) {
|
||||
group.displayName = settings.get("displayName").getAsString();
|
||||
}
|
||||
|
||||
if (settings.get("unlocalizedName") != null) {
|
||||
String unlocalizedName = settings.get("unlocalizedName").getAsString();
|
||||
String displayName = StatCollector.translateToLocal(unlocalizedName);
|
||||
|
||||
if (!displayName.equals(unlocalizedName)) {
|
||||
group.displayName = displayName;
|
||||
}
|
||||
group.unlocalizedName = settings.get("unlocalizedName").getAsString();
|
||||
}
|
||||
|
||||
if (settings.get("expanded") != null) {
|
||||
@@ -176,7 +240,7 @@ public class CollapsibleItems {
|
||||
public static String getDisplayName(int groupIndex) {
|
||||
|
||||
if (groupIndex < CollapsibleItems.groups.size()) {
|
||||
return CollapsibleItems.groups.get(groupIndex).displayName;
|
||||
return CollapsibleItems.groups.get(groupIndex).getDisplayName();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -24,7 +23,6 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
@@ -34,7 +32,6 @@ import codechicken.nei.api.API;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -95,7 +92,7 @@ public class FavoriteRecipes {
|
||||
for (int i = 0; i < handler.numRecipes(); i++) {
|
||||
RecipeHandlerRef ref = null;
|
||||
|
||||
for (PositionedStack result : getOutputs(handler, i)) {
|
||||
for (PositionedStack result : handler.getResultStacks(i)) {
|
||||
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
|
||||
final FastKey key = new FastKey(stack);
|
||||
|
||||
@@ -130,11 +127,6 @@ public class FavoriteRecipes {
|
||||
updateSubsetVisibility();
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
|
||||
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final String FAVORITE_ITEM = "FavoriteItem";
|
||||
@@ -244,14 +236,7 @@ public class FavoriteRecipes {
|
||||
|
||||
if (hasErrors) {
|
||||
|
||||
try {
|
||||
Files.copy(
|
||||
favoriteFile,
|
||||
new File(favoriteFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
|
||||
NEIClientConfig.logger.info("Backed up invalid favorite file to {}", favoriteFile);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to backup invalid favorite file to {}", favoriteFile, e);
|
||||
}
|
||||
NEIClientUtils.backupFile(favoriteFile);
|
||||
|
||||
save();
|
||||
}
|
||||
@@ -316,6 +301,10 @@ public class FavoriteRecipes {
|
||||
load();
|
||||
}
|
||||
|
||||
public static boolean hasManual() {
|
||||
return manualFavorites.size() > 0;
|
||||
}
|
||||
|
||||
public static boolean containsManual(ItemStack stack) {
|
||||
return getManualFavorite(stack) != null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
|
||||
final class GridOverlapScanner {
|
||||
|
||||
private static boolean scanRequested = true;
|
||||
private static boolean scanFrame = true;
|
||||
private static boolean wasShowTooltip = true;
|
||||
private static WeakReference<GuiContainer> lastGui = new WeakReference<>(null);
|
||||
|
||||
private boolean invalidated = true;
|
||||
|
||||
private final Rectangle4i rect = new Rectangle4i();
|
||||
private boolean[] checked = new boolean[0];
|
||||
private int[] queue = new int[0];
|
||||
private int tail;
|
||||
|
||||
private Predicate<Rectangle4i> isHidden;
|
||||
private boolean[] hidden;
|
||||
private int hiddenCount;
|
||||
private int left;
|
||||
private int top;
|
||||
private int slotSize;
|
||||
private int columns;
|
||||
|
||||
public static void requestScan() {
|
||||
scanRequested = true;
|
||||
}
|
||||
|
||||
public static void beginFrame(GuiContainer gui, boolean shouldShowTooltip) {
|
||||
|
||||
if (wasShowTooltip != shouldShowTooltip) {
|
||||
scanRequested = true;
|
||||
}
|
||||
|
||||
wasShowTooltip = shouldShowTooltip;
|
||||
|
||||
if (lastGui.get() != gui) {
|
||||
lastGui = new WeakReference<>(gui);
|
||||
scanRequested = true;
|
||||
}
|
||||
|
||||
scanFrame = scanRequested;
|
||||
scanRequested = false;
|
||||
}
|
||||
|
||||
public static boolean isScanFrame() {
|
||||
return scanFrame;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.invalidated = true;
|
||||
}
|
||||
|
||||
public boolean shouldRefresh() {
|
||||
final boolean refresh = this.invalidated || scanFrame;
|
||||
this.invalidated = false;
|
||||
return refresh;
|
||||
}
|
||||
|
||||
public int scan(Predicate<Rectangle4i> isHidden, int left, int top, int slotSize, int rows, int columns,
|
||||
boolean seedLeft, boolean seedRight, boolean[] hidden) {
|
||||
final int size = rows * columns;
|
||||
|
||||
if (this.checked.length < size) {
|
||||
this.checked = new boolean[size];
|
||||
this.queue = new int[size];
|
||||
} else {
|
||||
Arrays.fill(this.checked, 0, size, false);
|
||||
}
|
||||
|
||||
this.isHidden = isHidden;
|
||||
this.hidden = hidden;
|
||||
this.hiddenCount = 0;
|
||||
this.tail = 0;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
this.slotSize = slotSize;
|
||||
this.columns = columns;
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
if (seedLeft) {
|
||||
probe(r, 0);
|
||||
}
|
||||
if (seedRight) {
|
||||
probe(r, columns - 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int head = 0; head < this.tail; head++) {
|
||||
final int row = this.queue[head] / columns;
|
||||
final int column = this.queue[head] % columns;
|
||||
|
||||
for (int r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) {
|
||||
for (int c = Math.max(0, column - 1); c <= Math.min(columns - 1, column + 1); c++) {
|
||||
probe(r, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
for (int c = 0; c < columns; c++) {
|
||||
if (this.checked[r * columns + c]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int c1 = c;
|
||||
while (c1 + 1 < columns && !this.checked[r * columns + c1 + 1]) {
|
||||
c1++;
|
||||
}
|
||||
|
||||
int r1 = r;
|
||||
while (r1 + 1 < rows && isUnchecked(r1 + 1, c, c1)) {
|
||||
r1++;
|
||||
}
|
||||
|
||||
scanArea(r, r1, c, c1);
|
||||
c = c1;
|
||||
}
|
||||
}
|
||||
|
||||
this.isHidden = null;
|
||||
this.hidden = null;
|
||||
|
||||
return this.hiddenCount;
|
||||
}
|
||||
|
||||
private void probe(int row, int column) {
|
||||
final int idx = row * this.columns + column;
|
||||
|
||||
if (!this.checked[idx]) {
|
||||
this.checked[idx] = true;
|
||||
|
||||
if (test(row, row, column, column)) {
|
||||
this.hidden[idx] = true;
|
||||
this.queue[this.tail++] = idx;
|
||||
this.hiddenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scanArea(int r0, int r1, int c0, int c1) {
|
||||
|
||||
if (!test(r0, r1, c0, c1)) {
|
||||
for (int r = r0; r <= r1; r++) {
|
||||
Arrays.fill(this.checked, r * this.columns + c0, r * this.columns + c1 + 1, true);
|
||||
}
|
||||
} else if (r0 == r1 && c0 == c1) {
|
||||
this.checked[r0 * this.columns + c0] = true;
|
||||
this.hidden[r0 * this.columns + c0] = true;
|
||||
this.hiddenCount++;
|
||||
} else if (r1 - r0 >= c1 - c0) {
|
||||
final int mid = (r0 + r1) / 2;
|
||||
scanArea(r0, mid, c0, c1);
|
||||
scanArea(mid + 1, r1, c0, c1);
|
||||
} else {
|
||||
final int mid = (c0 + c1) / 2;
|
||||
scanArea(r0, r1, c0, mid);
|
||||
scanArea(r0, r1, mid + 1, c1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnchecked(int row, int c0, int c1) {
|
||||
for (int c = c0; c <= c1; c++) {
|
||||
if (this.checked[row * this.columns + c]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean test(int r0, int r1, int c0, int c1) {
|
||||
this.rect.set(
|
||||
this.left + c0 * this.slotSize,
|
||||
this.top + r0 * this.slotSize,
|
||||
(c1 - c0 + 1) * this.slotSize,
|
||||
(r1 - r0 + 1) * this.slotSize);
|
||||
return this.isHidden.test(this.rect);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.util.stream.IntStream;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.FluidContainerRegistry;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
@@ -24,6 +23,7 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeInfo;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -137,7 +137,11 @@ public class ItemCraftablesPanel
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
pendingResult.set(generateCraftables(ItemCraftablesPanel.this.pendingSnapshot));
|
||||
final CraftablesSnapshot snapshot = ItemCraftablesPanel.this.pendingSnapshot;
|
||||
|
||||
if (snapshot != null) {
|
||||
pendingResult.set(generateCraftables(snapshot));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -298,6 +302,11 @@ public class ItemCraftablesPanel
|
||||
}
|
||||
}
|
||||
|
||||
public void clearGuiReferences() {
|
||||
this.lastGuiContainer = null;
|
||||
this.pendingSnapshot = null;
|
||||
}
|
||||
|
||||
private GuiContainer getGuiContainer() {
|
||||
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
|
||||
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : firstGui;
|
||||
@@ -378,48 +387,19 @@ public class ItemCraftablesPanel
|
||||
}
|
||||
|
||||
private static ItemStack getCheapResultItem(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
|
||||
|
||||
if (resultStack != null) {
|
||||
return resultStack.item;
|
||||
}
|
||||
|
||||
final List<PositionedStack> otherStacks = handler.getOtherStacks(recipeIndex);
|
||||
|
||||
for (PositionedStack otherStack : otherStacks) {
|
||||
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|
||||
|| StackInfo.getFluid(otherStack.items[0]) != null) {
|
||||
return otherStack.item;
|
||||
}
|
||||
}
|
||||
|
||||
return otherStacks.isEmpty() ? null : otherStacks.get(0).item;
|
||||
final PositionedStack result = Recipe
|
||||
.getMainResult(handler.getResultStacks(recipeIndex), pStack -> pStack.items[0]);
|
||||
return result != null ? result.item : null;
|
||||
}
|
||||
|
||||
private static ItemStack getCheapResult(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
|
||||
final List<PositionedStack> results = resultStack != null ? Collections.singletonList(resultStack)
|
||||
: handler.getOtherStacks(recipeIndex);
|
||||
final List<PositionedStack> results = handler.getResultStacks(recipeIndex);
|
||||
|
||||
if (results.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStack stack = null;
|
||||
|
||||
if (results.size() > 1) {
|
||||
for (PositionedStack result : results) {
|
||||
if (!FluidContainerRegistry.isContainer(result.item) || StackInfo.getFluid(result.item) != null) {
|
||||
stack = result.item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
stack = results.get(0).item;
|
||||
}
|
||||
|
||||
final ItemStack stack = Recipe.getMainResult(results, pStack -> pStack.items[0]).item;
|
||||
int stackSize = 0;
|
||||
|
||||
for (PositionedStack result : results) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -206,6 +207,33 @@ public class ItemList {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public static ItemStack[] expandPermutations(ItemStack[] items) {
|
||||
List<ItemStack> stacks = new ArrayList<>();
|
||||
for (ItemStack item : items) {
|
||||
if (item == null || item.getItem() == null) continue;
|
||||
|
||||
if (item.getItemDamage() == Short.MAX_VALUE) {
|
||||
List<ItemStack> permutations = itemMap.get(item.getItem());
|
||||
if (!permutations.isEmpty()) {
|
||||
for (ItemStack stack : permutations) {
|
||||
ItemStack toAdd = stack.copy();
|
||||
toAdd.stackSize = item.stackSize;
|
||||
stacks.add(toAdd);
|
||||
}
|
||||
} else {
|
||||
ItemStack base = new ItemStack(item.getItem(), item.stackSize);
|
||||
base.stackTagCompound = item.stackTagCompound;
|
||||
stacks.add(base);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
stacks.add(item.copy());
|
||||
}
|
||||
|
||||
return stacks.toArray(new ItemStack[0]);
|
||||
}
|
||||
|
||||
public static ForkJoinPool forkJoinPool;
|
||||
|
||||
static {
|
||||
|
||||
@@ -36,7 +36,6 @@ import cpw.mods.fml.common.FMLLog;
|
||||
public class ItemListLoader {
|
||||
|
||||
private static final HashSet<Item> erroredItems = new HashSet<>();
|
||||
private static final HashSet<String> stackTraces = new HashSet<>();
|
||||
private static Map<ItemStack, Integer> ordering = new HashMap<>();
|
||||
|
||||
private ItemListLoader() {}
|
||||
@@ -60,11 +59,10 @@ public class ItemListLoader {
|
||||
} catch (TimeoutException t) {
|
||||
throw t;
|
||||
} catch (Throwable t) {
|
||||
NEIServerUtils.logOnce(
|
||||
t,
|
||||
stackTraces,
|
||||
NEIClientUtils.errorOnce(
|
||||
"ItemListLoader: " + item.toString(),
|
||||
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
|
||||
item.toString());
|
||||
t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,14 +118,7 @@ public class ItemSorter implements Comparator<ItemStack> {
|
||||
return name1.compareTo(name2);
|
||||
});
|
||||
tag.getTag("inventory.itemsort").setDefaultValue(getSaveString(list));
|
||||
API.addOption(new OptionOpenGui("inventory.itemsort", GuiItemSorter.class) {
|
||||
|
||||
@Override
|
||||
public void useGlobals() {
|
||||
super.useGlobals();
|
||||
list = fromSaveString(activeTag().getValue());
|
||||
}
|
||||
});
|
||||
API.addOption(new OptionOpenGui("inventory.itemsort", GuiItemSorter.class));
|
||||
}
|
||||
|
||||
public static String getSaveString(List<SortEntry> list) {
|
||||
|
||||
@@ -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 {
|
||||
@@ -121,13 +127,34 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
this.displayName = "Unnamed";
|
||||
}
|
||||
|
||||
this.stack = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0);
|
||||
// Copying the stack copies its whole NBT, so it is only done when the hovered stack changes
|
||||
final boolean sameStack = this.stack != null && this.stack.isItemEqual(stack)
|
||||
&& ItemStack.areItemStackTagsEqual(this.stack, stack);
|
||||
if (!sameStack) {
|
||||
this.stack = StackInfo.withAmount(stack, 0);
|
||||
}
|
||||
} else {
|
||||
this.stack = null;
|
||||
this.displayName = "";
|
||||
}
|
||||
}
|
||||
|
||||
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 +224,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")
|
||||
|
||||
@@ -282,6 +282,8 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
|
||||
protected boolean[] invalidSlotMap;
|
||||
|
||||
private final GridOverlapScanner overlapScanner = new GridOverlapScanner();
|
||||
|
||||
protected ScreenCapture screenCapture = null;
|
||||
|
||||
public ArrayList<ItemStack> getItems() {
|
||||
@@ -359,12 +361,13 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
|
||||
columns = width / SLOT_SIZE;
|
||||
rows = height / SLOT_SIZE;
|
||||
this.columns = newWidth / SLOT_SIZE;
|
||||
this.rows = newHeight / SLOT_SIZE;
|
||||
|
||||
paddingLeft = (width % SLOT_SIZE) / 2;
|
||||
|
||||
if (changed) {
|
||||
this.overlapScanner.invalidate();
|
||||
onGridChanged();
|
||||
}
|
||||
}
|
||||
@@ -405,55 +408,42 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
}
|
||||
|
||||
public void refresh(GuiContainer gui) {
|
||||
final int gridRenderingCacheMode = getGridRenderingCacheMode();
|
||||
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
|
||||
if (this.overlapScanner.shouldRefresh()) {
|
||||
updateGuiOverlapSlots(gui);
|
||||
}
|
||||
shiftPage(0);
|
||||
}
|
||||
|
||||
protected void updateGuiOverlapSlots(GuiContainer gui) {
|
||||
final boolean[] oldSlotMap = invalidSlotMap;
|
||||
final int oldRows = rows;
|
||||
final int oldColumns = columns;
|
||||
final boolean[] oldSlotMap = this.invalidSlotMap;
|
||||
final int size = this.rows * this.columns;
|
||||
final boolean[] slotMap = new boolean[size];
|
||||
|
||||
invalidSlotMap = new boolean[rows * columns];
|
||||
perPage = columns * rows;
|
||||
this.perPage = size;
|
||||
|
||||
if (gui != null) {
|
||||
if (gui != null && size > 0) {
|
||||
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
|
||||
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
|
||||
checkGuiOverlap(gui, handlers, 0, columns - 2, 1);
|
||||
checkGuiOverlap(gui, handlers, columns - 1, 1, -1);
|
||||
} else {
|
||||
checkGuiOverlap(gui, handlers, 0, columns, 1);
|
||||
}
|
||||
final Rectangle4i origin = getSlotRect(0, 0);
|
||||
|
||||
this.perPage -= this.overlapScanner.scan(
|
||||
rect -> GuiInfo.hideItemPanelSlot(gui, rect, handlers),
|
||||
origin.x,
|
||||
origin.y,
|
||||
SLOT_SIZE,
|
||||
this.rows,
|
||||
this.columns,
|
||||
origin.x + this.columns * SLOT_SIZE > gui.guiLeft,
|
||||
origin.x < gui.guiLeft + gui.xSize,
|
||||
slotMap);
|
||||
}
|
||||
|
||||
if (oldRows != rows || oldColumns != columns || !Arrays.equals(oldSlotMap, invalidSlotMap)) {
|
||||
this.invalidSlotMap = slotMap;
|
||||
|
||||
if (!Arrays.equals(oldSlotMap, slotMap)) {
|
||||
onGridChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkGuiOverlap(GuiContainer gui, INEIGuiHandler[] handlers, int start, int end, int dir) {
|
||||
boolean validColumn = false;
|
||||
|
||||
for (int c = start; c != end && (!NEIClientConfig.optimizeGuiOverlapComputation() || !validColumn); c += dir) {
|
||||
validColumn = true;
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
final int idx = columns * r + c;
|
||||
if (idx >= 0 && idx < invalidSlotMap.length
|
||||
&& !invalidSlotMap[idx]
|
||||
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c), handlers)) {
|
||||
invalidSlotMap[idx] = true;
|
||||
validColumn = false;
|
||||
perPage--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T getSlotMouseOver(int mousex, int mousey) {
|
||||
if (!contains(mousex, mousey)) {
|
||||
return null;
|
||||
|
||||
@@ -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;
|
||||
@@ -370,6 +388,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
}
|
||||
|
||||
public static void layout(GuiContainer gui) {
|
||||
GridOverlapScanner.beginFrame(gui, GuiContainerManager.shouldShowTooltip(gui));
|
||||
|
||||
VisiblityData visiblity = new VisiblityData();
|
||||
visiblity.showNEI = !isHidden();
|
||||
visiblity.showBookmarkPanel = !isBookmarkPanelHidden();
|
||||
@@ -427,14 +447,14 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
if (!rightclick) {
|
||||
if (Keyboard.getEventKeyState() && getIsAccessibleControlEventKey()
|
||||
&& NEIClientConfig.canChangeCheatMode()) {
|
||||
NEIClientConfig.cycleSetting("inventory.cheatmode", 3);
|
||||
NEIClientConfig.setCheatMode((NEIClientConfig.getCheatMode() + 1) % 3);
|
||||
} else {
|
||||
if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LSHIFT
|
||||
|| Keyboard.getEventKey() == Keyboard.KEY_RSHIFT)) {
|
||||
GuiRecipeTab.loadHandlerInfo();
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
}
|
||||
getOptionList().openGui(getGuiContainer(), false);
|
||||
getOptionList().openGui(getGuiContainer());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -803,6 +823,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
GridOverlapScanner.requestScan();
|
||||
AutoFocusWidget.instance.guiTick();
|
||||
|
||||
for (Widget widget : drawWidgets) widget.update();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.world.storage.SaveFormatComparator;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
@@ -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;
|
||||
@@ -45,7 +44,9 @@ import codechicken.nei.config.GuiHighlightTips;
|
||||
import codechicken.nei.config.GuiNEIOptionList;
|
||||
import codechicken.nei.config.GuiOptionList;
|
||||
import codechicken.nei.config.GuiPanelSettings;
|
||||
import codechicken.nei.config.OptionButton;
|
||||
import codechicken.nei.config.OptionCycled;
|
||||
import codechicken.nei.config.OptionExportConfig;
|
||||
import codechicken.nei.config.OptionGamemodes;
|
||||
import codechicken.nei.config.OptionIntegerField;
|
||||
import codechicken.nei.config.OptionList;
|
||||
@@ -56,7 +57,8 @@ import codechicken.nei.config.OptionToggleButtonBoubs;
|
||||
import codechicken.nei.config.OptionUtilities;
|
||||
import codechicken.nei.config.preset.GuiPresetList;
|
||||
import codechicken.nei.event.NEIConfigsLoadedEvent;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.moboverlay.MobOverlayScan;
|
||||
import codechicken.nei.moboverlay.MobOverlayStyle;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.recipe.RecipeCatalysts;
|
||||
@@ -69,6 +71,7 @@ public class NEIClientConfig {
|
||||
private static boolean pluginNEIConfigLoaded;
|
||||
private static boolean enabledOverride;
|
||||
private static String worldPath;
|
||||
private static final String CHEAT_MODE_KEY = "cheatmode";
|
||||
|
||||
public static Logger logger = LogManager.getLogger("NotEnoughItems");
|
||||
public static File configDir = new File(CommonUtils.getMinecraftDir(), "config/NEI/");
|
||||
@@ -157,6 +160,16 @@ public class NEIClientConfig {
|
||||
.getIntValue(-1);
|
||||
API.addOption(new OptionCycled("inventory.cheatmode", 3) {
|
||||
|
||||
@Override
|
||||
public int value() {
|
||||
return getCheatMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(int value) {
|
||||
setCheatMode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean optionValid(int index) {
|
||||
return getLockedMode() == -1 || getLockedMode() == index && NEIInfo.isValidMode(index);
|
||||
@@ -200,6 +213,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(
|
||||
@@ -224,6 +242,10 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.bookmarkRecipeWithCount", true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.craftingTreeZoomWithoutModifier")
|
||||
.setComment("Zoom Crafting Tree with mouse wheel without holding Ctrl").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.craftingTreeZoomWithoutModifier", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.jeiStyleTabs").setComment("Enable/disable JEI Style Tabs")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButtonBoubs("inventory.guirecipe.jeiStyleTabs", true));
|
||||
@@ -236,10 +258,6 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.cycledIngredientsTooltip", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.creativeTabStyle").setComment("Creative or JEI style tabs")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.creativeTabStyle", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.itemPresenceOverlay").setComment("Item presence overlay on ?-hover")
|
||||
.getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.guirecipe.itemPresenceOverlay", 3, true));
|
||||
@@ -263,6 +281,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));
|
||||
|
||||
@@ -368,6 +391,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);
|
||||
@@ -416,6 +444,46 @@ public class NEIClientConfig {
|
||||
tag.getTag("world.overlays.lock").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("world.overlays.lock", true));
|
||||
|
||||
tag.getTag("world.overlays.mobOverlayStyle")
|
||||
.setComment("Mark on each block of the mob spawn overlay: " + MobOverlayStyle.formatAllNames())
|
||||
.getValue(MobOverlayStyle.DEFAULT.formatName());
|
||||
API.addOption(new OptionCycled("world.overlays.mobOverlayStyle", MobOverlayStyle.values().length, true) {
|
||||
|
||||
@Override
|
||||
public int value() {
|
||||
return MobOverlayStyle.parseStyle(getTag().getValue()).ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(int value) {
|
||||
getTag().setValue(MobOverlayStyle.values()[value].formatName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getButtonText() {
|
||||
return translateN(
|
||||
"world.overlays.mobOverlayStyle."
|
||||
+ MobOverlayStyle.parseStyle(getTag().getValue()).formatName());
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("world.overlays.mobOverlayRadius")
|
||||
.setComment("Blocks scanned on each side of the player by the mob spawn overlay")
|
||||
.getIntValue(MobOverlayScan.DEFAULT_RADIUS);
|
||||
API.addOption(
|
||||
new OptionIntegerField(
|
||||
"world.overlays.mobOverlayRadius",
|
||||
MobOverlayScan.MIN_RADIUS,
|
||||
MobOverlayScan.MAX_RADIUS));
|
||||
tag.getTag("world.overlays.mobOverlayVerticalRadius")
|
||||
.setComment("Blocks scanned above and below the player by the mob spawn overlay")
|
||||
.getIntValue(MobOverlayScan.DEFAULT_VERTICAL_RADIUS);
|
||||
API.addOption(
|
||||
new OptionIntegerField(
|
||||
"world.overlays.mobOverlayVerticalRadius",
|
||||
MobOverlayScan.MIN_VERTICAL_RADIUS,
|
||||
MobOverlayScan.MAX_VERTICAL_RADIUS));
|
||||
|
||||
tag.getTag("inventory.itempanelScale").getIntValue(100);
|
||||
ItemsPanelGrid.updateScale();
|
||||
API.addOption(new OptionIntegerField("inventory.itempanelScale", 1, 1000) {
|
||||
@@ -472,10 +540,6 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.showItemQuantityWidget").setComment("Show Item Quantity Widget").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.showItemQuantityWidget", true));
|
||||
|
||||
tag.getTag("inventory.optimize_gui_overlap_computation").setComment("Optimize computation for GUI overlap")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.optimize_gui_overlap_computation", true));
|
||||
|
||||
tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.autocrafting", true));
|
||||
|
||||
@@ -497,29 +561,32 @@ public class NEIClientConfig {
|
||||
|
||||
});
|
||||
|
||||
tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) {
|
||||
API.addOption(
|
||||
new OptionButton(
|
||||
"tools.export_configs.all",
|
||||
"tools.export_configs.all",
|
||||
null,
|
||||
"tools.export_configs.all.tip") {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
GuiRecipeTab.loadHandlerInfo();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public String getButtonText() {
|
||||
return translateN("tools.export_configs.button");
|
||||
}
|
||||
|
||||
tag.getTag("tools.catalyst_load_from_config").setComment("ADVANCED: Load catalysts from config")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("tools.catalyst_load_from_config", true) {
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
int exported = 0;
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
for (String resource : ClientHandler.DEFAULT_CONFIGS) {
|
||||
if (ClientHandler.exportDefaultConfig(resource)) exported++;
|
||||
}
|
||||
|
||||
NEIClientUtils.printChatMessage(
|
||||
new ChatComponentTranslation("nei.options.tools.export_configs.all.done", exported));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
ClientHandler.DEFAULT_CONFIGS.forEach(fileName -> API.addOption(new OptionExportConfig(fileName)));
|
||||
|
||||
setDefaultKeyBindings();
|
||||
}
|
||||
@@ -528,23 +595,18 @@ public class NEIClientConfig {
|
||||
OptionList.setOptionList(new OptionList("nei.options") {
|
||||
|
||||
@Override
|
||||
public ConfigSet globalConfigSet() {
|
||||
public ConfigSet configSet() {
|
||||
return global;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSet worldConfigSet() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionList configBase() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
|
||||
return new GuiNEIOptionList(parent, list, world);
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
|
||||
return new GuiNEIOptionList(parent, list);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -911,15 +973,15 @@ public class NEIClientConfig {
|
||||
specificDir.mkdirs();
|
||||
}
|
||||
|
||||
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
|
||||
world = new ConfigSet(new File(specificDir, "NEI.dat"));
|
||||
|
||||
if (newWorld && Minecraft.getMinecraft().isSingleplayer() && getLockedMode() == -1) {
|
||||
final int globalMode = global.config.getTag("inventory.cheatmode").getIntValue();
|
||||
final int globalMode = getCheatMode();
|
||||
final int worldMode = Math
|
||||
.min(globalMode, NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
|
||||
|
||||
if (worldMode != globalMode) {
|
||||
world.config.getTag("inventory.cheatmode").setIntValue(worldMode);
|
||||
setCheatMode(worldMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,9 +995,6 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
private static void onWorldLoad() {
|
||||
world.config.setComment(
|
||||
"World based configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value");
|
||||
|
||||
setWorldDefaults();
|
||||
creativeInv = new ItemStack[54];
|
||||
LayoutManager.searchField.setText(getSearchExpression());
|
||||
@@ -1007,20 +1066,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Stream<Class<?>> stream = NEIClientConfig.getBooleanSetting("loadPluginsInParallel")
|
||||
? 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(
|
||||
NEIClientConfig.pluginsList,
|
||||
NEIClientConfig.getBooleanSetting("loadPluginsInParallel"),
|
||||
NEIModContainer.plugins,
|
||||
logger);
|
||||
|
||||
KeyManager.requestOptionsReload();
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
@@ -1050,14 +1100,8 @@ public class NEIClientConfig {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isWorldSpecific(String setting) {
|
||||
if (world == null) return false;
|
||||
ConfigTag tag = world.config.getTag(setting, false);
|
||||
return tag != null && tag.value != null;
|
||||
}
|
||||
|
||||
public static ConfigTag getSetting(String s) {
|
||||
return isWorldSpecific(s) ? world.config.getTag(s) : global.config.getTag(s);
|
||||
return global.config.getTag(s);
|
||||
}
|
||||
|
||||
public static boolean getBooleanSetting(String s) {
|
||||
@@ -1102,6 +1146,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");
|
||||
}
|
||||
@@ -1142,22 +1190,22 @@ public class NEIClientConfig {
|
||||
return getIntSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
|
||||
}
|
||||
|
||||
public static boolean useCreativeTabStyle() {
|
||||
return getBooleanSetting("inventory.guirecipe.creativeTabStyle");
|
||||
}
|
||||
|
||||
public static boolean ignorePotionOverlap() {
|
||||
return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap");
|
||||
}
|
||||
|
||||
public static boolean optimizeGuiOverlapComputation() {
|
||||
return getBooleanSetting("inventory.optimize_gui_overlap_computation");
|
||||
public static boolean craftingTreeZoomWithoutModifier() {
|
||||
return getBooleanSetting("inventory.bookmarks.craftingTreeZoomWithoutModifier");
|
||||
}
|
||||
|
||||
public static boolean showCycledIngredientsTooltip() {
|
||||
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
|
||||
}
|
||||
|
||||
public static boolean freezeCycleOnHover() {
|
||||
return getBooleanSetting("inventory.guirecipe.freezeCycleOnHover");
|
||||
}
|
||||
|
||||
public static boolean requireShiftForOverlayRecipe() {
|
||||
return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe");
|
||||
}
|
||||
@@ -1170,14 +1218,6 @@ public class NEIClientConfig {
|
||||
return mainNEIConfigLoaded && pluginNEIConfigLoaded;
|
||||
}
|
||||
|
||||
public static boolean loadHandlersFromJar() {
|
||||
return !getBooleanSetting("tools.handler_load_from_config");
|
||||
}
|
||||
|
||||
public static boolean loadCatalystsFromJar() {
|
||||
return !getBooleanSetting("tools.catalyst_load_from_config");
|
||||
}
|
||||
|
||||
public static boolean isProfileRecipeEnabled() {
|
||||
return NEIClientConfig.getBooleanSetting("inventory.guirecipe.profile");
|
||||
}
|
||||
@@ -1191,12 +1231,18 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
public static int getCheatMode() {
|
||||
if (world != null && world.nbt.hasKey(CHEAT_MODE_KEY)) return world.nbt.getInteger(CHEAT_MODE_KEY);
|
||||
return getIntSetting("inventory.cheatmode");
|
||||
}
|
||||
|
||||
public static void setCheatMode(int mode) {
|
||||
if (world != null) world.nbt.setInteger(CHEAT_MODE_KEY, mode);
|
||||
else setIntSetting("inventory.cheatmode", mode);
|
||||
}
|
||||
|
||||
public static boolean canChangeCheatMode() {
|
||||
if (getLockedMode() != -1) {
|
||||
setIntSetting("inventory.cheatmode", getLockedMode());
|
||||
setCheatMode(getLockedMode());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,12 @@ import static codechicken.nei.NEIClientConfig.invCreativeMode;
|
||||
import static codechicken.nei.NEIClientConfig.world;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.Files;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.MessageFormat;
|
||||
@@ -26,6 +29,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -64,6 +68,8 @@ import codechicken.nei.util.NEIKeyboardUtils;
|
||||
|
||||
public class NEIClientUtils extends NEIServerUtils {
|
||||
|
||||
private static final Set<String> REPORTED = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static class Alignment {
|
||||
|
||||
public final int x, y;
|
||||
@@ -639,4 +645,29 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
if (!isAlphaTest) GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
}
|
||||
|
||||
public static void errorOnce(String key, String message, Throwable error) {
|
||||
if (REPORTED.add(key)) {
|
||||
NEIClientConfig.logger.error(message, error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void warnOnce(String key, String message) {
|
||||
if (REPORTED.add(key)) {
|
||||
NEIClientConfig.logger.warn(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean backupFile(File file) {
|
||||
final File backup = new File(file.getAbsolutePath() + ".backup-" + System.currentTimeMillis());
|
||||
|
||||
try {
|
||||
Files.copy(file.toPath(), backup.toPath());
|
||||
NEIClientConfig.logger.info("Backed up file {} to {}", file, backup);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to backup file {} to {}", file, backup, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -4,12 +4,9 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
@@ -509,20 +506,6 @@ public class NEIServerUtils {
|
||||
return sb;
|
||||
}
|
||||
|
||||
public static void logOnce(Throwable t, Set<String> stackTraces, String message) {
|
||||
logOnce(t, stackTraces, message, "");
|
||||
}
|
||||
|
||||
public static void logOnce(Throwable t, Set<String> stackTraces, String message, String identifier) {
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
String stackTrace = identifier + sw.toString();
|
||||
if (!stackTraces.contains(stackTrace)) {
|
||||
NEIServerConfig.logger.error(message, t);
|
||||
stackTraces.add(stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack getModdedItem(String itemId, String nbtString) {
|
||||
int meta = -1;
|
||||
ItemStack itemStack;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,9 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
|
||||
|
||||
protected T grid;
|
||||
|
||||
private int headerPaddingLeft;
|
||||
private int headerPaddingRight;
|
||||
|
||||
public ArrayList<ItemStack> getItems() {
|
||||
return this.grid.getItems();
|
||||
}
|
||||
@@ -105,26 +108,15 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
|
||||
|
||||
protected int resizeHeader(GuiContainer gui) {
|
||||
final int BUTTON_SIZE = 16;
|
||||
int paddingLeft = 0;
|
||||
int paddingRight = 0;
|
||||
|
||||
while (paddingLeft < w && GuiInfo.hideItemPanelSlot(
|
||||
gui,
|
||||
new Rectangle4i(x + paddingLeft, y, ItemsGrid.SLOT_SIZE, ItemsGrid.SLOT_SIZE))) {
|
||||
paddingLeft += ItemsGrid.SLOT_SIZE;
|
||||
if (GridOverlapScanner.isScanFrame()) {
|
||||
updateHeaderPadding(gui);
|
||||
}
|
||||
|
||||
while (paddingRight < w && GuiInfo.hideItemPanelSlot(
|
||||
gui,
|
||||
new Rectangle4i(
|
||||
x + w - paddingRight - ItemsGrid.SLOT_SIZE,
|
||||
y,
|
||||
ItemsGrid.SLOT_SIZE,
|
||||
ItemsGrid.SLOT_SIZE))) {
|
||||
paddingRight += ItemsGrid.SLOT_SIZE;
|
||||
}
|
||||
final int paddingLeft = this.headerPaddingLeft;
|
||||
final int paddingRight = this.headerPaddingRight;
|
||||
|
||||
if (paddingLeft + paddingRight >= w) {
|
||||
if (paddingLeft + paddingRight >= this.w) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -132,16 +124,35 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
|
||||
pagePrev.h = pageNext.h = BUTTON_SIZE;
|
||||
pagePrev.y = pageNext.y = y;
|
||||
|
||||
pagePrev.x = x + paddingLeft;
|
||||
pageNext.x = x + w - pageNext.w - paddingRight;
|
||||
pagePrev.x = this.x + paddingLeft;
|
||||
pageNext.x = this.x + this.w - pageNext.w - paddingRight;
|
||||
|
||||
pageLabel.x = x + paddingLeft + (w - paddingLeft - paddingRight) / 2;
|
||||
pageLabel.y = y + 5;
|
||||
pageLabel.x = this.x + paddingLeft + (this.w - paddingLeft - paddingRight) / 2;
|
||||
pageLabel.y = this.y + 5;
|
||||
pageLabel.text = getLabelText();
|
||||
|
||||
return BUTTON_SIZE + 2;
|
||||
}
|
||||
|
||||
private void updateHeaderPadding(GuiContainer gui) {
|
||||
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
|
||||
final Rectangle4i slot = new Rectangle4i(this.x, this.y, ItemsGrid.SLOT_SIZE, ItemsGrid.SLOT_SIZE);
|
||||
this.headerPaddingLeft = 0;
|
||||
this.headerPaddingRight = 0;
|
||||
|
||||
while (this.headerPaddingLeft < this.w && GuiInfo.hideItemPanelSlot(gui, slot, handlers)) {
|
||||
this.headerPaddingLeft += ItemsGrid.SLOT_SIZE;
|
||||
slot.x += ItemsGrid.SLOT_SIZE;
|
||||
}
|
||||
|
||||
slot.x = this.x + this.w - ItemsGrid.SLOT_SIZE;
|
||||
|
||||
while (this.headerPaddingRight < this.w && GuiInfo.hideItemPanelSlot(gui, slot, handlers)) {
|
||||
this.headerPaddingRight += ItemsGrid.SLOT_SIZE;
|
||||
slot.x -= ItemsGrid.SLOT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int resizeFooter(GuiContainer gui);
|
||||
|
||||
public void setVisible() {
|
||||
@@ -305,7 +316,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,66 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
|
||||
final class PluginLoader {
|
||||
|
||||
private PluginLoader() {}
|
||||
|
||||
static void load(Collection<Class<?>> plugins, boolean parallel, Collection<IConfigureNEI> loadedPlugins,
|
||||
Logger logger) {
|
||||
if (parallel && canSetContextClassLoader()) {
|
||||
// Common pool workers use the system loader, which cannot see FML-only classes (old Log4j then throws
|
||||
// while logging a plugin's stack frames), and under a SecurityManager they forbid changing it.
|
||||
final ClassLoader classLoader = IConfigureNEI.class.getClassLoader();
|
||||
final ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), owner -> {
|
||||
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(owner);
|
||||
worker.setContextClassLoader(classLoader);
|
||||
return worker;
|
||||
}, null, false);
|
||||
|
||||
try {
|
||||
pool.submit(() -> plugins.parallelStream().forEach(clazz -> load(clazz, loadedPlugins, logger))).join();
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
}
|
||||
} else {
|
||||
if (parallel) {
|
||||
logger.warn("Context class loader cannot be changed, loading plugins sequentially");
|
||||
}
|
||||
|
||||
plugins.forEach(clazz -> load(clazz, loadedPlugins, logger));
|
||||
}
|
||||
}
|
||||
|
||||
private static void load(Class<?> clazz, Collection<IConfigureNEI> loadedPlugins, Logger logger) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canSetContextClassLoader() {
|
||||
final SecurityManager securityManager = System.getSecurityManager();
|
||||
|
||||
if (securityManager == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
securityManager.checkPermission(new RuntimePermission("setContextClassLoader"));
|
||||
return true;
|
||||
} catch (SecurityException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -14,12 +14,8 @@ import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.item.FluidDrawer;
|
||||
import codechicken.nei.item.FluidDrawer.FillDirection;
|
||||
@@ -70,31 +66,11 @@ public class PositionedStack implements Cloneable {
|
||||
public void generatePermutations() {
|
||||
if (permutated) return;
|
||||
|
||||
List<ItemStack> stacks = new ArrayList<>();
|
||||
for (ItemStack item : items) {
|
||||
if (item == null || item.getItem() == null) continue;
|
||||
items = ItemList.expandPermutations(items);
|
||||
|
||||
if (item.getItemDamage() == Short.MAX_VALUE) {
|
||||
List<ItemStack> permutations = ItemList.itemMap.get(item.getItem());
|
||||
if (!permutations.isEmpty()) {
|
||||
for (ItemStack stack : permutations) {
|
||||
ItemStack toAdd = stack.copy();
|
||||
toAdd.stackSize = item.stackSize;
|
||||
stacks.add(toAdd);
|
||||
}
|
||||
} else {
|
||||
ItemStack base = new ItemStack(item.getItem(), item.stackSize);
|
||||
base.stackTagCompound = item.stackTagCompound;
|
||||
stacks.add(base);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
stacks.add(item.copy());
|
||||
if (items.length == 0) {
|
||||
items = new ItemStack[] { new ItemStack(Blocks.fire) };
|
||||
}
|
||||
items = stacks.toArray(new ItemStack[0]);
|
||||
|
||||
if (items.length == 0) items = new ItemStack[] { new ItemStack(Blocks.fire) };
|
||||
|
||||
permutated = true;
|
||||
setPermutationToRender(0);
|
||||
@@ -163,7 +139,7 @@ public class PositionedStack implements Cloneable {
|
||||
}
|
||||
|
||||
public List<ItemStack> getFilteredPermutations(ItemFilter additionalFilter) {
|
||||
List<ItemStack> items = Arrays.asList(this.items);
|
||||
List<ItemStack> items = new ArrayList<>(Arrays.asList(this.items));
|
||||
|
||||
items = filteringPermutations(items, item -> !ItemInfo.isHidden(item));
|
||||
items = filteringPermutations(items, PresetsList.getItemFilter());
|
||||
@@ -263,7 +239,6 @@ public class PositionedStack implements Cloneable {
|
||||
protected int capacity = 0;
|
||||
protected boolean flowingTexture = false;
|
||||
protected FillDirection fillDirection = FillDirection.AUTO;
|
||||
protected DrawableResource overlay;
|
||||
|
||||
private ItemStack cachedFluidItem;
|
||||
private FluidStack cachedFluidStack;
|
||||
@@ -310,19 +285,6 @@ public class PositionedStack implements Cloneable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DrawableResource getOverlay() {
|
||||
return this.overlay;
|
||||
}
|
||||
|
||||
public Tank setOverlay(String resourceLocation, int u, int v, int width, int height) {
|
||||
return setOverlay(new DrawableBuilder(resourceLocation, u, v, width, height).build());
|
||||
}
|
||||
|
||||
public Tank setOverlay(DrawableResource overlay) {
|
||||
this.overlay = overlay;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected FluidStack getFluidStack() {
|
||||
|
||||
if (this.item != this.cachedFluidItem) {
|
||||
@@ -339,7 +301,7 @@ public class PositionedStack implements Cloneable {
|
||||
final List<String> customTooltip = super.getTooltip();
|
||||
final FluidStack fluidStack = getFluidStack();
|
||||
|
||||
if (fluidStack != null) {
|
||||
if (fluidStack != null && this.capacity > 0) {
|
||||
tooltip.add(
|
||||
EnumChatFormatting.GRAY + NEIClientUtils
|
||||
.translate("recipe.fluid.tank.amount", NEIClientUtils.formatFluid(fluidStack.amount)));
|
||||
@@ -361,7 +323,7 @@ public class PositionedStack implements Cloneable {
|
||||
return;
|
||||
}
|
||||
|
||||
FluidDrawer.drawFluid(
|
||||
FluidDrawer.drawTank(
|
||||
this.relx,
|
||||
this.rely,
|
||||
this.width,
|
||||
@@ -370,15 +332,6 @@ public class PositionedStack implements Cloneable {
|
||||
fluidStack,
|
||||
this.flowingTexture,
|
||||
this.fillDirection);
|
||||
|
||||
if (this.overlay != null) {
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
this.overlay.draw(this.relx, this.rely);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,19 +89,19 @@ public class PresetsList {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PositionedStack result = handler.getResultStack(recipeIndex);
|
||||
final List<PositionedStack> outputs = handler.getResultStacks(recipeIndex);
|
||||
|
||||
if (result != null && matchPositionedStack(result)) {
|
||||
if (matchPositionedStack(outputs, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
|
||||
final List<PositionedStack> auxiliaries = handler.getAuxiliaryStacks(recipeIndex);
|
||||
|
||||
if (!others.isEmpty() && matchPositionedStack(others, true)) {
|
||||
if (matchPositionedStack(auxiliaries, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return result == null && others.isEmpty();
|
||||
return outputs.isEmpty() && auxiliaries.isEmpty();
|
||||
}
|
||||
|
||||
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
|
||||
|
||||
@@ -24,7 +24,13 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
|
||||
field.setMaxStringLength(maxSearchLength);
|
||||
field.setCursorPositionZero();
|
||||
field.setSkipRecipeTokens(true);
|
||||
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
|
||||
field.setFormatter(new SearchTextFormatter(SearchField.searchParser) {
|
||||
|
||||
@Override
|
||||
protected String getContextToken() {
|
||||
return this.searchParser.getRecipeContextTokens();
|
||||
}
|
||||
});
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@@ -48,15 +54,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
|
||||
|
||||
@@ -20,6 +20,10 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
this.contextToken = contextToken;
|
||||
}
|
||||
|
||||
protected String getContextToken() {
|
||||
return this.contextToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(String text) {
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
@@ -32,13 +36,14 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
|
||||
} else {
|
||||
final StringBuilder formattedText = new StringBuilder();
|
||||
final String contextToken = getContextToken();
|
||||
|
||||
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
|
||||
final String filterText = orQuery[1];
|
||||
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
|
||||
|
||||
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
|
||||
for (String[] contextPart : searchParser.splitByDelimiters(filterText, contextToken, false)) {
|
||||
|
||||
if (!contextPart[0].isEmpty()) {
|
||||
formattedText.append(
|
||||
@@ -74,7 +79,11 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||
}
|
||||
|
||||
if (!token.rawText.isEmpty()) {
|
||||
if (patternMode == 1 && !token.quotes && isRegexToken(token.rawText)) {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + token.rawText.substring(0, 2))
|
||||
.append(tokenColor + token.rawText.substring(2, token.rawText.length() - 1))
|
||||
.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + "/");
|
||||
} else if (!token.rawText.isEmpty()) {
|
||||
formattedText.append(tokenColor + token.rawText);
|
||||
}
|
||||
|
||||
@@ -93,4 +102,8 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
return formattedText.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRegexToken(String search) {
|
||||
return search.length() >= 3 && search.regionMatches(true, 0, "r/", 0, 2) && search.endsWith("/");
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,7 @@ public class SearchTokenParser {
|
||||
if (patternMode != 3) {
|
||||
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
|
||||
final List<ItemFilter> searchTokens = new ArrayList<>();
|
||||
final String contextTokens = skipRecipeTokens ? getRecipeContextTokens() : "";
|
||||
|
||||
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
|
||||
|
||||
@@ -203,9 +204,10 @@ public class SearchTokenParser {
|
||||
|
||||
final List<ItemFilter> tokens = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
|
||||
for (String[] contextQuery : splitByDelimiters(subQuery[1], contextTokens, false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
|
||||
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")
|
||||
|| contextQuery[0].startsWith("^")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -277,7 +279,7 @@ public class SearchTokenParser {
|
||||
|
||||
final List<IRecipeFilter> contextList = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
|
||||
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], getRecipeContextTokens(), false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty()) {
|
||||
continue;
|
||||
@@ -436,7 +438,9 @@ public class SearchTokenParser {
|
||||
|
||||
final List<String[]> tokens = new ArrayList<>();
|
||||
final int length = input.length();
|
||||
final boolean regexTokens = NEIClientConfig.getIntSetting("inventory.search.patternMode") == 1;
|
||||
boolean insideQuotes = false;
|
||||
boolean insideRegex = false;
|
||||
String token = "";
|
||||
int lastEnd = 0;
|
||||
int index = 0;
|
||||
@@ -444,8 +448,17 @@ public class SearchTokenParser {
|
||||
while (index < length) {
|
||||
final char ch = input.charAt(index);
|
||||
|
||||
if (insideRegex) {
|
||||
if (ch == '/' && input.charAt(index - 1) != '\\') {
|
||||
insideRegex = false;
|
||||
}
|
||||
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -464,6 +477,9 @@ public class SearchTokenParser {
|
||||
|
||||
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
|
||||
insideQuotes = !insideQuotes;
|
||||
} else if (regexTokens && !insideQuotes && isRegexStart(input, index)) {
|
||||
insideRegex = true;
|
||||
index++;
|
||||
}
|
||||
|
||||
index++;
|
||||
@@ -474,6 +490,12 @@ public class SearchTokenParser {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private static boolean isRegexStart(String input, int index) {
|
||||
return Character.toLowerCase(input.charAt(index)) == 'r' && index + 1 < input.length()
|
||||
&& input.charAt(index + 1) == '/'
|
||||
&& (index == 0 || !Character.isLetterOrDigit(input.charAt(index - 1)));
|
||||
}
|
||||
|
||||
public String getPrefixes() {
|
||||
if (this.prefixes == null) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
@@ -490,6 +512,11 @@ public class SearchTokenParser {
|
||||
return this.prefixes;
|
||||
}
|
||||
|
||||
public String getRecipeContextTokens() {
|
||||
// '^' is the start anchor and the negated class in regex mode
|
||||
return NEIClientConfig.getIntSetting("inventory.search.patternMode") == 2 ? "<>" : "<>^";
|
||||
}
|
||||
|
||||
public char getRedefinedPrefix(char prefix) {
|
||||
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -51,6 +55,10 @@ public abstract class Widget {
|
||||
return bounds().contains(px, py);
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ItemStack getStackMouseOver(int mx, int my) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class WidgetContainer extends Widget {
|
||||
final Rectangle4i bounds = bounds();
|
||||
|
||||
for (Widget widget : this.widgets) {
|
||||
if (widget.bounds().intersects(bounds)) {
|
||||
if (widget.isVisible() && widget.bounds().intersects(bounds)) {
|
||||
widget.draw(mousex, mousey);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.contains(mousex, mousey)) {
|
||||
if (widget.isVisible() && widget.contains(mousex, mousey)) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,8 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (!widget.contains(mousex, mousey) && widget.handleClickExt(mousex, mousey, button)) {
|
||||
if (widget.isVisible() && !widget.contains(mousex, mousey)
|
||||
&& widget.handleClickExt(mousex, mousey, button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +127,7 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.mouseDragged(mousex, mousey, button, heldTime);
|
||||
if (widget.isVisible()) widget.mouseDragged(mousex, mousey, button, heldTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.mouseUp(mousex, mousey, button);
|
||||
if (widget.isVisible()) widget.mouseUp(mousex, mousey, button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.handleKeyPress(keyCode, keyChar)) {
|
||||
if (widget.isVisible() && widget.handleKeyPress(keyCode, keyChar)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -157,11 +158,24 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.lastKeyTyped(keyID, keyChar);
|
||||
if (widget.isVisible()) widget.lastKeyTyped(keyID, keyChar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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;
|
||||
@@ -214,7 +228,7 @@ public class WidgetContainer extends Widget {
|
||||
public void postDraw(int mousex, int mousey) {
|
||||
|
||||
for (Widget widget : this.widgets) {
|
||||
widget.postDraw(mousex, mousey);
|
||||
if (widget.isVisible()) widget.postDraw(mousex, mousey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -224,7 +238,7 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.onGuiClick(mousex, mousey);
|
||||
if (widget.isVisible()) widget.onGuiClick(mousex, mousey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -240,6 +254,17 @@ public class WidgetContainer extends Widget {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (widget != null) {
|
||||
return widget.handleItemTooltip(itemstack, mousex, mousey, tooltip);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
@@ -4,27 +4,22 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockSnow;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.SpawnerAnimals;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.cardinalstar.cubicchunks.world.api.IMinMaxHeight;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
import codechicken.nei.KeyManager.IKeyStateTracker;
|
||||
import codechicken.nei.moboverlay.MobOverlayDisplayList;
|
||||
import codechicken.nei.moboverlay.MobOverlayFace;
|
||||
import codechicken.nei.moboverlay.MobOverlayScan;
|
||||
import codechicken.nei.moboverlay.MobOverlayStyle;
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
|
||||
public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
@@ -32,14 +27,20 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
public static int chunkOverlay = 0;
|
||||
|
||||
public static int mobOverlay = 0;
|
||||
private static byte[] mobSpawnCache;
|
||||
private static long mobOverlayUpdateTime;
|
||||
private static final MobOverlayScan MOB_OVERLAY_SCAN = new MobOverlayScan();
|
||||
private static final MobOverlayFace MARKED_FACE = new MobOverlayFace();
|
||||
private static final MobOverlayDisplayList MOB_OVERLAY_LIST = new MobOverlayDisplayList();
|
||||
|
||||
public static void reset() {
|
||||
mobOverlay = 0;
|
||||
chunkOverlay = 0;
|
||||
}
|
||||
|
||||
public static void onWorldUnload() {
|
||||
MOB_OVERLAY_SCAN.clear();
|
||||
MOB_OVERLAY_LIST.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickKeyStates() {
|
||||
if (Minecraft.getMinecraft().currentScreen != null) return;
|
||||
@@ -82,158 +83,97 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
GL11.glPopAttrib();
|
||||
}
|
||||
|
||||
private static void buildMobSpawnOverlay(Entity entity) {
|
||||
if (mobSpawnCache == null) {
|
||||
mobSpawnCache = new byte[33 * 33 * 33]; // 35kB
|
||||
}
|
||||
|
||||
World world = entity.worldObj;
|
||||
int worldHeight = world.getHeight();
|
||||
|
||||
int x1 = (int) entity.posX;
|
||||
int z1 = (int) entity.posZ;
|
||||
int y1 = (int) MathHelper.clip(entity.posY, 16, worldHeight - 16);
|
||||
|
||||
for (int i = 0; i <= 32; i++) {
|
||||
int x = x1 - 16 + i;
|
||||
for (int j = 0; j <= 32; j++) {
|
||||
int z = z1 - 16 + j;
|
||||
int bufIndex = (i * 33 + j) * 33;
|
||||
|
||||
Chunk chunk = world.getChunkFromBlockCoords(x, z);
|
||||
BiomeGenBase biome = world.getBiomeGenForCoords(x, z);
|
||||
if (biome.getSpawnableList(EnumCreatureType.monster).isEmpty() || biome.getSpawningChance() <= 0) {
|
||||
mobSpawnCache[bufIndex] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int maxHeight = chunk.worldObj.getHeightValue(x, z);
|
||||
|
||||
for (int k = 0; k <= 32; k++) {
|
||||
int y = y1 - 16 + k;
|
||||
|
||||
if (y >= worldHeight) {
|
||||
mobSpawnCache[bufIndex + k] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (y > maxHeight) {
|
||||
mobSpawnCache[bufIndex + k] = -1;
|
||||
break;
|
||||
}
|
||||
mobSpawnCache[bufIndex + k] = getSpawnMode(chunk, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double getOverlayYOffset(World world, int x, int y, int z) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
if (block instanceof BlockSnow) {
|
||||
int meta = world.getBlockMetadata(x, y, z) & 7;
|
||||
return (1 + meta) / 8.0 + 0.02;
|
||||
}
|
||||
return 0.02;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model view matrix is relative to ({@code intOffsetX}, {@code intOffsetY}, {@code intOffsetZ}), the camera
|
||||
* position cast to {@code int}.
|
||||
*/
|
||||
private static void renderMobSpawnOverlay(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
|
||||
if (mobOverlay == 0) {
|
||||
if (mobSpawnCache != null) {
|
||||
mobSpawnCache = null;
|
||||
}
|
||||
MOB_OVERLAY_SCAN.clear();
|
||||
MOB_OVERLAY_LIST.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
long worldTime = entity.worldObj.getTotalWorldTime();
|
||||
if (mobSpawnCache == null || mobOverlayUpdateTime != worldTime) {
|
||||
mobOverlayUpdateTime = worldTime;
|
||||
buildMobSpawnOverlay(entity);
|
||||
MOB_OVERLAY_SCAN.update(
|
||||
entity,
|
||||
NEIClientConfig.getIntSetting("world.overlays.mobOverlayRadius"),
|
||||
NEIClientConfig.getIntSetting("world.overlays.mobOverlayVerticalRadius"));
|
||||
final MobOverlayStyle style = MobOverlayStyle
|
||||
.parseStyle(NEIClientConfig.getSetting("world.overlays.mobOverlayStyle").getValue());
|
||||
final long worldTicks = entity.worldObj.getTotalWorldTime();
|
||||
if (MOB_OVERLAY_LIST.isCompileDue(MOB_OVERLAY_SCAN.getModesVersion(), style, worldTicks)) {
|
||||
compileMobOverlayList(style, worldTicks);
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
beginMobOverlayDraw(style);
|
||||
MOB_OVERLAY_LIST.draw(intOffsetX, intOffsetY, intOffsetZ);
|
||||
endMobOverlayDraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a marked face to {@link #MOB_OVERLAY_LIST} for each block of {@link #MOB_OVERLAY_SCAN} where mobs can spawn.
|
||||
*/
|
||||
private static void compileMobOverlayList(MobOverlayStyle style, long worldTicks) {
|
||||
MOB_OVERLAY_LIST.beginCompile(MOB_OVERLAY_SCAN, style, worldTicks);
|
||||
final float alpha = style.computeAlpha();
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
tess.startDrawing(style.glMode);
|
||||
final int span = MOB_OVERLAY_SCAN.getSpan();
|
||||
for (int i = 0; i < span; i++) {
|
||||
for (int j = 0; j < span; j++) {
|
||||
for (int k = 0; k < MOB_OVERLAY_SCAN.getHeight(); k++) {
|
||||
if (MOB_OVERLAY_SCAN.readSpawnMode(i, k, j) == 0) continue;
|
||||
|
||||
MARKED_FACE.moveTo(i, k + MOB_OVERLAY_SCAN.readOverlayYOffset(i, k, j), j);
|
||||
loadNeighbourModes(i, k, j);
|
||||
MARKED_FACE.applyColor(style.colouring, alpha);
|
||||
style.addVertices(MARKED_FACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
tess.draw();
|
||||
MOB_OVERLAY_LIST.endCompile();
|
||||
}
|
||||
|
||||
private static void beginMobOverlayDraw(MobOverlayStyle style) {
|
||||
final ResourceLocation texture = style.texture;
|
||||
if (texture == null) {
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
} else {
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
|
||||
}
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glLineWidth(1.5F);
|
||||
// the outline of `MobOverlayStyle.CONNECTED` lies in the plane of the tint. With depth writes on, the depth
|
||||
// test discards most of the outline
|
||||
GL11.glDepthMask(false);
|
||||
}
|
||||
|
||||
int curSpawnMode = 2;
|
||||
|
||||
World world = entity.worldObj;
|
||||
int x1 = (int) entity.posX - intOffsetX;
|
||||
int z1 = (int) entity.posZ - intOffsetZ;
|
||||
int y1 = (int) MathHelper.clip(entity.posY, 16, world.getHeight() - 16) - intOffsetY;
|
||||
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
tess.startDrawing(GL11.GL_LINES);
|
||||
tess.setColorOpaque(255, 0, 0);
|
||||
|
||||
for (int i = 0; i <= 32; i++) {
|
||||
int x = x1 - 16 + i;
|
||||
for (int j = 0; j <= 32; j++) {
|
||||
int z = z1 - 16 + j;
|
||||
int cacheIndex = (i * 33 + j) * 33;
|
||||
|
||||
for (int k = 0; k <= 32; k++) {
|
||||
int y = y1 - 16 + k;
|
||||
int spawnMode = mobSpawnCache[cacheIndex + k];
|
||||
if (spawnMode == 0) {
|
||||
continue;
|
||||
} else if (spawnMode == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (spawnMode != curSpawnMode) {
|
||||
if (spawnMode == 1) {
|
||||
tess.setColorOpaque(255, 255, 0);
|
||||
} else {
|
||||
tess.setColorOpaque(255, 0, 0);
|
||||
}
|
||||
curSpawnMode = spawnMode;
|
||||
}
|
||||
|
||||
double yOff = getOverlayYOffset(world, x + intOffsetX, y + intOffsetY, z + intOffsetZ);
|
||||
|
||||
tess.addVertex(x, y + yOff, z);
|
||||
tess.addVertex(x + 1, y + yOff, z + 1);
|
||||
tess.addVertex(x + 1, y + yOff, z);
|
||||
tess.addVertex(x, y + yOff, z + 1);
|
||||
}
|
||||
private static void loadNeighbourModes(int i, int k, int j) {
|
||||
final int span = MOB_OVERLAY_SCAN.getSpan();
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
final int neighbourI = i + offsetX;
|
||||
final int neighbourJ = j + offsetZ;
|
||||
final boolean isInScan = neighbourI >= 0 && neighbourI < span && neighbourJ >= 0 && neighbourJ < span;
|
||||
final byte spawnMode = isInScan ? MOB_OVERLAY_SCAN.readSpawnMode(neighbourI, k, neighbourJ)
|
||||
: MobOverlayFace.OUTSIDE_SCAN;
|
||||
MARKED_FACE.setNeighbourMode(offsetX, offsetZ, spawnMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tess.draw();
|
||||
/** Undo the GL state changes of {@link #beginMobOverlayDraw}, except the texture binding. */
|
||||
private static void endMobOverlayDraw() {
|
||||
GL11.glDepthMask(true);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
private static final Entity dummyEntity = new EntityPig(null);
|
||||
private static final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
|
||||
|
||||
private static byte getSpawnMode(Chunk chunk, int x, int y, int z) {
|
||||
if (y >= chunk.worldObj.getHeight()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8
|
||||
|| !SpawnerAnimals.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aabb.minX = x + 0.2;
|
||||
aabb.maxX = x + 0.8;
|
||||
aabb.minY = y + 0.01;
|
||||
aabb.maxY = y + 1.8;
|
||||
aabb.minZ = z + 0.2;
|
||||
aabb.maxZ = z + 0.8;
|
||||
if (!chunk.worldObj.getCollidingBoundingBoxes(dummyEntity, aabb).isEmpty()
|
||||
|| chunk.worldObj.isAnyLiquid(aabb)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (byte) (chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15) >= 8 ? 1 : 2);
|
||||
}
|
||||
|
||||
private static void renderChunkBounds(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
|
||||
if (chunkOverlay == 0) return;
|
||||
|
||||
|
||||
@@ -12,12 +12,14 @@ 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;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import codechicken.nei.BookmarkContainerInfo;
|
||||
import codechicken.nei.CollapsibleItems;
|
||||
import codechicken.nei.ItemList;
|
||||
import codechicken.nei.ItemSorter;
|
||||
import codechicken.nei.ItemStackSet;
|
||||
@@ -33,6 +35,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 +181,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)
|
||||
@@ -337,6 +342,30 @@ public class API {
|
||||
SubsetWidget.addTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a group of items that is collapsed into a single entry in the item panel. Groups registered here have
|
||||
* the lowest priority: an item that matches a preset or a collapsibleitems.cfg group stays there. They are active
|
||||
* only while the Collapsible Items option is enabled. Must be called while plugins are loading.
|
||||
*
|
||||
* @param filter A search string for matching items that belong to this group. Also identifies the group when its
|
||||
* expanded state is saved, so changing it resets the saved state.
|
||||
* @return A handle to optionally set the name shown for the group
|
||||
*/
|
||||
public static CollapsibleItems.ApiGroup addCollapsibleGroup(String filter) {
|
||||
return CollapsibleItems.registerGroup(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #addCollapsibleGroup(String)}, but the group is given as a list of item stacks. The stacks
|
||||
* identify the group when its expanded state is saved, so changing them resets the saved state.
|
||||
*
|
||||
* @param items Item stacks that belong to this group
|
||||
* @return A handle to optionally set the name shown for the group
|
||||
*/
|
||||
public static CollapsibleItems.ApiGroup addCollapsibleGroup(Iterable<ItemStack> items) {
|
||||
return CollapsibleItems.registerGroup(items);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void addSearchProvider(ISearchProvider provider) {}
|
||||
|
||||
@@ -375,6 +404,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);
|
||||
}
|
||||
@@ -59,6 +59,7 @@ import codechicken.nei.SearchTokenParser.SearchMode;
|
||||
import codechicken.nei.config.ArrayDumper;
|
||||
import codechicken.nei.config.HandlerDumper;
|
||||
import codechicken.nei.config.ItemPanelDumper;
|
||||
import codechicken.nei.config.RecipeDumper;
|
||||
import codechicken.nei.config.RegistryDumper;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.item.FluidDisplayRenderer;
|
||||
@@ -284,6 +285,7 @@ public class ItemInfo {
|
||||
});
|
||||
API.addOption(new ItemPanelDumper("tools.dump.itempanel"));
|
||||
API.addOption(new HandlerDumper("tools.dump.handlers"));
|
||||
API.addOption(new RecipeDumper("tools.dump.recipes"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -333,12 +335,6 @@ public class ItemInfo {
|
||||
}
|
||||
|
||||
private static void addVanillaBlockProperties() {
|
||||
API.setOverrideName(new ItemStack(Blocks.flowing_water), "Water Source");
|
||||
API.setOverrideName(new ItemStack(Blocks.water), "Water Still");
|
||||
API.setOverrideName(new ItemStack(Blocks.flowing_lava), "Lava Source");
|
||||
API.setOverrideName(new ItemStack(Blocks.lava), "Lava Still");
|
||||
API.setOverrideName(new ItemStack(Blocks.end_portal), "End Portal");
|
||||
API.setOverrideName(new ItemStack(Blocks.end_portal_frame), "End Portal Frame");
|
||||
API.hideItem(new ItemStack(Blocks.double_stone_slab, 1, OreDictionary.WILDCARD_VALUE));
|
||||
API.hideItem(new ItemStack(Blocks.double_wooden_slab, 1, OreDictionary.WILDCARD_VALUE));
|
||||
API.hideItem(new ItemStack(Blocks.carrots));
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.LinkedList;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.config.GuiOptionList;
|
||||
import codechicken.nei.config.OptionCycled;
|
||||
|
||||
public class NEIInfo {
|
||||
@@ -14,11 +13,7 @@ public class NEIInfo {
|
||||
|
||||
public static void load(World world) {
|
||||
OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode");
|
||||
final boolean worldConfig = NEIClientConfig.isWorldSpecific("inventory.cheatmode");
|
||||
// kept alive so the WeakReference in Option.slotRef isn't GC'd before copyGlobals/cycle use it
|
||||
GuiOptionList env = modeOption.parent.synthesizeEnvironment(worldConfig);
|
||||
if (!modeOption.optionValid(modeOption.value())) {
|
||||
modeOption.copyGlobals();
|
||||
modeOption.cycle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -343,7 +343,6 @@ public abstract class ShortcutInputHandler {
|
||||
if (handlerRef != null) {
|
||||
final GuiFavoriteButton button = new GuiFavoriteButton(handlerRef, 0, 0);
|
||||
button.saveRecipeInBookmark();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -415,41 +414,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 +524,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 +726,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();
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.ItemSorter;
|
||||
@@ -132,7 +131,7 @@ public class BookmarkGridGenerator {
|
||||
itemIndex++;
|
||||
}
|
||||
|
||||
} else if (group.crafting != null && group.crafting.itemToRecipe.values().contains(bookmarkItem.recipeId)) {
|
||||
} else if (group.crafting != null && group.crafting.recipeToItems.containsKey(bookmarkItem.recipeId)) {
|
||||
absoluteSlotIndex = generateCollapsedRecipe(prevBookmarkItem, itemIndex, absoluteSlotIndex, borderGrid);
|
||||
prevBookmarkItem = bookmarkItem;
|
||||
|
||||
@@ -218,8 +217,7 @@ public class BookmarkGridGenerator {
|
||||
}
|
||||
|
||||
private List<Integer> getShadows(BookmarkGroup group, RecipeId recipeId) {
|
||||
return group.crafting.itemToRecipe.entrySet().stream().filter(entry -> recipeId.equals(entry.getValue()))
|
||||
.map(entry -> entry.getKey()).collect(Collectors.toList());
|
||||
return new ArrayList<>(group.crafting.recipeToItems.getOrDefault(recipeId, Collections.emptyList()));
|
||||
}
|
||||
|
||||
private int nextSlotIndex(int index, BookmarkItem previousMeta, BookmarkItem meta, BookmarkViewMode viewMode) {
|
||||
@@ -340,7 +338,8 @@ public class BookmarkGridGenerator {
|
||||
0,
|
||||
chainItem.getItem(),
|
||||
null,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
} else {
|
||||
return new BookmarksGridSlot(
|
||||
slotIndex,
|
||||
@@ -350,7 +349,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 {
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -23,7 +22,9 @@ 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.NEIClientUtils;
|
||||
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;
|
||||
@@ -213,14 +230,7 @@ public class BookmarkStorage {
|
||||
}
|
||||
|
||||
if (wasError) {
|
||||
try {
|
||||
Files.copy(
|
||||
bookmarkFile,
|
||||
new File(bookmarkFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
|
||||
NEIClientConfig.logger.info("Backed up invalid bookmark file to {}", bookmarkFile);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to backup invalid bookmark file to {}", bookmarkFile, e);
|
||||
}
|
||||
NEIClientUtils.backupFile(bookmarkFile);
|
||||
}
|
||||
|
||||
for (BookmarkGrid gr : namespaces) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -111,6 +111,7 @@ public class RecipeChainDetails {
|
||||
|
||||
public final Map<Integer, BookmarkChainItem> calculatedItems = new HashMap<>();
|
||||
public final Map<Integer, RecipeId> itemToRecipe = new HashMap<>();
|
||||
public final Map<RecipeId, List<Integer>> recipeToItems = new HashMap<>();
|
||||
public final Set<RecipeId> outputRecipes = new HashSet<>();
|
||||
public final Set<RecipeId> recipeInMiddle = new HashSet<>();
|
||||
|
||||
@@ -124,6 +125,7 @@ public class RecipeChainDetails {
|
||||
}
|
||||
|
||||
this.itemToRecipe.clear();
|
||||
this.recipeToItems.clear();
|
||||
this.outputRecipes.clear();
|
||||
this.recipeInMiddle.clear();
|
||||
this.calculatedItems.clear();
|
||||
@@ -180,6 +182,10 @@ public class RecipeChainDetails {
|
||||
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, RecipeId> entry : this.itemToRecipe.entrySet()) {
|
||||
this.recipeToItems.computeIfAbsent(entry.getValue(), recipeId -> new ArrayList<>()).add(entry.getKey());
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, BookmarkItem> entry : chainItems.entrySet()) {
|
||||
final int itemIndex = entry.getKey();
|
||||
if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -37,8 +37,8 @@ public class SortableItem {
|
||||
&& (group.viewMode == BookmarkViewMode.TODO_LIST || group.crafting != null);
|
||||
this.crossGroup = !group.collapsed && group.viewMode == BookmarkViewMode.TODO_LIST
|
||||
&& bookmarkItem.type != BookmarkItemType.INGREDIENT;
|
||||
this.isCollapsedRecipe = group.crafting != null && group.crafting.itemToRecipe.values().stream()
|
||||
.anyMatch(recipeId -> this.bookmarkItem.equalsRecipe(recipeId, this.bookmarkItem.groupId));
|
||||
this.isCollapsedRecipe = group.crafting != null && this.bookmarkItem.recipeId != null
|
||||
&& group.crafting.recipeToItems.containsKey(this.bookmarkItem.recipeId);
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -142,7 +142,11 @@ class CraftingTreeCanvas {
|
||||
public int treeWidth = 0;
|
||||
public int treeHeight = 0;
|
||||
|
||||
private final Camera camera = new Camera();
|
||||
private final Camera camera;
|
||||
|
||||
public CraftingTreeCanvas(Camera camera) {
|
||||
this.camera = camera;
|
||||
}
|
||||
|
||||
public void setViewport(int x, int y, int w, int h) {
|
||||
this.camera.setViewport(x, y, w, h);
|
||||
@@ -165,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() {
|
||||
@@ -255,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;
|
||||
|
||||
@@ -2,9 +2,12 @@ package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.bookmark.BookmarkGroup;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
|
||||
class CraftingTreeState {
|
||||
@@ -25,6 +28,8 @@ class CraftingTreeState {
|
||||
HANDLERS
|
||||
}
|
||||
|
||||
private static final Map<BookmarkGroup, CraftingTreeState> GROUP_STATES = new WeakHashMap<>();
|
||||
|
||||
private static final String KEY_STATS_VISIBLE = "craftingTree.statsVisible";
|
||||
private static final String KEY_STATS_SECTIONS = "craftingTree.statsSections";
|
||||
private static final String KEY_USE_INVENTORY_SNAPSHOT = "craftingTree.useInventorySnapshot";
|
||||
@@ -35,6 +40,8 @@ class CraftingTreeState {
|
||||
public boolean useInventorySnapshot = false;
|
||||
public CollapseMode collapseMode = CollapseMode.EXPANDED;
|
||||
|
||||
public boolean initialized;
|
||||
public final Camera camera = new Camera();
|
||||
public final Set<BookmarkItem> collapsedItems = new HashSet<>();
|
||||
public ItemTreeSlot replaceRecipeNode;
|
||||
|
||||
@@ -65,6 +72,9 @@ class CraftingTreeState {
|
||||
? collapseModes[savedCollapseMode]
|
||||
: CollapseMode.EXPANDED;
|
||||
this.replaceRecipeNode = null;
|
||||
this.searchItemResult = null;
|
||||
this.searchStatsResult = null;
|
||||
this.camera.endDrag();
|
||||
}
|
||||
|
||||
public void toggleStatsPanelVisible() {
|
||||
@@ -98,6 +108,10 @@ class CraftingTreeState {
|
||||
NEIClientConfig.world.nbt.setInteger(KEY_COLLAPSE_MODE, this.collapseMode.ordinal());
|
||||
}
|
||||
|
||||
public static CraftingTreeState of(BookmarkGroup group) {
|
||||
return GROUP_STATES.computeIfAbsent(group, g -> new CraftingTreeState());
|
||||
}
|
||||
|
||||
public SearchResult activeSearchResult() {
|
||||
return this.searchItemResult != null ? this.searchItemResult : this.searchStatsResult;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.LRUCache;
|
||||
import codechicken.nei.NEICPH;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.SearchField.GuiSearchField;
|
||||
@@ -57,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;
|
||||
@@ -76,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 {
|
||||
|
||||
@@ -101,24 +103,23 @@ 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;
|
||||
private boolean isFirstScreen = true;
|
||||
|
||||
private final BookmarkGrid grid;
|
||||
private final int groupId;
|
||||
@@ -127,7 +128,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
private final StatsPanel statsPanel = new StatsPanel();
|
||||
private final StatsSectionsMenu statsSectionsMenu = new StatsSectionsMenu();
|
||||
private final CraftingTreeCanvas canvas = new CraftingTreeCanvas();
|
||||
private final CraftingTreeCanvas canvas;
|
||||
private boolean statsPanelVisible;
|
||||
|
||||
private GuiSearchField searchField;
|
||||
@@ -136,7 +137,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
private AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler;
|
||||
private RecipeItemsTooltipLineHandler recipeItemsTooltipLineHandler;
|
||||
|
||||
private final CraftingTreeState uiState = new CraftingTreeState();
|
||||
private final CraftingTreeState uiState;
|
||||
private final CraftingTreeToolbar toolbar;
|
||||
|
||||
private ItemTreeSlot hoveredNode;
|
||||
@@ -148,6 +149,8 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
this.grid = grid;
|
||||
this.groupId = groupId;
|
||||
|
||||
this.uiState = CraftingTreeState.of(grid.getGroup(groupId));
|
||||
this.canvas = new CraftingTreeCanvas(this.uiState.camera);
|
||||
this.toolbar = new CraftingTreeToolbar(this.uiState);
|
||||
}
|
||||
|
||||
@@ -160,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;
|
||||
|
||||
@@ -188,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;
|
||||
|
||||
@@ -284,7 +294,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
this.canvas.graph.rebuild(this.math);
|
||||
|
||||
if (this.isFirstScreen) {
|
||||
if (!this.uiState.initialized) {
|
||||
updateCollapsedItems();
|
||||
} else {
|
||||
this.canvas.recalculateCoordinates(this.collapsedRecipes, this.uiState.collapsedItems, anchor);
|
||||
@@ -292,8 +302,8 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
rebuildStatsPanel();
|
||||
|
||||
if (this.isFirstScreen) {
|
||||
this.isFirstScreen = false;
|
||||
if (!this.uiState.initialized) {
|
||||
this.uiState.initialized = true;
|
||||
this.canvas.fitToView();
|
||||
}
|
||||
}
|
||||
@@ -329,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(
|
||||
@@ -756,7 +758,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
return true;
|
||||
}
|
||||
|
||||
this.canvas.startDrag(mouseX, mouseY);
|
||||
this.canvas.startDrag(getMousePosition());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -777,7 +779,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
|
||||
}
|
||||
|
||||
this.canvas.drag(mouseX, mouseY);
|
||||
this.canvas.drag(getMousePosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -842,7 +844,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
(int) mouse.getY(),
|
||||
direction,
|
||||
NEIClientUtils.shiftKey(),
|
||||
NEIClientUtils.controlKey());
|
||||
NEIClientUtils.controlKey() || NEIClientConfig.craftingTreeZoomWithoutModifier());
|
||||
}
|
||||
|
||||
private boolean handleItemWheel(int wheel, Point2D mouse) {
|
||||
@@ -904,6 +906,18 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == this.mc.gameSettings.keyBindInventory.getKeyCode()) {
|
||||
this.mc.displayGuiScreen(this.prevGui);
|
||||
NEICPH.sendRequestContainer();
|
||||
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());
|
||||
@@ -911,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);
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ public class ConfigSet {
|
||||
public NBTTagCompound nbt;
|
||||
public final ConfigTagParent config;
|
||||
|
||||
public ConfigSet(File nbtFile) {
|
||||
this(nbtFile, null);
|
||||
}
|
||||
|
||||
public ConfigSet(File nbtFile, ConfigTagParent config) {
|
||||
this.nbtFile = nbtFile;
|
||||
this.config = config;
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class DataDumper extends Option {
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return renderTag().getIntValue(0);
|
||||
return getTag().getIntValue(0);
|
||||
}
|
||||
|
||||
public void drawButton(int mousex, int mousey, Rectangle4i b, String text) {
|
||||
|
||||
@@ -29,7 +29,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
|
||||
private GuiCCButton toggleButton;
|
||||
private final Option opt;
|
||||
private final GuiOptionList parentGui;
|
||||
private final boolean world;
|
||||
private Point dragDown;
|
||||
|
||||
public GuiHighlightTips(Option opt) {
|
||||
@@ -37,7 +36,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
|
||||
this.opt = opt;
|
||||
name = opt.name;
|
||||
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
|
||||
this.world = parentGui.worldConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,15 +61,11 @@ public class GuiHighlightTips extends GuiScreenWidget {
|
||||
}
|
||||
|
||||
private boolean show() {
|
||||
return renderTag(name).getBooleanValue();
|
||||
return getTag(name).getBooleanValue();
|
||||
}
|
||||
|
||||
private ConfigTag getTag(String s) {
|
||||
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
|
||||
}
|
||||
|
||||
private ConfigTag renderTag(String s) {
|
||||
return (world && opt.worldSpecific(s) ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
|
||||
return opt.configSet().config.getTag(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +89,7 @@ public class GuiHighlightTips extends GuiScreenWidget {
|
||||
}
|
||||
|
||||
public Point getPos() {
|
||||
return new Point(renderTag(name + ".x").getIntValue(), renderTag(name + ".y").getIntValue());
|
||||
return new Point(getTag(name + ".x").getIntValue(), getTag(name + ".y").getIntValue());
|
||||
}
|
||||
|
||||
public Dimension sampleSize() // copied from HUDManager when running with the sample for this gui
|
||||
@@ -161,7 +155,6 @@ public class GuiHighlightTips extends GuiScreenWidget {
|
||||
}
|
||||
|
||||
private void setPos(Point p) {
|
||||
getTag(name).setBooleanValue(show()); // duplicates global tag for the option gui if in world mode
|
||||
getTag(name + ".x").setIntValue(p.x);
|
||||
getTag(name + ".y").setIntValue(p.y);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ public class GuiItemSorter extends GuiOptionPane {
|
||||
public final List<SortItem> slots = new ArrayList<>();
|
||||
public final List<SortEntry> list;
|
||||
private final GuiOptionList parentGui;
|
||||
private final boolean world;
|
||||
|
||||
boolean dragging;
|
||||
public SortItem dragged;
|
||||
@@ -62,8 +61,7 @@ public class GuiItemSorter extends GuiOptionPane {
|
||||
public GuiItemSorter(Option opt) {
|
||||
this.opt = opt;
|
||||
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
|
||||
this.world = parentGui.worldConfig();
|
||||
list = ItemSorter.fromSaveString(renderTag(opt.configName()).getValue());
|
||||
list = ItemSorter.fromSaveString(getTag(opt.configName()).getValue());
|
||||
for (SortEntry e : list) slots.add(new SortItem(e));
|
||||
}
|
||||
|
||||
@@ -122,10 +120,7 @@ public class GuiItemSorter extends GuiOptionPane {
|
||||
list.add(nslot, dragged.e);
|
||||
|
||||
getTag(opt.configName()).setValue(ItemSorter.getSaveString(list));
|
||||
|
||||
if (opt.activeTag() == getTag(opt.configName())) {
|
||||
ItemSorter.list = new ArrayList<>(list);
|
||||
}
|
||||
ItemSorter.list = new ArrayList<>(list);
|
||||
|
||||
ItemList.refreshItems.restart();
|
||||
}
|
||||
@@ -133,11 +128,7 @@ public class GuiItemSorter extends GuiOptionPane {
|
||||
}
|
||||
|
||||
private ConfigTag getTag(String s) {
|
||||
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
|
||||
}
|
||||
|
||||
private ConfigTag renderTag(String s) {
|
||||
return (world && opt.worldSpecific(s) ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
|
||||
return opt.configSet().config.getTag(s);
|
||||
}
|
||||
|
||||
public SortItem itemAt(int mx, int my) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import net.minecraft.client.gui.GuiYesNoCallback;
|
||||
|
||||
public class GuiNEIOptionList extends GuiOptionList implements GuiYesNoCallback {
|
||||
|
||||
public GuiNEIOptionList(GuiScreen parent, OptionList optionList, boolean world) {
|
||||
super(parent, optionList, world);
|
||||
public GuiNEIOptionList(GuiScreen parent, OptionList optionList) {
|
||||
super(parent, optionList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package codechicken.nei.config;
|
||||
|
||||
import static codechicken.lib.gui.GuiDraw.drawMultilineTip;
|
||||
import static codechicken.lib.gui.GuiDraw.drawStringC;
|
||||
import static codechicken.lib.gui.GuiDraw.fontRenderer;
|
||||
import static codechicken.lib.gui.GuiDraw.getMousePosition;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
@@ -10,7 +9,6 @@ import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -23,8 +21,6 @@ import org.lwjgl.opengl.GL11;
|
||||
import codechicken.core.gui.GuiCCButton;
|
||||
import codechicken.core.gui.GuiScreenWidget;
|
||||
import codechicken.core.gui.GuiScrollSlot;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.LayoutManager;
|
||||
|
||||
public class GuiOptionList extends GuiScreenWidget {
|
||||
|
||||
@@ -66,25 +62,11 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
protected void drawSlot(int slot, int x, int y, int mx, int my, float frame) {
|
||||
Option o = options.get(slot);
|
||||
GL11.glTranslatef(x, y, 0);
|
||||
if (o.showWorldSelector() && world) drawWorldSelector(o, mx, my);
|
||||
GL11.glTranslatef(24, 2, 0);
|
||||
o.draw(mx - 24, my - 2, frame);
|
||||
GL11.glTranslatef(-x - 24, -y - 2, 0);
|
||||
}
|
||||
|
||||
public Rectangle4i worldButtonSize() {
|
||||
return new Rectangle4i(0, 2, 20, 20);
|
||||
}
|
||||
|
||||
private void drawWorldSelector(Option o, int mousex, int mousey) {
|
||||
Rectangle4i b = worldButtonSize();
|
||||
boolean set = o.hasWorldOverride();
|
||||
boolean mouseover = b.contains(mousex, mousey);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
LayoutManager.drawButtonBackground(b.x, b.y, b.w, b.h, true, !set ? 0 : mouseover ? 2 : 1);
|
||||
drawStringC("W", b.x, b.y, b.w, b.h, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBackground(float frame) {
|
||||
Rectangle sbar = scrollbarBounds();
|
||||
@@ -116,16 +98,7 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
|
||||
@Override
|
||||
protected void slotClicked(int slot, int button, int mx, int my, int count) {
|
||||
Option o = options.get(slot);
|
||||
if (world && o.showWorldSelector() && worldButtonSize().contains(mx, my)) {
|
||||
if (button == 1 && o.hasWorldOverride()) {
|
||||
o.useGlobals();
|
||||
Option.playClickSound();
|
||||
} else if (button == 0 && !o.hasWorldOverride()) {
|
||||
o.copyGlobals();
|
||||
Option.playClickSound();
|
||||
}
|
||||
} else if (slotBounds(slot).contains(mx, my)) {
|
||||
if (slotBounds(slot).contains(mx, my)) {
|
||||
options.get(slot).mouseClicked(mx - 24, my - 2, button);
|
||||
}
|
||||
}
|
||||
@@ -167,26 +140,20 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
}
|
||||
|
||||
private List<String> handleTooltip(int slot, int mx, int my, List<String> tooltip) {
|
||||
Option o = options.get(slot);
|
||||
if (world && o.showWorldSelector() && worldButtonSize().contains(mx, my))
|
||||
tooltip.add(translateToLocal("nei.options.wbutton.tip." + (o.hasWorldOverride() ? "1" : "0")));
|
||||
if (slotBounds(slot).contains(mx, my)) return o.handleTooltip(mx - 24, my - 2, tooltip);
|
||||
if (slotBounds(slot).contains(mx, my)) return options.get(slot).handleTooltip(mx - 24, my - 2, tooltip);
|
||||
return tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
protected final GuiScreen parent;
|
||||
protected final OptionList optionList;
|
||||
protected boolean world;
|
||||
|
||||
protected OptionScrollSlot slot;
|
||||
protected GuiCCButton backButton;
|
||||
protected GuiCCButton worldButton;
|
||||
|
||||
public GuiOptionList(GuiScreen parent, OptionList optionList, boolean world) {
|
||||
public GuiOptionList(GuiScreen parent, OptionList optionList) {
|
||||
this.parent = parent;
|
||||
this.optionList = optionList;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,29 +169,18 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
backButton.width = Math.min(200, width - 40);
|
||||
backButton.x = (width - backButton.width) / 2;
|
||||
backButton.y = height - 25;
|
||||
worldButton.width = 60;
|
||||
worldButton.x = width - worldButton.width - 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWidgets() {
|
||||
add(slot = new OptionScrollSlot());
|
||||
add(backButton = new GuiCCButton(0, 0, 0, 20, translateToLocal("nei.options.back")).setActionCommand("back"));
|
||||
add(worldButton = new GuiCCButton(0, 2, 0, 16, worldButtonName()).setActionCommand("world"));
|
||||
}
|
||||
|
||||
private String worldButtonName() {
|
||||
return translateToLocal("nei.options." + (world ? "world" : "global"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(String ident, Object... params) {
|
||||
if (ident.equals("back")) {
|
||||
if (parent instanceof GuiOptionList) ((GuiOptionList) parent).world = world;
|
||||
Minecraft.getMinecraft().displayGuiScreen(parent);
|
||||
} else if (ident.equals("world")) {
|
||||
world = !world;
|
||||
worldButton.text = worldButtonName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,9 +198,6 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
private void drawTooltip() {
|
||||
List<String> tooltip = new LinkedList<>();
|
||||
Point mouse = getMousePosition();
|
||||
if (worldButton.pointInside(mouse.x, mouse.y))
|
||||
tooltip.addAll(Arrays.asList(translateToLocal("nei.options.global.tip." + (world ? "1" : "0")).split(":")));
|
||||
|
||||
tooltip = slot.handleTooltip(mouse.x, mouse.y, tooltip);
|
||||
drawMultilineTip(mouse.x + 12, mouse.y - 12, tooltip);
|
||||
}
|
||||
@@ -265,8 +218,4 @@ public class GuiOptionList extends GuiScreenWidget {
|
||||
super.keyTyped(c, keycode);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean worldConfig() {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,6 @@ public class GuiPanelSettings extends GuiScreenWidget {
|
||||
|
||||
private final Option opt;
|
||||
private final String name;
|
||||
private final boolean world;
|
||||
private Rectangle4i margin;
|
||||
private Point dragDown;
|
||||
private String dragDir;
|
||||
@@ -69,10 +68,9 @@ public class GuiPanelSettings extends GuiScreenWidget {
|
||||
private PanelResizeButton rightButton = new PanelResizeButton(PANEL_SIZE, PANEL_SIZE * 2);
|
||||
private PanelResizeButton bottomButton = new PanelResizeButton(PANEL_SIZE * 2, PANEL_SIZE);
|
||||
|
||||
public PanelPlaceholder(Option opt, String name, boolean world) {
|
||||
public PanelPlaceholder(Option opt, String name) {
|
||||
this.opt = opt;
|
||||
this.name = name;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void resize(Rectangle4i margin) {
|
||||
@@ -163,7 +161,7 @@ public class GuiPanelSettings extends GuiScreenWidget {
|
||||
}
|
||||
|
||||
private ConfigTag getTag(String s) {
|
||||
return (world ? opt.worldConfigSet() : opt.globalConfigSet()).config.getTag(s);
|
||||
return opt.configSet().config.getTag(s);
|
||||
}
|
||||
|
||||
protected void drawItems() {
|
||||
@@ -241,9 +239,8 @@ public class GuiPanelSettings extends GuiScreenWidget {
|
||||
super(176, 198);
|
||||
this.parentGui = (GuiOptionList) Minecraft.getMinecraft().currentScreen;
|
||||
|
||||
boolean world = parentGui.worldConfig();
|
||||
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks", world);
|
||||
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items", world);
|
||||
bookmarksPanel = new PanelPlaceholder(opt, opt.name + ".bookmarks");
|
||||
itemsPanel = new PanelPlaceholder(opt, opt.name + ".items");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ItemPanelDumper extends DataDumper {
|
||||
}
|
||||
|
||||
public int getRes() {
|
||||
int i = renderTag(name + ".res").getIntValue(0);
|
||||
int i = getTag(name + ".res").getIntValue(0);
|
||||
int size = 16 << i;
|
||||
if (size >= getSmallerScreenAxisSize() || i < 0) {
|
||||
getTag(name + ".res").setIntValue(0);
|
||||
@@ -95,7 +95,7 @@ public class ItemPanelDumper extends DataDumper {
|
||||
}
|
||||
if (resButtonSize().contains(mousex, mousey)) {
|
||||
NEIClientUtils.playClickSound();
|
||||
int current = renderTag(name + ".res").getIntValue(0);
|
||||
int current = getTag(name + ".res").getIntValue(0);
|
||||
int next;
|
||||
// 31 - numberOfLeadingZeros(x) = floor(log2(x))
|
||||
int max = 31 - Integer.numberOfLeadingZeros(getSmallerScreenAxisSize()) - 3;
|
||||
|
||||
@@ -59,12 +59,8 @@ public abstract class Option {
|
||||
return elem;
|
||||
}
|
||||
|
||||
public ConfigSet globalConfigSet() {
|
||||
return assertParentOverride(parent.globalConfigSet());
|
||||
}
|
||||
|
||||
public ConfigSet worldConfigSet() {
|
||||
return assertParentOverride(parent.worldConfigSet());
|
||||
public ConfigSet configSet() {
|
||||
return assertParentOverride(parent.configSet());
|
||||
}
|
||||
|
||||
public OptionList configBase() {
|
||||
@@ -72,127 +68,19 @@ public abstract class Option {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the world config contains a tag with this name
|
||||
*/
|
||||
public boolean worldSpecific() {
|
||||
return worldSpecific(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the world config contains a tag with this name
|
||||
*/
|
||||
public boolean worldSpecific(String s) {
|
||||
ConfigTag tag = worldConfigSet().config.getTag(s, false);
|
||||
return tag != null && tag.value != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The currently config set for the gui based on the world/global button
|
||||
*/
|
||||
public ConfigSet configSet() {
|
||||
return worldConfig() ? worldConfigSet() : globalConfigSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag to render, world if world override, otherwise global
|
||||
*/
|
||||
public ConfigTag renderTag() {
|
||||
return renderTag(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag to render, world if world override, otherwise global
|
||||
*/
|
||||
public ConfigTag renderTag(String s) {
|
||||
return (worldConfig() && worldSpecific(s) ? worldConfigSet() : globalConfigSet()).config.getTag(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag with this name from the current configSet
|
||||
* @return The tag with this name from the config set
|
||||
*/
|
||||
public ConfigTag getTag() {
|
||||
return getTag(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag with this name from the current configSet
|
||||
* @return The tag with this name from the config set
|
||||
*/
|
||||
public ConfigTag getTag(String s) {
|
||||
return configSet().config.getTag(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag currently being used ingame, world if a world override exists, otherwise global
|
||||
*/
|
||||
public ConfigTag activeTag() {
|
||||
return activeTag(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tag currently being used ingame, world if a world override exists, otherwise global
|
||||
*/
|
||||
public ConfigTag activeTag(String s) {
|
||||
return (worldSpecific() ? worldConfigSet() : globalConfigSet()).config.getTag(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the gui is in world config mode
|
||||
*/
|
||||
public boolean worldConfig() {
|
||||
return getSlot().getGui().worldConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if in world mode, but this tag has no world override. Changes should not be made in this mode
|
||||
*/
|
||||
public boolean defaulting() {
|
||||
return defaulting(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if in world mode, but this tag has no world override. Changes should not be made in this mode
|
||||
*/
|
||||
public boolean defaulting(String s) {
|
||||
return worldConfig() && !worldSpecific(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when world specific is disabled for this option. Should delete all world specific overrides
|
||||
*/
|
||||
public void useGlobals() {
|
||||
useGlobal(configName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a specific named tag from the worldConfig
|
||||
*/
|
||||
public void useGlobal(String s) {
|
||||
if (worldConfig()) worldConfigSet().config.removeTag(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when world specific is activated for this option. Should copy the global value into a world specific
|
||||
* override
|
||||
*/
|
||||
public void copyGlobals() {
|
||||
copyGlobal(configName(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the global value into a world specific override
|
||||
*/
|
||||
public void copyGlobal(String s) {
|
||||
copyGlobal(s, false);
|
||||
}
|
||||
|
||||
public void copyGlobal(String s, boolean recursive) {
|
||||
if (!worldConfig()) return;
|
||||
|
||||
ConfigTag tag = globalConfigSet().config.getTag(s);
|
||||
worldConfigSet().config.getTag(s).setValue(tag.getValue());
|
||||
if (recursive) for (String s2 : tag.childTagMap().keySet()) copyGlobal(s + "." + s2);
|
||||
}
|
||||
|
||||
public void onAdded(OptionScrollSlot slot) {
|
||||
this.slotRef = new WeakReference<>(slot);
|
||||
}
|
||||
@@ -217,12 +105,4 @@ public abstract class Option {
|
||||
public List<String> handleTooltip(int mx, int my, List<String> currenttip) {
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
public boolean showWorldSelector() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasWorldOverride() {
|
||||
return worldSpecific();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,11 @@ public class OptionCycled extends OptionButton {
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return renderTag().getIntValue();
|
||||
return getTag().getIntValue();
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
getTag().setIntValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,7 +56,7 @@ public class OptionCycled extends OptionButton {
|
||||
|
||||
if (next == value()) return false;
|
||||
|
||||
getTag().setIntValue(next);
|
||||
setValue(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package codechicken.nei.config;
|
||||
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
import codechicken.nei.ClientHandler;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
|
||||
public class OptionExportConfig extends OptionButton {
|
||||
|
||||
private final String fileName;
|
||||
|
||||
public OptionExportConfig(String fileName) {
|
||||
super("tools.export_configs." + fileName.substring(0, fileName.lastIndexOf('.')));
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getButtonText() {
|
||||
return translateN("tools.export_configs.button");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
final boolean exported = ClientHandler.exportDefaultConfig(fileName);
|
||||
|
||||
NEIClientUtils.printChatMessage(
|
||||
new ChatComponentTranslation(
|
||||
exported ? "nei.options.tools.export_configs.done" : "nei.options.tools.export_configs.skipped",
|
||||
fileName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public class OptionIntegerField extends OptionTextField {
|
||||
@Override
|
||||
protected int getMaxInputWidth() {
|
||||
|
||||
if (renderTag().getValue().startsWith("0x")) {
|
||||
if (getTag().getValue().startsWith("0x")) {
|
||||
return GuiDraw.getStringWidth("0x" + Long.toHexString(this.max).toUpperCase()) + 32;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,7 @@ public class OptionList extends OptionButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSet globalConfigSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSet worldConfigSet() {
|
||||
public ConfigSet configSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -35,8 +30,8 @@ public class OptionList extends OptionButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
|
||||
return new GuiOptionList(parent, list, world);
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
|
||||
return new GuiOptionList(parent, list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,41 +113,25 @@ public class OptionList extends OptionButton {
|
||||
*
|
||||
* @param parent The parent screen for the back button
|
||||
* @param list The option list to be displayed in the gui
|
||||
* @param world true if in world config mode, false for global
|
||||
*/
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list, boolean world) {
|
||||
return this.parent.getGui(parent, list, world);
|
||||
public GuiOptionList getGui(GuiScreen parent, OptionList list) {
|
||||
return this.parent.getGui(parent, list);
|
||||
}
|
||||
|
||||
public void openGui(GuiScreen parent, boolean world) {
|
||||
Minecraft.getMinecraft().displayGuiScreen(getGui(parent, this, world));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this option to a temporary slot and gui for internal setting manipulation.
|
||||
*/
|
||||
public GuiOptionList synthesizeEnvironment(boolean world) {
|
||||
GuiOptionList gui = getGui(null, this, world);
|
||||
gui.addWidgets();
|
||||
return gui;
|
||||
public void openGui(GuiScreen parent) {
|
||||
Minecraft.getMinecraft().displayGuiScreen(getGui(parent, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
openGui(getSlot().getGui(), getSlot().getGui().worldConfig());
|
||||
openGui(getSlot().getGui());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showWorldSelector() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded(OptionScrollSlot slot) {
|
||||
super.onAdded(slot);
|
||||
|
||||
globalConfigSet().config.getTag(configName()).useBraces();
|
||||
worldConfigSet().config.getTag(configName()).useBraces();
|
||||
configSet().config.getTag(configName()).useBraces();
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ public abstract class OptionStringSet extends Option {
|
||||
}
|
||||
|
||||
public List<String> values() {
|
||||
return Arrays.asList(renderTag().getValue().replace(" ", "").split(","));
|
||||
return Arrays.asList(getTag().getValue().replace(" ", "").split(","));
|
||||
}
|
||||
|
||||
public int buttonX() {
|
||||
|
||||
@@ -16,8 +16,7 @@ public class OptionTextField extends Option {
|
||||
|
||||
@Override
|
||||
public void onTextChange(String oldText) {
|
||||
// don't override global if text hasn't changed
|
||||
if (focused() && isValidValue(text()) && (!defaulting() || !text().equals(getTag().getValue()))) {
|
||||
if (focused() && isValidValue(text())) {
|
||||
getTag().setValue(text());
|
||||
OptionTextField.this.onTextChange(text());
|
||||
}
|
||||
@@ -25,7 +24,7 @@ public class OptionTextField extends Option {
|
||||
|
||||
@Override
|
||||
public void setFocus(boolean focus) {
|
||||
if (!focus && !isValidValue(text())) setText(renderTag().getValue());
|
||||
if (!focus && !isValidValue(text())) setText(getTag().getValue());
|
||||
super.setFocus(focus);
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ public class OptionTextField extends Option {
|
||||
@Override
|
||||
public void update() {
|
||||
textField.update();
|
||||
if (!textField.focused()) textField.setText(renderTag().getValue());
|
||||
if (!textField.focused()) textField.setText(getTag().getValue());
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
|
||||
@@ -14,7 +14,7 @@ public class OptionToggleButton extends OptionButton {
|
||||
}
|
||||
|
||||
public boolean state() {
|
||||
return renderTag().getBooleanValue();
|
||||
return getTag().getBooleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,7 +34,7 @@ public class OptionToggleButtonBoubs extends OptionButton {
|
||||
}
|
||||
|
||||
public boolean state() {
|
||||
return renderTag().getBooleanValue();
|
||||
return getTag().getBooleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package codechicken.nei.config;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.recipe.RecipeJsonWriter;
|
||||
|
||||
public class RecipeDumper extends DataDumper {
|
||||
|
||||
public RecipeDumper(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] header() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String[]> dump(int mode) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String renderName() {
|
||||
return translateN(name);
|
||||
}
|
||||
|
||||
public Rectangle4i separateButtonSize() {
|
||||
return dumpButtonSize();
|
||||
}
|
||||
|
||||
public Rectangle4i combinedButtonSize() {
|
||||
return modeButtonSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey, float frame) {
|
||||
drawPrefix();
|
||||
drawButton(mousex, mousey, combinedButtonSize(), translateN(name + ".combined"));
|
||||
drawButton(mousex, mousey, separateButtonSize(), translateN(name + ".separate"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int mousex, int mousey, int button) {
|
||||
final boolean combined = combinedButtonSize().contains(mousex, mousey);
|
||||
|
||||
if (combined || separateButtonSize().contains(mousex, mousey)) {
|
||||
NEIClientUtils.playClickSound();
|
||||
RecipeJsonWriter.dumpAll(!combined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
@@ -30,7 +30,10 @@ public class AllMultiRecipeFilter implements IRecipeFilter {
|
||||
try {
|
||||
if (filter != null && !filter.matches(handler, recipeIndex)) return false;
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
|
||||
NEIClientUtils.errorOnce(
|
||||
"filter:" + handler.getHandlerId() + ":" + filter.getClass().getName(),
|
||||
"Exception filtering " + handler.getHandlerId() + " with " + filter,
|
||||
e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package codechicken.nei.filter;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public class AllOthersRecipeFilter implements IRecipeFilter {
|
||||
|
||||
private final ItemFilter filter;
|
||||
|
||||
public AllOthersRecipeFilter(ItemFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex) {
|
||||
|
||||
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
|
||||
if (!match(pStack)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean match(PositionedStack pStack) {
|
||||
if (pStack == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ItemStack stack : pStack.items) {
|
||||
if (filter.matches(stack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
@@ -30,7 +30,10 @@ public class AnyMultiRecipeFilter implements IRecipeFilter {
|
||||
try {
|
||||
if (filter != null && filter.matches(handler, recipeIndex)) return true;
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
|
||||
NEIClientUtils.errorOnce(
|
||||
"filter:" + handler.getHandlerId() + ":" + filter.getClass().getName(),
|
||||
"Exception filtering " + handler.getHandlerId() + " with " + filter,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package codechicken.nei.filter;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public class AnyOthersRecipeFilter implements IRecipeFilter {
|
||||
|
||||
private final ItemFilter filter;
|
||||
|
||||
public AnyOthersRecipeFilter(ItemFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex) {
|
||||
|
||||
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
|
||||
if (match(pStack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean match(PositionedStack pStack) {
|
||||
if (pStack == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ItemStack stack : pStack.items) {
|
||||
if (filter.matches(stack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,8 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
|
||||
ANY,
|
||||
INPUT,
|
||||
OUTPUT;
|
||||
OUTPUT,
|
||||
AUXILIARY;
|
||||
|
||||
public static FilterContext fromChar(char c) {
|
||||
switch (c) {
|
||||
@@ -24,6 +25,8 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
return INPUT;
|
||||
case '>':
|
||||
return OUTPUT;
|
||||
case '^':
|
||||
return AUXILIARY;
|
||||
default:
|
||||
return ANY;
|
||||
}
|
||||
@@ -48,17 +51,14 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) {
|
||||
final PositionedStack result = handler.getResultStack(recipeIndex);
|
||||
|
||||
if (result != null && matchPositionedStack(result) == this.anyMatch) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if (matchPositionedStack(handler.getOtherStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
if ((this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT)
|
||||
&& matchPositionedStack(handler.getResultStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if ((this.context == FilterContext.ANY || this.context == FilterContext.AUXILIARY)
|
||||
&& matchPositionedStack(handler.getAuxiliaryStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
return !this.anyMatch;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -499,9 +501,60 @@ public class GuiContainerManager {
|
||||
public boolean lastKeyTyped(int keyID, char keyChar) {
|
||||
if (keyID == 1) return false;
|
||||
|
||||
// hotkeys go before lastKeyTyped, otherwise a third-party handler that consumes every key blocks them
|
||||
if (handleHotkeys()) return true;
|
||||
|
||||
for (IContainerInputHandler inputhander : inputHandlers)
|
||||
if (inputhander.lastKeyTyped(window, keyChar, keyID)) return true;
|
||||
|
||||
return handleScreenHotkeys();
|
||||
}
|
||||
|
||||
public boolean onHotkeyPressed() {
|
||||
return handleHotkeys() || handleScreenHotkeys();
|
||||
}
|
||||
|
||||
private boolean handleHotkeys() {
|
||||
|
||||
if (window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (IContainerInputHandler inputhander : inputHandlers) {
|
||||
if (inputhander.onHotkeyPressed(window)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleScreenHotkeys() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ public class ItemFluidDisplay extends Item implements IFluidContainerItem {
|
||||
public ItemFluidDisplay() {
|
||||
setUnlocalizedName("nei.fluidDisplay");
|
||||
setMaxStackSize(1);
|
||||
setHasSubtypes(true);
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
@@ -72,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;
|
||||
}
|
||||
|
||||
@@ -85,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package codechicken.nei.moboverlay;
|
||||
|
||||
import net.minecraft.client.renderer.GLAllocation;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/** The mob spawn overlay quads, compiled into a GL display list. */
|
||||
public final class MobOverlayDisplayList {
|
||||
|
||||
private int listId = -1;
|
||||
private int modesVersion;
|
||||
private MobOverlayStyle style;
|
||||
private int minX;
|
||||
private int minY;
|
||||
private int minZ;
|
||||
private long compiledTicks;
|
||||
|
||||
/**
|
||||
* Compiles after a {@link MobOverlayScan#getModesVersion} change are debounced to one per {@code MIN_COMPILE_TICKS}
|
||||
* ticks, because the version changes several times per second while the player moves fast, and a compile runs for a
|
||||
* millisecond or more.
|
||||
*/
|
||||
public boolean isCompileDue(int currentModesVersion, MobOverlayStyle currentStyle, long worldTicks) {
|
||||
final int MIN_COMPILE_TICKS = 5;
|
||||
if (listId < 0 || style != currentStyle) return true;
|
||||
|
||||
// after a dimension change or a server switch, `worldTicks` can be lower than `compiledTicks`
|
||||
final boolean isWaitOver = worldTicks - compiledTicks >= MIN_COMPILE_TICKS || worldTicks < compiledTicks;
|
||||
return modesVersion != currentModesVersion && isWaitOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quad positions are relative to block ({@link MobOverlayScan#getMinX}, {@link MobOverlayScan#getMinY},
|
||||
* {@link MobOverlayScan#getMinZ}) of {@code scan}.
|
||||
*/
|
||||
public void beginCompile(MobOverlayScan scan, MobOverlayStyle newStyle, long worldTicks) {
|
||||
if (listId < 0) listId = GLAllocation.generateDisplayLists(1);
|
||||
modesVersion = scan.getModesVersion();
|
||||
style = newStyle;
|
||||
minX = scan.getMinX();
|
||||
minY = scan.getMinY();
|
||||
minZ = scan.getMinZ();
|
||||
compiledTicks = worldTicks;
|
||||
GL11.glNewList(listId, GL11.GL_COMPILE);
|
||||
}
|
||||
|
||||
public void endCompile() {
|
||||
GL11.glEndList();
|
||||
}
|
||||
|
||||
/** The model view matrix is relative to block ({@code cameraX}, {@code cameraY}, {@code cameraZ}). */
|
||||
public void draw(int cameraX, int cameraY, int cameraZ) {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslated(minX - cameraX, minY - cameraY, minZ - cameraZ);
|
||||
GL11.glCallList(listId);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (listId < 0) return;
|
||||
|
||||
GLAllocation.deleteDisplayLists(listId);
|
||||
listId = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package codechicken.nei.moboverlay;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
|
||||
/** Vertex builder for one block's top face in the mob spawn overlay. */
|
||||
public final class MobOverlayFace {
|
||||
|
||||
public static final byte OUTSIDE_SCAN = -2;
|
||||
|
||||
private static final int NEIGHBOURHOOD_SIZE = 3;
|
||||
|
||||
private final byte[] neighbourModes = new byte[NEIGHBOURHOOD_SIZE * NEIGHBOURHOOD_SIZE];
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
|
||||
/** {@code (x, y, z)} is the north-west corner of the block's top face. */
|
||||
public void moveTo(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void setNeighbourMode(int offsetX, int offsetZ, byte spawnMode) {
|
||||
neighbourModes[indexNeighbour(offsetX, offsetZ)] = spawnMode;
|
||||
}
|
||||
|
||||
public void applyColor(MobOverlayStyle.Colouring colouring, float alpha) {
|
||||
final boolean isNightOnly = neighbourModes[indexNeighbour(0, 0)] == MobOverlayScan.NIGHT_ONLY;
|
||||
final int rgb = switch (colouring) {
|
||||
case OVERLAY -> isNightOnly ? 0xFFFF00 : 0xFF0000;
|
||||
case STICKER -> isNightOnly ? 0xFFFFFF : 0xFF7373;
|
||||
};
|
||||
Tessellator.instance.setColorRGBA_I(rgb, Math.round(alpha * 255));
|
||||
}
|
||||
|
||||
public boolean isJoined(int offsetX, int offsetZ) {
|
||||
final byte neighbourMode = neighbourModes[indexNeighbour(offsetX, offsetZ)];
|
||||
// no outline along the edge of the scan
|
||||
return neighbourMode == neighbourModes[indexNeighbour(0, 0)] || neighbourMode == OUTSIDE_SCAN;
|
||||
}
|
||||
|
||||
public void addFullFace() {
|
||||
addVertex(0, 0);
|
||||
addVertex(0, 1);
|
||||
addVertex(1, 1);
|
||||
addVertex(1, 0);
|
||||
}
|
||||
|
||||
/** Add one cell of a 3 x 3 grid over the face. The outer rows and columns of the grid are {@code width} wide. */
|
||||
public void addCell(int offsetX, int offsetZ, double width) {
|
||||
final double west = computeCellStart(offsetX, width);
|
||||
final double east = 1 - computeCellStart(-offsetX, width);
|
||||
final double north = computeCellStart(offsetZ, width);
|
||||
final double south = 1 - computeCellStart(-offsetZ, width);
|
||||
addVertex(west, north);
|
||||
addVertex(west, south);
|
||||
addVertex(east, south);
|
||||
addVertex(east, north);
|
||||
}
|
||||
|
||||
/** The texture is upright to a player facing north (-z). */
|
||||
public void addTexturedFace() {
|
||||
addTexturedVertex(0, 0);
|
||||
addTexturedVertex(0, 1);
|
||||
addTexturedVertex(1, 1);
|
||||
addTexturedVertex(1, 0);
|
||||
}
|
||||
|
||||
/** @param offsetX 0 at the west edge, 1 at the east edge */
|
||||
public void addVertex(double offsetX, double offsetZ) {
|
||||
Tessellator.instance.addVertex(x + offsetX, y, z + offsetZ);
|
||||
}
|
||||
|
||||
private void addTexturedVertex(double offsetX, double offsetZ) {
|
||||
Tessellator.instance.setTextureUV(offsetX, offsetZ);
|
||||
addVertex(offsetX, offsetZ);
|
||||
}
|
||||
|
||||
private static int indexNeighbour(int offsetX, int offsetZ) {
|
||||
return (offsetX + 1) * NEIGHBOURHOOD_SIZE + offsetZ + 1;
|
||||
}
|
||||
|
||||
private static double computeCellStart(int offset, double width) {
|
||||
if (offset < 0) return 0;
|
||||
return offset == 0 ? width : 1 - width;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
package codechicken.nei.moboverlay;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.IWorldAccess;
|
||||
import net.minecraft.world.SpawnerAnimals;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
|
||||
/**
|
||||
* Spawn modes, and the tops of flat blocks such as carpets, in a box around the player. The spawn modes are from
|
||||
* {@link Column#getSpawnMode}: 0 for no spawns, 1 for night only and 2 for any time. The box moves with the player once
|
||||
* the player is a few blocks from the box's centre, and {@link #update} scans only the blocks that enter the box.
|
||||
* {@link #update} rescans the column of a block or light change on the next tick. On each tick, {@link #update} also
|
||||
* rescans a slice of the columns, for changes with no block update, such as a boat that moves into the box.
|
||||
*/
|
||||
public final class MobOverlayScan {
|
||||
|
||||
public static final byte NIGHT_ONLY = 1;
|
||||
public static final int MIN_RADIUS = 8;
|
||||
public static final int MAX_RADIUS = 64;
|
||||
public static final int DEFAULT_RADIUS = 32;
|
||||
public static final int MIN_VERTICAL_RADIUS = 4;
|
||||
public static final int MAX_VERTICAL_RADIUS = 64;
|
||||
public static final int DEFAULT_VERTICAL_RADIUS = 16;
|
||||
private static final int RECENTRE_DISTANCE = 4;
|
||||
private static final int VERTICAL_RECENTRE_DISTANCE = 2;
|
||||
private static final int RESCAN_TICKS = 40;
|
||||
private static final int SPAWN_MODE_MASK = 3;
|
||||
private static final int COVER_TOP_SHIFT = 2;
|
||||
private static final int COVER_TOP_STEPS = 32;
|
||||
|
||||
private final IWorldAccess changeListener = new ChangeListener();
|
||||
private final Column column = new Column();
|
||||
private int radius;
|
||||
private int verticalRadius;
|
||||
private int span;
|
||||
private int height;
|
||||
// a ring buffer over the box, indexed by `findCell()`. A cell has the spawn mode in the bits of `SPAWN_MODE_MASK`,
|
||||
// and `Column.readCoverTop()` shifted left by `COVER_TOP_SHIFT`
|
||||
private byte[] modes = new byte[0];
|
||||
// indexed by `findColumn(x, z)`
|
||||
private boolean[] dirty = new boolean[0];
|
||||
private boolean hasDirtyColumns;
|
||||
private int minX;
|
||||
private int minY;
|
||||
private int minZ;
|
||||
// the slice rescan of `update()` continues from this column, numbered `(x - minX) * span + (z - minZ)`
|
||||
private int nextRescanColumn;
|
||||
private int modesVersion;
|
||||
private boolean hasModes;
|
||||
private long scannedTick;
|
||||
// world of `modes`, with `changeListener` added
|
||||
private World scannedWorld;
|
||||
|
||||
public void clear() {
|
||||
if (scannedWorld == null && span == 0) return;
|
||||
|
||||
if (scannedWorld != null) scannedWorld.removeWorldAccess(changeListener);
|
||||
radius = 0;
|
||||
verticalRadius = 0;
|
||||
span = 0;
|
||||
height = 0;
|
||||
modes = new byte[0];
|
||||
dirty = new boolean[0];
|
||||
hasModes = false;
|
||||
scannedWorld = null;
|
||||
// `Chunk.worldObj` references the world
|
||||
column.chunk = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan every column in this call after {@link #clear}, a change of either radius, a world change, or a move of
|
||||
* {@link #getSpan} blocks or more across or {@link #getHeight} blocks or more up or down. {@code newRadius} is
|
||||
* clamped between {@link #MIN_RADIUS} and {@link #MAX_RADIUS}, and {@code newVerticalRadius} between
|
||||
* {@link #MIN_VERTICAL_RADIUS} and {@link #MAX_VERTICAL_RADIUS}.
|
||||
*/
|
||||
public void update(Entity entity, int newRadius, int newVerticalRadius) {
|
||||
final World world = entity.worldObj;
|
||||
final int clampedRadius = (int) MathHelper.clip(newRadius, MIN_RADIUS, MAX_RADIUS);
|
||||
final int clampedVerticalRadius = (int) MathHelper
|
||||
.clip(newVerticalRadius, MIN_VERTICAL_RADIUS, MAX_VERTICAL_RADIUS);
|
||||
if (radius != clampedRadius || verticalRadius != clampedVerticalRadius) {
|
||||
resize(clampedRadius, clampedVerticalRadius);
|
||||
}
|
||||
final int centreX = MathHelper.floor_double(entity.posX);
|
||||
final int centreY = (int) MathHelper.clip(entity.posY, verticalRadius, world.getHeight() - verticalRadius);
|
||||
final int centreZ = MathHelper.floor_double(entity.posZ);
|
||||
final boolean isNearBox = Math.abs(centreX - radius - minX) < span && Math.abs(centreZ - radius - minZ) < span
|
||||
&& Math.abs(centreY - verticalRadius - minY) < height;
|
||||
if (!(hasModes && world == scannedWorld && isNearBox)) {
|
||||
if (world != scannedWorld) watchWorld(world);
|
||||
scanFullBox(centreX, centreY, centreZ);
|
||||
return;
|
||||
}
|
||||
|
||||
final long worldTicks = world.getTotalWorldTime();
|
||||
if (worldTicks == scannedTick) return;
|
||||
|
||||
scannedTick = worldTicks;
|
||||
boolean isChanged = moveBox(centreX, centreY, centreZ);
|
||||
if (hasDirtyColumns) isChanged |= rescanDirtyColumns();
|
||||
isChanged |= rescanSlice();
|
||||
if (isChanged) modesVersion++;
|
||||
}
|
||||
|
||||
/** The version changes when a spawn mode or the top of a flat block changes, or the box moves. */
|
||||
public int getModesVersion() {
|
||||
return modesVersion;
|
||||
}
|
||||
|
||||
public int getSpan() {
|
||||
return span;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getMinX() {
|
||||
return minX;
|
||||
}
|
||||
|
||||
public int getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
public int getMinZ() {
|
||||
return minZ;
|
||||
}
|
||||
|
||||
/** {@code i} and {@code j} are from 0 to {@link #getSpan}, and {@code k} is from 0 to {@link #getHeight}. */
|
||||
public byte readSpawnMode(int i, int k, int j) {
|
||||
return (byte) (modes[findCell(minX + i, minY + k, minZ + j)] & SPAWN_MODE_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of the overlay above the bottom of the block, so that a flat block such as a carpet does not cover the
|
||||
* overlay. The indices are those of {@link #readSpawnMode}.
|
||||
*/
|
||||
public double readOverlayYOffset(int i, int k, int j) {
|
||||
final double OVERLAY_GAP = 0.02;
|
||||
final int coverTopSteps = modes[findCell(minX + i, minY + k, minZ + j)] >> COVER_TOP_SHIFT;
|
||||
return (double) coverTopSteps / COVER_TOP_STEPS + OVERLAY_GAP;
|
||||
}
|
||||
|
||||
private void resize(int newRadius, int newVerticalRadius) {
|
||||
radius = newRadius;
|
||||
verticalRadius = newVerticalRadius;
|
||||
span = 2 * newRadius + 1;
|
||||
height = 2 * newVerticalRadius + 1;
|
||||
modes = new byte[span * span * height];
|
||||
dirty = new boolean[span * span];
|
||||
hasModes = false;
|
||||
nextRescanColumn = 0;
|
||||
}
|
||||
|
||||
private void scanFullBox(int centreX, int centreY, int centreZ) {
|
||||
minX = centreX - radius;
|
||||
minY = centreY - verticalRadius;
|
||||
minZ = centreZ - radius;
|
||||
for (int x = minX; x < minX + span; x++) {
|
||||
for (int z = minZ; z < minZ + span; z++) {
|
||||
scanColumn(x, z);
|
||||
}
|
||||
}
|
||||
hasModes = true;
|
||||
modesVersion++;
|
||||
scannedTick = scannedWorld.getTotalWorldTime();
|
||||
}
|
||||
|
||||
private void watchWorld(World world) {
|
||||
if (scannedWorld != null) scannedWorld.removeWorldAccess(changeListener);
|
||||
world.addWorldAccess(changeListener);
|
||||
scannedWorld = world;
|
||||
}
|
||||
|
||||
/** @return whether the box moved */
|
||||
private boolean moveBox(int centreX, int centreY, int centreZ) {
|
||||
boolean isMoved = false;
|
||||
final int offsetX = centreX - radius - minX;
|
||||
if (Math.abs(offsetX) > RECENTRE_DISTANCE) {
|
||||
final int enteringX = offsetX > 0 ? minX + span : minX + offsetX;
|
||||
minX += offsetX;
|
||||
for (int x = enteringX; x < enteringX + Math.abs(offsetX); x++) {
|
||||
for (int z = minZ; z < minZ + span; z++) {
|
||||
scanColumn(x, z);
|
||||
}
|
||||
}
|
||||
isMoved = true;
|
||||
}
|
||||
|
||||
final int offsetZ = centreZ - radius - minZ;
|
||||
if (Math.abs(offsetZ) > RECENTRE_DISTANCE) {
|
||||
final int enteringZ = offsetZ > 0 ? minZ + span : minZ + offsetZ;
|
||||
minZ += offsetZ;
|
||||
for (int x = minX; x < minX + span; x++) {
|
||||
for (int z = enteringZ; z < enteringZ + Math.abs(offsetZ); z++) {
|
||||
scanColumn(x, z);
|
||||
}
|
||||
}
|
||||
isMoved = true;
|
||||
}
|
||||
|
||||
final int offsetY = centreY - verticalRadius - minY;
|
||||
if (Math.abs(offsetY) > VERTICAL_RECENTRE_DISTANCE) {
|
||||
final int enteringY = offsetY > 0 ? minY + height : minY + offsetY;
|
||||
minY += offsetY;
|
||||
for (int x = minX; x < minX + span; x++) {
|
||||
for (int z = minZ; z < minZ + span; z++) {
|
||||
column.moveTo(x, z);
|
||||
column.scan(enteringY, enteringY + Math.abs(offsetY));
|
||||
}
|
||||
}
|
||||
isMoved = true;
|
||||
}
|
||||
return isMoved;
|
||||
}
|
||||
|
||||
private boolean rescanDirtyColumns() {
|
||||
hasDirtyColumns = false;
|
||||
boolean isChanged = false;
|
||||
for (int x = minX; x < minX + span; x++) {
|
||||
for (int z = minZ; z < minZ + span; z++) {
|
||||
if (dirty[findColumn(x, z)]) isChanged |= scanColumn(x, z);
|
||||
}
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
private boolean rescanSlice() {
|
||||
final int columnCount = span * span;
|
||||
final int sliceEnd = Math.min(columnCount, nextRescanColumn + (columnCount + RESCAN_TICKS - 1) / RESCAN_TICKS);
|
||||
boolean isChanged = false;
|
||||
for (; nextRescanColumn < sliceEnd; nextRescanColumn++) {
|
||||
final int x = minX + nextRescanColumn / span;
|
||||
final int z = minZ + nextRescanColumn % span;
|
||||
isChanged |= scanColumn(x, z);
|
||||
}
|
||||
if (nextRescanColumn >= columnCount) nextRescanColumn = 0;
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
/** @return whether a spawn mode or the top of a flat block changed */
|
||||
private boolean scanColumn(int x, int z) {
|
||||
column.moveTo(x, z);
|
||||
dirty[findColumn(x, z)] = false;
|
||||
return column.scan(minY, minY + height);
|
||||
}
|
||||
|
||||
private int findColumn(int x, int z) {
|
||||
return Math.floorMod(x, span) * span + Math.floorMod(z, span);
|
||||
}
|
||||
|
||||
private int findCell(int x, int y, int z) {
|
||||
return findColumn(x, z) * height + Math.floorMod(y, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #moveTo} reads the chunk, the height map and the biome of a column, and {@link #scan} reads the column's
|
||||
* blocks.
|
||||
*/
|
||||
private final class Column {
|
||||
|
||||
private final Entity dummyEntity = new EntityPig(null);
|
||||
private final AxisAlignedBB spawnBox = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
|
||||
private int x;
|
||||
private int z;
|
||||
private Chunk chunk;
|
||||
private int topY;
|
||||
private boolean canBiomeSpawn;
|
||||
|
||||
private void moveTo(int newX, int newZ) {
|
||||
x = newX;
|
||||
z = newZ;
|
||||
final BiomeGenBase biome = scannedWorld.getBiomeGenForCoords(x, z);
|
||||
canBiomeSpawn = !biome.getSpawnableList(EnumCreatureType.monster).isEmpty()
|
||||
&& biome.getSpawningChance() > 0;
|
||||
chunk = scannedWorld.getChunkFromBlockCoords(x, z);
|
||||
// on the client, `Chunk.generateHeightMap()` never tests the top layer of the highest non-empty section.
|
||||
// `getHeightValue()` can then be below a block on that layer, so test that layer here
|
||||
final int topLayerY = chunk.getTopFilledSegment() + 15;
|
||||
final boolean isTopLayerFilled = !chunk.isEmpty() && chunk.func_150808_b(x & 15, topLayerY, z & 15) != 0;
|
||||
final int heightValue = isTopLayerFilled ? topLayerY + 1 : scannedWorld.getHeightValue(x, z);
|
||||
// `getHeightValue()` is the height above the highest block with a light opacity above 0. A snow layer and a
|
||||
// hopper have a light opacity of 0, and a mob can spawn on top of a full snow block or a hopper, so scan
|
||||
// one block above the height value
|
||||
topY = Math.min(scannedWorld.getHeight() - 1, heightValue + 1);
|
||||
}
|
||||
|
||||
/** @return whether a spawn mode or the top of a flat block changed */
|
||||
private boolean scan(int fromY, int toY) {
|
||||
boolean isChanged = false;
|
||||
for (int y = fromY; y < toY; y++) {
|
||||
final int spawnMode = canBiomeSpawn && y <= topY ? getSpawnMode(y) : 0;
|
||||
final byte cellValue = (byte) (spawnMode == 0 ? 0 : spawnMode | (readCoverTop(y) << COVER_TOP_SHIFT));
|
||||
final int cell = findCell(x, y, z);
|
||||
isChanged |= modes[cell] != cellValue;
|
||||
modes[cell] = cellValue;
|
||||
}
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
/** The spawn mode of the block at height {@code y} in this column. */
|
||||
private byte getSpawnMode(int y) {
|
||||
if (y >= chunk.worldObj.getHeight()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8 || !SpawnerAnimals
|
||||
.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// the size of a zombie, a creeper and a skeleton that is not a wither skeleton. A taller or wider monster,
|
||||
// such as an enderman or a spider, needs more room
|
||||
final double MOB_WIDTH = 0.6;
|
||||
final double MOB_HEIGHT = 1.8;
|
||||
spawnBox.minX = x + 0.5 - MOB_WIDTH / 2;
|
||||
spawnBox.maxX = x + 0.5 + MOB_WIDTH / 2;
|
||||
spawnBox.minY = y;
|
||||
spawnBox.maxY = y + MOB_HEIGHT;
|
||||
spawnBox.minZ = z + 0.5 - MOB_WIDTH / 2;
|
||||
spawnBox.maxZ = z + 0.5 + MOB_WIDTH / 2;
|
||||
if (!chunk.worldObj.getCollidingBoundingBoxes(dummyEntity, spawnBox).isEmpty()
|
||||
|| chunk.worldObj.isAnyLiquid(spawnBox)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (byte) (chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15) >= 8 ? 1 : 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The top of the block at height {@code y}, in fractions of a block with {@link MobOverlayScan#COVER_TOP_STEPS}
|
||||
* as the denominator, or 0 for a tall block.
|
||||
*/
|
||||
private int readCoverTop(int y) {
|
||||
final double MAX_COVER_TOP = 0.25;
|
||||
final Block block = chunk.getBlock(x & 15, y, z & 15);
|
||||
block.setBlockBoundsBasedOnState(chunk.worldObj, x, y, z);
|
||||
final double top = block.getBlockBoundsMaxY();
|
||||
// a mark on the bounds of a plant or a sloped rail would float above most of the block. Keep the mark at
|
||||
// the floor for a tall block
|
||||
return top > MAX_COVER_TOP ? 0 : (int) Math.ceil(top * COVER_TOP_STEPS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Marks the columns of the block and light changes in {@link #scannedWorld}. */
|
||||
private final class ChangeListener implements IWorldAccess {
|
||||
|
||||
@Override
|
||||
public void markBlockForUpdate(int x, int y, int z) {
|
||||
markChanged(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockForRenderUpdate(int x, int y, int z) {
|
||||
markChanged(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockRangeForRenderUpdate(int fromX, int fromY, int fromZ, int toX, int toY, int toZ) {
|
||||
for (int x = Math.max(fromX, minX); x <= Math.min(toX, minX + span - 1); x++) {
|
||||
for (int z = Math.max(fromZ, minZ); z <= Math.min(toZ, minZ + span - 1); z++) {
|
||||
markColumn(x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {}
|
||||
|
||||
@Override
|
||||
public void playSoundToNearExcept(EntityPlayer player, String soundName, double x, double y, double z,
|
||||
float volume, float pitch) {}
|
||||
|
||||
@Override
|
||||
public void spawnParticle(String particleName, double x, double y, double z, double velocityX, double velocityY,
|
||||
double velocityZ) {}
|
||||
|
||||
@Override
|
||||
public void onEntityCreate(Entity entity) {}
|
||||
|
||||
@Override
|
||||
public void onEntityDestroy(Entity entity) {}
|
||||
|
||||
@Override
|
||||
public void playRecord(String recordName, int x, int y, int z) {}
|
||||
|
||||
@Override
|
||||
public void broadcastSound(int soundId, int x, int y, int z, int data) {}
|
||||
|
||||
@Override
|
||||
public void playAuxSFX(EntityPlayer player, int sfxType, int x, int y, int z, int data) {}
|
||||
|
||||
@Override
|
||||
public void destroyBlockPartially(int breakerId, int x, int y, int z, int progress) {}
|
||||
|
||||
@Override
|
||||
public void onStaticEntitiesChanged() {}
|
||||
|
||||
private void markChanged(int x, int y, int z) {
|
||||
// spawn modes depend on the block below and the block above
|
||||
final boolean isInBox = x >= minX && x < minX + span && z >= minZ && z < minZ + span;
|
||||
if (isInBox && y >= minY - 1 && y <= minY + height) markColumn(x, z);
|
||||
}
|
||||
|
||||
private void markColumn(int x, int z) {
|
||||
dirty[findColumn(x, z)] = true;
|
||||
hasDirtyColumns = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package codechicken.nei.moboverlay;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Each style is a mark drawn on every block where mobs can spawn. {@code world.overlays.mobOverlayStyle} is saved as
|
||||
* {@link #formatName()}.
|
||||
*/
|
||||
public enum MobOverlayStyle {
|
||||
|
||||
/** NEI's original cross from corner to corner. */
|
||||
X(GL11.GL_LINES) {
|
||||
|
||||
@Override
|
||||
public void addVertices(MobOverlayFace face) {
|
||||
face.addVertex(0, 0);
|
||||
face.addVertex(1, 1);
|
||||
face.addVertex(1, 0);
|
||||
face.addVertex(0, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A light tint, with a stronger outline around each area of one spawn mode, like a connected texture.
|
||||
*/
|
||||
CONNECTED(GL11.GL_QUADS) {
|
||||
|
||||
@Override
|
||||
public void addVertices(MobOverlayFace face) {
|
||||
final double OUTLINE_WIDTH = 1.0 / 16;
|
||||
face.addFullFace();
|
||||
face.applyColor(Colouring.OVERLAY, MARK_ALPHA);
|
||||
for (int offsetX = -1; offsetX <= 1; offsetX++) {
|
||||
for (int offsetZ = -1; offsetZ <= 1; offsetZ++) {
|
||||
// a corner cell is drawn when either edge next to the corner is outlined, and on an inside corner
|
||||
final boolean isOutline = !(face.isJoined(offsetX, offsetZ) && face.isJoined(offsetX, 0)
|
||||
&& face.isJoined(0, offsetZ));
|
||||
if (isOutline) face.addCell(offsetX, offsetZ, OUTLINE_WIDTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** A small square in the middle of the block, with no tint. */
|
||||
SQUARE(GL11.GL_QUADS) {
|
||||
|
||||
@Override
|
||||
public void addVertices(MobOverlayFace face) {
|
||||
final double CENTRE = 0.5;
|
||||
final double HALF_SIDE = 7.0 / 64;
|
||||
face.addVertex(CENTRE - HALF_SIDE, CENTRE - HALF_SIDE);
|
||||
face.addVertex(CENTRE - HALF_SIDE, CENTRE + HALF_SIDE);
|
||||
face.addVertex(CENTRE + HALF_SIDE, CENTRE + HALF_SIDE);
|
||||
face.addVertex(CENTRE + HALF_SIDE, CENTRE - HALF_SIDE);
|
||||
}
|
||||
},
|
||||
|
||||
/** The pufferfish sticker. */
|
||||
PUFFERFISH("pufferfish", Colouring.STICKER) {
|
||||
|
||||
@Override
|
||||
public void addVertices(MobOverlayFace face) {
|
||||
face.addTexturedFace();
|
||||
}
|
||||
};
|
||||
|
||||
public static final MobOverlayStyle DEFAULT = CONNECTED;
|
||||
|
||||
private static final float TINT_ALPHA = 0.15F;
|
||||
// the square and the outline cover a small part of the block and need more opacity than the tint to stay visible
|
||||
private static final float MARK_ALPHA = 0.45F;
|
||||
|
||||
public final int glMode;
|
||||
@Nullable
|
||||
public final ResourceLocation texture;
|
||||
public final Colouring colouring;
|
||||
|
||||
MobOverlayStyle(int glMode) {
|
||||
this.glMode = glMode;
|
||||
this.texture = null;
|
||||
this.colouring = Colouring.OVERLAY;
|
||||
}
|
||||
|
||||
MobOverlayStyle(String textureName, Colouring colouring) {
|
||||
this.glMode = GL11.GL_QUADS;
|
||||
this.texture = new ResourceLocation("nei", "textures/overlay/" + textureName + ".png");
|
||||
this.colouring = colouring;
|
||||
}
|
||||
|
||||
/** Case-insensitive. Returns {@link #DEFAULT} for an unknown name. */
|
||||
public static MobOverlayStyle parseStyle(String rawStyle) {
|
||||
for (MobOverlayStyle candidate : values()) {
|
||||
if (candidate.formatName().equalsIgnoreCase(rawStyle)) return candidate;
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/** @return the {@link #formatName} of every style, separated by commas */
|
||||
public static String formatAllNames() {
|
||||
final StringJoiner names = new StringJoiner(", ");
|
||||
for (MobOverlayStyle style : values()) {
|
||||
names.add(style.formatName());
|
||||
}
|
||||
return names.toString();
|
||||
}
|
||||
|
||||
public String formatName() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public float computeAlpha() {
|
||||
return switch (this) {
|
||||
case CONNECTED -> TINT_ALPHA;
|
||||
case SQUARE -> MARK_ALPHA;
|
||||
// opaque lines for X, and the alpha of the PNG for PUFFERFISH
|
||||
case X, PUFFERFISH -> 1.0F;
|
||||
};
|
||||
}
|
||||
|
||||
public abstract void addVertices(MobOverlayFace face);
|
||||
|
||||
public enum Colouring {
|
||||
/** Yellow where mobs spawn only at night, red where mobs spawn at any time. */
|
||||
OVERLAY,
|
||||
/** The texture's colours, tinted red where mobs spawn at any time. */
|
||||
STICKER
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user