mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-26 14:24:07 +00:00
Compare commits
148
Commits
2.8.73-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 | ||
|
|
841b73c6fe | ||
|
|
0c7eeebb1f | ||
|
|
65b56735a7 | ||
|
|
ff428bf538 | ||
|
|
a406021f26 | ||
|
|
8500ac57eb | ||
|
|
d97123ec13 | ||
|
|
434931bfac | ||
|
|
860011036b | ||
|
|
937c1858a4 | ||
|
|
4562ecf087 | ||
|
|
d621d5d5bd | ||
|
|
3175b316af | ||
|
|
61930ac7a8 | ||
|
|
fcbf1dcade | ||
|
|
3fa69e826c | ||
|
|
31fff21a55 | ||
|
|
2e9ab88692 | ||
|
|
a02d293d44 | ||
|
|
1265113390 | ||
|
|
4ffd614017 | ||
|
|
41554f4c5b | ||
|
|
2104184077 | ||
|
|
d5f25a4be8 | ||
|
|
ccbf94790a | ||
|
|
2f4f72a2d6 | ||
|
|
f8cec9a78c | ||
|
|
fb43a662eb | ||
|
|
b2e0b8878a | ||
|
|
374f019103 | ||
|
|
5dfcff6f30 | ||
|
|
bd17301cb9 | ||
|
|
96ccd4e109 | ||
|
|
b30ca57e68 | ||
|
|
8f949bc1d1 | ||
|
|
d25365942a | ||
|
|
eb58e1dabc | ||
|
|
f4601e7eed | ||
|
|
ba48806667 | ||
|
|
70c332796a | ||
|
|
bcc25d4369 | ||
|
|
10a3109714 | ||
|
|
b60769864f | ||
|
|
6bc899769f | ||
|
|
0ba14091fb | ||
|
|
6ed0be7961 | ||
|
|
d40b951cce | ||
|
|
ecdf7892ce | ||
|
|
1eaaac94bc | ||
|
|
e99167dddb | ||
|
|
34cb0dc43f | ||
|
|
acaad4bf91 | ||
|
|
3de75f4e26 | ||
|
|
47497090ea | ||
|
|
8b45a1757f |
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.
|
||||
+24
-2
@@ -1,12 +1,17 @@
|
||||
// Add your dependencies here
|
||||
configurations.create('gtnhlibEmbed') {
|
||||
canBeConsumed = false
|
||||
canBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.10:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.9.13: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')
|
||||
@@ -17,4 +22,21 @@ dependencies {
|
||||
|
||||
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
|
||||
|
||||
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(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/df211d3c3eefdc408b462041881bc575/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3426ffcaa54c3f62406beb1f1ab8b179/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/f636c800fdb3f9ae33f019dfa048ba72/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3cd7045fca9a72cd9bc7d14a385e594c/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/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.2.1-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/HEAD/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.19'
|
||||
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) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@@ -184,27 +185,33 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
if (itemStack != null && this.grid.indexOf(groupId, itemStack, recipeId, false) == -1) {
|
||||
final boolean existsRecipe = recipeId != null
|
||||
&& this.grid.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID);
|
||||
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(itemStack);
|
||||
long count = nbTag.getInteger("Count");
|
||||
long factor = existsRecipe ? Math.max(count, 1) : 1;
|
||||
final Recipe recipe = Recipe.of(recipeId);
|
||||
final BookmarkItem.Builder builder;
|
||||
|
||||
if (recipe != null) {
|
||||
builder = BookmarkItem.builder(groupId, itemStack, recipe, BookmarkItemType.RESULT).multiplier(1);
|
||||
} else if (recipeId != null) {
|
||||
builder = BookmarkItem.builder(groupId, itemStack).recipeId(recipeId)
|
||||
.factor(StackInfo.getAmount(itemStack)).multiplier(1);
|
||||
} else {
|
||||
builder = BookmarkItem.builder(groupId, itemStack).factor(1).multiplier(StackInfo.getAmount(itemStack));
|
||||
}
|
||||
|
||||
if (existsRecipe) {
|
||||
builder.type(BookmarkItemType.RESULT);
|
||||
|
||||
for (int i = 0; i < this.grid.size(); i++) {
|
||||
if (this.grid.getBookmarkItem(i).equalsRecipe(recipeId, groupId)) {
|
||||
count *= this.grid.getBookmarkItem(i).getMultiplier();
|
||||
builder.multiplier(this.grid.getBookmarkItem(i).getMultiplier());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
builder.type(BookmarkItemType.ITEM);
|
||||
}
|
||||
|
||||
this.grid.addItem(
|
||||
BookmarkItem.of(
|
||||
groupId,
|
||||
StackInfo.withAmount(itemStack, count),
|
||||
factor,
|
||||
recipeId,
|
||||
existsRecipe ? BookmarkItemType.RESULT : BookmarkItemType.ITEM),
|
||||
true);
|
||||
this.grid.addItem(builder.build(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -212,9 +219,11 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
}
|
||||
|
||||
public boolean addRecipe(Recipe recipe, int multiplier, int groupId) {
|
||||
if (recipe == null) return false;
|
||||
|
||||
final RecipeId recipeId = recipe.getRecipeId();
|
||||
|
||||
if (recipe != null && this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
|
||||
if (this.grid.getGroup(groupId) != null && !this.grid.existsRecipe(recipeId, groupId)) {
|
||||
this.grid.addRecipe(recipe, multiplier, groupId);
|
||||
return true;
|
||||
}
|
||||
@@ -245,15 +254,22 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
ItemStack stack = recipeId.getResult();
|
||||
|
||||
if (stack != null && uniqueRecipe.add(recipeId)) {
|
||||
this.grid.addItem(
|
||||
BookmarkItem
|
||||
.of(groupId, stack, StackInfo.getAmount(stack), recipeId, BookmarkItemType.ITEM),
|
||||
true);
|
||||
final Recipe recipe = Recipe.of(recipeId);
|
||||
final BookmarkItem.Builder builder;
|
||||
|
||||
if (recipe != null) {
|
||||
builder = BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT);
|
||||
} else {
|
||||
builder = BookmarkItem.builder(groupId, stack).recipeId(recipeId)
|
||||
.factor(StackInfo.getAmount(stack));
|
||||
}
|
||||
|
||||
this.grid.addItem(builder.multiplier(1).type(BookmarkItemType.ITEM).build(), true);
|
||||
uniqueStack.add(stack);
|
||||
}
|
||||
|
||||
} else {
|
||||
ItemStack stack = extractItem(item);
|
||||
final ItemStack stack = extractItem(item);
|
||||
|
||||
if (stack != null && !uniqueStack.contains(stack)) {
|
||||
this.grid.addItem(BookmarkItem.of(groupId, stack), true);
|
||||
@@ -510,8 +526,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle4i calculateBounds() {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
public Rectangle4i calculateBounds(GuiContainer gui) {
|
||||
final int maxWidth = (gui.width - 176) / 2 - PADDING * 2;
|
||||
final int maxHeight = gui.height - PADDING * 2;
|
||||
final int freeSpace = (gui.width - gui.xSize) / 2 - PADDING * 2;
|
||||
@@ -557,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);
|
||||
}
|
||||
|
||||
@@ -784,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"));
|
||||
}
|
||||
|
||||
@@ -888,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;
|
||||
}
|
||||
@@ -926,6 +944,17 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
|
||||
if (slot != null) {
|
||||
|
||||
if (slot.getBookmarkItem().chance != PositionedStack.CHANCE_FULL) {
|
||||
final String chanceText = NEIClientUtils
|
||||
.formatChance(slot.getBookmarkItem().chance / (float) PositionedStack.CHANCE_FULL);
|
||||
currenttip.add(
|
||||
1,
|
||||
EnumChatFormatting.GRAY + NEIClientUtils.translate(
|
||||
slot.getType() == BookmarkItemType.INGREDIENT ? "recipe.badge.chance.consume"
|
||||
: "recipe.badge.chance.output",
|
||||
chanceText));
|
||||
}
|
||||
|
||||
if (slot.getType() == BookmarkItemType.INGREDIENT && slot.getRecipeId() != null
|
||||
&& slot.getBookmarkItem().permutations.size() > 1) {
|
||||
RecipeId recipeId = slot.getRecipeId();
|
||||
@@ -994,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();
|
||||
@@ -1020,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;
|
||||
@@ -1070,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);
|
||||
}
|
||||
|
||||
@@ -1120,7 +1150,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
|
||||
for (BookmarkItem item : math.initialItems) {
|
||||
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
|
||||
final long amount = item.amount - invStackSize * item.fluidCellAmount;
|
||||
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount;
|
||||
if (amount > 0) {
|
||||
pullItemStacks.add(item.getItemStack(amount));
|
||||
}
|
||||
@@ -1134,7 +1164,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
|
||||
for (BookmarkItem item : math.initialItems) {
|
||||
final long invStackSize = inventory.getOrDefault(item.itemStack, 0L);
|
||||
final long strStackSize = containerStorage.getOrDefault(item.itemStack, 0L);
|
||||
final long amount = item.amount - invStackSize * item.fluidCellAmount
|
||||
final long amount = item.getAmount() - invStackSize * item.fluidCellAmount
|
||||
- strStackSize * item.fluidCellAmount;
|
||||
|
||||
if (amount > 0) {
|
||||
@@ -1268,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
|
||||
|
||||
@@ -57,6 +57,10 @@ public abstract class Button extends Widget {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getRenderLabelColor() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final String label;
|
||||
public Image icon;
|
||||
|
||||
|
||||
@@ -45,8 +45,10 @@ import codechicken.core.ClassDiscoverer;
|
||||
import codechicken.core.ClientUtils;
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
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;
|
||||
@@ -278,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();
|
||||
@@ -374,6 +377,7 @@ public class ClientHandler {
|
||||
} else if (event.gui == null) {
|
||||
/* prevent WorldClient reference being held in the Gui */
|
||||
NEIController.manager = null;
|
||||
GuiInfo.clearGuiHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.color.ColorResource;
|
||||
|
||||
public class ColorUtils {
|
||||
|
||||
private static final ColorResource.Factory color = new ColorResource.Factory("nei");
|
||||
|
||||
public static final ColorResource
|
||||
// spotless:off
|
||||
textGray = color.rgb("textGray", "0x404040"),
|
||||
textLightGray = color.rgb("textLightGray", "0x606060"),
|
||||
recipeTitle = color.rgb("recipeTitle", "0xFFFFFF"),
|
||||
recipeTitleHover = color.rgb("recipeTitleHover", "0xFFFF55"),
|
||||
subsetWidget = color.rgb("subsetWidget", "0xAA00AA"),
|
||||
recipeBadge = color.rgb("recipeBadge", "0xFDD835"),
|
||||
buttonLabelNormal = color.rgb("buttonLabelNormal", "0xE0E0E0"),
|
||||
buttonLabelHover = color.rgb("buttonLabelHover", "0xFFFFA0"),
|
||||
buttonLabelDisabled = color.rgb("buttonLabelDisabled", "0x601010"),
|
||||
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;
|
||||
@@ -135,6 +137,7 @@ public class FavoriteRecipes {
|
||||
|
||||
};
|
||||
|
||||
public static final String FAVORITE_ITEM = "FavoriteItem";
|
||||
private static final FavoriteStorage manualFavorites = new FavoriteStorage();
|
||||
private static FavoriteStorage autoFavorites = new FavoriteStorage();
|
||||
private static File favoriteFile;
|
||||
@@ -224,7 +227,7 @@ public class FavoriteRecipes {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RecipeHandlerRef.of(recipeId) != null) {
|
||||
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
|
||||
manualFavorites.add(stack, recipeId);
|
||||
} else {
|
||||
hasErrors = true;
|
||||
@@ -256,6 +259,20 @@ public class FavoriteRecipes {
|
||||
loadAutoGeneratedRecipes();
|
||||
}
|
||||
|
||||
public static RecipeId getGeneratedFavorite(ItemStack stack) {
|
||||
if (NEIClientConfig.favoritesEnabled() && stack != null) {
|
||||
return autoFavorites.getRecipeId(stack);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack getGeneratedFavorite(RecipeId recipeId) {
|
||||
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
|
||||
return autoFavorites.getItemStack(recipeId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static RecipeId getManualFavorite(ItemStack stack) {
|
||||
if (NEIClientConfig.favoritesEnabled() && stack != null) {
|
||||
return manualFavorites.getRecipeId(stack);
|
||||
@@ -299,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;
|
||||
}
|
||||
@@ -307,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;
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public class FormattedTextField extends GuiTextField {
|
||||
}
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
GL11.glColor4f(0.0F, 0.0F, 255.0F, 255.0F);
|
||||
GL11.glColor4f(0.0F, 0.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_COLOR_LOGIC_OP);
|
||||
GL11.glLogicOp(GL11.GL_OR_REVERSE);
|
||||
@@ -296,15 +296,26 @@ public class FormattedTextField extends GuiTextField {
|
||||
int shift = 0;
|
||||
|
||||
for (int i = 0; i < position; i++) {
|
||||
while (this.formattedText.length() > i + shift && this.formattedText.charAt(i + shift) == '\u00a7') {
|
||||
while (this.formattedText.length() > i + shift + 1 && this.formattedText.charAt(i + shift) == '\u00a7'
|
||||
&& isFormattingCode(this.formattedText.charAt(i + shift + 1))) {
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return position + shift;
|
||||
return Math.min(position + shift, this.formattedText.length());
|
||||
}
|
||||
|
||||
private static boolean isFormattingCode(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'k' && c <= 'o')
|
||||
|| c == 'r'
|
||||
|| (c >= 'A' && c <= 'F')
|
||||
|| (c >= 'K' && c <= 'O')
|
||||
|| c == 'R';
|
||||
}
|
||||
|
||||
private String getPreviousColor(int position) {
|
||||
position = Math.min(position, this.formattedText.length() - 1);
|
||||
while (position >= 0) {
|
||||
if (this.formattedText.charAt(position) == '\u00a7') {
|
||||
return this.formattedText.substring(position, position + 2);
|
||||
|
||||
@@ -24,8 +24,8 @@ public class GuiEnchantmentModifier extends GuiContainer {
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int par1, int par2) {
|
||||
fontRendererObj.drawString(translate("enchant"), 12, 6, 0x404040);
|
||||
fontRendererObj.drawString(translate("enchant.level"), 19, 20, 0x404040);
|
||||
fontRendererObj.drawString(translate("enchant"), 12, 6, ColorUtils.textGray.getColor());
|
||||
fontRendererObj.drawString(translate("enchant.level"), 19, 20, ColorUtils.textGray.getColor());
|
||||
}
|
||||
|
||||
protected void drawGuiContainerBackgroundLayer(float f, int i, int j) {
|
||||
@@ -39,7 +39,11 @@ public class GuiEnchantmentModifier extends GuiContainer {
|
||||
container.drawScrollBar(this);
|
||||
|
||||
String levelstring = "" + container.level;
|
||||
fontRendererObj.drawString(levelstring, 33 - fontRendererObj.getStringWidth(levelstring) / 2, 34, 0xFF606060);
|
||||
fontRendererObj.drawString(
|
||||
levelstring,
|
||||
33 - fontRendererObj.getStringWidth(levelstring) / 2,
|
||||
34,
|
||||
ColorUtils.textLightGray.getColor());
|
||||
|
||||
GL11.glTranslatef(-guiLeft, -guiTop, 0);
|
||||
}
|
||||
@@ -78,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,56 +7,51 @@ 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);
|
||||
}
|
||||
|
||||
protected int getTextColour(boolean mouseOver) {
|
||||
int color = 0xe0e0e0;
|
||||
|
||||
if (!enabled) {
|
||||
color = 0xffa0a0a0;
|
||||
return ColorUtils.buttonLabelInactive.getColor();
|
||||
} else if (mouseOver) {
|
||||
color = 0xffffa0;
|
||||
return ColorUtils.buttonLabelHover.getColor();
|
||||
}
|
||||
|
||||
return color;
|
||||
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);
|
||||
|
||||
|
||||
@@ -289,10 +289,10 @@ public class GuiPotionCreator extends GuiContainerWidget {
|
||||
CCRenderState.changeTexture("nei:textures/gui/potion.png");
|
||||
drawTexturedModalRect(0, 0, 0, 0, xSize, ySize);
|
||||
|
||||
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, 0x404040);
|
||||
fontRendererObj.drawString(translate("potion.duration"), 12, 40, 0x404040);
|
||||
fontRendererObj.drawString(translate("potion.level"), 19, 73, 0x404040);
|
||||
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, 0xFF606060);
|
||||
FontUtils.drawCenteredString(translate("potion.favourite"), xSize / 2, 4, ColorUtils.textGray.getColor());
|
||||
fontRendererObj.drawString(translate("potion.duration"), 12, 40, ColorUtils.textGray.getColor());
|
||||
fontRendererObj.drawString(translate("potion.level"), 19, 73, ColorUtils.textGray.getColor());
|
||||
FontUtils.drawCenteredString(translateAmplifier(amplifier), 33, 86, ColorUtils.textGray.getColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import net.minecraft.client.gui.GuiRepair;
|
||||
import net.minecraft.client.gui.inventory.GuiBrewingStand;
|
||||
import net.minecraft.client.gui.inventory.GuiCrafting;
|
||||
import net.minecraft.client.gui.inventory.GuiFurnace;
|
||||
import net.minecraft.client.gui.inventory.GuiInventory;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.recipe.BrewingOverlayHandler;
|
||||
import codechicken.nei.recipe.BrewingRecipeHandler;
|
||||
import codechicken.nei.recipe.DefaultOverlayHandler;
|
||||
import codechicken.nei.recipe.FireworkRecipeHandler;
|
||||
import codechicken.nei.recipe.FuelRecipeHandler;
|
||||
import codechicken.nei.recipe.FurnaceRecipeHandler;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.recipe.ProfilerRecipeHandler;
|
||||
import codechicken.nei.recipe.RepairOverlayHandler;
|
||||
import codechicken.nei.recipe.RepairRecipeHandler;
|
||||
import codechicken.nei.recipe.ShapedRecipeHandler;
|
||||
import codechicken.nei.recipe.ShapelessRecipeHandler;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
|
||||
public class IMCForNEI {
|
||||
|
||||
public static void IMCSender() {
|
||||
|
||||
API.registerRecipeHandler(new ShapedRecipeHandler());
|
||||
API.registerUsageHandler(new ShapedRecipeHandler());
|
||||
sendHandler("codechicken.nei.recipe.ShapedRecipeHandler", "minecraft:crafting_table");
|
||||
|
||||
API.registerRecipeHandler(new ShapelessRecipeHandler());
|
||||
API.registerUsageHandler(new ShapelessRecipeHandler());
|
||||
sendHandler("codechicken.nei.recipe.ShapelessRecipeHandler", "minecraft:crafting_table");
|
||||
|
||||
API.registerRecipeHandler(new FireworkRecipeHandler());
|
||||
API.registerUsageHandler(new FireworkRecipeHandler());
|
||||
sendHandler("codechicken.nei.recipe.FireworkRecipeHandler", "minecraft:fireworks");
|
||||
|
||||
API.registerRecipeHandler(new FurnaceRecipeHandler());
|
||||
API.registerUsageHandler(new FurnaceRecipeHandler());
|
||||
sendHandler("codechicken.nei.recipe.FurnaceRecipeHandler", "minecraft:furnace");
|
||||
|
||||
API.registerRecipeHandler(new BrewingRecipeHandler());
|
||||
API.registerUsageHandler(new BrewingRecipeHandler());
|
||||
sendHandler("codechicken.nei.recipe.BrewingRecipeHandler", "minecraft:brewing_stand");
|
||||
|
||||
API.registerRecipeHandler(new FuelRecipeHandler());
|
||||
API.registerUsageHandler(new FuelRecipeHandler());
|
||||
sendFuelRecipeHandler();
|
||||
|
||||
API.registerRecipeHandler(new InformationHandler());
|
||||
API.registerUsageHandler(new InformationHandler());
|
||||
sendInformationHandler();
|
||||
|
||||
API.registerRecipeHandler(new RepairRecipeHandler());
|
||||
API.registerUsageHandler(new RepairRecipeHandler());
|
||||
sendRepairHandler();
|
||||
sendCatalyst("repair", "minecraft:anvil");
|
||||
|
||||
API.registerGuiOverlay(GuiCrafting.class, "crafting");
|
||||
API.registerGuiOverlay(GuiInventory.class, "crafting2x2", 63, 20);
|
||||
API.registerGuiOverlay(GuiFurnace.class, "smelting");
|
||||
API.registerGuiOverlay(GuiFurnace.class, "fuel");
|
||||
API.registerGuiOverlay(GuiBrewingStand.class, "brewing");
|
||||
API.registerGuiOverlay(GuiRepair.class, "repair", 2, 37);
|
||||
|
||||
API.registerGuiOverlayHandler(GuiCrafting.class, new DefaultOverlayHandler(), "crafting");
|
||||
API.registerGuiOverlayHandler(GuiInventory.class, new DefaultOverlayHandler(63, 20), "crafting2x2");
|
||||
API.registerGuiOverlayHandler(GuiBrewingStand.class, new BrewingOverlayHandler(), "brewing");
|
||||
API.registerGuiOverlayHandler(GuiRepair.class, new RepairOverlayHandler(), "repair");
|
||||
|
||||
API.registerRecipeHandler(new ProfilerRecipeHandler(true));
|
||||
API.registerUsageHandler(new ProfilerRecipeHandler(false));
|
||||
sendProfilerRecipeHandler();
|
||||
}
|
||||
|
||||
private static void sendHandler(String aName, String aBlock) {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handler", aName);
|
||||
aNBT.setString("modName", "Minecraft");
|
||||
aNBT.setString("modId", "minecraft");
|
||||
aNBT.setString("itemName", aBlock);
|
||||
aNBT.setInteger("handlerWidth", 166);
|
||||
aNBT.setInteger("handlerHeight", 65);
|
||||
aNBT.setBoolean("multipleWidgetsAllowed", true);
|
||||
aNBT.setInteger("yShift", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
|
||||
}
|
||||
|
||||
private static void sendRepairHandler() {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handler", "codechicken.nei.recipe.RepairRecipeHandler");
|
||||
aNBT.setString("modName", "Minecraft");
|
||||
aNBT.setString("modId", "minecraft");
|
||||
aNBT.setString("itemName", "minecraft:anvil");
|
||||
aNBT.setInteger("handlerHeight", 39);
|
||||
aNBT.setInteger("handlerWidth", 166);
|
||||
aNBT.setBoolean("multipleWidgetsAllowed", true);
|
||||
aNBT.setBoolean("showFavoritesButton", false);
|
||||
aNBT.setInteger("yShift", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
|
||||
}
|
||||
|
||||
private static void sendFuelRecipeHandler() {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handler", "codechicken.nei.recipe.FuelRecipeHandler");
|
||||
aNBT.setString("modName", "Minecraft");
|
||||
aNBT.setString("modId", "minecraft");
|
||||
aNBT.setString("imageResource", "nei:textures/nei_tabbed_sprites.png");
|
||||
aNBT.setInteger("imageX", 80);
|
||||
aNBT.setInteger("imageY", 0);
|
||||
aNBT.setInteger("imageWidth", 14);
|
||||
aNBT.setInteger("imageHeight", 14);
|
||||
aNBT.setInteger("handlerHeight", 65);
|
||||
aNBT.setInteger("handlerWidth", 166);
|
||||
aNBT.setBoolean("multipleWidgetsAllowed", true);
|
||||
aNBT.setInteger("yShift", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
|
||||
}
|
||||
|
||||
private static void sendInformationHandler() {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handler", "codechicken.nei.recipe.InformationHandler");
|
||||
aNBT.setString("modName", "Not Enough Items");
|
||||
aNBT.setString("modId", "nei");
|
||||
aNBT.setString("imageResource", "nei:textures/nei_tabbed_sprites.png");
|
||||
aNBT.setInteger("imageX", 96);
|
||||
aNBT.setInteger("imageY", 0);
|
||||
aNBT.setInteger("imageWidth", 16);
|
||||
aNBT.setInteger("imageHeight", 16);
|
||||
aNBT.setInteger("handlerHeight", 65);
|
||||
aNBT.setInteger("handlerWidth", 166);
|
||||
aNBT.setBoolean("multipleWidgetsAllowed", true);
|
||||
aNBT.setInteger("yShift", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
|
||||
}
|
||||
|
||||
private static void sendProfilerRecipeHandler() {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handler", "codechicken.nei.recipe.ProfilerRecipeHandler");
|
||||
aNBT.setString("modName", "Not Enough Items");
|
||||
aNBT.setString("modId", "nei");
|
||||
aNBT.setString("itemName", "minecraft:clock");
|
||||
aNBT.setInteger("handlerHeight", 16);
|
||||
aNBT.setInteger("handlerWidth", 166);
|
||||
aNBT.setBoolean("multipleWidgetsAllowed", true);
|
||||
aNBT.setInteger("yShift", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", aNBT);
|
||||
}
|
||||
|
||||
private static void sendCatalyst(String aName, String aStack) {
|
||||
NBTTagCompound aNBT = new NBTTagCompound();
|
||||
aNBT.setString("handlerID", aName);
|
||||
aNBT.setString("itemName", aStack);
|
||||
aNBT.setInteger("priority", 0);
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "registerCatalystInfo", aNBT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,38 +2,24 @@ package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.fluids.IFluidContainerItem;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
||||
import codechicken.nei.ThreadOperationTimer.TimeoutException;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.search.TooltipFilter;
|
||||
import codechicken.nei.util.ItemUntranslator;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
public class ItemList {
|
||||
|
||||
@@ -51,9 +37,6 @@ public class ItemList {
|
||||
public static final List<ItemFilterProvider> itemFilterers = new LinkedList<>();
|
||||
public static final List<ItemsLoadedCallback> loadCallbacks = new LinkedList<>();
|
||||
|
||||
private static final HashSet<Item> erroredItems = new HashSet<>();
|
||||
private static final HashSet<String> stackTraces = new HashSet<>();
|
||||
private static Map<ItemStack, Integer> ordering = new HashMap<>();
|
||||
/**
|
||||
* Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not.
|
||||
*/
|
||||
@@ -115,7 +98,15 @@ public class ItemList {
|
||||
displayName = EnumChatFormatting
|
||||
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
|
||||
|
||||
return displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find();
|
||||
if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (String alias : ItemInfo.getAliases(item)) {
|
||||
if (this.pattern.matcher(StatCollector.translateToLocal(alias)).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -216,229 +207,6 @@ public class ItemList {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
|
||||
|
||||
private void damageSearch(Item item, List<ItemStack> permutations) {
|
||||
HashSet<String> damageIconSet = new HashSet<>();
|
||||
for (int damage = 0; damage < 16; damage++) try {
|
||||
ItemStack itemstack = new ItemStack(item, 1, damage);
|
||||
IIcon icon = item.getIconIndex(itemstack);
|
||||
String name = getTooltip(itemstack);
|
||||
String s = name + "@" + (icon == null ? 0 : icon.hashCode());
|
||||
if (damageIconSet.add(s)) {
|
||||
permutations.add(itemstack);
|
||||
}
|
||||
} catch (TimeoutException t) {
|
||||
throw t;
|
||||
} catch (Throwable t) {
|
||||
NEIServerUtils.logOnce(
|
||||
t,
|
||||
stackTraces,
|
||||
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
|
||||
item.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String getTooltip(ItemStack stack) {
|
||||
try {
|
||||
return String.join("\n", stack.getTooltip(Minecraft.getMinecraft().thePlayer, false));
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private void updateOrdering(List<ItemStack> items) {
|
||||
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
|
||||
ItemSorter.sort(items);
|
||||
|
||||
if (!CollapsibleItems.isEmpty()) {
|
||||
final HashMap<Integer, Integer> groups = new HashMap<>();
|
||||
int orderIndex = 0;
|
||||
|
||||
for (ItemStack stack : items) {
|
||||
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
|
||||
|
||||
if (groupIndex == -1) {
|
||||
newOrdering.put(stack, orderIndex++);
|
||||
} else {
|
||||
|
||||
if (!groups.containsKey(groupIndex)) {
|
||||
groups.put(groupIndex, orderIndex++);
|
||||
}
|
||||
|
||||
newOrdering.put(stack, groups.get(groupIndex));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int orderIndex = 0;
|
||||
|
||||
for (ItemStack stack : items) {
|
||||
newOrdering.put(stack, orderIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
ItemList.ordering = newOrdering;
|
||||
}
|
||||
|
||||
private List<ItemStack> getPermutations(Item item) {
|
||||
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
|
||||
|
||||
if (permutations.isEmpty()) {
|
||||
item.getSubItems(item, null, permutations);
|
||||
}
|
||||
|
||||
if (permutations.isEmpty()) {
|
||||
damageSearch(item, permutations);
|
||||
}
|
||||
|
||||
permutations.addAll(ItemInfo.itemVariants.get(item));
|
||||
|
||||
return permutations.stream()
|
||||
.filter(
|
||||
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
|
||||
&& !ItemInfo.isHidden(stack))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void runChecked(ItemStack stack, Runnable action, String reason) {
|
||||
int hashOld = 0;
|
||||
if (stack.hasTagCompound()) {
|
||||
hashOld = stack.stackTagCompound.hashCode();
|
||||
}
|
||||
|
||||
action.run();
|
||||
|
||||
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
|
||||
FMLLog.warning(
|
||||
"NEI: Forced tag update with reason (" + reason
|
||||
+ ") for "
|
||||
+ stack
|
||||
+ "("
|
||||
+ stack.getItem()
|
||||
+ ")");
|
||||
}
|
||||
}
|
||||
|
||||
private void forceTagCompoundInitialization(ItemStack stack) {
|
||||
Item item = stack.getItem();
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
if (item instanceof IFluidContainerItem) {
|
||||
IFluidContainerItem fluidItem = (IFluidContainerItem) item;
|
||||
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
|
||||
}
|
||||
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
|
||||
runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
|
||||
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
|
||||
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
|
||||
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
|
||||
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
|
||||
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
|
||||
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
|
||||
runChecked(stack, () -> item.getDigSpeed(stack, Blocks.stone, 0), "getDigSpeed");
|
||||
|
||||
/*
|
||||
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
|
||||
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
|
||||
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack,
|
||||
* () -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
|
||||
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
|
||||
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
|
||||
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, ()
|
||||
* -> item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () ->
|
||||
* item.getSmeltingExperience(stack), "getSmeltingExperience"); runChecked(stack, () ->
|
||||
* item.getMaxDamage(stack), "getMaxDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
* runChecked(stack, stack::isItemDamaged, "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack,
|
||||
* 0), "hasEffect"); runChecked(stack, () -> item.getItemEnchantability(stack), "getItemEnchantability");
|
||||
* runChecked(stack, () -> item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () ->
|
||||
* item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
* runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
*/
|
||||
}
|
||||
|
||||
// Generate itemlist, permutations, orders, collapsibleitems, and informationhandler stacks
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute() {
|
||||
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
|
||||
|
||||
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
|
||||
LayoutManager.itemsLoaded = true;
|
||||
loadFinished = false;
|
||||
|
||||
SearchField.searchParser.clearCache();
|
||||
ItemSorter.instance.ordering.clear();
|
||||
CollapsibleItems.clearCache();
|
||||
TooltipFilter.clearCache();
|
||||
InformationHandler.clearCache();
|
||||
|
||||
List<ItemStack> items = new ArrayList<>();
|
||||
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
|
||||
ItemStackSet unique = new ItemStackSet();
|
||||
|
||||
StreamSupport.stream(((Iterable<Item>) Item.itemRegistry).spliterator(), true).forEach(item -> {
|
||||
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
|
||||
|
||||
try {
|
||||
timer.reset(item);
|
||||
List<ItemStack> permutations = getPermutations(item);
|
||||
timer.reset();
|
||||
|
||||
for (ItemStack stack : permutations) {
|
||||
if (!unique.contains(stack)) {
|
||||
|
||||
synchronized (unique) {
|
||||
unique.add(stack);
|
||||
}
|
||||
|
||||
synchronized (items) {
|
||||
items.add(stack);
|
||||
}
|
||||
|
||||
forceTagCompoundInitialization(stack);
|
||||
|
||||
CollapsibleItems.putItem(stack);
|
||||
TooltipFilter.getSearchTooltip(stack);
|
||||
InformationHandler.populateStacks(stack);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (itemMap) {
|
||||
itemMap.putAll(item, permutations);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
NEIServerConfig.logger.error("Removing item: {} from list.", item, t);
|
||||
erroredItems.add(item);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
int index = 0;
|
||||
for (Item item : (Iterable<Item>) Item.itemRegistry) {
|
||||
for (ItemStack stack : itemMap.get(item)) {
|
||||
ItemSorter.instance.ordering.put(stack, index++);
|
||||
}
|
||||
}
|
||||
|
||||
if (interrupted()) return;
|
||||
ItemList.items = items;
|
||||
ItemList.itemMap = itemMap;
|
||||
for (ItemsLoadedCallback callback : loadCallbacks) callback.itemsLoaded();
|
||||
|
||||
if (interrupted()) return;
|
||||
updateOrdering(ItemList.items);
|
||||
|
||||
loadFinished = true;
|
||||
|
||||
FavoriteRecipes.loadAutoGeneratedRecipes();
|
||||
SubsetWidget.updateHiddenItems();
|
||||
ItemPanels.bookmarkPanel.load();
|
||||
updateFilter.restart();
|
||||
}
|
||||
};
|
||||
|
||||
public static ForkJoinPool forkJoinPool;
|
||||
|
||||
static {
|
||||
@@ -458,35 +226,12 @@ public class ItemList {
|
||||
forkJoinPool = new ForkJoinPool(poolSize, factory, null, false);
|
||||
}
|
||||
|
||||
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
|
||||
// The item list pipeline lives in ItemListLoader; these references are kept for backward compatibility.
|
||||
public static final RestartableTask loadItems = ItemListLoader.loadItems;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public static final RestartableTask refreshItems = ItemListLoader.refreshItems;
|
||||
|
||||
if (!loadFinished) return;
|
||||
ItemFilter filter = getItemListFilter();
|
||||
ArrayList<ItemStack> filtered;
|
||||
|
||||
try {
|
||||
filtered = ItemList.forkJoinPool.submit(
|
||||
() -> items.parallelStream().filter(filter::matches)
|
||||
.collect(Collectors.toCollection(ArrayList::new)))
|
||||
.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
filtered = new ArrayList<>();
|
||||
e.printStackTrace();
|
||||
stop();
|
||||
}
|
||||
|
||||
if (interrupted()) return;
|
||||
|
||||
filtered.sort(Comparator.comparingInt(ItemList.ordering::get));
|
||||
|
||||
if (interrupted()) return;
|
||||
|
||||
ItemPanel.updateItemList(filtered);
|
||||
}
|
||||
};
|
||||
public static final RestartableTask updateFilter = ItemListLoader.updateFilter;
|
||||
|
||||
/**
|
||||
* @deprecated Use updateFilter.restart()
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.fluids.IFluidContainerItem;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
||||
import codechicken.nei.ItemList.ItemsLoadedCallback;
|
||||
import codechicken.nei.ThreadOperationTimer.TimeoutException;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.RepairRecipeHandler;
|
||||
import codechicken.nei.search.TooltipFilter;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
public class ItemListLoader {
|
||||
|
||||
private static final HashSet<Item> erroredItems = new HashSet<>();
|
||||
private static final HashSet<String> stackTraces = new HashSet<>();
|
||||
private static Map<ItemStack, Integer> ordering = new HashMap<>();
|
||||
|
||||
private ItemListLoader() {}
|
||||
|
||||
private static void damageSearch(Item item, List<ItemStack> permutations) {
|
||||
final Set<String> damageIconSet = new HashSet<>();
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
|
||||
for (int damage = 0; damage < 16; damage++) {
|
||||
try {
|
||||
final ItemStack itemstack = new ItemStack(item, 1, damage);
|
||||
final IIcon icon = item.getIconIndex(itemstack);
|
||||
final String key = itemstack.getDisplayName() + "@"
|
||||
+ (icon == null ? 0 : icon.hashCode())
|
||||
+ "@"
|
||||
+ getInformation(itemstack, player);
|
||||
|
||||
if (damageIconSet.add(key)) {
|
||||
permutations.add(itemstack);
|
||||
}
|
||||
} catch (TimeoutException t) {
|
||||
throw t;
|
||||
} catch (Throwable t) {
|
||||
NEIServerUtils.logOnce(
|
||||
t,
|
||||
stackTraces,
|
||||
"Ommiting " + item + ":" + damage + " " + item.getClass().getSimpleName(),
|
||||
item.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getInformation(ItemStack stack, EntityPlayer player) {
|
||||
try {
|
||||
final List<String> tooltip = new ArrayList<>();
|
||||
stack.getItem().addInformation(stack, player, tooltip, false);
|
||||
return String.join("\n", tooltip);
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static void updateOrdering(List<ItemStack> items) {
|
||||
final Map<ItemStack, Integer> newOrdering = new HashMap<>();
|
||||
|
||||
if (!CollapsibleItems.isEmpty()) {
|
||||
final HashMap<Integer, Integer> groups = new HashMap<>();
|
||||
int orderIndex = 0;
|
||||
|
||||
for (ItemStack stack : items) {
|
||||
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
|
||||
|
||||
if (groupIndex == -1) {
|
||||
newOrdering.put(stack, orderIndex++);
|
||||
} else {
|
||||
|
||||
if (!groups.containsKey(groupIndex)) {
|
||||
groups.put(groupIndex, orderIndex++);
|
||||
}
|
||||
|
||||
newOrdering.put(stack, groups.get(groupIndex));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int orderIndex = 0;
|
||||
|
||||
for (ItemStack stack : items) {
|
||||
newOrdering.put(stack, orderIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
ordering = newOrdering;
|
||||
}
|
||||
|
||||
private static List<ItemStack> getPermutations(Item item) {
|
||||
final List<ItemStack> permutations = new LinkedList<>(ItemInfo.itemOverrides.get(item));
|
||||
|
||||
if (permutations.isEmpty()) {
|
||||
item.getSubItems(item, null, permutations);
|
||||
}
|
||||
|
||||
if (permutations.isEmpty()) {
|
||||
damageSearch(item, permutations);
|
||||
}
|
||||
|
||||
permutations.addAll(ItemInfo.itemVariants.get(item));
|
||||
|
||||
return permutations.stream()
|
||||
.filter(
|
||||
stack -> stack.getItem() != null && stack.getItem().delegate.name() != null
|
||||
&& !ItemInfo.isHidden(stack))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static void runChecked(ItemStack stack, Runnable action, String reason) {
|
||||
int hashOld = 0;
|
||||
if (stack.hasTagCompound()) {
|
||||
hashOld = stack.stackTagCompound.hashCode();
|
||||
}
|
||||
|
||||
action.run();
|
||||
|
||||
if (stack.hasTagCompound() && hashOld != stack.stackTagCompound.hashCode()) {
|
||||
FMLLog.warning(
|
||||
"NEI: Forced tag update with reason (" + reason + ") for " + stack + "(" + stack.getItem() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static void forceTagCompoundInitialization(ItemStack stack) {
|
||||
Item item = stack.getItem();
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
if (item instanceof IFluidContainerItem fluidItem) {
|
||||
runChecked(stack, () -> fluidItem.getFluid(stack), "getFluid");
|
||||
}
|
||||
runChecked(stack, () -> item.isDamaged(stack), "isDamaged");
|
||||
runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
runChecked(stack, () -> item.showDurabilityBar(stack), "showDurabilityBar");
|
||||
runChecked(stack, () -> item.getAttributeModifiers(stack), "getAttributeModifiers");
|
||||
runChecked(stack, () -> item.getDurabilityForDisplay(stack), "getDurabilityForDisplay");
|
||||
runChecked(stack, () -> item.getItemStackLimit(stack), "getItemStackLimit");
|
||||
runChecked(stack, () -> item.getToolClasses(stack), "getToolClasses");
|
||||
runChecked(stack, () -> item.getUnlocalizedNameInefficiently(stack), "getUnlocalizedNameInefficiently");
|
||||
runChecked(stack, () -> item.hasEffect(stack), "hasEffect");
|
||||
runChecked(stack, () -> item.getDigSpeed(stack, Blocks.stone, 0), "getDigSpeed");
|
||||
|
||||
/*
|
||||
* unused... for now runChecked(stack, () -> GameRegistry.getFuelValue(stack), "getFuelValue");
|
||||
* runChecked(stack, () -> item.getUnlocalizedName(stack), "getUnlocalizedName"); runChecked(stack, () ->
|
||||
* item.doesContainerItemLeaveCraftingGrid(stack), "doesContainerItemLeaveCraftingGrid"); runChecked(stack, ()
|
||||
* -> item.getItemUseAction(stack), "getItemUseAction"); runChecked(stack, () ->
|
||||
* item.getMaxItemUseDuration(stack), "getMaxItemUseDuration"); runChecked(stack, () ->
|
||||
* item.getPotionEffect(stack), "getPotionEffect"); runChecked(stack, () -> item.isPotionIngredient(stack),
|
||||
* "isPotionIngredient"); runChecked(stack, () -> item.getRarity(stack), "getRarity"); runChecked(stack, () ->
|
||||
* item.hasCustomEntity(stack), "hasCustomEntity"); runChecked(stack, () -> item.getSmeltingExperience(stack),
|
||||
* "getSmeltingExperience"); runChecked(stack, () -> item.getMaxDamage(stack), "getMaxDamage");
|
||||
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, stack::isItemDamaged,
|
||||
* "isItemDamaged"); runChecked(stack, () -> item.hasEffect(stack, 0), "hasEffect"); runChecked(stack, () ->
|
||||
* item.getItemEnchantability(stack), "getItemEnchantability"); runChecked(stack, () ->
|
||||
* item.isBeaconPayment(stack), "isBeaconPayment"); runChecked(stack, () -> item.getDamage(stack), "getDamage");
|
||||
* runChecked(stack, () -> item.getDamage(stack), "getDamage"); runChecked(stack, () -> item.getDamage(stack),
|
||||
* "getDamage");
|
||||
*/
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<ItemStack> gatherItems(ThreadOperationTimer timer) {
|
||||
ItemSorter.instance.ordering.clear();
|
||||
|
||||
List<ItemStack> items = new ArrayList<>();
|
||||
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
|
||||
ItemStackSet unique = new ItemStackSet();
|
||||
|
||||
final List<Item> registryItems = new ArrayList<>();
|
||||
for (Item item : (Iterable<Item>) Item.itemRegistry) registryItems.add(item);
|
||||
|
||||
registryItems.parallelStream().forEach(item -> {
|
||||
if (item == null || item.delegate.name() == null || erroredItems.contains(item)) return;
|
||||
|
||||
try {
|
||||
timer.reset(item);
|
||||
List<ItemStack> permutations = getPermutations(item);
|
||||
timer.reset();
|
||||
|
||||
synchronized (itemMap) {
|
||||
itemMap.putAll(item, permutations);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
NEIServerConfig.logger.error(
|
||||
"Removing item: {} from list.\n{}: {}",
|
||||
item,
|
||||
t.getMessage(),
|
||||
Throwables.getStackTraceAsString(t));
|
||||
erroredItems.add(item);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
int index = 0;
|
||||
for (Item item : (Iterable<Item>) Item.itemRegistry) {
|
||||
for (ItemStack stack : itemMap.get(item)) {
|
||||
ItemSorter.instance.ordering.put(stack, index++);
|
||||
|
||||
if (!unique.contains(stack)) {
|
||||
unique.add(stack);
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemList.itemMap = itemMap;
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static void buildItemCaches(List<ItemStack> items) {
|
||||
SearchField.searchParser.clearCache();
|
||||
TooltipFilter.clearCache();
|
||||
InformationHandler.clearCache();
|
||||
|
||||
items.parallelStream().forEach(stack -> {
|
||||
try {
|
||||
forceTagCompoundInitialization(stack);
|
||||
InformationHandler.populateStacks(stack);
|
||||
} catch (Throwable t) {
|
||||
NEIServerConfig.logger.error(
|
||||
"Failed to build caches for: {}\n{}: {}",
|
||||
stack,
|
||||
t.getMessage(),
|
||||
Throwables.getStackTraceAsString(t));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void indexCollapsibleItems(List<ItemStack> items) {
|
||||
CollapsibleItems.clearCache();
|
||||
items.parallelStream().forEach(CollapsibleItems::putItem);
|
||||
}
|
||||
|
||||
public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (!NEIClientConfig.isEnabled() || NEIClientUtils.mc().thePlayer == null) return;
|
||||
|
||||
final ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
|
||||
LayoutManager.itemsLoaded = true;
|
||||
ItemList.loadFinished = false;
|
||||
warmTooltips.stop();
|
||||
|
||||
List<ItemStack> items = gatherItems(timer);
|
||||
|
||||
if (interrupted()) return;
|
||||
buildItemCaches(items);
|
||||
indexCollapsibleItems(items);
|
||||
|
||||
if (interrupted()) return;
|
||||
ItemSorter.sort(items);
|
||||
|
||||
if (interrupted()) return;
|
||||
ItemList.items = items;
|
||||
for (ItemsLoadedCallback callback : ItemList.loadCallbacks) callback.itemsLoaded();
|
||||
|
||||
if (interrupted()) return;
|
||||
updateOrdering(ItemList.items);
|
||||
|
||||
ItemList.loadFinished = true;
|
||||
|
||||
RepairRecipeHandler.findRepairRecipesOnceParallel();
|
||||
RecipeHandlerRef.clearCache();
|
||||
FavoriteRecipes.reload();
|
||||
SubsetWidget.updateHiddenItems();
|
||||
updateFilter.restart();
|
||||
warmTooltips.restart();
|
||||
loadBookmarks.restart();
|
||||
loadHistory.restart();
|
||||
}
|
||||
};
|
||||
|
||||
public static final RestartableTask warmTooltips = new RestartableTask("NEI Tooltip Caching") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (!ItemList.loadFinished) return;
|
||||
// Use the common pool, not ItemList.forkJoinPool, so it does not queue behind updateFilter.
|
||||
ItemList.items.parallelStream().forEach(stack -> {
|
||||
if (interrupted()) return;
|
||||
try {
|
||||
TooltipFilter.getSearchTooltip(stack);
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static final RestartableTask loadBookmarks = new RestartableTask("NEI Bookmark Loading") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ItemPanels.bookmarkPanel.load();
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
public void execute() {
|
||||
if (!ItemList.loadFinished) return;
|
||||
|
||||
final List<ItemStack> items = new ArrayList<>(ItemList.items);
|
||||
|
||||
indexCollapsibleItems(items);
|
||||
|
||||
if (interrupted()) return;
|
||||
ItemSorter.sort(items);
|
||||
|
||||
if (interrupted()) return;
|
||||
ItemList.items = items;
|
||||
updateOrdering(ItemList.items);
|
||||
|
||||
FavoriteRecipes.reload();
|
||||
SubsetWidget.updateHiddenItems();
|
||||
updateFilter.restart();
|
||||
loadBookmarks.restart();
|
||||
loadHistory.restart();
|
||||
}
|
||||
};
|
||||
|
||||
public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
if (!ItemList.loadFinished) return;
|
||||
ItemFilter filter = ItemList.getItemListFilter();
|
||||
ArrayList<ItemStack> filtered;
|
||||
|
||||
try {
|
||||
filtered = ItemList.forkJoinPool.submit(
|
||||
() -> ItemList.items.parallelStream().filter(filter::matches)
|
||||
.collect(Collectors.toCollection(ArrayList::new)))
|
||||
.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
filtered = new ArrayList<>();
|
||||
e.printStackTrace();
|
||||
stop();
|
||||
}
|
||||
|
||||
if (interrupted()) return;
|
||||
|
||||
filtered.sort(Comparator.comparingInt(ordering::get));
|
||||
|
||||
if (interrupted()) return;
|
||||
|
||||
ItemPanel.updateItemList(filtered);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -122,8 +116,7 @@ public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle4i calculateBounds() {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
public Rectangle4i calculateBounds(GuiContainer gui) {
|
||||
final int maxWidth = (gui.width - 176) / 2 - PADDING * 2;
|
||||
final int maxHeight = gui.height - PADDING * 2;
|
||||
final int freeSpace = (gui.width - gui.xSize) / 2 - PADDING * 2;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -15,9 +15,11 @@ import net.minecraft.client.shader.Framebuffer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
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;
|
||||
@@ -27,7 +29,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
protected static class ScreenCapture {
|
||||
|
||||
private long nextCacheRefresh = 0;
|
||||
private Framebuffer framebuffer;
|
||||
private final Framebuffer framebuffer;
|
||||
|
||||
public ScreenCapture() {
|
||||
this.framebuffer = new Framebuffer(1, 1, true);
|
||||
@@ -51,7 +53,7 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
}
|
||||
|
||||
public void captureScreen(Runnable callback, int mode) {
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
|
||||
resetFramebuffer();
|
||||
this.framebuffer.bindFramebuffer(false);
|
||||
@@ -84,6 +86,21 @@ public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends Ite
|
||||
} else {
|
||||
this.framebuffer.framebufferClear();
|
||||
}
|
||||
|
||||
// Copy stencil values (required by mui2)
|
||||
OpenGlHelper.func_153171_g(GL30.GL_READ_FRAMEBUFFER, minecraft.getFramebuffer().framebufferObject);
|
||||
OpenGlHelper.func_153171_g(GL30.GL_DRAW_FRAMEBUFFER, this.framebuffer.framebufferObject);
|
||||
GL30.glBlitFramebuffer(
|
||||
0,
|
||||
0,
|
||||
minecraft.displayWidth,
|
||||
minecraft.displayHeight,
|
||||
0,
|
||||
0,
|
||||
minecraft.displayWidth,
|
||||
minecraft.displayHeight,
|
||||
GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT,
|
||||
GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
public void renderCapturedScreen() {
|
||||
@@ -332,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) {
|
||||
@@ -380,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);
|
||||
}
|
||||
|
||||
@@ -393,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,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) {
|
||||
@@ -416,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;
|
||||
@@ -66,9 +67,9 @@ import codechicken.nei.guihook.IContainerInputHandler;
|
||||
import codechicken.nei.guihook.IContainerObjectHandler;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
import codechicken.nei.recipe.DebugHandlerWidget;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.RecipeCatalysts;
|
||||
import codechicken.nei.recipe.debug.DebugHandlerWidget;
|
||||
|
||||
public class LayoutManager implements IContainerInputHandler, IContainerTooltipHandler, IContainerDrawHandler,
|
||||
IContainerObjectHandler, IKeyStateTracker {
|
||||
@@ -92,7 +93,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
public static SubsetWidget dropDown;
|
||||
public static SearchField searchField;
|
||||
public static ItemZoom itemZoom;
|
||||
public static DebugHandlerWidget recipeHandlerWidget;
|
||||
|
||||
public static ButtonCycled options;
|
||||
public static ButtonCycled bookmarksButton;
|
||||
@@ -119,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());
|
||||
|
||||
@@ -225,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;
|
||||
@@ -406,8 +419,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
itemZoom = new ItemZoom();
|
||||
|
||||
recipeHandlerWidget = new DebugHandlerWidget();
|
||||
|
||||
dropDown = new SubsetWidget();
|
||||
searchField = new SearchField("search");
|
||||
API.addItemFilter(searchField);
|
||||
@@ -786,8 +797,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
addWidget(dropDown);
|
||||
}
|
||||
|
||||
addWidget(recipeHandlerWidget);
|
||||
recipeHandlerWidget.setVisible();
|
||||
addWidget(DebugHandlerWidget.instance);
|
||||
}
|
||||
|
||||
public static LayoutStyle getLayoutStyle(int id) {
|
||||
@@ -811,6 +821,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
AutoFocusWidget.instance.guiTick();
|
||||
|
||||
for (Widget widget : drawWidgets) widget.update();
|
||||
}
|
||||
|
||||
@@ -911,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() {
|
||||
@@ -945,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
|
||||
@@ -244,7 +239,11 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
|
||||
Image icon = b.getRenderIcon();
|
||||
if (icon == null) {
|
||||
int colour = tex == 2 ? 0xffffa0 : tex == 0 ? 0x601010 : 0xe0e0e0;
|
||||
int labelColor = b.getRenderLabelColor();
|
||||
int colour = labelColor != 0 ? labelColor
|
||||
: tex == 2 ? ColorUtils.buttonLabelHover.getColor()
|
||||
: tex == 0 ? ColorUtils.buttonLabelDisabled.getColor()
|
||||
: ColorUtils.buttonLabelNormal.getColor();
|
||||
|
||||
drawStringC(b.getRenderLabel(), b.x + b.w / 2, b.y + (b.h - 8) / 2, colour);
|
||||
} else {
|
||||
|
||||
@@ -54,6 +54,7 @@ public class NEIActions {
|
||||
addAction("magnet", true);
|
||||
addAction("enchant", true);
|
||||
addAction("potion", true);
|
||||
addAction("autocraft");
|
||||
|
||||
canDisable.add("dawn");
|
||||
canDisable.add("noon");
|
||||
|
||||
@@ -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;
|
||||
@@ -56,14 +55,11 @@ import codechicken.nei.config.OptionToggleButtonBoubs;
|
||||
import codechicken.nei.config.OptionUtilities;
|
||||
import codechicken.nei.config.preset.GuiPresetList;
|
||||
import codechicken.nei.event.NEIConfigsLoadedEvent;
|
||||
import codechicken.nei.recipe.DebugHandlerWidget;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.InformationHandler;
|
||||
import codechicken.nei.recipe.RecipeCatalysts;
|
||||
import codechicken.nei.recipe.RecipeInfo;
|
||||
import codechicken.nei.util.ItemUntranslator;
|
||||
import codechicken.nei.util.NEIKeyboardUtils;
|
||||
import codechicken.obfuscator.ObfuscationRun;
|
||||
|
||||
public class NEIClientConfig {
|
||||
@@ -149,6 +145,7 @@ public class NEIClientConfig {
|
||||
API.addOption(new OptionToggleButton("inventory.widgetsenabled"));
|
||||
|
||||
tag.getTag("findFuelsParallel").getBooleanValue(true);
|
||||
tag.getTag("showRepairOnlyForDamageableItems").getBooleanValue(true);
|
||||
|
||||
tag.getTag("inventory.autocrafting").getBooleanValue(true);
|
||||
tag.getTag("inventory.dynamicFontSize").getBooleanValue(true);
|
||||
@@ -202,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(
|
||||
@@ -214,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));
|
||||
@@ -229,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));
|
||||
@@ -264,6 +267,15 @@ public class NEIClientConfig {
|
||||
.setComment("Require holding shift to move items when overlaying recipe").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.shiftOverlayRecipe", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.scrollPages").setComment("Disable page scroll when overlaying recipe")
|
||||
.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));
|
||||
|
||||
@@ -286,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));
|
||||
|
||||
@@ -308,7 +346,7 @@ public class NEIClientConfig {
|
||||
super.onClick(button);
|
||||
CollapsibleItems.saveStates();
|
||||
CollapsibleItems.load();
|
||||
LayoutManager.markItemsDirty();
|
||||
ItemList.refreshItems.restart();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -343,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);
|
||||
@@ -359,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));
|
||||
|
||||
@@ -796,7 +846,7 @@ public class NEIClientConfig {
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInGui", true));
|
||||
|
||||
tag.getTag("inventory.favorites.generateSingleRecipeFavorites")
|
||||
.setComment("Automatically generate favorites for items with only one recipe").getBooleanValue(true);
|
||||
.setComment("Automatically generate favorites for items with only one recipe").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.generateSingleRecipeFavorites", true) {
|
||||
|
||||
@Override
|
||||
@@ -807,39 +857,44 @@ public class NEIClientConfig {
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.favorites.depth").setComment("Bookmark creation depth").getIntValue(3);
|
||||
tag.getTag("inventory.favorites.depth").setComment("Bookmark creation depth").getIntValue(9);
|
||||
API.addOption(new OptionIntegerField("inventory.favorites.depth", 0, 100));
|
||||
}
|
||||
|
||||
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.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);
|
||||
@@ -851,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() {
|
||||
@@ -876,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();
|
||||
@@ -915,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();
|
||||
@@ -925,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() {
|
||||
@@ -977,7 +1014,6 @@ public class NEIClientConfig {
|
||||
// main NEI config loading
|
||||
ItemInfo.load();
|
||||
GuiInfo.load();
|
||||
RecipeInfo.load();
|
||||
HeldItemHandler.load();
|
||||
LayoutManager.load();
|
||||
NEIController.load();
|
||||
@@ -993,23 +1029,14 @@ 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();
|
||||
CollapsibleItems.load();
|
||||
ItemSorter.loadConfig();
|
||||
FavoriteRecipes.load();
|
||||
|
||||
// Set pluginNEIConfigLoaded here before posting the NEIConfigsLoadedEvent. This used to be the
|
||||
// other way around, but apparently if your modpack includes 800 mods the event poster might not
|
||||
@@ -1018,14 +1045,16 @@ public class NEIClientConfig {
|
||||
// API. I don't expect any handler to do this, but who knows what modders have come up with...
|
||||
pluginNEIConfigLoaded = true;
|
||||
MinecraftForge.EVENT_BUS.post(new NEIConfigsLoadedEvent());
|
||||
|
||||
DebugHandlerWidget.loadHandlerInfoPatch();
|
||||
ItemList.loadItems.restart();
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
SubsetWidget.loadHidden();
|
||||
ItemList.loadItems.restart();
|
||||
if (ItemList.loadFinished) {
|
||||
ItemList.refreshItems.restart();
|
||||
} else {
|
||||
ItemList.loadItems.restart();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1049,7 +1078,9 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
public static boolean autocraftingEnabled() {
|
||||
return getBooleanSetting("inventory.autocrafting");
|
||||
if (!getBooleanSetting("inventory.autocrafting")) return false;
|
||||
if (hasSMPCounterpart) return permissableActions.contains("autocraft");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean favoritesEnabled() {
|
||||
@@ -1080,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");
|
||||
}
|
||||
@@ -1128,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() {
|
||||
@@ -1140,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 {
|
||||
|
||||
@@ -95,6 +98,7 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
|
||||
/** Formats a number with group separator and at most 2 fraction digits. */
|
||||
private static final Map<Locale, DecimalFormat> decimalFormatters = new HashMap<>();
|
||||
private static final DecimalFormat CHANCE_FORMAT = new DecimalFormat("##0.##%");
|
||||
|
||||
public static final int ALT_HASH = NEIKeyboardUtils.ALT_HASH;
|
||||
public static final int SHIFT_HASH = NEIKeyboardUtils.SHIFT_HASH;
|
||||
@@ -126,7 +130,7 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static String formatNumber(long aNumber) {
|
||||
public static String formatNumber(Number aNumber) {
|
||||
|
||||
if (NEIModContainer.isGTNHLibLoaded()) {
|
||||
return NumberFormatUtil.formatNumber(aNumber);
|
||||
@@ -135,7 +139,7 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
return getDecimalFormat().format(aNumber);
|
||||
}
|
||||
|
||||
public static String formatFluid(long aNumber) {
|
||||
public static String formatFluid(Number aNumber) {
|
||||
|
||||
if (NEIModContainer.isGTNHLibLoaded()) {
|
||||
return NumberFormatUtil.formatFluid(aNumber);
|
||||
@@ -144,6 +148,10 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
return getDecimalFormat().format(aNumber) + " L";
|
||||
}
|
||||
|
||||
public static String formatChance(float chance) {
|
||||
return CHANCE_FORMAT.format(chance);
|
||||
}
|
||||
|
||||
public static void deleteHeldItem() {
|
||||
deleteSlotStack(-999);
|
||||
}
|
||||
@@ -289,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);
|
||||
}
|
||||
@@ -376,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() {
|
||||
@@ -451,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() {
|
||||
@@ -526,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);
|
||||
@@ -568,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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package codechicken.nei;
|
||||
|
||||
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;
|
||||
|
||||
@@ -26,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;
|
||||
|
||||
@@ -76,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) {
|
||||
@@ -151,13 +161,15 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
&& slot != null
|
||||
&& slot.getStack() != null
|
||||
&& slot.isItemValid(slot.getStack())) {
|
||||
if (!canCheatItem(slot.getStack())) return false;
|
||||
|
||||
NEIClientUtils.cheatItem(slot.getStack(), button, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -249,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<>();
|
||||
@@ -80,6 +85,7 @@ public class NEIModContainer extends DummyModContainer {
|
||||
public List<ArtifactVersion> getDependencies() {
|
||||
List<ArtifactVersion> deps = new ArrayList<>();
|
||||
deps.add(VersionParser.parseVersionReference("CodeChickenCore@[" + codechicken.core.asm.Tags.VERSION + ",)"));
|
||||
deps.add(VersionParser.parseVersionReference("gtnhlib@[0.9.6,)"));
|
||||
return deps;
|
||||
}
|
||||
|
||||
@@ -95,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();
|
||||
}
|
||||
@@ -103,6 +111,7 @@ public class NEIModContainer extends DummyModContainer {
|
||||
public void init(FMLInitializationEvent event) {
|
||||
if (CommonUtils.isClient()) {
|
||||
ClientHandler.load();
|
||||
IMCForNEI.IMCSender();
|
||||
}
|
||||
ServerHandler.load();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -108,6 +108,7 @@ public class NEIServerConfig {
|
||||
setDefaultFeature("save-state");
|
||||
setDefaultFeature("item");
|
||||
setDefaultFeature("delete");
|
||||
setDefaultFeature("autocraft", "ALL");
|
||||
setDefaultFeature("notify-item", "CONSOLE, OP");
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -183,7 +217,7 @@ public class NEIServerUtils {
|
||||
public static boolean areStacksSameTypeWithNBT(ItemStack stack1, ItemStack stack2) {
|
||||
return stack1 != null && stack2 != null
|
||||
&& stack1.getItem() == stack2.getItem()
|
||||
&& (!stack2.getHasSubtypes() || stack2.getItemDamage() == stack1.getItemDamage())
|
||||
&& (stack2.isItemStackDamageable() || stack2.getItemDamage() == stack1.getItemDamage())
|
||||
&& NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -82,14 +82,14 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
|
||||
|
||||
public abstract String getLabelText();
|
||||
|
||||
public abstract Rectangle4i calculateBounds();
|
||||
public abstract Rectangle4i calculateBounds(GuiContainer gui);
|
||||
|
||||
protected static int snapDownToMultiple(int value, int multiple) {
|
||||
return (value / multiple) * multiple;
|
||||
}
|
||||
|
||||
public void resize(GuiContainer gui) {
|
||||
final Rectangle4i bounds = calculateBounds();
|
||||
final Rectangle4i bounds = calculateBounds(gui);
|
||||
|
||||
x = bounds.x;
|
||||
y = bounds.y;
|
||||
@@ -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,30 +4,52 @@ 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private boolean permutated = false;
|
||||
protected int chance = CHANCE_FULL;
|
||||
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);
|
||||
@@ -82,14 +104,58 @@ 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 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() {
|
||||
return this.chance;
|
||||
}
|
||||
|
||||
public void setChance(int chance) {
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
public PositionedStack copy() {
|
||||
PositionedStack pStack = new PositionedStack(
|
||||
Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new),
|
||||
relx,
|
||||
rely,
|
||||
false);
|
||||
pStack.permutated = this.permutated;
|
||||
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() {
|
||||
@@ -97,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());
|
||||
@@ -146,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) {
|
||||
@@ -174,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)) {
|
||||
@@ -251,7 +249,7 @@ public class PresetsList {
|
||||
updateSubsets();
|
||||
CollapsibleItems.saveStates();
|
||||
CollapsibleItems.load();
|
||||
LayoutManager.markItemsDirty();
|
||||
ItemList.refreshItems.restart();
|
||||
}
|
||||
|
||||
private static void updateSubsets() {
|
||||
|
||||
@@ -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 {
|
||||
@@ -111,7 +117,7 @@ public class SearchTokenParser {
|
||||
clearCache();
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
public synchronized void clearCache() {
|
||||
this.providersCache.clear();
|
||||
this.filtersCache.clear();
|
||||
this.recipesCache.clear();
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -19,6 +18,7 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -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) {
|
||||
@@ -413,9 +417,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* All operations on this variable should be synchronised.
|
||||
* Canonical {@link StackInfo#getItemStackGUID(ItemStack)} of every hidden item, stored by GUID so the state
|
||||
* survives world reloads. All operations should be synchronised.
|
||||
*/
|
||||
private static final ItemStackSet hiddenItems = new ItemStackSet();
|
||||
private static final Set<String> hiddenItems = new HashSet<>();
|
||||
|
||||
private static final Map<String, SubsetTag> tags = new HashMap<>();
|
||||
private static final SubsetTag root = new SubsetTag("");
|
||||
@@ -456,11 +461,16 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public static boolean isHidden(ItemStack item) {
|
||||
final String guid = StackInfo.getItemStackGUID(item);
|
||||
synchronized (hiddenLock) {
|
||||
return hiddenItems.contains(item);
|
||||
return hiddenItems.contains(guid);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> toGUIDs(List<ItemStack> items) {
|
||||
return items.stream().map(StackInfo::getItemStackGUID).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<ItemStack> getItems(SubsetTag tag, List<ItemStack> items) {
|
||||
items.addAll(tag.items);
|
||||
|
||||
@@ -472,19 +482,22 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public static void showOnly(SubsetTag tag) {
|
||||
final List<String> allItems = toGUIDs(getItems(root, new ArrayList<>()));
|
||||
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
|
||||
|
||||
synchronized (hiddenLock) {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(getItems(root, new ArrayList<>()));
|
||||
hiddenItems.removeAll(getItems(tag, new ArrayList<>()));
|
||||
hiddenItems.addAll(allItems);
|
||||
hiddenItems.removeAll(tagItems);
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
}
|
||||
|
||||
public static void setHidden(SubsetTag tag, boolean hidden) {
|
||||
synchronized (hiddenLock) {
|
||||
final List<ItemStack> tagItems = getItems(tag, new ArrayList<>());
|
||||
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
|
||||
|
||||
synchronized (hiddenLock) {
|
||||
if (hidden) {
|
||||
hiddenItems.addAll(tagItems);
|
||||
} else {
|
||||
@@ -496,11 +509,13 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public static void setHidden(ItemStack item, boolean hidden) {
|
||||
final String guid = StackInfo.getItemStackGUID(item);
|
||||
|
||||
synchronized (hiddenLock) {
|
||||
if (hidden) {
|
||||
hiddenItems.add(item);
|
||||
hiddenItems.add(guid);
|
||||
} else {
|
||||
hiddenItems.remove(item);
|
||||
hiddenItems.remove(guid);
|
||||
}
|
||||
}
|
||||
calculateVisibility();
|
||||
@@ -582,13 +597,23 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public static void loadHidden() {
|
||||
final List<ItemStack> itemList = new LinkedList<>();
|
||||
final Set<String> guids = new HashSet<>();
|
||||
|
||||
try {
|
||||
final NBTTagList guidList = NEIClientConfig.world.nbt.getTagList("hiddenItems", 8);
|
||||
|
||||
for (int i = 0; i < guidList.tagCount(); i++) {
|
||||
guids.add(guidList.getStringTagAt(i));
|
||||
}
|
||||
|
||||
final NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10);
|
||||
|
||||
for (int i = 0; i < list.tagCount(); i++) {
|
||||
itemList.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i)));
|
||||
final ItemStack stack = ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i));
|
||||
|
||||
if (stack != null) {
|
||||
guids.add(StackInfo.getItemStackGUID(stack));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Error loading hiddenItems", e);
|
||||
@@ -597,7 +622,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
|
||||
synchronized (hiddenLock) {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(itemList);
|
||||
hiddenItems.addAll(guids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,8 +631,8 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
|
||||
final NBTTagList list = new NBTTagList();
|
||||
synchronized (hiddenLock) {
|
||||
for (ItemStack stack : hiddenItems.values()) {
|
||||
list.appendTag(stack.writeToNBT(new NBTTagCompound()));
|
||||
for (String guid : hiddenItems) {
|
||||
list.appendTag(new NBTTagString(guid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,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) {
|
||||
@@ -689,20 +714,17 @@ public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderLabel() {
|
||||
public int getRenderLabelColor() {
|
||||
return NEIClientConfig.subsetWidgetOnTop() ? 0 : ColorUtils.subsetWidget.getColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderLabel() {
|
||||
if (NEIClientConfig.subsetWidgetOnTop()) {
|
||||
return NEIClientUtils.translate("inventory.item_subsets");
|
||||
} else {
|
||||
final String translated = NEIClientUtils.translate("gui.button.label.subset");
|
||||
if (!translated.startsWith("nei.")) { // Optional localization string for resource packs
|
||||
return translated;
|
||||
}
|
||||
|
||||
return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))
|
||||
+ EnumChatFormatting.RESET;
|
||||
return String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package codechicken.nei;
|
||||
|
||||
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;
|
||||
@@ -15,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;
|
||||
@@ -26,24 +34,23 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
public static int mobOverlay = 0;
|
||||
private static byte[] mobSpawnCache;
|
||||
private static long mobOverlayUpdateTime;
|
||||
private static String oregenPatternName;
|
||||
|
||||
public static void reset() {
|
||||
mobOverlay = 0;
|
||||
chunkOverlay = 0;
|
||||
oregenPatternName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
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(
|
||||
@@ -53,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;
|
||||
|
||||
@@ -66,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) {
|
||||
@@ -116,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) {
|
||||
@@ -171,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,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);
|
||||
@@ -233,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;
|
||||
@@ -266,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++) {
|
||||
@@ -377,20 +422,55 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
private static String getOregenPatternName() {
|
||||
|
||||
if (oregenPatternName == null) {
|
||||
try {
|
||||
final ClassLoader loader = WorldOverlayRenderer.class.getClassLoader();
|
||||
final Class<?> gtWorldGenerator = ReflectionHelper
|
||||
.getClass(loader, "gregtech.common.GTWorldgenerator", "gregtech.common.GT_Worldgenerator");
|
||||
oregenPatternName = ((Enum<?>) gtWorldGenerator.getDeclaredField("oregenPattern").get(null)).name();
|
||||
} catch (Exception ignored) {
|
||||
oregenPatternName = "AXISSYMMETRICAL";
|
||||
}
|
||||
private static int worldMinHeight(World world) {
|
||||
if (NEIModContainer.isCCLoaded() && world instanceof IMinMaxHeight worldHeights) {
|
||||
return worldHeights.getMinHeight();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return oregenPatternName;
|
||||
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() {
|
||||
if (oregenPatternReader == null) {
|
||||
oregenPatternReader = resolveOregenPatternReader();
|
||||
}
|
||||
try {
|
||||
return oregenPatternReader.call();
|
||||
} catch (Exception ignored) {
|
||||
return "AXISSYMMETRICAL";
|
||||
}
|
||||
}
|
||||
|
||||
private static Callable<String> resolveOregenPatternReader() {
|
||||
try {
|
||||
final Class<?> gtWorldGenerator = ReflectionHelper.getClass(
|
||||
WorldOverlayRenderer.class.getClassLoader(),
|
||||
"gregtech.common.GTWorldgenerator",
|
||||
"gregtech.common.GT_Worldgenerator");
|
||||
|
||||
try {
|
||||
// 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");
|
||||
oregenPattern.setAccessible(true);
|
||||
return () -> ((Enum<?>) oregenPattern.get(null)).name();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return () -> "AXISSYMMETRICAL";
|
||||
}
|
||||
}
|
||||
|
||||
public static void load() {
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
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;
|
||||
|
||||
@@ -20,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;
|
||||
@@ -32,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;
|
||||
@@ -146,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);
|
||||
@@ -176,6 +178,23 @@ 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)
|
||||
.filter(s -> !s.isEmpty()).collect(Collectors.toList());
|
||||
|
||||
if (list.isEmpty()) {
|
||||
ItemInfo.itemAliases.remove(item);
|
||||
} else {
|
||||
ItemInfo.itemAliases.put(item, list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the item panel. Any items added using this function will override the default search pattern.
|
||||
*
|
||||
@@ -206,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) {
|
||||
@@ -368,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,14 +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.PositionedStack;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.recipe.GuiOverlayButton.ItemOverlayState;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
@@ -32,29 +30,19 @@ public interface IOverlayHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean requireShiftForOverlayRecipe() {
|
||||
return NEIClientConfig.requireShiftForOverlayRecipe();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@@ -35,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;
|
||||
@@ -59,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;
|
||||
@@ -76,14 +80,18 @@ 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();
|
||||
public static final ItemStackMap<String> nameOverrides = new ItemStackMap<>();
|
||||
public static final ItemStackMap<List<String>> itemAliases = new ItemStackMap<>();
|
||||
public static final ItemStackSet hiddenItems = new ItemStackSet();
|
||||
public static final AnyMultiItemFilter hiddenItemsRules = new AnyMultiItemFilter();
|
||||
public static final ItemStackSet finiteItems = new ItemStackSet();
|
||||
@@ -109,6 +117,11 @@ public class ItemInfo {
|
||||
return nameOverrides.get(stack);
|
||||
}
|
||||
|
||||
public static List<String> getAliases(ItemStack stack) {
|
||||
final List<String> aliases = itemAliases.get(stack);
|
||||
return aliases != null ? aliases : Collections.emptyList();
|
||||
}
|
||||
|
||||
public static boolean canBeInfinite(ItemStack stack) {
|
||||
return !finiteItems.contains(stack);
|
||||
}
|
||||
@@ -123,6 +136,7 @@ public class ItemInfo {
|
||||
|
||||
public static void preInit() {
|
||||
addMobSpawnerItem();
|
||||
setupFluidDisplayItemClient();
|
||||
}
|
||||
|
||||
public static void load() {
|
||||
@@ -308,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());
|
||||
@@ -392,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) {
|
||||
@@ -465,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);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.LinkedList;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.config.GuiOptionList;
|
||||
import codechicken.nei.config.OptionCycled;
|
||||
|
||||
public class NEIInfo {
|
||||
@@ -13,7 +14,9 @@ public class NEIInfo {
|
||||
|
||||
public static void load(World world) {
|
||||
OptionCycled modeOption = (OptionCycled) NEIClientConfig.getOptionList().getOption("inventory.cheatmode");
|
||||
modeOption.parent.synthesizeEnvironment(false);
|
||||
final boolean worldConfig = NEIClientConfig.isWorldSpecific("inventory.cheatmode");
|
||||
// kept alive so the WeakReference in Option.slotRef isn't GC'd before copyGlobals/cycle use it
|
||||
GuiOptionList env = modeOption.parent.synthesizeEnvironment(worldConfig);
|
||||
if (!modeOption.optionValid(modeOption.value())) {
|
||||
modeOption.copyGlobals();
|
||||
modeOption.cycle();
|
||||
|
||||
@@ -19,21 +19,25 @@ 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;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.SearchField;
|
||||
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;
|
||||
@@ -74,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;
|
||||
}
|
||||
@@ -85,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);
|
||||
@@ -117,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());
|
||||
}
|
||||
@@ -196,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;
|
||||
}
|
||||
|
||||
@@ -239,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()
|
||||
@@ -373,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;
|
||||
@@ -442,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"));
|
||||
}
|
||||
|
||||
@@ -476,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();
|
||||
@@ -516,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"));
|
||||
}
|
||||
|
||||
@@ -533,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(
|
||||
@@ -604,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"));
|
||||
@@ -624,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);
|
||||
@@ -646,8 +704,10 @@ public abstract class ShortcutInputHandler {
|
||||
final List<ItemStack> playerInventory = AutoCraftingManager.getInventoryItems(guiContainer).values();
|
||||
final RecipeId rootRecipeId = math.createMasterRoot();
|
||||
|
||||
for (BookmarkItem item : math.recipeIngredients) {
|
||||
if (item.amount > 0 && rootRecipeId.equals(item.recipeId)) {
|
||||
for (int i = 0; i < math.recipeIngredients.size(); i++) {
|
||||
final BookmarkItem item = math.recipeIngredients.get(i);
|
||||
|
||||
if (item.getAmount() > 0 && rootRecipeId.equals(item.recipeId)) {
|
||||
long amount = 0;
|
||||
|
||||
for (ItemStack stack : playerInventory) {
|
||||
@@ -656,43 +716,15 @@ public abstract class ShortcutInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (amount >= item.amount) {
|
||||
item.factor = item.amount = amount + item.amount - (amount % item.amount);
|
||||
if (amount >= item.getAmount()) {
|
||||
math.recipeIngredients.set(
|
||||
i,
|
||||
BookmarkItem.builder(item).multiplier(1)
|
||||
.factor(amount + item.getAmount() - (amount % item.getAmount()))
|
||||
.chance(PositionedStack.CHANCE_FULL).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.Recipe.RecipeIngredient;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.recipe.chain.RecipeChainMath;
|
||||
|
||||
@@ -139,17 +140,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
if (group.collapsedRecipes.contains(collapsedRecipeId)) {
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId)
|
||||
&& item.amount == item.factor) {
|
||||
item.amount = 0;
|
||||
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 1) {
|
||||
item.multiplier = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.amount == 0) {
|
||||
item.amount = item.factor;
|
||||
if (item.groupId == groupId && collapsedRecipeId.equals(item.recipeId) && item.multiplier == 0) {
|
||||
item.multiplier = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -627,15 +637,17 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
for (int itemIndex = 0; itemIndex < this.bookmarkItems.size(); itemIndex++) {
|
||||
final BookmarkItem item = this.bookmarkItems.get(itemIndex);
|
||||
if (item.groupId == groupId && (recipeRelations == null || recipeRelations.contains(item.recipeId))) {
|
||||
long amount = item.amount;
|
||||
|
||||
if (recipeId != null && group.crafting != null
|
||||
&& group.crafting.calculatedItems.containsKey(itemIndex)
|
||||
&& recipeId.equals(item.recipeId)) {
|
||||
amount = group.crafting.calculatedItems.get(itemIndex).getCalculatedAmount();
|
||||
chainItems.add(
|
||||
item.copyWithMultiplier(
|
||||
group.crafting.calculatedItems.get(itemIndex).getCalculatedMultiplier()));
|
||||
} else {
|
||||
chainItems.add(item.copy());
|
||||
}
|
||||
|
||||
chainItems.add(item.copyWithAmount(amount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,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) {
|
||||
@@ -877,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);
|
||||
}
|
||||
@@ -896,40 +918,44 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
|
||||
public void addRecipe(Recipe recipe, int multiplier, int groupId) {
|
||||
final RecipeId recipeId = recipe.getRecipeId();
|
||||
final List<ItemStack> results = recipe.getResults().stream().map(res -> res.getItemStack())
|
||||
.collect(Collectors.toList());
|
||||
final List<ItemStack> ingredients = recipe.getIngredients().stream().map(ingr -> ingr.getItemStack())
|
||||
.collect(Collectors.toList());
|
||||
final ItemStackAmount seenResults = new ItemStackAmount();
|
||||
final ItemStackAmount seenIngredients = new ItemStackAmount();
|
||||
final List<ItemStack> results = new ArrayList<>();
|
||||
final List<ItemStack> ingredients = new ArrayList<>();
|
||||
|
||||
for (ItemStack stack : ItemStackAmount.of(results).values()) {
|
||||
long factor = StackInfo.getAmount(stack);
|
||||
for (RecipeIngredient result : recipe.getResults()) {
|
||||
ItemStack stack = result.getItemStack();
|
||||
if (!seenResults.contains(stack)) {
|
||||
seenResults.add(stack);
|
||||
results.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (RecipeIngredient ingr : recipe.getIngredients()) {
|
||||
ItemStack stack = ingr.getItemStack();
|
||||
if (!seenIngredients.contains(stack)) {
|
||||
seenIngredients.add(stack);
|
||||
ingredients.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack stack : results) {
|
||||
this.addItem(
|
||||
BookmarkItem.of(
|
||||
groupId,
|
||||
StackInfo.withAmount(stack, factor * multiplier),
|
||||
factor,
|
||||
recipeId,
|
||||
BookmarkItemType.RESULT),
|
||||
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.RESULT).multiplier(multiplier)
|
||||
.build(),
|
||||
true);
|
||||
}
|
||||
|
||||
for (ItemStack stack : ItemStackAmount.of(ingredients).values()) {
|
||||
long factor = StackInfo.getAmount(stack);
|
||||
for (ItemStack stack : ingredients) {
|
||||
this.addItem(
|
||||
BookmarkItem.of(
|
||||
groupId,
|
||||
StackInfo.withAmount(stack, factor * multiplier),
|
||||
factor,
|
||||
recipeId,
|
||||
BookmarkItemType.INGREDIENT,
|
||||
BookmarkItem.generatePermutations(stack, recipe)),
|
||||
BookmarkItem.builder(groupId, stack, recipe, BookmarkItemType.INGREDIENT).multiplier(multiplier)
|
||||
.build(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRecipe(int itemIndex, boolean removeFullRecipe) {
|
||||
final BookmarkItem item = getCalculatedItem(itemIndex);
|
||||
final BookmarkItem item = getBookmarkItem(itemIndex);
|
||||
|
||||
if (item == null) {
|
||||
return;
|
||||
@@ -1133,8 +1159,8 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
final Set<Long> sizes = new HashSet<>();
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.groupId == targetGroupId && item.amount > 0) {
|
||||
sizes.add(Math.max(item.getStackSize(), item.factor / item.fluidCellAmount));
|
||||
if (item.groupId == targetGroupId && item.getAmount() > 0) {
|
||||
sizes.add(Math.max(item.getStackSize(), item.getStackSize(item.getAmount(1))));
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
@@ -1143,14 +1169,16 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
long multiplier = Integer.MAX_VALUE;
|
||||
|
||||
for (BookmarkItem item : items) {
|
||||
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
|
||||
long customMultiplier = (long) (customFactor > 0 ? Math.ceil(item.amount / (double) customFactor) : 0);
|
||||
final long amount = item.getAmount();
|
||||
final long customFactor = (amount + gcd - 1) / gcd;
|
||||
final long customMultiplier = customFactor > 0 ? ((amount + customFactor - 1) / customFactor) : 0;
|
||||
multiplier = Math.min(shiftMultiplier(customMultiplier, shift, 1), multiplier);
|
||||
}
|
||||
|
||||
for (BookmarkItem item : items) {
|
||||
long customFactor = (long) Math.ceil(item.amount / (double) gcd);
|
||||
item.amount = customFactor * multiplier;
|
||||
final long amount = item.getAmount();
|
||||
final long customFactor = (amount + gcd - 1) / gcd;
|
||||
item.multiplier = item.getMultiplierFromAmount(customFactor * multiplier);
|
||||
}
|
||||
} else {
|
||||
final Map<RecipeId, Long> recipeMultipliers = new HashMap<>();
|
||||
@@ -1158,7 +1186,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
final List<BookmarkItem> items = new ArrayList<>();
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.factor > 0
|
||||
if (!item.emptyFactor()
|
||||
&& outputRecipes.stream().anyMatch(recipeId -> item.equalsRecipe(recipeId, targetGroupId))) {
|
||||
final long multiplier = recipeMultipliers.getOrDefault(item.recipeId, (long) Integer.MAX_VALUE);
|
||||
final boolean recipeInMiddle = group.crafting != null
|
||||
@@ -1168,7 +1196,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
item.recipeId,
|
||||
Math.min(
|
||||
shiftMultiplier(
|
||||
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
|
||||
Math.max(recipeInMiddle ? 1 : 0, item.multiplier),
|
||||
shift,
|
||||
recipeInMiddle ? 1 : 0),
|
||||
multiplier));
|
||||
@@ -1177,7 +1205,7 @@ public class BookmarkGrid extends ItemsGrid<BookmarksGridSlot, BookmarkGrid.Book
|
||||
}
|
||||
|
||||
for (BookmarkItem item : items) {
|
||||
item.amount = item.factor * recipeMultipliers.getOrDefault(item.recipeId, 0L);
|
||||
item.multiplier = recipeMultipliers.getOrDefault(item.recipeId, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,37 +1219,45 @@ 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.factor > 0) {
|
||||
multiplier = Math.min(
|
||||
shiftMultiplier(
|
||||
Math.max(recipeInMiddle ? 1 : 0, item.getMultiplier()),
|
||||
shift,
|
||||
recipeInMiddle ? 1 : 0),
|
||||
multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (item.equalsRecipe(recipeId, targetItem.groupId) && item.factor > 0) {
|
||||
item.amount = item.factor * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
shiftRecipeAmount(
|
||||
targetItem.groupId,
|
||||
this.gridGenerator.itemToRecipe.getOrDefault(targetItemIndex, targetItem.recipeId),
|
||||
shift);
|
||||
} else {
|
||||
|
||||
for (BookmarkItem item : this.bookmarkItems) {
|
||||
if (targetItem.equals(item)) {
|
||||
item.amount = item.factor * shiftMultiplier(item.getMultiplier(), shift, 0);
|
||||
item.multiplier = shiftMultiplier(item.multiplier, shift, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -1249,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);
|
||||
@@ -1278,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);
|
||||
}
|
||||
@@ -1295,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();
|
||||
|
||||
|
||||
@@ -66,10 +66,14 @@ public class BookmarkGridGenerator {
|
||||
while (itemIndex < size && absoluteSlotIndex != -1) {
|
||||
final BookmarkItem bookmarkItem = this.grid.getBookmarkItem(itemIndex);
|
||||
final BookmarkGroup group = this.grid.getGroup(bookmarkItem.groupId);
|
||||
final boolean isCollapsedRecipeAnchor = group.crafting != null && bookmarkItem.recipeId != null
|
||||
&& bookmarkItem.type != BookmarkItemType.INGREDIENT
|
||||
&& group.collapsedRecipes.contains(bookmarkItem.recipeId);
|
||||
|
||||
if (group.crafting != null && (!group.crafting.calculatedItems.containsKey(itemIndex)
|
||||
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId
|
||||
.equals(group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
|
||||
if (group.crafting != null && !isCollapsedRecipeAnchor
|
||||
&& (!group.crafting.calculatedItems.containsKey(itemIndex)
|
||||
|| bookmarkItem.recipeId != null && !bookmarkItem.recipeId.equals(
|
||||
group.crafting.itemToRecipe.getOrDefault(itemIndex, bookmarkItem.recipeId)))) {
|
||||
itemIndex++;
|
||||
} else if (group.collapsed) {
|
||||
final List<Integer> results = getGroupResults(bookmarkItem.groupId, itemIndex, size);
|
||||
@@ -189,13 +193,11 @@ public class BookmarkGridGenerator {
|
||||
for (int shadowIndex : shadows) {
|
||||
final boolean isIngredient = shadows.get(0) != shadowIndex
|
||||
|| group.crafting.calculatedItems.get(shadowIndex).calculatedType == CalculatedType.INGREDIENT;
|
||||
final BookmarkItem shadowBookmarkItem = BookmarkItem.of(
|
||||
bookmarkItem.groupId,
|
||||
bookmarkItem.itemStack,
|
||||
1,
|
||||
bookmarkItem.recipeId,
|
||||
isIngredient ? BookmarkItemType.INGREDIENT : BookmarkItemType.RESULT,
|
||||
Collections.emptyMap());
|
||||
|
||||
final BookmarkItem shadowBookmarkItem = BookmarkItem.builder(bookmarkItem.groupId, bookmarkItem.itemStack)
|
||||
.multiplier(1).factor(StackInfo.getAmount(bookmarkItem.itemStack)).recipeId(bookmarkItem.recipeId)
|
||||
.type(isIngredient ? BookmarkItemType.INGREDIENT : BookmarkItemType.RESULT)
|
||||
.permutations(Collections.emptyMap()).build();
|
||||
|
||||
if ((absoluteSlotIndex = nextSlotIndex(
|
||||
absoluteSlotIndex + 1,
|
||||
@@ -228,7 +230,7 @@ public class BookmarkGridGenerator {
|
||||
index = Math.max(0, index);
|
||||
|
||||
while (j++ < maxIndex) {
|
||||
boolean isFirstColumn = (index % gridColumns) == 0;
|
||||
final boolean isFirstColumn = (index % gridColumns) == 0;
|
||||
|
||||
if (this.grid.isInvalidSlot(index % maxIndex)) {
|
||||
index++;
|
||||
@@ -239,7 +241,7 @@ public class BookmarkGridGenerator {
|
||||
return index;
|
||||
} else if (isFirstColumn && (meta.recipeId == null || previousMeta == null
|
||||
|| previousMeta.groupId != meta.groupId
|
||||
|| meta.type == BookmarkItemType.ITEM
|
||||
|| meta.type != BookmarkItemType.INGREDIENT
|
||||
|| !meta.recipeId.equals(previousMeta.recipeId)
|
||||
|| index + 1 < maxIndex && this.grid.isInvalidSlot((index + 1) % maxIndex))) {
|
||||
// In first column must be an item without recipe, a recipe result, or an ingredient
|
||||
@@ -248,6 +250,7 @@ public class BookmarkGridGenerator {
|
||||
} else
|
||||
if (!isFirstColumn && meta.type != BookmarkItemType.ITEM
|
||||
&& previousMeta.type != BookmarkItemType.ITEM
|
||||
&& meta.groupId == previousMeta.groupId
|
||||
&& meta.recipeId != null
|
||||
&& meta.recipeId.equals(previousMeta.recipeId)) {
|
||||
return index;
|
||||
@@ -319,7 +322,7 @@ public class BookmarkGridGenerator {
|
||||
return group.crafting.calculatedItems.get(itemIndex);
|
||||
} else {
|
||||
BookmarkChainItem item = BookmarkChainItem.of(this.grid.getBookmarkItem(itemIndex));
|
||||
item.setRealAmount(item.getShiftAmount());
|
||||
item.setRealMultiplier(item.getCalculatedMultiplier());
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -332,22 +335,24 @@ public class BookmarkGridGenerator {
|
||||
return new BookmarksGridSlot(
|
||||
slotIndex,
|
||||
itemIndex,
|
||||
chainItem.getRealAmount(),
|
||||
chainItem.getRealMultiplier(),
|
||||
0,
|
||||
0,
|
||||
chainItem.getItem(),
|
||||
null,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
} else {
|
||||
return new BookmarksGridSlot(
|
||||
slotIndex,
|
||||
itemIndex,
|
||||
chainItem.getRealAmount(),
|
||||
chainItem.getRealMultiplier(),
|
||||
chainItem.getShiftAmount(),
|
||||
chainItem.getCalculatedAmount(),
|
||||
chainItem.getCalculatedMultiplier(),
|
||||
chainItem.getItem(),
|
||||
chainItem.calculatedType,
|
||||
group);
|
||||
group,
|
||||
this.grid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package codechicken.nei.bookmark;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -9,14 +10,160 @@ 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 {
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
|
||||
|
||||
private final int groupId;
|
||||
private final ItemStack stack;
|
||||
private long factor;
|
||||
private long multiplier = -1;
|
||||
private long chance = PositionedStack.CHANCE_FULL;
|
||||
private RecipeId recipeId = null;
|
||||
private Recipe recipe = null;
|
||||
private BookmarkItemType type = BookmarkItemType.ITEM;
|
||||
private Map<String, ItemStack> permutations = null;
|
||||
|
||||
private Builder(int groupId, ItemStack stack) {
|
||||
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
|
||||
|
||||
this.groupId = groupId;
|
||||
this.stack = stack;
|
||||
this.factor = StackInfo.isFluidDisplayItem(stack) ? Math.max(nbTag.getInteger("Count"), 1) : 1;
|
||||
}
|
||||
|
||||
public Builder factor(long factor) {
|
||||
this.factor = factor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder multiplier(long multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder chance(long chance) {
|
||||
this.chance = chance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder recipe(Recipe recipe) {
|
||||
this.recipe = recipe;
|
||||
this.recipeId = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder recipeId(RecipeId recipeId) {
|
||||
this.recipeId = recipeId;
|
||||
this.recipe = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(BookmarkItemType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder permutations(Map<String, ItemStack> permutations) {
|
||||
this.permutations = permutations;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BookmarkItem build() {
|
||||
final Map<String, ItemStack> perms;
|
||||
long multiplier = 0;
|
||||
|
||||
if (this.permutations != null) {
|
||||
perms = this.permutations;
|
||||
} else if (this.type == BookmarkItemType.INGREDIENT) {
|
||||
|
||||
if (this.recipe != null) {
|
||||
perms = Builder.generatePermutations(this.stack, this.recipe);
|
||||
} else if (this.recipeId != null) {
|
||||
perms = Builder.generatePermutations(this.stack, Recipe.of(this.recipeId));
|
||||
} else {
|
||||
perms = Builder.generatePermutations(this.stack);
|
||||
}
|
||||
|
||||
} else {
|
||||
perms = Builder.generatePermutations(this.stack);
|
||||
}
|
||||
|
||||
if (this.multiplier >= 0) {
|
||||
multiplier = this.multiplier;
|
||||
} else if (this.factor > 0) {
|
||||
multiplier = StackInfo.getAmount(this.stack) / this.factor;
|
||||
}
|
||||
|
||||
return new BookmarkItem(
|
||||
this.groupId,
|
||||
multiplier,
|
||||
this.factor,
|
||||
this.chance,
|
||||
this.stack,
|
||||
perms,
|
||||
this.recipeId != null ? this.recipeId : this.recipe != null ? this.recipe.getRecipeId() : null,
|
||||
this.type);
|
||||
}
|
||||
|
||||
public static Map<String, ItemStack> generatePermutations(ItemStack stack) {
|
||||
return Collections.singletonMap(getItemGUID(stack), stack);
|
||||
}
|
||||
|
||||
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
|
||||
|
||||
if (recipe != null) {
|
||||
final RecipeIngredient ingr = recipe.getIngredients().stream()
|
||||
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
|
||||
|
||||
if (ingr != null) {
|
||||
final Map<String, ItemStack> permutations = new HashMap<>();
|
||||
for (ItemStack ingrStack : ingr.getPermutations()) {
|
||||
permutations.put(getItemGUID(ingrStack), ingrStack);
|
||||
}
|
||||
return permutations;
|
||||
}
|
||||
}
|
||||
|
||||
return generatePermutations(stack);
|
||||
}
|
||||
|
||||
private static synchronized String getItemGUID(ItemStack stack) {
|
||||
final FluidStack fluidStack = StackInfo.getFluid(stack);
|
||||
|
||||
if (fluidStack != null && (fluidStack.amount > 0 || !StackInfo.isFluidContainer(stack))) {
|
||||
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
|
||||
} else {
|
||||
|
||||
for (Map.Entry<ItemStack, String> entry : Builder.fuzzyPermutations.entrySet()) {
|
||||
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
|
||||
Builder.fuzzyPermutations.put(stack, entry.getValue());
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
final String stackGUID = StackInfo.getItemStackGUID(stack);
|
||||
Builder.fuzzyPermutations.put(stack, stackGUID);
|
||||
|
||||
return stackGUID;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum BookmarkItemType {
|
||||
|
||||
INGREDIENT,
|
||||
@@ -32,150 +179,238 @@ public class BookmarkItem {
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<ItemStack, String> fuzzyPermutations = new HashMap<>();
|
||||
|
||||
public int groupId;
|
||||
|
||||
public long amount = 0L;
|
||||
public final int fluidCellAmount;
|
||||
public ItemStack itemStack;
|
||||
public Map<String, ItemStack> permutations;
|
||||
|
||||
public long factor;
|
||||
public RecipeId recipeId;
|
||||
public BookmarkItemType type = BookmarkItemType.ITEM;
|
||||
|
||||
protected BookmarkItem(int groupId, long amount, int fluidCellAmount, ItemStack itemStack,
|
||||
Map<String, ItemStack> permutations, long factor, RecipeId recipeId, BookmarkItemType type) {
|
||||
public long multiplier = 1L;
|
||||
public final long factor;
|
||||
public final int fluidCellAmount;
|
||||
public final long chance;
|
||||
|
||||
protected BookmarkItem(int groupId, long multiplier, long factor, long chance, ItemStack itemStack,
|
||||
Map<String, ItemStack> permutations, RecipeId recipeId, BookmarkItemType type) {
|
||||
final FluidStack fluidStack = StackInfo.getFluid(itemStack);
|
||||
|
||||
if (fluidStack != null) {
|
||||
this.fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(itemStack) ? fluidStack.amount : 1);
|
||||
} else {
|
||||
this.fluidCellAmount = 1;
|
||||
}
|
||||
|
||||
this.groupId = groupId;
|
||||
|
||||
this.amount = amount;
|
||||
this.fluidCellAmount = fluidCellAmount;
|
||||
this.itemStack = itemStack;
|
||||
this.permutations = permutations;
|
||||
|
||||
this.factor = factor;
|
||||
this.recipeId = recipeId;
|
||||
this.type = type == null ? BookmarkItemType.ITEM : type;
|
||||
|
||||
this.multiplier = multiplier;
|
||||
this.factor = factor;
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type,
|
||||
Map<String, ItemStack> permutations) {
|
||||
final FluidStack fluidStack = StackInfo.getFluid(stack);
|
||||
int fluidCellAmount = 1;
|
||||
long amount;
|
||||
public static Builder builder(int groupId, ItemStack stack) {
|
||||
return new Builder(groupId, stack);
|
||||
}
|
||||
|
||||
if (fluidStack != null) {
|
||||
amount = fluidStack.amount * (long) Math.max(0, stack.stackSize);
|
||||
fluidCellAmount = Math.max(1, StackInfo.isFluidContainer(stack) ? fluidStack.amount : 1);
|
||||
} else {
|
||||
amount = StackInfo.getAmount(stack);
|
||||
public static Builder builder(BookmarkItem item) {
|
||||
return new Builder(item.groupId, item.itemStack).factor(item.factor).multiplier(item.multiplier)
|
||||
.chance(item.chance).recipeId(item.recipeId).type(item.type).permutations(item.permutations);
|
||||
}
|
||||
|
||||
public static Builder builder(int groupId, ItemStack stack, Recipe recipe, BookmarkItemType type) {
|
||||
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) {
|
||||
for (ItemStack perm : res.getPermutations()) {
|
||||
if (NEIClientUtils.areStacksSameTypeWithNBT(emptyStack, StackInfo.withAmount(perm, 0))) {
|
||||
amount += 1L * StackInfo.getAmount(perm) * res.getChance();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BookmarkItem(
|
||||
groupId,
|
||||
amount,
|
||||
fluidCellAmount,
|
||||
stack,
|
||||
permutations,
|
||||
factor * fluidCellAmount,
|
||||
recipeId,
|
||||
type);
|
||||
}
|
||||
if ((amount % PositionedStack.CHANCE_FULL) == 0) {
|
||||
builder.factor(amount / PositionedStack.CHANCE_FULL);
|
||||
} else {
|
||||
builder.factor(1).chance(amount);
|
||||
}
|
||||
|
||||
public static BookmarkItem of(int groupId, ItemStack stack, long factor, RecipeId recipeId, BookmarkItemType type) {
|
||||
return of(
|
||||
groupId,
|
||||
stack,
|
||||
factor,
|
||||
recipeId,
|
||||
type,
|
||||
generatePermutations(stack, recipeId, type == BookmarkItemType.INGREDIENT));
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static BookmarkItem of(int groupId, ItemStack stack) {
|
||||
final NBTTagCompound nbTag = StackInfo.itemStackToNBT(stack);
|
||||
final long factor = nbTag.hasKey("gtFluidName") ? Math.min(144, nbTag.getInteger("Count")) : 1;
|
||||
return BookmarkItem.of(
|
||||
groupId,
|
||||
stack,
|
||||
factor,
|
||||
null,
|
||||
BookmarkItemType.ITEM,
|
||||
Collections.singletonMap(getItemGUID(stack), stack));
|
||||
return builder(groupId, stack).build();
|
||||
}
|
||||
|
||||
public BookmarkItem copyWithAmount(long amount) {
|
||||
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,
|
||||
amount,
|
||||
this.fluidCellAmount,
|
||||
multiplier,
|
||||
this.factor,
|
||||
this.chance,
|
||||
this.itemStack,
|
||||
this.permutations,
|
||||
this.factor,
|
||||
this.recipeId,
|
||||
this.type);
|
||||
}
|
||||
|
||||
public BookmarkItem copyWithAmount(long amount) {
|
||||
return copyWithMultiplier(getMultiplierFromAmount(amount));
|
||||
}
|
||||
|
||||
public BookmarkItem copy() {
|
||||
return copyWithAmount(this.amount);
|
||||
return copyWithMultiplier(this.multiplier);
|
||||
}
|
||||
|
||||
public static Map<String, ItemStack> generatePermutations(ItemStack stack, RecipeId recipeId,
|
||||
boolean isIngredient) {
|
||||
public BookmarkItem copyWithPerm(ItemStack stack) {
|
||||
|
||||
if (isIngredient && recipeId != null) {
|
||||
return generatePermutations(stack, Recipe.of(recipeId));
|
||||
}
|
||||
if (this.recipeId != null
|
||||
&& !NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(stack, 0), this.getItemStack(0))) {
|
||||
final ItemStack oldPerm = findPermutation(this.itemStack);
|
||||
final ItemStack newPerm = findPermutation(stack);
|
||||
|
||||
return Collections.singletonMap(getItemGUID(stack), 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;
|
||||
|
||||
public static Map<String, ItemStack> generatePermutations(ItemStack stack, Recipe recipe) {
|
||||
|
||||
if (recipe != null) {
|
||||
final RecipeIngredient ingr = recipe.getIngredients().stream()
|
||||
.filter(ingredient -> ingredient.contains(stack)).findAny().orElse(null);
|
||||
|
||||
if (ingr != null) {
|
||||
final Map<String, ItemStack> permutations = new HashMap<>();
|
||||
for (ItemStack ingrStack : ingr.getPermutations()) {
|
||||
permutations.put(getItemGUID(ingrStack), ingrStack);
|
||||
}
|
||||
return permutations;
|
||||
return new BookmarkItem(
|
||||
this.groupId,
|
||||
this.multiplier,
|
||||
factor,
|
||||
chance,
|
||||
newPerm,
|
||||
this.permutations,
|
||||
this.recipeId,
|
||||
this.type);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.singletonMap(getItemGUID(stack), stack);
|
||||
return copy();
|
||||
}
|
||||
|
||||
private static synchronized String getItemGUID(ItemStack stack) {
|
||||
final FluidStack fluidStack = StackInfo.getFluid(stack);
|
||||
private ItemStack findPermutation(ItemStack stack) {
|
||||
|
||||
if (fluidStack != null) {
|
||||
return fluidStack.getFluid().getName() + ":" + fluidStack.tag;
|
||||
} else {
|
||||
|
||||
for (Map.Entry<ItemStack, String> entry : BookmarkItem.fuzzyPermutations.entrySet()) {
|
||||
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, entry.getKey())) {
|
||||
BookmarkItem.fuzzyPermutations.put(stack, entry.getValue());
|
||||
return entry.getValue();
|
||||
}
|
||||
for (ItemStack itemStack : this.permutations.values()) {
|
||||
if (StackInfo.equalItemAndNBT(stack, itemStack, true)) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
final String stackGUID = StackInfo.getItemStackGUID(stack);
|
||||
BookmarkItem.fuzzyPermutations.put(stack, stackGUID);
|
||||
|
||||
return stackGUID;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean containsItems(BookmarkItem item) {
|
||||
return this.permutations.keySet().stream().anyMatch(item.permutations::containsKey);
|
||||
}
|
||||
|
||||
public long getAmount() {
|
||||
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) {
|
||||
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 (chance != PositionedStack.CHANCE_FULL) {
|
||||
|
||||
if (this.type == BookmarkItemType.INGREDIENT) {
|
||||
// For ingredients, round up the expected amount to be conservative.
|
||||
return (amount * chance + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL;
|
||||
} else {
|
||||
// For results, round down the expected amount to be conservative.
|
||||
return (amount * chance) / PositionedStack.CHANCE_FULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack() {
|
||||
return getItemStack(this.amount);
|
||||
return getItemStack(getAmount());
|
||||
}
|
||||
|
||||
public ItemStack getItemStack(long amount) {
|
||||
@@ -183,23 +418,34 @@ public class BookmarkItem {
|
||||
}
|
||||
|
||||
public long getStackSize() {
|
||||
return getStackSize(this.amount);
|
||||
return getStackSize(getAmount());
|
||||
}
|
||||
|
||||
public long getStackSize(long amount) {
|
||||
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.fluidCellAmount) : 0);
|
||||
|
||||
if (this.fluidCellAmount > 1) {
|
||||
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
public long getMultiplierFromAmount(long amount) {
|
||||
if (this.factor <= 0 || this.chance <= 0) return 0;
|
||||
|
||||
if (this.fluidCellAmount > 1) {
|
||||
amount = (amount + this.fluidCellAmount - 1) / this.fluidCellAmount;
|
||||
}
|
||||
|
||||
if (this.chance == PositionedStack.CHANCE_FULL) {
|
||||
return (amount + this.factor - 1) / this.factor;
|
||||
}
|
||||
|
||||
return (amount * PositionedStack.CHANCE_FULL + this.factor * this.chance - 1) / (this.factor * this.chance);
|
||||
}
|
||||
|
||||
public long getMultiplier() {
|
||||
return getMultiplier(this.amount);
|
||||
}
|
||||
|
||||
public long getMultiplier(long amount) {
|
||||
return (long) (this.factor > 0 ? Math.ceil(amount / (double) this.factor) : 0);
|
||||
}
|
||||
|
||||
public long getFactor() {
|
||||
return this.factor / this.fluidCellAmount;
|
||||
return this.multiplier;
|
||||
}
|
||||
|
||||
public boolean equalsRecipe(BookmarkItem meta) {
|
||||
@@ -210,6 +456,10 @@ public class BookmarkItem {
|
||||
return groupId == this.groupId && recipeId != null && recipeId.equals(this.recipeId);
|
||||
}
|
||||
|
||||
public boolean emptyFactor() {
|
||||
return this.factor <= 0 || this.chance <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.groupId, this.fluidCellAmount, this.type.toInt(), this.recipeId);
|
||||
@@ -228,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,10 +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.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class BookmarkStorage {
|
||||
@@ -82,10 +80,21 @@ public class BookmarkStorage {
|
||||
|
||||
public int getNamespaceSize() {
|
||||
|
||||
if (this.namespaces.get(this.namespaces.size() - 1).size() > 0) {
|
||||
this.namespaces.add(new BookmarkGrid());
|
||||
} else if (this.activeNamespaceIndex == this.namespaces.size() - 2 && getActiveGrid().size() == 0) {
|
||||
this.namespaces.remove(this.namespaces.size() - 1);
|
||||
final int lastIndex = this.namespaces.size() - 1;
|
||||
final BookmarkGrid lastGrid = this.namespaces.get(lastIndex);
|
||||
final SortableGroup sortableGroup = ItemPanels.bookmarkPanel.sortableGroup;
|
||||
|
||||
if (lastGrid.size() > 0) {
|
||||
if (sortableGroup == null || sortableGroup.grid != lastGrid || !sortableGroup.aloneInGrid) {
|
||||
this.namespaces.add(new BookmarkGrid());
|
||||
}
|
||||
} else if (lastIndex > 0 && this.activeNamespaceIndex != lastIndex) {
|
||||
final BookmarkGrid prevGrid = this.namespaces.get(lastIndex - 1);
|
||||
|
||||
if (sortableGroup != null && sortableGroup.grid == prevGrid && sortableGroup.aloneInGrid
|
||||
|| prevGrid == getActiveGrid() && prevGrid.size() == 0) {
|
||||
this.namespaces.remove(lastIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return this.namespaces.size();
|
||||
@@ -198,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{}",
|
||||
@@ -237,47 +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) {
|
||||
RecipeId recipeId = null;
|
||||
boolean isFluid = itemStackNBT.hasKey("gtFluidName");
|
||||
int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt()
|
||||
: BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
int factor = jsonObject.has("factor") ? Math.abs(jsonObject.get("factor").getAsInt()) : (isFluid ? 144 : 1);
|
||||
BookmarkItemType type = jsonObject.has("type") ? BookmarkItemType.fromInt(jsonObject.get("type").getAsInt())
|
||||
: null;
|
||||
|
||||
if (type == null) {
|
||||
// old format
|
||||
type = jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()
|
||||
? BookmarkItemType.INGREDIENT
|
||||
: BookmarkItemType.RESULT;
|
||||
}
|
||||
|
||||
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
|
||||
recipeId = RecipeId.of(recipeJson);
|
||||
}
|
||||
|
||||
if (!grid.groups.containsKey(groupId)) {
|
||||
groupId = BookmarkGrid.DEFAULT_GROUP_ID;
|
||||
}
|
||||
|
||||
grid.addItem(BookmarkItem.of(groupId, itemStack, factor, recipeId, type), false);
|
||||
}
|
||||
|
||||
return itemStack != null;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
if (this.bookmarkFile == null) {
|
||||
@@ -318,21 +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("factor", new JsonPrimitive(item.getFactor()));
|
||||
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());
|
||||
}
|
||||
|
||||
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,12 +8,12 @@ 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;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIClientUtils.Alignment;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.bookmark.BookmarkGrid.BookmarkMouseContext;
|
||||
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
|
||||
import codechicken.nei.bookmark.RecipeChainDetails.CalculatedType;
|
||||
@@ -63,28 +63,29 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
protected final boolean isFirstOutput;
|
||||
protected final int rowIndex;
|
||||
|
||||
public BookmarksGridSlot(int slotIndex, int itemIndex, long realAmount, long shiftAmount, long calculatedAmount,
|
||||
BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group) {
|
||||
public BookmarksGridSlot(int slotIndex, int itemIndex, long realMultiplier, long shiftAmount,
|
||||
long calculatedMultiplier, BookmarkItem bookmarkItem, CalculatedType calculatedType, BookmarkGroup group,
|
||||
BookmarkGrid grid) {
|
||||
super(
|
||||
slotIndex,
|
||||
itemIndex,
|
||||
calculatedType != null ? bookmarkItem.getItemStack(calculatedAmount)
|
||||
: bookmarkItem.getItemStack(realAmount));
|
||||
calculatedType != null ? bookmarkItem.getItemStack(bookmarkItem.getAmount(calculatedMultiplier))
|
||||
: bookmarkItem.getItemStack(bookmarkItem.getAmount(realMultiplier)));
|
||||
|
||||
this.emptyItemStack = bookmarkItem.getItemStack(0);
|
||||
this.realAmount = realAmount;
|
||||
this.realAmount = bookmarkItem.getAmount(realMultiplier);
|
||||
this.shiftAmount = shiftAmount;
|
||||
this.calculatedAmount = calculatedAmount;
|
||||
this.calculatedAmount = calculatedType != null ? bookmarkItem.getAmount(calculatedMultiplier) : this.realAmount;
|
||||
|
||||
this.group = group;
|
||||
this.calculatedType = calculatedType;
|
||||
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 = bookmarkItem.getMultiplier(realAmount);
|
||||
this.calculatedMultiplier = bookmarkItem.getMultiplier(calculatedAmount);
|
||||
this.realMultiplier = realMultiplier;
|
||||
this.calculatedMultiplier = calculatedMultiplier;
|
||||
|
||||
if (this.group.crafting != null && this.group.crafting.calculatedItems.containsKey(this.itemIndex)) {
|
||||
this.realType = this.group.crafting.calculatedItems.get(this.itemIndex).calculatedType
|
||||
@@ -95,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
|
||||
@@ -192,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;
|
||||
}
|
||||
@@ -347,17 +347,22 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
}
|
||||
|
||||
protected void drawStackSize(Rectangle4i rect, ShownItemType shownItemType) {
|
||||
boolean drawChance = this.calculatedMultiplier > 0;
|
||||
long amount = this.calculatedAmount;
|
||||
|
||||
if (shownItemType == null || shownItemType == ShownItemType.REAL || this.calculatedType == null) {
|
||||
amount = this.realAmount;
|
||||
drawChance = this.realMultiplier > 0;
|
||||
} else if (shownItemType == ShownItemType.SHIFT) {
|
||||
amount = this.shiftAmount;
|
||||
drawChance = false;
|
||||
}
|
||||
|
||||
if (amount > 0 || shownItemType == ShownItemType.REAL && this.bookmarkItem.factor > 0) {
|
||||
if (amount > 0 || this.bookmarkItem.chance != PositionedStack.CHANCE_FULL && drawChance
|
||||
|| shownItemType == ShownItemType.REAL && !this.bookmarkItem.emptyFactor()) {
|
||||
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
|
||||
final long stackSize = this.bookmarkItem.getStackSize(amount);
|
||||
int color = 0xFFFFFF;
|
||||
String amountString = "";
|
||||
|
||||
if (stackSize < 10_000) {
|
||||
@@ -370,11 +375,16 @@ public class BookmarksGridSlot extends ItemsGridSlot {
|
||||
amountString += "L";
|
||||
}
|
||||
|
||||
if (this.bookmarkItem.chance != PositionedStack.CHANCE_FULL) {
|
||||
amountString = "~" + amountString;
|
||||
color = 0xFFAA00;
|
||||
}
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
amountString,
|
||||
new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2),
|
||||
panelFactor,
|
||||
0xFFFFFF,
|
||||
color,
|
||||
true,
|
||||
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
|
||||
}
|
||||
|
||||
@@ -42,34 +42,35 @@ public class RecipeChainDetails {
|
||||
|
||||
private final BookmarkItem item;
|
||||
|
||||
private long realAmount = 0L;
|
||||
private long realMultiplier = 0L;
|
||||
private long shiftAmount = 0L;
|
||||
private long calculatedAmount = 0L;
|
||||
private long calculatedMultiplier = 0L;
|
||||
|
||||
public CalculatedType calculatedType;
|
||||
|
||||
protected BookmarkChainItem(BookmarkItem item, long shiftItem, long calculatedItem,
|
||||
protected BookmarkChainItem(BookmarkItem item, long shiftItem, long calculatedMultiplier,
|
||||
CalculatedType calculatedType) {
|
||||
this.item = item.copy();
|
||||
this.shiftAmount = shiftItem;
|
||||
this.calculatedAmount = calculatedItem;
|
||||
this.calculatedMultiplier = calculatedMultiplier;
|
||||
this.calculatedType = calculatedType;
|
||||
}
|
||||
|
||||
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, long calculatedAmount,
|
||||
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, long calculatedMultiplier,
|
||||
CalculatedType calculatedType) {
|
||||
return new BookmarkChainItem(item, shiftItem, calculatedAmount, calculatedType);
|
||||
return new BookmarkChainItem(item, shiftItem, calculatedMultiplier, calculatedType);
|
||||
}
|
||||
|
||||
public static BookmarkChainItem of(BookmarkItem item, long shiftItem, CalculatedType calculatedType) {
|
||||
return new BookmarkChainItem(item, shiftItem, item.amount, calculatedType);
|
||||
return new BookmarkChainItem(item, shiftItem, item.getMultiplier(), calculatedType);
|
||||
}
|
||||
|
||||
public static BookmarkChainItem of(BookmarkItem item) {
|
||||
final long amount = item.getAmount();
|
||||
return new BookmarkChainItem(
|
||||
item,
|
||||
item.amount,
|
||||
item.amount,
|
||||
amount,
|
||||
item.getMultiplier(),
|
||||
item.type == BookmarkItemType.INGREDIENT ? CalculatedType.INGREDIENT : CalculatedType.RESULT);
|
||||
}
|
||||
|
||||
@@ -77,30 +78,34 @@ public class RecipeChainDetails {
|
||||
return this.item.getItemStack(amount);
|
||||
}
|
||||
|
||||
public void setRealAmount(long amount) {
|
||||
this.realAmount = amount;
|
||||
public void setRealMultiplier(long multiplier) {
|
||||
this.realMultiplier = multiplier;
|
||||
}
|
||||
|
||||
public long getRealAmount() {
|
||||
return this.realAmount;
|
||||
public long getRealMultiplier() {
|
||||
return this.realMultiplier;
|
||||
}
|
||||
|
||||
public void setCalculatedMultiplier(long multiplier) {
|
||||
this.calculatedMultiplier = multiplier;
|
||||
}
|
||||
|
||||
public long getCalculatedMultiplier() {
|
||||
return this.calculatedMultiplier;
|
||||
}
|
||||
|
||||
public long getShiftAmount() {
|
||||
return this.shiftAmount;
|
||||
}
|
||||
|
||||
public long getCalculatedAmount() {
|
||||
return this.calculatedAmount;
|
||||
}
|
||||
|
||||
public BookmarkItem getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public void append(long amount, long calculatedAmount) {
|
||||
this.shiftAmount += amount;
|
||||
this.calculatedAmount += calculatedAmount;
|
||||
this.item.amount = this.calculatedAmount;
|
||||
this.calculatedMultiplier += this.item.getMultiplierFromAmount(calculatedAmount);
|
||||
this.item.multiplier = this.calculatedMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,10 +185,9 @@ public class RecipeChainDetails {
|
||||
if (itemIndex >= 0 && this.calculatedItems.containsKey(itemIndex)) {
|
||||
final BookmarkItem item = entry.getValue();
|
||||
if (this.recipeInMiddle.contains(item.recipeId)) {
|
||||
this.calculatedItems.get(itemIndex)
|
||||
.setRealAmount(Math.max(0, item.factor * (item.getMultiplier() - 1)));
|
||||
this.calculatedItems.get(itemIndex).setRealMultiplier(Math.max(0, item.getMultiplier() - 1));
|
||||
} else {
|
||||
this.calculatedItems.get(itemIndex).setRealAmount(item.amount);
|
||||
this.calculatedItems.get(itemIndex).setRealMultiplier(item.getMultiplier());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,7 +243,7 @@ public class RecipeChainDetails {
|
||||
|
||||
for (BookmarkItem item : chainItems) {
|
||||
if (recipeRelations.contains(item.recipeId)) {
|
||||
subChainItems.add(item.copyWithAmount(recipeId.equals(item.recipeId) ? item.amount : 0));
|
||||
subChainItems.add(item.copyWithMultiplier(recipeId.equals(item.recipeId) ? item.multiplier : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +262,8 @@ public class RecipeChainDetails {
|
||||
}
|
||||
|
||||
if (this.calculatedItems.containsKey(itemIndex)) {
|
||||
this.calculatedItems.get(itemIndex).setRealAmount(value.getShiftAmount());
|
||||
this.calculatedItems.get(itemIndex)
|
||||
.setRealMultiplier(value.getItem().getMultiplierFromAmount(value.getShiftAmount()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,18 +277,19 @@ public class RecipeChainDetails {
|
||||
for (RecipeId relRecipeId : recipeRelations) {
|
||||
for (BookmarkItem item : math.recipeResults) {
|
||||
if (item.recipeId.equals(relRecipeId)) {
|
||||
final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L);
|
||||
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
|
||||
|
||||
if (item.recipeId.equals(recipeId)) {
|
||||
multiplier.put(item.recipeId, item.getMultiplier());
|
||||
items.computeIfAbsent(
|
||||
StackInfo.getItemStackGUID(item.itemStack),
|
||||
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT))
|
||||
.append(amount, item.amount);
|
||||
.append(amount, item.getAmount());
|
||||
} else if (amount > 0) {
|
||||
items.computeIfAbsent(
|
||||
StackInfo.getItemStackGUID(item.itemStack),
|
||||
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT)).append(amount, amount);
|
||||
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.RESULT))
|
||||
.append(amount, item.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,17 +298,17 @@ public class RecipeChainDetails {
|
||||
if (item.recipeId.equals(relRecipeId)) {
|
||||
final BookmarkItem prefItem = math.preferredItems.get(item);
|
||||
final long amount = math.requiredAmount.containsKey(prefItem) ? 0
|
||||
: math.requiredAmount.getOrDefault(item, item.amount);
|
||||
: math.requiredAmount.getOrDefault(item, item.getAmount());
|
||||
final long refAmount = prefItem != null && !topLevelRecipes.contains(prefItem.recipeId)
|
||||
? math.requiredAmount.getOrDefault(prefItem, 0L)
|
||||
: 0;
|
||||
|
||||
if (amount != 0 || item.amount > refAmount && math.requiredAmount.containsKey(prefItem)
|
||||
if (amount != 0 || item.getAmount() > refAmount && math.requiredAmount.containsKey(prefItem)
|
||||
|| recipeId.equals(item.recipeId) && multiplier.get(recipeId) == 0) {
|
||||
items.computeIfAbsent(
|
||||
StackInfo.getItemStackGUID(item.itemStack),
|
||||
is -> BookmarkChainItem.of(item, 0, 0, CalculatedType.INGREDIENT))
|
||||
.append(amount, item.amount - refAmount);
|
||||
.append(amount, item.getAmount() - refAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,7 +318,7 @@ public class RecipeChainDetails {
|
||||
}
|
||||
|
||||
private void generateResult(RecipeChainMath math, BookmarkItem item, int itemIndex) {
|
||||
final long amount = item.amount - math.requiredAmount.getOrDefault(item, 0L);
|
||||
final long amount = item.getAmount() - math.requiredAmount.getOrDefault(item, 0L);
|
||||
final CalculatedType calculatedType = math.outputRecipes.containsKey(item.recipeId) ? CalculatedType.RESULT
|
||||
: CalculatedType.REMAINDER;
|
||||
|
||||
@@ -322,7 +328,7 @@ public class RecipeChainDetails {
|
||||
private void generateIngredient(RecipeChainMath math, BookmarkItem item, int itemIndex) {
|
||||
final BookmarkItem prefItem = math.preferredItems.get(item);
|
||||
final long amount = math.requiredAmount.containsKey(prefItem) ? 0
|
||||
: math.requiredAmount.getOrDefault(item, item.amount);
|
||||
: math.requiredAmount.getOrDefault(item, item.getAmount());
|
||||
|
||||
this.calculatedItems.put(itemIndex, BookmarkChainItem.of(item, amount, CalculatedType.INGREDIENT));
|
||||
}
|
||||
|
||||
@@ -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
Loaded 100 of 224 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user