mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-26 14:24:07 +00:00
Compare commits
93
Commits
2.8.110-GTNH
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5faed21218 | ||
|
|
aa9da2e0a0 | ||
|
|
e9a3a494d5 | ||
|
|
ebe83f970f | ||
|
|
ad9beb7f90 | ||
|
|
68bdb51c6b | ||
|
|
e4e1964dec | ||
|
|
560618024f | ||
|
|
e2a2d2a9df | ||
|
|
7163630cb1 | ||
|
|
ab168ad6ba | ||
|
|
e13e3483f0 | ||
|
|
de26547b64 | ||
|
|
c49f8808fd | ||
|
|
1c6cc8b693 | ||
|
|
43ae1b613a | ||
|
|
402c4325a6 | ||
|
|
550e85c456 | ||
|
|
1673079cbb | ||
|
|
e6174c446a | ||
|
|
869b4571b0 | ||
|
|
35f5400ecb | ||
|
|
1e2801c891 | ||
|
|
1b3b495fec | ||
|
|
2844ea35bc | ||
|
|
c020ecbf1f | ||
|
|
a105a60a1a | ||
|
|
3a4a6734dc | ||
|
|
11f9194de9 | ||
|
|
a685efb3b7 | ||
|
|
9f3697bb67 | ||
|
|
18abb880f8 | ||
|
|
5868187410 | ||
|
|
f84c4ef2bd | ||
|
|
a54c414595 | ||
|
|
3c472a02cb | ||
|
|
382278bebb | ||
|
|
55c9811a2b | ||
|
|
0ec87a8b9e | ||
|
|
939845a9ed | ||
|
|
c9db437dd1 | ||
|
|
b1f20104a6 | ||
|
|
ab8d5f275f | ||
|
|
9e87f2b158 | ||
|
|
ab512d3fd8 | ||
|
|
4a9f17590b | ||
|
|
a845e6c3e5 | ||
|
|
4f3a0826fe | ||
|
|
6cd7697ca0 | ||
|
|
38dc14eca1 | ||
|
|
1fc824410b | ||
|
|
af6dba1269 | ||
|
|
2bf5a1cf9f | ||
|
|
2126ec78bc | ||
|
|
5f6209366a | ||
|
|
f5fc4e704d | ||
|
|
3d6c1c6e19 | ||
|
|
fda7688807 | ||
|
|
3f38e6f39b | ||
|
|
866b796411 | ||
|
|
7817640d96 | ||
|
|
bd595f739c | ||
|
|
4c74a01930 | ||
|
|
6ced1e0604 | ||
|
|
f8d00a5445 | ||
|
|
75d53b12ca | ||
|
|
f93d5977ed | ||
|
|
c4f9c54200 | ||
|
|
7d4c8b82ba | ||
|
|
c6170f9795 | ||
|
|
5a533c8994 | ||
|
|
d70a47a3a4 | ||
|
|
6c3db443d7 | ||
|
|
a51836fe9b | ||
|
|
139ca22e51 | ||
|
|
10fc78bdcf | ||
|
|
5fb5ab6698 | ||
|
|
f47a8766a9 | ||
|
|
4cc72444d9 | ||
|
|
b48956ef94 | ||
|
|
583d28ed12 | ||
|
|
384813347c | ||
|
|
2b28068382 | ||
|
|
2c204c25aa | ||
|
|
5e6ff1d63b | ||
|
|
53ce50b35a | ||
|
|
835c4f7e01 | ||
|
|
f8c1f39faf | ||
|
|
4c4716e64f | ||
|
|
c84ac27a18 | ||
|
|
47baee272c | ||
|
|
f55dc716e3 | ||
|
|
26beb079f0 |
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
-1
@@ -28,6 +28,7 @@ whitelist.json
|
||||
*.iws
|
||||
src/main/resources/mixins.*([!.]).json
|
||||
*.bat
|
||||
*.sh
|
||||
*.DS_Store
|
||||
!gradlew.bat
|
||||
.factorypath
|
||||
@@ -35,4 +36,5 @@ addon.local.gradle
|
||||
addon.local.gradle.kts
|
||||
addon.late.local.gradle
|
||||
addon.late.local.gradle.kts
|
||||
layout.json
|
||||
layout.json
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# NotEnoughItems Unofficial - By the GTNH Team
|
||||
|
||||
🌐 **Languages:** [English](README.md) | [Українська](README_uk.md)
|
||||
|
||||
A continuation of NotEnoughItems for 1.7.10 by the developers of Gregtech: New Horizons modpack, with features either inspired and/or backported from JustEnoughItems
|
||||
|
||||
We've tested this against all of the mods included in GTNH, as well as a limited set of other mods (like Reika's mods). Every effort has been made to maintain backwards compatibility, however the focus is on the mods contained in the GTNH modpack.
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# NotEnoughItems Unofficial — від команди GTNH
|
||||
|
||||
🌐 **Мови:** [English](README.md) | [Українська](README_uk.md)
|
||||
|
||||
Продовження модифікації NotEnoughItems для 1.7.10 від розробників збірки GregTech: New Horizons із функціями, натхненними та/або бекпортованими з JustEnoughItems.
|
||||
|
||||
Ми протестували цю версію з усіма модами, що входять до GTNH, а також із обмеженим набором інших модів (як-от моди від Reika). Було докладено всіх зусиль для збереження зворотної сумісності, однак основну увагу приділено модам зі збірки GTNH.
|
||||
|
||||
Якщо у вас виникли проблеми з NEI за межами збірки GTNH, ви можете повідомити про них у [GitHub-репозиторії GTNH NEI](https://github.com/GTNewHorizons/NotEnoughItems).
|
||||
|
||||
## Нові можливості:
|
||||
|
||||
* Швидкодія
|
||||
- Використовує паралельний потік для пошуку у списку предметів на кількох ядрах процесора, що в середньому пришвидшує пошук у 2–6 разів.
|
||||
- Завантажує обробники рецептів паралельно.
|
||||
* Текстове поле пошуку з більшістю очікуваних функцій — переміщення курсора вперед/назад, виділення тексту тощо.
|
||||
* Закладки! Перебуваєте у процесі крафту? Додайте потрібний рецепт до закладок за допомогою клавіші «A» або налаштуйте власну клавішу.
|
||||
* Перемикання видимості панелі закладок. Гаряча клавіша за замовчуванням — `B`. Меню «Підгрупи предметів» доступне лише тоді, коли панель закладок прихована.
|
||||
* Кнопки утиліт та читів вирівнюються в ряд та переносяться на новий рядок залежно від розміру графічного інтерфейсу.
|
||||
* Список предметів (`ItemList`) більше не генерується повторно з `ItemRegistry` під час кожного відкриття інвентарю.
|
||||
* Вкладки у стилі JEI (або творчого режиму) [За бажанням]. Примітка: вимагає явної підтримки для рендерингу `ItemStack`, інакше використовуються перші дві літери назви обробника.
|
||||
* Вкладки/обробники завантажуються з конфігураційного CSV-файлу в JAR-архіві (або за бажанням з папки `config`). Для авторів модів доступні IMC-повідомлення з NBT-даними `registerHandlerInfo` та `removeHandlerInfo` для додавання інформації про обробники з використанням тих самих полів, що й у CSV-файлі.
|
||||
* Пошук за допомогою синтаксису `@[Мод]->[предмет]`. Наприклад: `@Mod.gregtech->iron ingot`.
|
||||
* Перемикання між режимами рецептів, утиліт та читів за допомогою CTRL + ЛКМ по іконці гайкового ключа.
|
||||
* Інструменти/предмети з GT5u та інструменти з GT6 тепер мають коректно працювати з функцією накладення та перенесення рецепта.
|
||||
|
||||
### Обробник інформаційних сторінок
|
||||
Хочете додати інформацію про блок чи предмет, не створюючи для нього величезну підказку? Ви можете додати інформацію про будь-який блок чи предмет, зареєструвавши його в обробнику інформації.
|
||||
|
||||
Інформаційні сторінки відображаються під час пошуку рецептів або способів використання відповідного предмета. Усі відповідні предмети будуть циклічно змінюватися у верхній частині інформаційної сторінки.
|
||||
|
||||
Ваш мод може викликати:
|
||||
```java
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", nbt);
|
||||
```
|
||||
Де `nbt` — це `NBTTagCompound`, відформатований, як описано нижче.
|
||||
|
||||
| Тег | Тип | Опис |
|
||||
|------------------|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `filter` | `String` | Текстовий фільтр для визначення предметів, для яких має відображатися ця інформаційна сторінка.<br/>Можна використовувати `modname:itemname`, метадані, теги OreDictionary та регулярні вирази. Повний формат можна переглянути у `config/NEI/collapsibleitems.cfg`. |
|
||||
| `page` / `pages` | `String` / `String NBTTagList` | Безпосередньо текст для відображення. Різницю між одно- та багатосторінковими повідомленнями див. нижче.<br/>Обидва формати підтримують автоматичний переклад наданих рядків. |
|
||||
|
||||
#### Односторінковий формат
|
||||
|
||||
Використовуйте текстовий тег `"page"`, якщо хочете додати лише одну сторінку інформації:
|
||||
```java
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setString("filter", "minecraft:log 3");
|
||||
tag.setString("page", "Exotic wood found in tropical climates.");
|
||||
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
|
||||
```
|
||||
|
||||
#### Багатосторінковий формат
|
||||
|
||||
Використовуйте тег `"pages"` як `NBTTagList` із рядків, якщо потрібно додати понад одну сторінку:
|
||||
```java
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setString("filter", "minecraft:diamond_sword");
|
||||
|
||||
NBTTagList pages = new NBTTagList();
|
||||
pages.appendTag(new NBTTagString("A powerful melee weapon."));
|
||||
pages.appendTag(new NBTTagString("Can be enchanted for extra effects."));
|
||||
|
||||
tag.setTag("pages", pages);
|
||||
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", tag);
|
||||
```
|
||||
* Кожен елемент списку — це одна сторінка.
|
||||
* Сторінки з’являтимуться в NEI з кнопками для їх гортання.
|
||||
|
||||
#### Відповідність предметів
|
||||
|
||||
Усі предмети, що відповідають фільтру `filter`, відображатимуть вашу інформаційну сторінку. Повний формат фільтра див. у `config/NEI/collapsibleitems.cfg`.
|
||||
|
||||
#### Файл конфігурації інформації про предмети
|
||||
|
||||
Якщо ви творець збірки модів і хочете створити власні інформаційні сторінки для предметів, їх можна легко додати у файлі `config/NEI/informationpages.cfg`, де також наведено ще кілька прикладів.
|
||||
|
||||
## Інші примітки:
|
||||
|
||||
* Вилучено стиль TMI
|
||||
* Вилучено збереження/завантаження стану інвентарю
|
||||
|
||||
## Ліцензія
|
||||
|
||||
Авторські права на модифікації від команди GTNH (c) 2019-2024 належать mitchej123 та команді GTNH.
|
||||
|
||||
Поширюється за ліцензією LGPL-3.0 або новішої версії — використовуйте цей код на власний розсуд, але, будь ласка, діліться з розробниками будь-якими внесеними вами змінами.
|
||||
|
||||
Окремі частини коду натхненні, запозичені або бекпортовані з [JEI](https://github.com/mezz/JustEnoughItems/tree/1.12) за ліцензією MIT.
|
||||
|
||||
Авторські права на оригінальний код (c) 2014-2015 належать mezz; код поширювався за ліцензією MIT.
|
||||
+11
-5
@@ -5,12 +5,13 @@ configurations.create('gtnhlibEmbed') {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.16:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.20: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')
|
||||
|
||||
compileOnly('maven.modrinth:forge-essentials:7.5.1')
|
||||
compileOnly('com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.14-alpha:dev')
|
||||
|
||||
antlr('org.antlr:antlr4:4.13.2')
|
||||
shadowImplementation('org.antlr:antlr4-runtime:4.13.2')
|
||||
@@ -22,13 +23,18 @@ dependencies {
|
||||
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.20") { transitive = false }
|
||||
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.20") { 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
|
||||
def gtnhlibEmbedContents = configurations.gtnhlibEmbed.elements.map { files ->
|
||||
files.collect { embedArchiveOperations.zipTree(it.asFile) }
|
||||
}
|
||||
|
||||
[tasks.jar, tasks.shadowJar].each {
|
||||
it.configure {
|
||||
from({ project.configurations.getByName('gtnhlibEmbed').collect { zipTree(it) } }) {
|
||||
from(gtnhlibEmbedContents) {
|
||||
include 'com/gtnewhorizon/gtnhlib/color/ColorResource.class'
|
||||
include 'com/gtnewhorizon/gtnhlib/color/ColorResource$*.class'
|
||||
}
|
||||
|
||||
@@ -186,6 +186,11 @@ modrinthRelations = required-project\:codechickencore-unofficial
|
||||
# Leave this empty if you don't want to publish on CurseForge.
|
||||
curseForgeProjectId = 358228
|
||||
|
||||
# Comma-separated supported CurseForge environments: client, server (case-insensitive).
|
||||
# Use client or server for mods that only support one environment. Defaults to client,server.
|
||||
# These tags describe where the mod can run, not whether installation is required on both sides.
|
||||
curseForgeEnvironments = client,server
|
||||
|
||||
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
|
||||
# Syntax: type1:name1;type2:name2;...
|
||||
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9064f22734b42ac03e85df04b632e167/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/4d0277c49991306ac78048580fe34bcf/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/539ea72837c6244075f8ee037babc29e/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/876dbbf00bf4ef6190f7da074cee903a/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a9fa3b809e484a1e1ac4308a5dda661a/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/aa99913fa6d767d899ffc96082da01ca/redirect
|
||||
toolchainVersion=25
|
||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,7 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
# gradlew start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
@@ -29,7 +29,7 @@
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
# ksh gradlew
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
Vendored
+12
-23
@@ -19,12 +19,12 @@
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem gradlew startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@@ -51,7 +51,7 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
@@ -65,29 +65,18 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
@rem Execute gradlew
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.34'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.ChatAllowedCharacters;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@@ -30,6 +32,8 @@ public class AutoFocusWidget implements IContainerInputHandler {
|
||||
|
||||
protected boolean autofocus = false;
|
||||
protected Point mouse;
|
||||
protected Point pendingMouse;
|
||||
protected GuiTextField textInputHolder;
|
||||
|
||||
public AutoFocusWidget() {
|
||||
GuiContainerManager.addInputHandler(this);
|
||||
@@ -38,32 +42,83 @@ public class AutoFocusWidget implements IContainerInputHandler {
|
||||
public void load(GuiContainer gui) {
|
||||
this.autofocus = LayoutManager.searchField.isVisible() && NEIClientConfig.searchWidgetAutofocus() != 0
|
||||
&& isAllowedGuiAutoSearchFocus(gui);
|
||||
this.mouse = getMousePosition();
|
||||
this.mouse = null;
|
||||
this.pendingMouse = null;
|
||||
|
||||
if (this.autofocus) {
|
||||
beginTextInput();
|
||||
}
|
||||
}
|
||||
|
||||
protected void beginTextInput() {
|
||||
if (this.textInputHolder == null) {
|
||||
this.textInputHolder = new GuiTextField(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
this.textInputHolder.setFocused(true);
|
||||
}
|
||||
|
||||
protected void endTextInput() {
|
||||
if (this.textInputHolder != null) {
|
||||
this.textInputHolder.setFocused(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void guiTick() {
|
||||
if (!this.autofocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Point current = getMousePosition();
|
||||
|
||||
if (this.mouse == null) {
|
||||
if (current.equals(this.pendingMouse)) {
|
||||
this.mouse = current;
|
||||
}
|
||||
|
||||
this.pendingMouse = current;
|
||||
} else if (!this.mouse.equals(current)) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
protected void cancel() {
|
||||
if (this.autofocus) {
|
||||
this.autofocus = false;
|
||||
endTextInput();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) {
|
||||
|
||||
if (this.autofocus) {
|
||||
this.autofocus = false;
|
||||
|
||||
if (!this.mouse.equals(getMousePosition())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.searchWidgetAutofocus() == 2
|
||||
&& GameSettings.isKeyDown(Minecraft.getMinecraft().gameSettings.keyBindInventory)) {
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyCode == Keyboard.KEY_RETURN || keyCode == Keyboard.KEY_NUMPADENTER
|
||||
|| keyCode == Keyboard.KEY_ESCAPE) {
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ChatAllowedCharacters.isAllowedCharacter(keyChar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.autofocus = false;
|
||||
|
||||
LayoutManager.searchField.setFocus(true);
|
||||
endTextInput();
|
||||
|
||||
final String oldText = LayoutManager.searchField.text();
|
||||
LayoutManager.searchField.handleKeyPress(keyCode, keyChar);
|
||||
return true;
|
||||
|
||||
return !LayoutManager.searchField.text().equals(oldText);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -76,13 +131,13 @@ public class AutoFocusWidget implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,22 +146,22 @@ public class AutoFocusWidget implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {
|
||||
this.autofocus = false;
|
||||
cancel();
|
||||
}
|
||||
|
||||
protected boolean isAllowedGuiAutoSearchFocus(GuiContainer gui) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -799,15 +800,15 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("bookmark.group.toggle_crafting_chain"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.remove_recipe"),
|
||||
KeyManager.getKeyName("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.remove_recipe"));
|
||||
|
||||
if (BookmarkContainerInfo.getBookmarkContainerHandler(NEIClientUtils.getGuiContainer()) != null) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
|
||||
KeyManager.getKeyName("bookmark.pull_items"),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
|
||||
}
|
||||
|
||||
@@ -903,19 +904,21 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
}
|
||||
|
||||
private List<String> recipeChainTooltip(int groupId, List<String> currenttip) {
|
||||
boolean crafting = this.grid.getGroup(groupId).crafting != null;
|
||||
boolean crafting = this.grid.isCraftingMode(groupId);
|
||||
|
||||
if (this.recipeChainTooltipLineHandler == null || this.recipeChainTooltipLineHandler.groupId != groupId
|
||||
|| this.recipeChainTooltipLineHandler.crafting != crafting) {
|
||||
this.recipeChainTooltipLineHandler = new RecipeChainTooltipLineHandler(
|
||||
groupId,
|
||||
crafting,
|
||||
this.grid.createRecipeChainMath(groupId));
|
||||
final RecipeChainMath recipeChainMath = this.grid.createRecipeChainMath(groupId);
|
||||
|
||||
if (recipeChainMath != null) {
|
||||
this.recipeChainTooltipLineHandler = new RecipeChainTooltipLineHandler(
|
||||
groupId,
|
||||
crafting,
|
||||
recipeChainMath);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.recipeChainTooltipLineHandler != null) {
|
||||
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeChainTooltipLineHandler));
|
||||
}
|
||||
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeChainTooltipLineHandler));
|
||||
|
||||
return currenttip;
|
||||
}
|
||||
@@ -1020,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();
|
||||
@@ -1046,15 +1053,9 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
final BookmarksGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (slot != null) {
|
||||
int multiplier = 1;
|
||||
|
||||
if (NEIClientUtils.altKey()) {
|
||||
multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
|
||||
if (multiplier == 0) {
|
||||
multiplier = StackInfo.itemStackToNBT(slot.getItemStack()).hasKey("gtFluidName") ? 144
|
||||
: slot.getItemStack().getMaxStackSize();
|
||||
}
|
||||
}
|
||||
final long multiplier = NEIClientUtils.altKey()
|
||||
? NEIClientUtils.getScrollMultiplier(slot.getItemStack())
|
||||
: 1;
|
||||
|
||||
this.grid.shiftItemAmount(slot.itemIndex, multiplier * shift);
|
||||
return true;
|
||||
@@ -1096,20 +1097,23 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
}
|
||||
|
||||
activeStack = items.get((items.size() - shift + stackIndex) % items.size());
|
||||
|
||||
this.acceptsFollowingTooltipLineHandler.setActiveStack(activeStack);
|
||||
this.grid.getBookmarkItem(Math.abs(itemIndex)).itemStack = activeStack;
|
||||
this.grid.onItemsChanged();
|
||||
|
||||
final int targetItemIndex = Math.abs(itemIndex);
|
||||
final BookmarkItem oldItem = this.grid.getBookmarkItem(targetItemIndex);
|
||||
final BookmarkItem newItem = oldItem.copyWithPerm(activeStack);
|
||||
|
||||
this.grid.replaceBookmarkItem(targetItemIndex, newItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1294,7 +1298,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
public BookmarkRecipeId getBookmarkRecipeId(int itemIndex) {
|
||||
BookmarkItem item = this.grid.getCalculatedItem(itemIndex);
|
||||
return item == null || item.type == BookmarkItemType.INGREDIENT || item.recipeId == null ? null
|
||||
: new BookmarkRecipeId(item.recipeId.getHandleName(), item.recipeId.getIngredients());
|
||||
: new BookmarkRecipeId(item.recipeId.getHandlerName(), item.recipeId.getIngredients());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
||||
@@ -48,6 +48,7 @@ import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
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.StackInfo;
|
||||
@@ -279,6 +280,7 @@ public class ClientHandler {
|
||||
PacketCustom.assignHandler(NEICPH.channel, new NEICPH());
|
||||
FMLCommonHandler.instance().bus().register(instance);
|
||||
MinecraftForge.EVENT_BUS.register(instance);
|
||||
MinecraftForge.EVENT_BUS.register(new GuiCraftingTree.EventHandler());
|
||||
|
||||
API.registerHighlightHandler(new DefaultHighlightHandler(), ItemInfo.Layout.HEADER);
|
||||
HUDRenderer.load();
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTBase.NBTPrimitive;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.StatCollector;
|
||||
@@ -112,7 +113,7 @@ public class CollapsibleItems {
|
||||
group.setFilter(itemStr);
|
||||
}
|
||||
|
||||
if (group != null && group.filter != null) {
|
||||
if (group.filter != null) {
|
||||
addGroup(group);
|
||||
group = new GroupItem();
|
||||
}
|
||||
@@ -227,17 +228,17 @@ public class CollapsibleItems {
|
||||
|
||||
if (NEIClientConfig.world.nbt.hasKey(STATE_KEY)) {
|
||||
NBTTagCompound states = NEIClientConfig.world.nbt.getCompoundTag(STATE_KEY);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, NBTPrimitive> list = (Map<String, NBTPrimitive>) states.tagMap;
|
||||
final Map<String, NBTBase> list = states.tagMap;
|
||||
final Map<String, GroupItem> mapping = new HashMap<>();
|
||||
|
||||
for (GroupItem group : CollapsibleItems.groups) {
|
||||
mapping.put(group.guid, group);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, NBTPrimitive> nbtEntry : list.entrySet()) {
|
||||
for (Map.Entry<String, NBTBase> nbtEntry : list.entrySet()) {
|
||||
if (mapping.containsKey(nbtEntry.getKey())) {
|
||||
mapping.get(nbtEntry.getKey()).expanded = nbtEntry.getValue().func_150290_f() == 1;
|
||||
mapping.get(nbtEntry.getKey()).expanded = ((NBTPrimitive) nbtEntry.getValue()).func_150290_f()
|
||||
== 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ public class ColorUtils {
|
||||
buttonLabelNormal = color.rgb("buttonLabelNormal", "0xE0E0E0"),
|
||||
buttonLabelHover = color.rgb("buttonLabelHover", "0xFFFFA0"),
|
||||
buttonLabelDisabled = color.rgb("buttonLabelDisabled", "0x601010"),
|
||||
buttonLabelInactive = color.rgb("buttonLabelInactive", "0xA0A0A0");
|
||||
buttonLabelInactive = color.rgb("buttonLabelInactive", "0xA0A0A0"),
|
||||
|
||||
craftingTreeLine = color.argb("craftingTreeLine", "0xFF888888");
|
||||
// spotless:on
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
|
||||
|
||||
public abstract class CompositeTooltipLineHandler implements ITooltipLineHandler {
|
||||
|
||||
protected static final int SCREEN_MARGIN = 8 * 2 + 10;
|
||||
protected static final int RESERVED_HEIGHT = 40;
|
||||
|
||||
protected final List<ItemsTooltipLineHandler> lines = new ArrayList<>();
|
||||
protected final Dimension size = new Dimension();
|
||||
|
||||
protected boolean created = false;
|
||||
protected int screenHeight = 0;
|
||||
|
||||
protected abstract void createLines();
|
||||
|
||||
protected boolean needsUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getSize() {
|
||||
ensureCreated();
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int x, int y) {
|
||||
if (this.size.height == 0) return;
|
||||
|
||||
for (ItemsTooltipLineHandler line : this.lines) {
|
||||
line.draw(x, y);
|
||||
y += line.getSize().height;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ensureCreated() {
|
||||
boolean update = needsUpdate();
|
||||
update = this.screenHeight != (this.screenHeight = GuiDraw.displaySize().height) || update;
|
||||
|
||||
if (this.created && !update) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.created = true;
|
||||
this.lines.clear();
|
||||
this.size.setSize(0, 0);
|
||||
|
||||
createLines();
|
||||
fitToScreen();
|
||||
|
||||
for (ItemsTooltipLineHandler line : this.lines) {
|
||||
this.size.width = Math.max(this.size.width, line.getSize().width);
|
||||
this.size.height += line.getSize().height;
|
||||
}
|
||||
}
|
||||
|
||||
protected void fitToScreen() {
|
||||
if (this.lines.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ItemsTooltipLineHandler> ordered = new ArrayList<>(this.lines);
|
||||
ordered.sort(Comparator.comparingInt(ItemsTooltipLineHandler::getRows));
|
||||
|
||||
int budget = availableHeight() / ItemsTooltipLineHandler.SLOT_SIZE;
|
||||
int rest = ordered.size();
|
||||
|
||||
for (ItemsTooltipLineHandler line : ordered) {
|
||||
final int fair = Math.max(1, budget / rest);
|
||||
|
||||
if (line.getRows() > fair) {
|
||||
line.setMaxRows(fair);
|
||||
}
|
||||
|
||||
budget -= line.getRows();
|
||||
rest--;
|
||||
}
|
||||
}
|
||||
|
||||
protected int availableHeight() {
|
||||
return GuiDraw.displaySize().height - SCREEN_MARGIN
|
||||
- RESERVED_HEIGHT
|
||||
- this.lines.size() * ItemsTooltipLineHandler.headerHeight();
|
||||
}
|
||||
|
||||
protected static int maxLineRows() {
|
||||
final int height = GuiDraw.displaySize().height - SCREEN_MARGIN
|
||||
- RESERVED_HEIGHT
|
||||
- ItemsTooltipLineHandler.headerHeight();
|
||||
return Math.max(1, height / ItemsTooltipLineHandler.SLOT_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,14 +79,13 @@ public class ContainerCreativeInv extends Container {
|
||||
@Override
|
||||
public ItemStack transferStackInSlot(EntityPlayer par1EntityPlayer, int slotIndex) {
|
||||
ItemStack transferredStack = null;
|
||||
Slot slot = (Slot) inventorySlots.get(slotIndex);
|
||||
Slot slot = inventorySlots.get(slotIndex);
|
||||
|
||||
if (slot != null && slot.getHasStack()) {
|
||||
ItemStack stack = slot.getStack();
|
||||
transferredStack = stack.copy();
|
||||
|
||||
if (stack.getItem() instanceof ItemArmor) {
|
||||
ItemArmor armor = (ItemArmor) stack.getItem();
|
||||
if (stack.getItem() instanceof ItemArmor armor) {
|
||||
if (!getSlot(90 + armor.armorType).getHasStack()) {
|
||||
getSlot(90 + armor.armorType).putStack(transferredStack);
|
||||
slot.putStack(null);
|
||||
|
||||
@@ -8,7 +8,6 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.ContainerEnchantment;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
@@ -164,14 +163,14 @@ public class ContainerEnchantmentModifier extends ContainerEnchantment {
|
||||
|
||||
public boolean addEnchantment(int e, int level) {
|
||||
if (e < Enchantment.enchantmentsList.length && Enchantment.enchantmentsList[e] != null) {
|
||||
((Slot) inventorySlots.get(0)).getStack().addEnchantment(Enchantment.enchantmentsList[e], level);
|
||||
inventorySlots.get(0).getStack().addEnchantment(Enchantment.enchantmentsList[e], level);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeEnchantment(int e) {
|
||||
ItemStack stack = ((Slot) inventorySlots.get(0)).getStack();
|
||||
ItemStack stack = inventorySlots.get(0).getStack();
|
||||
NBTTagList nbttaglist = stack.getEnchantmentTagList();
|
||||
if (nbttaglist != null) {
|
||||
for (int i = 0; i < nbttaglist.tagCount(); i++) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DefaultHighlightHandler implements IHighlightHandler {
|
||||
String name = null;
|
||||
try {
|
||||
String s = GuiContainerManager.itemDisplayNameShort(stack);
|
||||
if (s != null && !s.endsWith("Unnamed")) name = s;
|
||||
if (!s.endsWith("Unnamed")) name = s;
|
||||
|
||||
if (name != null) currenttip.add(name);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class FastTransferManager {
|
||||
|
||||
public static void clearSlots(Container container) {
|
||||
for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++)
|
||||
((Slot) container.inventorySlots.get(slotNo)).putStack(null);
|
||||
container.inventorySlots.get(slotNo).putStack(null);
|
||||
}
|
||||
|
||||
public void performMassTransfer(GuiContainer window, int fromSlot, int toSlot, ItemStack heldStack) {
|
||||
@@ -289,7 +289,7 @@ public class FastTransferManager {
|
||||
|
||||
if (held == null) return -1;
|
||||
|
||||
for (int i = 0; held != null && i < window.inventorySlots.inventorySlots.size(); i++) {
|
||||
for (int i = 0; i < window.inventorySlots.inventorySlots.size(); i++) {
|
||||
final Slot slot = window.inventorySlots.inventorySlots.get(i);
|
||||
|
||||
if (slot.inventory instanceof InventoryPlayer && !slot.getHasStack() && slot.isItemValid(held)) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.io.InputStream;
|
||||
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;
|
||||
@@ -16,6 +17,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -225,7 +227,7 @@ public class FavoriteRecipes {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FAVORITE_ITEM.equals(recipeId.getHandleName()) || RecipeHandlerRef.of(recipeId) != null) {
|
||||
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
|
||||
manualFavorites.add(stack, recipeId);
|
||||
} else {
|
||||
hasErrors = true;
|
||||
@@ -314,6 +316,10 @@ public class FavoriteRecipes {
|
||||
load();
|
||||
}
|
||||
|
||||
public static boolean hasManual() {
|
||||
return manualFavorites.size() > 0;
|
||||
}
|
||||
|
||||
public static boolean containsManual(ItemStack stack) {
|
||||
return getManualFavorite(stack) != null;
|
||||
}
|
||||
@@ -322,6 +328,21 @@ public class FavoriteRecipes {
|
||||
return getFavorite(stack) != null;
|
||||
}
|
||||
|
||||
public static void toggleFavoriteItem(ItemStack stack) {
|
||||
if (stack == null) return;
|
||||
|
||||
if (containsManual(stack)) {
|
||||
setFavorite(stack, null);
|
||||
} else {
|
||||
setFavorite(
|
||||
stack,
|
||||
RecipeId.of(
|
||||
stack,
|
||||
FavoriteRecipes.FAVORITE_ITEM,
|
||||
Arrays.asList(new ItemStack(Blocks.fire, 1), stack)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
|
||||
if (stack == null) return;
|
||||
|
||||
|
||||
@@ -82,8 +82,8 @@ public class GuiEnchantmentModifier extends GuiContainer {
|
||||
|
||||
private void changeLevel(int i) {
|
||||
container.level += i;
|
||||
((GuiButton) buttonList.get(0)).enabled = container.level != 1;
|
||||
((GuiButton) buttonList.get(1)).enabled = container.level != 10;
|
||||
buttonList.get(0).enabled = container.level != 1;
|
||||
buttonList.get(1).enabled = container.level != 10;
|
||||
}
|
||||
|
||||
protected void mouseClicked(int i, int j, int k) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public class HUDRenderer implements IKeyStateTracker {
|
||||
|
||||
@Override
|
||||
public void tickKeyStates() {
|
||||
if (KeyManager.keyStates.get("world.highlight_tips").down) {
|
||||
if (KeyManager.isPressed("world.highlight_tips")) {
|
||||
ConfigTag tag = NEIClientConfig.getSetting("world.highlight_tips");
|
||||
tag.setBooleanValue(!tag.getBooleanValue());
|
||||
}
|
||||
|
||||
@@ -30,14 +30,7 @@ public class InfiniteStackSizeHandler implements IInfiniteItemHandler {
|
||||
|
||||
@Override
|
||||
public void replenishInfiniteStack(InventoryPlayer inv, int slotNo) {
|
||||
ItemStack stack = inv.getStackInSlot(slotNo);
|
||||
stack.stackSize = 111;
|
||||
|
||||
for (int i = 0; i < inv.getSizeInventory(); i++) {
|
||||
if (i == slotNo) continue;
|
||||
|
||||
if (NEIServerUtils.areStacksSameType(stack, inv.getStackInSlot(i))) inv.setInventorySlotContents(i, null);
|
||||
}
|
||||
inv.getStackInSlot(slotNo).stackSize = 111;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,11 +6,14 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
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;
|
||||
@@ -21,7 +24,6 @@ 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;
|
||||
@@ -39,7 +41,7 @@ public class ItemCraftablesPanel
|
||||
public CraftablesGridSlot(int slotIndex, int itemIndex, ItemStack itemStack, RecipeId recipeId) {
|
||||
super(slotIndex, itemIndex, itemStack);
|
||||
this.realAmount = StackInfo.getAmount(itemStack);
|
||||
this.isFluidDisplay = StackInfo.itemStackToNBT(itemStack).hasKey("gtFluidName");
|
||||
this.isFluidDisplay = StackInfo.isFluidDisplayItem(itemStack);
|
||||
this.recipeId = recipeId;
|
||||
}
|
||||
|
||||
@@ -82,6 +84,42 @@ public class ItemCraftablesPanel
|
||||
|
||||
}
|
||||
|
||||
private static class CraftablesSnapshot {
|
||||
|
||||
final GuiContainer guiContainer;
|
||||
final ItemStackAmount inventory;
|
||||
final boolean showOnlyFavorites;
|
||||
final int maxCandidates;
|
||||
|
||||
CraftablesSnapshot(GuiContainer guiContainer, ItemStackAmount inventory, boolean showOnlyFavorites,
|
||||
int maxCandidates) {
|
||||
this.guiContainer = guiContainer;
|
||||
this.inventory = inventory;
|
||||
this.showOnlyFavorites = showOnlyFavorites;
|
||||
this.maxCandidates = maxCandidates;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RecipeCandidate {
|
||||
|
||||
final IRecipeHandler handler;
|
||||
final int recipeIndex;
|
||||
final List<PositionedStack> ingredientStacks;
|
||||
final ItemStack sortKeyItem;
|
||||
|
||||
RecipeCandidate(IRecipeHandler handler, int recipeIndex, List<PositionedStack> ingredientStacks,
|
||||
ItemStack sortKeyItem) {
|
||||
this.handler = handler;
|
||||
this.recipeIndex = recipeIndex;
|
||||
this.ingredientStacks = ingredientStacks;
|
||||
this.sortKeyItem = sortKeyItem;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<ItemStack> ITEM_SORT_COMPARATOR = Comparator
|
||||
.comparing(FavoriteRecipes::containsManual).thenComparing(FavoriteRecipes::contains).reversed()
|
||||
.thenComparing(ItemSorter.instance);
|
||||
|
||||
public static List<String> guiBlacklist = new ArrayList<>();
|
||||
private final int MIN_DELAY = 600;
|
||||
|
||||
@@ -93,6 +131,16 @@ public class ItemCraftablesPanel
|
||||
private long lastPlayerInventorySync = 0;
|
||||
private int lastFavoritesCount = 0;
|
||||
|
||||
private volatile CraftablesSnapshot pendingSnapshot;
|
||||
private final AtomicReference<Map<ItemStack, RecipeId>> pendingResult = new AtomicReference<>();
|
||||
private final RestartableTask task = new RestartableTask("NEI Craftables Panel") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
pendingResult.set(generateCraftables(ItemCraftablesPanel.this.pendingSnapshot));
|
||||
}
|
||||
};
|
||||
|
||||
public ItemCraftablesPanel() {
|
||||
this.grid = new ItemsGrid<>() {
|
||||
|
||||
@@ -114,12 +162,7 @@ public class ItemCraftablesPanel
|
||||
|
||||
if (!ItemCraftablesPanel.this.availableRecipes.isEmpty()) {
|
||||
final List<Map.Entry<ItemStack, RecipeId>> entries = ItemCraftablesPanel.this.availableRecipes
|
||||
.entrySet().stream()
|
||||
.sorted(
|
||||
Map.Entry.comparingByKey(
|
||||
Comparator.comparing(FavoriteRecipes::containsManual)
|
||||
.thenComparing(FavoriteRecipes::contains).reversed()
|
||||
.thenComparing(ItemSorter.instance)))
|
||||
.entrySet().stream().sorted(Map.Entry.comparingByKey(ITEM_SORT_COMPARATOR))
|
||||
.limit(maxSlotIndex).collect(Collectors.toList());
|
||||
int slotIndex = 0;
|
||||
int itemIndex = 0;
|
||||
@@ -195,10 +238,20 @@ public class ItemCraftablesPanel
|
||||
|
||||
public void update() {
|
||||
this.splittingLineColor = NEIClientConfig.getSetting("inventory.craftables.color").getHexValue();
|
||||
applyPendingResult();
|
||||
updateCraftables();
|
||||
super.update();
|
||||
}
|
||||
|
||||
private void applyPendingResult() {
|
||||
final Map<ItemStack, RecipeId> result = this.pendingResult.getAndSet(null);
|
||||
|
||||
if (result != null && NEIClientConfig.showCraftablesPanelWidget()) {
|
||||
this.availableRecipes = result;
|
||||
this.grid.onGridChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCraftables() {
|
||||
|
||||
if (!NEIClientConfig.showCraftablesPanelWidget()) {
|
||||
@@ -219,8 +272,14 @@ public class ItemCraftablesPanel
|
||||
this.lastPlayerInventory = inv;
|
||||
this.lastGuiContainer = firstGui;
|
||||
this.lastFavoritesCount = FavoriteRecipes.size();
|
||||
this.availableRecipes = generateCraftables(firstGui);
|
||||
this.grid.onGridChanged();
|
||||
final int maxRows = NEIClientConfig.getIntSetting("inventory.craftables.useRows") + 2;
|
||||
final int maxCandidates = Math.max(this.grid.columns, 1) * maxRows;
|
||||
this.pendingSnapshot = new CraftablesSnapshot(
|
||||
firstGui,
|
||||
inv,
|
||||
showOnlyFavorites,
|
||||
maxCandidates);
|
||||
this.task.restart();
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -241,20 +300,31 @@ public class ItemCraftablesPanel
|
||||
|
||||
private GuiContainer getGuiContainer() {
|
||||
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
|
||||
return (firstGui instanceof GuiRecipe gui) ? gui.firstGui : firstGui;
|
||||
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : firstGui;
|
||||
}
|
||||
|
||||
private Map<ItemStack, RecipeId> generateCraftables(GuiContainer firstGui) {
|
||||
final List<IRecipeHandler> availableHandlers = getAvailableHandlers(firstGui);
|
||||
private Map<ItemStack, RecipeId> generateCraftables(CraftablesSnapshot snapshot) {
|
||||
final List<IRecipeHandler> availableHandlers = getAvailableHandlers(snapshot.guiContainer);
|
||||
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
|
||||
|
||||
if (!availableHandlers.isEmpty()) {
|
||||
final List<ItemStack> invStacks = ItemCraftablesPanel.this.lastPlayerInventory.values();
|
||||
final Map<Item, List<ItemStack>> invByItem = indexInventoryByItem(snapshot.inventory.values());
|
||||
|
||||
for (IRecipeHandler handler : availableHandlers) {
|
||||
availableRecipes.putAll(filterHandlerRecipes(handler, firstGui, invStacks));
|
||||
if (snapshot.showOnlyFavorites) {
|
||||
for (IRecipeHandler handler : availableHandlers) {
|
||||
availableRecipes.putAll(filterHandlerRecipes(handler, invByItem, true));
|
||||
}
|
||||
} else {
|
||||
final List<RecipeCandidate> candidates = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
for (IRecipeHandler handler : availableHandlers) {
|
||||
collectHandlerCandidates(handler, invByItem, candidates);
|
||||
}
|
||||
|
||||
candidates.sort(Comparator.comparing(c -> c.sortKeyItem, ITEM_SORT_COMPARATOR));
|
||||
candidates.subList(0, Math.min(candidates.size(), snapshot.maxCandidates)).parallelStream()
|
||||
.forEach(candidate -> buildCandidate(candidate, availableRecipes));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return availableRecipes;
|
||||
@@ -270,19 +340,126 @@ public class ItemCraftablesPanel
|
||||
return availableHandlers;
|
||||
}
|
||||
|
||||
private Map<ItemStack, RecipeId> filterHandlerRecipes(IRecipeHandler handler, GuiContainer firstGui,
|
||||
List<ItemStack> invStacks) {
|
||||
private static Map<Item, List<ItemStack>> indexInventoryByItem(List<ItemStack> invStacks) {
|
||||
final Map<Item, List<ItemStack>> index = new HashMap<>();
|
||||
|
||||
for (ItemStack stack : invStacks) {
|
||||
index.computeIfAbsent(stack.getItem(), i -> new ArrayList<>()).add(stack);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private void collectHandlerCandidates(IRecipeHandler handler, Map<Item, List<ItemStack>> invByItem,
|
||||
List<RecipeCandidate> candidates) {
|
||||
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
|
||||
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
|
||||
|
||||
if (existsIngredients(ingredientStacks, invByItem)) {
|
||||
final ItemStack sortKeyItem = getCheapResultItem(handler, recipeIndex);
|
||||
|
||||
if (sortKeyItem != null) {
|
||||
candidates.add(new RecipeCandidate(handler, recipeIndex, ingredientStacks, sortKeyItem));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void buildCandidate(RecipeCandidate candidate, Map<ItemStack, RecipeId> availableRecipes) {
|
||||
final ItemStack result = getCheapResult(candidate.handler, candidate.recipeIndex);
|
||||
|
||||
if (result != null) {
|
||||
final RecipeId recipeId = RecipeId.of(candidate.handler, candidate.recipeIndex, candidate.ingredientStacks);
|
||||
|
||||
synchronized (availableRecipes) {
|
||||
availableRecipes.put(result, recipeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int stackSize = 0;
|
||||
|
||||
for (PositionedStack result : results) {
|
||||
if (containsSameType(result, stack)) {
|
||||
stackSize += StackInfo.getAmount(result.item);
|
||||
}
|
||||
}
|
||||
|
||||
return StackInfo.withAmount(stack, stackSize);
|
||||
}
|
||||
|
||||
private static boolean containsSameType(PositionedStack pStack, ItemStack stack) {
|
||||
final ItemStack normalized = StackInfo.withAmount(stack, 0);
|
||||
|
||||
for (ItemStack permutation : pStack.items) {
|
||||
if (NEIServerUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(permutation, 0), normalized)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Map<ItemStack, RecipeId> filterHandlerRecipes(IRecipeHandler handler, Map<Item, List<ItemStack>> invByItem,
|
||||
boolean showOnlyFavorites) {
|
||||
final Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
|
||||
final boolean showOnlyFavorites = NEIClientConfig.getBooleanSetting("inventory.craftables.favoritesOnly");
|
||||
|
||||
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
|
||||
if (existsIngredients(firstGui, handler.getIngredientStacks(recipeIndex), invStacks)) {
|
||||
final Recipe recipe = Recipe.of(handler, recipeIndex);
|
||||
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
|
||||
|
||||
if (!recipe.getResults().isEmpty()
|
||||
&& (!showOnlyFavorites || FavoriteRecipes.getFavorite(recipe.getRecipeId()) != null)) {
|
||||
synchronized (availableRecipes) {
|
||||
availableRecipes.put(recipe.getResult(), recipe.getRecipeId());
|
||||
if (existsIngredients(ingredientStacks, invByItem)) {
|
||||
final ItemStack result = getCheapResult(handler, recipeIndex);
|
||||
|
||||
if (result != null) {
|
||||
final RecipeId recipeId = RecipeId.of(handler, recipeIndex, ingredientStacks);
|
||||
|
||||
if (!showOnlyFavorites || FavoriteRecipes.getFavorite(recipeId) != null) {
|
||||
synchronized (availableRecipes) {
|
||||
availableRecipes.put(result, recipeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,14 +468,27 @@ public class ItemCraftablesPanel
|
||||
return availableRecipes;
|
||||
}
|
||||
|
||||
private boolean existsIngredients(GuiContainer firstGui, List<PositionedStack> ingredients,
|
||||
List<ItemStack> invStacks) {
|
||||
private boolean existsIngredients(List<PositionedStack> ingredients, Map<Item, List<ItemStack>> invByItem) {
|
||||
final Map<ItemStack, Integer> usedItems = new HashMap<>();
|
||||
|
||||
for (PositionedStack pStack : ingredients) {
|
||||
final ItemStack used = invStacks.stream().filter(
|
||||
is -> usedItems.getOrDefault(is, is.stackSize) >= pStack.item.stackSize && pStack.contains(is))
|
||||
.findAny().orElse(null);
|
||||
ItemStack used = null;
|
||||
|
||||
for (ItemStack permutation : pStack.items) {
|
||||
final List<ItemStack> candidates = invByItem.get(permutation.getItem());
|
||||
|
||||
if (candidates == null) continue;
|
||||
|
||||
for (ItemStack is : candidates) {
|
||||
if (usedItems.getOrDefault(is, is.stackSize) >= pStack.item.stackSize
|
||||
&& NEIServerUtils.areStacksSameTypeCrafting(permutation, is)) {
|
||||
used = is;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (used != null) break;
|
||||
}
|
||||
|
||||
if (used != null) {
|
||||
usedItems.put(used, usedItems.getOrDefault(used, used.stackSize) - pStack.item.stackSize);
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.ItemsGrid.MouseContext;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPanel.HistoryGridSlot, MouseContext>> {
|
||||
|
||||
protected File historyFile = null;
|
||||
|
||||
public static class HistoryGridSlot extends ItemsGridSlot {
|
||||
|
||||
public HistoryGridSlot(int slotIndex, int itemIndex, ItemStack item) {
|
||||
@@ -115,4 +132,107 @@ public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPane
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
|
||||
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String worldPath = "global";
|
||||
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.history.worldSpecific")) {
|
||||
worldPath = NEIClientConfig.getWorldPath();
|
||||
}
|
||||
|
||||
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
|
||||
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
final File historyFile = new File(dir, "history.ini");
|
||||
|
||||
if (historyFile.equals(this.historyFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> itemStrings = Collections.emptyList();
|
||||
|
||||
if (historyFile.exists()) {
|
||||
try (FileInputStream reader = new FileInputStream(historyFile)) {
|
||||
NEIClientConfig.logger.info("Loading history from file {}", historyFile);
|
||||
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load history from file {}", historyFile, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final JsonParser parser = new JsonParser();
|
||||
final int maxSize = Math.max(50, this.grid.rows * this.grid.columns);
|
||||
|
||||
this.grid.realItems.clear();
|
||||
|
||||
for (String itemStr : itemStrings) {
|
||||
|
||||
if (itemStr.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.grid.realItems.size() >= maxSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonObject row = parser.parse(itemStr).getAsJsonObject();
|
||||
NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(row.get("item"));
|
||||
ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
|
||||
|
||||
if (itemStack != null) {
|
||||
this.grid.realItems.add(itemStack);
|
||||
} else {
|
||||
NEIClientConfig.logger.warn(
|
||||
"Failed to load history ItemStack from json string, the item no longer exists:\n{}",
|
||||
itemStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Failed to load history ItemStack from json string:\n{}", itemStr);
|
||||
}
|
||||
}
|
||||
|
||||
this.grid.onItemsChanged();
|
||||
|
||||
this.historyFile = historyFile;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.historyFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> strings = new ArrayList<>();
|
||||
|
||||
for (ItemStack stack : this.grid.realItems) {
|
||||
|
||||
try {
|
||||
final JsonObject row = new JsonObject();
|
||||
row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(stack)));
|
||||
strings.add(NBTJson.toJson(row));
|
||||
} catch (JsonSyntaxException e) {
|
||||
NEIClientConfig.logger.error("Failed to stringify history ItemStack to json string");
|
||||
}
|
||||
}
|
||||
|
||||
try (FileOutputStream output = new FileOutputStream(this.historyFile)) {
|
||||
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save history list to file {}", this.historyFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +150,7 @@ public class ItemListLoader {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
if (item instanceof IFluidContainerItem) {
|
||||
IFluidContainerItem fluidItem = (IFluidContainerItem) item;
|
||||
if (item instanceof IFluidContainerItem fluidItem) {
|
||||
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
|
||||
}
|
||||
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
|
||||
@@ -294,6 +293,7 @@ public class ItemListLoader {
|
||||
updateFilter.restart();
|
||||
warmTooltips.restart();
|
||||
loadBookmarks.restart();
|
||||
loadHistory.restart();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -320,6 +320,14 @@ public class ItemListLoader {
|
||||
}
|
||||
};
|
||||
|
||||
public static final RestartableTask loadHistory = new RestartableTask("NEI History Loading") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ItemPanels.itemPanel.historyPanel.load();
|
||||
}
|
||||
};
|
||||
|
||||
public static final RestartableTask refreshItems = new RestartableTask("NEI Item Reordering") {
|
||||
|
||||
@Override
|
||||
@@ -341,6 +349,7 @@ public class ItemListLoader {
|
||||
SubsetWidget.updateHiddenItems();
|
||||
updateFilter.restart();
|
||||
loadBookmarks.restart();
|
||||
loadHistory.restart();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
@@ -65,27 +66,41 @@ public class ItemMobSpawner extends ItemBlock {
|
||||
@Override
|
||||
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int side,
|
||||
float hitX, float hitY, float hitZ) {
|
||||
int targetX = x;
|
||||
int targetY = y;
|
||||
int targetZ = z;
|
||||
|
||||
placedX = x;
|
||||
placedY = y;
|
||||
placedZ = z;
|
||||
if (!world.getBlock(x, y, z).isReplaceable(world, x, y, z)) {
|
||||
ForgeDirection direction = ForgeDirection.getOrientation(side);
|
||||
targetX += direction.offsetX;
|
||||
targetY += direction.offsetY;
|
||||
targetZ += direction.offsetZ;
|
||||
}
|
||||
|
||||
placedX = targetX;
|
||||
placedY = targetY;
|
||||
placedZ = targetZ;
|
||||
|
||||
String mobType = getMobTypeFromItemStack(itemstack);
|
||||
|
||||
boolean placed = super.onItemUse(itemstack, entityplayer, world, x, y, z, side, hitX, hitY, hitZ);
|
||||
|
||||
if (placed && !world.isRemote) {
|
||||
TileEntity tileEntity = world.getTileEntity(x, y, z);
|
||||
if (tileEntity instanceof TileEntityMobSpawner) {
|
||||
TileEntityMobSpawner spawner = (TileEntityMobSpawner) tileEntity;
|
||||
String mobType = getMobTypeFromItemStack(itemstack);
|
||||
if (mobType != null) {
|
||||
spawner.func_145881_a().setEntityName(mobType);
|
||||
world.markBlockForUpdate(x, y, z);
|
||||
NEICPH.sendMobSpawnerID(x, y, z, mobType);
|
||||
if (placed) {
|
||||
TileEntity tileEntity = world.getTileEntity(targetX, targetY, targetZ);
|
||||
|
||||
if (tileEntity instanceof TileEntityMobSpawner spawner && mobType != null) {
|
||||
spawner.func_145881_a().setEntityName(mobType);
|
||||
|
||||
if (!world.isRemote) {
|
||||
world.markBlockForUpdate(targetX, targetY, targetZ);
|
||||
NEICPH.sendMobSpawnerID(targetX, targetY, targetZ, mobType);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return placed;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getMobTypeFromItemStack(ItemStack stack) {
|
||||
@@ -223,7 +238,6 @@ public class ItemMobSpawner extends ItemBlock {
|
||||
NEIClientConfig.logger.debug("Cleared all entity references from cache");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void loadSpawners() {
|
||||
if (loaded) {
|
||||
return;
|
||||
@@ -231,8 +245,7 @@ public class ItemMobSpawner extends ItemBlock {
|
||||
|
||||
synchronized (ItemMobSpawner.class) {
|
||||
try {
|
||||
for (Map.Entry<Class<? extends Entity>, String> entry : ((Map<Class<? extends Entity>, String>) EntityList.classToStringMapping)
|
||||
.entrySet()) {
|
||||
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
|
||||
|
||||
Class<? extends Entity> clazz = entry.getKey();
|
||||
String name = entry.getValue();
|
||||
@@ -308,7 +321,7 @@ public class ItemMobSpawner extends ItemBlock {
|
||||
|
||||
String name = ENTITY_CLASS_TO_NAME_CACHE.get(entityClass);
|
||||
if (name == null) {
|
||||
name = (String) EntityList.classToStringMapping.get(entityClass);
|
||||
name = EntityList.classToStringMapping.get(entityClass);
|
||||
}
|
||||
|
||||
return name != null && shouldRegisterEntity(entityClass, name);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,11 +83,7 @@ public class ItemQuantityField extends TextField {
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
|
||||
|
||||
if (amount == 0) {
|
||||
amount = StackInfo.itemStackToNBT(stack).hasKey("gtFluidName") ? 144 : stack.getMaxStackSize();
|
||||
}
|
||||
amount = NEIClientUtils.getScrollMultiplier(stack);
|
||||
}
|
||||
|
||||
return StackInfo.withAmount(stack, amount);
|
||||
|
||||
@@ -46,8 +46,7 @@ public class ItemStackMap<T> {
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof StackMetaKey)) return false;
|
||||
StackMetaKey t = (StackMetaKey) o;
|
||||
if (!(o instanceof StackMetaKey t)) return false;
|
||||
return damage == t.damage && Objects.equal(tag, t.tag);
|
||||
}
|
||||
}
|
||||
@@ -218,8 +217,7 @@ public class ItemStackMap<T> {
|
||||
public void put(ItemStack key, T value) {
|
||||
if (key == null || key.getItem() == null) return;
|
||||
|
||||
DetailMap map = itemMap.get(key.getItem());
|
||||
if (map == null) itemMap.put(key.getItem(), map = new DetailMap());
|
||||
DetailMap map = itemMap.computeIfAbsent(key.getItem(), k -> new DetailMap());
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
|
||||
if (this.stack != null && this.availableAreaWidth >= SLOT_SIZE * 2
|
||||
&& (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
|
||||
|| NEIClientConfig.isKeyHashDown("gui.itemzoom_hold"))
|
||||
|| KeyManager.isKeyDown("itemzoom.hold"))
|
||||
&& GuiContainerManager.shouldShowTooltip(NEIClientUtils.getGuiContainer())) {
|
||||
final float inverseScaleFactor = 1.0f / this.scale;
|
||||
final float screenScale = getScreenScale();
|
||||
@@ -74,7 +74,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
|
||||
&& NEIClientConfig.getBooleanSetting("inventory.itemzoom.helpText")) {
|
||||
String keyName = NEIClientConfig.getKeyName("gui.itemzoom_toggle");
|
||||
String keyName = KeyManager.getKeyName("itemzoom.toggle", NEIClientUtils.SHIFT_HASH);
|
||||
|
||||
if (keyName != null) {
|
||||
String helpText = NEIClientUtils.translate("itemzoom.toggle", keyName);
|
||||
@@ -111,8 +111,14 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
this.xPosition = (availableAreaWidth - this.scale * SLOT_SIZE) / 2;
|
||||
this.availableAreaWidth = (int) (availableAreaWidth / screenScale);
|
||||
|
||||
if (availableAreaWidth / screenScale >= mouse.x) {
|
||||
this.xPosition += gui.width * screenScale - availableAreaWidth;
|
||||
final float rightSideShift = gui.width * screenScale - availableAreaWidth;
|
||||
final boolean leftSideFree = availableAreaWidth / screenScale < mouse.x;
|
||||
final boolean rightSideFree = mouse.x < gui.width - availableAreaWidth / screenScale;
|
||||
|
||||
final boolean showOnRight = leftSideFree && rightSideFree ? !preferLeftSide() : !leftSideFree;
|
||||
|
||||
if (showOnRight) {
|
||||
this.xPosition += rightSideShift;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -128,6 +134,22 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preferLeftSide() {
|
||||
switch (NEIClientConfig.getIntSetting("inventory.itemzoom.preferredSide")) {
|
||||
case 1:
|
||||
return true;
|
||||
case 2:
|
||||
return false;
|
||||
}
|
||||
|
||||
final int leftCount = NEIClientConfig.isBookmarkPanelHidden()
|
||||
|| ItemPanels.bookmarkPanel.getGrid().getPerPage() == 0 ? 0 : ItemPanels.bookmarkPanel.getGrid().size();
|
||||
final int rightCount = ItemPanels.itemPanel.getGrid().getPerPage() == 0 ? 0
|
||||
: ItemPanels.itemPanel.getGrid().size();
|
||||
|
||||
return leftCount <= rightCount;
|
||||
}
|
||||
|
||||
private float getPointSize(GuiContainer gui) {
|
||||
final float screenScale = 1.0f / getScreenScale();
|
||||
final float availableAreaWidth = (gui.width - (gui.xSize + gui.width) / 2) * screenScale;
|
||||
@@ -197,25 +219,30 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.itemzoom_toggle")) {
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
|
||||
if (KeyManager.isHashDown("itemzoom.toggle", NEIClientUtils.SHIFT_HASH)) {
|
||||
NEIClientConfig.getSetting("inventory.itemzoom.enabled")
|
||||
.setBooleanValue(!NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_hold")) {
|
||||
if (this.stack != null && KeyManager.isKeyDown("itemzoom.hold")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_zoom_in")) {
|
||||
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_in")) {
|
||||
increaseZoom(gui);
|
||||
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.stack != null && NEIClientConfig.isKeyHashDown("gui.itemzoom_zoom_out")) {
|
||||
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_out")) {
|
||||
decreaseZoom(gui);
|
||||
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -348,16 +349,24 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
}
|
||||
|
||||
public void setGridSize(int mleft, int mtop, int w, int h) {
|
||||
final int newWidth = Math.max(0, w);
|
||||
final int newHeight = Math.max(0, h);
|
||||
final boolean changed = marginLeft != mleft || marginTop != mtop || width != newWidth || height != newHeight;
|
||||
|
||||
marginLeft = mleft;
|
||||
marginTop = mtop;
|
||||
|
||||
width = Math.max(0, w);
|
||||
height = Math.max(0, h);
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
|
||||
columns = width / SLOT_SIZE;
|
||||
rows = height / SLOT_SIZE;
|
||||
|
||||
paddingLeft = (width % SLOT_SIZE) / 2;
|
||||
|
||||
if (changed) {
|
||||
onGridChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void shiftPage(int shift) {
|
||||
@@ -396,7 +405,10 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
}
|
||||
|
||||
public void refresh(GuiContainer gui) {
|
||||
updateGuiOverlapSlots(gui);
|
||||
final int gridRenderingCacheMode = getGridRenderingCacheMode();
|
||||
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
|
||||
updateGuiOverlapSlots(gui);
|
||||
}
|
||||
shiftPage(0);
|
||||
}
|
||||
|
||||
@@ -409,11 +421,12 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
perPage = columns * rows;
|
||||
|
||||
if (gui != null) {
|
||||
final INEIGuiHandler[] handlers = GuiInfo.getGuiHandlersSnapshot();
|
||||
if (NEIClientConfig.optimizeGuiOverlapComputation()) {
|
||||
checkGuiOverlap(gui, 0, columns - 2, 1);
|
||||
checkGuiOverlap(gui, columns - 1, 1, -1);
|
||||
checkGuiOverlap(gui, handlers, 0, columns - 2, 1);
|
||||
checkGuiOverlap(gui, handlers, columns - 1, 1, -1);
|
||||
} else {
|
||||
checkGuiOverlap(gui, 0, columns, 1);
|
||||
checkGuiOverlap(gui, handlers, 0, columns, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +435,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
}
|
||||
}
|
||||
|
||||
private void checkGuiOverlap(GuiContainer gui, int start, int end, int dir) {
|
||||
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) {
|
||||
@@ -432,7 +445,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
final int idx = columns * r + c;
|
||||
if (idx >= 0 && idx < invalidSlotMap.length
|
||||
&& !invalidSlotMap[idx]
|
||||
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c))) {
|
||||
&& GuiInfo.hideItemPanelSlot(gui, getSlotRect(r, c), handlers)) {
|
||||
invalidSlotMap[idx] = true;
|
||||
validColumn = false;
|
||||
perPage--;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4,6 +4,7 @@ import static codechicken.lib.gui.GuiDraw.fontRenderer;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -17,56 +18,265 @@ import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.NEIClientUtils.Alignment;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
|
||||
public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
|
||||
protected static final int SLOT_SIZE = 18;
|
||||
protected static final int MAX_COLUMNS = 11;
|
||||
protected static final int MARGIN_TOP = 2;
|
||||
@FunctionalInterface
|
||||
public interface AmountRenderer {
|
||||
|
||||
protected String label;
|
||||
protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY;
|
||||
protected List<ItemStack> items;
|
||||
protected int activeStackIndex = -1;
|
||||
protected Dimension size = new Dimension();
|
||||
protected boolean saveStackSize = false;
|
||||
protected int columns = 0;
|
||||
protected int count = 0;
|
||||
protected int rows = 0;
|
||||
protected int length = 0;
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
|
||||
this(label, items, true, 5);
|
||||
void draw(int x, int y, ItemStack stack, long amount);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
|
||||
this.label = label;
|
||||
this.items = groupingItemStacks(items);
|
||||
this.saveStackSize = saveStackSize;
|
||||
this.length = this.items.size();
|
||||
public static class NoAmountRenderer implements AmountRenderer {
|
||||
|
||||
if (this.length > 0) {
|
||||
this.columns = Math.min(MAX_COLUMNS, this.length);
|
||||
this.rows = Math.min(maxRows, (int) Math.ceil((float) this.length / this.columns));
|
||||
@Override
|
||||
public void draw(int x, int y, ItemStack stack, long amount) {
|
||||
GuiContainerManager.drawItem(x, y, stack, true, "");
|
||||
}
|
||||
|
||||
this.size.width = Math.max(this.columns * SLOT_SIZE, fontRenderer.getStringWidth(this.label) + 15);
|
||||
this.size.height = this.rows * SLOT_SIZE + fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP;
|
||||
protected static void drawAmountText(String text, Rectangle4i rect, int color, Alignment alignment) {
|
||||
NEIClientUtils.drawNEIOverlayText(text, rect, 1, color, true, alignment);
|
||||
}
|
||||
}
|
||||
|
||||
this.count = Math.min(
|
||||
this.length,
|
||||
Math.min(
|
||||
this.columns * this.rows,
|
||||
this.length > MAX_COLUMNS * maxRows ? (MAX_COLUMNS * maxRows) : Integer.MAX_VALUE));
|
||||
public static class TotalAmountRenderer extends NoAmountRenderer {
|
||||
|
||||
if (this.items.size() > this.count) {
|
||||
String text = "+" + (this.items.size() - this.count);
|
||||
this.count -= (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / SLOT_SIZE);
|
||||
private static class AmountText {
|
||||
|
||||
public final String text;
|
||||
public final Alignment alignment;
|
||||
|
||||
public AmountText(String text, Alignment alignment) {
|
||||
this.text = text;
|
||||
this.alignment = alignment;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<ItemStack, AmountText> cache = new IdentityHashMap<>();
|
||||
|
||||
@Override
|
||||
public void draw(int x, int y, ItemStack stack, long amount) {
|
||||
super.draw(x, y, stack, amount);
|
||||
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
|
||||
}
|
||||
|
||||
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
|
||||
final AmountText amountText = this.cache.computeIfAbsent(stack, key -> {
|
||||
final boolean isFluidDisplay = StackInfo.isFluidDisplayItem(key);
|
||||
final String text = formatAmount(key, amount);
|
||||
|
||||
return new AmountText(
|
||||
isFluidDisplay && !text.isEmpty() ? text + "L" : text,
|
||||
isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
|
||||
});
|
||||
|
||||
if (!amountText.text.isEmpty()) {
|
||||
drawAmountText(amountText.text, rect, 0xFFFFFF, amountText.alignment);
|
||||
}
|
||||
}
|
||||
|
||||
protected String formatAmount(ItemStack stack, long amount) {
|
||||
return amount == 0 ? "" : ReadableNumberConverter.INSTANCE.toWideReadableForm(amount);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StacksAmountRenderer extends NoAmountRenderer {
|
||||
|
||||
private static class StacksText {
|
||||
|
||||
public final String stacks;
|
||||
public final String remainder;
|
||||
public final int color;
|
||||
|
||||
public StacksText(String stacks, String remainder, int color) {
|
||||
this.stacks = stacks;
|
||||
this.remainder = remainder;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<ItemStack, StacksText> cache = new IdentityHashMap<>();
|
||||
|
||||
@Override
|
||||
public void draw(int x, int y, ItemStack stack, long amount) {
|
||||
super.draw(x, y, stack, amount);
|
||||
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
|
||||
}
|
||||
|
||||
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
|
||||
final StacksText stacksText = this.cache.computeIfAbsent(stack, key -> formatAmount(key, amount));
|
||||
|
||||
if (!stacksText.stacks.isEmpty()) {
|
||||
drawAmountText(stacksText.stacks, rect, stacksText.color, Alignment.TopLeft);
|
||||
}
|
||||
|
||||
if (!stacksText.remainder.isEmpty()) {
|
||||
drawAmountText(stacksText.remainder, rect, stacksText.color, Alignment.BottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
private StacksText formatAmount(ItemStack stack, long amount) {
|
||||
final long stackSize = StackInfo.isFluidDisplayItem(stack) ? FLUID_STACK_SIZE
|
||||
: Math.max(1, stack.getMaxStackSize());
|
||||
final long stacks = amount / stackSize;
|
||||
final long remainder = amount % stackSize;
|
||||
final String prefix = getPrefix(stack);
|
||||
|
||||
return new StacksText(
|
||||
stacks > 0 ? prefix + "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(stacks) : "",
|
||||
remainder > 0
|
||||
? (stacks > 0 ? "+" : prefix)
|
||||
+ ReadableNumberConverter.INSTANCE.toWideReadableForm(remainder)
|
||||
: "",
|
||||
getColor(stack));
|
||||
}
|
||||
|
||||
protected String getPrefix(ItemStack stack) {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected int getColor(ItemStack stack) {
|
||||
return 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
protected static final int SLOT_SIZE = 18;
|
||||
protected static final int ICON_SIZE = 16;
|
||||
protected static final int ICON_OFFSET = 1;
|
||||
protected static final int MAX_COLUMNS = 11;
|
||||
protected static final int MARGIN_TOP = 2;
|
||||
protected static final int LABEL_MARGIN = 15;
|
||||
protected static final int NAME_MARGIN = 2;
|
||||
protected static final int DEFAULT_MAX_ROWS = 5;
|
||||
protected static final int FLUID_STACK_SIZE = 144;
|
||||
|
||||
protected final List<ItemStack> items;
|
||||
protected final List<Long> amounts = new ArrayList<>();
|
||||
protected final List<String> names = new ArrayList<>();
|
||||
|
||||
protected AmountRenderer amountRenderer;
|
||||
|
||||
protected String label;
|
||||
protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY;
|
||||
|
||||
protected int activeStackIndex = -1;
|
||||
protected Dimension size = new Dimension();
|
||||
|
||||
protected int maxNamedRows = Integer.MAX_VALUE;
|
||||
|
||||
protected int length = 0;
|
||||
protected int rows = 0;
|
||||
protected int nameRows = 0;
|
||||
protected int gridColumns = 0;
|
||||
protected int gridCount = 0;
|
||||
|
||||
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, 0);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
|
||||
this(label, items, saveStackSize, maxRows, 0);
|
||||
}
|
||||
|
||||
protected ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows,
|
||||
int maxNamedRows) {
|
||||
this.label = label;
|
||||
this.items = groupingItemStacks(items);
|
||||
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
|
||||
this.length = this.items.size();
|
||||
this.maxNamedRows = maxNamedRows;
|
||||
|
||||
setMaxRows(maxRows);
|
||||
}
|
||||
|
||||
protected static int headerHeight() {
|
||||
return fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP;
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler setMaxRows(int maxRows) {
|
||||
if (this.length == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.names.clear();
|
||||
this.nameRows = 0;
|
||||
|
||||
if (!layoutWithNames(maxRows)) {
|
||||
layoutAsGrid(maxRows);
|
||||
}
|
||||
|
||||
this.size.height = this.rows * SLOT_SIZE + headerHeight();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
protected boolean layoutWithNames(int maxRows) {
|
||||
final int maxNames = Math.min(this.maxNamedRows, this.length <= maxRows ? this.length : maxRows - 1);
|
||||
final int[] lineWidth = new int[maxNames + 1];
|
||||
lineWidth[0] = labelWidth();
|
||||
|
||||
for (int index = 0; index < maxNames; index++) {
|
||||
final String name = this.items.get(index).getDisplayName();
|
||||
this.names.add(name);
|
||||
lineWidth[index + 1] = Math
|
||||
.max(lineWidth[index], SLOT_SIZE + NAME_MARGIN + fontRenderer.getStringWidth(name));
|
||||
}
|
||||
|
||||
for (int named = maxNames; named >= 0; named--) {
|
||||
final int rest = this.length - named;
|
||||
final int width = Math.max(lineWidth[named], Math.min(rest, MAX_COLUMNS) * SLOT_SIZE);
|
||||
final int columns = Math.max(1, width / SLOT_SIZE);
|
||||
final int restRows = (int) Math.ceil((float) rest / columns);
|
||||
|
||||
if (named + restRows <= maxRows) {
|
||||
this.nameRows = named;
|
||||
this.gridColumns = columns;
|
||||
this.gridCount = rest;
|
||||
this.rows = named + restRows;
|
||||
this.size.width = width;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
this.names.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void layoutAsGrid(int maxRows) {
|
||||
this.gridColumns = Math.min(MAX_COLUMNS, this.length);
|
||||
this.rows = Math.min(maxRows, (int) Math.ceil((float) this.length / this.gridColumns));
|
||||
this.gridCount = Math.min(this.length, this.gridColumns * this.rows);
|
||||
this.size.width = Math.max(this.gridColumns * SLOT_SIZE, labelWidth());
|
||||
|
||||
if (this.gridCount < this.length) {
|
||||
this.gridCount -= reservedColumns(this.length - this.gridCount);
|
||||
}
|
||||
}
|
||||
|
||||
protected int labelWidth() {
|
||||
return fontRenderer.getStringWidth(this.label) + LABEL_MARGIN;
|
||||
}
|
||||
|
||||
protected int reservedColumns(int hidden) {
|
||||
return (int) Math.ceil((float) (fontRenderer.getStringWidth("+" + hidden) - 2) / SLOT_SIZE);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler setAmountRenderer(AmountRenderer amountRenderer) {
|
||||
this.amountRenderer = amountRenderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setActiveStack(ItemStack activeStack) {
|
||||
@@ -79,6 +289,10 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
return this.activeStackIndex == -1 ? null : this.items.get(this.activeStackIndex);
|
||||
}
|
||||
|
||||
public List<ItemStack> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.items.isEmpty();
|
||||
}
|
||||
@@ -92,6 +306,11 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
this.labelColor = color;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
this.size.width = Math.max(this.size.width, labelWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int x, int y) {
|
||||
if (this.length == 0) return;
|
||||
@@ -110,62 +329,79 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
final int zTranslation = GuiContainerManager.TOOLTIP_Z_OFFSET;
|
||||
GL11.glTranslatef(xTranslation, yTranslation, zTranslation);
|
||||
|
||||
int indexShift = 0;
|
||||
final int gridShift = gridIndexShift();
|
||||
|
||||
if (this.activeStackIndex != -1) {
|
||||
indexShift = Math.max(0, Math.min(this.items.size() - this.count, this.activeStackIndex - this.count + 2));
|
||||
for (int index = 0; index < this.nameRows; index++) {
|
||||
drawSlot(0, index * SLOT_SIZE, index);
|
||||
}
|
||||
|
||||
for (int index = 0; index < this.count && index + indexShift < this.items.size(); index++) {
|
||||
ItemStack drawStack = this.items.get(index + indexShift);
|
||||
int col = index % this.columns;
|
||||
int row = index / this.columns;
|
||||
for (int index = 0; index < this.gridCount; index++) {
|
||||
drawSlot(
|
||||
(index % this.gridColumns) * SLOT_SIZE,
|
||||
(this.nameRows + index / this.gridColumns) * SLOT_SIZE,
|
||||
this.nameRows + gridShift + index);
|
||||
}
|
||||
|
||||
String stackSize = !this.saveStackSize || drawStack.stackSize == 0 ? ""
|
||||
: ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize);
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
final int textShift = ICON_OFFSET + Math.round((ICON_SIZE - fontRenderer.FONT_HEIGHT) / 2f);
|
||||
|
||||
if (this.activeStackIndex == index + indexShift) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.drawRect(col * SLOT_SIZE - 1, row * SLOT_SIZE - 1, 18, 18, 0x66555555));
|
||||
for (int index = 0; index < this.nameRows; index++) {
|
||||
fontRenderer.drawStringWithShadow(
|
||||
EnumChatFormatting.GRAY + this.names.get(index),
|
||||
SLOT_SIZE + NAME_MARGIN,
|
||||
index * SLOT_SIZE + textShift,
|
||||
0xFFFFFF);
|
||||
}
|
||||
|
||||
drawItem(col * SLOT_SIZE, row * SLOT_SIZE, drawStack, stackSize);
|
||||
}
|
||||
final int hidden = this.length - this.nameRows - this.gridCount;
|
||||
|
||||
if (this.count < this.items.size()) {
|
||||
final String text = "+" + (this.items.size() - this.count);
|
||||
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
if (hidden > 0) {
|
||||
final String text = "+" + hidden;
|
||||
fontRenderer.drawStringWithShadow(
|
||||
text,
|
||||
MAX_COLUMNS * SLOT_SIZE - fontRenderer.getStringWidth(text) - 2,
|
||||
(this.rows - 1) * SLOT_SIZE + (SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2,
|
||||
0xee555555);
|
||||
});
|
||||
}
|
||||
EnumChatFormatting.GRAY + text,
|
||||
this.size.width - fontRenderer.getStringWidth(text) - 2,
|
||||
(this.rows - 1) * SLOT_SIZE + textShift,
|
||||
0xFFFFFF);
|
||||
}
|
||||
});
|
||||
|
||||
GL11.glTranslatef(-xTranslation, -yTranslation, -zTranslation);
|
||||
GL11.glPopAttrib();
|
||||
}
|
||||
|
||||
protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) {
|
||||
GuiContainerManager.drawItem(x, y, drawStack, true, stackSize);
|
||||
protected int gridIndexShift() {
|
||||
if (this.nameRows > 0 || this.activeStackIndex == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.max(0, Math.min(this.length - this.gridCount, this.activeStackIndex - this.gridCount + 2));
|
||||
}
|
||||
|
||||
protected void drawSlot(int x, int y, int index) {
|
||||
if (this.activeStackIndex == index) {
|
||||
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(x, y, SLOT_SIZE, SLOT_SIZE, 0x66555555));
|
||||
}
|
||||
|
||||
drawStackWithAmount(x, y, index);
|
||||
}
|
||||
|
||||
protected void drawStackWithAmount(int x, int y, int index) {
|
||||
this.amountRenderer.draw(x + ICON_OFFSET, y + ICON_OFFSET, this.items.get(index), this.amounts.get(index));
|
||||
}
|
||||
|
||||
private List<ItemStack> groupingItemStacks(List<ItemStack> items) {
|
||||
final List<ItemStack> result = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : ItemStackAmount.of(items).entrySet()) {
|
||||
final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue()));
|
||||
|
||||
if (entry.getKey().hasKey("gtFluidName")) {
|
||||
stack.stackSize = 0;
|
||||
}
|
||||
result.add(stack);
|
||||
result.add(StackInfo.loadFromNBT(entry.getKey(), 0));
|
||||
this.amounts.add(Math.max(0, entry.getValue()));
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.addAll(items);
|
||||
for (ItemStack stack : items) {
|
||||
result.add(stack);
|
||||
this.amounts.add(0L);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -2,14 +2,22 @@ package codechicken.nei;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import codechicken.nei.util.NEIKeyboardUtils;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
|
||||
/**
|
||||
* Good old down/held/up keystate tracker
|
||||
* Registers NEI key bindings as vanilla {@link KeyBinding}s and exposes their state to the rest of NEI.
|
||||
*/
|
||||
public class KeyManager {
|
||||
|
||||
@@ -18,31 +26,173 @@ public class KeyManager {
|
||||
public void tickKeyStates();
|
||||
}
|
||||
|
||||
public static class KeyState {
|
||||
public static LinkedList<IKeyStateTracker> trackers = new LinkedList<>();
|
||||
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;
|
||||
|
||||
public boolean down;
|
||||
public boolean held;
|
||||
public boolean up;
|
||||
static {
|
||||
// Aliases for legacy keybinds
|
||||
keyAliases.put("gui.recipe", "recipe.recipe");
|
||||
keyAliases.put("gui.usage", "recipe.usage");
|
||||
keyAliases.put("gui.hide_bookmarks", "bookmark.hide");
|
||||
}
|
||||
|
||||
public static HashMap<String, KeyState> keyStates = new HashMap<>();
|
||||
public static LinkedList<IKeyStateTracker> trackers = new LinkedList<>();
|
||||
public static KeyBinding registerKeyBinding(String ident, int defaultKey) {
|
||||
return keyBindings.computeIfAbsent(ident, id -> {
|
||||
final KeyBinding binding = new KeyBinding(
|
||||
"nei.options.keys." + id,
|
||||
NEIKeyboardUtils.unhash(defaultKey),
|
||||
categoryFor(id));
|
||||
ClientRegistry.registerKeyBinding(binding);
|
||||
return binding;
|
||||
});
|
||||
}
|
||||
|
||||
public static KeyBinding getKeyBinding(String ident) {
|
||||
return keyBindings.get(keyAliases.getOrDefault(ident, ident));
|
||||
}
|
||||
|
||||
public static boolean isRegistered(String description) {
|
||||
for (KeyBinding binding : keyBindings.values()) {
|
||||
if (binding.getKeyDescription().equals(description)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getKeyCode(String ident) {
|
||||
final KeyBinding binding = getKeyBinding(ident);
|
||||
return binding == null ? Keyboard.KEY_NONE : binding.getKeyCode();
|
||||
}
|
||||
|
||||
public static boolean isKeyDown(String ident) {
|
||||
final KeyBinding binding = getKeyBinding(ident);
|
||||
|
||||
if (binding != null) {
|
||||
final int keyCode = binding.getKeyCode();
|
||||
|
||||
if (keyCode < 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static boolean isHashDown(String ident, int modifierMask) {
|
||||
|
||||
if (!isKeyDown(ident)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIKeyboardUtils.isHashKey(getKeyCode(ident))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((modifierMask & NEIKeyboardUtils.CTRL_HASH) != 0) == NEIClientUtils.controlKey()
|
||||
&& ((modifierMask & NEIKeyboardUtils.SHIFT_HASH) != 0) == NEIClientUtils.shiftKey()
|
||||
&& ((modifierMask & NEIKeyboardUtils.ALT_HASH) != 0) == NEIClientUtils.altKey();
|
||||
}
|
||||
|
||||
public static String getKeyName(String ident) {
|
||||
return getKeyName(ident, 0);
|
||||
}
|
||||
|
||||
public static String getKeyName(String ident, int meta) {
|
||||
final int keyCode = getKeyCode(ident);
|
||||
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);
|
||||
}
|
||||
|
||||
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 = keyCode == Keyboard.KEY_NONE && hash == 0 ? "" : NEIKeyboardUtils.getKeyName(keyCode, hash);
|
||||
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
|
||||
|
||||
if (!keyHash.isEmpty()) {
|
||||
keyText.add(keyHash);
|
||||
}
|
||||
|
||||
if (!mouseHash.isEmpty()) {
|
||||
keyText.add(mouseHash);
|
||||
}
|
||||
|
||||
return keyText.toString();
|
||||
}
|
||||
|
||||
public static boolean isPressed(String ident) {
|
||||
final KeyBinding binding = getKeyBinding(ident);
|
||||
return binding != null && binding.isPressed();
|
||||
}
|
||||
|
||||
private static String categoryFor(String ident) {
|
||||
final int dot = ident.indexOf('.');
|
||||
return dot < 0 ? "nei.options.keys" : "nei.options.keys." + ident.substring(0, dot);
|
||||
}
|
||||
|
||||
public static void requestOptionsReload() {
|
||||
pendingOptionsReload = true;
|
||||
}
|
||||
|
||||
public static void tickKeyStates() {
|
||||
for (Entry<String, KeyState> entry : keyStates.entrySet()) {
|
||||
final int keyCode = NEIKeyboardUtils.unhash(NEIClientConfig.getKeyBinding(entry.getKey()));
|
||||
final boolean down = keyCode > 0 && keyCode < Keyboard.KEYBOARD_SIZE && Keyboard.isKeyDown(keyCode);
|
||||
final KeyState state = entry.getValue();
|
||||
|
||||
if (down) {
|
||||
state.down = !state.held;
|
||||
state.up = false;
|
||||
} else {
|
||||
state.up = state.held;
|
||||
state.down = false;
|
||||
}
|
||||
|
||||
state.held = down;
|
||||
if (pendingOptionsReload) {
|
||||
pendingOptionsReload = false;
|
||||
Minecraft.getMinecraft().gameSettings.loadOptions();
|
||||
}
|
||||
|
||||
for (IKeyStateTracker tracker : trackers) tracker.tickKeyStates();
|
||||
|
||||
@@ -43,6 +43,7 @@ import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.gui.inventory.GuiContainerCreative;
|
||||
import net.minecraft.client.renderer.InventoryEffectRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -118,11 +119,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
*/
|
||||
public static boolean itemsLoaded = false;
|
||||
|
||||
/**
|
||||
* Not present, Present
|
||||
*/
|
||||
public static Image[] itemPresenceOverlays = new Image[2];
|
||||
|
||||
public static void load() {
|
||||
API.addLayoutStyle(0, new LayoutStyleMinecraft());
|
||||
|
||||
@@ -224,22 +220,40 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.hide")) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.hide_bookmarks")) {
|
||||
if (KeyManager.isKeyDown("bookmark.hide")) {
|
||||
toggleBooleanSetting("inventory.bookmarks.enabled");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.next_tooltip") && GuiContainerManager.incrementTooltipPage()) {
|
||||
if (KeyManager.isKeyDown("gui.next_tooltip") && GuiContainerManager.incrementTooltipPage()) {
|
||||
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;
|
||||
@@ -807,6 +821,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
AutoFocusWidget.instance.guiTick();
|
||||
|
||||
for (Widget widget : drawWidgets) widget.update();
|
||||
}
|
||||
|
||||
@@ -907,30 +923,35 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
changeTexture("textures/gui/widgets.png");
|
||||
drawTexturedModalRect(x, y, tx1, ty1, w1, h1); // top left
|
||||
drawTexturedModalRect(x, y2, tx1, ty2, w1, h2); // bottom left
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
addTexturedModalRect(tessellator, x, y, tx1, ty1, w1, h1); // top left
|
||||
addTexturedModalRect(tessellator, x, y2, tx1, ty2, w1, h2); // bottom left
|
||||
|
||||
for (int tile = 0; tile < wTiles; tile++) {
|
||||
final int tileX = x + w1 + 50 * tile;
|
||||
drawTexturedModalRect(tileX, y, tx3, ty1, 50, h1); // top
|
||||
drawTexturedModalRect(tileX, y2, tx3, ty2, 50, h2); // bottom
|
||||
addTexturedModalRect(tessellator, tileX, y, tx3, ty1, 50, h1); // top
|
||||
addTexturedModalRect(tessellator, tileX, y2, tx3, ty2, 50, h2); // bottom
|
||||
}
|
||||
|
||||
drawTexturedModalRect(x2, y, tx2, ty1, w2, h1); // top right
|
||||
drawTexturedModalRect(x2, y2, tx2, ty2, w2, h2); // bottom right
|
||||
addTexturedModalRect(tessellator, x2, y, tx2, ty1, w2, h1); // top right
|
||||
addTexturedModalRect(tessellator, x2, y2, tx2, ty2, w2, h2); // bottom right
|
||||
tessellator.draw();
|
||||
|
||||
if (!is2DTexture) GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
public static void drawItemPresenceOverlay(int slotX, int slotY, boolean isPresent, boolean slotHighlight) {
|
||||
|
||||
if (slotHighlight) {
|
||||
NEIClientUtils.gl2DRenderContext(() -> drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000));
|
||||
} else {
|
||||
Image icon = itemPresenceOverlays[isPresent ? 1 : 0];
|
||||
drawIcon(slotX + 16 - icon.width, slotY + 16 - icon.height, icon);
|
||||
}
|
||||
|
||||
private static void addTexturedModalRect(Tessellator tessellator, int x, int y, int textureX, int textureY,
|
||||
int width, int height) {
|
||||
final double z = GuiDraw.gui.getZLevel();
|
||||
final double u = textureX / 256D;
|
||||
final double v = textureY / 256D;
|
||||
final double maxU = (textureX + width) / 256D;
|
||||
final double maxV = (textureY + height) / 256D;
|
||||
tessellator.addVertexWithUV(x, y + height, z, u, maxV);
|
||||
tessellator.addVertexWithUV(x + width, y + height, z, maxU, maxV);
|
||||
tessellator.addVertexWithUV(x + width, y, z, maxU, v);
|
||||
tessellator.addVertexWithUV(x, y, z, u, v);
|
||||
}
|
||||
|
||||
public static LayoutManager instance() {
|
||||
@@ -941,12 +962,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
public void tickKeyStates() {
|
||||
if (Minecraft.getMinecraft().currentScreen != null) return;
|
||||
|
||||
if (KeyManager.keyStates.get("world.dawn").down) timeButtons[0].onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.noon").down) timeButtons[1].onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.dusk").down) timeButtons[2].onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.midnight").down) timeButtons[3].onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.rain").down) rain.onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.heal").down) heal.onButtonPress(false);
|
||||
if (KeyManager.keyStates.get("world.creative").down) gamemode.onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.dawn")) timeButtons[0].onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.noon")) timeButtons[1].onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.dusk")) timeButtons[2].onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.midnight")) timeButtons[3].onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.rain")) rain.onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.heal")) heal.onButtonPress(false);
|
||||
if (KeyManager.isPressed("world.creative")) gamemode.onButtonPress(false);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.api.LayoutStyle;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
|
||||
public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
|
||||
@@ -38,10 +37,6 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
LayoutManager.timeButtons[2].icon = new Image(144, 24, 12, 12);
|
||||
LayoutManager.timeButtons[3].icon = new Image(156, 24, 12, 12);
|
||||
LayoutManager.heal.icon = new Image(168, 24, 12, 12);
|
||||
LayoutManager.itemPresenceOverlays[0] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 0, 40, 8, 8)
|
||||
.build();
|
||||
LayoutManager.itemPresenceOverlays[1] = new DrawableBuilder("nei:textures/nei_tabbed_sprites.png", 8, 40, 8, 8)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,7 @@ import static codechicken.nei.PacketIDs.S2C;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.inventory.GuiInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.play.INetHandlerPlayClient;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
@@ -14,6 +15,7 @@ import codechicken.core.ClientUtils;
|
||||
import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
|
||||
import codechicken.nei.commands.CommandBookmarkAdd;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
public class NEICPH implements IClientPacketHandler {
|
||||
@@ -56,6 +58,21 @@ public class NEICPH implements IClientPacketHandler {
|
||||
case S2C.OPEN_POTION_GUI:
|
||||
ClientUtils.openSMPGui(packet.readUByte(), new GuiPotionCreator(mc.thePlayer.inventory));
|
||||
break;
|
||||
case S2C.CACHE_BOOKMARK_DATA:
|
||||
handleCacheBookmarkData(packet);
|
||||
break;
|
||||
case S2C.SYNC:
|
||||
ServerSync.onReply(packet.readInt());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCacheBookmarkData(PacketCustom packet) {
|
||||
final String payloadId = packet.readString();
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
|
||||
if (payloadTag != null) {
|
||||
CommandBookmarkAdd.cachePayload(payloadId, payloadTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +203,9 @@ public class NEICPH implements IClientPacketHandler {
|
||||
packet.sendToServer();
|
||||
}
|
||||
|
||||
public static void sendChatLink(ItemStack stackover) {
|
||||
public static void sendChatLink(NBTTagCompound payloadTag) {
|
||||
PacketCustom packet = new PacketCustom(channel, C2S.SEND_CHAT_ITEM_LINK);
|
||||
packet.writeItemStack(stackover);
|
||||
packet.writeNBTTagCompound(payloadTag);
|
||||
packet.sendToServer();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -61,7 +60,6 @@ import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.recipe.RecipeCatalysts;
|
||||
import codechicken.nei.util.ItemUntranslator;
|
||||
import codechicken.nei.util.NEIKeyboardUtils;
|
||||
import codechicken.obfuscator.ObfuscationRun;
|
||||
|
||||
public class NEIClientConfig {
|
||||
@@ -201,6 +199,11 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.maxVerticalTooltipRows")
|
||||
.setComment("Max rows in vertical crafting chain tooltip lists (0 - disable, always use grid)")
|
||||
.getIntValue(12);
|
||||
API.addOption(new OptionIntegerField("inventory.bookmarks.maxVerticalTooltipRows", 0, 50));
|
||||
|
||||
tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker")
|
||||
.getHexValue(0xADADAD);
|
||||
API.addOption(
|
||||
@@ -213,9 +216,6 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.showRecipeHandlerIcon", true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.recipeChainDir").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.recipeChainDir", 2, true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.ignorePotionOverlap").setComment("Ignore overlap with potion effect HUD")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.ignorePotionOverlap", true));
|
||||
@@ -228,6 +228,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));
|
||||
@@ -267,6 +271,11 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.scrollPages", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.freezeCycleOnHover")
|
||||
.setComment("Stop cycling the recipe while hovering over an ingredient or a button")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.freezeCycleOnHover", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
|
||||
|
||||
@@ -289,6 +298,32 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.history.useRows").setComment("Rows used in historical areas").getIntValue(2);
|
||||
API.addOption(new OptionIntegerField("inventory.history.useRows", 1, 5));
|
||||
|
||||
tag.getTag("inventory.history.worldSpecific").setComment("Global or world specific history")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.history.worldSpecific", true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
ItemPanels.itemPanel.historyPanel.save();
|
||||
ItemPanels.itemPanel.historyPanel.load();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.history.save").setComment("Enable/disable History save").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.history.save", true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
ItemPanels.itemPanel.historyPanel.save();
|
||||
ItemPanels.itemPanel.historyPanel.load();
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
tag.getTag("inventory.craftables.enabled").setComment("Enable/disable Craftables Panel").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.craftables.enabled", true));
|
||||
|
||||
@@ -346,6 +381,11 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.itemzoom.onlySolid").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.itemzoom.onlySolid", true));
|
||||
|
||||
tag.getTag("inventory.itemzoom.preferredSide")
|
||||
.setComment("Which side to prefer showing the zoomed item on, when both sides are free.")
|
||||
.getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.itemzoom.preferredSide", 3, true));
|
||||
|
||||
tag.getTag("inventory.itemzoom.helpText")
|
||||
.setComment("Display name \"Item Zoom\" and the hotkey to toggle this mod below the zoomed item.")
|
||||
.getBooleanValue(true);
|
||||
@@ -362,6 +402,13 @@ public class NEIClientConfig {
|
||||
|
||||
setFavoriteDefaults(tag);
|
||||
|
||||
tag.getTag("inventory.tooltip.showFluidTemperature").setComment("Enabled show Fluid Temperature")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.tooltip.showFluidTemperature", true));
|
||||
|
||||
tag.getTag("inventory.tooltip.showFluidState").setComment("Enabled show Fluid State").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.tooltip.showFluidState", true));
|
||||
|
||||
tag.getTag("inventory.itemIDs").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.itemIDs", 3, true));
|
||||
|
||||
@@ -815,35 +862,39 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
private static void setDefaultKeyBindings() {
|
||||
API.addHashBind("gui.recipe", Keyboard.KEY_R);
|
||||
API.addHashBind("gui.usage", Keyboard.KEY_U);
|
||||
API.addKeyBind("gui.back", Keyboard.KEY_BACK);
|
||||
API.addHashBind("gui.enchant", Keyboard.KEY_X);
|
||||
API.addHashBind("gui.potion", Keyboard.KEY_P);
|
||||
API.addKeyBind("gui.enchant", Keyboard.KEY_X);
|
||||
API.addKeyBind("gui.potion", Keyboard.KEY_P);
|
||||
API.addKeyBind("gui.prev", Keyboard.KEY_PRIOR);
|
||||
API.addKeyBind("gui.next", Keyboard.KEY_NEXT);
|
||||
API.addKeyBind("gui.prev_machine", Keyboard.KEY_UP);
|
||||
API.addKeyBind("gui.next_machine", Keyboard.KEY_DOWN);
|
||||
API.addKeyBind("gui.prev_recipe", Keyboard.KEY_LEFT);
|
||||
API.addKeyBind("gui.next_recipe", Keyboard.KEY_RIGHT);
|
||||
API.addHashBind("gui.hide", Keyboard.KEY_O);
|
||||
API.addHashBind("gui.search", Keyboard.KEY_F);
|
||||
API.addKeyBind("gui.bookmark", Keyboard.KEY_A);
|
||||
API.addHashBind("gui.favorite", Keyboard.KEY_F + NEIClientUtils.SHIFT_HASH);
|
||||
API.addKeyBind("gui.favorite_item", Keyboard.KEY_F);
|
||||
API.addHashBind("gui.remove_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH);
|
||||
API.addKeyBind("gui.bookmark_pull_items", Keyboard.KEY_V);
|
||||
API.addKeyBind("gui.hide", Keyboard.KEY_O);
|
||||
API.addKeyBind("gui.search", Keyboard.KEY_F);
|
||||
API.addKeyBind("gui.overlay", Keyboard.KEY_S);
|
||||
API.addKeyBind("gui.craft_items", Keyboard.KEY_C);
|
||||
API.addHashBind("gui.hide_bookmarks", Keyboard.KEY_B);
|
||||
API.addKeyBind("gui.getprevioussearch", Keyboard.KEY_UP);
|
||||
API.addKeyBind("gui.getnextsearch", Keyboard.KEY_DOWN);
|
||||
API.addKeyBind("gui.next_tooltip", Keyboard.KEY_Z);
|
||||
|
||||
API.addHashBind("gui.itemzoom_toggle", Keyboard.KEY_Z + NEIClientUtils.SHIFT_HASH);
|
||||
API.addHashBind("gui.itemzoom_hold", 0);
|
||||
API.addHashBind("gui.itemzoom_zoom_in", 0);
|
||||
API.addHashBind("gui.itemzoom_zoom_out", 0);
|
||||
API.addKeyBind("recipe.recipe", Keyboard.KEY_R);
|
||||
API.addKeyBind("recipe.usage", Keyboard.KEY_U);
|
||||
API.addKeyBind("recipe.back", Keyboard.KEY_BACK);
|
||||
API.addKeyBind("recipe.prev_machine", Keyboard.KEY_UP);
|
||||
API.addKeyBind("recipe.next_machine", Keyboard.KEY_DOWN);
|
||||
API.addKeyBind("recipe.prev_recipe", Keyboard.KEY_LEFT);
|
||||
API.addKeyBind("recipe.next_recipe", Keyboard.KEY_RIGHT);
|
||||
|
||||
API.addKeyBind("bookmark.add", Keyboard.KEY_A);
|
||||
API.addKeyBind("bookmark.favorite", Keyboard.KEY_F);
|
||||
API.addKeyBind("bookmark.favorite_item", Keyboard.KEY_F);
|
||||
API.addKeyBind("bookmark.remove_recipe", Keyboard.KEY_A);
|
||||
API.addKeyBind("bookmark.pull_items", Keyboard.KEY_V);
|
||||
API.addKeyBind("bookmark.chat_link", Keyboard.KEY_L);
|
||||
API.addKeyBind("bookmark.open_crafting_tree", Keyboard.KEY_T);
|
||||
API.addKeyBind("bookmark.hide", Keyboard.KEY_B);
|
||||
|
||||
API.addKeyBind("itemzoom.toggle", Keyboard.KEY_Z);
|
||||
API.addKeyBind("itemzoom.hold", 0);
|
||||
API.addKeyBind("itemzoom.zoom_in", 0);
|
||||
API.addKeyBind("itemzoom.zoom_out", 0);
|
||||
|
||||
API.addKeyBind("world.chunkoverlay", Keyboard.KEY_F9);
|
||||
API.addKeyBind("world.moboverlay", Keyboard.KEY_F7);
|
||||
@@ -855,10 +906,10 @@ public class NEIClientConfig {
|
||||
API.addKeyBind("world.rain", 0);
|
||||
API.addKeyBind("world.heal", 0);
|
||||
API.addKeyBind("world.creative", 0);
|
||||
API.addHashBind("gui.copy_name", Keyboard.KEY_C + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind("gui.copy_oredict", Keyboard.KEY_D + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind("gui.copy_id", Keyboard.KEY_X + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind("gui.chat_link_item", Keyboard.KEY_L + NEIClientUtils.CTRL_HASH);
|
||||
|
||||
API.addKeyBind("copy.name", Keyboard.KEY_C);
|
||||
API.addKeyBind("copy.oredict", Keyboard.KEY_D);
|
||||
API.addKeyBind("copy.identifier", Keyboard.KEY_X);
|
||||
}
|
||||
|
||||
public static OptionList getOptionList() {
|
||||
@@ -880,9 +931,14 @@ public class NEIClientConfig {
|
||||
|
||||
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
|
||||
|
||||
if (newWorld && Minecraft.getMinecraft().isSingleplayer()) {
|
||||
world.config.getTag("inventory.cheatmode")
|
||||
.setIntValue(NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
|
||||
if (newWorld && Minecraft.getMinecraft().isSingleplayer() && getLockedMode() == -1) {
|
||||
final int globalMode = global.config.getTag("inventory.cheatmode").getIntValue();
|
||||
final int worldMode = Math
|
||||
.min(globalMode, NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
|
||||
|
||||
if (worldMode != globalMode) {
|
||||
world.config.getTag("inventory.cheatmode").setIntValue(worldMode);
|
||||
}
|
||||
}
|
||||
|
||||
bootNEI();
|
||||
@@ -919,9 +975,8 @@ public class NEIClientConfig {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ItemPanels.bookmarkPanel != null) {
|
||||
ItemPanels.bookmarkPanel.save();
|
||||
}
|
||||
ItemPanels.bookmarkPanel.save();
|
||||
ItemPanels.itemPanel.historyPanel.save();
|
||||
|
||||
SubsetWidget.saveHidden();
|
||||
FavoriteRecipes.save();
|
||||
@@ -929,50 +984,28 @@ public class NEIClientConfig {
|
||||
world.saveNBT();
|
||||
}
|
||||
|
||||
private static final Map<String, String> keySettings = new HashMap<>();
|
||||
|
||||
public static int getKeyBinding(String string) {
|
||||
final String key = keySettings.computeIfAbsent(string, s -> "keys." + s);
|
||||
return getSetting(key).getIntValue(Keyboard.KEY_NONE);
|
||||
return KeyManager.getKeyCode(string);
|
||||
}
|
||||
|
||||
public static void setDefaultKeyBinding(String string, int key) {
|
||||
getSetting("keys." + string).getIntValue(key);
|
||||
KeyManager.registerKeyBinding(string, key);
|
||||
}
|
||||
|
||||
public static boolean isKeyHashDown(String string) {
|
||||
final int hash = getKeyBinding(string);
|
||||
|
||||
if (hash != Keyboard.CHAR_NONE && Keyboard.getEventKeyState()) {
|
||||
return KeyManager.keyStates.containsKey(string) ? Keyboard.isKeyDown(NEIKeyboardUtils.unhash(hash))
|
||||
: hash == NEIClientUtils.getKeyHash();
|
||||
}
|
||||
|
||||
return false;
|
||||
return KeyManager.isHashDown(string);
|
||||
}
|
||||
|
||||
public static String getKeyName(String keyBind) {
|
||||
return getKeyName(keyBind, 0);
|
||||
return KeyManager.getKeyName(keyBind);
|
||||
}
|
||||
|
||||
public static String getKeyName(String keyBind, int meta, int mouseBind) {
|
||||
final int hash = getKeyBinding(keyBind);
|
||||
|
||||
if (hash == Keyboard.CHAR_NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NEIClientUtils.getKeyName(hash + meta, mouseBind);
|
||||
return KeyManager.getKeyName(keyBind, meta, mouseBind);
|
||||
}
|
||||
|
||||
public static String getKeyName(String keyBind, int meta) {
|
||||
final int hash = getKeyBinding(keyBind);
|
||||
|
||||
if (hash == Keyboard.CHAR_NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NEIKeyboardUtils.getKeyName(hash + meta);
|
||||
return KeyManager.getKeyName(keyBind, meta);
|
||||
}
|
||||
|
||||
public static void bootNEI() {
|
||||
@@ -996,17 +1029,9 @@ public class NEIClientConfig {
|
||||
? NEIClientConfig.pluginsList.parallelStream()
|
||||
: NEIClientConfig.pluginsList.stream();
|
||||
|
||||
stream.forEach(clazz -> {
|
||||
try {
|
||||
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
|
||||
config.loadConfig();
|
||||
NEIModContainer.plugins.add(config);
|
||||
logger.debug("Loaded {}", clazz.getName());
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to Load {}", clazz.getName(), e);
|
||||
}
|
||||
});
|
||||
PluginLoader.load(stream, NEIModContainer.plugins, logger);
|
||||
|
||||
KeyManager.requestOptionsReload();
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
SubsetWidget.loadCustomSubsets();
|
||||
SubsetWidget.loadHidden();
|
||||
@@ -1086,6 +1111,10 @@ public class NEIClientConfig {
|
||||
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
|
||||
}
|
||||
|
||||
public static int getMaxVerticalTooltipRows() {
|
||||
return getIntSetting("inventory.bookmarks.maxVerticalTooltipRows");
|
||||
}
|
||||
|
||||
public static boolean showItemQuantityWidget() {
|
||||
return getBooleanSetting("inventory.showItemQuantityWidget");
|
||||
}
|
||||
@@ -1134,8 +1163,8 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap");
|
||||
}
|
||||
|
||||
public static int recipeChainDir() {
|
||||
return getIntSetting("inventory.bookmarks.recipeChainDir");
|
||||
public static boolean craftingTreeZoomWithoutModifier() {
|
||||
return getBooleanSetting("inventory.bookmarks.craftingTreeZoomWithoutModifier");
|
||||
}
|
||||
|
||||
public static boolean optimizeGuiOverlapComputation() {
|
||||
@@ -1146,6 +1175,10 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
|
||||
}
|
||||
|
||||
public static boolean freezeCycleOnHover() {
|
||||
return getBooleanSetting("inventory.guirecipe.freezeCycleOnHover");
|
||||
}
|
||||
|
||||
public static boolean requireShiftForOverlayRecipe() {
|
||||
return getBooleanSetting("inventory.guirecipe.shiftOverlayRecipe");
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -39,8 +39,10 @@ import net.minecraft.inventory.Container;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -56,8 +58,9 @@ import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.IInfiniteItemHandler;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NEIKeyboardUtils;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public class NEIClientUtils extends NEIServerUtils {
|
||||
|
||||
@@ -294,6 +297,83 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
return InventoryUtils.getInsertibleQuantity(new InventoryRange(player.inventory, 0, 36), itemstack) > 0;
|
||||
}
|
||||
|
||||
public static List<ItemOverlayState> presenceOverlay(List<PositionedStack> ingredients, List<ItemStack> invStacks) {
|
||||
final List<ItemOverlayState> states = new ArrayList<>();
|
||||
final Map<ItemStack, Long> remainingFluid = new IdentityHashMap<>();
|
||||
|
||||
for (PositionedStack stack : ingredients) {
|
||||
boolean found;
|
||||
|
||||
if (StackInfo.isFluidDisplayItem(stack.item)) {
|
||||
final FluidStack requiredFluid = StackInfo.getFluid(stack.item);
|
||||
found = requiredFluid != null && consumeFluid(invStacks, remainingFluid, requiredFluid);
|
||||
} else {
|
||||
found = false;
|
||||
|
||||
for (ItemStack is : invStacks) {
|
||||
if (is.stackSize > 0 && stack.contains(is)) {
|
||||
is.stackSize--;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states.add(new ItemOverlayState(stack, found));
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
private static boolean consumeFluid(List<ItemStack> invStacks, Map<ItemStack, Long> remainingFluid,
|
||||
FluidStack requiredFluid) {
|
||||
long need = requiredFluid.amount;
|
||||
final List<ItemStack> matched = new ArrayList<>();
|
||||
|
||||
for (ItemStack is : invStacks) {
|
||||
if (need <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is.stackSize <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final FluidStack invFluid = StackInfo.getFluid(is);
|
||||
|
||||
if (invFluid == null || !invFluid.isFluidEqual(requiredFluid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final long available = remainingFluid.computeIfAbsent(is, k -> (long) invFluid.amount * is.stackSize);
|
||||
if (available <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matched.add(is);
|
||||
need -= available;
|
||||
}
|
||||
|
||||
if (need > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long remainingNeed = requiredFluid.amount;
|
||||
for (ItemStack is : matched) {
|
||||
final long available = remainingFluid.get(is);
|
||||
final long consumed = Math.min(available, remainingNeed);
|
||||
|
||||
remainingFluid.put(is, available - consumed);
|
||||
remainingNeed -= consumed;
|
||||
|
||||
if (remainingNeed <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean shiftKey() {
|
||||
return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
|
||||
}
|
||||
@@ -381,8 +461,8 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
else sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName());
|
||||
}
|
||||
|
||||
public static void sendChatItemLink(ItemStack stackover) {
|
||||
if (hasSMPCounterPart()) NEICPH.sendChatLink(stackover);
|
||||
public static void sendChatItemLink(NBTTagCompound payload) {
|
||||
if (hasSMPCounterPart()) NEICPH.sendChatLink(payload);
|
||||
}
|
||||
|
||||
public static void toggleMagnetMode() {
|
||||
@@ -456,55 +536,8 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU);
|
||||
}
|
||||
|
||||
public static int getMetaHash() {
|
||||
if (Keyboard.getEventKeyState()) {
|
||||
int hash = 0;
|
||||
|
||||
if (altKey()) {
|
||||
hash = hash | ALT_HASH;
|
||||
}
|
||||
|
||||
if (shiftKey()) {
|
||||
hash = hash | SHIFT_HASH;
|
||||
}
|
||||
|
||||
if (controlKey()) {
|
||||
hash = hash | CTRL_HASH;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int getKeyHash() {
|
||||
|
||||
if (Keyboard.getEventKeyState()) {
|
||||
final int keycode = Keyboard.getEventKey();
|
||||
|
||||
if (!NEIKeyboardUtils.isHashKey(keycode)) {
|
||||
return getMetaHash() + keycode;
|
||||
}
|
||||
}
|
||||
|
||||
return Keyboard.CHAR_NONE;
|
||||
}
|
||||
|
||||
public static String getKeyName(int keyBind, int mouseBind) {
|
||||
StringJoiner keyText = new StringJoiner(" + ");
|
||||
String keyHash = keyBind == Keyboard.KEY_NONE ? "" : NEIKeyboardUtils.getKeyName(keyBind);
|
||||
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
|
||||
|
||||
if (!keyHash.isEmpty()) {
|
||||
keyText.add(keyHash);
|
||||
}
|
||||
|
||||
if (!mouseHash.isEmpty()) {
|
||||
keyText.add(mouseHash);
|
||||
}
|
||||
|
||||
return keyText.toString();
|
||||
return KeyManager.getKeyName(keyBind, mouseBind);
|
||||
}
|
||||
|
||||
public static void playClickSound() {
|
||||
@@ -531,6 +564,16 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static long getScrollMultiplier(ItemStack stack) {
|
||||
final long multiplier = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
|
||||
|
||||
if (multiplier != 0) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
return StackInfo.isFluidDisplayItem(stack) ? 144 : stack.getMaxStackSize();
|
||||
}
|
||||
|
||||
public static void drawRect(double left, double top, double width, double height, Color color) {
|
||||
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlend = GL11.glGetBoolean(GL11.GL_BLEND);
|
||||
@@ -573,6 +616,7 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
GuiDraw.fontRenderer.drawString(text, 0, 0, color, shadow);
|
||||
GL11.glScaled(1 / smallTextScale, 1 / smallTextScale, 1);
|
||||
GL11.glTranslated(-1 * offsetX, -1 * offsetY, 0);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import static codechicken.lib.gui.GuiDraw.getMousePosition;
|
||||
import static codechicken.nei.NEIClientConfig.canCheatItem;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.gui.inventory.GuiContainerCreative;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.inventory.SlotCrafting;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
|
||||
@@ -27,7 +27,7 @@ import codechicken.nei.commands.CommandUntranslator;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerInputHandler;
|
||||
import codechicken.nei.guihook.IContainerSlotClickHandler;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.util.EmptyContainer;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -77,29 +77,38 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
public static void updateUnlimitedItems(InventoryPlayer inventory) {
|
||||
if (!NEIClientConfig.canPerformAction("item") || !NEIClientConfig.hasSMPCounterPart()) return;
|
||||
|
||||
LinkedList<ItemStack> beforeStacks = new LinkedList<>();
|
||||
for (int i = 0; i < inventory.getSizeInventory(); i++)
|
||||
beforeStacks.add(NEIServerUtils.copyStack(inventory.getStackInSlot(i)));
|
||||
|
||||
for (int i = 0; i < inventory.getSizeInventory(); i++) {
|
||||
int size = inventory.getSizeInventory();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ItemStack stack = inventory.getStackInSlot(i);
|
||||
if (stack == null) continue;
|
||||
|
||||
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers)
|
||||
if (handler.canHandleItem(stack) && handler.isItemInfinite(stack))
|
||||
handler.replenishInfiniteStack(inventory, i);
|
||||
}
|
||||
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) {
|
||||
if (!handler.canHandleItem(stack) || !handler.isItemInfinite(stack)) continue;
|
||||
|
||||
for (int i = 0; i < inventory.getSizeInventory(); i++) {
|
||||
ItemStack newstack = inventory.getStackInSlot(i);
|
||||
long beforeSize = packStackSize(stack.stackSize, stack.getItemDamage());
|
||||
handler.replenishInfiniteStack(inventory, i);
|
||||
|
||||
if (!NEIServerUtils.areStacksIdentical(beforeStacks.get(i), newstack)) {
|
||||
inventory.setInventorySlotContents(i, beforeStacks.get(i)); // restore in case of SMP fail
|
||||
NEIClientUtils.setSlotContents(i, newstack, false); // sends via SMP handler ;)
|
||||
ItemStack newstack = inventory.getStackInSlot(i);
|
||||
if (!sameStack(stack, beforeSize, newstack)) {
|
||||
NEIClientUtils.setSlotContents(i, newstack, false); // sends via SMP handler ;)
|
||||
stack.stackSize = (int) (beforeSize >> 32);
|
||||
stack.setItemDamage((int) beforeSize);
|
||||
inventory.setInventorySlotContents(i, stack); // restore in case of SMP fail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long packStackSize(int stackSize, int itemDamage) {
|
||||
return ((long) stackSize << 32) | (itemDamage & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static boolean sameStack(ItemStack before, long beforeSize, ItemStack stack) {
|
||||
if (stack == null) return before == null;
|
||||
return before != null && Item.getIdFromItem(before.getItem()) == Item.getIdFromItem(stack.getItem())
|
||||
&& beforeSize == packStackSize(stack.stackSize, stack.getItemDamage());
|
||||
}
|
||||
|
||||
public static void processCreativeCycling(InventoryPlayer inventory) {
|
||||
if (NEIClientConfig.invCreativeMode() && NEIClientUtils.controlKey()) {
|
||||
if (selectedItem != inventory.currentItem) {
|
||||
@@ -160,7 +169,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
|
||||
if (GuiInfo.hasCustomSlots(gui)) return false;
|
||||
|
||||
if (NEIClientUtils.shiftKey()) {
|
||||
if (NEIClientUtils.shiftKey() && button != 2) { // MMB is used by other mods when an itemstack is picked.
|
||||
// fix small mouse movement while clicking causing slotIndex to be -999
|
||||
if (slotIndex == -999) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
@@ -252,7 +261,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
|
||||
// slot in GuiRecipe does not contain actual stack
|
||||
// and scroll should be handled by GuiRecipe, not here
|
||||
if (gui instanceof GuiRecipe) return false;
|
||||
if (gui.inventorySlots instanceof EmptyContainer) return false;
|
||||
|
||||
Point mousePos = getMousePosition();
|
||||
Slot mouseover = manager.window.getSlotAtPosition(mousePos.x, mousePos.y);
|
||||
|
||||
@@ -43,6 +43,7 @@ public class NEIModContainer extends DummyModContainer {
|
||||
|
||||
private static boolean gregTech5Loaded;
|
||||
private static boolean gtnhLibLoaded;
|
||||
private static boolean cubicChunksLoaded;
|
||||
|
||||
private static ASMDataTable asmDataTable;
|
||||
|
||||
@@ -69,6 +70,10 @@ public class NEIModContainer extends DummyModContainer {
|
||||
return gtnhLibLoaded;
|
||||
}
|
||||
|
||||
public static boolean isCCLoaded() {
|
||||
return cubicChunksLoaded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ArtifactVersion> getRequirements() {
|
||||
Set<ArtifactVersion> deps = new HashSet<>();
|
||||
@@ -96,6 +101,8 @@ public class NEIModContainer extends DummyModContainer {
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
gregTech5Loaded = Loader.isModLoaded("gregtech") && !Loader.isModLoaded("gregapi_post");
|
||||
gtnhLibLoaded = Loader.isModLoaded("gtnhlib");
|
||||
cubicChunksLoaded = Loader.isModLoaded("cubicchunks");
|
||||
ServerHandler.preInit();
|
||||
if (CommonUtils.isClient()) ClientHandler.preInit();
|
||||
asmDataTable = event.getAsmData();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.InventoryEffectRenderer;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.api.INEIGuiAdapter;
|
||||
|
||||
/**
|
||||
@@ -18,35 +14,37 @@ public class NEIPotionGuiHandler extends INEIGuiAdapter {
|
||||
|
||||
@Override
|
||||
public boolean hideItemPanelSlot(GuiContainer guiContainer, int slotX, int slotY, int slotW, int slotH) {
|
||||
if (NEIClientConfig.ignorePotionOverlap()) {
|
||||
if (!(guiContainer instanceof InventoryEffectRenderer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (guiContainer instanceof InventoryEffectRenderer) {
|
||||
int x = guiContainer.guiLeft - 124;
|
||||
int y = guiContainer.guiTop;
|
||||
Minecraft minecraft = guiContainer.mc;
|
||||
if (minecraft == null) {
|
||||
return false;
|
||||
}
|
||||
EntityPlayerSP player = minecraft.thePlayer;
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
Collection<PotionEffect> activePotionEffects = player.getActivePotionEffects();
|
||||
if (activePotionEffects.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int height = 33;
|
||||
if (activePotionEffects.size() > 5) {
|
||||
height = 132 / (activePotionEffects.size() - 1);
|
||||
}
|
||||
Rectangle4i slotRect = new Rectangle4i(slotX, slotY, slotW, slotH);
|
||||
for (PotionEffect potioneffect : activePotionEffects) {
|
||||
Rectangle4i box = new Rectangle4i(x, y, 140, 32);
|
||||
if (box.intersects(slotRect)) return true;
|
||||
y += height;
|
||||
}
|
||||
int x = guiContainer.guiLeft - 124;
|
||||
if (slotX + slotW <= x || slotX >= x + 140) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Minecraft minecraft = guiContainer.mc;
|
||||
if (minecraft == null) {
|
||||
return false;
|
||||
}
|
||||
EntityPlayerSP player = minecraft.thePlayer;
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
int potionCount = player.getActivePotionEffects().size();
|
||||
if (potionCount == 0 || NEIClientConfig.ignorePotionOverlap()) {
|
||||
return false;
|
||||
}
|
||||
int height = 33;
|
||||
if (potionCount > 5) {
|
||||
height = 132 / (potionCount - 1);
|
||||
}
|
||||
return intersectsPotionEffects(slotY, slotH, guiContainer.guiTop, potionCount, height);
|
||||
}
|
||||
|
||||
static boolean intersectsPotionEffects(int slotY, int slotH, int effectY, int potionCount, int height) {
|
||||
for (int i = 0; i < potionCount; i++, effectY += height) {
|
||||
if (slotY + slotH > effectY && slotY < effectY + 32) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.inventory.InventoryBasic;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.network.play.INetHandlerPlayServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityMobSpawner;
|
||||
@@ -91,11 +92,20 @@ 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) {
|
||||
NEIServerUtils.sendChatItemLink(sender, packet.readItemStack());
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
NEIServerUtils.sendChatItemLink(sender, payloadTag);
|
||||
}
|
||||
|
||||
private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
|
||||
import net.minecraft.command.ICommandSender;
|
||||
@@ -28,12 +29,13 @@ import net.minecraft.nbt.NBTException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.WorldSettings.GameType;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
@@ -44,6 +46,8 @@ import codechicken.lib.inventory.InventoryRange;
|
||||
import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.nei.PacketIDs.S2C;
|
||||
import codechicken.nei.item.ItemFluidDisplay;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTHelper;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
@@ -73,7 +77,7 @@ public class NEIServerUtils {
|
||||
public static void toggleRaining(World world, boolean notify) {
|
||||
boolean raining = !world.isRaining();
|
||||
if (!raining) // turn off
|
||||
((WorldServer) world).provider.resetRainAndThunder();
|
||||
world.provider.resetRainAndThunder();
|
||||
else world.getWorldInfo().setRaining(!isRaining(world));
|
||||
|
||||
if (notify)
|
||||
@@ -86,16 +90,46 @@ public class NEIServerUtils {
|
||||
player.extinguish();
|
||||
}
|
||||
|
||||
public static void sendChatItemLink(EntityPlayerMP sender, ItemStack stackover) {
|
||||
ServerUtils.sendChatToAll(
|
||||
new ChatComponentTranslation(
|
||||
"nei.chat.item_link.text",
|
||||
sender.getDisplayName(),
|
||||
stackover.func_151000_E().setChatStyle(
|
||||
stackover.func_151000_E().getChatStyle().setChatClickEvent(
|
||||
new ClickEvent(
|
||||
ClickEvent.Action.RUN_COMMAND,
|
||||
"/nei_bookmark " + stackover.writeToNBT(new NBTTagCompound()))))));
|
||||
public static void sendChatItemLink(EntityPlayerMP sender, NBTTagCompound payload) {
|
||||
if (payload == null) {
|
||||
sender.addChatMessage(new ChatComponentText("Invalid bookmark payload"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String linkType = payload.getString("linkType");
|
||||
final String payloadId = UUID.randomUUID().toString().substring(0, 8);
|
||||
final PacketCustom packet = new PacketCustom(NEISPH.channel, S2C.CACHE_BOOKMARK_DATA).writeString(payloadId)
|
||||
.writeNBTTagCompound((NBTTagCompound) payload.copy());
|
||||
|
||||
for (EntityPlayer player : ServerUtils.getPlayers()) {
|
||||
if (player instanceof EntityPlayerMP playerMP) {
|
||||
packet.sendToPlayer(playerMP);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final IChatComponent clickable;
|
||||
|
||||
if (payload.hasKey("item")) {
|
||||
clickable = StackInfo.loadFromNBT(payload.getCompoundTag("item")).func_151000_E();
|
||||
} else {
|
||||
clickable = new ChatComponentText("[")
|
||||
.appendSibling(new ChatComponentTranslation("nei.chat.item_link." + linkType + ".text"))
|
||||
.appendText("]");
|
||||
}
|
||||
|
||||
if (clickable == null) {
|
||||
throw new Exception("Invalid ItemStack NBT");
|
||||
}
|
||||
|
||||
clickable.getChatStyle()
|
||||
.setChatClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/nei_bookmark " + payloadId));
|
||||
ServerUtils.sendChatToAll(
|
||||
new ChatComponentTranslation("nei.chat.item_link.text", sender.getDisplayName(), clickable));
|
||||
} catch (Exception e) {
|
||||
sender.addChatMessage(new ChatComponentText(e.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static long getTime(World world) {
|
||||
@@ -229,9 +263,6 @@ public class NEIServerUtils {
|
||||
/**
|
||||
* GT Items don't have any NBT set for the recipe, so if either of the stacks has a NULL nbt, and the other doesn't,
|
||||
* pretend they stack
|
||||
*
|
||||
* @param ItemStack stack
|
||||
* @return
|
||||
*/
|
||||
public static boolean isItemTool(ItemStack stack) {
|
||||
return GTMetaBaseItemHolder.gtMetaBaseItem != null
|
||||
@@ -413,17 +444,53 @@ public class NEIServerUtils {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ItemStack[] extractRecipeItems(Object obj) {
|
||||
if (obj instanceof ItemStack) return new ItemStack[] { (ItemStack) obj };
|
||||
if (obj instanceof ItemStack[]) return (ItemStack[]) obj;
|
||||
if (obj instanceof List) return ((List<ItemStack>) obj).toArray(new ItemStack[0]);
|
||||
|
||||
throw new ClassCastException(obj + " not an ItemStack, ItemStack[] or List<ItemStack?");
|
||||
if (obj instanceof ItemStack item) {
|
||||
return new ItemStack[] { item };
|
||||
}
|
||||
|
||||
if (obj instanceof ItemStack[]arr) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (obj instanceof FluidStack fluid) {
|
||||
return new ItemStack[] { ItemFluidDisplay.createStack(fluid) };
|
||||
}
|
||||
|
||||
if (obj instanceof FluidStack[]arr) {
|
||||
final ItemStack[] items = new ItemStack[arr.length];
|
||||
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = ItemFluidDisplay.createStack(arr[i]);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
if (obj instanceof List list) {
|
||||
final ItemStack[] items = new ItemStack[list.size()];
|
||||
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = extractRecipeItem(list.get(i));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
throw new ClassCastException(
|
||||
obj + " not an ItemStack, ItemStack[], FluidStack, FluidStack[] or a List of them");
|
||||
}
|
||||
|
||||
private static ItemStack extractRecipeItem(Object obj) {
|
||||
if (obj == null) return null;
|
||||
if (obj instanceof ItemStack item) return item;
|
||||
if (obj instanceof FluidStack fluid) return ItemFluidDisplay.createStack(fluid);
|
||||
throw new ClassCastException(obj + " not an ItemStack or FluidStack");
|
||||
}
|
||||
|
||||
public static List<Integer> getRange(final int start, final int end) {
|
||||
return new AbstractList<Integer>() {
|
||||
return new AbstractList<>() {
|
||||
|
||||
@Override
|
||||
public Integer get(int index) {
|
||||
|
||||
@@ -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 {
|
||||
@@ -37,5 +38,7 @@ public final class PacketIDs {
|
||||
public static final int OPEN_ENCHANTMENT_GUI = 21;
|
||||
public static final int SET_CREATIVE_PLUS_MODE = 23;
|
||||
public static final int OPEN_POTION_GUI = 24;
|
||||
public static final int CACHE_BOOKMARK_DATA = 25;
|
||||
public static final int SYNC = 26;
|
||||
}
|
||||
}
|
||||
@@ -305,16 +305,16 @@ 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;
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.next")) {
|
||||
if (KeyManager.isKeyDown("gui.next")) {
|
||||
this.grid.shiftPage(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.prev")) {
|
||||
if (KeyManager.isKeyDown("gui.prev")) {
|
||||
this.grid.shiftPage(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
|
||||
final class PluginLoader {
|
||||
|
||||
private PluginLoader() {}
|
||||
|
||||
static void load(Stream<Class<?>> plugins, Collection<IConfigureNEI> loadedPlugins, Logger logger) {
|
||||
final ClassLoader classLoader = IConfigureNEI.class.getClassLoader();
|
||||
|
||||
plugins.forEach(clazz -> {
|
||||
final Thread thread = Thread.currentThread();
|
||||
final ClassLoader previousClassLoader = thread.getContextClassLoader();
|
||||
|
||||
// Common-pool workers may use the system loader, which cannot see FML-only classes. Keep FML's loader
|
||||
// through error logging too: old Log4j can otherwise throw while resolving a plugin's stack frames.
|
||||
try {
|
||||
thread.setContextClassLoader(classLoader);
|
||||
try {
|
||||
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
|
||||
config.loadConfig();
|
||||
loadedPlugins.add(config);
|
||||
logger.debug("Loaded {}", clazz.getName());
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to Load {}", clazz.getName(), e);
|
||||
}
|
||||
} finally {
|
||||
thread.setContextClassLoader(previousClassLoader);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,18 @@ public class PopupInputHandler implements IContainerInputHandler {
|
||||
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.enchant") && canPerformAction("enchant")) {
|
||||
@Override
|
||||
public boolean onHotkeyPressed(GuiContainer gui) {
|
||||
|
||||
if (KeyManager.isHashDown("gui.enchant") && canPerformAction("enchant")) {
|
||||
NEICPH.sendOpenEnchantmentWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.potion") && canPerformAction("potion")) {
|
||||
if (KeyManager.isHashDown("gui.potion") && canPerformAction("potion")) {
|
||||
NEICPH.sendOpenPotionWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,25 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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;
|
||||
import codechicken.nei.recipe.Badge;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -20,19 +30,26 @@ import codechicken.nei.recipe.StackInfo;
|
||||
/**
|
||||
* Simply an {@link ItemStack} with position. Mainly used in the recipe handlers.
|
||||
*/
|
||||
public class PositionedStack {
|
||||
public class PositionedStack implements Cloneable {
|
||||
|
||||
/** Maximum chance value representing 100% probability (1 unit = 0.01%). */
|
||||
public static final int CHANCE_FULL = 10_000;
|
||||
|
||||
public int relx;
|
||||
public int rely;
|
||||
public int width = 16;
|
||||
public int height = 16;
|
||||
public ItemStack[] items;
|
||||
// compatibility dummy
|
||||
public ItemStack item;
|
||||
|
||||
protected int chance = CHANCE_FULL;
|
||||
private boolean permutated = false;
|
||||
protected boolean permutated = false;
|
||||
|
||||
protected String acceptsLabel;
|
||||
|
||||
protected Function<ItemStack, List<String>> tooltip;
|
||||
protected List<Badge> badges;
|
||||
|
||||
public PositionedStack(Object object, int x, int y, boolean genPerms) {
|
||||
items = NEIServerUtils.extractRecipeItems(object);
|
||||
@@ -87,8 +104,32 @@ public class PositionedStack {
|
||||
for (ItemStack item : items) if (item.stackSize > i) item.stackSize = i;
|
||||
}
|
||||
|
||||
public void setAcceptsLabel(String acceptsLabel) {
|
||||
this.acceptsLabel = acceptsLabel;
|
||||
}
|
||||
|
||||
public String getAcceptsLabel() {
|
||||
return this.acceptsLabel;
|
||||
}
|
||||
|
||||
public void setBadges(List<Badge> badges) {
|
||||
this.badges = badges;
|
||||
}
|
||||
|
||||
public List<Badge> getBadges() {
|
||||
return null;
|
||||
return this.badges;
|
||||
}
|
||||
|
||||
public void setTooltip(List<String> tooltip) {
|
||||
this.tooltip = tooltip == null ? null : item -> tooltip;
|
||||
}
|
||||
|
||||
public void setTooltipProvider(Function<ItemStack, List<String>> provider) {
|
||||
this.tooltip = provider;
|
||||
}
|
||||
|
||||
public List<String> getTooltip() {
|
||||
return this.tooltip == null ? null : this.tooltip.apply(this.item);
|
||||
}
|
||||
|
||||
public int getChance() {
|
||||
@@ -100,14 +141,21 @@ public class PositionedStack {
|
||||
}
|
||||
|
||||
public PositionedStack copy() {
|
||||
PositionedStack pStack = new PositionedStack(
|
||||
Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new),
|
||||
relx,
|
||||
rely,
|
||||
false);
|
||||
pStack.permutated = this.permutated;
|
||||
pStack.chance = this.chance;
|
||||
return pStack;
|
||||
try {
|
||||
PositionedStack pStack = (PositionedStack) super.clone();
|
||||
pStack.items = Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new);
|
||||
pStack.item = this.item == null ? null : this.item.copy();
|
||||
pStack.badges = this.badges == null ? null : new ArrayList<>(this.badges);
|
||||
return pStack;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(int mousex, int mousey) {
|
||||
final int x = this.relx + (this.width - 16) / 2;
|
||||
final int y = this.rely + (this.height - 16) / 2;
|
||||
GuiContainerManager.drawItem(x, y, this.item);
|
||||
}
|
||||
|
||||
public List<ItemStack> getFilteredPermutations() {
|
||||
@@ -115,7 +163,7 @@ public class PositionedStack {
|
||||
}
|
||||
|
||||
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());
|
||||
@@ -164,7 +212,9 @@ public class PositionedStack {
|
||||
}
|
||||
|
||||
public boolean contains(int mx, int my) {
|
||||
return mx >= this.relx - 1 && mx < this.relx + 17 && my >= this.rely - 1 && my < this.rely + 17;
|
||||
return mx >= this.relx - 1 && mx < this.relx + this.width + 1
|
||||
&& my >= this.rely - 1
|
||||
&& my < this.rely + this.height + 1;
|
||||
}
|
||||
|
||||
public boolean contains(ItemStack ingredient) {
|
||||
@@ -192,4 +242,143 @@ public class PositionedStack {
|
||||
public String toString() {
|
||||
return "PositionedStack(output='" + item.toString() + "')";
|
||||
}
|
||||
|
||||
public static class Placeholder extends PositionedStack {
|
||||
|
||||
public Placeholder(Object object, int x, int y, boolean genPerms) {
|
||||
super(object, x, y, genPerms);
|
||||
}
|
||||
|
||||
public Placeholder(Object object, int x, int y) {
|
||||
this(object, x, y, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {}
|
||||
}
|
||||
|
||||
public static class Tank extends PositionedStack {
|
||||
|
||||
/** The tank size the fill level is measured against, in mB. 0 means "always render full". */
|
||||
protected int capacity = 0;
|
||||
protected boolean flowingTexture = false;
|
||||
protected FillDirection fillDirection = FillDirection.AUTO;
|
||||
protected DrawableResource overlay;
|
||||
|
||||
private ItemStack cachedFluidItem;
|
||||
private FluidStack cachedFluidStack;
|
||||
|
||||
public Tank(Object object, int x, int y, boolean genPerms) {
|
||||
super(object, x, y, genPerms);
|
||||
}
|
||||
|
||||
public Tank(Object object, int x, int y) {
|
||||
this(object, x, y, true);
|
||||
}
|
||||
|
||||
public Tank(Object object, int x, int y, int width, int height, int capacity) {
|
||||
super(object, x, y, false);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setCapacity(capacity);
|
||||
}
|
||||
|
||||
public int getCapacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
|
||||
public Tank setCapacity(int capacity) {
|
||||
this.capacity = Math.max(capacity, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasFlowingTexture() {
|
||||
return this.flowingTexture;
|
||||
}
|
||||
|
||||
public Tank setFlowingTexture(boolean flowingTexture) {
|
||||
this.flowingTexture = flowingTexture;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FillDirection getFillDirection() {
|
||||
return this.fillDirection;
|
||||
}
|
||||
|
||||
public Tank setFillDirection(FillDirection fillDirection) {
|
||||
this.fillDirection = fillDirection == null ? FillDirection.AUTO : fillDirection;
|
||||
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) {
|
||||
this.cachedFluidItem = this.item;
|
||||
this.cachedFluidStack = StackInfo.isFluidDisplayItem(this.item) ? StackInfo.getFluid(this.item) : null;
|
||||
}
|
||||
|
||||
return this.cachedFluidStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTooltip() {
|
||||
final List<String> tooltip = new ArrayList<>();
|
||||
final List<String> customTooltip = super.getTooltip();
|
||||
final FluidStack fluidStack = getFluidStack();
|
||||
|
||||
if (fluidStack != null) {
|
||||
tooltip.add(
|
||||
EnumChatFormatting.GRAY + NEIClientUtils
|
||||
.translate("recipe.fluid.tank.amount", NEIClientUtils.formatFluid(fluidStack.amount)));
|
||||
}
|
||||
|
||||
if (customTooltip != null) {
|
||||
tooltip.addAll(customTooltip);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
final FluidStack fluidStack = getFluidStack();
|
||||
|
||||
if (fluidStack == null) {
|
||||
super.draw(mousex, mousey);
|
||||
return;
|
||||
}
|
||||
|
||||
FluidDrawer.drawTank(
|
||||
this.relx,
|
||||
this.rely,
|
||||
this.width,
|
||||
this.height,
|
||||
this.capacity,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,9 +163,7 @@ public class PresetsList {
|
||||
public static void load() {
|
||||
final File presetsFile = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/presets.ini");
|
||||
|
||||
if (presetsFile == null || !presetsFile.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!presetsFile.exists()) return;
|
||||
|
||||
List<String> itemStrings;
|
||||
try (FileInputStream reader = new FileInputStream(presetsFile)) {
|
||||
|
||||
@@ -48,15 +48,19 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,6 @@ import net.minecraftforge.fluids.FluidStack;
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.ItemList.NothingItemFilter;
|
||||
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
||||
import codechicken.nei.SearchTokenParser.SearchMode;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
@@ -85,8 +84,8 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchMode getSearchMode() {
|
||||
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + this.name + "SearchMode"));
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,18 +227,24 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
if (isVisible() && NEIClientConfig.isKeyHashDown("gui.search")) {
|
||||
if (isVisible() && KeyManager.isHashDown("gui.search")) {
|
||||
setFocus(true);
|
||||
}
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
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
|
||||
|
||||
@@ -59,7 +59,13 @@ public class SearchTokenParser {
|
||||
|
||||
public EnumChatFormatting getHighlightedColor();
|
||||
|
||||
public SearchMode getSearchMode();
|
||||
default SearchMode getSearchMode() {
|
||||
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search." + getName() + "SearchMode"));
|
||||
}
|
||||
|
||||
default String getName() {
|
||||
return String.valueOf(getPrefix());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProvidersCache {
|
||||
@@ -179,7 +185,7 @@ public class SearchTokenParser {
|
||||
public synchronized ItemFilter getFilter(String rawText, boolean skipRecipeTokens) {
|
||||
final String filterText = EnumChatFormatting.getTextWithoutFormattingCodes(rawText).toLowerCase();
|
||||
|
||||
if (filterText == null || filterText.isEmpty()) {
|
||||
if (filterText.isEmpty()) {
|
||||
return new EverythingItemFilter();
|
||||
}
|
||||
|
||||
@@ -253,7 +259,7 @@ public class SearchTokenParser {
|
||||
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
|
||||
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
||||
|
||||
if (filterText == null || filterText.isEmpty()) {
|
||||
if (filterText.isEmpty()) {
|
||||
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
|
||||
}
|
||||
|
||||
@@ -439,7 +445,7 @@ public class SearchTokenParser {
|
||||
final char ch = input.charAt(index);
|
||||
|
||||
if (!insideQuotes && delimiters.indexOf(ch) >= 0
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
|
||||
|
||||
if (lastEnd == index) {
|
||||
token += String.valueOf(ch);
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.nei.item.ItemFluidDisplay;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent;
|
||||
@@ -18,6 +19,7 @@ import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent;
|
||||
import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
public class ServerHandler {
|
||||
|
||||
@@ -31,6 +33,14 @@ public class ServerHandler {
|
||||
NEIActions.init();
|
||||
}
|
||||
|
||||
public static void preInit() {
|
||||
registerFluidDisplayItem();
|
||||
}
|
||||
|
||||
private static void registerFluidDisplayItem() {
|
||||
GameRegistry.registerItem(new ItemFluidDisplay(), "neiFluidDisplay");
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void tickEvent(TickEvent.WorldTickEvent event) {
|
||||
if (event.phase == Phase.START && !event.world.isRemote
|
||||
@@ -46,8 +56,7 @@ public class ServerHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void tickEvent(TickEvent.PlayerTickEvent event) {
|
||||
if (event.phase == Phase.START && event.player instanceof EntityPlayerMP) {
|
||||
EntityPlayerMP player = (EntityPlayerMP) event.player;
|
||||
if (event.phase == Phase.START && event.player instanceof EntityPlayerMP player) {
|
||||
PlayerSave save = NEIServerConfig.forPlayer(player.getCommandSenderName());
|
||||
if (save == null) return;
|
||||
updateMagneticPlayer(player, save);
|
||||
|
||||
@@ -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;
|
||||
@@ -399,8 +403,8 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchMode getSearchMode() {
|
||||
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode"));
|
||||
public String getName() {
|
||||
return "subsets";
|
||||
}
|
||||
|
||||
private boolean matches(String name, String searchText, Pattern pattern) {
|
||||
@@ -686,12 +690,12 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
|
||||
for (SubsetTag tag : list) {
|
||||
tag.children.sort(Comparator.comparing(SubsetTag::displayName));
|
||||
tag.calculatedWidth = tag.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
|
||||
tag.calculatedWidth = -1;
|
||||
}
|
||||
|
||||
root.children.removeIf(child -> child.children.isEmpty() && child.items.isEmpty());
|
||||
root.children.sort(Comparator.comparing(SubsetTag::displayName));
|
||||
root.calculatedWidth = root.children.stream().mapToInt(SubsetTag::nameWidth).max().orElse(0);
|
||||
root.calculatedWidth = -1;
|
||||
|
||||
calculateVisibility(root);
|
||||
} catch (Throwable e) {
|
||||
|
||||
@@ -29,6 +29,10 @@ public abstract class Widget {
|
||||
|
||||
public void lastKeyTyped(int keyID, char keyChar) {}
|
||||
|
||||
public boolean onHotkeyPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean handleClickExt(int mx, int my, int button) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,19 @@ public class WidgetContainer extends Widget {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHotkeyPressed() {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.onHotkeyPressed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return this.isFocused;
|
||||
|
||||
@@ -4,6 +4,8 @@ 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;
|
||||
@@ -19,6 +21,8 @@ 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 cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
@@ -40,12 +44,13 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
public void tickKeyStates() {
|
||||
if (Minecraft.getMinecraft().currentScreen != null) return;
|
||||
|
||||
if (KeyManager.keyStates.get("world.moboverlay").down) {
|
||||
if (KeyManager.isPressed("world.moboverlay")) {
|
||||
mobOverlay = (mobOverlay + 1) % 2;
|
||||
NEIClientUtils
|
||||
.printChatMessage(new ChatComponentText(NEIClientUtils.translate("chat.moboverlay." + mobOverlay)));
|
||||
}
|
||||
if (KeyManager.keyStates.get("world.chunkoverlay").down) {
|
||||
|
||||
if (KeyManager.isPressed("world.chunkoverlay")) {
|
||||
chunkOverlay = (chunkOverlay + 1) % (NEIModContainer.isGT5Loaded() ? 4 : 3);
|
||||
// 0-2 (or 3 with gt)
|
||||
NEIClientUtils.printChatMessage(
|
||||
@@ -55,6 +60,7 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
}
|
||||
|
||||
public static void render(float frame) {
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_LINE_BIT);
|
||||
GL11.glPushMatrix();
|
||||
Entity entity = Minecraft.getMinecraft().renderViewEntity;
|
||||
|
||||
@@ -68,9 +74,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
|
||||
GL11.glTranslated(-interpPosX + intOffsetX, -interpPosY + intOffsetY, -interpPosZ + intOffsetZ);
|
||||
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glDepthFunc(GL11.GL_LEQUAL);
|
||||
renderChunkBounds(entity, intOffsetX, intOffsetY, intOffsetZ);
|
||||
renderMobSpawnOverlay(entity, intOffsetX, intOffsetY, intOffsetZ);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glPopAttrib();
|
||||
}
|
||||
|
||||
private static void buildMobSpawnOverlay(Entity entity) {
|
||||
@@ -118,6 +127,15 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static void renderMobSpawnOverlay(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
|
||||
if (mobOverlay == 0) {
|
||||
if (mobSpawnCache != null) {
|
||||
@@ -173,10 +191,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
curSpawnMode = spawnMode;
|
||||
}
|
||||
|
||||
tess.addVertex(x, y + 0.02, z);
|
||||
tess.addVertex(x + 1, y + 0.02, z + 1);
|
||||
tess.addVertex(x + 1, y + 0.02, z);
|
||||
tess.addVertex(x, y + 0.02, z + 1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,6 +237,9 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
private static void renderChunkBounds(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
|
||||
if (chunkOverlay == 0) return;
|
||||
|
||||
int worldMinY = worldMinHeight(entity.worldObj);
|
||||
int worldMaxY = worldMaxHeight(entity.worldObj);
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -235,14 +258,13 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
double dy = 128;
|
||||
double y1 = Math.floor(entity.posY - dy / 2);
|
||||
double y2 = y1 + dy;
|
||||
if (y1 < 0) {
|
||||
y1 = 0;
|
||||
y2 = dy;
|
||||
if (y1 < worldMinY) {
|
||||
y1 = worldMinY;
|
||||
y2 = worldMinY + dy;
|
||||
}
|
||||
|
||||
if (y1 > entity.worldObj.getHeight()) {
|
||||
y2 = entity.worldObj.getHeight();
|
||||
y1 = y2 - dy;
|
||||
if (y2 > worldMaxY) {
|
||||
y2 = worldMaxY;
|
||||
y1 = Math.max(worldMaxY - dy, worldMinY);
|
||||
}
|
||||
|
||||
y1 -= intOffsetY;
|
||||
@@ -268,23 +290,44 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
tess.addVertex(x1, y2, z1);
|
||||
}
|
||||
|
||||
if (NEIModContainer.isCCLoaded()) {
|
||||
tess.setColorRGBA_F(0, 0.5F, 0.9F, (float) dist * 0.5F);
|
||||
int cubeBase = (int) Math.floor((y1 + intOffsetY) / 16.0) * 16;
|
||||
for (int cubeY = cubeBase; cubeY <= y2 + intOffsetY; cubeY += 16) {
|
||||
double ry = cubeY - intOffsetY;
|
||||
tess.addVertex(x1, ry, z1);
|
||||
tess.addVertex(x2, ry, z1);
|
||||
tess.addVertex(x1, ry, z2);
|
||||
tess.addVertex(x2, ry, z2);
|
||||
tess.addVertex(x1, ry, z1);
|
||||
tess.addVertex(x1, ry, z2);
|
||||
tess.addVertex(x2, ry, z1);
|
||||
tess.addVertex(x2, ry, z2);
|
||||
}
|
||||
}
|
||||
|
||||
if (cx == 0 && cz == 0) {
|
||||
if (chunkOverlay == 2) {
|
||||
dy = 32;
|
||||
y1 = Math.floor(entity.posY - dy / 2);
|
||||
y2 = y1 + dy;
|
||||
if (y1 < 0) {
|
||||
y1 = 0;
|
||||
y2 = dy;
|
||||
if (y1 < worldMinY) {
|
||||
y1 = worldMinY;
|
||||
y2 = worldMinY + dy;
|
||||
}
|
||||
|
||||
if (y1 > entity.worldObj.getHeight()) {
|
||||
y2 = entity.worldObj.getHeight();
|
||||
y1 = y2 - dy;
|
||||
if (y2 > worldMaxY) {
|
||||
y2 = worldMaxY;
|
||||
y1 = Math.max(worldMaxY - dy, worldMinY);
|
||||
}
|
||||
|
||||
y1 -= intOffsetY;
|
||||
y2 -= intOffsetY;
|
||||
double centerX = (x1 + x2) / 2;
|
||||
double centerZ = (z1 + z2) / 2;
|
||||
|
||||
tess.setColorRGBA_F(0.9F, 0.9F, 0, (float) dist);
|
||||
tess.addVertex(centerX, y1, centerZ);
|
||||
tess.addVertex(centerX, y2, centerZ);
|
||||
|
||||
tess.setColorRGBA_F(0, 0.9F, 0, 0.4F);
|
||||
for (double y = (int) y1; y <= y2; y++) {
|
||||
@@ -379,6 +422,22 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
private static int worldMinHeight(World world) {
|
||||
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
|
||||
return worldHeights.getMinHeight();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int worldMaxHeight(World world) {
|
||||
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
|
||||
return worldHeights.getMaxHeight();
|
||||
} else {
|
||||
return world.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private static Callable<String> oregenPatternReader;
|
||||
|
||||
private static String getOregenPatternName() {
|
||||
@@ -400,13 +459,9 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
"gregtech.common.GT_Worldgenerator");
|
||||
|
||||
try {
|
||||
// GT5-Unofficial >= 5.09.54.02 support
|
||||
final Method server = gtWorldGenerator.getMethod("getServerOregenPattern");
|
||||
final Method client = gtWorldGenerator.getMethod("getClientOregenPattern");
|
||||
return () -> {
|
||||
final Method accessor = Minecraft.getMinecraft().isSingleplayer() ? server : client;
|
||||
return ((Enum<?>) accessor.invoke(null)).name();
|
||||
};
|
||||
// GT5-Unofficial >= 5.09.54.29 support
|
||||
final Method accessor = gtWorldGenerator.getMethod("getOregenPattern");
|
||||
return () -> ((Enum<?>) accessor.invoke(null)).name();
|
||||
} catch (NoSuchMethodException legacy) {
|
||||
// Older GT
|
||||
final Field oregenPattern = gtWorldGenerator.getDeclaredField("oregenPattern");
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@@ -22,7 +23,6 @@ import codechicken.nei.ItemList;
|
||||
import codechicken.nei.ItemSorter;
|
||||
import codechicken.nei.ItemStackSet;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.KeyManager.KeyState;
|
||||
import codechicken.nei.LayoutManager;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.OffsetPositioner;
|
||||
@@ -34,7 +34,7 @@ import codechicken.nei.SubsetWidget.SubsetTag;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.config.Option;
|
||||
import codechicken.nei.config.OptionKeyBind;
|
||||
import codechicken.nei.item.FluidDrawer;
|
||||
import codechicken.nei.recipe.CatalystInfo;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
@@ -148,14 +148,14 @@ public class API {
|
||||
* Hide an item from the item panel Damage values of OreDictionary.WILDCARD_VALUE and ItemStackMap.WILDCARD_TAG tags
|
||||
* function as wildcards for their respective variables
|
||||
*/
|
||||
public static void hideItem(ItemStack item) {
|
||||
public static synchronized void hideItem(ItemStack item) {
|
||||
if (!ItemInfo.hiddenItems.contains(item)) {
|
||||
ItemInfo.hiddenItems.add(item);
|
||||
LayoutManager.markItemsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public static void hideItem(String rule) {
|
||||
public static synchronized void hideItem(String rule) {
|
||||
ItemFilter filter = ItemStackFilterParser.parse(rule);
|
||||
if (filter != null) {
|
||||
ItemInfo.hiddenItemsRules.filters.add(filter);
|
||||
@@ -180,6 +180,8 @@ public class API {
|
||||
|
||||
/**
|
||||
* Add an alias name that this item can be found by in the search field, in addition to its display name.
|
||||
*
|
||||
* @param aliases translation keys, localized at search time
|
||||
*/
|
||||
public static void setAliases(ItemStack item, String... aliases) {
|
||||
final List<String> list = Arrays.asList(aliases).stream().filter(Objects::nonNull).map(String::trim)
|
||||
@@ -223,20 +225,12 @@ public class API {
|
||||
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
|
||||
*/
|
||||
public static void addKeyBind(String ident, int defaultKey) {
|
||||
NEIClientConfig.setDefaultKeyBinding(ident, defaultKey);
|
||||
KeyManager.keyStates.put(ident, new KeyState());
|
||||
addOption(new OptionKeyBind(ident));
|
||||
KeyManager.registerKeyBinding(ident, defaultKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom KeyBinding to be configured in the Controls menu.
|
||||
*
|
||||
* @param ident An identifier for your key, eg "shoot"
|
||||
* @param defaultKey The default value, commonly obtained from {@link Keyboard}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void addHashBind(String ident, int defaultKey) {
|
||||
NEIClientConfig.setDefaultKeyBinding(ident, defaultKey);
|
||||
addOption(new OptionKeyBind(ident, true));
|
||||
KeyManager.registerKeyBinding(ident, defaultKey);
|
||||
}
|
||||
|
||||
public static void addOption(Option option) {
|
||||
@@ -385,6 +379,16 @@ public class API {
|
||||
StackInfo.stackStringifyHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the default rendering of a fluid in NEI fluid display slots and fluid tanks.
|
||||
*
|
||||
* @param fluid the fluid to render
|
||||
* @param renderer the renderer, or null to restore the default rendering
|
||||
*/
|
||||
public static void registerFluidRenderer(Fluid fluid, IFluidRenderer renderer) {
|
||||
FluidDrawer.registerFluidRenderer(fluid, renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an association between an ingredient and what it can craft. (i.e. Furnace ItemStack -> Smelting and Fuel
|
||||
* Recipes) Allows players to see what ingredient they need to craft in order to make recipes from a recipe
|
||||
|
||||
@@ -51,20 +51,29 @@ public class GuiInfo {
|
||||
}
|
||||
|
||||
public static boolean hideItemPanelSlot(GuiContainer gui, Rectangle4i rect) {
|
||||
return hideItemPanelSlot(gui, rect, getGuiHandlersSnapshot());
|
||||
}
|
||||
|
||||
public static INEIGuiHandler[] getGuiHandlersSnapshot() {
|
||||
try {
|
||||
readLock.lock();
|
||||
if (guiHandlers.stream()
|
||||
.anyMatch(handler -> handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h))) {
|
||||
return true;
|
||||
}
|
||||
return guiHandlers.toArray(new INEIGuiHandler[0]);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hideItemPanelSlot(GuiContainer gui, Rectangle4i rect, INEIGuiHandler[] handlers) {
|
||||
for (INEIGuiHandler handler : handlers) {
|
||||
if (handler.hideItemPanelSlot(gui, rect.x, rect.y, rect.w, rect.h)) return true;
|
||||
}
|
||||
|
||||
for (Object item : gui.buttonList) {
|
||||
GuiButton button = (GuiButton) item;
|
||||
|
||||
if ((new Rectangle4i(button.xPosition, button.yPosition, button.width, button.height)).intersects(rect)) {
|
||||
if (button.xPosition + button.width > rect.x && button.xPosition < rect.x + rect.w
|
||||
&& button.yPosition + button.height > rect.y
|
||||
&& button.yPosition < rect.y + rect.h) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,15 +1,9 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import codechicken.nei.VisiblityData;
|
||||
|
||||
/**
|
||||
* Lets you just override those things you want to
|
||||
*/
|
||||
@@ -17,31 +11,6 @@ public class INEIGuiAdapter implements INEIGuiHandler {
|
||||
|
||||
static final Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\].+*?^$\\\\|]");
|
||||
|
||||
@Override
|
||||
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
||||
return currentVisibility;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String formattingText(String displayName) {
|
||||
return SPECIAL_REGEX_CHARS.matcher(EnumChatFormatting.getTextWithoutFormattingCodes(displayName))
|
||||
.replaceAll("\\\\$0");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
@@ -12,23 +13,29 @@ import codechicken.nei.VisiblityData;
|
||||
*/
|
||||
public interface INEIGuiHandler {
|
||||
|
||||
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility);
|
||||
default VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
||||
return currentVisibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* NEI will give the specified item to the InventoryRange returned if the player's inventory is full. Should not
|
||||
* return null, just an empty list
|
||||
*/
|
||||
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item);
|
||||
default Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of TaggedInventoryAreas that will be used with the savestates.
|
||||
*/
|
||||
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui);
|
||||
default List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks while an itemstack has been dragged from the item panel. Use this to set configurable slots and
|
||||
* the like. Changes made to the stackSize of the dragged stack will be kept
|
||||
*
|
||||
*
|
||||
* @param gui The current gui instance
|
||||
* @param mousex The x position of the mouse
|
||||
* @param mousey The y position of the mouse
|
||||
@@ -37,18 +44,22 @@ public interface INEIGuiHandler {
|
||||
* @return True if the drag n drop was handled. False to resume processing through other routes. The held stack will
|
||||
* be deleted if draggedStack.stackSize == 0
|
||||
*/
|
||||
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button);
|
||||
default boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to prevent the item panel from drawing on top of other gui elements. This function will also be called with
|
||||
* a 1x1 size rectangle on the mouse position for determining if the given coordinate should override item panel
|
||||
* functions such as scrolling
|
||||
*
|
||||
*
|
||||
* @param x The x coordinate of the rectangle bounding the slot
|
||||
* @param y The y coordinate of the rectangle bounding the slot
|
||||
* @param w The w coordinate of the rectangle bounding the slot
|
||||
* @param h The h coordinate of the rectangle bounding the slot
|
||||
* @return true if the item panel slot within the specified rectangle should not be rendered.
|
||||
*/
|
||||
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h);
|
||||
default boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
@@ -38,28 +35,14 @@ public interface IOverlayHandler {
|
||||
}
|
||||
|
||||
default List<ItemOverlayState> presenceOverlay(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) {
|
||||
final List<ItemOverlayState> itemPresenceSlots = new ArrayList<>();
|
||||
final List<PositionedStack> ingredients = recipe.getIngredientStacks(recipeIndex);
|
||||
final List<ItemStack> invStacks = firstGui.inventorySlots.inventorySlots.stream()
|
||||
.filter(
|
||||
s -> s != null && s.getStack() != null
|
||||
&& s.getStack().stackSize > 0
|
||||
&& s.isItemValid(s.getStack())
|
||||
&& s.canTakeStack(firstGui.mc.thePlayer))
|
||||
.map(s -> s.getStack().copy()).collect(Collectors.toList());
|
||||
|
||||
for (PositionedStack stack : ingredients) {
|
||||
Optional<ItemStack> used = invStacks.stream().filter(is -> is.stackSize > 0 && stack.contains(is))
|
||||
.findAny();
|
||||
|
||||
itemPresenceSlots.add(new ItemOverlayState(stack, used.isPresent()));
|
||||
|
||||
if (used.isPresent()) {
|
||||
ItemStack is = used.get();
|
||||
is.stackSize -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return itemPresenceSlots;
|
||||
return NEIClientUtils.presenceOverlay(
|
||||
recipe.getIngredientStacks(recipeIndex),
|
||||
firstGui.inventorySlots.inventorySlots.stream()
|
||||
.filter(
|
||||
s -> s != null && s.getStack() != null
|
||||
&& s.getStack().stackSize > 0
|
||||
&& s.isItemValid(s.getStack())
|
||||
&& s.canTakeStack(firstGui.mc.thePlayer))
|
||||
.map(s -> s.getStack().copy()).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
@@ -24,4 +24,8 @@ public interface IStackStringifyHandler {
|
||||
|
||||
default void pauseItemDamageSound(boolean pause) {}
|
||||
|
||||
default boolean isFluidDisplayItem(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.RegistryNamespaced;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import net.minecraftforge.common.BiomeDictionary;
|
||||
import net.minecraftforge.common.IShearable;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
@@ -60,6 +61,8 @@ import codechicken.nei.config.HandlerDumper;
|
||||
import codechicken.nei.config.ItemPanelDumper;
|
||||
import codechicken.nei.config.RegistryDumper;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.item.FluidDisplayRenderer;
|
||||
import codechicken.nei.item.ItemFluidDisplay;
|
||||
import codechicken.nei.recipe.BrewingRecipeHandler;
|
||||
import codechicken.nei.recipe.RecipeItemInputHandler;
|
||||
import codechicken.nei.search.IdentifierFilter;
|
||||
@@ -77,10 +80,13 @@ import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier;
|
||||
*/
|
||||
public class ItemInfo {
|
||||
|
||||
public static enum Layout {
|
||||
public enum Layout {
|
||||
|
||||
HEADER,
|
||||
BODY,
|
||||
FOOTER
|
||||
FOOTER;
|
||||
|
||||
public static final Layout[] VALUES = values();
|
||||
}
|
||||
|
||||
public static final ArrayListMultimap<Layout, IHighlightHandler> highlightHandlers = ArrayListMultimap.create();
|
||||
@@ -130,6 +136,7 @@ public class ItemInfo {
|
||||
|
||||
public static void preInit() {
|
||||
addMobSpawnerItem();
|
||||
setupFluidDisplayItemClient();
|
||||
}
|
||||
|
||||
public static void load() {
|
||||
@@ -315,6 +322,11 @@ public class ItemInfo {
|
||||
GameDataManipulator.replaceItem(Block.getIdFromBlock(Blocks.mob_spawner), new ItemMobSpawner());
|
||||
}
|
||||
|
||||
private static void setupFluidDisplayItemClient() {
|
||||
ItemFluidDisplay.INSTANCE.setCreativeTab(ItemFluidDisplay.tabFluids);
|
||||
MinecraftForgeClient.registerItemRenderer(ItemFluidDisplay.INSTANCE, new FluidDisplayRenderer());
|
||||
}
|
||||
|
||||
private static void addInfiniteHandlers() {
|
||||
API.addInfiniteItemHandler(new InfiniteStackSizeHandler());
|
||||
API.addInfiniteItemHandler(new InfiniteToolHandler());
|
||||
@@ -399,6 +411,7 @@ public class ItemInfo {
|
||||
item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 2);
|
||||
API.addSubset("Items.Armor.Boots", item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 3);
|
||||
API.addSubset("Items.Food", item -> item.getItem() instanceof ItemFood);
|
||||
API.addSubset("Items.Fluids", item -> item.getItem() instanceof ItemFluidDisplay);
|
||||
API.addSubset("Items.Potions.Ingredients", BrewingRecipeHandler.ingredients);
|
||||
|
||||
for (CreativeTabs tab : CreativeTabs.creativeTabArray) {
|
||||
@@ -472,7 +485,7 @@ public class ItemInfo {
|
||||
MovingObjectPosition mop) {
|
||||
List<String> retString = new ArrayList<>();
|
||||
|
||||
for (ItemInfo.Layout layout : ItemInfo.Layout.values())
|
||||
for (ItemInfo.Layout layout : Layout.VALUES)
|
||||
for (IHighlightHandler handler : ItemInfo.highlightHandlers.get(layout))
|
||||
retString = handler.handleTextData(itemStack, world, player, mop, retString, layout);
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ 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(false);
|
||||
GuiOptionList env = modeOption.parent.synthesizeEnvironment(worldConfig);
|
||||
if (!modeOption.optionValid(modeOption.value())) {
|
||||
modeOption.copyGlobals();
|
||||
modeOption.cycle();
|
||||
|
||||
@@ -19,7 +19,7 @@ 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;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
@@ -29,12 +29,15 @@ import codechicken.nei.bookmark.BookmarkGrid;
|
||||
import codechicken.nei.bookmark.BookmarkGroup;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.bookmark.BookmarkPayload;
|
||||
import codechicken.nei.bookmark.BookmarksGridSlot;
|
||||
import codechicken.nei.bookmark.tree.GuiCraftingTree;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
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;
|
||||
@@ -75,8 +78,7 @@ public abstract class ShortcutInputHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.overlay") && stackover == null
|
||||
&& LayoutManager.overlayRenderer != null) {
|
||||
if (KeyManager.isKeyDown("gui.overlay") && stackover == null && LayoutManager.overlayRenderer != null) {
|
||||
LayoutManager.overlayRenderer = null;
|
||||
return true;
|
||||
}
|
||||
@@ -86,16 +88,27 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
if (groupId != -1) {
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.remove_recipe")) {
|
||||
if (KeyManager.isHashDown("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH)) {
|
||||
ItemPanels.bookmarkPanel.removeGroup(groupId);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
|
||||
if (KeyManager.isKeyDown("bookmark.pull_items")) {
|
||||
return ItemPanels.bookmarkPanel.pullBookmarkItems(groupId, NEIClientUtils.shiftKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled() && NEIClientConfig.isKeyHashDown("gui.craft_items")
|
||||
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
|
||||
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(groupId).toNBT());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)
|
||||
&& KeyManager.isKeyDown("bookmark.open_crafting_tree")) {
|
||||
GuiCraftingTree.openCraftingTreeGui(ItemPanels.bookmarkPanel.getGrid(), groupId);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
|
||||
&& NEIClientUtils.shiftKey()
|
||||
&& ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) {
|
||||
final RecipeChainMath math = ItemPanels.bookmarkPanel.getGrid().createRecipeChainMath(groupId);
|
||||
@@ -118,46 +131,47 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
stackover = stackover.copy();
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.overlay")) {
|
||||
if (KeyManager.isKeyDown("gui.overlay")) {
|
||||
return openOverlayRecipe(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.copy_name")) {
|
||||
if (KeyManager.isHashDown("copy.name", NEIClientUtils.CTRL_HASH)) {
|
||||
return copyItemStackName(stackover);
|
||||
}
|
||||
if (NEIClientConfig.isKeyHashDown("gui.copy_id")) {
|
||||
|
||||
if (KeyManager.isHashDown("copy.identifier", NEIClientUtils.CTRL_HASH)) {
|
||||
return copyItemStackID(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.copy_oredict")) {
|
||||
if (KeyManager.isHashDown("copy.oredict", NEIClientUtils.CTRL_HASH)) {
|
||||
return copyItemStackOreDictionary(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.chat_link_item")) {
|
||||
return sendItemStackChatLink(stackover);
|
||||
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
|
||||
return sendRecipeInChatLink(stackover, NEIClientUtils.shiftKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.recipe")) {
|
||||
if (KeyManager.isKeyDown("recipe.recipe")) {
|
||||
return GuiCraftingRecipe.openRecipeGui("item", stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.usage")) {
|
||||
if (KeyManager.isKeyDown("recipe.usage")) {
|
||||
return GuiUsageRecipe.openRecipeGui("item", stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.favorite")) {
|
||||
if (KeyManager.isHashDown("bookmark.favorite", NEIClientUtils.SHIFT_HASH)) {
|
||||
return saveFavoriteTree(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark")) {
|
||||
if (KeyManager.isKeyDown("bookmark.add")) {
|
||||
return saveRecipeInBookmark(stackover, NEIClientUtils.shiftKey(), NEIClientUtils.controlKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
|
||||
if (KeyManager.isKeyDown("bookmark.pull_items")) {
|
||||
return pullRecipeItems(stackover, NEIClientUtils.shiftKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled() && NEIClientConfig.isKeyHashDown("gui.craft_items")
|
||||
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
|
||||
&& NEIClientUtils.shiftKey()) {
|
||||
return runAutoCrafting(stackover, !NEIClientUtils.controlKey());
|
||||
}
|
||||
@@ -197,10 +211,37 @@ public abstract class ShortcutInputHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean sendItemStackChatLink(ItemStack stackover) {
|
||||
if (stackover == null) return false;
|
||||
private static boolean sendRecipeInChatLink(ItemStack stackover, boolean saveIngredients) {
|
||||
final Point mousePos = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousePos.x, mousePos.y);
|
||||
|
||||
NEIClientUtils.sendChatItemLink(stackover); // I wish clients could just send formatted messages
|
||||
if (slot != null && saveIngredients && slot.getType() == BookmarkItemType.RESULT) {
|
||||
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(slot.getBookmarkItem()).toNBT());
|
||||
return true;
|
||||
}
|
||||
|
||||
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
|
||||
|
||||
if (recipe != null && saveIngredients) {
|
||||
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(recipe).toNBT());
|
||||
return true;
|
||||
}
|
||||
|
||||
final RecipeId recipeId;
|
||||
|
||||
if (slot != null) {
|
||||
recipeId = slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.getRecipeId();
|
||||
} else {
|
||||
recipeId = recipe != null ? recipe.getRecipeId() : null;
|
||||
}
|
||||
|
||||
final boolean existsRecipe = NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkItemsWithRecipe");
|
||||
|
||||
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
|
||||
}
|
||||
|
||||
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(stackover, existsRecipe ? recipeId : null).toNBT());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,7 +281,7 @@ public abstract class ShortcutInputHandler {
|
||||
if (slot.getGroup().crafting == null) {
|
||||
final Recipe recipe = Recipe.of(slot.getRecipeId());
|
||||
if (recipe != null) {
|
||||
math = RecipeChainMath.of(recipe, Math.max(1, slot != null ? slot.getMultiplier() : 1));
|
||||
math = RecipeChainMath.of(recipe, Math.max(1, slot.getMultiplier()));
|
||||
}
|
||||
} else {
|
||||
math = ItemPanels.bookmarkPanel.getGrid()
|
||||
@@ -374,41 +415,37 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
private static Recipe getFocusedRecipe(ItemStack stackover, int mousex, int mousey, boolean useFavorites) {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
Recipe recipe = null;
|
||||
|
||||
if (useFavorites) {
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
final RecipeId recipeId = itemSlot.getRecipeId();
|
||||
|
||||
if (recipeId != null && (recipe = Recipe.of(recipeId)) != null) {
|
||||
|
||||
recipe.getIngredients().stream().forEach(ingr -> {
|
||||
final List<ItemStack> permutations = ingr.getPermutations();
|
||||
for (int index = 0; index < permutations.size(); index++) {
|
||||
if (FavoriteRecipes.contains(permutations.get(index))) {
|
||||
ingr.setActiveIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
if (gui instanceof GuiRecipe<?>guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return focusedRecipe;
|
||||
}
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe guiRecipe) {
|
||||
recipe = guiRecipe.getFocusedRecipe();
|
||||
if (!useFavorites) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final HoveredRecipe hovered = HoveredRecipe.of(gui, stackover);
|
||||
|
||||
if (hovered == null || hovered.source != HoveredRecipe.Source.ITEM_PANEL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Recipe recipe = Recipe.of(hovered.recipeId);
|
||||
|
||||
if (recipe != null) {
|
||||
recipe.getIngredients().stream().forEach(ingr -> {
|
||||
final List<ItemStack> permutations = ingr.getPermutations();
|
||||
for (int index = 0; index < permutations.size(); index++) {
|
||||
if (FavoriteRecipes.contains(permutations.get(index))) {
|
||||
ingr.setActiveIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return recipe;
|
||||
@@ -443,27 +480,38 @@ public abstract class ShortcutInputHandler {
|
||||
final Map<String, String> hotkeys = new HashMap<>();
|
||||
|
||||
if (groupId != -1) {
|
||||
final boolean isCraftingMode = ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId);
|
||||
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.remove_recipe"),
|
||||
KeyManager.getKeyName("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.remove_recipe"));
|
||||
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.chat_link"));
|
||||
|
||||
if (isCraftingMode) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.open_crafting_tree"),
|
||||
NEIClientUtils.translate("bookmark.group.open_crafting_tree"));
|
||||
}
|
||||
|
||||
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
|
||||
KeyManager.getKeyName("bookmark.pull_items"),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
|
||||
}
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled() && ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) {
|
||||
if (NEIClientConfig.autocraftingEnabled() && isCraftingMode) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig
|
||||
.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.craft_missing"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.craft_all"));
|
||||
}
|
||||
|
||||
@@ -477,37 +525,37 @@ 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();
|
||||
|
||||
if (group.crafting != null && group.collapsed) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.remove_recipe"));
|
||||
} else if (slot.getRecipeId() == null || slot.getType() == BookmarkItemType.ITEM) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark"),
|
||||
NEIClientUtils.translate("bookmark.remove_item"));
|
||||
hotkeys.put(KeyManager.getKeyName("bookmark.add"), NEIClientUtils.translate("bookmark.remove_item"));
|
||||
} else if (group.crafting != null && group.crafting.recipeRelations.entrySet().stream().anyMatch(
|
||||
entry -> entry.getKey().equals(slot.getRecipeId())
|
||||
|| entry.getValue().contains(slot.getRecipeId()))) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
|
||||
if (slot.getType() == BookmarkItemType.INGREDIENT) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark"),
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_item"));
|
||||
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
final BookmarkItem item = slot.getBookmarkItem();
|
||||
@@ -517,14 +565,14 @@ public abstract class ShortcutInputHandler {
|
||||
m -> m.type == BookmarkItemType.RESULT && !m.equals(item)
|
||||
&& item.equalsRecipe(m))) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark"),
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark"),
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_item"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
}
|
||||
|
||||
@@ -534,60 +582,66 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items"),
|
||||
KeyManager.getKeyName("bookmark.pull_items"),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark_pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
|
||||
}
|
||||
|
||||
} else {
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.bookmark"), NEIClientUtils.translate("bookmark.add_item"));
|
||||
hotkeys.put(KeyManager.getKeyName("bookmark.add"), NEIClientUtils.translate("bookmark.add_item"));
|
||||
|
||||
if (StackInfo.getAmount(stack) > 0) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.CTRL_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.add_item_with_count"));
|
||||
}
|
||||
|
||||
if (recipeId != null) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig
|
||||
.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.add_item_with_recipe_and_count"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.bookmark", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.add_item_with_recipe"));
|
||||
}
|
||||
|
||||
if ((ItemPanels.itemPanel.contains(mousex, mousey)
|
||||
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey)) && FavoriteRecipes.contains(stack)) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.favorite"),
|
||||
KeyManager.getKeyName("bookmark.favorite", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("recipe.favorite.bookmark_recipe"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.usage"), NEIClientUtils.translate("itempanel.open_usage"));
|
||||
hotkeys.put(KeyManager.getKeyName("recipe.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(KeyManager.getKeyName("recipe.usage"), NEIClientUtils.translate("itempanel.open_usage"));
|
||||
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.copy_name"), NEIClientUtils.translate("itempanel.copy_name"));
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.copy_oredict"), NEIClientUtils.translate("itempanel.copy_oredict"));
|
||||
hotkeys.put(NEIClientConfig.getKeyName("gui.copy_id"), NEIClientUtils.translate("itempanel.copy_id"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.chat_link_item"),
|
||||
NEIClientUtils.translate("itempanel.chat_link_item"));
|
||||
KeyManager.getKeyName("copy.name", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.copy_name"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("copy.oredict", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.copy_oredict"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("copy.identifier", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.copy_id"));
|
||||
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.item.chat_link"));
|
||||
|
||||
if (!(gui instanceof GuiRecipe) && NEIClientConfig.canCheatItem(stack)) {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("itempanel.open_usage"));
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
KeyManager.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("itempanel.infinite_item"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
@@ -605,18 +659,18 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
if (handlerRef.canUseOverlayRenderer(gui)) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.overlay"),
|
||||
KeyManager.getKeyName("gui.overlay"),
|
||||
NEIClientUtils.translate("itempanel.overlay_recipe"));
|
||||
}
|
||||
|
||||
if (handlerRef.canFillCraftingGrid(gui)) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("itempanel.fill_crafting_grid"));
|
||||
|
||||
if (handlerRef.allowedTransferAlghoritm(gui)) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName(
|
||||
KeyManager.getKeyName(
|
||||
"gui.overlay",
|
||||
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.fill_crafting_grid_quantity"));
|
||||
@@ -625,16 +679,19 @@ public abstract class ShortcutInputHandler {
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled()) {
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName(
|
||||
KeyManager.getKeyName(
|
||||
"gui.craft_items",
|
||||
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.craft_missing"));
|
||||
hotkeys.put(
|
||||
NEIClientConfig.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
|
||||
KeyManager.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("itempanel.craft_all"));
|
||||
}
|
||||
}
|
||||
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.recipe.chat_link"));
|
||||
}
|
||||
|
||||
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
|
||||
@@ -670,36 +727,4 @@ public abstract class ShortcutInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static RecipeId getHotkeyRecipeId(GuiContainer gui, int mousex, int mousey, ItemStack stack,
|
||||
BookmarksGridSlot slot) {
|
||||
|
||||
if (slot != null) {
|
||||
return slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.getRecipeId();
|
||||
}
|
||||
|
||||
ItemsGridSlot itemSlot = ItemPanels.itemPanel.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.historyPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot == null) {
|
||||
itemSlot = ItemPanels.itemPanel.craftablesPanel.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
if (itemSlot != null) {
|
||||
return itemSlot.getRecipeId();
|
||||
}
|
||||
|
||||
if (gui instanceof GuiRecipe guiRecipe) {
|
||||
final Recipe focusedRecipe = guiRecipe.getFocusedRecipe();
|
||||
|
||||
if (focusedRecipe != null) {
|
||||
return focusedRecipe.getRecipeId();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package codechicken.nei.asm;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
|
||||
public final class KeyBindingFixHooks {
|
||||
|
||||
private static volatile boolean initialized;
|
||||
private static Field keybindArrayField;
|
||||
private static Field pressedField;
|
||||
private static Field pressTimeField;
|
||||
|
||||
private KeyBindingFixHooks() {}
|
||||
|
||||
public static void onTickAll(int keyCode) {
|
||||
|
||||
if (keyCode == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (KeyBinding binding : getBindings()) {
|
||||
if (binding.getKeyCode() == keyCode) {
|
||||
try {
|
||||
pressTimeField.setInt(binding, pressTimeField.getInt(binding) + 1);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void setKeyBindStateAll(int keyCode, boolean pressed) {
|
||||
|
||||
if (keyCode == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (KeyBinding binding : getBindings()) {
|
||||
if (binding.getKeyCode() == keyCode) {
|
||||
try {
|
||||
pressedField.setBoolean(binding, pressed);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<KeyBinding> getBindings() {
|
||||
init();
|
||||
|
||||
try {
|
||||
return (List<KeyBinding>) keybindArrayField.get(null);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static synchronized void init() {
|
||||
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
keybindArrayField = ReflectionHelper.findField(KeyBinding.class, "keybindArray", "field_74516_a");
|
||||
pressedField = ReflectionHelper.findField(KeyBinding.class, "pressed", "field_74513_e");
|
||||
pressTimeField = ReflectionHelper.findField(KeyBinding.class, "pressTime", "field_151474_i");
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -56,7 +60,8 @@ public class NEITransformer implements IClassTransformer {
|
||||
|
||||
public NEITransformer() {
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
// Generates method to set the placed position of a mob spawner for the item callback. More portable than
|
||||
// Generates method to set the placed position of a mob spawner for the item
|
||||
// callback. More portable than
|
||||
// copying vanilla placement code
|
||||
transformer.add(
|
||||
new MethodWriter(
|
||||
@@ -82,7 +87,6 @@ public class NEITransformer implements IClassTransformer {
|
||||
asmblocks.get("commaFix"),
|
||||
true));
|
||||
|
||||
// fix workbench container losing items on shift click output without room for the full stack
|
||||
transformer.add(
|
||||
new MethodTransformer(
|
||||
new ObfMapping(
|
||||
@@ -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(
|
||||
@@ -105,6 +151,33 @@ public class NEITransformer implements IClassTransformer {
|
||||
asmblocks.get("d_glintAlphaFix"),
|
||||
asmblocks.get("glintAlphaFix")));
|
||||
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
transformer.add(
|
||||
new MethodWriter(
|
||||
ACC_PUBLIC | ACC_STATIC,
|
||||
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74507_a", "(I)V"),
|
||||
asmblocks.get("m_keyBindingOnTickAll")));
|
||||
|
||||
transformer.add(
|
||||
new MethodWriter(
|
||||
ACC_PUBLIC | ACC_STATIC,
|
||||
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74510_a", "(IZ)V"),
|
||||
asmblocks.get("m_keyBindingSetStateAll")));
|
||||
|
||||
transformer.add(
|
||||
new MethodInjector(
|
||||
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
|
||||
asmblocks.get("saveOptionsHook"),
|
||||
true));
|
||||
|
||||
transformer.add(
|
||||
new MethodInjector(
|
||||
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
|
||||
asmblocks.get("n_saveOptionsSendSettings"),
|
||||
asmblocks.get("saveOptionsRestoreHook"),
|
||||
false));
|
||||
}
|
||||
|
||||
String GuiContainer = "net/minecraft/client/gui/inventory/GuiContainer";
|
||||
// add manager field
|
||||
transformer.add(
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package codechicken.nei.asm;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
|
||||
public final class SaveOptionsFixHooks {
|
||||
|
||||
private static final String KEY_PREFIX = "key_nei.options.keys.";
|
||||
|
||||
private static List<String> pendingRestoreLines;
|
||||
|
||||
private SaveOptionsFixHooks() {}
|
||||
|
||||
public static void onBeforeSaveOptions() {
|
||||
pendingRestoreLines = null;
|
||||
|
||||
if (NEIClientConfig.isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
|
||||
if (!optionsFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> missing = new ArrayList<>();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(optionsFile))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!line.startsWith(KEY_PREFIX)) continue;
|
||||
final int colon = line.indexOf(':');
|
||||
if (colon <= 0) continue;
|
||||
|
||||
final String description = line.substring(4, colon);
|
||||
if (!KeyManager.isRegistered(description)) {
|
||||
missing.add(line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to read key bindings before options save", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
pendingRestoreLines = missing;
|
||||
}
|
||||
}
|
||||
|
||||
public static void onAfterSaveOptions() {
|
||||
final List<String> restore = pendingRestoreLines;
|
||||
pendingRestoreLines = null;
|
||||
|
||||
if (restore == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
|
||||
|
||||
try (FileWriter writer = new FileWriter(optionsFile, true)) {
|
||||
for (String line : restore) {
|
||||
writer.write(line);
|
||||
writer.write(System.lineSeparator());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to restore NEI key bindings after options save", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemStackSet;
|
||||
import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.ItemsGrid;
|
||||
import codechicken.nei.ItemsGrid.MouseContext;
|
||||
import codechicken.nei.LayoutManager;
|
||||
@@ -179,11 +179,15 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
|
||||
public int addGroup(BookmarkGroup group) {
|
||||
int groupId = this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1;
|
||||
final int groupId = getNextGroupId();
|
||||
this.groups.put(groupId, group);
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public int getNextGroupId() {
|
||||
return this.bookmarkItems.stream().mapToInt(item -> item.groupId).max().orElse(DEFAULT_GROUP_ID) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumPages() {
|
||||
|
||||
@@ -448,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;
|
||||
|
||||
@@ -600,6 +605,11 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
|
||||
public RecipeChainMath createRecipeChainMath(int groupId, RecipeId recipeId) {
|
||||
final BookmarkGroup group = getGroup(groupId);
|
||||
|
||||
if (group == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<BookmarkItem> chainItems = new ArrayList<>();
|
||||
final Set<RecipeId> collapsedRecipes = group.crafting != null && recipeId == null ? group.collapsedRecipes
|
||||
: Collections.emptySet();
|
||||
@@ -786,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) {
|
||||
@@ -879,6 +889,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
return this.bookmarkItems.get(itemIndex);
|
||||
}
|
||||
|
||||
public void replaceBookmarkItem(int itemIndex, BookmarkItem newItem) {
|
||||
|
||||
if (itemIndex < 0 || itemIndex >= this.bookmarkItems.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bookmarkItems.set(itemIndex, newItem);
|
||||
onItemsChanged();
|
||||
}
|
||||
|
||||
public int getAbsoluteSlotIndex(int itemIndex) {
|
||||
return this.gridGenerator.itemToSlot.getOrDefault(itemIndex, -1);
|
||||
}
|
||||
@@ -898,8 +918,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
|
||||
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
|
||||
final ItemStackSet seenResults = new ItemStackSet();
|
||||
final ItemStackSet seenIngredients = new ItemStackSet();
|
||||
final ItemStackAmount seenResults = new ItemStackAmount();
|
||||
final ItemStackAmount seenIngredients = new ItemStackAmount();
|
||||
final List<ItemStack> results = new ArrayList<>();
|
||||
final List<ItemStack> ingredients = new ArrayList<>();
|
||||
|
||||
@@ -935,7 +955,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
|
||||
public void removeRecipe(int itemIndex, boolean removeFullRecipe) {
|
||||
final BookmarkItem item = getCalculatedItem(itemIndex);
|
||||
final BookmarkItem item = getBookmarkItem(itemIndex);
|
||||
|
||||
if (item == null) {
|
||||
return;
|
||||
@@ -1199,28 +1219,10 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
if (group.collapsed) {
|
||||
shiftGroupAmount(targetItem.groupId, shift);
|
||||
} else if (targetItem.recipeId != null && targetItem.type != BookmarkItemType.ITEM) {
|
||||
final RecipeId recipeId = this.gridGenerator.itemToRecipe
|
||||
.getOrDefault(targetItemIndex, targetItem.recipeId);
|
||||
final boolean recipeInMiddle = group.crafting != null && group.crafting.recipeInMiddle.contains(recipeId);
|
||||
long multiplier = Integer.MAX_VALUE;
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
|
||||
multiplier = Math.min(
|
||||
shiftMultiplier(
|
||||
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
|
||||
shift,
|
||||
recipeInMiddle ? 1 : 0),
|
||||
multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.equalsRecipe(recipeId, targetItem.groupId) && !item.emptyFactor()) {
|
||||
item.multiplier = multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
shiftRecipeAmount(
|
||||
targetItem.groupId,
|
||||
this.gridGenerator.itemToRecipe.getOrDefault(targetItemIndex, targetItem.recipeId),
|
||||
shift);
|
||||
} else {
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
@@ -1230,6 +1232,32 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
}
|
||||
|
||||
this.onItemsChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void shiftRecipeAmount(int targetGroupId, RecipeId recipeId, long shift) {
|
||||
final BookmarkGroup group = getGroup(targetGroupId);
|
||||
final boolean recipeInMiddle = group != null && group.crafting != null
|
||||
&& group.crafting.recipeInMiddle.contains(recipeId);
|
||||
long multiplier = Integer.MAX_VALUE;
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.equalsRecipe(recipeId, targetGroupId) && !item.emptyFactor()) {
|
||||
multiplier = Math.min(
|
||||
shiftMultiplier(
|
||||
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
|
||||
shift,
|
||||
recipeInMiddle ? 1 : 0),
|
||||
multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.equalsRecipe(recipeId, targetGroupId) && !item.emptyFactor()) {
|
||||
item.multiplier = multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
this.onItemsChanged();
|
||||
@@ -1257,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);
|
||||
@@ -1286,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);
|
||||
}
|
||||
@@ -1303,7 +1331,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
|
||||
for (BookmarksGridSlot slot : getMask()) {
|
||||
|
||||
if ((sortableGroup == null || slot.getGroupId() != sortableGroup.groupId)
|
||||
if ((sortableGroup == null || !sortableGroup.matches(this, slot.getGroupId()))
|
||||
&& (sortableItem == null || !sortableItem.items.contains(slot.itemIndex))) {
|
||||
final BookmarkItem item = slot.getBookmarkItem();
|
||||
|
||||
|
||||
@@ -340,7 +340,8 @@ public class BookmarkGridGenerator {
|
||||
0,
|
||||
chainItem.getItem(),
|
||||
null,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
} else {
|
||||
return new BookmarksGridSlot(
|
||||
slotIndex,
|
||||
@@ -350,7 +351,8 @@ public class BookmarkGridGenerator {
|
||||
chainItem.getCalculatedMultiplier(),
|
||||
chainItem.getItem(),
|
||||
chainItem.calculatedType,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,16 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.Recipe.RecipeIngredient;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class BookmarkItem {
|
||||
|
||||
@@ -38,7 +42,7 @@ public class BookmarkItem {
|
||||
|
||||
this.groupId = groupId;
|
||||
this.stack = stack;
|
||||
this.factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
|
||||
this.factor = StackInfo.isFluidDisplayItem(stack) ? Math.max(nbTag.getInteger("Count"), 1) : 1;
|
||||
}
|
||||
|
||||
public Builder factor(long factor) {
|
||||
@@ -140,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 {
|
||||
|
||||
@@ -224,11 +228,15 @@ public class BookmarkItem {
|
||||
final List<RecipeIngredient> items = type == BookmarkItemType.RESULT ? recipe.getResults()
|
||||
: recipe.getIngredients();
|
||||
final Builder builder = builder(groupId, stack).type(type).recipe(recipe);
|
||||
final ItemStack emptyStack = StackInfo.withAmount(stack, 0);
|
||||
long amount = 0;
|
||||
|
||||
for (RecipeIngredient res : items) {
|
||||
if (res.contains(stack)) {
|
||||
amount += 1L * res.getAmount() * res.getChance();
|
||||
for (ItemStack perm : res.getPermutations()) {
|
||||
if (NEIClientUtils.areStacksSameTypeWithNBT(emptyStack, StackInfo.withAmount(perm, 0))) {
|
||||
amount += 1L * StackInfo.getAmount(perm) * res.getChance();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +253,45 @@ public class BookmarkItem {
|
||||
return builder(groupId, stack).build();
|
||||
}
|
||||
|
||||
public static BookmarkItem of(JsonObject jsonObject) {
|
||||
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
|
||||
final ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
|
||||
|
||||
if (itemStack != null) {
|
||||
final int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt()
|
||||
: BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
|
||||
|
||||
if (jsonObject.has("type")) {
|
||||
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
|
||||
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
|
||||
builder.type(BookmarkItemType.INGREDIENT);
|
||||
} else {
|
||||
builder.type(BookmarkItemType.RESULT);
|
||||
}
|
||||
|
||||
if (jsonObject.has("multiplier")) {
|
||||
builder.multiplier(jsonObject.get("multiplier").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.has("factor")) {
|
||||
builder.factor(jsonObject.get("factor").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.has("chance")) {
|
||||
builder.chance(jsonObject.get("chance").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
|
||||
builder.recipeId(RecipeId.of(recipeJson));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public BookmarkItem copyWithMultiplier(long multiplier) {
|
||||
return new BookmarkItem(
|
||||
this.groupId,
|
||||
@@ -265,6 +312,47 @@ public class BookmarkItem {
|
||||
return copyWithMultiplier(this.multiplier);
|
||||
}
|
||||
|
||||
public BookmarkItem copyWithPerm(ItemStack stack) {
|
||||
|
||||
if (this.recipeId != null
|
||||
&& !NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(stack, 0), this.getItemStack(0))) {
|
||||
final ItemStack oldPerm = findPermutation(this.itemStack);
|
||||
final ItemStack newPerm = findPermutation(stack);
|
||||
|
||||
if (oldPerm != null && newPerm != null) {
|
||||
final long oldAmount = StackInfo.getAmount(oldPerm);
|
||||
final long amount = oldAmount > 0 ? this.factor * this.chance * StackInfo.getAmount(newPerm) / oldAmount
|
||||
: 0;
|
||||
final long factor = (amount % PositionedStack.CHANCE_FULL) == 0 ? amount / PositionedStack.CHANCE_FULL
|
||||
: 1;
|
||||
final long chance = factor == 1 ? amount : PositionedStack.CHANCE_FULL;
|
||||
|
||||
return new BookmarkItem(
|
||||
this.groupId,
|
||||
this.multiplier,
|
||||
factor,
|
||||
chance,
|
||||
newPerm,
|
||||
this.permutations,
|
||||
this.recipeId,
|
||||
this.type);
|
||||
}
|
||||
}
|
||||
|
||||
return copy();
|
||||
}
|
||||
|
||||
private ItemStack findPermutation(ItemStack stack) {
|
||||
|
||||
for (ItemStack itemStack : this.permutations.values()) {
|
||||
if (StackInfo.equalItemAndNBT(stack, itemStack, true)) {
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean containsItems(BookmarkItem item) {
|
||||
return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey);
|
||||
}
|
||||
@@ -273,21 +361,47 @@ public class BookmarkItem {
|
||||
return getAmount(this.multiplier);
|
||||
}
|
||||
|
||||
public long getAmount(long multiplier, ItemStack perm) {
|
||||
|
||||
if (this.recipeId != null && perm != null
|
||||
&& !NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(perm, 0), this.getItemStack(0))) {
|
||||
final ItemStack oldPerm = findPermutation(this.itemStack);
|
||||
final ItemStack newPerm = findPermutation(perm);
|
||||
|
||||
if (oldPerm != null && newPerm != null) {
|
||||
final long oldAmount = StackInfo.getAmount(oldPerm);
|
||||
final long amount = oldAmount > 0 ? this.factor * this.chance * StackInfo.getAmount(newPerm) / oldAmount
|
||||
: 0;
|
||||
final long factor = (amount % PositionedStack.CHANCE_FULL) == 0 ? amount / PositionedStack.CHANCE_FULL
|
||||
: 1;
|
||||
final long chance = factor == 1 ? amount : PositionedStack.CHANCE_FULL;
|
||||
|
||||
return getAmount(multiplier, factor, chance);
|
||||
}
|
||||
}
|
||||
|
||||
return getAmount(multiplier);
|
||||
}
|
||||
|
||||
public long getAmount(long multiplier) {
|
||||
long amount = this.factor * multiplier;
|
||||
return getAmount(multiplier, this.factor, this.chance);
|
||||
}
|
||||
|
||||
protected long getAmount(long multiplier, long factor, long chance) {
|
||||
long amount = factor * multiplier;
|
||||
|
||||
if (this.fluidCellAmount > 1) {
|
||||
amount = amount * this.fluidCellAmount;
|
||||
}
|
||||
|
||||
if (this.chance != PositionedStack.CHANCE_FULL) {
|
||||
if (chance != PositionedStack.CHANCE_FULL) {
|
||||
|
||||
if (this.type == BookmarkItemType.INGREDIENT) {
|
||||
// For ingredients, round up the expected amount to be conservative.
|
||||
amount = (amount * this.chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL;
|
||||
return (amount * chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL;
|
||||
} else {
|
||||
// For results, round down the expected amount to be conservative.
|
||||
amount = (amount * this.chance) / PositionedStack.CHANCE_FULL;
|
||||
return (amount * chance) / PositionedStack.CHANCE_FULL;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -364,4 +478,27 @@ public class BookmarkItem {
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsonObject toJsonObject() {
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
|
||||
jsonObject.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(getItemStack())));
|
||||
jsonObject.add("multiplier", new JsonPrimitive(this.multiplier));
|
||||
jsonObject.add("factor", new JsonPrimitive(this.factor));
|
||||
jsonObject.add("type", new JsonPrimitive(this.type.toInt()));
|
||||
|
||||
if (this.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
jsonObject.add("groupId", new JsonPrimitive(this.groupId));
|
||||
}
|
||||
|
||||
if (this.recipeId != null) {
|
||||
jsonObject.add("recipeId", this.recipeId.toJsonObject());
|
||||
}
|
||||
|
||||
if (this.chance != PositionedStack.CHANCE_FULL) {
|
||||
jsonObject.add("chance", new JsonPrimitive(this.chance));
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package codechicken.nei.bookmark;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.ItemQuantityField;
|
||||
import codechicken.nei.ItemStackSet;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.Recipe.RecipeIngredient;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class BookmarkPayload {
|
||||
|
||||
private String linkType;
|
||||
private NBTTagCompound target;
|
||||
|
||||
private NBTTagCompound group;
|
||||
|
||||
private NBTTagList items = new NBTTagList();
|
||||
private NBTTagList recipes = new NBTTagList();
|
||||
|
||||
private BookmarkPayload(String linkType, ItemStack stack) {
|
||||
this.linkType = linkType;
|
||||
this.target = stack != null ? StackInfo.itemStackToNBT(stack) : null;
|
||||
}
|
||||
|
||||
public static BookmarkPayload of(NBTTagCompound nbt) {
|
||||
final BookmarkPayload payload = new BookmarkPayload(nbt.getString("linkType"), null);
|
||||
|
||||
if (nbt.hasKey("item")) {
|
||||
payload.target = nbt.getCompoundTag("item");
|
||||
}
|
||||
|
||||
if (nbt.hasKey("group")) {
|
||||
payload.group = nbt.getCompoundTag("group");
|
||||
}
|
||||
|
||||
payload.items = nbt.getTagList("items", 10);
|
||||
payload.recipes = nbt.getTagList("recipes", 10);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static BookmarkPayload of(BookmarkItem item) {
|
||||
final BookmarkPayload payload = new BookmarkPayload("recipe", item.getItemStack());
|
||||
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
|
||||
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
|
||||
final RecipeId recipeId = item.recipeId;
|
||||
final int groupId = item.groupId;
|
||||
|
||||
payload.items = new NBTTagList();
|
||||
payload.recipes = new NBTTagList();
|
||||
|
||||
if (recipeId != null) {
|
||||
|
||||
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
|
||||
final BookmarkItem currentItem = grid.getBookmarkItem(itemIndex);
|
||||
|
||||
if (currentItem != null && currentItem.groupId == groupId && recipeId.equals(currentItem.recipeId)) {
|
||||
addBookmarkItemToPayload(payload, currentItem, recipesIndexes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
addBookmarkItemToPayload(payload, item, recipesIndexes);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static BookmarkPayload of(Recipe recipe) {
|
||||
final BookmarkPayload payload = new BookmarkPayload("recipe", recipe.getResult());
|
||||
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
|
||||
final ItemStackSet seenIngredients = new ItemStackSet();
|
||||
final ItemStackSet seenResults = new ItemStackSet();
|
||||
|
||||
for (RecipeIngredient result : recipe.getResults()) {
|
||||
ItemStack stack = result.getItemStack();
|
||||
if (!seenResults.contains(stack)) {
|
||||
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.RESULT).build();
|
||||
addBookmarkItemToPayload(payload, item, recipesIndexes);
|
||||
seenResults.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (RecipeIngredient ingr : recipe.getIngredients()) {
|
||||
ItemStack stack = ingr.getItemStack();
|
||||
if (!seenIngredients.contains(stack)) {
|
||||
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.INGREDIENT).build();
|
||||
addBookmarkItemToPayload(payload, item, recipesIndexes);
|
||||
seenIngredients.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static BookmarkPayload of(int groupId) {
|
||||
final BookmarkPayload payload = new BookmarkPayload("group", null);
|
||||
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
|
||||
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
|
||||
|
||||
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
|
||||
final BookmarkItem item = grid.getBookmarkItem(itemIndex);
|
||||
|
||||
if (item != null && item.groupId == groupId) {
|
||||
addBookmarkItemToPayload(payload, item, recipesIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
payload.group = generateGroup(grid.getGroup(groupId));
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static BookmarkPayload of(ItemStack stackover, RecipeId recipeId) {
|
||||
final BookmarkPayload payload = new BookmarkPayload("item", stackover);
|
||||
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
|
||||
final Point mousePos = GuiDraw.getMousePosition();
|
||||
|
||||
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
|
||||
}
|
||||
|
||||
final BookmarkItem item = BookmarkItem.builder(-1, stackover).recipeId(recipeId).type(BookmarkItemType.ITEM)
|
||||
.build();
|
||||
|
||||
addBookmarkItemToPayload(payload, item, recipesIndexes);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static NBTTagCompound generateGroup(BookmarkGroup group) {
|
||||
final NBTTagCompound groupTag = new NBTTagCompound();
|
||||
final NBTTagList collapsedRecipes = new NBTTagList();
|
||||
|
||||
groupTag.setString("viewmode", group.viewMode.toString());
|
||||
groupTag.setBoolean("crafting", group.crafting != null);
|
||||
groupTag.setBoolean("collapsed", group.collapsed);
|
||||
|
||||
for (RecipeId recipeId : group.collapsedRecipes) {
|
||||
collapsedRecipes.appendTag(NBTJson.toNbt(recipeId.toJsonObject()));
|
||||
}
|
||||
|
||||
groupTag.setTag("collapsedRecipes", collapsedRecipes);
|
||||
|
||||
return groupTag;
|
||||
}
|
||||
|
||||
private static void addBookmarkItemToPayload(BookmarkPayload payload, BookmarkItem item,
|
||||
Map<RecipeId, Integer> recipesIndexes) {
|
||||
final JsonObject itemTag = item.toJsonObject();
|
||||
itemTag.remove("groupId");
|
||||
|
||||
if (item.recipeId != null) {
|
||||
final int recipeIndex = recipesIndexes
|
||||
.computeIfAbsent(item.recipeId, recipeId -> addRecipeToPayload(payload, recipeId, recipesIndexes));
|
||||
itemTag.addProperty("recipeId", recipeIndex);
|
||||
}
|
||||
|
||||
payload.items.appendTag(NBTJson.toNbt(itemTag));
|
||||
}
|
||||
|
||||
private static int addRecipeToPayload(BookmarkPayload payload, RecipeId recipeId,
|
||||
Map<RecipeId, Integer> recipesIndexes) {
|
||||
final NBTTagCompound recipeTag = new NBTTagCompound();
|
||||
final int recipeIndex = payload.recipes.tagCount();
|
||||
|
||||
recipeTag.setTag("recipeId", NBTJson.toNbt(recipeId.toJsonObject()));
|
||||
recipeTag.setInteger("recipeIndex", recipeIndex);
|
||||
payload.recipes.appendTag(recipeTag);
|
||||
|
||||
return recipeIndex;
|
||||
}
|
||||
|
||||
public NBTTagCompound toNBT() {
|
||||
final NBTTagCompound payloadTag = new NBTTagCompound();
|
||||
payloadTag.setString("linkType", linkType);
|
||||
|
||||
if (target != null) {
|
||||
payloadTag.setTag("item", target);
|
||||
}
|
||||
|
||||
if (group != null) {
|
||||
payloadTag.setTag("group", group);
|
||||
}
|
||||
|
||||
payloadTag.setTag("items", items);
|
||||
payloadTag.setTag("recipes", recipes);
|
||||
|
||||
return payloadTag;
|
||||
}
|
||||
|
||||
public String getLinkType() {
|
||||
return this.linkType;
|
||||
}
|
||||
|
||||
public ItemStack getTargetStack() {
|
||||
return this.target != null ? StackInfo.loadFromNBT(this.target) : null;
|
||||
}
|
||||
|
||||
public BookmarkGroup getGroup() {
|
||||
if (this.group == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean crafting = this.group.hasKey("crafting") && this.group.getBoolean("crafting");
|
||||
final BookmarkViewMode viewMode;
|
||||
|
||||
if (this.group.hasKey("viewmode")) {
|
||||
viewMode = BookmarkViewMode.valueOf(this.group.getString("viewmode"));
|
||||
} else {
|
||||
viewMode = BookmarkViewMode.TODO_LIST;
|
||||
}
|
||||
|
||||
final BookmarkGroup group = new BookmarkGroup(viewMode, crafting);
|
||||
|
||||
if (this.group.hasKey("collapsed")) {
|
||||
group.collapsed = this.group.getBoolean("collapsed");
|
||||
}
|
||||
|
||||
if (this.group.hasKey("collapsedRecipes")) {
|
||||
final NBTTagList collapsedRecipes = this.group.getTagList("collapsedRecipes", 10);
|
||||
|
||||
for (int i = 0; i < collapsedRecipes.tagCount(); i++) {
|
||||
final NBTTagCompound recipeTag = collapsedRecipes.getCompoundTagAt(i);
|
||||
final RecipeId recipeId = RecipeId.of((JsonObject) NBTJson.toJsonObject(recipeTag));
|
||||
group.collapsedRecipes.add(recipeId);
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public List<BookmarkItem> getBookmarkItems(int groupId) {
|
||||
final List<BookmarkItem> bookmarkItems = new ArrayList<>();
|
||||
final Map<Integer, RecipeId> recipesMap = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < recipes.tagCount(); i++) {
|
||||
final NBTTagCompound recipeTag = recipes.getCompoundTagAt(i);
|
||||
final RecipeId recipeId = RecipeId
|
||||
.of((JsonObject) NBTJson.toJsonObject(recipeTag.getCompoundTag("recipeId")));
|
||||
recipesMap.put(recipeTag.getInteger("recipeIndex"), recipeId);
|
||||
}
|
||||
|
||||
for (int i = 0; i < items.tagCount(); i++) {
|
||||
final NBTTagCompound itemTag = items.getCompoundTagAt(i);
|
||||
RecipeId recipeId = null;
|
||||
|
||||
if (itemTag.hasKey("recipeId")) {
|
||||
recipeId = recipesMap.get(itemTag.getInteger("recipeId"));
|
||||
itemTag.removeTag("recipeId");
|
||||
}
|
||||
|
||||
final BookmarkItem item = BookmarkItem.of((JsonObject) NBTJson.toJsonObject(itemTag));
|
||||
|
||||
if (item != null) {
|
||||
item.groupId = groupId;
|
||||
item.recipeId = recipeId;
|
||||
bookmarkItems.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return bookmarkItems;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -24,11 +23,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.PositionedStack;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class BookmarkStorage {
|
||||
@@ -83,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();
|
||||
@@ -199,7 +207,16 @@ public class BookmarkStorage {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!addItemToGrid(grid, parser.parse(itemStr).getAsJsonObject())) {
|
||||
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;
|
||||
NEIClientConfig.logger.warn(
|
||||
"Failed to load bookmarked ItemStack from json string, the item no longer exists:\n{}",
|
||||
@@ -238,53 +255,6 @@ public class BookmarkStorage {
|
||||
this.bookmarkFile = bookmarkFile;
|
||||
}
|
||||
|
||||
private boolean addItemToGrid(BookmarkGrid grid, JsonObject jsonObject) {
|
||||
NBTTagCompound itemStackNBT;
|
||||
|
||||
if (jsonObject.get("item") != null) {
|
||||
itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
|
||||
} else { // old format
|
||||
itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject);
|
||||
}
|
||||
|
||||
ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
|
||||
|
||||
if (itemStack != null) {
|
||||
int groupId = jsonObject.has("groupId") && grid.groups.containsKey(jsonObject.get("groupId").getAsInt())
|
||||
? jsonObject.get("groupId").getAsInt()
|
||||
: BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
|
||||
|
||||
if (jsonObject.has("type")) {
|
||||
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
|
||||
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
|
||||
builder.type(BookmarkItemType.INGREDIENT);
|
||||
} else {
|
||||
builder.type(BookmarkItemType.RESULT);
|
||||
}
|
||||
|
||||
if (jsonObject.has("multiplier")) {
|
||||
builder.multiplier(jsonObject.get("multiplier").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.has("factor")) {
|
||||
builder.factor(jsonObject.get("factor").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.has("chance")) {
|
||||
builder.chance(jsonObject.get("chance").getAsInt());
|
||||
}
|
||||
|
||||
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
|
||||
builder.recipeId(RecipeId.of(recipeJson));
|
||||
}
|
||||
|
||||
grid.addItem(builder.build(), false);
|
||||
}
|
||||
|
||||
return itemStack != null;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
if (this.bookmarkFile == null) {
|
||||
@@ -325,26 +295,7 @@ public class BookmarkStorage {
|
||||
|
||||
for (BookmarkItem item : grid.bookmarkItems) {
|
||||
try {
|
||||
JsonObject row = new JsonObject();
|
||||
|
||||
row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.getItemStack())));
|
||||
row.add("multiplier", new JsonPrimitive(item.multiplier));
|
||||
row.add("factor", new JsonPrimitive(item.factor));
|
||||
row.add("type", new JsonPrimitive(item.type.toInt()));
|
||||
|
||||
if (item.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
row.add("groupId", new JsonPrimitive(item.groupId));
|
||||
}
|
||||
|
||||
if (item.recipeId != null) {
|
||||
row.add("recipeId", item.recipeId.toJsonObject());
|
||||
}
|
||||
|
||||
if (item.chance != PositionedStack.CHANCE_FULL) {
|
||||
row.add("chance", new JsonPrimitive(item.chance));
|
||||
}
|
||||
|
||||
strings.add(NBTJson.toJson(row));
|
||||
strings.add(NBTJson.toJson(item.toJsonObject()));
|
||||
} catch (JsonSyntaxException e) {
|
||||
NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -82,7 +82,7 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
this.bookmarkItem = bookmarkItem;
|
||||
|
||||
this.isOutputRecipe = group.crafting != null && group.crafting.outputRecipes.contains(bookmarkItem.recipeId);
|
||||
this.isFluidDisplay = StackInfo.itemStackToNBT(bookmarkItem.itemStack).hasKey("gtFluidName");
|
||||
this.isFluidDisplay = StackInfo.isFluidDisplayItem(bookmarkItem.itemStack);
|
||||
|
||||
this.realMultiplier = realMultiplier;
|
||||
this.calculatedMultiplier = calculatedMultiplier;
|
||||
@@ -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
|
||||
@@ -193,7 +192,7 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
|
||||
protected HandlerInfo getHandlerInfo() {
|
||||
if (this.bookmarkItem.recipeId != null && this.handlerInfo == null) {
|
||||
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.bookmarkItem.recipeId.getHandleName(), null);
|
||||
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.bookmarkItem.recipeId.getHandlerName(), null);
|
||||
}
|
||||
return this.handlerInfo;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package codechicken.nei.bookmark;
|
||||
|
||||
import static codechicken.nei.bookmark.BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -139,13 +141,11 @@ public class SortableItem {
|
||||
return;
|
||||
}
|
||||
|
||||
if (overGroupId != BookmarkGrid.DEFAULT_GROUP_ID
|
||||
&& beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
beforeGroupId = BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
if (overGroupId != DEFAULT_GROUP_ID && beforeGroupId != DEFAULT_GROUP_ID) {
|
||||
beforeGroupId = DEFAULT_GROUP_ID;
|
||||
}
|
||||
|
||||
if (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID
|
||||
&& afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
if (overGroupId == DEFAULT_GROUP_ID && afterGroupId != DEFAULT_GROUP_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,12 +160,11 @@ public class SortableItem {
|
||||
return;
|
||||
}
|
||||
|
||||
if (overGroupId != BookmarkGrid.DEFAULT_GROUP_ID && afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
afterGroupId = BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
if (overGroupId != DEFAULT_GROUP_ID && afterGroupId != DEFAULT_GROUP_ID) {
|
||||
afterGroupId = DEFAULT_GROUP_ID;
|
||||
}
|
||||
|
||||
if (overGroupId == BookmarkGrid.DEFAULT_GROUP_ID && beforeGroupId != BookmarkGrid.DEFAULT_GROUP_ID
|
||||
&& afterGroupId != BookmarkGrid.DEFAULT_GROUP_ID) {
|
||||
if (overGroupId == DEFAULT_GROUP_ID && beforeGroupId != DEFAULT_GROUP_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
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;
|
||||
private static final float ZOOM_MAX = 8.0f;
|
||||
private static final double ZOOM_STEP_BASE = 1.4;
|
||||
private static final float SCROLL_STEP_DIVISOR = 5f;
|
||||
private static final int CULL_MARGIN = 32;
|
||||
private static final int CULL_ROW_MARGIN = 16;
|
||||
|
||||
private static final float INITIAL_SCROLL = -8f;
|
||||
|
||||
public int widgetX;
|
||||
public int widgetY;
|
||||
public int widgetW;
|
||||
public int widgetH;
|
||||
|
||||
public float scrollX = INITIAL_SCROLL;
|
||||
public float scrollY = INITIAL_SCROLL;
|
||||
public float zoomLevel = 1.0f;
|
||||
|
||||
private float lastDragX;
|
||||
private float lastDragY;
|
||||
private boolean dragging;
|
||||
|
||||
public void setViewport(int x, int y, int w, int h) {
|
||||
this.widgetX = x;
|
||||
this.widgetY = y;
|
||||
this.widgetW = w;
|
||||
this.widgetH = h;
|
||||
}
|
||||
|
||||
public boolean isInWidget(int x, int y) {
|
||||
return x >= this.widgetX && y >= this.widgetY
|
||||
&& x < this.widgetX + this.widgetW
|
||||
&& y < this.widgetY + this.widgetH;
|
||||
}
|
||||
|
||||
public void startDrag(Point2D mouse) {
|
||||
this.dragging = true;
|
||||
this.lastDragX = (float) mouse.getX();
|
||||
this.lastDragY = (float) mouse.getY();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean isDragging() {
|
||||
return this.dragging;
|
||||
}
|
||||
|
||||
public void scroll(int mouseX, int mouseY, int direction, boolean shiftKey, boolean ctrlKey) {
|
||||
|
||||
if (shiftKey) {
|
||||
this.scrollX -= direction * this.widgetW / (SCROLL_STEP_DIVISOR * this.zoomLevel);
|
||||
} else if (ctrlKey) {
|
||||
final int localX = mouseX - this.widgetX;
|
||||
final int localY = mouseY - this.widgetY;
|
||||
final float oldZoom = this.zoomLevel;
|
||||
|
||||
this.zoomLevel = clamp((float) (this.zoomLevel * Math.pow(ZOOM_STEP_BASE, direction)), ZOOM_MIN, ZOOM_MAX);
|
||||
this.scrollX += localX / oldZoom - localX / this.zoomLevel;
|
||||
this.scrollY += localY / oldZoom - localY / this.zoomLevel;
|
||||
} else {
|
||||
this.scrollY -= direction * this.widgetH / (SCROLL_STEP_DIVISOR * this.zoomLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public void centerOn(float worldX, float worldY) {
|
||||
this.scrollX = worldX - (this.widgetW / this.zoomLevel) / 2f;
|
||||
this.scrollY = worldY - (this.widgetH / this.zoomLevel) / 2f;
|
||||
}
|
||||
|
||||
public void clampToBounds(int treeWidth, int treeHeight) {
|
||||
this.scrollX = clamp(this.scrollX, -this.widgetW / this.zoomLevel + 4, treeWidth - 4);
|
||||
this.scrollY = clamp(this.scrollY, -this.widgetH / this.zoomLevel + 4, treeHeight - 4);
|
||||
}
|
||||
|
||||
public int visibleRowMin() {
|
||||
final float zScrollY = this.scrollY * this.zoomLevel;
|
||||
return (int) ((zScrollY - CULL_MARGIN) / this.zoomLevel) - CULL_ROW_MARGIN;
|
||||
}
|
||||
|
||||
public int visibleRowMax() {
|
||||
final float zScrollY = this.scrollY * this.zoomLevel;
|
||||
return (int) ((zScrollY + CULL_MARGIN) / this.zoomLevel) + (int) (this.widgetH / this.zoomLevel)
|
||||
+ CULL_ROW_MARGIN;
|
||||
}
|
||||
|
||||
public Point2D.Double toWorldPoint(Point2D mouse) {
|
||||
final float zScrollX = this.scrollX * this.zoomLevel;
|
||||
final float zScrollY = this.scrollY * this.zoomLevel;
|
||||
|
||||
return new Point2D.Double(
|
||||
(mouse.getX() - this.widgetX + zScrollX) / this.zoomLevel,
|
||||
(mouse.getY() - this.widgetY + zScrollY) / this.zoomLevel);
|
||||
}
|
||||
|
||||
static float clamp(float value, float min, float max) {
|
||||
return value < min ? min : Math.min(value, max);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ColorUtils;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIClientUtils.Alignment;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.scroll.GuiHelper;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
|
||||
class CraftingTreeCanvas {
|
||||
|
||||
private static final DrawableResource SCROLLBAR_H = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
0,
|
||||
88,
|
||||
2,
|
||||
24).build();
|
||||
|
||||
private static final DrawableResource SCROLLBAR_W = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
0,
|
||||
84,
|
||||
24,
|
||||
2).build();
|
||||
|
||||
private static final DrawableResource ICON_COLLAPSED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
4,
|
||||
88,
|
||||
5,
|
||||
5).build();
|
||||
|
||||
private static final DrawableResource ICON_COLLAPSED_HOVER = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
11,
|
||||
88,
|
||||
5,
|
||||
5).build();
|
||||
|
||||
private static final DrawableResource ICON_EXPANDED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
4,
|
||||
95,
|
||||
5,
|
||||
5).build();
|
||||
|
||||
private static final DrawableResource ICON_EXPANDED_HOVER = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
11,
|
||||
95,
|
||||
5,
|
||||
5).build();
|
||||
|
||||
private static final DrawableResource ICON_REMOVE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
85,
|
||||
48,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_CRAFTING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
67,
|
||||
48,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource SLOT_DEFAULT = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
68,
|
||||
2,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_MISSING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
92,
|
||||
2,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_CRAFTING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
116,
|
||||
2,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_DISABLED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
140,
|
||||
2,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_HIGHLIGHTED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
164,
|
||||
2,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final Color LINE_COLOR = new Color(ColorUtils.craftingTreeLine.getColor(), true);
|
||||
private static final int HOVER_COLOR = 0x66888888;
|
||||
private static final float BADGE_SCALE = 0.6f;
|
||||
|
||||
public static final int SLOT_SIZE = 18;
|
||||
public static final int X_SPACING = 24;
|
||||
public static final int Y_SPACING = 14;
|
||||
public static final int ROOT_SPACING = 36;
|
||||
private static final float FIT_MARGIN = 8f;
|
||||
|
||||
public final CraftingTreeGraph graph = new CraftingTreeGraph();
|
||||
|
||||
public final Map<ItemTreeSlot, Point> pointByNode = new HashMap<>();
|
||||
public final TreeMap<Integer, List<ItemTreeSlot>> rows = new TreeMap<>();
|
||||
public final Set<RecipeId> collapsedRecipes = Collections.newSetFromMap(new HashMap<>());
|
||||
public int treeWidth = 0;
|
||||
public int treeHeight = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public boolean isInWidget(int x, int y) {
|
||||
return this.camera.isInWidget(x, y);
|
||||
}
|
||||
|
||||
public void fitToView() {
|
||||
final float firstResultCenterX = this.graph.roots.isEmpty() ? this.treeWidth / 2f
|
||||
: this.pointByNode.get(this.graph.roots.get(0)).x + SLOT_SIZE / 2f;
|
||||
|
||||
this.camera.zoomLevel = 1.0f;
|
||||
this.camera.scrollX = this.treeWidth <= (this.camera.widgetW - FIT_MARGIN)
|
||||
? (this.treeWidth - this.camera.widgetW) / 2f
|
||||
: firstResultCenterX - this.camera.widgetW / 2f;
|
||||
this.camera.scrollY = this.treeHeight <= (this.camera.widgetH - FIT_MARGIN)
|
||||
? (this.treeHeight - this.camera.widgetH) / 2f
|
||||
: -FIT_MARGIN;
|
||||
}
|
||||
|
||||
public void startDrag(Point2D mouse) {
|
||||
this.camera.startDrag(mouse);
|
||||
}
|
||||
|
||||
public void drag(Point2D mouse) {
|
||||
this.camera.drag(mouse);
|
||||
}
|
||||
|
||||
public void endDrag() {
|
||||
this.camera.endDrag();
|
||||
}
|
||||
|
||||
public boolean isDragging() {
|
||||
return this.camera.isDragging();
|
||||
}
|
||||
|
||||
public void scroll(int mouseX, int mouseY, int direction, boolean shiftKey, boolean ctrlKey) {
|
||||
this.camera.scroll(mouseX, mouseY, direction, shiftKey, ctrlKey);
|
||||
}
|
||||
|
||||
public void scrollTo(ItemTreeSlot node) {
|
||||
if (node == null || !this.pointByNode.containsKey(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Point point = this.pointByNode.get(node);
|
||||
final float nodeCenterX = point.x + SLOT_SIZE / 2f;
|
||||
final float nodeCenterY = point.y + SLOT_SIZE / 2f;
|
||||
|
||||
this.camera.centerOn(nodeCenterX, nodeCenterY);
|
||||
}
|
||||
|
||||
public ItemTreeSlot getNodeMouseOver(Point2D mouse) {
|
||||
return findVisibleNode(mouse, (node, local) -> {
|
||||
final Point point = this.pointByNode.get(node);
|
||||
|
||||
return local.x >= point.x && local.x < point.x + SLOT_SIZE
|
||||
&& local.y >= point.y
|
||||
&& local.y < point.y + SLOT_SIZE;
|
||||
});
|
||||
}
|
||||
|
||||
public ItemTreeSlot getHandlerBadgeMouseOver(Point2D mouse) {
|
||||
return findVisibleNode(mouse, (node, local) -> {
|
||||
if (node.prefItem == null || !this.graph.childrenOf.containsKey(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Point point = this.pointByNode.get(node);
|
||||
final float[] badge = badgeRect(point.x, point.y);
|
||||
|
||||
return local.x >= badge[0] && local.x < badge[0] + badge[2]
|
||||
&& local.y >= badge[1]
|
||||
&& local.y < badge[1] + badge[3];
|
||||
});
|
||||
}
|
||||
|
||||
public ItemTreeSlot getCollapseArrowMouseOver(Point2D mouse) {
|
||||
return findVisibleNode(mouse, (node, local) -> {
|
||||
if (!this.graph.childrenOf.containsKey(node) || node.prefItem == null
|
||||
|| this.collapsedRecipes.contains(node.prefItem.recipeId) && !this.graph.roots.contains(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Point point = this.pointByNode.get(node);
|
||||
final float iconX = point.x + SLOT_SIZE / 2 - ICON_COLLAPSED.width - 1;
|
||||
final float iconY = point.y + SLOT_SIZE + 1;
|
||||
final float iconW = ICON_COLLAPSED.width;
|
||||
final float iconH = ICON_COLLAPSED.height;
|
||||
|
||||
return local.x >= iconX && local.x < iconX + iconW && local.y >= iconY && local.y < iconY + iconH;
|
||||
});
|
||||
}
|
||||
|
||||
private ItemTreeSlot findVisibleNode(Point2D mouse, BiPredicate<ItemTreeSlot, Point2D.Double> hitTest) {
|
||||
final Point2D.Double local = this.camera.toWorldPoint(mouse);
|
||||
|
||||
for (List<ItemTreeSlot> row : this.rows.subMap(camera.visibleRowMin(), camera.visibleRowMax()).values()) {
|
||||
for (ItemTreeSlot node : row) {
|
||||
if (hitTest.test(node, local)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void draw(Point2D mouse, Set<BookmarkItem> collapsedItems, SearchResult searchResult,
|
||||
boolean useInventorySnapshot, ItemTreeSlot hoveredNode, ItemTreeSlot deleteHoverNode) {
|
||||
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;
|
||||
final int cropXMin = (int) ((zScrollX - 32) / this.camera.zoomLevel);
|
||||
final int cropXMax = (int) ((zScrollX + 32) / this.camera.zoomLevel)
|
||||
+ (int) (this.camera.widgetW / this.camera.zoomLevel);
|
||||
final ItemTreeSlot collapseHoverNode = this.camera.isDragging() ? null : getCollapseArrowMouseOver(mouse);
|
||||
|
||||
GuiHelper.useScissor(this.camera.widgetX, this.camera.widgetY, this.camera.widgetW, this.camera.widgetH, () -> {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glTranslatef(this.camera.widgetX - zScrollX, this.camera.widgetY - zScrollY, 0.0f);
|
||||
GL11.glScalef(this.camera.zoomLevel, this.camera.zoomLevel, 1.0f);
|
||||
|
||||
for (List<ItemTreeSlot> row : this.rows.subMap(this.camera.visibleRowMin(), this.camera.visibleRowMax())
|
||||
.values()) {
|
||||
for (ItemTreeSlot node : row) {
|
||||
final Point point = this.pointByNode.get(node);
|
||||
|
||||
drawParentLine(node);
|
||||
|
||||
if (point.x >= cropXMin && point.x <= cropXMax) {
|
||||
drawSlot(node, useInventorySnapshot, searchResult, collapsedItems, node == hoveredNode);
|
||||
drawCollapseIcon(node, collapsedItems, node == collapseHoverNode);
|
||||
|
||||
if (node == hoveredNode) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> {
|
||||
GuiDraw.drawRect(
|
||||
point.x + 1,
|
||||
point.y + 1,
|
||||
SLOT_SIZE - 2,
|
||||
SLOT_SIZE - 2,
|
||||
HOVER_COLOR);
|
||||
});
|
||||
}
|
||||
|
||||
if (node.prefItem != null && this.graph.childrenOf.containsKey(node)) {
|
||||
drawSlotBadge(node, point.x, point.y);
|
||||
|
||||
if (node == deleteHoverNode) {
|
||||
drawDeleteButton(point);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glPopMatrix();
|
||||
});
|
||||
|
||||
drawScrollbars();
|
||||
}
|
||||
|
||||
public void drawFull(Set<BookmarkItem> collapsedItems, SearchResult searchResult, boolean useInventorySnapshot) {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
for (List<ItemTreeSlot> row : this.rows.values()) {
|
||||
for (ItemTreeSlot node : row) {
|
||||
final Point point = this.pointByNode.get(node);
|
||||
|
||||
drawParentLine(node);
|
||||
drawSlot(node, useInventorySnapshot, searchResult, collapsedItems, false);
|
||||
|
||||
if (node.prefItem != null && this.graph.childrenOf.containsKey(node)) {
|
||||
drawSlotBadge(node, point.x, point.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
private void drawSlot(ItemTreeSlot node, boolean useInventorySnapshot, SearchResult searchResult,
|
||||
Set<BookmarkItem> collapsedItems, boolean hovered) {
|
||||
final Point point = this.pointByNode.get(node);
|
||||
|
||||
if (searchResult != null) {
|
||||
|
||||
if (node == searchResult.highlightNode) {
|
||||
SLOT_HIGHLIGHTED.draw(point.x, point.y);
|
||||
} else if (searchResult.searchMatches.contains(node)) {
|
||||
SLOT_DEFAULT.draw(point.x, point.y);
|
||||
} else {
|
||||
SLOT_DISABLED.draw(point.x, point.y);
|
||||
}
|
||||
|
||||
} else if (useInventorySnapshot) {
|
||||
|
||||
if (node.leftAmount == 0) {
|
||||
|
||||
if (this.graph.parentOf.get(node) != null && this.graph.parentOf.get(node).leftAmount == 0
|
||||
|| node.requestedAmount == 0) {
|
||||
SLOT_DISABLED.draw(point.x, point.y);
|
||||
} else {
|
||||
SLOT_DEFAULT.draw(point.x, point.y);
|
||||
}
|
||||
|
||||
} else if (!this.graph.childrenOf.containsKey(node)) {
|
||||
SLOT_MISSING.draw(point.x, point.y);
|
||||
} else if (collapsedItems.contains(node.ingrItem)) {
|
||||
|
||||
if (this.graph.subtreeMissingCache.contains(node)) {
|
||||
SLOT_MISSING.draw(point.x, point.y);
|
||||
} else {
|
||||
SLOT_CRAFTING.draw(point.x, point.y);
|
||||
}
|
||||
|
||||
} else if (!this.graph.childrenNeedCraftingCache.contains(node)) {
|
||||
SLOT_CRAFTING.draw(point.x, point.y);
|
||||
} else {
|
||||
SLOT_DEFAULT.draw(point.x, point.y);
|
||||
}
|
||||
|
||||
} else {
|
||||
SLOT_DEFAULT.draw(point.x, point.y);
|
||||
}
|
||||
|
||||
GuiContainerManager
|
||||
.drawItem(point.x + (SLOT_SIZE - 16) / 2, point.y + (SLOT_SIZE - 16) / 2, node.emptyStack, true);
|
||||
|
||||
if (node.requestedAmount > 0 && node.leftAmount == 0) {
|
||||
drawStackSize(node, node.ingrItem.getStackSize(node.requestedAmount), point.x, point.y, true);
|
||||
} else if (node.requestedAmount > 0 && node.leftAmount > 0) {
|
||||
final long amount = hovered && node.prefItem != null
|
||||
&& this.graph.roots.contains(node)
|
||||
&& this.collapsedRecipes.contains(node.prefItem.recipeId)
|
||||
&& NEIClientUtils.controlKey() ? node.currentRequestedAmount : node.requestedAmount;
|
||||
|
||||
drawStackSize(node, node.ingrItem.getStackSize(amount), point.x, point.y, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void drawStackSize(ItemTreeSlot node, long stackSize, int x, int y, boolean resolved) {
|
||||
String amountString = stackSize < 10_000 ? String.valueOf(stackSize)
|
||||
: ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
|
||||
|
||||
if (node.isFluidDisplay) {
|
||||
amountString += "L";
|
||||
}
|
||||
|
||||
final boolean hasOutputChance = node.prefItem != null && node.prefItem.chance != PositionedStack.CHANCE_FULL;
|
||||
int color = 0xFFFFFF;
|
||||
|
||||
if (node.ingrItem.chance != PositionedStack.CHANCE_FULL || hasOutputChance || node.isContainerItem) {
|
||||
amountString = "~" + amountString;
|
||||
color = 0xFFAA00;
|
||||
}
|
||||
|
||||
if (resolved) {
|
||||
color = 0x55FF55;
|
||||
}
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
amountString,
|
||||
new Rectangle4i(
|
||||
x + (SLOT_SIZE - 16) / 2,
|
||||
y + (SLOT_SIZE - 16) / 2,
|
||||
SLOT_SIZE - (SLOT_SIZE - 16),
|
||||
SLOT_SIZE - (SLOT_SIZE - 16)),
|
||||
1,
|
||||
color,
|
||||
true,
|
||||
node.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
|
||||
}
|
||||
|
||||
protected void drawSlotBadge(ItemTreeSlot node, int x, int y) {
|
||||
final float[] badge = badgeRect(x, y);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glTranslatef(badge[0], badge[1], 4.0f);
|
||||
GL11.glScalef(BADGE_SCALE, BADGE_SCALE, 1.0f);
|
||||
|
||||
if (node.info.hasImageOrItem()) {
|
||||
final DrawableResource image = node.info.getImage();
|
||||
|
||||
if (image != null) {
|
||||
image.draw(0, 0);
|
||||
} else {
|
||||
GuiContainerManager.drawItem(0, 0, node.info.getItemStack(), true);
|
||||
}
|
||||
|
||||
} else {
|
||||
ICON_CRAFTING.draw(0, 0);
|
||||
}
|
||||
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
private void drawCollapseIcon(ItemTreeSlot node, Set<BookmarkItem> collapsedItems, boolean hover) {
|
||||
|
||||
if (!this.graph.childrenOf.containsKey(node) || node.prefItem == null
|
||||
|| this.collapsedRecipes.contains(node.prefItem.recipeId) && !this.graph.roots.contains(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final DrawableResource icon;
|
||||
|
||||
if (collapsedItems.contains(node.ingrItem)) {
|
||||
icon = hover ? ICON_COLLAPSED_HOVER : ICON_COLLAPSED;
|
||||
} else {
|
||||
icon = hover ? ICON_EXPANDED_HOVER : ICON_EXPANDED;
|
||||
}
|
||||
|
||||
final Point point = this.pointByNode.get(node);
|
||||
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
icon.draw(point.x + SLOT_SIZE / 2 - icon.width - 1, point.y + SLOT_SIZE + 1);
|
||||
}
|
||||
|
||||
private void drawDeleteButton(Point point) {
|
||||
final float[] badge = badgeRect(point.x, point.y);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glTranslatef(badge[0], badge[1], 350.0f);
|
||||
GL11.glScalef(BADGE_SCALE, BADGE_SCALE, 1.0f);
|
||||
ICON_REMOVE.draw(0, 0);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
private static float[] badgeRect(float x, float y) {
|
||||
final float w = ICON_REMOVE.width * BADGE_SCALE;
|
||||
final float h = ICON_REMOVE.height * BADGE_SCALE;
|
||||
|
||||
return new float[] { x - w / 2 + 1, y - h / 2 + 1, w, h };
|
||||
}
|
||||
|
||||
private void drawParentLine(ItemTreeSlot node) {
|
||||
|
||||
if (!this.graph.parentOf.containsKey(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Point point = this.pointByNode.get(node);
|
||||
final float x0 = point.x + SLOT_SIZE / 2f;
|
||||
final float y0 = point.y - 1;
|
||||
|
||||
final ItemTreeSlot parentNode = this.graph.parentOf.get(node);
|
||||
final Point parentPoint = this.pointByNode.get(parentNode);
|
||||
final float x1 = parentPoint.x + SLOT_SIZE / 2f;
|
||||
final float y1 = parentPoint.y + SLOT_SIZE;
|
||||
|
||||
if (x0 != x1) {
|
||||
final float midY = (y0 + y1) / 2f;
|
||||
|
||||
NEIClientUtils.drawRect(x0, Math.min(y0, midY), 1, Math.abs(midY - y0) + 1, LINE_COLOR);
|
||||
NEIClientUtils.drawRect(Math.min(x0, x1), midY, Math.abs(x1 - x0) + 1, 1, LINE_COLOR);
|
||||
NEIClientUtils.drawRect(x1, Math.min(midY, y1), 1, Math.abs(y1 - midY) + 1, LINE_COLOR);
|
||||
} else {
|
||||
NEIClientUtils.drawRect(x0, Math.min(y0, y1), 1, Math.abs(y1 - y0) + 1, LINE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawScrollbars() {
|
||||
if (this.treeWidth <= 0 || this.treeHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float scrollXPct = Camera.clamp(this.camera.scrollX / this.treeWidth, 0.0f, 1.0f);
|
||||
final float scrollYPct = Camera.clamp(this.camera.scrollY / this.treeHeight, 0.0f, 1.0f);
|
||||
|
||||
GL11.glTranslatef(0, 0, 500.0f);
|
||||
|
||||
SCROLLBAR_H.draw(
|
||||
this.camera.widgetX + this.camera.widgetW - 3,
|
||||
(int) (this.camera.widgetY + scrollYPct * (this.camera.widgetH - SCROLLBAR_H.height)));
|
||||
|
||||
SCROLLBAR_W.draw(
|
||||
(int) (this.camera.widgetX + scrollXPct * (this.camera.widgetW - SCROLLBAR_W.width)),
|
||||
this.camera.widgetY + this.camera.widgetH - 3);
|
||||
|
||||
GL11.glTranslatef(0, 0, -500.0f);
|
||||
}
|
||||
|
||||
public void recalculateCoordinates(Set<RecipeId> collapsedRecipes, Set<BookmarkItem> collapsedItems,
|
||||
ItemTreeSlot anchor) {
|
||||
final Map<ItemTreeSlot, Integer> offsetFromParent = new HashMap<>();
|
||||
final Point anchorOldPoint = anchor != null ? resolveAnchorPoint(anchor) : null;
|
||||
|
||||
this.collapsedRecipes.clear();
|
||||
this.pointByNode.clear();
|
||||
this.rows.clear();
|
||||
this.treeWidth = 0;
|
||||
this.treeHeight = 0;
|
||||
|
||||
this.collapsedRecipes.addAll(collapsedRecipes);
|
||||
|
||||
for (ItemTreeSlot node : this.graph.items) {
|
||||
this.pointByNode.put(node, new Point(0, 0));
|
||||
}
|
||||
|
||||
final List<int[]> contour = new ArrayList<>();
|
||||
|
||||
for (ItemTreeSlot root : this.graph.roots) {
|
||||
final List<int[]> layout = computeLayout(root, collapsedItems, offsetFromParent);
|
||||
final int shift = contour.isEmpty() ? 0 : requiredRootShift(contour, layout);
|
||||
|
||||
mergeContour(contour, layout, shift);
|
||||
assignPositions(root, shift, 0, collapsedItems, offsetFromParent);
|
||||
}
|
||||
|
||||
int minX = 0;
|
||||
|
||||
for (Point point : this.pointByNode.values()) {
|
||||
minX = Math.min(minX, point.x);
|
||||
}
|
||||
|
||||
if (minX < 0) {
|
||||
for (Point point : this.pointByNode.values()) {
|
||||
point.x -= minX;
|
||||
}
|
||||
}
|
||||
|
||||
for (List<ItemTreeSlot> row : this.rows.values()) {
|
||||
for (ItemTreeSlot node : row) {
|
||||
this.treeWidth = Math.max(this.treeWidth, this.pointByNode.get(node).x + CraftingTreeCanvas.SLOT_SIZE);
|
||||
this.treeHeight = Math
|
||||
.max(this.treeHeight, this.pointByNode.get(node).y + CraftingTreeCanvas.SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (anchorOldPoint != null) {
|
||||
final Point anchorNewPoint = resolveAnchorPoint(anchor);
|
||||
|
||||
if (anchorNewPoint != null) {
|
||||
this.camera.scrollX += anchorNewPoint.x - anchorOldPoint.x;
|
||||
this.camera.scrollY += anchorNewPoint.y - anchorOldPoint.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Point resolveAnchorPoint(ItemTreeSlot anchor) {
|
||||
Point result = this.pointByNode.get(anchor);
|
||||
|
||||
if (result == null) {
|
||||
for (Map.Entry<ItemTreeSlot, Point> entry : this.pointByNode.entrySet()) {
|
||||
if (entry.getKey().ingrItem.equals(anchor.ingrItem)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<int[]> computeLayout(ItemTreeSlot node, Set<BookmarkItem> collapsedItems,
|
||||
Map<ItemTreeSlot, Integer> offsetFromParent) {
|
||||
List<ItemTreeSlot> children = Collections.emptyList();
|
||||
|
||||
if (!collapsedItems.contains(node.ingrItem)) {
|
||||
children = this.graph.childrenOf.getOrDefault(node, Collections.emptyList());
|
||||
}
|
||||
|
||||
if (children.isEmpty()) {
|
||||
final List<int[]> rows = new ArrayList<>();
|
||||
rows.add(new int[] { 0, 0 });
|
||||
return rows;
|
||||
}
|
||||
|
||||
final int[] offsets = new int[children.size()];
|
||||
final List<int[]> combined = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
final List<int[]> childRows = computeLayout(children.get(i), collapsedItems, offsetFromParent);
|
||||
final int shift = i == 0 ? 0 : requiredShift(combined, childRows, X_SPACING);
|
||||
|
||||
offsets[i] = shift;
|
||||
mergeContour(combined, childRows, shift);
|
||||
}
|
||||
|
||||
final int selfX = (offsets[0] + offsets[children.size() - 1]) / 2;
|
||||
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
offsetFromParent.put(children.get(i), offsets[i] - selfX);
|
||||
}
|
||||
|
||||
for (int[] row : combined) {
|
||||
row[0] -= selfX;
|
||||
row[1] -= selfX;
|
||||
}
|
||||
|
||||
final List<int[]> rows = new ArrayList<>(combined.size() + 1);
|
||||
rows.add(new int[] { 0, 0 });
|
||||
rows.addAll(combined);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static int requiredShift(List<int[]> existing, List<int[]> incoming, int spacing) {
|
||||
int shift = 0;
|
||||
final int rows = Math.min(existing.size(), incoming.size());
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
shift = Math.max(shift, existing.get(r)[1] + spacing - incoming.get(r)[0]);
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
private static int requiredRootShift(List<int[]> existing, List<int[]> incoming) {
|
||||
int existingRight = Integer.MIN_VALUE;
|
||||
|
||||
for (int[] row : existing) {
|
||||
existingRight = Math.max(existingRight, row[1]);
|
||||
}
|
||||
|
||||
int incomingLeft = Integer.MAX_VALUE;
|
||||
|
||||
for (int[] row : incoming) {
|
||||
incomingLeft = Math.min(incomingLeft, row[0]);
|
||||
}
|
||||
|
||||
return Math.max(0, existingRight + ROOT_SPACING - incomingLeft);
|
||||
}
|
||||
|
||||
private static void mergeContour(List<int[]> target, List<int[]> source, int shift) {
|
||||
for (int r = 0; r < source.size(); r++) {
|
||||
final int left = source.get(r)[0] + shift;
|
||||
final int right = source.get(r)[1] + shift;
|
||||
|
||||
if (r < target.size()) {
|
||||
target.get(r)[0] = Math.min(target.get(r)[0], left);
|
||||
target.get(r)[1] = Math.max(target.get(r)[1], right);
|
||||
} else {
|
||||
target.add(new int[] { left, right });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assignPositions(ItemTreeSlot node, int x, int y, Set<BookmarkItem> collapsedItems,
|
||||
Map<ItemTreeSlot, Integer> offsetFromParent) {
|
||||
this.rows.computeIfAbsent(y, ignored -> new ArrayList<>()).add(node);
|
||||
this.pointByNode.put(node, new Point(x, y));
|
||||
|
||||
final int childY = y + CraftingTreeCanvas.SLOT_SIZE + Y_SPACING;
|
||||
|
||||
if (!collapsedItems.contains(node.ingrItem)) {
|
||||
for (ItemTreeSlot child : this.graph.childrenOf.getOrDefault(node, Collections.emptyList())) {
|
||||
assignPositions(child, x + offsetFromParent.get(child), childY, collapsedItems, offsetFromParent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
public class CraftingTreeGraph {
|
||||
|
||||
public final List<ItemTreeSlot> roots = new ArrayList<>();
|
||||
public final List<ItemTreeSlot> items = new ArrayList<>();
|
||||
public final Map<ItemTreeSlot, ItemTreeSlot> parentOf = new HashMap<>();
|
||||
public final Map<ItemTreeSlot, List<ItemTreeSlot>> childrenOf = new HashMap<>();
|
||||
|
||||
public final Set<ItemTreeSlot> subtreeMissingCache = new HashSet<>();
|
||||
public final Set<ItemTreeSlot> childrenNeedCraftingCache = new HashSet<>();
|
||||
|
||||
public void rebuild(CraftingTreeMath math) {
|
||||
this.subtreeMissingCache.clear();
|
||||
this.childrenNeedCraftingCache.clear();
|
||||
|
||||
math.refresh();
|
||||
|
||||
this.roots.clear();
|
||||
this.roots.addAll(math.rootSlots);
|
||||
this.items.clear();
|
||||
this.items.addAll(math.items);
|
||||
this.parentOf.clear();
|
||||
this.parentOf.putAll(math.parentOf);
|
||||
this.childrenOf.clear();
|
||||
this.childrenOf.putAll(math.childrenOf);
|
||||
|
||||
for (ItemTreeSlot root : this.roots) {
|
||||
computeSubtreeMissing(root);
|
||||
}
|
||||
|
||||
for (Map.Entry<ItemTreeSlot, List<ItemTreeSlot>> entry : this.childrenOf.entrySet()) {
|
||||
if (entry.getValue().stream().anyMatch(child -> child.leftAmount != 0)) {
|
||||
this.childrenNeedCraftingCache.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<RecipeId> collectUnusedRecipes(ItemTreeSlot node) {
|
||||
final Map<RecipeId, Set<RecipeId>> children = new HashMap<>();
|
||||
final Map<RecipeId, Set<RecipeId>> consumers = new HashMap<>();
|
||||
int rootCount = 0;
|
||||
|
||||
for (ItemTreeSlot item : this.items) {
|
||||
if (item.prefItem != null) {
|
||||
children.computeIfAbsent(item.ingrItem.recipeId, k -> new HashSet<>()).add(item.prefItem.recipeId);
|
||||
consumers.computeIfAbsent(item.prefItem.recipeId, k -> new HashSet<>()).add(item.ingrItem.recipeId);
|
||||
|
||||
if (item.ingrItem.recipeId.equals(node.prefItem.recipeId)) {
|
||||
rootCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Set<RecipeId> unusedRecipes = new HashSet<>();
|
||||
final RecipeId rootRecipeId = node.prefItem.recipeId;
|
||||
|
||||
if (rootCount <= 1) {
|
||||
unusedRecipes.add(rootRecipeId);
|
||||
collectSubtree(rootRecipeId, children, consumers, unusedRecipes);
|
||||
}
|
||||
|
||||
return unusedRecipes;
|
||||
}
|
||||
|
||||
private void collectSubtree(RecipeId recipeId, Map<RecipeId, Set<RecipeId>> children,
|
||||
Map<RecipeId, Set<RecipeId>> consumers, Set<RecipeId> unusedRecipes) {
|
||||
for (RecipeId child : children.getOrDefault(recipeId, Collections.emptySet())) {
|
||||
if (consumers.get(child).size() == 1 && unusedRecipes.add(child)) {
|
||||
collectSubtree(child, children, consumers, unusedRecipes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean computeSubtreeMissing(ItemTreeSlot node) {
|
||||
boolean hasMissing = !this.childrenOf.containsKey(node);
|
||||
|
||||
for (ItemTreeSlot child : this.childrenOf.getOrDefault(node, Collections.emptyList())) {
|
||||
hasMissing |= child.leftAmount != 0 && computeSubtreeMissing(child);
|
||||
}
|
||||
|
||||
if (hasMissing) {
|
||||
this.subtreeMissingCache.add(node);
|
||||
}
|
||||
|
||||
return hasMissing;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.chain.RecipeChainMath;
|
||||
|
||||
public class CraftingTreeMath extends RecipeChainMath {
|
||||
|
||||
public final List<ItemTreeSlot> rootSlots = new ArrayList<>();
|
||||
public final List<ItemTreeSlot> items = new ArrayList<>();
|
||||
public final Map<ItemTreeSlot, ItemTreeSlot> parentOf = new HashMap<>();
|
||||
public final Map<ItemTreeSlot, List<ItemTreeSlot>> childrenOf = new HashMap<>();
|
||||
|
||||
private final Deque<ItemTreeSlot> parentStack = new ArrayDeque<>();
|
||||
private ItemTreeSlot lastItem = null;
|
||||
|
||||
public CraftingTreeMath(List<BookmarkItem> chainItems, Set<RecipeId> collapsedRecipes) {
|
||||
super(chainItems, collapsedRecipes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeChainMath refresh() {
|
||||
this.rootSlots.clear();
|
||||
this.items.clear();
|
||||
this.parentOf.clear();
|
||||
this.childrenOf.clear();
|
||||
this.parentStack.clear();
|
||||
this.lastItem = null;
|
||||
|
||||
return super.refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void calculationStepTrigger(BookmarkItem ingrItem, BookmarkItem prefItem, long requestedAmount,
|
||||
long containerAmount, long inventoryAmount, long ingrAmount, long produced) {
|
||||
|
||||
if (ingrItem.type == BookmarkItemType.RESULT) {
|
||||
final ItemTreeSlot slot = new ItemTreeSlot(
|
||||
ingrItem,
|
||||
Math.max(0, this.requiredAmount.get(prefItem) - requestedAmount),
|
||||
requestedAmount,
|
||||
this.requiredAmount.get(prefItem),
|
||||
0,
|
||||
prefItem);
|
||||
|
||||
this.rootSlots.add(slot);
|
||||
this.items.add(slot);
|
||||
|
||||
this.lastItem = slot;
|
||||
} else {
|
||||
|
||||
if (prefItem != null && hasContainerItem(prefItem.itemStack)) {
|
||||
|
||||
if (requestedAmount > 1) {
|
||||
final long requestedSteps = getToolsContainerItems(
|
||||
prefItem.itemStack.copy(),
|
||||
requestedAmount).leftSteps;
|
||||
requestedAmount = (long) Math
|
||||
.ceil((double) requestedAmount / Math.max(1, requestedAmount - requestedSteps));
|
||||
}
|
||||
|
||||
if (containerAmount > 1) {
|
||||
final long containerSteps = getToolsContainerItems(
|
||||
prefItem.itemStack.copy(),
|
||||
containerAmount).leftSteps;
|
||||
containerAmount = (long) Math
|
||||
.ceil((double) containerAmount / Math.max(1, containerAmount - containerSteps));
|
||||
}
|
||||
}
|
||||
|
||||
final ItemTreeSlot slot = new ItemTreeSlot(
|
||||
ingrItem,
|
||||
0,
|
||||
requestedAmount,
|
||||
produced,
|
||||
containerAmount,
|
||||
prefItem);
|
||||
|
||||
this.items.add(slot);
|
||||
this.parentOf.put(slot, this.parentStack.peek());
|
||||
this.childrenOf.computeIfAbsent(this.parentStack.peek(), k -> new ArrayList<>()).add(slot);
|
||||
|
||||
this.lastItem = slot;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareIngredients(RecipeId recipeId, long multiplier, List<RecipeId> visited) {
|
||||
this.parentStack.push(this.lastItem);
|
||||
super.prepareIngredients(recipeId, multiplier, visited);
|
||||
this.lastItem = this.parentStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrMultiplier, List<RecipeId> visited) {
|
||||
final BookmarkItem prefItem = this.preferredItems.get(ingrItem);
|
||||
super.calculateSuitableRecipe(ingrItem, ingrMultiplier, visited);
|
||||
|
||||
if (prefItem != null && !isForeignBoxBoundary(ingrItem)
|
||||
&& !visited.contains(prefItem.recipeId)
|
||||
&& !this.childrenOf.containsKey(this.lastItem)) {
|
||||
visited.add(prefItem.recipeId);
|
||||
prepareIngredients(prefItem.recipeId, visited);
|
||||
visited.remove(prefItem.recipeId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void prepareIngredients(RecipeId recipeId, List<RecipeId> visited) {
|
||||
this.parentStack.push(this.lastItem);
|
||||
|
||||
for (BookmarkItem item : this.ingredientsByRecipe.getOrDefault(recipeId, Collections.emptyList())) {
|
||||
if (!item.emptyFactor()) {
|
||||
calculateSuitableRecipe(item, visited);
|
||||
}
|
||||
}
|
||||
|
||||
this.lastItem = this.parentStack.pop();
|
||||
}
|
||||
|
||||
protected void calculateSuitableRecipe(BookmarkItem ingrItem, List<RecipeId> visited) {
|
||||
final BookmarkItem prefItem = this.preferredItems.get(ingrItem);
|
||||
final ItemTreeSlot slot = new ItemTreeSlot(ingrItem, 0, 0, 0, 0, prefItem);
|
||||
|
||||
this.items.add(slot);
|
||||
this.parentOf.put(slot, this.parentStack.peek());
|
||||
this.childrenOf.computeIfAbsent(this.parentStack.peek(), k -> new ArrayList<>()).add(slot);
|
||||
|
||||
this.lastItem = slot;
|
||||
|
||||
if (prefItem != null && !isForeignBoxBoundary(ingrItem) && !visited.contains(prefItem.recipeId)) {
|
||||
visited.add(prefItem.recipeId);
|
||||
prepareIngredients(prefItem.recipeId, visited);
|
||||
visited.remove(prefItem.recipeId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
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 {
|
||||
|
||||
enum CollapseMode {
|
||||
EXPANDED,
|
||||
SMART,
|
||||
ALL
|
||||
}
|
||||
|
||||
enum StatsSection {
|
||||
INGREDIENTS_NEEDED,
|
||||
INGREDIENTS_AVAILABLE,
|
||||
CRAFTING_NEEDED,
|
||||
CRAFTING_AVAILABLE,
|
||||
RESULTS,
|
||||
REMAINDERS,
|
||||
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";
|
||||
private static final String KEY_COLLAPSE_MODE = "craftingTree.collapseMode";
|
||||
|
||||
public boolean statsPanelVisible = true;
|
||||
public final Set<StatsSection> visibleStatsSections = EnumSet.allOf(StatsSection.class);
|
||||
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;
|
||||
|
||||
public SearchResult searchItemResult;
|
||||
public SearchResult searchStatsResult;
|
||||
|
||||
public void load() {
|
||||
this.statsPanelVisible = !NEIClientConfig.world.nbt.hasKey(KEY_STATS_VISIBLE)
|
||||
|| NEIClientConfig.world.nbt.getBoolean(KEY_STATS_VISIBLE);
|
||||
|
||||
final int savedSectionsMask = NEIClientConfig.world.nbt.hasKey(KEY_STATS_SECTIONS)
|
||||
? NEIClientConfig.world.nbt.getInteger(KEY_STATS_SECTIONS)
|
||||
: -1;
|
||||
|
||||
this.visibleStatsSections.clear();
|
||||
|
||||
for (StatsSection section : StatsSection.values()) {
|
||||
if ((savedSectionsMask & (1 << section.ordinal())) != 0) {
|
||||
this.visibleStatsSections.add(section);
|
||||
}
|
||||
}
|
||||
|
||||
this.useInventorySnapshot = NEIClientConfig.world.nbt.getBoolean(KEY_USE_INVENTORY_SNAPSHOT);
|
||||
|
||||
final CollapseMode[] collapseModes = CollapseMode.values();
|
||||
final int savedCollapseMode = NEIClientConfig.world.nbt.getInteger(KEY_COLLAPSE_MODE);
|
||||
this.collapseMode = savedCollapseMode >= 0 && savedCollapseMode < collapseModes.length
|
||||
? collapseModes[savedCollapseMode]
|
||||
: CollapseMode.EXPANDED;
|
||||
this.replaceRecipeNode = null;
|
||||
this.searchItemResult = null;
|
||||
this.searchStatsResult = null;
|
||||
this.camera.endDrag();
|
||||
}
|
||||
|
||||
public void toggleStatsPanelVisible() {
|
||||
this.statsPanelVisible = !this.statsPanelVisible;
|
||||
NEIClientConfig.world.nbt.setBoolean(KEY_STATS_VISIBLE, this.statsPanelVisible);
|
||||
}
|
||||
|
||||
public void toggleStatsSection(StatsSection section) {
|
||||
|
||||
if (!this.visibleStatsSections.remove(section)) {
|
||||
this.visibleStatsSections.add(section);
|
||||
}
|
||||
|
||||
int mask = 0;
|
||||
|
||||
for (StatsSection visibleSection : this.visibleStatsSections) {
|
||||
mask |= 1 << visibleSection.ordinal();
|
||||
}
|
||||
|
||||
NEIClientConfig.world.nbt.setInteger(KEY_STATS_SECTIONS, mask);
|
||||
}
|
||||
|
||||
public void toggleUseInventorySnapshot() {
|
||||
this.useInventorySnapshot = !this.useInventorySnapshot;
|
||||
NEIClientConfig.world.nbt.setBoolean(KEY_USE_INVENTORY_SNAPSHOT, this.useInventorySnapshot);
|
||||
}
|
||||
|
||||
public void cycleCollapseMode() {
|
||||
final CollapseMode[] collapseModes = CollapseMode.values();
|
||||
this.collapseMode = collapseModes[(this.collapseMode.ordinal() + 1) % collapseModes.length];
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.GuiNEIButton;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
|
||||
class CraftingTreeToolbar {
|
||||
|
||||
private static class GuiStatsButton extends GuiNEIButton {
|
||||
|
||||
private final Supplier<DrawableResource> icon;
|
||||
private final BooleanSupplier isEnabled;
|
||||
private final Supplier<String> tooltip;
|
||||
|
||||
public GuiStatsButton(int buttonId, Supplier<DrawableResource> icon, Supplier<String> tooltip,
|
||||
BooleanSupplier isEnabled) {
|
||||
super(buttonId, 0, 0, 16, 16, "");
|
||||
this.tooltip = tooltip;
|
||||
this.icon = icon;
|
||||
this.isEnabled = isEnabled;
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return this.tooltip.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoverState(boolean mouseOver) {
|
||||
return this.isEnabled.getAsBoolean() ? 0 : super.getHoverState(mouseOver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
|
||||
final DrawableResource icon = this.icon.get();
|
||||
final int iconX = this.xPosition + (this.width - icon.width / 2) / 2;
|
||||
final int iconY = this.yPosition + (this.height - icon.height / 2) / 2;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glTranslated(iconX, iconY, 0);
|
||||
GL11.glScalef(0.5f, 0.5f, 1);
|
||||
|
||||
icon.bindTexture();
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
icon.draw(0, 0);
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final DrawableResource STATS_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
2,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource COMPACT_STATS_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
20,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource FIT_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
38,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource INV_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
56,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource LINK_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
74,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_COLLAPSED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
92,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_INDETERMINATE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
110,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_EXPANDED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
128,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_SCREENSHOT = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
146,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final DrawableResource ICON_SETTINGS = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
164,
|
||||
66,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final int BUTTON_SPACING = 20;
|
||||
|
||||
public static final int INV_BUTTON_ID = 0;
|
||||
public static final int FIT_BUTTON_ID = 1;
|
||||
public static final int COLLAPSE_ALL_BUTTON_ID = 2;
|
||||
public static final int TOGGLE_STATS_BUTTON_ID = 3;
|
||||
public static final int STATS_SECTIONS_BUTTON_ID = 4;
|
||||
public static final int LINK_BUTTON_ID = 5;
|
||||
public static final int SCREENSHOT_BUTTON_ID = 6;
|
||||
|
||||
private final CraftingTreeState uiState;
|
||||
private final GuiStatsButton[] buttons;
|
||||
|
||||
public CraftingTreeToolbar(CraftingTreeState uiState) {
|
||||
this.uiState = uiState;
|
||||
|
||||
this.buttons = new GuiStatsButton[] {
|
||||
new GuiStatsButton(
|
||||
INV_BUTTON_ID,
|
||||
() -> INV_TEXTURE,
|
||||
() -> NEIClientUtils.translate(
|
||||
this.uiState.useInventorySnapshot ? "bookmark.tree.toolbar.inventory_on"
|
||||
: "bookmark.tree.toolbar.inventory_off"),
|
||||
() -> this.uiState.useInventorySnapshot),
|
||||
|
||||
new GuiStatsButton(
|
||||
FIT_BUTTON_ID,
|
||||
() -> FIT_TEXTURE,
|
||||
() -> NEIClientUtils.translate("bookmark.tree.toolbar.fit_to_view"),
|
||||
() -> false),
|
||||
|
||||
new GuiStatsButton(COLLAPSE_ALL_BUTTON_ID, () -> switch (this.uiState.collapseMode) {
|
||||
case EXPANDED -> ICON_EXPANDED;
|
||||
case SMART -> ICON_INDETERMINATE;
|
||||
case ALL -> ICON_COLLAPSED;
|
||||
}, () -> NEIClientUtils.translate(switch (this.uiState.collapseMode) {
|
||||
case EXPANDED -> "bookmark.tree.toolbar.collapse_resolved";
|
||||
case SMART -> "bookmark.tree.toolbar.collapse_all";
|
||||
case ALL -> "bookmark.tree.toolbar.expand_all";
|
||||
}), () -> this.uiState.collapseMode != CraftingTreeState.CollapseMode.EXPANDED),
|
||||
|
||||
new GuiStatsButton(
|
||||
TOGGLE_STATS_BUTTON_ID,
|
||||
() -> this.uiState.visibleStatsSections.size() == CraftingTreeState.StatsSection.values().length
|
||||
? STATS_TEXTURE
|
||||
: COMPACT_STATS_TEXTURE,
|
||||
() -> NEIClientUtils.translate(
|
||||
this.uiState.statsPanelVisible ? "bookmark.tree.toolbar.stats_on"
|
||||
: "bookmark.tree.toolbar.stats_off"),
|
||||
() -> this.uiState.statsPanelVisible),
|
||||
|
||||
new GuiStatsButton(
|
||||
STATS_SECTIONS_BUTTON_ID,
|
||||
() -> ICON_SETTINGS,
|
||||
() -> NEIClientUtils.translate("bookmark.tree.toolbar.stats_sections"),
|
||||
() -> false),
|
||||
new GuiStatsButton(
|
||||
LINK_BUTTON_ID,
|
||||
() -> LINK_TEXTURE,
|
||||
() -> NEIClientUtils.translate("bookmark.tree.toolbar.link"),
|
||||
() -> false),
|
||||
new GuiStatsButton(
|
||||
SCREENSHOT_BUTTON_ID,
|
||||
() -> ICON_SCREENSHOT,
|
||||
() -> NEIClientUtils.translate("bookmark.tree.toolbar.screenshot"),
|
||||
() -> false)
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public void layout(int x, int y) {
|
||||
for (int i = 0; i < this.buttons.length; i++) {
|
||||
this.buttons[i].xPosition = x + BUTTON_SPACING * i;
|
||||
this.buttons[i].yPosition = y;
|
||||
}
|
||||
}
|
||||
|
||||
public void addTo(List<GuiButton> buttonList) {
|
||||
buttonList.addAll(Arrays.asList(this.buttons));
|
||||
}
|
||||
|
||||
public GuiButton getButton(int buttonId) {
|
||||
|
||||
for (int i = 0; i < this.buttons.length; i++) {
|
||||
if (this.buttons[i].id == buttonId) {
|
||||
return this.buttons[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getTooltip(Minecraft mc, int mousex, int mousey) {
|
||||
for (GuiStatsButton button : this.buttons) {
|
||||
if (button.mousePressed(mc, mousex, mousey)) {
|
||||
return button.getTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,90 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
|
||||
class ItemTreeSlot {
|
||||
|
||||
public final BookmarkItem ingrItem;
|
||||
public final BookmarkItem prefItem;
|
||||
|
||||
public final long ingeritRequestedAmount;
|
||||
public final long currentRequestedAmount;
|
||||
public final long requestedAmount;
|
||||
|
||||
public final long containerAmount;
|
||||
public final long producedAmount;
|
||||
public final long leftAmount;
|
||||
|
||||
public final ItemStack emptyStack;
|
||||
public final ItemStack itemStack;
|
||||
public final boolean isFluidDisplay;
|
||||
public final boolean isContainerItem;
|
||||
|
||||
public final HandlerInfo info;
|
||||
public final long multiplier;
|
||||
|
||||
public ItemTreeSlot(BookmarkItem ingrItem, long ingeritRequestedAmount, long currentRequestedAmount,
|
||||
long producedAmount, long containerAmount, BookmarkItem prefItem) {
|
||||
|
||||
this.ingrItem = ingrItem;
|
||||
this.ingeritRequestedAmount = ingeritRequestedAmount;
|
||||
this.currentRequestedAmount = currentRequestedAmount;
|
||||
this.requestedAmount = ingeritRequestedAmount + currentRequestedAmount;
|
||||
|
||||
this.containerAmount = containerAmount;
|
||||
this.producedAmount = producedAmount;
|
||||
this.leftAmount = producedAmount + containerAmount;
|
||||
this.prefItem = prefItem;
|
||||
|
||||
if (prefItem != null) {
|
||||
this.info = handlerInfo(prefItem.recipeId.getHandlerName());
|
||||
this.multiplier = prefItem.getMultiplierFromAmount(this.requestedAmount);
|
||||
|
||||
this.itemStack = prefItem.getItemStack(this.requestedAmount);
|
||||
this.emptyStack = prefItem.getItemStack(0);
|
||||
} else {
|
||||
this.info = null;
|
||||
this.multiplier = ingrItem.getMultiplierFromAmount(this.requestedAmount);
|
||||
this.itemStack = ingrItem.getItemStack(this.requestedAmount);
|
||||
this.emptyStack = ingrItem.getItemStack(0);
|
||||
}
|
||||
|
||||
this.isFluidDisplay = StackInfo.isFluidDisplayItem(this.emptyStack);
|
||||
this.isContainerItem = isContainerItem(this.emptyStack);
|
||||
}
|
||||
|
||||
private static HandlerInfo handlerInfo(String handlerName) {
|
||||
final HandlerInfo info = GuiRecipeTab.getHandlerInfo(handlerName, null);
|
||||
return info != null ? info : GuiRecipeTab.DEFAULT_HANDLER_INFO;
|
||||
}
|
||||
|
||||
private static boolean isContainerItem(ItemStack stack) {
|
||||
boolean isContainerItem = false;
|
||||
|
||||
if (stack.getItem().hasContainerItem(stack)) {
|
||||
final boolean isPausedItemDamageSound = StackInfo.isPausedItemDamageSound();
|
||||
|
||||
try {
|
||||
|
||||
StackInfo.pauseItemDamageSound(true);
|
||||
|
||||
final ItemStack containerItem = stack.getItem().getContainerItem(stack);
|
||||
|
||||
if (containerItem != null) {
|
||||
isContainerItem = stack.getItem() == containerItem.getItem();
|
||||
}
|
||||
|
||||
} finally {
|
||||
StackInfo.pauseItemDamageSound(isPausedItemDamageSound);
|
||||
}
|
||||
}
|
||||
|
||||
return isContainerItem;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
|
||||
class SearchResult {
|
||||
|
||||
public final Object searchGUID;
|
||||
public ItemTreeSlot highlightNode;
|
||||
public final Set<ItemTreeSlot> searchMatches = new LinkedHashSet<>();
|
||||
|
||||
public SearchResult(Object searchGUID) {
|
||||
this.searchGUID = searchGUID;
|
||||
}
|
||||
|
||||
public boolean matchesGUID(Object guid) {
|
||||
if (guid instanceof ItemStack stack && this.searchGUID instanceof ItemStack searchStack) {
|
||||
return NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, searchStack);
|
||||
}
|
||||
|
||||
return this.searchGUID.equals(guid);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemSorter;
|
||||
import codechicken.nei.ItemStackAmount;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIClientUtils.Alignment;
|
||||
import codechicken.nei.Widget;
|
||||
import codechicken.nei.bookmark.BookmarkItem;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.recipe.chain.RecipeChainMath;
|
||||
import codechicken.nei.scroll.ScrollBar;
|
||||
import codechicken.nei.scroll.ScrollContainer;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
|
||||
class StatsPanel extends ScrollContainer {
|
||||
|
||||
public static class HandlerStats {
|
||||
|
||||
public long iterations;
|
||||
public long recipeCount;
|
||||
}
|
||||
|
||||
private static class SectionLabel extends Widget {
|
||||
|
||||
public final String text;
|
||||
|
||||
public SectionLabel(String text) {
|
||||
this.text = text;
|
||||
this.w = GuiDraw.fontRenderer.getStringWidth(text);
|
||||
this.h = GuiDraw.fontRenderer.FONT_HEIGHT + 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mx, int my) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.fontRenderer.drawStringWithShadow(this.text, this.x, this.y, 0xFFFFFFFF));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ItemStatsSlot extends Widget {
|
||||
|
||||
public final ItemStack stack;
|
||||
public final long stackSize;
|
||||
public final RecipeId recipeId;
|
||||
public final long multiplier;
|
||||
public final HandlerInfo info;
|
||||
public final DrawableResource icon;
|
||||
private final boolean isFluidDisplay;
|
||||
public boolean highlight = false;
|
||||
|
||||
public ItemStatsSlot(ItemStack stack, long stackSize, DrawableResource icon, RecipeId recipeId,
|
||||
long multiplier) {
|
||||
this.stack = StackInfo.withAmount(stack, 0);
|
||||
this.stackSize = stackSize;
|
||||
this.icon = icon;
|
||||
this.recipeId = recipeId;
|
||||
this.multiplier = multiplier;
|
||||
this.isFluidDisplay = StackInfo.isFluidDisplayItem(stack);
|
||||
this.info = this.recipeId != null ? handlerInfo(this.recipeId.getHandlerName()) : null;
|
||||
this.w = SLOT_SIZE;
|
||||
this.h = SLOT_SIZE;
|
||||
}
|
||||
|
||||
public ItemStatsSlot(ItemStack stack, long stackSize, DrawableResource icon) {
|
||||
this(stack, stackSize, icon, null, 1);
|
||||
}
|
||||
|
||||
public ItemStack getStack() {
|
||||
return StackInfo.withAmount(this.stack, stackSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mx, int my) {
|
||||
|
||||
if (this.highlight) {
|
||||
SLOT_HIGHLIGHTED.draw(this.x, this.y);
|
||||
} else {
|
||||
this.icon.draw(this.x, this.y);
|
||||
}
|
||||
|
||||
GuiContainerManager
|
||||
.drawItem(this.x + (SLOT_SIZE - 16) / 2, this.y + (SLOT_SIZE - 16) / 2, this.stack, true, "");
|
||||
|
||||
drawStackSize();
|
||||
|
||||
if (mx >= this.x && mx < this.x + SLOT_SIZE && my >= this.y && my < this.y + SLOT_SIZE) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.drawRect(this.x + 1, this.y + 1, SLOT_SIZE - 2, SLOT_SIZE - 2, HOVER_COLOR));
|
||||
}
|
||||
|
||||
if (this.recipeId != null) {
|
||||
drawHandlerBadge();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawHandlerBadge() {
|
||||
final float scale = 0.6f;
|
||||
final float badgeX = x - 6 * scale;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glTranslatef(badgeX, y - 6 * scale, 10.0f);
|
||||
GL11.glScalef(scale, scale, 1.0f);
|
||||
|
||||
if (this.info.hasImageOrItem()) {
|
||||
final DrawableResource image = this.info.getImage();
|
||||
|
||||
if (image != null) {
|
||||
image.draw(0, 0);
|
||||
} else {
|
||||
GuiContainerManager.drawItem(0, 0, this.info.getItemStack(), true);
|
||||
}
|
||||
|
||||
} else {
|
||||
ICON_CRAFTING.draw(0, 0);
|
||||
}
|
||||
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
private void drawStackSize() {
|
||||
String amountString = stackSize < 10_000 ? String.valueOf(stackSize)
|
||||
: ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
|
||||
|
||||
if (this.isFluidDisplay) {
|
||||
amountString += "L";
|
||||
}
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
amountString,
|
||||
new Rectangle4i(
|
||||
x + (SLOT_SIZE - 16) / 2,
|
||||
y + (SLOT_SIZE - 16) / 2,
|
||||
SLOT_SIZE - (SLOT_SIZE - 16),
|
||||
SLOT_SIZE - (SLOT_SIZE - 16)),
|
||||
1,
|
||||
0xFFFFFF,
|
||||
true,
|
||||
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class HandlerStatsSlot extends Widget {
|
||||
|
||||
public final String handlerName;
|
||||
public final HandlerInfo info;
|
||||
public final HandlerStats stats;
|
||||
public boolean highlight = false;
|
||||
|
||||
public HandlerStatsSlot(String handlerName, HandlerStats stats) {
|
||||
this.handlerName = handlerName;
|
||||
this.info = handlerInfo(handlerName);
|
||||
this.stats = stats;
|
||||
this.w = SLOT_SIZE;
|
||||
this.h = SLOT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mx, int my) {
|
||||
|
||||
if (this.highlight) {
|
||||
SLOT_HIGHLIGHTED.draw(this.x, this.y);
|
||||
} else {
|
||||
SLOT_DEFAULT.draw(this.x, this.y);
|
||||
}
|
||||
|
||||
if (this.info.hasImageOrItem()) {
|
||||
final DrawableResource image = this.info.getImage();
|
||||
|
||||
if (image != null) {
|
||||
image.draw(this.x + (SLOT_SIZE - 16) / 2, this.y + (SLOT_SIZE - 16) / 2);
|
||||
} else {
|
||||
GuiContainerManager.drawItem(
|
||||
this.x + (SLOT_SIZE - 16) / 2,
|
||||
this.y + (SLOT_SIZE - 16) / 2,
|
||||
this.info.getItemStack(),
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
ICON_CRAFTING.draw(this.x + (SLOT_SIZE - 16) / 2, this.y + (SLOT_SIZE - 16) / 2);
|
||||
}
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
"x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(this.stats.iterations),
|
||||
new Rectangle4i(this.x + 1, this.y + 1, SLOT_SIZE - 2, SLOT_SIZE - 2),
|
||||
1,
|
||||
0xFFFFFF,
|
||||
true,
|
||||
Alignment.TopLeft);
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
ReadableNumberConverter.INSTANCE.toWideReadableForm(this.stats.recipeCount),
|
||||
new Rectangle4i(this.x + 1, this.y + 1, SLOT_SIZE - 2, SLOT_SIZE - 2),
|
||||
1,
|
||||
0xFFFFFF,
|
||||
true,
|
||||
Alignment.BottomRight);
|
||||
|
||||
if (mx >= this.x && mx < this.x + SLOT_SIZE && my >= this.y && my < this.y + SLOT_SIZE) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.drawRect(this.x + 1, this.y + 1, SLOT_SIZE - 2, SLOT_SIZE - 2, HOVER_COLOR));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final int SLOT_SIZE = 18;
|
||||
|
||||
private static final int SLOT_INLINE_GAP = 2;
|
||||
private static final int SLOT_BLOCK_GAP = 2;
|
||||
private static final int COLUMN_GAP = 10;
|
||||
|
||||
private static final int HOVER_COLOR = 0x66999999;
|
||||
|
||||
private static final DrawableResource BG_TEXTURE = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
26,
|
||||
84,
|
||||
46,
|
||||
16).setSlices(0, 0, 2, 0).build();
|
||||
|
||||
private static final DrawableResource SLOT_DEFAULT = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
68,
|
||||
26,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_MISSING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
92,
|
||||
26,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_CRAFTING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
116,
|
||||
26,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_DISABLED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
140,
|
||||
26,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource SLOT_HIGHLIGHTED = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
164,
|
||||
26,
|
||||
18,
|
||||
18).build();
|
||||
|
||||
private static final DrawableResource ICON_CRAFTING = new DrawableBuilder(
|
||||
"nei:textures/gui/craftingtree.png",
|
||||
67,
|
||||
48,
|
||||
16,
|
||||
16).build();
|
||||
|
||||
private static final int Z_OFFSET = 100;
|
||||
|
||||
public StatsPanel() {
|
||||
final ScrollBar scrollbar = new ScrollBar().setTrackWidth(4)
|
||||
.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);
|
||||
setPaddingBlock(4, 4);
|
||||
setPaddingInline(1, 1);
|
||||
}
|
||||
|
||||
public void rebuild(RecipeChainMath math, CraftingTreeGraph graph, boolean useInventorySnapshot,
|
||||
Set<CraftingTreeState.StatsSection> sections, int widgetX, int widgetY, int widgetW, int widgetH) {
|
||||
|
||||
clear();
|
||||
|
||||
final List<String> labels = new ArrayList<>();
|
||||
final List<List<Widget>> sectionGroups = new ArrayList<>();
|
||||
final List<Widget> inputsAndAvailable = new ArrayList<>();
|
||||
|
||||
if (sections.contains(CraftingTreeState.StatsSection.INGREDIENTS_NEEDED)) {
|
||||
inputsAndAvailable.addAll(collectInputs(math, useInventorySnapshot));
|
||||
}
|
||||
|
||||
if (sections.contains(CraftingTreeState.StatsSection.INGREDIENTS_AVAILABLE)) {
|
||||
inputsAndAvailable.addAll(collectAvailable(math));
|
||||
}
|
||||
|
||||
if (!inputsAndAvailable.isEmpty()) {
|
||||
labels.add(NEIClientUtils.translate("bookmark.tree.stats.ingredients"));
|
||||
sectionGroups.add(inputsAndAvailable);
|
||||
}
|
||||
|
||||
final boolean showCraftingNeeded = sections.contains(CraftingTreeState.StatsSection.CRAFTING_NEEDED);
|
||||
final boolean showCraftingAvailable = sections.contains(CraftingTreeState.StatsSection.CRAFTING_AVAILABLE);
|
||||
|
||||
if (showCraftingNeeded || showCraftingAvailable) {
|
||||
final List<Widget> craftingNeededSlot = new ArrayList<>();
|
||||
final List<Widget> craftingAvailableSlot = new ArrayList<>();
|
||||
final Map<BookmarkItem, Boolean> recipeMissing = new LinkedHashMap<>();
|
||||
final Map<BookmarkItem, Integer> recipeLevel = new HashMap<>();
|
||||
|
||||
for (Map.Entry<ItemTreeSlot, List<ItemTreeSlot>> entry : graph.childrenOf.entrySet()) {
|
||||
final ItemTreeSlot node = entry.getKey();
|
||||
final boolean needsCrafting = graph.childrenNeedCraftingCache.contains(node);
|
||||
|
||||
recipeMissing.merge(node.prefItem, needsCrafting, (a, b) -> a || b);
|
||||
recipeLevel.merge(node.prefItem, treeLevel(node, graph.parentOf), Math::max);
|
||||
}
|
||||
|
||||
final List<Map.Entry<BookmarkItem, Boolean>> sortedRecipeMissing = new ArrayList<>(
|
||||
recipeMissing.entrySet());
|
||||
|
||||
sortedRecipeMissing.sort(
|
||||
Comparator.<Map.Entry<BookmarkItem, Boolean>>comparingInt(entry -> recipeLevel.get(entry.getKey()))
|
||||
.thenComparing(entry -> entry.getKey().itemStack, ItemSorter.instance));
|
||||
|
||||
for (Map.Entry<BookmarkItem, Boolean> entry : sortedRecipeMissing) {
|
||||
final BookmarkItem resultItem = entry.getKey();
|
||||
final long amount = resultItem.getAmount();
|
||||
|
||||
if (amount > 0) {
|
||||
final boolean missingIngredients = entry.getValue();
|
||||
|
||||
if (missingIngredients ? !showCraftingNeeded : !showCraftingAvailable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<Widget> slot = missingIngredients ? craftingNeededSlot : craftingAvailableSlot;
|
||||
|
||||
slot.add(
|
||||
new ItemStatsSlot(
|
||||
resultItem.itemStack,
|
||||
resultItem.getStackSize(amount),
|
||||
missingIngredients ? SLOT_DEFAULT : SLOT_CRAFTING,
|
||||
resultItem.recipeId,
|
||||
resultItem.getMultiplier()));
|
||||
}
|
||||
}
|
||||
|
||||
final List<Widget> craftingSlots = new ArrayList<>(craftingNeededSlot);
|
||||
craftingSlots.addAll(craftingAvailableSlot);
|
||||
|
||||
if (!craftingSlots.isEmpty()) {
|
||||
labels.add(NEIClientUtils.translate("bookmark.tree.stats.crafting"));
|
||||
sectionGroups.add(craftingSlots);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Widget> outputsAndRemainders = new ArrayList<>();
|
||||
|
||||
if (sections.contains(CraftingTreeState.StatsSection.RESULTS)) {
|
||||
outputsAndRemainders.addAll(collectOutputs(math));
|
||||
}
|
||||
|
||||
if (sections.contains(CraftingTreeState.StatsSection.REMAINDERS)) {
|
||||
outputsAndRemainders.addAll(collectRemainder(math));
|
||||
}
|
||||
|
||||
if (!outputsAndRemainders.isEmpty()) {
|
||||
labels.add(NEIClientUtils.translate("bookmark.tree.stats.results"));
|
||||
sectionGroups.add(outputsAndRemainders);
|
||||
}
|
||||
|
||||
if (sections.contains(CraftingTreeState.StatsSection.HANDLERS)) {
|
||||
final List<Widget> handlers = collectHandlers(math);
|
||||
|
||||
if (!handlers.isEmpty()) {
|
||||
labels.add(NEIClientUtils.translate("bookmark.tree.stats.handlers"));
|
||||
sectionGroups.add(handlers);
|
||||
}
|
||||
}
|
||||
|
||||
layoutSections(labels, sectionGroups, widgetX, widgetY, widgetW, widgetH);
|
||||
}
|
||||
|
||||
private void layoutSections(List<String> labels, List<List<Widget>> sections, int widgetX, int widgetY, int widgetW,
|
||||
int widgetH) {
|
||||
final int maxRowWidth = Math.max(SLOT_SIZE, widgetW);
|
||||
final int sectionCount = sections.size();
|
||||
final int[] sizes = new int[sectionCount];
|
||||
final int[] labelWidths = new int[sectionCount];
|
||||
final int[] columns = new int[sectionCount];
|
||||
final int maxRows = widgetH / ((GuiDraw.fontRenderer.FONT_HEIGHT + 4) + SLOT_SIZE * 2 + SLOT_BLOCK_GAP) > 3 ? 2
|
||||
: 1;
|
||||
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
sizes[i] = sections.get(i).size();
|
||||
labelWidths[i] = GuiDraw.fontRenderer.getStringWidth(labels.get(i) + ":");
|
||||
columns[i] = sizes[i] == 0 ? 0 : (sizes[i] + maxRows - 1) / maxRows;
|
||||
}
|
||||
|
||||
int compactWidth = COLUMN_GAP * Math.max(0, sectionCount - 1);
|
||||
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
compactWidth += sectionWidth(columns[i], labelWidths[i]);
|
||||
}
|
||||
|
||||
if (compactWidth <= maxRowWidth) {
|
||||
int budget = (maxRowWidth - compactWidth) / (SLOT_SIZE + SLOT_INLINE_GAP);
|
||||
|
||||
while (budget > 0) {
|
||||
boolean grew = false;
|
||||
|
||||
for (int i = 0; i < sectionCount && budget > 0; i++) {
|
||||
if (columns[i] < sizes[i]) {
|
||||
columns[i]++;
|
||||
--budget;
|
||||
grew = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!grew) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
int cursorX = 0;
|
||||
int maxHeight = 0;
|
||||
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
final int height = placeSection(widgets, labels.get(i), sections.get(i), columns[i], cursorX, 0);
|
||||
maxHeight = Math.max(maxHeight, height);
|
||||
cursorX += sectionWidth(columns[i], labelWidths[i]) + COLUMN_GAP;
|
||||
}
|
||||
|
||||
final int contentW = Math.max(0, cursorX - COLUMN_GAP);
|
||||
final int contentH = maxHeight;
|
||||
|
||||
final int maxVisibleH = maxRows * SLOT_SIZE + Math.max(0, maxRows - 1) * SLOT_BLOCK_GAP
|
||||
+ GuiDraw.fontRenderer.FONT_HEIGHT
|
||||
+ 4;
|
||||
|
||||
final int visibleHeight = Math.min(contentH, maxVisibleH);
|
||||
|
||||
this.w = widgetW;
|
||||
this.x = widgetX;
|
||||
this.h = visibleHeight + this.paddingBlockStart + this.paddingBlockEnd;
|
||||
this.y = widgetY + widgetH - this.h;
|
||||
|
||||
final int xShift = contentW < this.w ? (this.w - contentW) / 2 : 0;
|
||||
|
||||
for (Widget widget : widgets) {
|
||||
widget.x += xShift;
|
||||
}
|
||||
|
||||
setWidgets(widgets);
|
||||
update();
|
||||
}
|
||||
|
||||
private static int sectionWidth(int columns, int labelWidth) {
|
||||
return Math.max(columns * SLOT_SIZE + Math.max(0, columns - 1) * SLOT_INLINE_GAP, labelWidth);
|
||||
}
|
||||
|
||||
private static int placeSection(List<Widget> widgets, String label, List<Widget> group, int columns, int x, int y) {
|
||||
final SectionLabel sectionLabel = new SectionLabel(label + ":");
|
||||
sectionLabel.x = x;
|
||||
sectionLabel.y = y;
|
||||
widgets.add(sectionLabel);
|
||||
|
||||
final int gridY = y + GuiDraw.fontRenderer.FONT_HEIGHT + 4;
|
||||
final int rowWidth = columns * SLOT_SIZE + Math.max(0, columns - 1) * SLOT_INLINE_GAP;
|
||||
int cursorX = 0;
|
||||
int row = 0;
|
||||
boolean any = false;
|
||||
|
||||
for (Widget entry : group) {
|
||||
|
||||
if (cursorX > 0) {
|
||||
if (cursorX + SLOT_INLINE_GAP + SLOT_SIZE > rowWidth) {
|
||||
cursorX = 0;
|
||||
row++;
|
||||
} else {
|
||||
cursorX += SLOT_INLINE_GAP;
|
||||
}
|
||||
} else if (cursorX + SLOT_SIZE > rowWidth) {
|
||||
cursorX = 0;
|
||||
row++;
|
||||
}
|
||||
|
||||
entry.x = x + cursorX;
|
||||
entry.y = gridY + row * (SLOT_SIZE + SLOT_BLOCK_GAP);
|
||||
widgets.add(entry);
|
||||
|
||||
cursorX += SLOT_SIZE;
|
||||
any = true;
|
||||
}
|
||||
|
||||
final int rows = any ? row + 1 : 0;
|
||||
return (gridY - y) + rows * SLOT_SIZE + Math.max(0, rows - 1) * SLOT_BLOCK_GAP;
|
||||
}
|
||||
|
||||
public ItemStack getStackMouseOver(int mouseX, int mouseY) {
|
||||
final Widget widget = getWidgetUnderMouse(mouseX, mouseY);
|
||||
return widget instanceof ItemStatsSlot slot && slot.recipeId == null ? slot.getStack() : null;
|
||||
}
|
||||
|
||||
public void setHighlight(SearchResult searchResult) {
|
||||
for (Widget widget : getWidgets()) {
|
||||
if (widget instanceof HandlerStatsSlot slot) {
|
||||
slot.highlight = searchResult != null && searchResult.matchesGUID(slot.handlerName);
|
||||
} else if (widget instanceof ItemStatsSlot slot) {
|
||||
slot.highlight = searchResult != null && searchResult.matchesGUID(slot.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object getWidgetGUIDUnderMouse(int mouseX, int mouseY) {
|
||||
final Widget widget = getWidgetUnderMouse(mouseX, mouseY);
|
||||
|
||||
if (widget instanceof HandlerStatsSlot slot) {
|
||||
return slot.handlerName;
|
||||
} else if (widget instanceof ItemStatsSlot slot) {
|
||||
return slot.stack;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getHandlerMouseOver(int mouseX, int mouseY) {
|
||||
final Widget widget = getWidgetUnderMouse(mouseX, mouseY);
|
||||
|
||||
if (widget instanceof HandlerStatsSlot slot) {
|
||||
return slot.handlerName;
|
||||
} else if (widget instanceof ItemStatsSlot slot && slot.recipeId != null) {
|
||||
return slot.recipeId.getHandlerName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public RecipeId getRecipeIdMouseOver(int mouseX, int mouseY) {
|
||||
final Widget widget = getWidgetUnderMouse(mouseX, mouseY);
|
||||
return widget instanceof ItemStatsSlot slot ? slot.recipeId : null;
|
||||
}
|
||||
|
||||
public boolean isDraggingScrollbar() {
|
||||
return canScrollHorizontal() && getHorizontalScroll().isMouseDragged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int direction, int mouseX, int mouseY) {
|
||||
|
||||
if (canScrollHorizontal() && boundsOutside().contains(mouseX, mouseY)) {
|
||||
getHorizontalScroll().setScrollOffset(this, getHorizontalScrollOffset() - direction * MOUSE_SCROLL_SPEED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onMouseWheel(direction, mouseX, mouseY);
|
||||
}
|
||||
|
||||
public void draw(int mouseX, int mouseY) {
|
||||
GL11.glTranslatef(0, 0, Z_OFFSET);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
BG_TEXTURE.draw(this.x, this.y, this.w, this.h);
|
||||
|
||||
super.draw(mouseX, mouseY);
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glTranslatef(0, 0, -Z_OFFSET);
|
||||
}
|
||||
|
||||
public void drawFull() {
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
|
||||
drawContent(-1, -1);
|
||||
GL11.glPopAttrib();
|
||||
}
|
||||
|
||||
private List<Widget> collectAvailable(RecipeChainMath math) {
|
||||
final ItemStackAmount items = new ItemStackAmount();
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
|
||||
for (BookmarkItem item : math.initialItems) {
|
||||
final long amount = math.requiredAmount.getOrDefault(item, 0L);
|
||||
|
||||
if (amount > 0) {
|
||||
items.add(item.itemStack, item.getStackSize(amount));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : items.entrySet()) {
|
||||
if (entry.getValue() > 0) {
|
||||
widgets.add(
|
||||
new ItemStatsSlot(StackInfo.loadFromNBT(entry.getKey(), 0), entry.getValue(), SLOT_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
private List<Widget> collectInputs(RecipeChainMath math, boolean useInventorySnapshot) {
|
||||
final ItemStackAmount items = new ItemStackAmount();
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
|
||||
for (BookmarkItem item : math.recipeIngredients) {
|
||||
final long amount = math.requiredAmount.containsKey(math.preferredItems.get(item)) ? 0
|
||||
: math.requiredAmount.getOrDefault(item, item.getAmount());
|
||||
|
||||
if (amount > 0) {
|
||||
items.add(item.itemStack, item.getStackSize(amount));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : items.entrySet()) {
|
||||
if (entry.getValue() > 0) {
|
||||
widgets.add(
|
||||
new ItemStatsSlot(
|
||||
StackInfo.loadFromNBT(entry.getKey(), 0),
|
||||
entry.getValue(),
|
||||
useInventorySnapshot ? SLOT_MISSING : SLOT_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
private List<Widget> collectOutputs(RecipeChainMath math) {
|
||||
final ItemStackAmount items = new ItemStackAmount();
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
|
||||
for (BookmarkItem item : math.recipeResults) {
|
||||
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
|
||||
|
||||
if (amount > 0 && math.outputRecipes.containsKey(item.recipeId)) {
|
||||
items.add(item.itemStack, item.getStackSize(amount));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : items.entrySet()) {
|
||||
if (entry.getValue() > 0) {
|
||||
widgets.add(
|
||||
new ItemStatsSlot(StackInfo.loadFromNBT(entry.getKey(), 0), entry.getValue(), SLOT_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
private List<Widget> collectRemainder(RecipeChainMath math) {
|
||||
final ItemStackAmount items = new ItemStackAmount();
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
|
||||
for (BookmarkItem item : math.recipeResults) {
|
||||
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
|
||||
|
||||
if (amount > 0 && !math.outputRecipes.containsKey(item.recipeId)) {
|
||||
items.add(item.itemStack, item.getStackSize(amount));
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack stack : math.containerItemsInventory) {
|
||||
if (stack != null) {
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack stack : math.containerItemsCrafting) {
|
||||
if (stack != null) {
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : items.entrySet()) {
|
||||
if (entry.getValue() > 0) {
|
||||
widgets.add(
|
||||
new ItemStatsSlot(StackInfo.loadFromNBT(entry.getKey(), 0), entry.getValue(), SLOT_DISABLED));
|
||||
}
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
private List<Widget> collectHandlers(RecipeChainMath math) {
|
||||
final Map<String, HandlerStats> handlerStats = new HashMap<>();
|
||||
final List<Widget> widgets = new ArrayList<>();
|
||||
final Set<RecipeId> countedRecipes = new HashSet<>();
|
||||
|
||||
for (BookmarkItem item : math.recipeResults) {
|
||||
// a recipe adds one result item per output, count it only once
|
||||
if (!countedRecipes.add(item.recipeId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final HandlerStats stats = handlerStats
|
||||
.computeIfAbsent(item.recipeId.getHandlerName(), k -> new HandlerStats());
|
||||
stats.iterations += item.getMultiplier();
|
||||
stats.recipeCount++;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, HandlerStats> entry : handlerStats.entrySet()) {
|
||||
widgets.add(new HandlerStatsSlot(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
private static int treeLevel(ItemTreeSlot node, Map<ItemTreeSlot, ItemTreeSlot> parentOf) {
|
||||
int level = 0;
|
||||
|
||||
for (ItemTreeSlot current = parentOf.get(node); current != null; current = parentOf.get(current)) {
|
||||
level++;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
private static HandlerInfo handlerInfo(String handlerName) {
|
||||
final HandlerInfo info = GuiRecipeTab.getHandlerInfo(handlerName, null);
|
||||
return info != null ? info : GuiRecipeTab.DEFAULT_HANDLER_INFO;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import static codechicken.lib.gui.GuiDraw.drawStringC;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.LayoutManager;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.bookmark.tree.CraftingTreeState.StatsSection;
|
||||
|
||||
class StatsSectionsMenu {
|
||||
|
||||
private static final StatsSection[] SECTIONS = StatsSection.values();
|
||||
|
||||
private static final int ROW_HEIGHT = 16;
|
||||
private static final int LABEL_PADDING = 10;
|
||||
private static final int MIN_WIDTH = 70;
|
||||
private static final int TOP_PADDING = 4;
|
||||
private static final int Z_OFFSET = 500;
|
||||
|
||||
private static final int COLOR_HOVER = 0xFFFFFFA0;
|
||||
private static final int COLOR_ENABLED = 0xFFE0E0E0;
|
||||
private static final int COLOR_DISABLED = 0xFFA0A0A0;
|
||||
|
||||
public boolean visible = false;
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private int w;
|
||||
private int h;
|
||||
|
||||
public void open(int anchorX, int anchorY) {
|
||||
this.visible = true;
|
||||
|
||||
int maxLabelWidth = 0;
|
||||
|
||||
for (StatsSection section : SECTIONS) {
|
||||
maxLabelWidth = Math.max(maxLabelWidth, GuiDraw.fontRenderer.getStringWidth(label(section)));
|
||||
}
|
||||
|
||||
this.w = Math.max(MIN_WIDTH, maxLabelWidth + LABEL_PADDING * 2);
|
||||
this.h = TOP_PADDING + SECTIONS.length * ROW_HEIGHT;
|
||||
this.x = anchorX;
|
||||
this.y = anchorY;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
public boolean isInWidget(int mouseX, int mouseY) {
|
||||
return this.visible && mouseX >= this.x
|
||||
&& mouseX < this.x + this.w
|
||||
&& mouseY >= this.y
|
||||
&& mouseY < this.y + this.h;
|
||||
}
|
||||
|
||||
public boolean handleClick(int mouseX, int mouseY, CraftingTreeState uiState) {
|
||||
|
||||
if (!isInWidget(mouseX, mouseY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int relativeY = mouseY - this.y - TOP_PADDING;
|
||||
|
||||
if (relativeY >= 0) {
|
||||
final int index = relativeY / ROW_HEIGHT;
|
||||
|
||||
if (index < SECTIONS.length) {
|
||||
uiState.toggleStatsSection(SECTIONS[index]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void draw(int mouseX, int mouseY, CraftingTreeState uiState) {
|
||||
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
GL11.glTranslatef(0, 0, Z_OFFSET);
|
||||
|
||||
for (int i = 0; i < SECTIONS.length; i++) {
|
||||
final StatsSection section = SECTIONS[i];
|
||||
final int rowY = this.y + TOP_PADDING + i * ROW_HEIGHT;
|
||||
final boolean hover = mouseX >= this.x && mouseX < this.x + this.w
|
||||
&& mouseY >= rowY
|
||||
&& mouseY < rowY + ROW_HEIGHT;
|
||||
final boolean checked = uiState.visibleStatsSections.contains(section);
|
||||
final int color = hover ? COLOR_HOVER : checked ? COLOR_ENABLED : COLOR_DISABLED;
|
||||
|
||||
if (checked) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
LayoutManager.drawButtonBackground(this.x, rowY, this.w, ROW_HEIGHT, false, 1);
|
||||
} else {
|
||||
GL11.glColor4f(0.65F, 0.65F, 0.65F, 1.0F);
|
||||
LayoutManager.drawButtonBackground(this.x, rowY, this.w, ROW_HEIGHT, false, 0);
|
||||
}
|
||||
|
||||
drawStringC(label(section), this.x, rowY, this.w, ROW_HEIGHT, color);
|
||||
}
|
||||
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
GL11.glTranslatef(0, 0, -Z_OFFSET);
|
||||
}
|
||||
|
||||
private static String label(StatsSection section) {
|
||||
return NEIClientUtils.translate(switch (section) {
|
||||
case INGREDIENTS_NEEDED -> "bookmark.tree.stats.ingredients_needed";
|
||||
case INGREDIENTS_AVAILABLE -> "bookmark.tree.stats.ingredients_available";
|
||||
case CRAFTING_NEEDED -> "bookmark.tree.stats.crafting_needed";
|
||||
case CRAFTING_AVAILABLE -> "bookmark.tree.stats.crafting_available";
|
||||
case RESULTS -> "bookmark.tree.stats.results";
|
||||
case REMAINDERS -> "bookmark.tree.stats.remainders";
|
||||
case HANDLERS -> "bookmark.tree.stats.handlers";
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package codechicken.nei.bookmark.tree;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.recipe.GuiRecipeButton;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
|
||||
public class TreeOverlayButton extends GuiRecipeButton {
|
||||
|
||||
protected static final DrawableResource ICON_FILL = new DrawableBuilder(
|
||||
"nei:textures/nei_sprites.png",
|
||||
28,
|
||||
76,
|
||||
9,
|
||||
10).build();
|
||||
protected static final int BUTTON_ID_SHIFT = 4;
|
||||
|
||||
public final GuiCraftingTree treeGui;
|
||||
|
||||
public TreeOverlayButton(GuiCraftingTree treeGui, RecipeHandlerRef handlerRef, int x, int y) {
|
||||
super(handlerRef, x, y, handlerRef.recipeIndex + BUTTON_ID_SHIFT, "+");
|
||||
this.treeGui = treeGui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(List<String> currenttip) {
|
||||
currenttip.add(NEIClientUtils.translate("bookmark.tree.overlay"));
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(int mouseX, int mouseY) {
|
||||
if (this.treeGui != null) {
|
||||
this.treeGui.addRecipe(Recipe.of(this.handlerRef));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawContent(Minecraft minecraft, int y, int x, boolean mouseOver) {
|
||||
final int iconX = this.xPosition + (this.width - ICON_FILL.width - 1) / 2;
|
||||
final int iconY = this.yPosition + (this.height - ICON_FILL.height) / 2;
|
||||
|
||||
GL11.glColor4f(1, 1, 1, this.enabled ? 1 : 0.5f);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
ICON_FILL.draw(iconX, iconY);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawItemOverlay() {}
|
||||
|
||||
public Recipe getRecipe() {
|
||||
return Recipe.of(this.handlerRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(char keyChar, int keyID) {}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,39 @@
|
||||
package codechicken.nei.commands;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.JsonToNBT;
|
||||
import net.minecraft.nbt.NBTException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
import codechicken.nei.ItemPanels;
|
||||
import codechicken.nei.LRUCache;
|
||||
import codechicken.nei.bookmark.BookmarkGrid;
|
||||
import codechicken.nei.bookmark.BookmarkGroup;
|
||||
import codechicken.nei.bookmark.BookmarkPayload;
|
||||
|
||||
public class CommandBookmarkAdd extends CommandBase {
|
||||
|
||||
private static final LRUCache<String, NBTTagCompound> PAYLOAD_CACHE = new LRUCache<>(64);
|
||||
|
||||
public static void cachePayload(String payloadId, NBTTagCompound payload) {
|
||||
if (payloadId == null || payloadId.isEmpty() || payload == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (PAYLOAD_CACHE) {
|
||||
PAYLOAD_CACHE.put(payloadId, (NBTTagCompound) payload.copy());
|
||||
}
|
||||
}
|
||||
|
||||
private static NBTTagCompound getCachedPayload(String payloadId) {
|
||||
synchronized (PAYLOAD_CACHE) {
|
||||
return PAYLOAD_CACHE.get(payloadId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "nei_bookmark";
|
||||
@@ -20,7 +41,7 @@ public class CommandBookmarkAdd extends CommandBase {
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender sender) {
|
||||
return "/nei_bookmark <nbt>";
|
||||
return "/nei_bookmark <payloadId>";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,19 +51,37 @@ public class CommandBookmarkAdd extends CommandBase {
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {
|
||||
|
||||
if (args.length < 1 || args[0].length() < 3) {
|
||||
sender.addChatMessage(new ChatComponentText("§c/nei_bookmark <nbt>"));
|
||||
if (args.length < 1) {
|
||||
sender.addChatMessage(new ChatComponentText("§c/nei_bookmark <payloadId>"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ItemPanels.bookmarkPanel.addItem(
|
||||
ItemStack.loadItemStackFromNBT((NBTTagCompound) JsonToNBT.func_150315_a(String.join(" ", args))));
|
||||
final String payloadId = String.join(" ", Arrays.copyOfRange(args, 0, args.length));
|
||||
final NBTTagCompound payload = getCachedPayload(payloadId);
|
||||
|
||||
if (payload == null) {
|
||||
throw new IllegalArgumentException("Missing cached payload");
|
||||
}
|
||||
|
||||
addBookmarkFromPayload((NBTTagCompound) payload.copy());
|
||||
sender.addChatMessage(new ChatComponentTranslation("nei.chat.bookmark_added.text"));
|
||||
} catch (NBTException e) {
|
||||
} catch (Exception e) {
|
||||
sender.addChatMessage(new ChatComponentText(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addBookmarkFromPayload(NBTTagCompound nbt) {
|
||||
final BookmarkPayload payload = BookmarkPayload.of(nbt);
|
||||
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
|
||||
final BookmarkGroup group = payload.getGroup();
|
||||
int groupId = BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
|
||||
if (group != null) {
|
||||
groupId = grid.addGroup(group);
|
||||
}
|
||||
|
||||
payload.getBookmarkItems(groupId).forEach(item -> grid.addItem(item, true));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.GuiUsageRecipe;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class HandlerDumper extends DataDumper {
|
||||
@@ -37,10 +36,7 @@ public class HandlerDumper extends DataDumper {
|
||||
LinkedList<String[]> list = new LinkedList<>();
|
||||
for (IRecipeHandler handler : GuiUsageRecipe.usagehandlers) {
|
||||
final String handlerName = handler.getHandlerId();
|
||||
final String handlerId = Objects.firstNonNull(
|
||||
handler instanceof TemplateRecipeHandler ? ((TemplateRecipeHandler) handler).getOverlayIdentifier()
|
||||
: null,
|
||||
"null");
|
||||
final String handlerId = Objects.firstNonNull(handler.getOverlayIdentifier(), "null");
|
||||
HandlerInfo info = GuiRecipeTab.getHandlerInfo(handlerName, handlerId);
|
||||
|
||||
list.add(
|
||||
|
||||
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user