mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-15 09:44:44 +00:00
Compare commits
301
Commits
2.7.27
...
2.8.155-GTNH
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2782f8252e | ||
|
|
a5538b2566 | ||
|
|
a1f324b796 | ||
|
|
f3b6be26df | ||
|
|
9c764e3682 | ||
|
|
08605c3d73 | ||
|
|
9ec1e0f0cd | ||
|
|
e61151186c | ||
|
|
f7787a9a22 | ||
|
|
a91083bb5a | ||
|
|
51f830bdf6 | ||
|
|
9326ac48ad | ||
|
|
c125b71ae0 | ||
|
|
805b90d6f8 | ||
|
|
900b9f90e7 | ||
|
|
06d4a96c3a | ||
|
|
9a5a4a77ef | ||
|
|
4d874ec910 | ||
|
|
7f0f2466a5 | ||
|
|
83e1966ff2 | ||
|
|
548f754823 | ||
|
|
e036fd14a8 | ||
|
|
463f4c9b60 | ||
|
|
5070fde640 | ||
|
|
454b4f7e1d | ||
|
|
7b83456f01 | ||
|
|
4b7b84b031 | ||
|
|
75309b81ec | ||
|
|
24e46bb43e | ||
|
|
2ba0773401 | ||
|
|
eac913c80c | ||
|
|
126f0ea73c | ||
|
|
cd4668ec84 | ||
|
|
7c247dd0d2 | ||
|
|
87b549fab5 | ||
|
|
8ed1f7a043 | ||
|
|
f2efa35654 | ||
|
|
bcef287e71 | ||
|
|
7f555f7a7f | ||
|
|
9fd38da205 | ||
|
|
2fa5563408 | ||
|
|
541c0e2def | ||
|
|
ac868097e7 | ||
|
|
2f0de0b6a8 | ||
|
|
32bc29e654 | ||
|
|
ef8a7d8bd5 | ||
|
|
adc5a5df16 | ||
|
|
a828ff227a | ||
|
|
9b6f98330e | ||
|
|
667c8cb5b6 | ||
|
|
a186f69444 | ||
|
|
16f4f20239 | ||
|
|
bac207cacb | ||
|
|
630e856d83 | ||
|
|
42d96aed0c | ||
|
|
bd6384bf24 | ||
|
|
af9726d356 | ||
|
|
1e1d2f1076 | ||
|
|
7cb94fb22a | ||
|
|
87aa4f9ed5 | ||
|
|
9760fd3eaf | ||
|
|
1054871982 | ||
|
|
d90a667f68 | ||
|
|
69e6df5848 | ||
|
|
25fa7ce44e | ||
|
|
e3cd7b860d | ||
|
|
2a3aabc073 | ||
|
|
76db43c835 | ||
|
|
40e53eea7a | ||
|
|
6c4d16d6b9 | ||
|
|
4a2abe9fd7 | ||
|
|
d47417302e | ||
|
|
e13401ded8 | ||
|
|
7ada842ef3 | ||
|
|
fe0c5d4e50 | ||
|
|
2ea9ae1eb9 | ||
|
|
f7f0596fda | ||
|
|
d5e919a9cd | ||
|
|
a0ed5da699 | ||
|
|
99fb5a656e | ||
|
|
4b663222b1 | ||
|
|
3e5d0088bc | ||
|
|
948be399d2 | ||
|
|
5f6091924d | ||
|
|
c5bd1f897a | ||
|
|
61d4b6ab62 | ||
|
|
791d05d5f5 | ||
|
|
66001275c4 | ||
|
|
db6b83c906 | ||
|
|
58ef6c85e0 | ||
|
|
ab0aeef872 | ||
|
|
23b7c69e2f | ||
|
|
bb89384493 | ||
|
|
e96e4dfdde | ||
|
|
0a6a6771d8 | ||
|
|
c96199a727 | ||
|
|
ab4ea2cfa1 | ||
|
|
b679883d02 | ||
|
|
0048ef80d0 | ||
|
|
84576aa7a2 | ||
|
|
c5cd5b9e61 | ||
|
|
b1bedcf403 | ||
|
|
bba0a11466 | ||
|
|
b80948f2f2 | ||
|
|
28539bbacc | ||
|
|
ec28bdf116 | ||
|
|
e5d55a9bd8 | ||
|
|
c1f7489de1 | ||
|
|
fa6fd01a3a | ||
|
|
453ba6667f | ||
|
|
4e11a45852 | ||
|
|
95274339a0 | ||
|
|
31fcd6af51 | ||
|
|
860bc059ca | ||
|
|
2fc2152809 | ||
|
|
57e865fc6f | ||
|
|
6dcd26a978 | ||
|
|
aeb59081d9 | ||
|
|
ef8706ae38 | ||
|
|
b49d066231 | ||
|
|
594fbe6f79 | ||
|
|
3edb85b317 | ||
|
|
5fd0a74363 | ||
|
|
2ae94e94a0 | ||
|
|
fa3adde3e6 | ||
|
|
56941cc45d | ||
|
|
cd7d014704 | ||
|
|
0634466851 | ||
|
|
b87e389132 | ||
|
|
c788a07cf5 | ||
|
|
7d4ed91e86 | ||
|
|
ca79cdd3c9 | ||
|
|
3a0722adc2 | ||
|
|
77fa3bafd6 | ||
|
|
e23e542aac | ||
|
|
7ec2ad132e | ||
|
|
7b43826d05 | ||
|
|
c7a18dd288 | ||
|
|
4fb1d7b1d8 | ||
|
|
081071562f | ||
|
|
81e05dc030 | ||
|
|
efc54fb662 | ||
|
|
6e75124ea2 | ||
|
|
2c9f17983a | ||
|
|
e0b6d62143 | ||
|
|
3d5e9aecf0 | ||
|
|
07cec30f3e | ||
|
|
f3add01e74 | ||
|
|
d469fdc90d | ||
|
|
763d9b8cd4 | ||
|
|
55ccb70c8b | ||
|
|
2e1c7764b7 | ||
|
|
9daa9cabb2 | ||
|
|
74dc7c34d9 | ||
|
|
e5f85e32ec | ||
|
|
5383e0891a | ||
|
|
6142d4cc6d | ||
|
|
6aa6b2e6d2 | ||
|
|
29ade6f327 | ||
|
|
5ad6095c43 | ||
|
|
8ae3304861 | ||
|
|
0dc7bc1228 | ||
|
|
19d8b42993 | ||
|
|
3b7eae67af | ||
|
|
64a747e4bd | ||
|
|
4f60928b61 | ||
|
|
8dba2ff4c1 | ||
|
|
eb40e3b112 | ||
|
|
98c8981f05 | ||
|
|
1ed35c8357 | ||
|
|
6249bab886 | ||
|
|
b525760dd3 | ||
|
|
83b4855f5e | ||
|
|
75352caf11 | ||
|
|
475f5811f6 | ||
|
|
7503e1dfcb | ||
|
|
9d1f475941 | ||
|
|
ac50d1b27e |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
name: Optimize images
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
push:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
jobs:
|
||||
optimize-images:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
|
||||
secrets: inherit
|
||||
+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.
|
||||
@@ -22,6 +24,59 @@ If you have issues with NEI outside of the GTNH modpack you may report them in t
|
||||
* Cycle between Recipe, Utility, and Cheat mode by ctrl clicking on the Wrench Icon
|
||||
* GT5u Tools/Items and GT6 tools should now properly work with the Overlay Recipe Transfer
|
||||
|
||||
### Information Page Handler
|
||||
Want to add some information about a block or item without making a massive tooltip for it? You can add information about any block or item by registering it in the Information Handler.
|
||||
|
||||
Information page(s) are displayed when either the uses or recipes of a matching item are searched. All matching items will cycle at the top of the information page.
|
||||
|
||||
Your mod can call:
|
||||
```java
|
||||
FMLInterModComms.sendMessage("NotEnoughItems", "addItemInfo", nbt);
|
||||
```
|
||||
Where nbt is an NBTTagCompound formatted as described below.
|
||||
|
||||
| Tag | Type | Description |
|
||||
|------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `filter` | String | A string filter to match what items should return this information page.<br/>modname:itemname, metadata, Ore Dictionary tags, and regex can all be used. The full format may be seen in `config/NEI/collapsibleitems.cfg`. |
|
||||
| `page` / `pages` | String / String NBTTagList | The actual text to display. See below for differences between single and multi-page messages.<br/>Both formats will automatically translate provided strings. |
|
||||
|
||||
#### Single Page Format
|
||||
|
||||
Use the `"page"` string tag when you only want to add one page of information:
|
||||
```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);
|
||||
```
|
||||
|
||||
#### Multiple Page Format
|
||||
|
||||
Use the `"pages"` tag as an `NBTTagList` of strings when you want more than one page:
|
||||
```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);
|
||||
```
|
||||
* Each list entry is one page.
|
||||
* Pages will appear in NEI with buttons to scroll through them.
|
||||
|
||||
#### Item Matching
|
||||
|
||||
All items that match `filter` will show your information page. See `config/NEI/collapsibleitems.cfg` for the full filter format.
|
||||
|
||||
#### Item Information Config File
|
||||
|
||||
If you're a modpack creator and want to make custom item information pages, they can easily be added in `config/NEI/informationpages.cfg`, which has a few more examples.
|
||||
|
||||
## Other items of note:
|
||||
|
||||
* Remove TMI style
|
||||
|
||||
@@ -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.
|
||||
@@ -1,3 +1,29 @@
|
||||
compileJava {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
maxHeapSize = "128m"
|
||||
arguments += ['-package', 'codechicken.nei.search', '-visitor', '-no-listener']
|
||||
outputDirectory = file("${projectDir}/build/generated/sources/search/antlr/codechicken/nei/search/")
|
||||
}
|
||||
|
||||
compileJava.dependsOn generateGrammarSource
|
||||
sourcesJar.dependsOn generateGrammarSource
|
||||
|
||||
sourcesJar {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs += ["build/generated/sources/search/antlr/"]
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//version: 1707058017
|
||||
|
||||
plugins {
|
||||
id 'antlr'
|
||||
id 'com.gtnewhorizons.gtnhconvention'
|
||||
}
|
||||
+36
-2
@@ -1,8 +1,42 @@
|
||||
// Add your dependencies here
|
||||
configurations.create('gtnhlibEmbed') {
|
||||
canBeConsumed = false
|
||||
canBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.1:dev')
|
||||
implementation('com.github.GTNewHorizons:GTNHLib:0.6.5:dev')
|
||||
api('com.github.GTNewHorizons:CodeChickenCore:1.4.21:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GTNHLib:0.11.49: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')
|
||||
|
||||
testImplementation(platform('org.junit:junit-bom:5.13.4'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
testImplementation("org.mockito:mockito-core:5.+")
|
||||
|
||||
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
|
||||
gtnhlibEmbed("com.github.GTNewHorizons:GTNHLib:0.11.49") { transitive = false }
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-6
@@ -14,7 +14,7 @@ modId = NotEnoughItems
|
||||
modGroup = codechicken.nei
|
||||
|
||||
# Whether to use modGroup as the maven publishing group.
|
||||
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
|
||||
# When false, com.github.GTNewHorizons is used.
|
||||
useModGroupForPublishing = false
|
||||
|
||||
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
|
||||
@@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
|
||||
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
|
||||
# See https://github.com/bsideup/jabel for details on how this works.
|
||||
enableModernJavaSyntax = true
|
||||
# Enables modern Java syntax support. Valid values:
|
||||
# - false: No modern syntax, Java 8 only
|
||||
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||
# - modern: Native modern Java bytecode, no downgrading
|
||||
enableModernJavaSyntax = jabel
|
||||
|
||||
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||
# and should *not* be used in most situations. -1 disables this.
|
||||
# forceToolchainVersion = -1
|
||||
|
||||
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||
# downgradeTargetVersion = 8
|
||||
|
||||
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||
# jvmDowngraderMultiReleaseVersions = 21,25
|
||||
|
||||
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||
# - shade: Shade minimized stubs into the jar
|
||||
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||
# - (empty): Warning reminding you to configure stubs
|
||||
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||
# jvmDowngraderStubsProvider =
|
||||
|
||||
# Enables injecting missing generics into the decompiled source code for a better coding experience.
|
||||
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
|
||||
@@ -96,7 +120,9 @@ usesMixinDebug = false
|
||||
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
||||
mixinPlugin =
|
||||
|
||||
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
|
||||
# Specify the package that contains all of your Mixins. The package must exist or
|
||||
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||
# it must match with this or the build will fail.
|
||||
mixinsPackage =
|
||||
|
||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||
@@ -151,7 +177,7 @@ modrinthProjectId = notenoughitems-unofficial
|
||||
# type can be one of [project, version],
|
||||
# and the name is the Modrinth project or version slug/id of the other mod.
|
||||
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
|
||||
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
|
||||
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
|
||||
modrinthRelations = required-project\:codechickencore-unofficial
|
||||
|
||||
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
|
||||
@@ -160,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],
|
||||
@@ -172,6 +203,12 @@ curseForgeRelations = requiredDependency\:codechickencore-unofficial
|
||||
# projects. New projects should not use this parameter.
|
||||
# customArchiveBaseName =
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
|
||||
# runClientWorkingDirectory = run/client
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
|
||||
# runServerWorkingDirectory = run/server
|
||||
|
||||
# Optional parameter to have the build automatically fail if an illegal version is used.
|
||||
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
|
||||
# The check is ONLY performed if the version is a git tag.
|
||||
@@ -201,3 +238,6 @@ curseForgeRelations = requiredDependency\:codechickencore-unofficial
|
||||
# This is meant to be set in $HOME/.gradle/gradle.properties.
|
||||
# ideaCheckSpotlessOnBuild = true
|
||||
|
||||
# Non-GTNH properties
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.parallel = true
|
||||
@@ -0,0 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
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-8.11.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
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -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/.
|
||||
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -115,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -173,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -206,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
Vendored
+12
-24
@@ -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,30 +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
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
@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%
|
||||
+5
-1
@@ -1,4 +1,8 @@
|
||||
// Add any additional repositiroes for your dependencies here
|
||||
// Add any additional repositories for your dependencies here
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Modrinth"
|
||||
url = "https://api.modrinth.com/maven"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.31'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.31'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
lexer grammar SearchExpressionLexer;
|
||||
|
||||
// Antlr4 generates imports with .*
|
||||
@header {
|
||||
// CHECKSTYLE:OFF
|
||||
import codechicken.nei.SearchTokenParser;
|
||||
}
|
||||
|
||||
@members {
|
||||
private SearchTokenParser searchParser;
|
||||
|
||||
public SearchExpressionLexer(CharStream input, SearchTokenParser searchParser) {
|
||||
this(input);
|
||||
this.searchParser = searchParser;
|
||||
}
|
||||
}
|
||||
|
||||
// Lexer rules
|
||||
REGEX_LEFT : 'r'? '/' -> pushMode(REGEX) ;
|
||||
QUOTE_LEFT : '"' -> pushMode(QUOTED) ;
|
||||
|
||||
DASH : '-' ;
|
||||
|
||||
RECIPE_INGREDIENTS : '<&' | '<' ;
|
||||
RECIPE_RESULT : '>&' | '>' ;
|
||||
RECIPE_OTHERS : '^&' | '^' ;
|
||||
|
||||
OR : '|' ;
|
||||
LEFT_BRACKET : '{' ;
|
||||
RIGHT_BRACKET : '}' ;
|
||||
NEWLINE_OR_TAB : [\t\r\n] -> skip ;
|
||||
SPACE : ' ' ;
|
||||
ESCAPED_SYMBOL : '\\' . ;
|
||||
|
||||
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
|
||||
CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
|
||||
|
||||
mode REGEX;
|
||||
|
||||
REGEX_CONTENT : (~[/] | '\\/')+ ;
|
||||
REGEX_RIGHT : '/' -> popMode ;
|
||||
|
||||
mode QUOTED;
|
||||
|
||||
QUOTED_CONTENT : (~["] | '\\"')+ ;
|
||||
QUOTE_RIGHT : '"' -> popMode ;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
parser grammar SearchExpressionParser;
|
||||
|
||||
// Antlr4 generates imports with .*
|
||||
@header {
|
||||
// CHECKSTYLE:OFF
|
||||
}
|
||||
|
||||
options { tokenVocab=SearchExpressionLexer; }
|
||||
|
||||
// Parser rules
|
||||
|
||||
// Should only be used for recipes. Thanks to antlr inability to properly
|
||||
// inherit grammars, this is included where it's not supposed to be
|
||||
recipeSearchExpression
|
||||
: recipeClauseExpression*
|
||||
;
|
||||
|
||||
recipeClauseExpression
|
||||
: ingredientsPrefix=RECIPE_INGREDIENTS searchExpression[0, $ingredientsPrefix.getText().length() > 1]
|
||||
| resultPrefix=RECIPE_RESULT searchExpression[1, $resultPrefix.getText().length() > 1]
|
||||
| othersPrefix=RECIPE_OTHERS searchExpression[2, $othersPrefix.getText().length() > 1]
|
||||
| searchExpression[3, false]
|
||||
;
|
||||
|
||||
// General search expression rules
|
||||
searchExpression[int type, boolean allRecipe]
|
||||
: orExpression
|
||||
;
|
||||
|
||||
orExpression
|
||||
: sequenceExpression (OR sequenceExpression)*
|
||||
;
|
||||
|
||||
sequenceExpression
|
||||
: (SPACE* unaryExpression SPACE*)+
|
||||
;
|
||||
|
||||
unaryExpression
|
||||
: LEFT_BRACKET orExpression RIGHT_BRACKET?
|
||||
| prefixedExpression
|
||||
| negateExpression
|
||||
| token['\0']
|
||||
;
|
||||
|
||||
negateExpression
|
||||
: DASH unaryExpression
|
||||
;
|
||||
|
||||
prefixedExpression
|
||||
locals [
|
||||
Character prefix
|
||||
]
|
||||
: prefixToken=PREFIX { $prefix = $prefixToken.getText().charAt(0); } token[$prefix]
|
||||
;
|
||||
|
||||
token[Character prefix]
|
||||
: regex[prefix]
|
||||
| quoted[prefix]
|
||||
| plainText
|
||||
;
|
||||
|
||||
regex[Character prefix]
|
||||
: REGEX_LEFT REGEX_CONTENT REGEX_RIGHT?
|
||||
;
|
||||
|
||||
quoted[Character prefix]
|
||||
: QUOTE_LEFT QUOTED_CONTENT QUOTE_RIGHT?
|
||||
;
|
||||
|
||||
plainText
|
||||
: (CLEAN_SYMBOL | ESCAPED_SYMBOL)+
|
||||
;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
|
||||
public abstract class AbstractSubpanel<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
|
||||
extends Widget implements IContainerTooltipHandler {
|
||||
|
||||
final DrawableResource DASH = new DrawableBuilder("nei:textures/dash.png", 0, 0, 6, 1).setTextureSize(6, 1).build();
|
||||
|
||||
protected int mouseDownItemIndex = -1;
|
||||
protected int splittingLineColor = 0xffffffff;
|
||||
protected int linePaddingStart = 0;
|
||||
protected T grid;
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.grid.isEmpty();
|
||||
}
|
||||
|
||||
public void draw(int mousex, int mousey) {
|
||||
drawSplittingLine();
|
||||
this.grid.draw(mousex, mousey);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
this.grid.update();
|
||||
}
|
||||
|
||||
public abstract int setPanelWidth(int width);
|
||||
|
||||
public void resize(GuiContainer gui) {
|
||||
this.grid.setGridSize(x, y + 4, w, h - 8);
|
||||
this.grid.refresh(gui);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mousex, int mousey) {
|
||||
final ItemsGridSlot slot = getSlotMouseOver(mousex, mousey);
|
||||
return slot == null ? null : slot.getItemStack();
|
||||
}
|
||||
|
||||
public ItemsGridSlot getSlotMouseOver(int mousex, int mousey) {
|
||||
return this.grid.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
protected void updateLinePadding() {
|
||||
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
this.linePaddingStart = 0;
|
||||
|
||||
while (GuiInfo
|
||||
.hideItemPanelSlot(guiContainer, new Rectangle4i(this.x + this.linePaddingStart, this.y - 1, 10, 2))) {
|
||||
this.linePaddingStart += 10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void drawSplittingLine() {
|
||||
float alpha = (this.splittingLineColor >> 24 & 255) / 255.0F;
|
||||
float red = (this.splittingLineColor >> 16 & 255) / 255.0F;
|
||||
float green = (this.splittingLineColor >> 8 & 255) / 255.0F;
|
||||
float blue = (this.splittingLineColor & 255) / 255.0F;
|
||||
|
||||
int width = this.grid.columns * ItemsGrid.SLOT_SIZE - this.linePaddingStart;
|
||||
int repeat = width / 6;
|
||||
int shiftX = this.x + this.linePaddingStart + (this.w - width) / 2;
|
||||
|
||||
GL11.glColor4f(red, green, blue, alpha);
|
||||
for (int i = 0; i < repeat; i++) {
|
||||
DASH.draw(shiftX + i * 6, this.y);
|
||||
}
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
|
||||
|
||||
if (this.mouseDownItemIndex >= 0 && ItemPanels.itemPanel.draggedStack == null
|
||||
&& NEIClientUtils.getHeldItem() == null
|
||||
&& NEIClientConfig.hasSMPCounterPart()) {
|
||||
|
||||
final ItemsGridSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (mouseOverSlot == null || mouseOverSlot.itemIndex != this.mouseDownItemIndex || heldTime > 500) {
|
||||
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(
|
||||
this.grid.getItem(this.mouseDownItemIndex));
|
||||
this.mouseDownItemIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mousex, int mousey, int button) {
|
||||
if (handleClickExt(mousex, mousey, button)) return true;
|
||||
|
||||
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
if (hoverSlot != null) {
|
||||
|
||||
if (button == 2) {
|
||||
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
|
||||
} else {
|
||||
mouseDownItemIndex = hoverSlot.itemIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack);
|
||||
|
||||
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
|
||||
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
|
||||
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseUp(int mousex, int mousey, int button) {
|
||||
final ItemsGridSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hoverSlot != null && hoverSlot.itemIndex == this.mouseDownItemIndex
|
||||
&& ItemPanels.itemPanel.draggedStack == null) {
|
||||
|
||||
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|
||||
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
|
||||
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack().copy());
|
||||
} else {
|
||||
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.mouseDownItemIndex = -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
import codechicken.nei.BookmarkPanel.ItemStackMetadata;
|
||||
import codechicken.nei.recipe.BookmarkRecipeId;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
|
||||
public class BookmarkCraftingChain {
|
||||
|
||||
public HashMap<ItemStack, ItemStack> calculatedItems = new HashMap<>();
|
||||
public HashMap<ItemStack, Integer> multiplier = new HashMap<>();
|
||||
|
||||
public HashMap<ItemStack, ItemStack> inputs = new HashMap<>();
|
||||
public HashMap<ItemStack, ItemStack> outputs = new HashMap<>();
|
||||
public HashMap<ItemStack, ItemStack> remainder = new HashMap<>();
|
||||
public HashMap<ItemStack, ItemStack> intermediate = new HashMap<>();
|
||||
|
||||
protected static class CraftingChainItem {
|
||||
|
||||
public String guid;
|
||||
public int stackIndex = 0;
|
||||
public int recipeIndex = 0;
|
||||
public int factor = 0;
|
||||
public int count = 0;
|
||||
public boolean ingredient = false;
|
||||
public int fluidCellAmount = 1;
|
||||
public ItemStack stack = null;
|
||||
|
||||
public CraftingChainItem(ItemStack stack, ItemStackMetadata stackMetadata) {
|
||||
FluidStack fluid = StackInfo.getFluid(stack);
|
||||
this.recipeIndex = getRecipeIndex(stackMetadata.recipeId);
|
||||
this.ingredient = stackMetadata.ingredient;
|
||||
this.stack = stack;
|
||||
|
||||
if (fluid != null) {
|
||||
this.stackIndex = getStackIndex(fluid);
|
||||
this.count = fluid.amount * Math.max(0, stack.stackSize);
|
||||
this.fluidCellAmount = StackInfo.isFluidContainer(stack) ? fluid.amount : 1;
|
||||
this.factor = this.fluidCellAmount * stackMetadata.factor;
|
||||
} else {
|
||||
this.stackIndex = getStackIndex(stack);
|
||||
this.count = StackInfo.itemStackToNBT(stack).getInteger("Count");
|
||||
this.factor = stackMetadata.factor;
|
||||
}
|
||||
|
||||
this.guid = this.stackIndex + " " + this.recipeIndex;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack(long count) {
|
||||
return StackInfo.loadFromNBT(
|
||||
StackInfo.itemStackToNBT(this.stack),
|
||||
this.factor > 0 ? (count / this.fluidCellAmount) : 0);
|
||||
}
|
||||
|
||||
public static void clearStatic() {
|
||||
itemCache.clear();
|
||||
fluidCache.clear();
|
||||
recipeCache.clear();
|
||||
}
|
||||
|
||||
private static HashMap<ItemStack, Integer> itemCache = new HashMap<>();
|
||||
|
||||
private static int getStackIndex(ItemStack stackA) {
|
||||
|
||||
if (!itemCache.containsKey(stackA)) {
|
||||
|
||||
for (ItemStack item : itemCache.keySet()) {
|
||||
if (StackInfo.equalItemAndNBT(stackA, item, true)) {
|
||||
itemCache.put(stackA, itemCache.get(item));
|
||||
return itemCache.get(stackA);
|
||||
}
|
||||
}
|
||||
|
||||
itemCache.put(stackA, itemCache.size() + fluidCache.size());
|
||||
}
|
||||
|
||||
return itemCache.get(stackA);
|
||||
}
|
||||
|
||||
private static HashMap<FluidStack, Integer> fluidCache = new HashMap<>();
|
||||
|
||||
private static int getStackIndex(FluidStack fluidA) {
|
||||
|
||||
if (!fluidCache.containsKey(fluidA)) {
|
||||
for (FluidStack item : fluidCache.keySet()) {
|
||||
if (fluidA.isFluidEqual(item)) {
|
||||
fluidCache.put(fluidA, fluidCache.get(item));
|
||||
return fluidCache.get(fluidA);
|
||||
}
|
||||
}
|
||||
|
||||
fluidCache.put(fluidA, itemCache.size() + fluidCache.size());
|
||||
}
|
||||
|
||||
return fluidCache.get(fluidA);
|
||||
}
|
||||
|
||||
private static HashMap<BookmarkRecipeId, Integer> recipeCache = new HashMap<>();
|
||||
|
||||
private static int getRecipeIndex(BookmarkRecipeId recipe) {
|
||||
|
||||
if (recipe == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!recipeCache.containsKey(recipe)) {
|
||||
for (BookmarkRecipeId item : recipeCache.keySet()) {
|
||||
if (item.equals(recipe)) {
|
||||
recipeCache.put(recipe, recipeCache.get(item));
|
||||
return recipeCache.get(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
recipeCache.put(recipe, recipeCache.size());
|
||||
}
|
||||
|
||||
return recipeCache.get(recipe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class CraftingChainRequest {
|
||||
|
||||
public List<CraftingChainItem> items = new ArrayList<>();
|
||||
|
||||
public HashMap<Integer, Integer> multiplier = new HashMap<>();
|
||||
public HashMap<String, Long> counts = new HashMap<>();
|
||||
|
||||
public HashMap<Integer, HashSet<CraftingChainItem>> inputs = new HashMap<>();
|
||||
public HashMap<Integer, HashSet<CraftingChainItem>> outputs = new HashMap<>();
|
||||
|
||||
public HashSet<Integer> initialItems = new HashSet<>();
|
||||
public HashSet<Integer> startedIngrs = new HashSet<>();
|
||||
|
||||
public CraftingChainRequest(List<ItemStack> items, List<ItemStackMetadata> metadata) {
|
||||
HashSet<Integer> inputs = new HashSet<>();
|
||||
HashSet<Integer> outputs = new HashSet<>();
|
||||
|
||||
for (int index = 0; index < items.size(); index++) {
|
||||
CraftingChainItem item = new CraftingChainItem(items.get(index), metadata.get(index));
|
||||
item.guid = item.ingredient ? "i" + item.guid : "r" + item.guid;
|
||||
this.items.add(item);
|
||||
|
||||
if (this.multiplier.get(item.recipeIndex) == null) {
|
||||
this.multiplier.put(item.recipeIndex, 0);
|
||||
}
|
||||
|
||||
this.counts.put(item.guid, this.counts.getOrDefault(item.guid, 0L) + item.count);
|
||||
|
||||
if (item.ingredient) {
|
||||
inputs.add(item.recipeIndex);
|
||||
|
||||
if (this.inputs.get(item.stackIndex) == null) {
|
||||
this.inputs.put(item.stackIndex, new HashSet<>());
|
||||
}
|
||||
|
||||
this.inputs.get(item.stackIndex).add(item);
|
||||
} else {
|
||||
outputs.add(item.recipeIndex);
|
||||
|
||||
if (this.outputs.get(item.stackIndex) == null) {
|
||||
this.outputs.put(item.stackIndex, new HashSet<>());
|
||||
}
|
||||
|
||||
this.outputs.get(item.stackIndex).add(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (int recipeIndex : this.multiplier.keySet()) {
|
||||
if (recipeIndex == -1 || !inputs.contains(recipeIndex) || !outputs.contains(recipeIndex)) {
|
||||
this.initialItems.add(recipeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
for (CraftingChainItem item : this.items) {
|
||||
if (item.ingredient == false && !this.initialItems.contains(item.recipeIndex)) {
|
||||
this.startedIngrs.add(item.stackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
CraftingChainItem.clearStatic();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void refresh(List<ItemStack> items, List<ItemStackMetadata> metadata) {
|
||||
CraftingChainRequest request = new CraftingChainRequest(items, metadata);
|
||||
HashMap<Integer, Long> iShift = new HashMap<>();
|
||||
HashMap<Integer, Long> oShift = new HashMap<>();
|
||||
boolean change = true;
|
||||
int iteration = 100;
|
||||
|
||||
while (--iteration > 0 && change) {
|
||||
change = false;
|
||||
for (int stackIndex : request.startedIngrs) {
|
||||
change = calculateShift(request, stackIndex) || change;
|
||||
}
|
||||
}
|
||||
|
||||
this.inputs.clear();
|
||||
this.outputs.clear();
|
||||
this.remainder.clear();
|
||||
this.intermediate.clear();
|
||||
this.calculatedItems.clear();
|
||||
this.multiplier.clear();
|
||||
|
||||
for (CraftingChainItem item : request.items) {
|
||||
if (request.initialItems.contains(item.recipeIndex)) {
|
||||
long iCount = calculateCount(request, request.inputs.get(item.stackIndex))
|
||||
- iShift.getOrDefault(item.stackIndex, 0L);
|
||||
this.calculatedItems.put(item.stack, item.stack);
|
||||
|
||||
if (iCount == 0) {
|
||||
this.inputs.put(item.stack, item.getItemStack(0));
|
||||
} else {
|
||||
long count = Math.min(iCount, item.count);
|
||||
this.inputs.put(item.stack, item.getItemStack(count));
|
||||
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (CraftingChainItem item : request.items) {
|
||||
if (!request.initialItems.contains(item.recipeIndex)) {
|
||||
long count = item.count + item.factor * request.multiplier.get(item.recipeIndex);
|
||||
this.calculatedItems.put(item.stack, item.getItemStack(count));
|
||||
|
||||
if (item.ingredient) { // input
|
||||
long oCount = calculateCount(request, request.outputs.get(item.stackIndex))
|
||||
- oShift.getOrDefault(item.stackIndex, 0L);
|
||||
|
||||
if (oCount < count) {
|
||||
this.inputs.put(item.stack, item.getItemStack(count - oCount));
|
||||
oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + oCount);
|
||||
} else if (oCount >= count) {
|
||||
this.intermediate.put(item.stack, item.getItemStack(0));
|
||||
oShift.put(item.stackIndex, oShift.getOrDefault(item.stackIndex, 0L) + count);
|
||||
}
|
||||
|
||||
} else { // output
|
||||
long iCalcCount = calculateCount(request, request.inputs.get(item.stackIndex));
|
||||
long iCount = iCalcCount - iShift.getOrDefault(item.stackIndex, 0L);
|
||||
this.multiplier.put(item.stack, item.factor > 0 ? (int) (count / item.factor) : 0);
|
||||
|
||||
if (iCount >= count) {
|
||||
this.intermediate.put(item.stack, item.getItemStack(0));
|
||||
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + count);
|
||||
} else if (iCalcCount > 0) {
|
||||
this.remainder.put(item.stack, item.getItemStack(count - iCount));
|
||||
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount);
|
||||
} else {
|
||||
this.outputs.put(item.stack, item.getItemStack(count - iCount));
|
||||
iShift.put(item.stackIndex, iShift.getOrDefault(item.stackIndex, 0L) + iCount);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean calculateShift(CraftingChainRequest request, int stackIndex) {
|
||||
|
||||
if (!request.outputs.containsKey(stackIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int minRecipeIndex = 0;
|
||||
int minShift = Integer.MAX_VALUE;
|
||||
|
||||
for (CraftingChainItem item : request.outputs.get(stackIndex)) {
|
||||
if (!request.initialItems.contains(item.recipeIndex) && item.factor > 0) {
|
||||
long ingrCount = calculateCount(request, request.inputs.get(item.stackIndex), item.recipeIndex);
|
||||
long outputCount = calculateCount(request, request.outputs.get(item.stackIndex));
|
||||
long shift = (long) Math.ceil((ingrCount - outputCount) / (double) item.factor);
|
||||
|
||||
if (shift > 0 && shift < minShift) {
|
||||
minShift = (int) shift;
|
||||
minRecipeIndex = item.recipeIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minShift < Integer.MAX_VALUE) {
|
||||
request.multiplier.put(minRecipeIndex, request.multiplier.get(minRecipeIndex) + minShift);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private long calculateCount(CraftingChainRequest request, HashSet<CraftingChainItem> items) {
|
||||
return calculateCount(request, items, -1);
|
||||
}
|
||||
|
||||
private long calculateCount(CraftingChainRequest request, HashSet<CraftingChainItem> items, int recipeIndex) {
|
||||
long count = 0L;
|
||||
|
||||
if (items != null) {
|
||||
for (CraftingChainItem item : items) {
|
||||
if (item.recipeIndex != recipeIndex || recipeIndex == -1) {
|
||||
count += request.counts.get(item.guid) + item.factor * request.multiplier.get(item.recipeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -15,7 +15,7 @@ public abstract class Button extends Widget {
|
||||
}
|
||||
|
||||
public int contentWidth() {
|
||||
return getRenderIcon() == null ? getStringWidth(label) : getRenderIcon().width;
|
||||
return getRenderIcon() == null ? getStringWidth(getRenderLabel()) : getRenderIcon().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,6 +57,10 @@ public abstract class Button extends Widget {
|
||||
return label;
|
||||
}
|
||||
|
||||
public int getRenderLabelColor() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final String label;
|
||||
public Image icon;
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -19,13 +22,17 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiErrorScreen;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiSelectWorld;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
@@ -36,16 +43,20 @@ import com.google.common.collect.Lists;
|
||||
|
||||
import codechicken.core.ClassDiscoverer;
|
||||
import codechicken.core.ClientUtils;
|
||||
import codechicken.core.GuiModListScroll;
|
||||
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;
|
||||
import cpw.mods.fml.client.CustomModLoadingErrorDisplayException;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModClassLoader;
|
||||
import cpw.mods.fml.common.discovery.ASMDataTable;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
@@ -57,7 +68,6 @@ public class ClientHandler {
|
||||
private static ClientHandler instance;
|
||||
private final ArrayList<EntityItem> SMPmagneticItems = new ArrayList<>();
|
||||
private World lastworld;
|
||||
private GuiScreen lastGui;
|
||||
|
||||
public void addSMPMagneticItem(int i, World world) {
|
||||
WorldClient cworld = (WorldClient) world;
|
||||
@@ -144,6 +154,8 @@ public class ClientHandler {
|
||||
loadHeightHackHandlers();
|
||||
loadHiddenHandlers();
|
||||
loadEnableAutoFocus();
|
||||
loadGuiCraftablesBlacklist();
|
||||
loadDefaultBookmarkContainers();
|
||||
ItemInfo.preInit();
|
||||
StackInfo.loadGuidFilters();
|
||||
}
|
||||
@@ -161,11 +173,34 @@ public class ClientHandler {
|
||||
}, callback);
|
||||
}
|
||||
|
||||
public static void loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
|
||||
public static boolean loadSettingsResource(String resource, Consumer<Stream<String>> callback) {
|
||||
final ResourceLocation location = new ResourceLocation("nei", resource);
|
||||
|
||||
try {
|
||||
final IResource res = Minecraft.getMinecraft().getResourceManager().getResource(location);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
NEIClientConfig.logger.info("Loading '{}' from jar {}", resource, location);
|
||||
callback.accept(
|
||||
IOUtils.readLines(reader).stream()
|
||||
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
|
||||
.map(String::trim).filter(line -> !line.isEmpty()));
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (Exception io) {
|
||||
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, location);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean loadSettingsFile(String resource, BiConsumer<File, FileWriter> createDefault,
|
||||
Consumer<Stream<String>> callback) {
|
||||
File file = new File(NEIClientConfig.configDir, resource);
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!file.exists() && createDefault != null) {
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
NEIClientConfig.logger.info("Creating default '{}' {}", resource, file);
|
||||
createDefault.accept(file, writer);
|
||||
@@ -174,13 +209,20 @@ public class ClientHandler {
|
||||
}
|
||||
}
|
||||
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
|
||||
callback.accept(
|
||||
IOUtils.readLines(reader).stream().filter(line -> !line.startsWith("#") && !line.trim().isEmpty()));
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
|
||||
if (file.exists()) {
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
NEIClientConfig.logger.info("Loading '{}' file {}", resource, file);
|
||||
callback.accept(
|
||||
IOUtils.readLines(reader).stream()
|
||||
.map(line -> line.contains("#") ? line.substring(0, line.indexOf('#')) : line)
|
||||
.map(String::trim).filter(line -> !line.isEmpty()));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load '{}' file {}", resource, file, e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void loadSerialHandlers() {
|
||||
@@ -205,8 +247,27 @@ public class ClientHandler {
|
||||
public static void loadEnableAutoFocus() {
|
||||
loadSettingsFile(
|
||||
"enableautofocus.cfg",
|
||||
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines
|
||||
.collect(Collectors.toCollection(ArrayList::new)));
|
||||
lines -> AutoFocusWidget.enableAutoFocusPrefixes = lines.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static void loadGuiCraftablesBlacklist() {
|
||||
loadSettingsFile(
|
||||
"guicraftablesblacklist.cfg",
|
||||
lines -> ItemCraftablesPanel.guiBlacklist = lines.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static void loadDefaultBookmarkContainers() {
|
||||
loadSettingsFile("defaultbookmarkcontainers.cfg", lines -> {
|
||||
for (String clazz : lines.collect(Collectors.toList())) {
|
||||
try {
|
||||
API.registerBookmarkContainerHandler(
|
||||
(Class<? extends GuiContainer>) Class.forName(clazz),
|
||||
new DefaultBookmarkContainerHandler());
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Failed to load class '{}' for defaultbookmarkcontainers.cfg", clazz);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadHiddenItems() {
|
||||
@@ -216,10 +277,10 @@ public class ClientHandler {
|
||||
public static void load() {
|
||||
instance = new ClientHandler();
|
||||
|
||||
GuiModListScroll.register("NotEnoughItems");
|
||||
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();
|
||||
@@ -254,11 +315,34 @@ public class ClientHandler {
|
||||
}
|
||||
|
||||
public static void loadPluginsList() {
|
||||
final ClassDiscoverer classDiscoverer = new ClassDiscoverer(
|
||||
test -> test.startsWith("NEI") && test.endsWith("Config.class"),
|
||||
IConfigureNEI.class);
|
||||
final boolean jarjar = Launch.blackboard.getOrDefault("jarjar.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!jarjar) {
|
||||
// Do the old style class discovery
|
||||
final ClassDiscoverer classDiscoverer = new ClassDiscoverer(
|
||||
test -> test.startsWith("NEI") && test.endsWith("Config.class"),
|
||||
IConfigureNEI.class);
|
||||
|
||||
NEIClientConfig.pluginsList.addAll(classDiscoverer.findClasses());
|
||||
NEIClientConfig.pluginsList.addAll(classDiscoverer.findClasses());
|
||||
NEIClientConfig.logger
|
||||
.info("Loading NEI Plugins via ClassDiscoverer: " + NEIClientConfig.pluginsList.size());
|
||||
|
||||
} else {
|
||||
// If JarJar's loaded, the ASMDataTable now includes interfaces in addition to Annotations, so use that
|
||||
// instead Includes a small change in behavior of no longer requiring the class to start with NEI and end
|
||||
// with Config; This could easily be changed if desired, but the previous behavior was likely to speed
|
||||
// things up by limiting the scope when looking at all of the jars and classes in the classpath.
|
||||
final ASMDataTable dataTable = NEIModContainer.getAsmDataTable();
|
||||
final ModClassLoader modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader();
|
||||
for (ASMDataTable.ASMData asmData : dataTable.getAll(IConfigureNEI.class.getName().replace('.', '/'))) {
|
||||
final String className = asmData.getClassName().replace('/', '.');
|
||||
try {
|
||||
NEIClientConfig.pluginsList.add(Class.forName(className, true, modClassLoader));
|
||||
} catch (ClassNotFoundException e) {
|
||||
NEIClientConfig.logger.error("Failed to load plugin class {}", className, e);
|
||||
}
|
||||
}
|
||||
NEIClientConfig.logger.info("Loading NEI Plugins via JarJar: " + NEIClientConfig.pluginsList.size());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -281,18 +365,20 @@ public class ClientHandler {
|
||||
if (mc.currentScreen == null) NEIController.processCreativeCycling(mc.thePlayer.inventory);
|
||||
|
||||
updateMagnetMode(mc.theWorld, mc.thePlayer);
|
||||
} else {
|
||||
lastworld = null;
|
||||
}
|
||||
}
|
||||
|
||||
GuiScreen gui = mc.currentScreen;
|
||||
if (gui != lastGui) {
|
||||
if (gui instanceof GuiMainMenu) lastworld = null;
|
||||
else if (gui instanceof GuiSelectWorld) NEIClientConfig.reloadSaves();
|
||||
else if (gui == null) {
|
||||
/* prevent WorldClient reference being held in the Gui */
|
||||
NEIController.manager = null;
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void onGuiScreen(GuiOpenEvent event) {
|
||||
if (event.gui instanceof GuiSelectWorld) {
|
||||
NEIClientConfig.reloadSaves();
|
||||
} else if (event.gui == null) {
|
||||
/* prevent WorldClient reference being held in the Gui */
|
||||
NEIController.manager = null;
|
||||
GuiInfo.clearGuiHandlers();
|
||||
}
|
||||
lastGui = gui;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -311,14 +397,16 @@ public class ClientHandler {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (event.world == mc.theWorld) {
|
||||
NEIClientConfig.unloadWorld();
|
||||
ItemMobSpawner.clearEntityReferences();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadWorld(World world, boolean fromServer) {
|
||||
if (world != lastworld) {
|
||||
SMPmagneticItems.clear();
|
||||
WorldOverlayRenderer.reset();
|
||||
|
||||
if (!NEIClientConfig.getBooleanSetting("world.overlays.lock")) {
|
||||
WorldOverlayRenderer.reset();
|
||||
}
|
||||
if (!fromServer) {
|
||||
NEIClientConfig.setHasSMPCounterPart(false);
|
||||
NEIClientConfig.setInternalEnabled(false);
|
||||
@@ -326,8 +414,6 @@ public class ClientHandler {
|
||||
if (!Minecraft.getMinecraft().isSingleplayer()) // wait for server to initiate in singleplayer
|
||||
NEIClientConfig.loadWorld("remote/" + ClientUtils.getServerIP().replace(':', '~'));
|
||||
}
|
||||
ItemMobSpawner.clearEntityReferences(world);
|
||||
|
||||
lastworld = world;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -150,10 +151,16 @@ public class CollapsibleItems {
|
||||
}
|
||||
|
||||
public static void putItem(ItemStack stack) {
|
||||
final GroupItem group = CollapsibleItems.groups.stream().filter(g -> g.matches(stack)).findFirst().orElse(null);
|
||||
int groupIndex = -1;
|
||||
|
||||
if (group != null) {
|
||||
CollapsibleItems.cache.put(stack, CollapsibleItems.groups.indexOf(group));
|
||||
for (int i = 0; i < CollapsibleItems.groups.size() && groupIndex == -1; i++) {
|
||||
if (CollapsibleItems.groups.get(i).matches(stack)) {
|
||||
groupIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (groupIndex != -1) {
|
||||
CollapsibleItems.cache.put(stack, groupIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,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++) {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.api.IBookmarkContainerHandler;
|
||||
@@ -12,36 +13,18 @@ public class DefaultBookmarkContainerHandler implements IBookmarkContainerHandle
|
||||
|
||||
@Override
|
||||
public void pullBookmarkItemsFromContainer(GuiContainer guiContainer, ArrayList<ItemStack> bookmarkItems) {
|
||||
FastTransferManager manager = new FastTransferManager();
|
||||
LinkedList<ItemStack> containerStacks = manager.saveContainer(guiContainer.inventorySlots);
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final FastTransferManager manager = new FastTransferManager();
|
||||
|
||||
for (ItemStack bookmarkItem : bookmarkItems) {
|
||||
|
||||
int bookmarkSizeBackup = bookmarkItem.stackSize;
|
||||
|
||||
for (int i = 0; i < containerStacks.size() - 4 * 9; i++) { // Last 36 slots are player inventory
|
||||
ItemStack containerItem = containerStacks.get(i);
|
||||
|
||||
if (containerItem == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bookmarkItem.isItemEqual(containerItem)) {
|
||||
if (bookmarkItem.stackSize <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int transferAmount = Math.min(bookmarkItem.stackSize, containerItem.stackSize);
|
||||
|
||||
for (int i = 0; i < slots.size() - 4 * 9; i++) {
|
||||
final Slot slot = slots.get(i);
|
||||
if (slot.getHasStack() && bookmarkItem.isItemEqual(slot.getStack())) {
|
||||
final int transferAmount = Math.min(bookmarkItem.stackSize, slot.getStack().stackSize);
|
||||
manager.transferItems(guiContainer, i, transferAmount);
|
||||
bookmarkItem.stackSize -= transferAmount;
|
||||
|
||||
if (bookmarkItem.stackSize == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bookmarkItem.stackSize = bookmarkSizeBackup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
|
||||
public class DoubleFilteredFilter implements ItemFilter {
|
||||
|
||||
private ItemFilter filter;
|
||||
private ItemList.PatternItemFilter patternFilter;
|
||||
|
||||
public DoubleFilteredFilter(ItemFilter filter, String itemFilter) {
|
||||
this.filter = filter;
|
||||
this.patternFilter = new ItemList.PatternItemFilter(SearchField.getPattern(itemFilter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ItemStack item) {
|
||||
if (filter.matches(item)) {
|
||||
return patternFilter.matches(item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -89,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) {
|
||||
@@ -281,6 +283,27 @@ public class FastTransferManager {
|
||||
GuiContainerManager.getManager(window).handleSlotClick(slotIndex, button, modifier);
|
||||
}
|
||||
|
||||
public static int dropHeldItem(GuiContainer window) {
|
||||
final EntityPlayerSP player = window.mc.thePlayer;
|
||||
final ItemStack held = player.inventory.getItemStack();
|
||||
|
||||
if (held == null) return -1;
|
||||
|
||||
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)) {
|
||||
clickSlot(window, i, 0, 0);
|
||||
|
||||
if (player.inventory.getItemStack() == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean fillZoneWithHeldItem(GuiContainer window, int zoneIndex) {
|
||||
for (int transferTo : slotZones.get(zoneIndex)) {
|
||||
ItemStack held = NEIClientUtils.getHeldItem();
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.FavoriteStorage;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
|
||||
public class FavoriteRecipes {
|
||||
|
||||
protected static final class FastKey {
|
||||
|
||||
public final ItemStack stack;
|
||||
private int hash;
|
||||
|
||||
public FastKey(ItemStack stack) {
|
||||
this.stack = stack;
|
||||
|
||||
this.hash = 31 + Item.getIdFromItem(stack.getItem());
|
||||
this.hash = 31 * this.hash + stack.getItemDamage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
if (o instanceof FastKey k && this.stack.getItem() == k.stack.getItem()
|
||||
&& this.stack.getItemDamage() == k.stack.getItemDamage()) {
|
||||
return this.stack == k.stack || Objects.equals(this.stack.stackTagCompound, k.stack.stackTagCompound);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final RestartableTask singleRecipeFavoritesTask = new RestartableTask(
|
||||
"Generate Single-Recipe Favorites") {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
final List<ICraftingHandler> handlers = GuiCraftingRecipe.getCraftingHandlers("all");
|
||||
final Map<FastKey, RecipeHandlerRef> autogeneratedRecipes = new HashMap<>();
|
||||
final Set<FastKey> whitelistItems = new HashSet<>(ItemList.items.size());
|
||||
final FavoriteStorage storage = new FavoriteStorage();
|
||||
|
||||
for (ItemStack stack : ItemList.items) {
|
||||
whitelistItems.add(new FastKey(stack));
|
||||
}
|
||||
|
||||
if (interrupted()) return;
|
||||
|
||||
for (ICraftingHandler handler : handlers) {
|
||||
if (!GuiRecipeTab.getHandlerInfo(handler).getShowFavoritesButton()) continue;
|
||||
|
||||
for (int i = 0; i < handler.numRecipes(); i++) {
|
||||
RecipeHandlerRef ref = null;
|
||||
|
||||
for (PositionedStack result : getOutputs(handler, i)) {
|
||||
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
|
||||
final FastKey key = new FastKey(stack);
|
||||
|
||||
if (!whitelistItems.contains(key)) continue;
|
||||
|
||||
if (autogeneratedRecipes.get(key) == null) {
|
||||
|
||||
if (ref == null) {
|
||||
ref = RecipeHandlerRef.of(handler, i);
|
||||
}
|
||||
|
||||
autogeneratedRecipes.put(key, ref);
|
||||
} else {
|
||||
autogeneratedRecipes.remove(key);
|
||||
whitelistItems.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (interrupted()) return;
|
||||
}
|
||||
|
||||
final Map<RecipeHandlerRef, RecipeId> recipesCache = new HashMap<>();
|
||||
|
||||
for (Map.Entry<FastKey, RecipeHandlerRef> entry : autogeneratedRecipes.entrySet()) {
|
||||
final RecipeId recipeId = recipesCache
|
||||
.computeIfAbsent(entry.getValue(), r -> RecipeId.of(r.handler, r.recipeIndex));
|
||||
storage.add(entry.getKey().stack, recipeId);
|
||||
}
|
||||
|
||||
FavoriteRecipes.autoFavorites = storage;
|
||||
updateSubsetVisibility();
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
|
||||
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final String FAVORITE_ITEM = "FavoriteItem";
|
||||
private static final FavoriteStorage manualFavorites = new FavoriteStorage();
|
||||
private static FavoriteStorage autoFavorites = new FavoriteStorage();
|
||||
private static File favoriteFile;
|
||||
private static int revision = 0;
|
||||
|
||||
static {
|
||||
API.addSubset("Favorites.Manual", manualFavorites::contains);
|
||||
API.addSubset("Favorites.Generated", stack -> FavoriteRecipes.autoFavorites.contains(stack));
|
||||
}
|
||||
|
||||
private FavoriteRecipes() {}
|
||||
|
||||
public static void load() {
|
||||
String worldPath = "global";
|
||||
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.favorites.worldSpecific")) {
|
||||
worldPath = NEIClientConfig.getWorldPath();
|
||||
}
|
||||
|
||||
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
|
||||
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
favoriteFile = new File(dir, "favorites.ini");
|
||||
|
||||
if (!favoriteFile.exists()) {
|
||||
final File globalFavorites = new File(CommonUtils.getMinecraftDir(), "saves/NEI/global/favorites.ini");
|
||||
final File configFavorites = new File(NEIClientConfig.configDir, "favorites.ini");
|
||||
final File defaultFavorites = configFavorites.exists() ? configFavorites : globalFavorites;
|
||||
|
||||
if (defaultFavorites.exists()) {
|
||||
try {
|
||||
if (favoriteFile.createNewFile()) {
|
||||
InputStream src = new FileInputStream(defaultFavorites);
|
||||
OutputStream dst = new FileOutputStream(favoriteFile);
|
||||
|
||||
IOUtils.copy(src, dst);
|
||||
|
||||
src.close();
|
||||
dst.close();
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
private static void loadData() {
|
||||
|
||||
if (favoriteFile == null || !favoriteFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> itemStrings;
|
||||
|
||||
try (FileInputStream reader = new FileInputStream(favoriteFile)) {
|
||||
NEIClientConfig.logger.info("Loading favorites from file {}", favoriteFile);
|
||||
itemStrings = IOUtils.readLines(reader, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to load favorites from file {}", favoriteFile, e);
|
||||
return;
|
||||
}
|
||||
|
||||
final JsonParser parser = new JsonParser();
|
||||
boolean hasErrors = false;
|
||||
|
||||
manualFavorites.clear();
|
||||
|
||||
for (String itemStr : itemStrings) {
|
||||
|
||||
if (itemStr.isEmpty() || itemStr.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonObject jsonObject = parser.parse(itemStr).getAsJsonObject();
|
||||
|
||||
if (jsonObject.get("item") != null && jsonObject.get("recipeId") != null) {
|
||||
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
|
||||
final RecipeId recipeId = RecipeId.of(jsonObject.getAsJsonObject("recipeId"));
|
||||
final ItemStack stack = StackInfo.loadFromNBT(itemStackNBT);
|
||||
|
||||
if (recipeId == null || stack == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
|
||||
manualFavorites.add(stack, recipeId);
|
||||
} else {
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Failed to load favorite from json string: {}", itemStr);
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
|
||||
try {
|
||||
Files.copy(
|
||||
favoriteFile,
|
||||
new File(favoriteFile.getAbsolutePath() + ".backup-" + System.currentTimeMillis()));
|
||||
NEIClientConfig.logger.info("Backed up invalid favorite file to {}", favoriteFile);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to backup invalid favorite file to {}", favoriteFile, e);
|
||||
}
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack getManualFavorite(RecipeId recipeId) {
|
||||
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
|
||||
return manualFavorites.getItemStack(recipeId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static RecipeId getFavorite(ItemStack stack) {
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled() && stack != null) {
|
||||
final RecipeId recipeId = manualFavorites.getRecipeId(stack);
|
||||
return recipeId != null ? recipeId : autoFavorites.getRecipeId(stack);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack getFavorite(RecipeId recipeId) {
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled() && recipeId != null) {
|
||||
final ItemStack stack = manualFavorites.getItemStack(recipeId);
|
||||
return stack != null ? stack : autoFavorites.getItemStack(recipeId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int size() {
|
||||
return manualFavorites.size() + autoFavorites.size();
|
||||
}
|
||||
|
||||
public static void reload() {
|
||||
save();
|
||||
load();
|
||||
}
|
||||
|
||||
public static boolean hasManual() {
|
||||
return manualFavorites.size() > 0;
|
||||
}
|
||||
|
||||
public static boolean containsManual(ItemStack stack) {
|
||||
return getManualFavorite(stack) != null;
|
||||
}
|
||||
|
||||
public static boolean contains(ItemStack stack) {
|
||||
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;
|
||||
|
||||
manualFavorites.removeItemStack(stack);
|
||||
|
||||
if (recipeId != null) {
|
||||
manualFavorites.removeRecipeId(recipeId);
|
||||
manualFavorites.add(stack, recipeId);
|
||||
}
|
||||
|
||||
updateSubsetVisibility();
|
||||
}
|
||||
|
||||
public static void loadAutoGeneratedRecipes() {
|
||||
if (ItemList.loadFinished
|
||||
&& NEIClientConfig.getBooleanSetting("inventory.favorites.generateSingleRecipeFavorites")) {
|
||||
singleRecipeFavoritesTask.restart();
|
||||
} else {
|
||||
singleRecipeFavoritesTask.stop();
|
||||
autoFavorites.clear();
|
||||
|
||||
updateSubsetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
protected static void updateSubsetVisibility() {
|
||||
SubsetWidget.updateHiddenItems();
|
||||
FavoriteRecipes.revision++;
|
||||
}
|
||||
|
||||
public static int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public static void save() {
|
||||
|
||||
if (favoriteFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> strings = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<NBTTagCompound, RecipeId> entry : manualFavorites.getAllFavorites()) {
|
||||
|
||||
try {
|
||||
final JsonObject line = new JsonObject();
|
||||
|
||||
line.add("item", NBTJson.toJsonObject(entry.getKey()));
|
||||
line.add("recipeId", entry.getValue().toJsonObject());
|
||||
|
||||
strings.add(NBTJson.toJson(line));
|
||||
} catch (JsonSyntaxException e) {
|
||||
NEIClientConfig.logger.error("Failed to stringify favorites ItemStack to json string");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try (FileOutputStream output = new FileOutputStream(favoriteFile)) {
|
||||
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Failed to save favorites list to file {}", favoriteFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.ChatAllowedCharacters;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -20,6 +21,7 @@ public class FormattedTextField extends GuiTextField {
|
||||
protected FontRenderer fontRenderer;
|
||||
protected String rawText = "";
|
||||
protected String formattedText = "";
|
||||
protected String placeholder = "";
|
||||
protected boolean editable = true;
|
||||
protected int editableColor = 14737632;
|
||||
protected int notEditableColor = 7368816;
|
||||
@@ -36,6 +38,14 @@ public class FormattedTextField extends GuiTextField {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public TextFormatter getFormatter() {
|
||||
return this.formatter;
|
||||
}
|
||||
|
||||
public void setPlaceholder(String placeholder) {
|
||||
this.placeholder = placeholder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCursorCounter() {
|
||||
super.updateCursorCounter();
|
||||
@@ -51,6 +61,11 @@ public class FormattedTextField extends GuiTextField {
|
||||
super.setFocused(focus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String rawText) {
|
||||
super.setText(EnumChatFormatting.getTextWithoutFormattingCodes(rawText));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionPos(int p_146199_1_) {
|
||||
String text = this.getText();
|
||||
@@ -102,89 +117,140 @@ public class FormattedTextField extends GuiTextField {
|
||||
this.editable = p_146184_1_;
|
||||
}
|
||||
|
||||
protected boolean beforeWrite(String text) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeText(String textPart) {
|
||||
String newText = "";
|
||||
String substr = ChatAllowedCharacters.filerAllowedCharacters(textPart);
|
||||
int cursorPosition = getCursorPosition();
|
||||
int maxStringLength = getMaxStringLength();
|
||||
String text = getText();
|
||||
|
||||
if (text.isEmpty()) {
|
||||
newText = substr.substring(0, Math.min(maxStringLength, substr.length()));
|
||||
cursorPosition = newText.length();
|
||||
} else {
|
||||
int selectionEnd = getSelectionEnd();
|
||||
String textStart = text.substring(0, Math.min(cursorPosition, selectionEnd));
|
||||
String textEnd = text.substring(Math.max(cursorPosition, selectionEnd));
|
||||
newText = textStart + substr + textEnd;
|
||||
|
||||
if (newText.length() > maxStringLength) {
|
||||
newText = newText.substring(0, maxStringLength);
|
||||
}
|
||||
|
||||
cursorPosition = Math.min(textStart.length() + substr.length(), newText.length());
|
||||
}
|
||||
|
||||
if (beforeWrite(newText)) {
|
||||
setText(newText);
|
||||
setCursorPosition(cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNthWordFromPos(int p_146183_1_, int p_146183_2_) {
|
||||
return this.func_146197_a(p_146183_1_, p_146183_2_, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTextBox() {
|
||||
if (this.getVisible()) {
|
||||
if (this.getEnableBackgroundDrawing()) {
|
||||
drawRect(
|
||||
this.xPosition - 1,
|
||||
this.yPosition - 1,
|
||||
this.xPosition + this.width + 1,
|
||||
this.yPosition + this.height + 1,
|
||||
-6250336);
|
||||
drawRect(
|
||||
this.xPosition,
|
||||
this.yPosition,
|
||||
this.xPosition + this.width,
|
||||
this.yPosition + this.height,
|
||||
-16777216);
|
||||
|
||||
if (!this.getVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getEnableBackgroundDrawing()) {
|
||||
drawRect(
|
||||
this.xPosition - 1,
|
||||
this.yPosition - 1,
|
||||
this.xPosition + this.width + 1,
|
||||
this.yPosition + this.height + 1,
|
||||
-6250336);
|
||||
drawRect(
|
||||
this.xPosition,
|
||||
this.yPosition,
|
||||
this.xPosition + this.width,
|
||||
this.yPosition + this.height,
|
||||
-16777216);
|
||||
}
|
||||
|
||||
if (!this.rawText.equals(getText())) {
|
||||
this.rawText = getText();
|
||||
this.formattedText = this.formatter.format(this.rawText);
|
||||
if (!EnumChatFormatting.getTextWithoutFormattingCodes(this.formattedText).toLowerCase()
|
||||
.equals(this.rawText.toLowerCase())) {
|
||||
this.formattedText = this.rawText;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.rawText.equals(getText())) {
|
||||
this.rawText = getText();
|
||||
this.formattedText = this.formatter.format(this.rawText);
|
||||
if (!EnumChatFormatting.getTextWithoutFormattingCodes(this.formattedText).toLowerCase()
|
||||
.equals(this.rawText.toLowerCase())) {
|
||||
this.formattedText = this.rawText;
|
||||
}
|
||||
if (!isFocused() && this.formattedText.isEmpty()) {
|
||||
int x = getEnableBackgroundDrawing() ? this.xPosition + 4 : this.xPosition;
|
||||
int y = getEnableBackgroundDrawing() ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
this.fontRenderer.trimStringToWidth(this.placeholder, this.getWidth()),
|
||||
x,
|
||||
y,
|
||||
this.notEditableColor);
|
||||
return;
|
||||
}
|
||||
|
||||
int firstCharacterIndex = getFormattedTextShift(this.lineScrollOffset);
|
||||
String rawTextClipped = this.fontRenderer
|
||||
.trimStringToWidth(this.rawText.substring(this.lineScrollOffset), this.getWidth());
|
||||
String textClipped = this.formattedText
|
||||
.substring(firstCharacterIndex, getFormattedTextShift(this.lineScrollOffset + rawTextClipped.length()));
|
||||
int cursorPosition = getFormattedTextShift(getCursorPosition());
|
||||
int selectionEnd = getFormattedTextShift(getSelectionEnd());
|
||||
|
||||
int color = this.editable ? this.editableColor : this.notEditableColor;
|
||||
int cursorA = cursorPosition - firstCharacterIndex;
|
||||
int cursorB = selectionEnd - firstCharacterIndex;
|
||||
boolean flag = cursorA >= 0 && cursorA <= textClipped.length();
|
||||
boolean flag1 = isFocused() && this.frame / 6 % 2 == 0 && flag;
|
||||
boolean flag2 = getCursorPosition() < this.rawText.length() || this.rawText.length() >= getMaxStringLength();
|
||||
int x = getEnableBackgroundDrawing() ? this.xPosition + 4 : this.xPosition;
|
||||
int y = getEnableBackgroundDrawing() ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
|
||||
int x2 = x;
|
||||
|
||||
if (cursorB > textClipped.length()) {
|
||||
cursorB = textClipped.length();
|
||||
}
|
||||
|
||||
if (textClipped.length() > 0) {
|
||||
String s1 = flag ? textClipped.substring(0, cursorA) : textClipped;
|
||||
String colorA = getPreviousColor(firstCharacterIndex);
|
||||
x2 = this.fontRenderer.drawStringWithShadow(colorA + s1, x, y, color);
|
||||
}
|
||||
|
||||
int k1 = x2;
|
||||
|
||||
if (!flag) {
|
||||
k1 = cursorA > 0 ? x + this.width : x;
|
||||
} else if (flag2) {
|
||||
k1 = x2 - 1;
|
||||
--x2;
|
||||
}
|
||||
|
||||
if (textClipped.length() > 0 && flag && cursorA < textClipped.length()) {
|
||||
String colorB = getPreviousColor(firstCharacterIndex + cursorA);
|
||||
this.fontRenderer.drawStringWithShadow(colorB + textClipped.substring(cursorA), x2, y, color);
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
if (flag2) {
|
||||
Gui.drawRect(k1, y - 1, k1 + 1, y + 1 + this.fontRenderer.FONT_HEIGHT, -3092272);
|
||||
} else {
|
||||
this.fontRenderer.drawStringWithShadow("_", k1, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
int firstCharacterIndex = getFormattedTextShift(this.lineScrollOffset);
|
||||
String rawTextClipped = this.fontRenderer
|
||||
.trimStringToWidth(this.rawText.substring(this.lineScrollOffset), this.getWidth());
|
||||
String textClipped = this.formattedText.substring(
|
||||
firstCharacterIndex,
|
||||
getFormattedTextShift(this.lineScrollOffset + rawTextClipped.length()));
|
||||
int cursorPosition = getFormattedTextShift(this.getCursorPosition());
|
||||
int selectionEnd = getFormattedTextShift(this.getSelectionEnd());
|
||||
|
||||
int color = this.editable ? this.editableColor : this.notEditableColor;
|
||||
int cursorA = cursorPosition - firstCharacterIndex;
|
||||
int cursorB = selectionEnd - firstCharacterIndex;
|
||||
boolean flag = cursorA >= 0 && cursorA <= textClipped.length();
|
||||
boolean flag1 = this.isFocused() && this.frame / 6 % 2 == 0 && flag;
|
||||
boolean flag2 = this.getCursorPosition() < this.rawText.length()
|
||||
|| this.rawText.length() >= this.getMaxStringLength();
|
||||
int x = this.getEnableBackgroundDrawing() ? this.xPosition + 4 : this.xPosition;
|
||||
int y = this.getEnableBackgroundDrawing() ? this.yPosition + (this.height - 8) / 2 : this.yPosition;
|
||||
int x2 = x;
|
||||
|
||||
if (cursorB > textClipped.length()) {
|
||||
cursorB = textClipped.length();
|
||||
}
|
||||
|
||||
if (textClipped.length() > 0) {
|
||||
String s1 = flag ? textClipped.substring(0, cursorA) : textClipped;
|
||||
String colorA = getPreviousColor(firstCharacterIndex);
|
||||
x2 = this.fontRenderer.drawStringWithShadow(colorA + s1, x, y, color);
|
||||
}
|
||||
|
||||
int k1 = x2;
|
||||
|
||||
if (!flag) {
|
||||
k1 = cursorA > 0 ? x + this.width : x;
|
||||
} else if (flag2) {
|
||||
k1 = x2 - 1;
|
||||
--x2;
|
||||
}
|
||||
|
||||
if (textClipped.length() > 0 && flag && cursorA < textClipped.length()) {
|
||||
String colorB = getPreviousColor(firstCharacterIndex + cursorA);
|
||||
this.fontRenderer.drawStringWithShadow(colorB + textClipped.substring(cursorA), x2, y, color);
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
if (flag2) {
|
||||
Gui.drawRect(k1, y - 1, k1 + 1, y + 1 + this.fontRenderer.FONT_HEIGHT, -3092272);
|
||||
} else {
|
||||
this.fontRenderer.drawStringWithShadow("_", k1, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursorB != cursorA) {
|
||||
int l1 = x + this.fontRenderer.getStringWidth(textClipped.substring(0, cursorB));
|
||||
drawCursorVertical(k1, y - 1, l1 - 1, y + 1 + this.fontRenderer.FONT_HEIGHT);
|
||||
}
|
||||
if (cursorB != cursorA) {
|
||||
int l1 = x + this.fontRenderer.getStringWidth(textClipped.substring(0, cursorB));
|
||||
drawCursorVertical(k1, y - 1, l1 - 1, y + 1 + this.fontRenderer.FONT_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,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);
|
||||
@@ -230,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) {
|
||||
|
||||
@@ -45,15 +45,14 @@ public class GuiNEIButton extends GuiButton {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -42,13 +42,11 @@ public class HUDRenderer implements IKeyStateTracker {
|
||||
&& mc.objectMouseOver != null
|
||||
&& mc.objectMouseOver.typeOfHit == MovingObjectType.BLOCK) {
|
||||
World world = mc.theWorld;
|
||||
ItemStack[] items = ItemInfo.getIdentifierItems(world, mc.thePlayer, mc.objectMouseOver)
|
||||
.toArray(new ItemStack[0]);
|
||||
final List<ItemStack> items = ItemInfo.getIdentifierItems(world, mc.thePlayer, mc.objectMouseOver);
|
||||
|
||||
if (items.length == 0) return;
|
||||
|
||||
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(items);
|
||||
if (items.isEmpty()) return;
|
||||
|
||||
final ItemStack stack = StackInfo.getItemStackWithMinimumDamage(items.toArray(new ItemStack[0]));
|
||||
renderOverlay(stack, ItemInfo.getText(stack, world, mc.thePlayer, mc.objectMouseOver), getPositioning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import static codechicken.nei.NEIClientUtils.translate;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerInputHandler;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
|
||||
public class HeldItemHandler implements IContainerInputHandler, IContainerTooltipHandler {
|
||||
|
||||
@@ -25,9 +25,10 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
|
||||
}
|
||||
|
||||
protected boolean contains(int mousex, int mousey) {
|
||||
return NEIClientUtils.getHeldItem() != null
|
||||
&& (ItemPanels.bookmarkPanel.contains(mousex, mousey) || ItemPanels.itemPanel.contains(mousex, mousey)
|
||||
|| ItemPanels.itemPanel.historyPanel.contains(mousex, mousey));
|
||||
return NEIClientUtils.getHeldItem() != null && !ItemPanels.bookmarkPanel.inEditingState()
|
||||
&& !AutoCraftingManager.processing()
|
||||
&& (ItemPanels.bookmarkPanel.contains(mousex, mousey)
|
||||
|| ItemPanels.itemPanel.containsWithSubpanels(mousex, mousey));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,7 +36,8 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
|
||||
|
||||
if (contains(mousex, mousey)) {
|
||||
|
||||
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
|
||||
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|
||||
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
|
||||
if (button == 1) {
|
||||
NEIClientUtils.decreaseSlotStack(-999);
|
||||
} else {
|
||||
@@ -94,24 +96,14 @@ public class HeldItemHandler implements IContainerInputHandler, IContainerToolti
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List<String> currenttip) {
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
|
||||
List<String> currenttip) {
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
|
||||
|
||||
if (contains(mousex, mousey)) {
|
||||
currenttip.clear();
|
||||
|
||||
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")) {
|
||||
if (NEIClientConfig.canPerformAction("delete") && NEIClientConfig.canPerformAction("item")
|
||||
|| NEIClientUtils.mc().playerController.isInCreativeMode()) {
|
||||
currenttip.add(EnumChatFormatting.RED + translate("itempanel.deleteItem"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
import codechicken.nei.ItemsGrid.MouseContext;
|
||||
import codechicken.nei.NEIClientUtils.Alignment;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
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.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeInfo;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
|
||||
public class ItemCraftablesPanel
|
||||
extends AbstractSubpanel<ItemsGrid<ItemCraftablesPanel.CraftablesGridSlot, MouseContext>> {
|
||||
|
||||
public static class CraftablesGridSlot extends ItemsGridSlot {
|
||||
|
||||
protected final long realAmount;
|
||||
protected final boolean isFluidDisplay;
|
||||
protected final RecipeId recipeId;
|
||||
|
||||
public CraftablesGridSlot(int slotIndex, int itemIndex, ItemStack itemStack, RecipeId recipeId) {
|
||||
super(slotIndex, itemIndex, itemStack);
|
||||
this.realAmount = StackInfo.getAmount(itemStack);
|
||||
this.isFluidDisplay = StackInfo.isFluidDisplayItem(itemStack);
|
||||
this.recipeId = recipeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeId getRecipeId() {
|
||||
return this.recipeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {
|
||||
drawStackSize(rect);
|
||||
}
|
||||
|
||||
protected void drawStackSize(Rectangle4i rect) {
|
||||
long stackSize = this.realAmount;
|
||||
|
||||
if (stackSize > 1) {
|
||||
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
|
||||
String amountString = "";
|
||||
|
||||
if (stackSize < 10_000) {
|
||||
amountString = String.valueOf(stackSize);
|
||||
} else {
|
||||
amountString = ReadableNumberConverter.INSTANCE.toWideReadableForm(stackSize);
|
||||
}
|
||||
|
||||
if (this.isFluidDisplay) {
|
||||
amountString += "L";
|
||||
}
|
||||
|
||||
NEIClientUtils.drawNEIOverlayText(
|
||||
amountString,
|
||||
new Rectangle4i(rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2),
|
||||
panelFactor,
|
||||
0xFFFFFF,
|
||||
true,
|
||||
this.isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private Map<String, ArrayList<ICraftingHandler>> identHandlers = new HashMap<>();
|
||||
private Map<ItemStack, RecipeId> availableRecipes = new HashMap<>();
|
||||
|
||||
private GuiContainer lastGuiContainer = null;
|
||||
private ItemStackAmount lastPlayerInventory = null;
|
||||
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<>() {
|
||||
|
||||
protected List<CraftablesGridSlot> gridMask;
|
||||
|
||||
@Override
|
||||
protected void onGridChanged() {
|
||||
this.gridMask = null;
|
||||
super.onGridChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CraftablesGridSlot> getMask() {
|
||||
|
||||
if (this.gridMask == null) {
|
||||
final int maxSlotIndex = this.rows * this.columns;
|
||||
final List<CraftablesGridSlot> gridMask = new ArrayList<>();
|
||||
this.realItems.clear();
|
||||
|
||||
if (!ItemCraftablesPanel.this.availableRecipes.isEmpty()) {
|
||||
final List<Map.Entry<ItemStack, RecipeId>> entries = ItemCraftablesPanel.this.availableRecipes
|
||||
.entrySet().stream().sorted(Map.Entry.comparingByKey(ITEM_SORT_COMPARATOR))
|
||||
.limit(maxSlotIndex).collect(Collectors.toList());
|
||||
int slotIndex = 0;
|
||||
int itemIndex = 0;
|
||||
|
||||
for (Map.Entry<ItemStack, RecipeId> entry : entries) {
|
||||
if (!isInvalidSlot(slotIndex)) {
|
||||
this.realItems.add(entry.getKey());
|
||||
gridMask.add(
|
||||
new CraftablesGridSlot(
|
||||
slotIndex,
|
||||
itemIndex++,
|
||||
entry.getKey(),
|
||||
entry.getValue()));
|
||||
}
|
||||
if (slotIndex++ >= maxSlotIndex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.gridMask = gridMask;
|
||||
} else {
|
||||
this.gridMask = Collections.emptyList();
|
||||
}
|
||||
|
||||
ItemCraftablesPanel.this.updateLinePadding();
|
||||
}
|
||||
|
||||
return this.gridMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MouseContext getMouseContext(int mousex, int mousey) {
|
||||
final ItemsGridSlot hovered = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hovered != null) {
|
||||
return new MouseContext(
|
||||
hovered.slotIndex,
|
||||
hovered.slotIndex / this.columns,
|
||||
hovered.slotIndex % this.columns);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
|
||||
return ItemQuantityField.prepareStackWithQuantity(itemStack, StackInfo.getAmount(itemStack));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
if (!this.availableRecipes.isEmpty()) {
|
||||
super.draw(mousex, mousey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setPanelWidth(int width) {
|
||||
if (getGuiContainer() != this.lastGuiContainer) updateCraftables();
|
||||
|
||||
final int columns = width / ItemsGrid.SLOT_SIZE;
|
||||
final int useRows = NEIClientConfig.getIntSetting("inventory.craftables.useRows");
|
||||
final int rows = (int) Math.min(Math.ceil(this.availableRecipes.size() * 1f / columns), useRows);
|
||||
|
||||
this.w = width;
|
||||
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
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()) {
|
||||
clearCraftables();
|
||||
} else if (this.lastPlayerInventory == null
|
||||
|| Math.abs(System.currentTimeMillis() - this.lastPlayerInventorySync) >= MIN_DELAY) {
|
||||
final GuiContainer firstGui = getGuiContainer();
|
||||
this.lastPlayerInventorySync = System.currentTimeMillis();
|
||||
|
||||
if (firstGui != null && !ItemCraftablesPanel.guiBlacklist.contains(firstGui.getClass().getName())) {
|
||||
final boolean showOnlyFavorites = NEIClientConfig
|
||||
.getBooleanSetting("inventory.craftables.favoritesOnly");
|
||||
final ItemStackAmount inv = AutoCraftingManager.getInventoryItems(firstGui);
|
||||
|
||||
if (this.lastGuiContainer != firstGui
|
||||
|| showOnlyFavorites && this.lastFavoritesCount != FavoriteRecipes.size()
|
||||
|| !inv.equals(this.lastPlayerInventory)) {
|
||||
this.lastPlayerInventory = inv;
|
||||
this.lastGuiContainer = firstGui;
|
||||
this.lastFavoritesCount = FavoriteRecipes.size();
|
||||
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 {
|
||||
clearCraftables();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void clearCraftables() {
|
||||
if (!this.availableRecipes.isEmpty()) {
|
||||
this.availableRecipes = Collections.emptyMap();
|
||||
this.lastPlayerInventory = null;
|
||||
this.grid.onGridChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private GuiContainer getGuiContainer() {
|
||||
final GuiContainer firstGui = NEIClientUtils.getGuiContainer();
|
||||
return (firstGui instanceof GuiRecipe<?>gui) ? gui.firstGui : 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 Map<Item, List<ItemStack>> invByItem = indexInventoryByItem(snapshot.inventory.values());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private List<IRecipeHandler> getAvailableHandlers(GuiContainer firstGui) {
|
||||
final List<IRecipeHandler> availableHandlers = new ArrayList<>();
|
||||
|
||||
for (String ident : RecipeInfo.getOverlayHandlerIdents(firstGui)) {
|
||||
availableHandlers.addAll(identHandlers.computeIfAbsent(ident, GuiCraftingRecipe::getCraftingHandlers));
|
||||
}
|
||||
|
||||
return availableHandlers;
|
||||
}
|
||||
|
||||
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<>();
|
||||
|
||||
IntStream.range(0, handler.numRecipes()).parallel().forEach(recipeIndex -> {
|
||||
final List<PositionedStack> ingredientStacks = handler.getIngredientStacks(recipeIndex);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return availableRecipes;
|
||||
}
|
||||
|
||||
private boolean existsIngredients(List<PositionedStack> ingredients, Map<Item, List<ItemStack>> invByItem) {
|
||||
final Map<ItemStack, Integer> usedItems = new HashMap<>();
|
||||
|
||||
for (PositionedStack pStack : ingredients) {
|
||||
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);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !usedItems.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,189 +1,238 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
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.lwjgl.opengl.GL11;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.ItemPanel.ItemPanelSlot;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.GuiUsageRecipe;
|
||||
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 Widget {
|
||||
public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemHistoryPanel.HistoryGridSlot, MouseContext>> {
|
||||
|
||||
protected int mouseDownSlot = -1;
|
||||
protected ItemsGrid grid;
|
||||
protected File historyFile = null;
|
||||
|
||||
public ItemHistoryPanel() {
|
||||
grid = new ItemsGrid();
|
||||
}
|
||||
public static class HistoryGridSlot extends ItemsGridSlot {
|
||||
|
||||
public boolean isEmpty() {
|
||||
return grid.isEmpty();
|
||||
}
|
||||
|
||||
public void draw(int mousex, int mousey) {
|
||||
|
||||
if (NEIClientConfig.getIntSetting("inventory.history.splittingMode") == 0) {
|
||||
GuiDraw.drawRect(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
|
||||
} else {
|
||||
drawSplittingArea(x, y, w, h, NEIClientConfig.getSetting("inventory.history.historyColor").getHexValue());
|
||||
public HistoryGridSlot(int slotIndex, int itemIndex, ItemStack item) {
|
||||
super(slotIndex, itemIndex, item);
|
||||
}
|
||||
|
||||
grid.draw(mousex, mousey);
|
||||
@Override
|
||||
public RecipeId getRecipeId() {
|
||||
return FavoriteRecipes.getFavorite(this.item);
|
||||
}
|
||||
}
|
||||
|
||||
public ItemHistoryPanel() {
|
||||
this.grid = new ItemsGrid<>() {
|
||||
|
||||
protected List<HistoryGridSlot> gridMask;
|
||||
|
||||
@Override
|
||||
protected void onGridChanged() {
|
||||
this.gridMask = null;
|
||||
super.onGridChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HistoryGridSlot> getMask() {
|
||||
|
||||
if (this.gridMask == null) {
|
||||
this.gridMask = new ArrayList<>();
|
||||
int itemIndex = 0;
|
||||
for (int slotIndex = 0; slotIndex < this.rows * this.columns && itemIndex < size(); slotIndex++) {
|
||||
if (!isInvalidSlot(slotIndex)) {
|
||||
this.gridMask.add(new HistoryGridSlot(slotIndex, itemIndex, getItem(itemIndex)));
|
||||
itemIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
ItemHistoryPanel.this.updateLinePadding();
|
||||
}
|
||||
|
||||
return this.gridMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MouseContext getMouseContext(int mousex, int mousey) {
|
||||
final HistoryGridSlot hovered = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hovered != null) {
|
||||
return new MouseContext(
|
||||
hovered.slotIndex,
|
||||
hovered.slotIndex / this.columns,
|
||||
hovered.slotIndex % this.columns);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
|
||||
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
if (this.grid.size() > 0) {
|
||||
super.draw(mousex, mousey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setPanelWidth(int width) {
|
||||
final int columns = width / ItemsGrid.SLOT_SIZE;
|
||||
final int useRows = NEIClientConfig.getIntSetting("inventory.history.useRows");
|
||||
final int rows = (int) Math.min(Math.ceil(this.grid.size() * 1f / columns), useRows);
|
||||
|
||||
this.w = width;
|
||||
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
grid.update();
|
||||
this.splittingLineColor = NEIClientConfig.getSetting("inventory.history.color").getHexValue();
|
||||
super.update();
|
||||
}
|
||||
|
||||
public void addItem(ItemStack stack) {
|
||||
if (stack != null) {
|
||||
ItemStack is = StackInfo.loadFromNBT(StackInfo.itemStackToNBT(stack), 0);
|
||||
ItemStack is = StackInfo.withAmount(stack, 0);
|
||||
|
||||
grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true));
|
||||
grid.realItems.add(0, is);
|
||||
this.grid.realItems.removeIf(historyStack -> StackInfo.equalItemAndNBT(historyStack, stack, true));
|
||||
this.grid.realItems.add(0, is);
|
||||
|
||||
if (grid.realItems.size() > Math.max(50, grid.rows * grid.columns)) {
|
||||
grid.realItems.remove(grid.realItems.size() - 1);
|
||||
if (this.grid.realItems.size() > Math.max(50, this.grid.rows * this.grid.columns)) {
|
||||
this.grid.realItems.remove(this.grid.realItems.size() - 1);
|
||||
}
|
||||
|
||||
grid.onItemsChanged();
|
||||
this.grid.onItemsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void resize(GuiContainer gui) {
|
||||
grid.setGridSize(x, y, w, h);
|
||||
grid.refresh(gui);
|
||||
}
|
||||
public void load() {
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mousex, int mousey) {
|
||||
ItemPanelSlot slot = getSlotMouseOver(mousex, mousey);
|
||||
return slot == null ? null : slot.item;
|
||||
}
|
||||
|
||||
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
|
||||
return grid.getSlotMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
private void drawSplittingArea(int x, int y, int width, int height, int color) {
|
||||
|
||||
float alpha = (color >> 24 & 255) / 255.0F;
|
||||
float red = (color >> 16 & 255) / 255.0F;
|
||||
float green = (color >> 8 & 255) / 255.0F;
|
||||
float blue = (color & 255) / 255.0F;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_LINE_STIPPLE);
|
||||
GL11.glColor4f(red, green, blue, alpha);
|
||||
GL11.glLineWidth(2F);
|
||||
GL11.glLineStipple(2, (short) 0x00FF);
|
||||
|
||||
GL11.glBegin(GL11.GL_LINE_LOOP);
|
||||
|
||||
GL11.glVertex2i(x, y);
|
||||
GL11.glVertex2i(x + width, y);
|
||||
GL11.glVertex2i(x + width, y + height);
|
||||
GL11.glVertex2i(x, y + height);
|
||||
|
||||
GL11.glEnd();
|
||||
|
||||
GL11.glLineStipple(1, (short) 0xFFFF);
|
||||
GL11.glLineWidth(1F);
|
||||
GL11.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glColor4f(1F, 1F, 1F, 1F);
|
||||
|
||||
GL11.glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
|
||||
|
||||
if (mouseDownSlot >= 0 && ItemPanels.itemPanel.draggedStack == null
|
||||
&& NEIClientUtils.getHeldItem() == null
|
||||
&& NEIClientConfig.hasSMPCounterPart()) {
|
||||
|
||||
ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) {
|
||||
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(mouseDownSlot);
|
||||
mouseDownSlot = -1;
|
||||
}
|
||||
if (!NEIClientConfig.getBooleanSetting("inventory.history.save")) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
String worldPath = "global";
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mousex, int mousey, int button) {
|
||||
if (handleClickExt(mousex, mousey, button)) return true;
|
||||
|
||||
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
if (hoverSlot != null) {
|
||||
|
||||
if (button == 2) {
|
||||
|
||||
if (hoverSlot.item != null) {
|
||||
ItemPanels.itemPanel.draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex);
|
||||
}
|
||||
|
||||
} else {
|
||||
mouseDownSlot = hoverSlot.slotIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.history.worldSpecific")) {
|
||||
worldPath = NEIClientConfig.getWorldPath();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
|
||||
|
||||
@Override
|
||||
public void mouseUp(int mousex, int mousey, int button) {
|
||||
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && ItemPanels.itemPanel.draggedStack == null) {
|
||||
ItemStack item = hoverSlot.item.copy();
|
||||
final File historyFile = new File(dir, "history.ini");
|
||||
|
||||
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|
||||
|| !NEIClientConfig.canCheatItem(item)) {
|
||||
if (historyFile.equals(this.historyFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (button == 0) {
|
||||
GuiCraftingRecipe.openRecipeGui("item", item);
|
||||
} else if (button == 1) {
|
||||
GuiUsageRecipe.openRecipeGui("item", item);
|
||||
}
|
||||
List<String> itemStrings = Collections.emptyList();
|
||||
|
||||
mouseDownSlot = -1;
|
||||
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;
|
||||
}
|
||||
|
||||
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1);
|
||||
}
|
||||
|
||||
mouseDownSlot = -1;
|
||||
}
|
||||
final JsonParser parser = new JsonParser();
|
||||
final int maxSize = Math.max(50, this.grid.rows * this.grid.columns);
|
||||
|
||||
protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) {
|
||||
ItemStack stack = grid.getItem(mouseDownSlot);
|
||||
this.grid.realItems.clear();
|
||||
|
||||
if (stack != null) {
|
||||
int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
|
||||
for (String itemStr : itemStrings) {
|
||||
|
||||
if (amount == 0) {
|
||||
amount = stack.getMaxStackSize();
|
||||
if (itemStr.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return NEIServerUtils.copyStack(stack, amount);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
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,32 +2,23 @@ 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.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.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
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.search.TooltipFilter;
|
||||
import codechicken.nei.util.ItemUntranslator;
|
||||
|
||||
public class ItemList {
|
||||
|
||||
@@ -45,9 +36,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 HashMap<ItemStack, Integer> ordering = new HashMap<>();
|
||||
/**
|
||||
* Unlike {@link LayoutManager#itemsLoaded}, this indicates whether item loading is actually finished or not.
|
||||
*/
|
||||
@@ -95,7 +83,13 @@ public class ItemList {
|
||||
public boolean matches(ItemStack item) {
|
||||
String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName());
|
||||
|
||||
if (displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find()) {
|
||||
if (displayName != null && !displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
displayName = ItemUntranslator.getInstance().getItemStackDisplayName(item);
|
||||
|
||||
if (!displayName.isEmpty() && this.pattern.matcher(displayName).find()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -103,7 +97,15 @@ public class ItemList {
|
||||
displayName = EnumChatFormatting
|
||||
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
|
||||
|
||||
return displayName != null && !displayName.isEmpty() && 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(alias).find()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -204,171 +206,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.contains(s)) {
|
||||
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) {
|
||||
ItemSorter.sort(items);
|
||||
|
||||
HashMap<ItemStack, Integer> newOrdering = new HashMap<>();
|
||||
|
||||
if (!CollapsibleItems.isEmpty()) {
|
||||
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());
|
||||
}
|
||||
|
||||
// For optimization it generate itemslist, permutations, orders & collapsibleitems
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void execute() {
|
||||
if (!NEIClientConfig.isEnabled()) return;
|
||||
|
||||
ThreadOperationTimer timer = getTimer(NEIClientConfig.getItemLoadingTimeout());
|
||||
LayoutManager.itemsLoaded = true;
|
||||
loadFinished = false;
|
||||
|
||||
SearchField.searchParser.clearCache();
|
||||
ItemSorter.instance.ordering.clear();
|
||||
CollapsibleItems.clearCache();
|
||||
TooltipFilter.clearCache();
|
||||
|
||||
List<ItemStack> items = new ArrayList<>();
|
||||
ListMultimap<Item, ItemStack> itemMap = ArrayListMultimap.create();
|
||||
ItemStackSet unique = new ItemStackSet();
|
||||
|
||||
timer.setLimit(NEIClientConfig.getItemLoadingTimeout());
|
||||
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);
|
||||
}
|
||||
|
||||
CollapsibleItems.putItem(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);
|
||||
|
||||
new Thread(
|
||||
() -> ItemList.items.parallelStream().forEach(TooltipFilter::getSearchTooltip),
|
||||
"NEI Tooltip Filter Loader").start();
|
||||
|
||||
loadFinished = true;
|
||||
|
||||
SubsetWidget.updateHiddenItems();
|
||||
updateFilter.restart();
|
||||
}
|
||||
};
|
||||
|
||||
public static ForkJoinPool forkJoinPool;
|
||||
|
||||
static {
|
||||
@@ -388,36 +225,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;
|
||||
|
||||
Comparator<ItemStack> comparator = Comparator.comparingInt(ItemList.ordering::get);
|
||||
filtered.sort(comparator::compare);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -15,125 +17,313 @@ import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityMobSpawner;
|
||||
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;
|
||||
|
||||
public class ItemMobSpawner extends ItemBlock {
|
||||
|
||||
private static final Map<Integer, EntityLiving> entityHashMap = new HashMap<>();
|
||||
private static final Map<Integer, String> IDtoNameMap = new HashMap<>();
|
||||
public static int idPig;
|
||||
private static boolean loaded;
|
||||
private static final Map<Integer, EntityLiving> entityHashMap = new ConcurrentHashMap<>();
|
||||
private static final Map<Integer, String> IDtoNameMap = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<? extends Entity>, String> ENTITY_CLASS_TO_NAME_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public static int idPig = 90;
|
||||
private static volatile boolean loaded = false;
|
||||
|
||||
// For asm
|
||||
public static int placedX;
|
||||
public static int placedY;
|
||||
public static int placedZ;
|
||||
|
||||
public static void setLastPlacedPosition(int x, int y, int z) {
|
||||
placedX = x;
|
||||
placedY = y;
|
||||
placedZ = z;
|
||||
}
|
||||
|
||||
public static int[] getLastPlacedPosition() {
|
||||
return new int[] { placedX, placedY, placedZ };
|
||||
}
|
||||
|
||||
public ItemMobSpawner() {
|
||||
super(Blocks.mob_spawner);
|
||||
|
||||
hasSubtypes = true;
|
||||
MinecraftForgeClient.registerItemRenderer(this, new SpawnerRenderer());
|
||||
}
|
||||
|
||||
/**
|
||||
* These are ASM translated from BlockMobSpawner
|
||||
*/
|
||||
public static int placedX;
|
||||
|
||||
public static int placedY;
|
||||
public static int placedZ;
|
||||
|
||||
@Override
|
||||
public IIcon getIconFromDamage(int par1) {
|
||||
public IIcon getIconFromDamage(int meta) {
|
||||
return Blocks.mob_spawner.getBlockTextureFromSide(0);
|
||||
}
|
||||
|
||||
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7,
|
||||
float par8, float par9, float par10) {
|
||||
if (super.onItemUse(itemstack, entityplayer, world, x, y, z, par7, par8, par9, par10) && world.isRemote) {
|
||||
TileEntityMobSpawner tileentitymobspawner = (TileEntityMobSpawner) world
|
||||
.getTileEntity(placedX, placedY, placedZ);
|
||||
if (tileentitymobspawner != null) {
|
||||
setDefaultTag(itemstack);
|
||||
String mobtype = IDtoNameMap.get(itemstack.getItemDamage());
|
||||
if (mobtype != null) {
|
||||
NEICPH.sendMobSpawnerID(placedX, placedY, placedZ, mobtype);
|
||||
tileentitymobspawner.func_145881_a().setEntityName(mobtype);
|
||||
@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;
|
||||
|
||||
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) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
private String getMobTypeFromItemStack(ItemStack stack) {
|
||||
int meta = getValidMetaData(stack);
|
||||
return IDtoNameMap.get(meta);
|
||||
}
|
||||
|
||||
private int getValidMetaData(ItemStack stack) {
|
||||
int meta = stack.getItemDamage();
|
||||
if (meta == 0 || !IDtoNameMap.containsKey(meta)) {
|
||||
return idPig;
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void addInformation(ItemStack itemstack, EntityPlayer par2EntityPlayer, List<String> list, boolean par4) {
|
||||
setDefaultTag(itemstack);
|
||||
int meta = itemstack.getItemDamage();
|
||||
if (meta == 0) {
|
||||
meta = idPig;
|
||||
public void addInformation(ItemStack itemstack, EntityPlayer player, List<String> tooltip, boolean advanced) {
|
||||
int meta = getValidMetaData(itemstack);
|
||||
EntityLiving entity = getEntity(meta);
|
||||
String mobName = IDtoNameMap.get(meta);
|
||||
|
||||
if (entity != null && mobName != null) {
|
||||
EnumChatFormatting color = (entity instanceof IMob) ? EnumChatFormatting.DARK_RED
|
||||
: EnumChatFormatting.DARK_AQUA;
|
||||
tooltip.add(color + mobName);
|
||||
}
|
||||
Entity e = getEntity(meta);
|
||||
list.add("\u00A7" + (e instanceof IMob ? "4" : "3") + IDtoNameMap.get(meta));
|
||||
}
|
||||
|
||||
public static EntityLiving getEntity(int ID) {
|
||||
EntityLiving e = entityHashMap.get(ID);
|
||||
if (e == null) {
|
||||
World world = NEIClientUtils.mc().theWorld;
|
||||
loadSpawners(world);
|
||||
Class<?> clazz = (Class<?>) EntityList.IDtoClassMapping.get(ID);
|
||||
try {
|
||||
e = (EntityLiving) clazz.getConstructor(new Class[] { World.class }).newInstance(world);
|
||||
} catch (Throwable t) {
|
||||
if (clazz == null)
|
||||
NEIClientConfig.logger.error("Null class for entity (" + ID + ", " + IDtoNameMap.get(ID));
|
||||
else NEIClientConfig.logger.error("Error creating instance of entity: " + clazz.getName(), t);
|
||||
e = getEntity(idPig);
|
||||
public static EntityLiving getEntity(int id) {
|
||||
EntityLiving cached = entityHashMap.get(id);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
loadSpawners();
|
||||
|
||||
Class<?> entityClass = EntityList.IDtoClassMapping.get(id);
|
||||
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
|
||||
|
||||
if (isInvalidEntityClass(entityClass)) {
|
||||
return cacheFallback(id);
|
||||
}
|
||||
|
||||
EntityLiving entity = createEntityInstance(entityClass, world, id);
|
||||
if (entity != null) {
|
||||
entityHashMap.put(id, entity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static boolean isInvalidEntityClass(Class<?> entityClass) {
|
||||
if (entityClass == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int modifiers = entityClass.getModifiers();
|
||||
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
|
||||
NEIClientConfig.logger.warn("Skipping abstract/interface entity class: {}", entityClass.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EntityLiving.class.isAssignableFrom(entityClass)) {
|
||||
NEIClientConfig.logger.warn("Skipping non-EntityLiving class: {}", entityClass.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static EntityLiving createEntityInstance(Class<?> entityClass, World world, int id) {
|
||||
if (entityClass == null || world == null) {
|
||||
NEIClientConfig.logger.debug("World or entity class is null, using fallback for ID: {}", id);
|
||||
return getFallbackEntity(id);
|
||||
}
|
||||
|
||||
try {
|
||||
Class<? extends EntityLiving> livingClass = (Class<? extends EntityLiving>) entityClass;
|
||||
return livingClass.getConstructor(World.class).newInstance(world);
|
||||
} catch (Throwable t) {
|
||||
logEntityCreationError(entityClass, id, t);
|
||||
return getFallbackEntity(id);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logEntityCreationError(Class<?> entityClass, int id, Throwable t) {
|
||||
String entityName = IDtoNameMap.getOrDefault(id, "unknown");
|
||||
if (entityClass == null) {
|
||||
NEIClientConfig.logger
|
||||
.error("Failed to create entity instance: Class is null for ID {}, Name {}", id, entityName, t);
|
||||
} else {
|
||||
NEIClientConfig.logger.error(
|
||||
"Failed to create instance of entity: Class {}, ID {}, Name {}",
|
||||
entityClass.getName(),
|
||||
id,
|
||||
entityName,
|
||||
t);
|
||||
}
|
||||
}
|
||||
|
||||
private static EntityLiving cacheFallback(int id) {
|
||||
EntityLiving fallback = getFallbackEntity(id);
|
||||
if (fallback != null) {
|
||||
entityHashMap.put(id, fallback);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static EntityLiving getFallbackEntity(int originalId) {
|
||||
EntityLiving pig = entityHashMap.get(idPig);
|
||||
if (pig == null) {
|
||||
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
|
||||
|
||||
if (world != null) {
|
||||
try {
|
||||
pig = new EntityPig(world);
|
||||
entityHashMap.put(idPig, pig);
|
||||
NEIClientConfig.logger.debug("Created fallback pig entity for ID: {}", originalId);
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger
|
||||
.error("Failed to create fallback pig entity for original ID: {}", originalId, e);
|
||||
}
|
||||
} else {
|
||||
NEIClientConfig.logger.warn("World is null, cannot create fallback entity for ID: {}", originalId);
|
||||
}
|
||||
entityHashMap.put(ID, e);
|
||||
}
|
||||
return e;
|
||||
|
||||
return pig;
|
||||
}
|
||||
|
||||
public static void clearEntityReferences(World newWorld) {
|
||||
entityHashMap.values().removeIf(e -> e.worldObj != newWorld);
|
||||
public static void clearEntityReferences() {
|
||||
entityHashMap.clear();
|
||||
NEIClientConfig.logger.debug("Cleared all entity references from cache");
|
||||
}
|
||||
|
||||
private void setDefaultTag(ItemStack itemstack) {
|
||||
if (!IDtoNameMap.containsKey(itemstack.getItemDamage())) itemstack.setItemDamage(idPig);
|
||||
}
|
||||
public static void loadSpawners() {
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static void loadSpawners(World world) {
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
Map<Class<? extends Entity>, String> classToStringMapping = EntityList.classToStringMapping;
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Class<? extends Entity>, Integer> classToIDMapping = (Map<Class<? extends Entity>, Integer>) EntityList.classToIDMapping;
|
||||
for (Class<? extends Entity> eclass : classToStringMapping.keySet()) {
|
||||
if (!EntityLiving.class.isAssignableFrom(eclass)) continue;
|
||||
synchronized (ItemMobSpawner.class) {
|
||||
try {
|
||||
EntityLiving entityliving = (EntityLiving) eclass.getConstructor(new Class[] { World.class })
|
||||
.newInstance(world);
|
||||
entityliving.isChild();
|
||||
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
|
||||
|
||||
int id = classToIDMapping.get(eclass);
|
||||
String name = classToStringMapping.get(eclass);
|
||||
Class<? extends Entity> clazz = entry.getKey();
|
||||
String name = entry.getValue();
|
||||
|
||||
if (name.equals("EnderDragon")) continue;
|
||||
if (shouldRegisterEntity(clazz, name)) {
|
||||
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
|
||||
if (id != null) {
|
||||
IDtoNameMap.put(id, name);
|
||||
ENTITY_CLASS_TO_NAME_CACHE.put(clazz, name);
|
||||
|
||||
IDtoNameMap.put(id, name);
|
||||
if ("Pig".equals(name)) {
|
||||
idPig = id;
|
||||
NEIClientConfig.logger.debug("Set pig entity ID to: {}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loaded = true;
|
||||
NEIClientConfig.logger.info("Loaded {} spawner entity mappings", IDtoNameMap.size());
|
||||
|
||||
if (name.equals("Pig")) idPig = id;
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Error loading spawners from entity list", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldRegisterEntity(Class<? extends Entity> clazz, String name) {
|
||||
if (clazz == null || name == null || name.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IDtoNameMap.entrySet()
|
||||
.removeIf(e -> getEntity(e.getKey()).getClass() == EntityPig.class && !e.getValue().equals("Pig"));
|
||||
if (!EntityLiving.class.isAssignableFrom(clazz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("EnderDragon".equals(name) || "Giant".equals(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) {
|
||||
if (!NEIClientConfig.hasSMPCounterPart()) list.add(new ItemStack(item));
|
||||
else for (int i : IDtoNameMap.keySet()) list.add(new ItemStack(item, 1, i));
|
||||
if (!NEIClientConfig.hasSMPCounterPart()) {
|
||||
list.add(new ItemStack(item));
|
||||
} else {
|
||||
loadSpawners();
|
||||
for (int id : IDtoNameMap.keySet()) {
|
||||
ItemStack stack = new ItemStack(item, 1, id);
|
||||
list.add(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getEntityNameForMeta(int meta) {
|
||||
loadSpawners();
|
||||
return IDtoNameMap.getOrDefault(meta, IDtoNameMap.getOrDefault(idPig, "Pig"));
|
||||
}
|
||||
|
||||
public static boolean isValidEntityMeta(int meta) {
|
||||
loadSpawners();
|
||||
return IDtoNameMap.containsKey(meta) || meta == 0;
|
||||
}
|
||||
|
||||
public static Map<Integer, String> getRegisteredSpawnerEntities() {
|
||||
loadSpawners();
|
||||
return Collections.unmodifiableMap(IDtoNameMap);
|
||||
}
|
||||
|
||||
public static boolean isEntityValidForSpawner(Class<? extends Entity> entityClass) {
|
||||
if (entityClass == null) return false;
|
||||
|
||||
String name = ENTITY_CLASS_TO_NAME_CACHE.get(entityClass);
|
||||
if (name == null) {
|
||||
name = EntityList.classToStringMapping.get(entityClass);
|
||||
}
|
||||
|
||||
return name != null && shouldRegisterEntity(entityClass, name);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,18 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.ItemsPanelGrid.ItemsPanelGridSlot;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public class ItemPanel extends PanelWidget {
|
||||
public class ItemPanel extends PanelWidget<ItemsPanelGrid> {
|
||||
|
||||
/**
|
||||
* Backwards compat :-/
|
||||
@@ -41,285 +32,56 @@ public class ItemPanel extends PanelWidget {
|
||||
return super.getStackMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
public ItemHistoryPanel historyPanel;
|
||||
public Button toggleGroups;
|
||||
public ItemHistoryPanel historyPanel = new ItemHistoryPanel();
|
||||
public ItemCraftablesPanel craftablesPanel = new ItemCraftablesPanel();
|
||||
protected Button toggleGroups;
|
||||
|
||||
public static class ItemPanelSlot {
|
||||
@Deprecated
|
||||
public static class ItemPanelSlot extends ItemsPanelGridSlot {
|
||||
|
||||
public ItemStack item;
|
||||
public int slotIndex;
|
||||
|
||||
public ItemPanelSlot(int idx, ItemStack stack) {
|
||||
slotIndex = idx;
|
||||
item = stack;
|
||||
@Deprecated
|
||||
public ItemPanelSlot(int idx, ItemStack item) {
|
||||
this(idx, 0, item);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ItemPanelSlot(int idx) {
|
||||
this(idx, ItemPanels.itemPanel.getGrid().getItem(idx));
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MaskMetadata {
|
||||
|
||||
public int groupIndex = -1;
|
||||
public String displayName = "";
|
||||
public boolean extended = false;
|
||||
|
||||
public Color bgColor = null;
|
||||
public Color color = null;
|
||||
|
||||
public boolean left = false;
|
||||
public boolean top = false;
|
||||
public boolean right = false;
|
||||
public boolean bottom = false;
|
||||
|
||||
}
|
||||
|
||||
protected static class ItemPanelGrid extends ItemsGrid {
|
||||
|
||||
public ArrayList<ItemStack> newItems;
|
||||
public ArrayList<ItemStack> rawItems;
|
||||
|
||||
protected final HashMap<Integer, List<ItemStack>> groupItems = new HashMap<>();
|
||||
protected final HashMap<Integer, MaskMetadata> maskMetadata = new HashMap<>();
|
||||
protected boolean forceExpand = false;
|
||||
|
||||
public void setItems(ArrayList<ItemStack> items) {
|
||||
newItems = items;
|
||||
this(idx, 0, ItemPanels.itemPanel.getGrid().getItem(idx));
|
||||
}
|
||||
|
||||
public void refresh(GuiContainer gui) {
|
||||
private ItemPanelSlot(int slotIndex, int itemIndex, ItemStack item) {
|
||||
super(slotIndex, itemIndex, item);
|
||||
}
|
||||
|
||||
if (this.newItems != null) {
|
||||
this.groupItems.clear();
|
||||
this.forceExpand = false;
|
||||
protected static ItemPanelSlot of(ItemsPanelGridSlot slot) {
|
||||
|
||||
if (!CollapsibleItems.isEmpty() && !this.newItems.isEmpty()) {
|
||||
final Set<Integer> groups = new HashSet<>();
|
||||
boolean outsideGroup = false;
|
||||
|
||||
this.realItems = new ArrayList<>();
|
||||
this.rawItems = new ArrayList<>(this.newItems);
|
||||
|
||||
for (ItemStack stack : this.newItems) {
|
||||
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
|
||||
|
||||
if (groupIndex == -1) {
|
||||
this.realItems.add(stack);
|
||||
outsideGroup = true;
|
||||
} else {
|
||||
|
||||
if (!groups.contains(groupIndex) || CollapsibleItems.isExpanded(groupIndex)) {
|
||||
this.realItems.add(stack);
|
||||
groups.add(groupIndex);
|
||||
}
|
||||
|
||||
if (!this.groupItems.containsKey(groupIndex)) {
|
||||
this.groupItems.put(groupIndex, new ArrayList<>());
|
||||
}
|
||||
|
||||
this.groupItems.get(groupIndex).add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
// automatically opens if there are elements from only one group
|
||||
if (!outsideGroup && groups.size() == 1) {
|
||||
this.realItems = new ArrayList<>(this.newItems);
|
||||
this.forceExpand = true;
|
||||
}
|
||||
} else {
|
||||
this.realItems = new ArrayList<>(this.newItems);
|
||||
}
|
||||
|
||||
this.newItems = null;
|
||||
onItemsChanged();
|
||||
if (slot != null) {
|
||||
ItemPanelSlot customSlot = new ItemPanelSlot(slot.slotIndex, slot.itemIndex, slot.item);
|
||||
customSlot.groupIndex = slot.groupIndex;
|
||||
customSlot.displayName = slot.displayName;
|
||||
customSlot.bgItemStack = slot.bgItemStack;
|
||||
customSlot.groupSize = slot.groupSize;
|
||||
customSlot.extended = slot.extended;
|
||||
customSlot.bgColor = slot.bgColor;
|
||||
return customSlot;
|
||||
}
|
||||
|
||||
super.refresh(gui);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Integer> getMask() {
|
||||
final boolean updateBorders = this.gridMask == null;
|
||||
final List<Integer> mask = super.getMask();
|
||||
|
||||
if (updateBorders) {
|
||||
calculateGroupBorders(mask);
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
protected void calculateGroupBorders(List<Integer> mask) {
|
||||
this.maskMetadata.clear();
|
||||
|
||||
if (mask.isEmpty() || this.groupItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final HashMap<Integer, Integer> maskGroup = new HashMap<>();
|
||||
final Color collapsedColor = new Color(
|
||||
NEIClientConfig.getSetting("inventory.collapsibleItems.collapsedColor").getHexValue(),
|
||||
true);
|
||||
final Color expandedColor = new Color(
|
||||
NEIClientConfig.getSetting("inventory.collapsibleItems.expandedColor").getHexValue(),
|
||||
true);
|
||||
|
||||
for (int slotIndex = 0; slotIndex < mask.size(); slotIndex++) {
|
||||
if (mask.get(slotIndex) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = mask.get(slotIndex);
|
||||
maskGroup.put(idx, CollapsibleItems.getGroupIndex(getItem(idx)));
|
||||
}
|
||||
|
||||
for (int slotIndex = 0; slotIndex < mask.size(); slotIndex++) {
|
||||
if (mask.get(slotIndex) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = mask.get(slotIndex);
|
||||
int groupIndex = maskGroup.get(idx);
|
||||
|
||||
if (groupIndex == -1 || this.groupItems.get(groupIndex).size() == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int column = slotIndex % this.columns;
|
||||
int row = slotIndex / this.columns;
|
||||
int prevSlotIndex = (row - 1) * this.columns + column;
|
||||
int nextSlotIndex = (row + 1) * this.columns + column;
|
||||
MaskMetadata metadata = new MaskMetadata();
|
||||
|
||||
metadata.groupIndex = groupIndex;
|
||||
metadata.displayName = CollapsibleItems.getDisplayName(groupIndex);
|
||||
metadata.extended = this.forceExpand || CollapsibleItems.isExpanded(groupIndex);
|
||||
metadata.bgColor = metadata.extended ? expandedColor : collapsedColor;
|
||||
metadata.color = darkerColor(metadata.bgColor);
|
||||
metadata.left = column == 0 || idx == 0
|
||||
|| mask.get(slotIndex - 1) == null
|
||||
|| maskGroup.getOrDefault(idx - 1, -1) != groupIndex;
|
||||
metadata.right = column == this.columns - 1 || maskGroup.getOrDefault(idx + 1, -1) != groupIndex;
|
||||
|
||||
if (prevSlotIndex >= 0) {
|
||||
Integer previdx = mask.get(prevSlotIndex);
|
||||
metadata.top = previdx == null || maskGroup.getOrDefault(previdx, -1) != groupIndex;
|
||||
} else {
|
||||
metadata.top = true;
|
||||
}
|
||||
|
||||
if (nextSlotIndex < mask.size()) {
|
||||
Integer nextidx = mask.get(nextSlotIndex);
|
||||
metadata.bottom = nextidx == null || maskGroup.getOrDefault(nextidx, -1) != groupIndex;
|
||||
} else {
|
||||
metadata.bottom = true;
|
||||
}
|
||||
|
||||
this.maskMetadata.put(idx, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
|
||||
final List<Integer> mask = getMask();
|
||||
|
||||
super.beforeDrawItems(mousex, mousey, focused);
|
||||
|
||||
for (int i = 0; i < mask.size(); i++) {
|
||||
if (mask.get(i) != null) {
|
||||
drawBorder(mask.get(i), getSlotRect(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawBorder(int slotIdx, Rectangle4i rect) {
|
||||
final MaskMetadata metadata = this.maskMetadata.get(slotIdx);
|
||||
|
||||
if (metadata != null) {
|
||||
final float LINE_WIDTH = 0.75f;
|
||||
|
||||
drawRect(rect.x, rect.y, rect.w, rect.h, metadata.bgColor);
|
||||
|
||||
if (metadata.left) {
|
||||
float leftBottom = metadata.bottom ? -LINE_WIDTH : 0;
|
||||
drawRect(rect.x, rect.y, LINE_WIDTH, rect.h + leftBottom, metadata.color);
|
||||
}
|
||||
|
||||
if (metadata.right) {
|
||||
float rightTop = metadata.top ? LINE_WIDTH : 0;
|
||||
drawRect(rect.x + rect.w, rect.y - rightTop, LINE_WIDTH, rect.h + rightTop, metadata.color);
|
||||
}
|
||||
|
||||
if (metadata.top) {
|
||||
drawRect(rect.x, rect.y - LINE_WIDTH, rect.w, LINE_WIDTH, metadata.color);
|
||||
}
|
||||
|
||||
if (metadata.bottom) {
|
||||
drawRect(rect.x, rect.y + rect.h - LINE_WIDTH, rect.w, LINE_WIDTH, metadata.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Color darkerColor(Color color) {
|
||||
return new Color(
|
||||
color.getRed(),
|
||||
color.getGreen(),
|
||||
color.getBlue(),
|
||||
Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255));
|
||||
}
|
||||
|
||||
private static void drawRect(double left, double top, double width, double height, Color color) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
|
||||
GL11.glColor4f(
|
||||
color.getRed() / 255f,
|
||||
color.getGreen() / 255f,
|
||||
color.getBlue() / 255f,
|
||||
color.getAlpha() / 255f);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertex(left, top + height, 0.0D);
|
||||
tessellator.addVertex(left + width, top + height, 0.0D);
|
||||
tessellator.addVertex(left + width, top, 0.0D);
|
||||
tessellator.addVertex(left, top, 0.0D);
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawItem(Rectangle4i rect, int slotIdx) {
|
||||
final MaskMetadata metadata = this.maskMetadata.get(slotIdx);
|
||||
|
||||
if (metadata != null && !metadata.extended) {
|
||||
final List<ItemStack> groupItems = this.groupItems.get(metadata.groupIndex);
|
||||
|
||||
GuiContainerManager.drawItems.zLevel -= 10F;
|
||||
GuiContainerManager.drawItem(rect.x + 1, rect.y - 1, groupItems.get(groupItems.size() - 1), true, "");
|
||||
GuiContainerManager.drawItems.zLevel += 10F;
|
||||
|
||||
GuiContainerManager.drawItem(rect.x - 1, rect.y + 1, getItem(slotIdx), true, "");
|
||||
} else {
|
||||
super.drawItem(rect, slotIdx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageOnEmpty() {
|
||||
return ItemList.loadFinished ? null : NEIClientUtils.translate("itempanel.loading");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ItemPanel() {
|
||||
grid = new ItemPanelGrid();
|
||||
this.grid = new ItemsPanelGrid();
|
||||
}
|
||||
|
||||
public static void updateItemList(ArrayList<ItemStack> newItems) {
|
||||
((ItemPanelGrid) ItemPanels.itemPanel.getGrid()).setItems(newItems);
|
||||
final ItemsPanelGrid grid = ItemPanels.itemPanel.getGrid();
|
||||
|
||||
if (grid.rawItems.size() != newItems.size()) {
|
||||
grid.setPage(0);
|
||||
}
|
||||
|
||||
grid.setItems(newItems);
|
||||
ItemPanels.itemPanel.realItems = newItems;
|
||||
}
|
||||
|
||||
@@ -329,6 +91,11 @@ public class ItemPanel extends PanelWidget {
|
||||
|
||||
toggleGroups = new Button("G") {
|
||||
|
||||
@Override
|
||||
public String getRenderLabel() {
|
||||
return NEIClientUtils.translate("itempanel.collapsed.button");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getButtonTip() {
|
||||
return NEIClientUtils.translate("itempanel.collapsed.button.tip");
|
||||
@@ -342,36 +109,41 @@ public class ItemPanel extends PanelWidget {
|
||||
}
|
||||
};
|
||||
|
||||
historyPanel = new ItemHistoryPanel();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void scroll(int i) {
|
||||
grid.shiftPage(i);
|
||||
@Override
|
||||
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
|
||||
// use ItemPanelSlot for Backwards compat
|
||||
return ItemPanelSlot.of(this.grid.getSlotMouseOver(mousex, mousey));
|
||||
}
|
||||
|
||||
public String getLabelText() {
|
||||
return String.format("%d/%d", getPage(), Math.max(1, getNumPages()));
|
||||
}
|
||||
|
||||
protected String getPositioningSettingName() {
|
||||
return "world.panels.items";
|
||||
}
|
||||
@Override
|
||||
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;
|
||||
|
||||
public int getMarginLeft(GuiContainer gui) {
|
||||
return (gui.width + gui.xSize) / 2 + PADDING;
|
||||
}
|
||||
final int paddingLeft = (int) Math
|
||||
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.items.left").getIntValue() / 100000.0);
|
||||
final int paddingTop = (int) Math
|
||||
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.items.top").getIntValue() / 100000.0);
|
||||
final int paddingRight = (int) Math
|
||||
.ceil(maxWidth * NEIClientConfig.getSetting("world.panels.items.right").getIntValue() / 100000.0);
|
||||
final int paddingBottom = (int) Math
|
||||
.ceil(maxHeight * NEIClientConfig.getSetting("world.panels.items.bottom").getIntValue() / 100000.0);
|
||||
|
||||
public int getMarginTop(GuiContainer gui) {
|
||||
return PADDING;
|
||||
}
|
||||
final int width = Math.max(
|
||||
ItemsGrid.SLOT_SIZE,
|
||||
snapDownToMultiple(
|
||||
Math.min(freeSpace - paddingRight, maxWidth - paddingLeft - paddingRight),
|
||||
ItemsGrid.SLOT_SIZE));
|
||||
final int height = Math.max(ItemsGrid.SLOT_SIZE, maxHeight - paddingTop - paddingBottom);
|
||||
|
||||
public int getWidth(GuiContainer gui) {
|
||||
return gui.width - (gui.xSize + gui.width) / 2 - PADDING * 2;
|
||||
}
|
||||
|
||||
public int getHeight(GuiContainer gui) {
|
||||
return gui.height - getMarginTop(gui) - PADDING;
|
||||
return new Rectangle4i(gui.width - width - PADDING - paddingRight, paddingTop + PADDING, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -387,26 +159,38 @@ public class ItemPanel extends PanelWidget {
|
||||
}
|
||||
|
||||
protected int resizeFooter(GuiContainer gui) {
|
||||
if (!NEIClientConfig.showItemQuantityWidget() && NEIClientConfig.isSearchWidgetCentered()
|
||||
&& !NEIClientConfig.showHistoryPanelWidget()) {
|
||||
int footerY = y + h;
|
||||
int footerHeight = 0;
|
||||
|
||||
if (grid.getPerPage() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) {
|
||||
footerY = LayoutManager.quantity.y;
|
||||
footerHeight = LayoutManager.quantity.h + PanelWidget.PADDING;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showHistoryPanelWidget()) {
|
||||
historyPanel.x = x;
|
||||
historyPanel.w = w;
|
||||
historyPanel.h = ItemsGrid.SLOT_SIZE * NEIClientConfig.getIntSetting("inventory.history.useRows");
|
||||
|
||||
if (NEIClientConfig.showItemQuantityWidget() || !NEIClientConfig.isSearchWidgetCentered()) {
|
||||
historyPanel.y = LayoutManager.quantity.y - historyPanel.h - PanelWidget.PADDING;
|
||||
return LayoutManager.quantity.h + historyPanel.h + PanelWidget.PADDING * 2;
|
||||
} else {
|
||||
historyPanel.y = y + h - historyPanel.h;
|
||||
return historyPanel.h + PanelWidget.PADDING;
|
||||
if (historyPanel.setPanelWidth(w) != 0) {
|
||||
footerY = historyPanel.y = footerY - historyPanel.h;
|
||||
footerHeight += historyPanel.h;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showCraftablesPanelWidget()) {
|
||||
craftablesPanel.x = x;
|
||||
|
||||
if (craftablesPanel.setPanelWidth(w) != 0) {
|
||||
footerY = craftablesPanel.y = footerY - craftablesPanel.h;
|
||||
footerHeight += craftablesPanel.h;
|
||||
}
|
||||
}
|
||||
|
||||
return LayoutManager.quantity.h + PanelWidget.PADDING;
|
||||
return footerHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -414,92 +198,86 @@ public class ItemPanel extends PanelWidget {
|
||||
super.setVisible();
|
||||
|
||||
if (grid.getPerPage() > 0) {
|
||||
|
||||
if (!CollapsibleItems.isEmpty() && !grid.isEmpty()) {
|
||||
LayoutManager.addWidget(toggleGroups);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showHistoryPanelWidget() && (!grid.isEmpty() || !historyPanel.isEmpty())) {
|
||||
if (NEIClientConfig.showCraftablesPanelWidget()) {
|
||||
LayoutManager.addWidget(craftablesPanel);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.showHistoryPanelWidget()) {
|
||||
LayoutManager.addWidget(historyPanel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(GuiContainer gui) {
|
||||
super.resize(gui);
|
||||
historyPanel.resize(gui);
|
||||
this.historyPanel.resize(gui);
|
||||
this.craftablesPanel.resize(gui);
|
||||
}
|
||||
|
||||
protected ItemStack getDraggedStackWithQuantity(int mouseDownSlot) {
|
||||
ItemStack stack = grid.getItem(mouseDownSlot);
|
||||
|
||||
if (stack != null) {
|
||||
int amount = NEIClientConfig.showItemQuantityWidget() ? NEIClientConfig.getItemQuantity() : 0;
|
||||
|
||||
if (amount == 0) {
|
||||
amount = stack.getMaxStackSize();
|
||||
}
|
||||
|
||||
return NEIServerUtils.copyStack(stack, amount);
|
||||
}
|
||||
|
||||
return null;
|
||||
protected ItemStack getDraggedStackWithQuantity(ItemStack itemStack) {
|
||||
return ItemQuantityField.prepareStackWithQuantity(itemStack, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
|
||||
List<String> currenttip) {
|
||||
final ItemPanelGrid panelGrid = ((ItemPanelGrid) this.grid);
|
||||
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> currenttip) {
|
||||
|
||||
if (!panelGrid.forceExpand && !NEIClientConfig.isHidden() && !currenttip.isEmpty()) {
|
||||
final ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
if (!this.grid.forceExpand && !currenttip.isEmpty()) {
|
||||
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hoverSlot != null) {
|
||||
final MaskMetadata metadata = panelGrid.maskMetadata.get(hoverSlot.slotIndex);
|
||||
|
||||
if (metadata != null) {
|
||||
final List<ItemStack> items = panelGrid.groupItems.get(metadata.groupIndex);
|
||||
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.collapsibleItems.customName") && !metadata.extended
|
||||
&& !"".equals(metadata.displayName)) {
|
||||
currenttip.clear();
|
||||
currenttip.add(metadata.displayName + GuiDraw.TOOLTIP_LINESPACE);
|
||||
}
|
||||
|
||||
if (items != null && items.size() > 1) {
|
||||
String message = metadata.extended ? "itempanel.collapsed.hint.collapse"
|
||||
: "itempanel.collapsed.hint.expand";
|
||||
currenttip.add(
|
||||
1,
|
||||
EnumChatFormatting.GRAY + NEIClientUtils.translate(message, items.size())
|
||||
+ EnumChatFormatting.RESET);
|
||||
}
|
||||
}
|
||||
if (hoverSlot != null && hoverSlot.groupIndex >= 0
|
||||
&& NEIClientConfig.getBooleanSetting("inventory.collapsibleItems.customName")
|
||||
&& !hoverSlot.extended
|
||||
&& !"".equals(hoverSlot.displayName)) {
|
||||
currenttip.clear();
|
||||
currenttip.add(hoverSlot.displayName + GuiDraw.TOOLTIP_LINESPACE);
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleItemTooltip(gui, itemstack, mousex, mousey, currenttip);
|
||||
return super.handleItemTooltip(itemstack, mousex, mousey, currenttip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mousex, int mousey, int button) {
|
||||
final ItemPanelGrid panelGrid = ((ItemPanelGrid) this.grid);
|
||||
|
||||
if (NEIClientUtils.altKey() && button == 0 && !panelGrid.forceExpand) {
|
||||
final ItemPanelSlot hoverSlot = grid.getSlotMouseOver(mousex, mousey);
|
||||
if (button == 0 && !this.grid.forceExpand && NEIClientUtils.altKey()) {
|
||||
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hoverSlot != null) {
|
||||
final MaskMetadata metadata = panelGrid.maskMetadata.get(hoverSlot.slotIndex);
|
||||
|
||||
if (metadata != null) {
|
||||
CollapsibleItems.setExpanded(metadata.groupIndex, !metadata.extended);
|
||||
panelGrid.setItems(panelGrid.rawItems);
|
||||
return true;
|
||||
}
|
||||
if (hoverSlot != null && hoverSlot.groupIndex >= 0) {
|
||||
CollapsibleItems.setExpanded(hoverSlot.groupIndex, !hoverSlot.extended);
|
||||
this.grid.setItems(this.grid.rawItems);
|
||||
NEIClientUtils.playClickSound();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleClick(mousex, mousey, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
final ItemsPanelGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (!this.grid.forceExpand && hoverSlot != null && hoverSlot.groupIndex >= 0 && hoverSlot.groupSize > 1) {
|
||||
final String message = hoverSlot.extended ? "itempanel.collapsed.hint.collapse"
|
||||
: "itempanel.collapsed.hint.expand";
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.ALT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate(message, hoverSlot.groupSize));
|
||||
}
|
||||
|
||||
return super.handleHotkeys(mousex, mousey, hotkeys);
|
||||
}
|
||||
|
||||
public boolean containsWithSubpanels(int px, int py) {
|
||||
return contains(px, py) || NEIClientConfig.showHistoryPanelWidget() && this.historyPanel.contains(px, py)
|
||||
|| NEIClientConfig.showCraftablesPanelWidget() && this.craftablesPanel.contains(px, py);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import static codechicken.nei.NEIClientUtils.translate;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
|
||||
public class ItemQuantityField extends TextField {
|
||||
|
||||
@@ -10,18 +15,80 @@ public class ItemQuantityField extends TextField {
|
||||
field.setDisabledTextColour(0xFF303030);
|
||||
}
|
||||
|
||||
public int intValue() {
|
||||
return intValue(text());
|
||||
@Override
|
||||
protected void initInternalTextField() {
|
||||
field = new FormattedTextField(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0) {
|
||||
|
||||
{
|
||||
setPlaceholder(NEIClientUtils.translate("itempanel.quantity.default"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
String text = super.getText();
|
||||
return !isFocused() && "0".equals(text) ? "" : text;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean beforeWrite(String text) {
|
||||
if (text == null || text.isEmpty()) return true;
|
||||
try {
|
||||
return Integer.parseInt(text) >= 0;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean textboxKeyTyped(char keyChar, int keyID) {
|
||||
if (!isFocused()) return false;
|
||||
|
||||
if (super.textboxKeyTyped(keyChar, keyID)) {
|
||||
return true;
|
||||
} else if (keyID == Keyboard.KEY_DOWN) {
|
||||
setText(Integer.toString(Math.max(0, getInteger() - 1)));
|
||||
return true;
|
||||
} else if (keyID == Keyboard.KEY_UP) {
|
||||
setText(Integer.toString(getInteger() + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getInteger() {
|
||||
try {
|
||||
return Math.max(0, Integer.parseInt(text()));
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
field.setMaxStringLength(maxSearchLength);
|
||||
field.setCursorPositionZero();
|
||||
}
|
||||
|
||||
public int intValue(String s) {
|
||||
private int getInteger() {
|
||||
try {
|
||||
return Math.max(0, Integer.parseInt(s));
|
||||
return Math.max(0, Integer.parseInt(text()));
|
||||
} catch (NumberFormatException nfe) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack prepareStackWithQuantity(ItemStack stack, long amount) {
|
||||
if (stack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
amount = NEIClientUtils.getScrollMultiplier(stack);
|
||||
}
|
||||
|
||||
return StackInfo.withAmount(stack, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gainFocus() {
|
||||
if (text().equals("0")) {
|
||||
@@ -36,36 +103,21 @@ public class ItemQuantityField extends TextField {
|
||||
|
||||
@Override
|
||||
public void onTextChange(String oldText) {
|
||||
NEIClientUtils.setItemQuantity(intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
|
||||
if (!focused() && intValue() == 0) {
|
||||
field.setText(translate("itempanel.quantity.default"));
|
||||
field.setCursorPositionZero();
|
||||
field.setEnabled(false);
|
||||
super.draw(mousex, mousey);
|
||||
field.setEnabled(true);
|
||||
field.setText("0");
|
||||
} else {
|
||||
super.draw(mousex, mousey);
|
||||
}
|
||||
NEIClientUtils.setItemQuantity(getInteger());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int i, int mx, int my) {
|
||||
if (!contains(mx, my)) return false;
|
||||
int multiplier = 1;
|
||||
|
||||
if (NEIClientUtils.shiftKey()) {
|
||||
multiplier = 10;
|
||||
} else if (NEIClientUtils.controlKey()) {
|
||||
multiplier = 64;
|
||||
}
|
||||
|
||||
int quantity = intValue() + i * multiplier;
|
||||
setText(Integer.toString(quantity));
|
||||
setText(Integer.toString(Math.max(0, getInteger() + i * multiplier)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ public class ItemStackAmount {
|
||||
}
|
||||
}
|
||||
|
||||
public void addAll(Iterable<ItemStack> items) {
|
||||
for (ItemStack stack : items) {
|
||||
add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ItemStack item) {
|
||||
add(item, (long) StackInfo.getAmount(item));
|
||||
}
|
||||
@@ -30,7 +36,9 @@ public class ItemStackAmount {
|
||||
if (stack == null || stack.getItem() == null) return;
|
||||
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
|
||||
|
||||
this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L));
|
||||
if (key != null) {
|
||||
this.itemMap.put(key, value + this.itemMap.getOrDefault(key, 0L));
|
||||
}
|
||||
}
|
||||
|
||||
public Long get(ItemStack stack) {
|
||||
@@ -44,7 +52,9 @@ public class ItemStackAmount {
|
||||
if (stack == null || stack.getItem() == null) return;
|
||||
final NBTTagCompound key = StackInfo.itemStackToNBT(stack, false);
|
||||
|
||||
this.itemMap.put(key, value);
|
||||
if (key != null) {
|
||||
this.itemMap.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public long getOrDefault(ItemStack stack, long defaultAmount) {
|
||||
@@ -53,6 +63,10 @@ public class ItemStackAmount {
|
||||
return e == null ? defaultAmount : e;
|
||||
}
|
||||
|
||||
public boolean contains(ItemStack stack) {
|
||||
return get(stack) != null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.itemMap.clear();
|
||||
}
|
||||
@@ -90,6 +104,16 @@ public class ItemStackAmount {
|
||||
return this.itemMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj instanceof ItemStackAmount items) {
|
||||
return this.itemMap.equals(items.itemMap);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ItemStackAmount of(ItemStackAmount map) {
|
||||
ItemStackAmount result = new ItemStackAmount();
|
||||
result.itemMap.putAll(map.itemMap);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
@Override
|
||||
public void draw(int mx, int my) {
|
||||
|
||||
if (this.stack != null
|
||||
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();
|
||||
@@ -58,7 +58,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
|
||||
GL11.glPopMatrix();
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GuiContainerManager.enable2DRender();
|
||||
|
||||
if (NEIClientConfig.getBooleanSetting("inventory.itemzoom.showName")) {
|
||||
@@ -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);
|
||||
@@ -198,24 +198,24 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.itemzoom_toggle")) {
|
||||
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);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import static codechicken.lib.gui.GuiDraw.drawRect;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
@@ -20,17 +18,18 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemPanel.ItemPanelSlot;
|
||||
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;
|
||||
|
||||
public class ItemsGrid {
|
||||
public abstract class ItemsGrid<T extends ItemsGrid.ItemsGridSlot, M extends ItemsGrid.MouseContext> {
|
||||
|
||||
private static class ScreenCapture {
|
||||
protected static class ScreenCapture {
|
||||
|
||||
private long nextCacheRefresh = 0;
|
||||
private Framebuffer framebuffer;
|
||||
private final Framebuffer framebuffer;
|
||||
|
||||
public ScreenCapture() {
|
||||
this.framebuffer = new Framebuffer(1, 1, true);
|
||||
@@ -54,7 +53,7 @@ public class ItemsGrid {
|
||||
}
|
||||
|
||||
public void captureScreen(Runnable callback, int mode) {
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
|
||||
resetFramebuffer();
|
||||
this.framebuffer.bindFramebuffer(false);
|
||||
@@ -88,7 +87,7 @@ public class ItemsGrid {
|
||||
this.framebuffer.framebufferClear();
|
||||
}
|
||||
|
||||
// copy depth buffer from MC (fix Angelica)
|
||||
// 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(
|
||||
@@ -140,7 +139,130 @@ public class ItemsGrid {
|
||||
|
||||
}
|
||||
|
||||
public static final int SLOT_SIZE = 18;
|
||||
public static class ItemsGridSlot {
|
||||
|
||||
protected static final float DEFAULT_SLOT_SIZE = 18f;
|
||||
protected static final float LINE_WIDTH = 0.75f;
|
||||
|
||||
public final ItemStack item;
|
||||
public final int slotIndex;
|
||||
public final int itemIndex;
|
||||
|
||||
public Color borderColor = Color.BLACK;
|
||||
|
||||
public boolean borderLeft = false;
|
||||
public boolean borderTop = false;
|
||||
public boolean borderRight = false;
|
||||
public boolean borderBottom = false;
|
||||
|
||||
public ItemsGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) {
|
||||
this.slotIndex = slotIndex;
|
||||
this.itemIndex = itemIndex;
|
||||
this.item = itemStack;
|
||||
}
|
||||
|
||||
public ItemStack getItemStack() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public <M extends MouseContext> void beforeDraw(Rectangle4i rect, M mouseContext) {
|
||||
if (mouseContext != null && mouseContext.slotIndex == this.slotIndex) {
|
||||
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, HIGHLIGHT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
public <M extends MouseContext> void afterDraw(Rectangle4i rect, M mouseContext) {}
|
||||
|
||||
public RecipeId getRecipeId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void drawItem(Rectangle4i rect) {
|
||||
drawItem(getItemStack(), rect);
|
||||
}
|
||||
|
||||
protected void drawItem(ItemStack stack, Rectangle4i rect) {
|
||||
|
||||
if (rect.w != DEFAULT_SLOT_SIZE) {
|
||||
final float panelFactor = (rect.w - 2) / (DEFAULT_SLOT_SIZE - 2);
|
||||
GL11.glTranslatef(rect.x + 1, rect.y + 1, 0);
|
||||
GL11.glScaled(panelFactor, panelFactor, 1);
|
||||
|
||||
GuiContainerManager.drawItem(0, 0, stack, true, "");
|
||||
|
||||
GL11.glScaled(1f / panelFactor, 1f / panelFactor, 1);
|
||||
GL11.glTranslatef(-1 * (rect.x + 1), -1 * (rect.y + 1), 0);
|
||||
} else {
|
||||
GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, stack, true, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void drawBorder(Rectangle4i rect) {
|
||||
float halfLineWidth = LINE_WIDTH / 2f;
|
||||
|
||||
if (this.borderLeft) {
|
||||
NEIClientUtils
|
||||
.drawRect(rect.x - halfLineWidth, rect.y + halfLineWidth, LINE_WIDTH, rect.h, this.borderColor);
|
||||
}
|
||||
|
||||
if (this.borderRight) {
|
||||
NEIClientUtils.drawRect(
|
||||
rect.x + rect.w - halfLineWidth,
|
||||
rect.y - halfLineWidth,
|
||||
LINE_WIDTH,
|
||||
rect.h,
|
||||
this.borderColor);
|
||||
}
|
||||
|
||||
if (this.borderTop) {
|
||||
NEIClientUtils
|
||||
.drawRect(rect.x - halfLineWidth, rect.y - halfLineWidth, rect.w, LINE_WIDTH, this.borderColor);
|
||||
}
|
||||
|
||||
if (this.borderBottom) {
|
||||
NEIClientUtils.drawRect(
|
||||
rect.x + halfLineWidth,
|
||||
rect.y + rect.h - halfLineWidth,
|
||||
rect.w,
|
||||
LINE_WIDTH,
|
||||
this.borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MouseContext {
|
||||
|
||||
public final int slotIndex;
|
||||
public final int columnIndex;
|
||||
public final int rowIndex;
|
||||
|
||||
public MouseContext(int slotIndex, int rowIndex, int columnIndex) {
|
||||
this.slotIndex = slotIndex;
|
||||
this.rowIndex = rowIndex;
|
||||
this.columnIndex = columnIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.rowIndex, this.slotIndex, this.columnIndex);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj instanceof MouseContext context) {
|
||||
return this.columnIndex == context.columnIndex && this.rowIndex == context.rowIndex
|
||||
&& this.slotIndex == context.slotIndex;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static int SLOT_SIZE = 18;
|
||||
public static final Color HIGHLIGHT_COLOR = new Color(0xee555555, true);
|
||||
|
||||
protected int width;
|
||||
protected int height;
|
||||
@@ -157,20 +279,17 @@ public class ItemsGrid {
|
||||
|
||||
protected int rows;
|
||||
protected int columns;
|
||||
protected List<Integer> gridMask = null;
|
||||
|
||||
protected boolean[] invalidSlotMap;
|
||||
|
||||
protected Label messageLabel = new Label(getMessageOnEmpty(), true);
|
||||
|
||||
protected ScreenCapture screenCapture = null;
|
||||
|
||||
public ArrayList<ItemStack> getItems() {
|
||||
return realItems;
|
||||
}
|
||||
|
||||
public ItemStack getItem(int idx) {
|
||||
return realItems.get(idx);
|
||||
public ItemStack getItem(int itemIndex) {
|
||||
return realItems.get(itemIndex);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@@ -222,23 +341,32 @@ public class ItemsGrid {
|
||||
}
|
||||
|
||||
public int getLastRowIndex() {
|
||||
return this.columns > 0 ? (int) Math.ceil((float) this.getMask().size() / this.columns) - 1 : 0;
|
||||
final List<T> mask = getMask();
|
||||
|
||||
return this.columns > 0 && !mask.isEmpty()
|
||||
? (int) Math.ceil((float) (mask.get(mask.size() - 1).slotIndex + 1) / this.columns) - 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
messageLabel.x = marginLeft + width / 2;
|
||||
messageLabel.y = marginTop + height / 2;
|
||||
if (changed) {
|
||||
onGridChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void shiftPage(int shift) {
|
||||
@@ -270,15 +398,17 @@ public class ItemsGrid {
|
||||
if (this.screenCapture != null) {
|
||||
this.screenCapture.refreshBuffer();
|
||||
}
|
||||
this.gridMask = null;
|
||||
}
|
||||
|
||||
protected void onItemsChanged() {
|
||||
public void onItemsChanged() {
|
||||
onGridChanged();
|
||||
}
|
||||
|
||||
public void refresh(GuiContainer gui) {
|
||||
updateGuiOverlapSlots(gui);
|
||||
final int gridRenderingCacheMode = getGridRenderingCacheMode();
|
||||
if (gridRenderingCacheMode <= 0 || screenCapture == null || screenCapture.needRefresh(gridRenderingCacheMode)) {
|
||||
updateGuiOverlapSlots(gui);
|
||||
}
|
||||
shiftPage(0);
|
||||
}
|
||||
|
||||
@@ -291,11 +421,12 @@ public class ItemsGrid {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +435,7 @@ public class ItemsGrid {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -314,7 +445,7 @@ public class ItemsGrid {
|
||||
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--;
|
||||
@@ -323,20 +454,37 @@ public class ItemsGrid {
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle4i getItemRect(int idx) {
|
||||
final List<Integer> mask = getMask();
|
||||
|
||||
for (int i = 0; i < mask.size(); i++) {
|
||||
if (mask.get(i) != null && idx == mask.get(i)) {
|
||||
return getSlotRect(i);
|
||||
}
|
||||
public T getSlotMouseOver(int mousex, int mousey) {
|
||||
if (!contains(mousex, mousey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
final int overRow = getRowIndex(mousey);
|
||||
final int overColumn = getColumnIndex(mousex);
|
||||
final int slt = columns * overRow + overColumn;
|
||||
|
||||
if (overRow >= rows || overColumn >= columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getSlotBySlotIndex(slt);
|
||||
}
|
||||
|
||||
public Rectangle4i getSlotRect(int i) {
|
||||
return getSlotRect(i / columns, i % columns);
|
||||
public T getSlotBySlotIndex(int slotIndex) {
|
||||
return getMask().stream().filter(item -> item.slotIndex == slotIndex).findAny().orElse(null);
|
||||
}
|
||||
|
||||
public T getSlotByItemIndex(int itemIndex) {
|
||||
return getMask().stream().filter(item -> item.itemIndex == itemIndex).findAny().orElse(null);
|
||||
}
|
||||
|
||||
public Rectangle4i getItemRect(int itemIndex) {
|
||||
final T item = getSlotByItemIndex(itemIndex);
|
||||
return item != null ? getSlotRect(item.slotIndex) : null;
|
||||
}
|
||||
|
||||
public Rectangle4i getSlotRect(int slotIndex) {
|
||||
return getSlotRect(slotIndex / columns, slotIndex % columns);
|
||||
}
|
||||
|
||||
public Rectangle4i getSlotRect(int row, int column) {
|
||||
@@ -347,67 +495,45 @@ public class ItemsGrid {
|
||||
SLOT_SIZE);
|
||||
}
|
||||
|
||||
public int getRowIndex(int mousey) {
|
||||
return (mousey - this.marginTop) / SLOT_SIZE;
|
||||
}
|
||||
|
||||
public int getColumnIndex(int mousex) {
|
||||
return (mousex - this.marginLeft - this.paddingLeft) / SLOT_SIZE;
|
||||
}
|
||||
|
||||
public boolean isInvalidSlot(int index) {
|
||||
return invalidSlotMap[index];
|
||||
}
|
||||
|
||||
protected List<Integer> getMask() {
|
||||
public abstract List<T> getMask();
|
||||
|
||||
if (this.gridMask == null) {
|
||||
this.gridMask = new ArrayList<>();
|
||||
int idx = page * perPage;
|
||||
|
||||
for (int i = 0; i < rows * columns && idx < size(); i++) {
|
||||
this.gridMask.add(isInvalidSlot(i) ? null : idx++);
|
||||
}
|
||||
protected abstract M getMouseContext(int mousex, int mousey);
|
||||
|
||||
protected void beforeDrawItems(int mousex, int mousey, M mouseContext) {
|
||||
for (T item : getMask()) {
|
||||
item.beforeDraw(getSlotRect(item.slotIndex), mouseContext);
|
||||
}
|
||||
|
||||
return this.gridMask;
|
||||
}
|
||||
|
||||
protected void beforeDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
|
||||
|
||||
final List<Integer> mask = getMask();
|
||||
|
||||
for (int i = 0; i < mask.size(); i++) {
|
||||
if (mask.get(i) != null) {
|
||||
beforeDrawSlot(focused, mask.get(i), getSlotRect(i));
|
||||
}
|
||||
for (T item : getMask()) {
|
||||
item.drawBorder(getSlotRect(item.slotIndex));
|
||||
}
|
||||
}
|
||||
|
||||
protected void afterDrawItems(int mousex, int mousey, @Nullable ItemPanelSlot focused) {
|
||||
final List<Integer> mask = getMask();
|
||||
|
||||
for (int i = 0; i < mask.size(); i++) {
|
||||
if (mask.get(i) != null) {
|
||||
afterDrawSlot(focused, mask.get(i), getSlotRect(i));
|
||||
}
|
||||
protected void afterDrawItems(int mousex, int mousey, M mouseContext) {
|
||||
for (T item : getMask()) {
|
||||
item.afterDraw(getSlotRect(item.slotIndex), mouseContext);
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {}
|
||||
|
||||
protected void beforeDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {
|
||||
if (focused != null && focused.slotIndex == slotIdx) {
|
||||
drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); // highlight
|
||||
}
|
||||
}
|
||||
protected void drawItems() {
|
||||
|
||||
protected void afterDrawSlot(@Nullable ItemPanelSlot focused, int slotIdx, Rectangle4i rect) {}
|
||||
|
||||
private void drawItems() {
|
||||
GuiContainerManager.enableMatrixStackLogging();
|
||||
|
||||
final List<Integer> mask = getMask();
|
||||
for (int i = 0; i < mask.size(); i++) {
|
||||
if (mask.get(i) != null) {
|
||||
drawItem(getSlotRect(i), mask.get(i));
|
||||
}
|
||||
for (T item : getMask()) {
|
||||
item.drawItem(getSlotRect(item.slotIndex));
|
||||
}
|
||||
|
||||
GuiContainerManager.disableMatrixStackLogging();
|
||||
}
|
||||
|
||||
protected int getGridRenderingCacheMode() {
|
||||
@@ -419,10 +545,10 @@ public class ItemsGrid {
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemPanelSlot focused = getSlotMouseOver(mousex, mousey);
|
||||
final int gridRenderingCacheMode = getGridRenderingCacheMode();
|
||||
final M mouseContext = getMouseContext(mousex, mousey);
|
||||
|
||||
beforeDrawItems(mousex, mousey, focused);
|
||||
beforeDrawItems(mousex, mousey, mouseContext);
|
||||
|
||||
if (gridRenderingCacheMode > 0) {
|
||||
|
||||
@@ -431,49 +557,21 @@ public class ItemsGrid {
|
||||
}
|
||||
|
||||
if (this.screenCapture.needRefresh(gridRenderingCacheMode)) {
|
||||
this.screenCapture.captureScreen(this::drawItems, gridRenderingCacheMode);
|
||||
this.screenCapture.captureScreen(() -> {
|
||||
GuiContainerManager.enableMatrixStackLogging();
|
||||
drawItems();
|
||||
GuiContainerManager.disableMatrixStackLogging();
|
||||
}, gridRenderingCacheMode);
|
||||
}
|
||||
|
||||
this.screenCapture.renderCapturedScreen();
|
||||
} else {
|
||||
GuiContainerManager.enableMatrixStackLogging();
|
||||
drawItems();
|
||||
GuiContainerManager.disableMatrixStackLogging();
|
||||
}
|
||||
|
||||
afterDrawItems(mousex, mousey, focused);
|
||||
}
|
||||
|
||||
public void setVisible() {
|
||||
if (getItems().isEmpty() && getMessageOnEmpty() != null) {
|
||||
LayoutManager.addWidget(messageLabel);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawItem(Rectangle4i rect, int idx) {
|
||||
GuiContainerManager.drawItem(rect.x + 1, rect.y + 1, getItem(idx));
|
||||
}
|
||||
|
||||
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
|
||||
if (!contains(mousex, mousey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int overRow = (mousey - marginTop) / SLOT_SIZE;
|
||||
final int overColumn = (mousex - marginLeft - paddingLeft) / SLOT_SIZE;
|
||||
final int slt = columns * overRow + overColumn;
|
||||
|
||||
if (overRow >= rows || overColumn >= columns) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<Integer> mask = getMask();
|
||||
|
||||
if (mask.size() > slt && mask.get(slt) != null) {
|
||||
final int idx = mask.get(slt);
|
||||
|
||||
return new ItemPanelSlot(idx, getItem(idx));
|
||||
}
|
||||
|
||||
return null;
|
||||
afterDrawItems(mousex, mousey, mouseContext);
|
||||
}
|
||||
|
||||
public boolean contains(int px, int py) {
|
||||
@@ -487,13 +585,7 @@ public class ItemsGrid {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int r = (py - marginTop) / SLOT_SIZE;
|
||||
final int c = (px - marginLeft - paddingLeft) / SLOT_SIZE;
|
||||
|
||||
return !isInvalidSlot(columns * r + c);
|
||||
return !isInvalidSlot(columns * getRowIndex(py) + getColumnIndex(px));
|
||||
}
|
||||
|
||||
public String getMessageOnEmpty() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
public class ItemsPanelGrid extends ItemsGrid<ItemsPanelGrid.ItemsPanelGridSlot, ItemsPanelGrid.MouseContext> {
|
||||
|
||||
public static class ItemsPanelGridSlot extends ItemsGridSlot {
|
||||
|
||||
protected static final float LINE_WIDTH = 0.75f;
|
||||
|
||||
public int groupIndex = -1;
|
||||
public String displayName = "";
|
||||
public ItemStack bgItemStack;
|
||||
public int groupSize = 1;
|
||||
public boolean extended = false;
|
||||
|
||||
public Color bgColor = null;
|
||||
|
||||
public ItemsPanelGridSlot(int slotIndex, int itemIndex, ItemStack itemStack) {
|
||||
super(slotIndex, itemIndex, itemStack);
|
||||
}
|
||||
|
||||
public ItemStack getBGItem() {
|
||||
return this.bgItemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeId getRecipeId() {
|
||||
return FavoriteRecipes.getFavorite(this.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDraw(Rectangle4i rect, MouseContext mouseContext) {
|
||||
super.beforeDraw(rect, mouseContext);
|
||||
|
||||
if (this.bgColor != null) {
|
||||
NEIClientUtils.drawRect(rect.x, rect.y, rect.w, rect.h, this.bgColor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawItem(Rectangle4i rect) {
|
||||
|
||||
if (!this.extended && getBGItem() != null) {
|
||||
GuiContainerManager.drawItems.zLevel -= 10F;
|
||||
super.drawItem(getBGItem(), rect.offset(1, -1));
|
||||
GuiContainerManager.drawItems.zLevel += 10F;
|
||||
super.drawItem(getItemStack(), rect.offset(-2, 2));
|
||||
} else {
|
||||
super.drawItem(rect);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ArrayList<ItemStack> newItems;
|
||||
public ArrayList<ItemStack> rawItems = new ArrayList<>();
|
||||
protected List<ItemsPanelGridSlot> gridMask;
|
||||
|
||||
protected final Map<Integer, Integer> itemToGroup = new HashMap<>();
|
||||
protected final Map<Integer, List<ItemStack>> groupItems = new HashMap<>();
|
||||
protected boolean forceExpand = false;
|
||||
|
||||
public void setItems(ArrayList<ItemStack> items) {
|
||||
newItems = items;
|
||||
}
|
||||
|
||||
public static void updateScale() {
|
||||
SLOT_SIZE = 2 + (int) Math.ceil(8f * NEIClientConfig.getIntSetting("inventory.itempanelScale") / 100) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(GuiContainer gui) {
|
||||
|
||||
if (this.newItems != null) {
|
||||
this.groupItems.clear();
|
||||
this.itemToGroup.clear();
|
||||
this.forceExpand = false;
|
||||
|
||||
if (!CollapsibleItems.isEmpty() && !this.newItems.isEmpty()) {
|
||||
final Set<Integer> groups = new HashSet<>();
|
||||
boolean outsideGroup = false;
|
||||
|
||||
this.realItems = new ArrayList<>();
|
||||
this.rawItems = new ArrayList<>(this.newItems);
|
||||
|
||||
for (ItemStack stack : this.newItems) {
|
||||
final int groupIndex = CollapsibleItems.getGroupIndex(stack);
|
||||
|
||||
if (groupIndex == -1) {
|
||||
this.realItems.add(stack);
|
||||
outsideGroup = true;
|
||||
} else {
|
||||
|
||||
if (!groups.contains(groupIndex) || CollapsibleItems.isExpanded(groupIndex)) {
|
||||
this.itemToGroup.put(this.realItems.size(), groupIndex);
|
||||
this.realItems.add(stack);
|
||||
groups.add(groupIndex);
|
||||
}
|
||||
|
||||
this.groupItems.computeIfAbsent(groupIndex, gi -> new ArrayList<>()).add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
// automatically opens if there are elements from only one group
|
||||
if (!outsideGroup && groups.size() == 1) {
|
||||
final int groupIndex = groups.iterator().next();
|
||||
this.realItems = new ArrayList<>(this.newItems);
|
||||
this.itemToGroup.clear();
|
||||
this.forceExpand = true;
|
||||
|
||||
for (int itemIndex = 0; itemIndex < this.realItems.size(); itemIndex++) {
|
||||
this.itemToGroup.put(itemIndex, groupIndex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.realItems = new ArrayList<>(this.newItems);
|
||||
}
|
||||
|
||||
this.newItems = null;
|
||||
onItemsChanged();
|
||||
}
|
||||
|
||||
super.refresh(gui);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onGridChanged() {
|
||||
this.gridMask = null;
|
||||
super.onGridChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemsPanelGridSlot> getMask() {
|
||||
|
||||
if (this.gridMask == null) {
|
||||
this.gridMask = new ArrayList<>();
|
||||
int itemIndex = page * perPage;
|
||||
|
||||
for (int slotIndex = 0; slotIndex < rows * columns && itemIndex < size(); slotIndex++) {
|
||||
if (!isInvalidSlot(slotIndex)) {
|
||||
this.gridMask.add(new ItemsPanelGridSlot(slotIndex, itemIndex, getItem(itemIndex)));
|
||||
itemIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
calculateGroupBorders(this.gridMask);
|
||||
}
|
||||
|
||||
return this.gridMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MouseContext getMouseContext(int mousex, int mousey) {
|
||||
final ItemsPanelGridSlot hovered = getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hovered != null) {
|
||||
return new MouseContext(
|
||||
hovered.slotIndex,
|
||||
hovered.slotIndex / this.columns,
|
||||
hovered.slotIndex % this.columns);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void calculateGroupBorders(List<ItemsPanelGridSlot> gridMask) {
|
||||
|
||||
if (gridMask.isEmpty() || this.groupItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Color collapsedBGColor = new Color(
|
||||
NEIClientConfig.getSetting("inventory.collapsibleItems.collapsedColor").getHexValue(),
|
||||
true);
|
||||
final Color expandedBGColor = new Color(
|
||||
NEIClientConfig.getSetting("inventory.collapsibleItems.expandedColor").getHexValue(),
|
||||
true);
|
||||
final Color collapsedBorderColor = darkerColor(collapsedBGColor);
|
||||
final Color expandedBorderColor = darkerColor(expandedBGColor);
|
||||
final Map<Integer, Integer> borderGrid = new HashMap<>();
|
||||
|
||||
for (ItemsPanelGridSlot item : gridMask) {
|
||||
borderGrid.put(item.slotIndex, this.itemToGroup.getOrDefault(item.itemIndex, -1));
|
||||
}
|
||||
|
||||
for (ItemsPanelGridSlot item : gridMask) {
|
||||
final int groupIndex = borderGrid.get(item.slotIndex);
|
||||
final List<ItemStack> groupItems = this.groupItems.get(groupIndex);
|
||||
|
||||
if (groupItems == null || groupItems.size() == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int column = item.slotIndex % this.columns;
|
||||
int row = item.slotIndex / this.columns;
|
||||
int prevSlotIndex = (row - 1) * this.columns + column;
|
||||
int nextSlotIndex = (row + 1) * this.columns + column;
|
||||
|
||||
item.groupIndex = groupIndex;
|
||||
item.groupSize = groupItems.size();
|
||||
item.bgItemStack = groupItems.get(groupItems.size() - 1);
|
||||
item.displayName = CollapsibleItems.getDisplayName(groupIndex);
|
||||
item.extended = this.forceExpand || CollapsibleItems.isExpanded(groupIndex);
|
||||
item.bgColor = item.extended ? expandedBGColor : collapsedBGColor;
|
||||
|
||||
item.borderColor = item.extended ? expandedBorderColor : collapsedBorderColor;
|
||||
item.borderLeft = column == 0 || borderGrid.getOrDefault(item.slotIndex - 1, -1) != groupIndex;
|
||||
item.borderRight = column + 1 == this.columns
|
||||
|| borderGrid.getOrDefault(item.slotIndex + 1, -1) != groupIndex;
|
||||
item.borderTop = borderGrid.getOrDefault(prevSlotIndex, -1) != groupIndex;
|
||||
item.borderBottom = borderGrid.getOrDefault(nextSlotIndex, -1) != groupIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Color darkerColor(Color color) {
|
||||
return new Color(
|
||||
color.getRed(),
|
||||
color.getGreen(),
|
||||
color.getBlue(),
|
||||
Math.min((int) (color.getAlpha() + (255f / 5) * 2), 255));
|
||||
}
|
||||
|
||||
public void draw(int mousex, int mousey) {
|
||||
if (ItemList.loadFinished) {
|
||||
super.draw(mousex, mousey);
|
||||
} else {
|
||||
GuiDraw.drawStringC(
|
||||
NEIClientUtils
|
||||
.cropText(GuiDraw.fontRenderer, NEIClientUtils.translate("itempanel.loading"), width - 10),
|
||||
marginLeft + width / 2,
|
||||
marginTop + height / 2,
|
||||
0xffffff,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,65 +3,298 @@ package codechicken.nei;
|
||||
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;
|
||||
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
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 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 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 * ItemsGrid.SLOT_SIZE, fontRenderer.getStringWidth(this.label) + 15);
|
||||
this.size.height = this.rows * ItemsGrid.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) / ItemsGrid.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 final boolean showNames;
|
||||
|
||||
protected int length = 0;
|
||||
protected int rows = 0;
|
||||
protected int nameRows = 0;
|
||||
protected int gridColumns = 0;
|
||||
protected int gridCount = 0;
|
||||
|
||||
public static ItemsTooltipLineHandler grid(String label, List<ItemStack> items, int maxRows) {
|
||||
return new ItemsTooltipLineHandler(label, items, true, maxRows, false);
|
||||
}
|
||||
|
||||
public static ItemsTooltipLineHandler list(String label, List<ItemStack> items, int maxRows) {
|
||||
return new ItemsTooltipLineHandler(label, items, true, maxRows, true);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items) {
|
||||
this(label, items, true, DEFAULT_MAX_ROWS, false);
|
||||
}
|
||||
|
||||
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
|
||||
this(label, items, saveStackSize, maxRows, false);
|
||||
}
|
||||
|
||||
protected ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows,
|
||||
boolean showNames) {
|
||||
this.label = label;
|
||||
this.items = groupingItemStacks(items);
|
||||
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
|
||||
this.length = this.items.size();
|
||||
this.showNames = showNames;
|
||||
|
||||
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 (!this.showNames || !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 = 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) {
|
||||
final ItemStack realStack = items.stream().filter(stack -> NEIClientUtils.areStacksSameType(stack, activeStack))
|
||||
.findFirst().orElse(null);
|
||||
this.activeStackIndex = items.indexOf(realStack);
|
||||
}
|
||||
|
||||
public ItemStack getActiveStack() {
|
||||
return this.activeStackIndex == -1 ? null : this.items.get(this.activeStackIndex);
|
||||
}
|
||||
|
||||
public List<ItemStack> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
@@ -77,6 +310,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;
|
||||
@@ -85,50 +323,88 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
|
||||
|
||||
fontRenderer.drawStringWithShadow(this.labelColor + this.label + ":", x, y, 0);
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
|
||||
GL11.glScaled(1, 1, 3);
|
||||
GL11.glTranslatef(x, y + fontRenderer.FONT_HEIGHT + 2, 0);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
|
||||
for (int i = 0; i < this.count; i++) {
|
||||
ItemStack drawStack = this.items.get(i);
|
||||
int col = i % this.columns;
|
||||
int row = i / this.columns;
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
|
||||
String stackSize = !this.saveStackSize || drawStack.stackSize == 0 ? ""
|
||||
: ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize);
|
||||
final int xTranslation = x;
|
||||
final int yTranslation = y + fontRenderer.FONT_HEIGHT + 2;
|
||||
final int zTranslation = GuiContainerManager.TOOLTIP_Z_OFFSET;
|
||||
GL11.glTranslatef(xTranslation, yTranslation, zTranslation);
|
||||
|
||||
drawItem(col * ItemsGrid.SLOT_SIZE, row * ItemsGrid.SLOT_SIZE, drawStack, stackSize);
|
||||
final int gridShift = gridIndexShift();
|
||||
|
||||
for (int index = 0; index < this.nameRows; index++) {
|
||||
drawSlot(0, index * SLOT_SIZE, index);
|
||||
}
|
||||
|
||||
if (this.count < this.items.size()) {
|
||||
String text = "+" + (this.items.size() - this.count);
|
||||
|
||||
fontRenderer.drawStringWithShadow(
|
||||
text,
|
||||
MAX_COLUMNS * ItemsGrid.SLOT_SIZE - fontRenderer.getStringWidth(text) - 2,
|
||||
(this.rows - 1) * ItemsGrid.SLOT_SIZE + (ItemsGrid.SLOT_SIZE - fontRenderer.FONT_HEIGHT) / 2,
|
||||
0xee555555);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
GL11.glPopMatrix();
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
final int textShift = ICON_OFFSET + Math.round((ICON_SIZE - fontRenderer.FONT_HEIGHT) / 2f);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
final int hidden = this.length - this.nameRows - this.gridCount;
|
||||
|
||||
if (hidden > 0) {
|
||||
final String text = "+" + hidden;
|
||||
fontRenderer.drawStringWithShadow(
|
||||
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 = ItemStackAmount.of(items).values();
|
||||
final List<ItemStack> result = new ArrayList<>();
|
||||
|
||||
for (ItemStack stack : result) {
|
||||
if (StackInfo.itemStackToNBT(stack).hasKey("gtFluidName")) {
|
||||
stack.stackSize = 0;
|
||||
for (Map.Entry<NBTTagCompound, Long> entry : ItemStackAmount.of(items).entrySet()) {
|
||||
result.add(StackInfo.loadFromNBT(entry.getKey(), 0));
|
||||
this.amounts.add(Math.max(0, entry.getValue()));
|
||||
}
|
||||
|
||||
if (result.isEmpty()) {
|
||||
for (ItemStack stack : items) {
|
||||
result.add(stack);
|
||||
this.amounts.add(0L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,21 @@ 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 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 {
|
||||
|
||||
@@ -14,30 +25,132 @@ 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;
|
||||
|
||||
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) {
|
||||
return Mouse.isButtonDown(keyCode + 100);
|
||||
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
|
||||
return Keyboard.isKeyDown(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
|
||||
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 boolean down = NEIClientConfig.isKeyHashDown(entry.getKey());
|
||||
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;
|
||||
@@ -65,8 +66,10 @@ import codechicken.nei.guihook.IContainerDrawHandler;
|
||||
import codechicken.nei.guihook.IContainerInputHandler;
|
||||
import codechicken.nei.guihook.IContainerObjectHandler;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.AutoCraftingManager;
|
||||
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 {
|
||||
@@ -101,7 +104,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
public static Button delete;
|
||||
public static ButtonCycled gamemode;
|
||||
public static Button rain;
|
||||
public static Button magnet;
|
||||
public static ButtonCycled magnet;
|
||||
public static Button[] timeButtons = new Button[4];
|
||||
public static Button heal;
|
||||
|
||||
@@ -116,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());
|
||||
|
||||
@@ -135,10 +133,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
@Override
|
||||
public void onPreDraw(GuiContainer gui) {
|
||||
if (!isHidden() && isEnabled() && gui instanceof InventoryEffectRenderer) // Reset the gui to the center of the
|
||||
// screen, for potion effect offsets
|
||||
// etc
|
||||
{
|
||||
// Reset the gui to the center of the screen, for potion effect offsets etc
|
||||
if (isEnabled() && gui instanceof InventoryEffectRenderer) {
|
||||
gui.guiLeft = (gui.width - gui.xSize) / 2;
|
||||
gui.guiTop = (gui.height - gui.ySize) / 2;
|
||||
|
||||
@@ -224,18 +220,17 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.hide")) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -293,19 +288,6 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRenderTooltips(GuiContainer gui, int mousex, int mousey, List<String> tooltip) {
|
||||
|
||||
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
|
||||
final Widget focused = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (focused != null) {
|
||||
focused.postDrawTooltips(mousex, mousey, tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
|
||||
|
||||
@@ -356,6 +338,10 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
|
||||
final Widget focused = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (focused != null) {
|
||||
currenttip = focused.handleItemTooltip(itemstack, mousex, mousey, currenttip);
|
||||
}
|
||||
|
||||
if (focused instanceof IContainerTooltipHandler tip) {
|
||||
currenttip = tip.handleItemTooltip(gui, itemstack, mousex, mousey, currenttip);
|
||||
}
|
||||
@@ -370,9 +356,14 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) {
|
||||
final Widget focused = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (focused instanceof IContainerTooltipHandler tip) {
|
||||
hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys);
|
||||
if (focused != null) {
|
||||
hotkeys = focused.handleHotkeys(mousex, mousey, hotkeys);
|
||||
|
||||
if (focused instanceof IContainerTooltipHandler tip) {
|
||||
hotkeys = tip.handleHotkeys(gui, mousex, mousey, hotkeys);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return hotkeys;
|
||||
@@ -602,7 +593,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
return getStateTip("rain", state);
|
||||
}
|
||||
};
|
||||
magnet = new Button() {
|
||||
magnet = new ButtonCycled(2) {
|
||||
|
||||
@Override
|
||||
public boolean onButtonPress(boolean rightclick) {
|
||||
@@ -785,9 +776,10 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
addWidget(itemZoom);
|
||||
|
||||
if (visiblity.showSubsetDropdown) {
|
||||
// Bookmarks or Subset/dropdown
|
||||
addWidget(dropDown);
|
||||
}
|
||||
|
||||
addWidget(DebugHandlerWidget.instance);
|
||||
}
|
||||
|
||||
public static LayoutStyle getLayoutStyle(int id) {
|
||||
@@ -811,6 +803,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
if (!isEnabled()) return;
|
||||
|
||||
AutoFocusWidget.instance.guiTick();
|
||||
|
||||
for (Widget widget : drawWidgets) widget.update();
|
||||
}
|
||||
|
||||
@@ -830,7 +824,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
|
||||
@Override
|
||||
public boolean shouldShowTooltip(GuiContainer gui) {
|
||||
return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState();
|
||||
return itemPanel.draggedStack == null && !bookmarkPanel.inEditingState() && !AutoCraftingManager.processing();
|
||||
}
|
||||
|
||||
/** Note: this method isn't actually used by this mod, but NEI add-ons might need it. */
|
||||
@@ -858,13 +852,11 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
ItemStack item = slot.getStack();
|
||||
|
||||
if (item == null ? !getSearchExpression().equals("") : !searchField.getFilter().matches(item)) {
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glTranslatef(0, 0, 150);
|
||||
drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000);
|
||||
GL11.glTranslatef(0, 0, -150);
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
GL11.glTranslatef(0, 0, 150);
|
||||
drawRect(slot.xDisplayPosition, slot.yDisplayPosition, 16, 16, 0x80000000);
|
||||
GL11.glTranslatef(0, 0, -150);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,33 +901,39 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
|
||||
final int ty2 = ty + 20 - h2 - te;
|
||||
final int tx2 = 200 - w2 - te;
|
||||
|
||||
final boolean is2DTexture = GL11.glGetBoolean(GL11.GL_TEXTURE_2D);
|
||||
|
||||
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) {
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
drawRect(slotX, slotY, 16, 16, isPresent ? 0x8000AA00 : 0x80AA0000);
|
||||
GL11.glPopAttrib();
|
||||
} 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() {
|
||||
@@ -946,12 +944,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 {
|
||||
|
||||
@@ -31,16 +30,13 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
LayoutManager.gamemode.icons[0] = new Image(132, 12, 12, 12);
|
||||
LayoutManager.gamemode.icons[1] = new Image(156, 12, 12, 12);
|
||||
LayoutManager.gamemode.icons[2] = new Image(168, 12, 12, 12);
|
||||
LayoutManager.magnet.icon = new Image(180, 24, 12, 12);
|
||||
LayoutManager.magnet.icons[0] = new Image(180, 24, 12, 12);
|
||||
LayoutManager.magnet.icons[1] = new Image(180, 36, 12, 12);
|
||||
LayoutManager.timeButtons[0].icon = new Image(132, 24, 12, 12);
|
||||
LayoutManager.timeButtons[1].icon = new Image(120, 24, 12, 12);
|
||||
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
|
||||
@@ -52,7 +48,7 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
public void layout(GuiContainer gui, VisiblityData visiblity) {
|
||||
reset();
|
||||
|
||||
leftSize = ItemPanels.bookmarkPanel.getWidth(gui);
|
||||
leftSize = (gui.width - gui.xSize) / 2 - 4;
|
||||
numButtons = Math.max(leftSize / 20, 1);
|
||||
|
||||
LayoutManager.delete.state = 0x4;
|
||||
@@ -86,6 +82,7 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
LayoutManager.options.index = NEIClientConfig.getCheatMode();
|
||||
|
||||
LayoutManager.magnet.state = 0x4 | (NEIClientConfig.getMagnetMode() ? 1 : 0);
|
||||
LayoutManager.magnet.index = (LayoutManager.magnet.state & 0x1) != 0 ? 1 : 0;
|
||||
|
||||
if (NEIClientConfig.canPerformAction("delete")) {
|
||||
layoutButton(LayoutManager.delete);
|
||||
@@ -184,7 +181,7 @@ public class LayoutStyleMinecraft extends LayoutStyle {
|
||||
final Rectangle4i area = new Rectangle4i(0, 0, 0, BUTTON_SIZE);
|
||||
|
||||
if (NEIClientConfig.isSearchWidgetCentered()) {
|
||||
area.w = gui.xSize - MARGIN * 2;
|
||||
area.w = Math.min(gui.xSize, 176) - MARGIN * 2;
|
||||
area.x = (gui.width - area.w) / 2;
|
||||
area.y = gui.height - BUTTON_SIZE - MARGIN;
|
||||
} else if (NEIClientConfig.showItemQuantityWidget()) {
|
||||
@@ -242,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,18 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCacheBookmarkData(PacketCustom packet) {
|
||||
final String payloadId = packet.readString();
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
|
||||
if (payloadTag != null) {
|
||||
CommandBookmarkAdd.cachePayload(payloadId, payloadTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +200,12 @@ public class NEICPH implements IClientPacketHandler {
|
||||
packet.sendToServer();
|
||||
}
|
||||
|
||||
public static void sendChatLink(NBTTagCompound payloadTag) {
|
||||
PacketCustom packet = new PacketCustom(channel, C2S.SEND_CHAT_ITEM_LINK);
|
||||
packet.writeNBTTagCompound(payloadTag);
|
||||
packet.sendToServer();
|
||||
}
|
||||
|
||||
public static void sendOpenEnchantmentWindow() {
|
||||
PacketCustom packet = new PacketCustom(channel, C2S.REQUEST_ENCHANTMENT_GUI);
|
||||
packet.sendToServer();
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -16,7 +17,6 @@ import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.SaveFormatComparator;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
@@ -45,7 +45,6 @@ import codechicken.nei.config.GuiHighlightTips;
|
||||
import codechicken.nei.config.GuiNEIOptionList;
|
||||
import codechicken.nei.config.GuiOptionList;
|
||||
import codechicken.nei.config.GuiPanelSettings;
|
||||
import codechicken.nei.config.OptionButton;
|
||||
import codechicken.nei.config.OptionCycled;
|
||||
import codechicken.nei.config.OptionGamemodes;
|
||||
import codechicken.nei.config.OptionIntegerField;
|
||||
@@ -59,8 +58,9 @@ import codechicken.nei.config.preset.GuiPresetList;
|
||||
import codechicken.nei.event.NEIConfigsLoadedEvent;
|
||||
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.obfuscator.ObfuscationRun;
|
||||
|
||||
public class NEIClientConfig {
|
||||
@@ -145,6 +145,11 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.widgetsenabled").getBooleanValue(true);
|
||||
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);
|
||||
tag.getTag("inventory.hidden").getBooleanValue(false);
|
||||
tag.getTag("inventory.cheatmode").getIntValue(2);
|
||||
tag.getTag("inventory.lockmode").setComment(
|
||||
@@ -178,6 +183,7 @@ public class NEIClientConfig {
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
ItemPanels.bookmarkPanel.save();
|
||||
ItemPanels.bookmarkPanel.load();
|
||||
return true;
|
||||
}
|
||||
@@ -191,27 +197,44 @@ public class NEIClientConfig {
|
||||
.getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.recipeTooltipsMode", 4, true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.showRecipeMarker").setComment("Show Recipe Marker").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.showRecipeMarker", true));
|
||||
tag.getTag("inventory.bookmarks.showRecipeMarkerMode").setComment("Show Recipe Marker").getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.showRecipeMarkerMode", 3, true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.craftingChainDir").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.bookmarks.craftingChainDir", 2, true));
|
||||
tag.getTag("inventory.bookmarks.recipeMarkerColor").setComment("Color of the icon marker")
|
||||
.getHexValue(0xADADAD);
|
||||
API.addOption(
|
||||
new OptionIntegerField(
|
||||
"inventory.bookmarks.recipeMarkerColor",
|
||||
0,
|
||||
OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
|
||||
tag.getTag("inventory.bookmarks.showRecipeHandlerIcon").setComment("Show Recipe Handler Icon")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.showRecipeHandlerIcon", true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.ignorePotionOverlap").setComment("Ignore overlap with potion effect HUD")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.ignorePotionOverlap", true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.bookmarkItemsWithRecipe").setComment("Bookmark items with recipe")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.bookmarks.bookmarkItemsWithRecipe", true));
|
||||
|
||||
tag.getTag("inventory.bookmarks.bookmarkRecipeWithCount").setComment("Bookmark recipe with count")
|
||||
.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));
|
||||
|
||||
tag.getTag("inventory.guirecipe.jeiStyleRecipeCatalyst").setComment("Enable/disable JEI Style Recipe Catalysts")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleRecipeCatalyst", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.jeiStyleCycledIngredients").setComment("JEI styled cycled ingredients")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.jeiStyleCycledIngredients", true));
|
||||
tag.getTag("inventory.guirecipe.jeiStyleRecipeCatalyst")
|
||||
.setComment("JEI Style Recipe Catalysts (0 - none, 1 - grid, 2 - scroll)").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.guirecipe.jeiStyleRecipeCatalyst", 3, true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.cycledIngredientsTooltip").setComment("Show cycled ingredients tooltip")
|
||||
.getBooleanValue(true);
|
||||
@@ -229,31 +252,82 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.slotHighlightPresent", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.infiniteScroll").setComment("Use Infinite Scroll for Handlers")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.infiniteScroll", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.maxHeight").setComment("Recipe GUI Max Height").getIntValue(370);
|
||||
API.addOption(new OptionIntegerField("inventory.guirecipe.maxHeight", 166, 4000));
|
||||
|
||||
tag.getTag("inventory.guirecipe.shiftOverlayRecipe")
|
||||
.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.profile").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.handlerInfo").setComment("ADVANCED: Handler Info Widget")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.handlerInfo", true));
|
||||
|
||||
tag.getTag("inventory.subsets.enabled").setComment("Enable/disable Subsets Dropdown").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.subsets.enabled", true));
|
||||
|
||||
tag.getTag("inventory.subsets.widgetPosition").setComment("Subsets Widget Position").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.subsets.widgetPosition", true));
|
||||
|
||||
tag.getTag("inventory.guirecipe.profile").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.guirecipe.profile", true));
|
||||
|
||||
tag.getTag("inventory.history.enabled").setComment("Enable/disable History Panel").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.history.enabled", true));
|
||||
|
||||
tag.getTag("inventory.history.historyColor").setComment("Color of the history area display")
|
||||
.getHexValue(0xee555555);
|
||||
API.addOption(new OptionIntegerField("inventory.history.historyColor", 0, OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
tag.getTag("inventory.history.color").setComment("Color of the history area display").getHexValue(0xee555555);
|
||||
API.addOption(new OptionIntegerField("inventory.history.color", 0, OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
|
||||
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.splittingMode").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.history.splittingMode", 2, true));
|
||||
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));
|
||||
|
||||
tag.getTag("inventory.craftables.color").setComment("Color of the craftables area display")
|
||||
.getHexValue(0xee555555);
|
||||
API.addOption(new OptionIntegerField("inventory.craftables.color", 0, OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
|
||||
tag.getTag("inventory.craftables.useRows").setComment("Rows used in craftables areas").getIntValue(2);
|
||||
API.addOption(new OptionIntegerField("inventory.craftables.useRows", 1, 5));
|
||||
|
||||
tag.getTag("inventory.craftables.favoritesOnly").setComment("Show Only Favorites Recipes")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.craftables.favoritesOnly", true));
|
||||
|
||||
tag.getTag("inventory.collapsibleItems.enabled").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.collapsibleItems.enabled", true) {
|
||||
@@ -263,7 +337,7 @@ public class NEIClientConfig {
|
||||
super.onClick(button);
|
||||
CollapsibleItems.saveStates();
|
||||
CollapsibleItems.load();
|
||||
LayoutManager.markItemsDirty();
|
||||
ItemList.refreshItems.restart();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -287,19 +361,6 @@ public class NEIClientConfig {
|
||||
0,
|
||||
OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
|
||||
API.addOption(
|
||||
new OptionButton(
|
||||
"inventory.collapsibleItems.reloadLabel",
|
||||
"inventory.collapsibleItems.reloadButton",
|
||||
"inventory.collapsibleItems.reloadTip") {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
LayoutManager.markItemsDirty();
|
||||
return super.onClick(button);
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.itemzoom.enabled").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.itemzoom.enabled", true));
|
||||
|
||||
@@ -325,6 +386,15 @@ public class NEIClientConfig {
|
||||
tag.getTag("inventory.itemzoom.nameColor").getHexValue(0xFFFFFFFF);
|
||||
API.addOption(new OptionIntegerField("inventory.itemzoom.nameColor", 0, OptionIntegerField.UNSIGNED_INT_MAX));
|
||||
|
||||
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));
|
||||
|
||||
@@ -347,6 +417,19 @@ public class NEIClientConfig {
|
||||
|
||||
API.addOption(new OptionOpenGui("world.presets", GuiPresetList.class));
|
||||
|
||||
tag.getTag("world.overlays.lock").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("world.overlays.lock", true));
|
||||
|
||||
tag.getTag("inventory.itempanelScale").getIntValue(100);
|
||||
ItemsPanelGrid.updateScale();
|
||||
API.addOption(new OptionIntegerField("inventory.itempanelScale", 1, 1000) {
|
||||
|
||||
@Override
|
||||
public void onTextChange(String text) {
|
||||
ItemsPanelGrid.updateScale();
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.disableMouseScrollTransfer").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.disableMouseScrollTransfer", true));
|
||||
|
||||
@@ -359,11 +442,24 @@ public class NEIClientConfig {
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.gridRenderingCacheFPS").getIntValue(8);
|
||||
|
||||
tag.getTag("inventory.gridRenderingCacheMode").getIntValue(0);
|
||||
API.addOption(new OptionCycled("inventory.gridRenderingCacheMode", 3, true));
|
||||
|
||||
tag.getTag("inventory.gridRenderingCacheFPS").getIntValue(8);
|
||||
API.addOption(new OptionIntegerField("inventory.gridRenderingCacheFPS", 1, 144) {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return OpenGlHelper.framebufferSupported && getIntSetting("inventory.gridRenderingCacheMode") == 1;
|
||||
}
|
||||
|
||||
});
|
||||
tag.getTag("inventory.hotkeysHelpText").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.hotkeysHelpText", true));
|
||||
tag.getTag("inventory.showHotkeys").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.showHotkeys", true));
|
||||
|
||||
tag.getTag("loadPluginsInParallel").getBooleanValue(true);
|
||||
tag.getTag("itemLoadingTimeout").getIntValue(500);
|
||||
|
||||
tag.getTag("command.creative").setDefaultValue("/gamemode {0} {1}");
|
||||
@@ -384,6 +480,27 @@ public class NEIClientConfig {
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.optimize_gui_overlap_computation", true));
|
||||
|
||||
tag.getTag("inventory.autocrafting").setComment("Autocrafting").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.autocrafting", true));
|
||||
|
||||
tag.getTag("inventory.itemUntranslator").setComment("Item Untranslator").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.itemUntranslator", true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
|
||||
if (enableItemUntranslator()) {
|
||||
ItemUntranslator.getInstance().load();
|
||||
} else {
|
||||
ItemUntranslator.getInstance().unload();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
tag.getTag("tools.handler_load_from_config").setComment("ADVANCED: Load handlers from config")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("tools.handler_load_from_config", true) {
|
||||
@@ -460,12 +577,15 @@ public class NEIClientConfig {
|
||||
API.addOption(new OptionCycled("inventory.search.widgetAutofocus", 3, true));
|
||||
|
||||
tag.getTag("inventory.search.patternMode").setComment("Search Mode").getIntValue(1);
|
||||
API.addOption(new OptionCycled("inventory.search.patternMode", 3, true) {
|
||||
API.addOption(new OptionCycled("inventory.search.patternMode", 4, true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -518,7 +638,7 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
// SearchField.searchParser.clearCache();
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
}
|
||||
|
||||
@@ -535,8 +655,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -560,8 +683,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -584,8 +710,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -608,8 +737,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -632,8 +764,11 @@ public class NEIClientConfig {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
if (!super.onClick(button)) {
|
||||
return false;
|
||||
}
|
||||
SearchField.searchParser.clearCache();
|
||||
return super.onClick(button);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -651,6 +786,9 @@ public class NEIClientConfig {
|
||||
|
||||
});
|
||||
|
||||
tag.getTag("inventory.search.logSearchExceptions").setComment("Search exceptions for extended+")
|
||||
.getBooleanValue(false);
|
||||
|
||||
String prefixRedefinitions = tag.getTag("inventory.search.prefixRedefinitions").setComment(
|
||||
"Redefine search prefixes by providing a char-to-char map (JSON). The keys are the original prefixes, the values the new ones. Example: { \"$\": \"€\", \"#\": \"+\", \"@\": \"°\", \"%\": \"!\" }")
|
||||
.getValue("{}");
|
||||
@@ -661,47 +799,88 @@ public class NEIClientConfig {
|
||||
};
|
||||
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
|
||||
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
|
||||
SearchField.searchParser.clearCache();
|
||||
} catch (JsonParseException e) {
|
||||
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void setFavoriteDefaults(ConfigTagParent tag) {
|
||||
|
||||
tag.getTag("inventory.favorites.enabled").getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.enabled", true));
|
||||
|
||||
tag.getTag("inventory.favorites.worldSpecific").setComment("Global or world specific favorites")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.worldSpecific", true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
FavoriteRecipes.reload();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
tag.getTag("inventory.favorites.showRecipeTooltipInPanel").setComment("Show recipe tooltips in Items Panel")
|
||||
.getBooleanValue(true);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInPanel", true));
|
||||
|
||||
tag.getTag("inventory.favorites.showRecipeTooltipInGui").setComment("Show recipe tooltips in Recipe Gui")
|
||||
.getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.showRecipeTooltipInGui", true));
|
||||
|
||||
tag.getTag("inventory.favorites.generateSingleRecipeFavorites")
|
||||
.setComment("Automatically generate favorites for items with only one recipe").getBooleanValue(false);
|
||||
API.addOption(new OptionToggleButton("inventory.favorites.generateSingleRecipeFavorites", true) {
|
||||
|
||||
@Override
|
||||
public boolean onClick(int button) {
|
||||
super.onClick(button);
|
||||
FavoriteRecipes.loadAutoGeneratedRecipes();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
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.addHashBind("gui.bookmark", Keyboard.KEY_A);
|
||||
API.addHashBind("gui.bookmark_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH);
|
||||
API.addHashBind("gui.remove_recipe", Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH);
|
||||
API.addHashBind("gui.bookmark_count", Keyboard.KEY_A + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind(
|
||||
"gui.bookmark_recipe_count",
|
||||
Keyboard.KEY_A + NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind("gui.bookmark_pull_items", Keyboard.KEY_V);
|
||||
API.addHashBind("gui.bookmark_pull_items_ingredients", Keyboard.KEY_V + NEIClientUtils.SHIFT_HASH);
|
||||
API.addHashBind("gui.overlay", Keyboard.KEY_S);
|
||||
API.addHashBind("gui.overlay_use", Keyboard.KEY_S + NEIClientUtils.SHIFT_HASH);
|
||||
API.addHashBind("gui.overlay_hide", Keyboard.KEY_S + NEIClientUtils.CTRL_HASH);
|
||||
API.addHashBind("gui.hide_bookmarks", Keyboard.KEY_B);
|
||||
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.addKeyBind("gui.getprevioussearch", Keyboard.KEY_UP);
|
||||
API.addKeyBind("gui.getnextsearch", Keyboard.KEY_DOWN);
|
||||
API.addHashBind("gui.next_tooltip", Keyboard.KEY_Z);
|
||||
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);
|
||||
@@ -713,8 +892,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.addKeyBind("copy.name", Keyboard.KEY_C);
|
||||
API.addKeyBind("copy.oredict", Keyboard.KEY_D);
|
||||
API.addKeyBind("copy.identifier", Keyboard.KEY_X);
|
||||
}
|
||||
|
||||
public static OptionList getOptionList() {
|
||||
@@ -722,24 +903,32 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
public static void loadWorld(String worldPath) {
|
||||
final File specificDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
|
||||
final boolean newWorld = !specificDir.exists();
|
||||
unloadWorld();
|
||||
setInternalEnabled(true);
|
||||
|
||||
if (!worldPath.equals(NEIClientConfig.worldPath)) {
|
||||
if (!worldPath.equals(NEIClientConfig.worldPath) || newWorld) {
|
||||
NEIClientConfig.worldPath = worldPath;
|
||||
|
||||
logger.debug("Loading " + (Minecraft.getMinecraft().isSingleplayer() ? "Local" : "Remote") + " World");
|
||||
|
||||
final File specificDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath);
|
||||
final boolean newWorld = !specificDir.exists();
|
||||
|
||||
if (newWorld) {
|
||||
specificDir.mkdirs();
|
||||
}
|
||||
|
||||
world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg")));
|
||||
bootNEI(ClientUtils.getWorld());
|
||||
onWorldLoad(newWorld);
|
||||
|
||||
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();
|
||||
onWorldLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,7 +936,7 @@ public class NEIClientConfig {
|
||||
return NEIClientConfig.worldPath;
|
||||
}
|
||||
|
||||
private static void onWorldLoad(boolean newWorld) {
|
||||
private static void onWorldLoad() {
|
||||
world.config.setComment(
|
||||
"World based configuration of NEI.\nMost of these options can be changed ingame.\nDeleting any element will restore it to it's default value");
|
||||
|
||||
@@ -755,12 +944,6 @@ public class NEIClientConfig {
|
||||
creativeInv = new ItemStack[54];
|
||||
LayoutManager.searchField.setText(getSearchExpression());
|
||||
LayoutManager.quantity.setText(Integer.toString(getItemQuantity()));
|
||||
|
||||
if (newWorld && Minecraft.getMinecraft().isSingleplayer()) {
|
||||
world.config.getTag("inventory.cheatmode")
|
||||
.setIntValue(NEIClientUtils.mc().playerController.isInCreativeMode() ? 2 : 0);
|
||||
}
|
||||
|
||||
NEIInfo.load(ClientUtils.getWorld());
|
||||
}
|
||||
|
||||
@@ -774,62 +957,65 @@ public class NEIClientConfig {
|
||||
}
|
||||
|
||||
public static void unloadWorld() {
|
||||
if (ItemPanels.bookmarkPanel != null) {
|
||||
ItemPanels.bookmarkPanel.saveBookmarks();
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemPanels.bookmarkPanel.save();
|
||||
ItemPanels.itemPanel.historyPanel.save();
|
||||
|
||||
SubsetWidget.saveHidden();
|
||||
FavoriteRecipes.save();
|
||||
CollapsibleItems.saveStates();
|
||||
|
||||
if (world != null) {
|
||||
world.saveNBT();
|
||||
}
|
||||
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);
|
||||
return hash != Keyboard.CHAR_NONE && hash == NEIClientUtils.getKeyHash();
|
||||
return KeyManager.isHashDown(string);
|
||||
}
|
||||
|
||||
public static String getKeyName(String keyBind) {
|
||||
final int hash = getKeyBinding(keyBind);
|
||||
|
||||
if (hash == Keyboard.CHAR_NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return NEIClientUtils.getKeyName(hash);
|
||||
return KeyManager.getKeyName(keyBind);
|
||||
}
|
||||
|
||||
public static void bootNEI(World world) {
|
||||
public static String getKeyName(String keyBind, int meta, int mouseBind) {
|
||||
return KeyManager.getKeyName(keyBind, meta, mouseBind);
|
||||
}
|
||||
|
||||
public static String getKeyName(String keyBind, int meta) {
|
||||
return KeyManager.getKeyName(keyBind, meta);
|
||||
}
|
||||
|
||||
public static void bootNEI() {
|
||||
|
||||
if (!mainNEIConfigLoaded) {
|
||||
// main NEI config loading
|
||||
ItemInfo.load(world);
|
||||
ItemInfo.load();
|
||||
GuiInfo.load();
|
||||
RecipeInfo.load();
|
||||
HeldItemHandler.load();
|
||||
LayoutManager.load();
|
||||
NEIController.load();
|
||||
BookmarkContainerInfo.load();
|
||||
InformationHandler.load();
|
||||
mainNEIConfigLoaded = true;
|
||||
|
||||
new Thread("NEI Plugin Loader") {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Stream<Class<?>> stream = NEIClientConfig.getBooleanSetting("loadPluginsInParallel")
|
||||
? NEIClientConfig.pluginsList.parallelStream()
|
||||
: NEIClientConfig.pluginsList.stream();
|
||||
|
||||
NEIClientConfig.pluginsList.parallelStream().forEach(clazz -> {
|
||||
stream.forEach(clazz -> {
|
||||
try {
|
||||
IConfigureNEI config = (IConfigureNEI) clazz.getConstructor().newInstance();
|
||||
config.loadConfig();
|
||||
@@ -840,8 +1026,9 @@ public class NEIClientConfig {
|
||||
}
|
||||
});
|
||||
|
||||
KeyManager.requestOptionsReload();
|
||||
RecipeCatalysts.loadCatalystInfo();
|
||||
ItemPanels.bookmarkPanel.load();
|
||||
SubsetWidget.loadCustomSubsets();
|
||||
SubsetWidget.loadHidden();
|
||||
CollapsibleItems.load();
|
||||
ItemSorter.loadConfig();
|
||||
@@ -853,15 +1040,18 @@ 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());
|
||||
|
||||
ItemList.loadItems.restart();
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
ItemPanels.bookmarkPanel.load();
|
||||
SubsetWidget.loadHidden();
|
||||
ItemList.loadItems.restart();
|
||||
if (ItemList.loadFinished) {
|
||||
ItemList.refreshItems.restart();
|
||||
} else {
|
||||
ItemList.loadItems.restart();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isWorldSpecific(String setting) {
|
||||
@@ -882,6 +1072,16 @@ public class NEIClientConfig {
|
||||
return !enabledOverride || getBooleanSetting("inventory.hidden");
|
||||
}
|
||||
|
||||
public static boolean autocraftingEnabled() {
|
||||
if (!getBooleanSetting("inventory.autocrafting")) return false;
|
||||
if (hasSMPCounterpart) return permissableActions.contains("autocraft");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean favoritesEnabled() {
|
||||
return getBooleanSetting("inventory.favorites.enabled");
|
||||
}
|
||||
|
||||
public static boolean isBookmarkPanelHidden() {
|
||||
return !getBooleanSetting("inventory.bookmarks.enabled");
|
||||
}
|
||||
@@ -894,8 +1094,16 @@ public class NEIClientConfig {
|
||||
return getIntSetting("inventory.bookmarks.recipeTooltipsMode");
|
||||
}
|
||||
|
||||
public static boolean showRecipeMarker() {
|
||||
return getBooleanSetting("inventory.bookmarks.showRecipeMarker");
|
||||
public static boolean showRecipeTooltipInPanel() {
|
||||
return getBooleanSetting("inventory.favorites.showRecipeTooltipInPanel");
|
||||
}
|
||||
|
||||
public static boolean showRecipeTooltipInGui() {
|
||||
return getBooleanSetting("inventory.favorites.showRecipeTooltipInGui");
|
||||
}
|
||||
|
||||
public static int showRecipeMarkerMode() {
|
||||
return getIntSetting("inventory.bookmarks.showRecipeMarkerMode");
|
||||
}
|
||||
|
||||
public static boolean showItemQuantityWidget() {
|
||||
@@ -934,8 +1142,8 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.guirecipe.slotHighlightPresent");
|
||||
}
|
||||
|
||||
public static boolean areJEIStyleRecipeCatalystsVisible() {
|
||||
return getBooleanSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
|
||||
public static int getJEIStyleRecipeCatalysts() {
|
||||
return getIntSetting("inventory.guirecipe.jeiStyleRecipeCatalyst");
|
||||
}
|
||||
|
||||
public static boolean useCreativeTabStyle() {
|
||||
@@ -946,18 +1154,14 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.bookmarks.ignorePotionOverlap");
|
||||
}
|
||||
|
||||
public static int craftingChainDir() {
|
||||
return getIntSetting("inventory.bookmarks.craftingChainDir");
|
||||
public static boolean craftingTreeZoomWithoutModifier() {
|
||||
return getBooleanSetting("inventory.bookmarks.craftingTreeZoomWithoutModifier");
|
||||
}
|
||||
|
||||
public static boolean optimizeGuiOverlapComputation() {
|
||||
return getBooleanSetting("inventory.optimize_gui_overlap_computation");
|
||||
}
|
||||
|
||||
public static boolean useJEIStyledCycledIngredients() {
|
||||
return getBooleanSetting("inventory.guirecipe.jeiStyleCycledIngredients");
|
||||
}
|
||||
|
||||
public static boolean showCycledIngredientsTooltip() {
|
||||
return getBooleanSetting("inventory.guirecipe.cycledIngredientsTooltip");
|
||||
}
|
||||
@@ -1066,6 +1270,10 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.history.enabled");
|
||||
}
|
||||
|
||||
public static boolean showCraftablesPanelWidget() {
|
||||
return getBooleanSetting("inventory.craftables.enabled");
|
||||
}
|
||||
|
||||
public static int getGridRenderingCacheMode() {
|
||||
return OpenGlHelper.framebufferSupported ? getIntSetting("inventory.gridRenderingCacheMode") : 0;
|
||||
}
|
||||
@@ -1074,6 +1282,10 @@ public class NEIClientConfig {
|
||||
return getBooleanSetting("inventory.collapsibleItems.enabled");
|
||||
}
|
||||
|
||||
public static boolean enableItemUntranslator() {
|
||||
return getBooleanSetting("inventory.itemUntranslator");
|
||||
}
|
||||
|
||||
public static boolean getMagnetMode() {
|
||||
return enabledActions.contains("magnet");
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import static codechicken.nei.NEIClientConfig.hasSMPCounterPart;
|
||||
import static codechicken.nei.NEIClientConfig.invCreativeMode;
|
||||
import static codechicken.nei.NEIClientConfig.world;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
@@ -20,62 +20,89 @@ 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;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
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;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.inventory.InventoryRange;
|
||||
import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.lib.util.LangProxy;
|
||||
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;
|
||||
|
||||
public class NEIClientUtils extends NEIServerUtils {
|
||||
|
||||
public static class Alignment {
|
||||
|
||||
public final int x, y;
|
||||
|
||||
public static final Alignment TopLeft = new Alignment(-1, -1);
|
||||
public static final Alignment TopCenter = new Alignment(0, -1);
|
||||
public static final Alignment TopRight = new Alignment(1, -1);
|
||||
public static final Alignment CenterLeft = new Alignment(-1, 0);
|
||||
public static final Alignment Center = new Alignment(0, 0);
|
||||
public static final Alignment CenterRight = new Alignment(1, 0);
|
||||
public static final Alignment BottomLeft = new Alignment(-1, 1);
|
||||
public static final Alignment BottomCenter = new Alignment(0, 1);
|
||||
public static final Alignment BottomRight = new Alignment(1, 1);
|
||||
|
||||
public Alignment(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public float getX(float parentWidth, float childWidth) {
|
||||
final float x = (this.x + 1) / 2f;
|
||||
return parentWidth * x - childWidth * x;
|
||||
}
|
||||
|
||||
public float getY(float parentHeight, float childHeight) {
|
||||
final float y = (this.y + 1) / 2f;
|
||||
return parentHeight * y - childHeight * y;
|
||||
}
|
||||
}
|
||||
|
||||
public static LangProxy lang = new LangProxy("nei");
|
||||
|
||||
/** Formats a number with group separator and at most 2 fraction digits. */
|
||||
private static final Map<Locale, DecimalFormat> decimalFormatters = new HashMap<>();
|
||||
private static final Map<Integer, Integer> keyHashMap = new HashMap<>();
|
||||
private static final DecimalFormat CHANCE_FORMAT = new DecimalFormat("##0.##%");
|
||||
|
||||
public static final int ALT_HASH = 1 << 27;
|
||||
public static final int SHIFT_HASH = 1 << 26;
|
||||
public static final int CTRL_HASH = 1 << 25;
|
||||
|
||||
static {
|
||||
keyHashMap.put(Keyboard.KEY_LSHIFT, SHIFT_HASH);
|
||||
keyHashMap.put(Keyboard.KEY_RSHIFT, SHIFT_HASH);
|
||||
|
||||
keyHashMap.put(Keyboard.KEY_LCONTROL, CTRL_HASH);
|
||||
keyHashMap.put(Keyboard.KEY_LCONTROL, CTRL_HASH);
|
||||
|
||||
keyHashMap.put(Keyboard.KEY_LMETA, CTRL_HASH);
|
||||
keyHashMap.put(Keyboard.KEY_RMETA, CTRL_HASH);
|
||||
|
||||
keyHashMap.put(Keyboard.KEY_LMENU, ALT_HASH);
|
||||
keyHashMap.put(Keyboard.KEY_LMENU, ALT_HASH);
|
||||
}
|
||||
public static final int ALT_HASH = NEIKeyboardUtils.ALT_HASH;
|
||||
public static final int SHIFT_HASH = NEIKeyboardUtils.SHIFT_HASH;
|
||||
public static final int CTRL_HASH = NEIKeyboardUtils.CTRL_HASH;
|
||||
|
||||
public static Minecraft mc() {
|
||||
return Minecraft.getMinecraft();
|
||||
@@ -103,16 +130,26 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static String formatNumbers(BigInteger aNumber) {
|
||||
public static String formatNumber(Number aNumber) {
|
||||
|
||||
if (NEIModContainer.isGTNHLibLoaded()) {
|
||||
return NumberFormatUtil.formatNumber(aNumber);
|
||||
}
|
||||
|
||||
return getDecimalFormat().format(aNumber);
|
||||
}
|
||||
|
||||
public static String formatNumbers(long aNumber) {
|
||||
return getDecimalFormat().format(aNumber);
|
||||
public static String formatFluid(Number aNumber) {
|
||||
|
||||
if (NEIModContainer.isGTNHLibLoaded()) {
|
||||
return NumberFormatUtil.formatFluid(aNumber);
|
||||
}
|
||||
|
||||
return getDecimalFormat().format(aNumber) + " L";
|
||||
}
|
||||
|
||||
public static String formatNumbers(double aNumber) {
|
||||
return getDecimalFormat().format(aNumber);
|
||||
public static String formatChance(float chance) {
|
||||
return CHANCE_FORMAT.format(chance);
|
||||
}
|
||||
|
||||
public static void deleteHeldItem() {
|
||||
@@ -175,7 +212,7 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
public static void cheatItem(ItemStack stack, int button, int mode) {
|
||||
if (!canCheatItem(stack)) return;
|
||||
|
||||
if (mode == -1 && button == 0 && shiftKey() && NEIClientConfig.hasSMPCounterPart()) {
|
||||
if (mode == -1 && button == 0 && controlKey() && NEIClientConfig.hasSMPCounterPart()) {
|
||||
for (IInfiniteItemHandler handler : ItemInfo.infiniteHandlers) {
|
||||
if (!handler.canHandleItem(stack)) continue;
|
||||
|
||||
@@ -260,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);
|
||||
}
|
||||
@@ -347,6 +461,10 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
else sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName());
|
||||
}
|
||||
|
||||
public static void sendChatItemLink(NBTTagCompound payload) {
|
||||
if (hasSMPCounterPart()) NEICPH.sendChatLink(payload);
|
||||
}
|
||||
|
||||
public static void toggleMagnetMode() {
|
||||
if (hasSMPCounterPart()) NEICPH.sendToggleMagnetMode();
|
||||
}
|
||||
@@ -386,18 +504,11 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
if (textWidth > containerWidth) {
|
||||
int dots = fontRenderer.getStringWidth("...");
|
||||
|
||||
if (containerWidth > dots) {
|
||||
textWidth += dots;
|
||||
|
||||
while (textWidth > containerWidth) {
|
||||
textWidth -= fontRenderer.getCharWidth(text.charAt(text.length() - 1));
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
|
||||
return text + "...";
|
||||
if (containerWidth <= dots) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
return "...";
|
||||
return fontRenderer.trimStringToWidth(text, containerWidth - dots) + "...";
|
||||
}
|
||||
|
||||
return text;
|
||||
@@ -425,78 +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 (!keyHashMap.containsKey(keycode)) {
|
||||
return getMetaHash() + keycode;
|
||||
}
|
||||
}
|
||||
|
||||
return Keyboard.CHAR_NONE;
|
||||
}
|
||||
|
||||
public static String getKeyHashName(int keyBind) {
|
||||
StringJoiner keyText = new StringJoiner(" + ");
|
||||
|
||||
if ((keyBind & CTRL_HASH) != 0) {
|
||||
keyText.add(translate(Minecraft.isRunningOnMac ? "key.ctrl.mac" : "key.ctrl"));
|
||||
}
|
||||
|
||||
if ((keyBind & SHIFT_HASH) != 0) {
|
||||
keyText.add(translate("key.shift"));
|
||||
}
|
||||
|
||||
if ((keyBind & ALT_HASH) != 0) {
|
||||
keyText.add(translate("key.alt"));
|
||||
}
|
||||
|
||||
return keyText.toString();
|
||||
}
|
||||
|
||||
public static String getKeyName(int keyBind) {
|
||||
keyBind = keyHashMap.getOrDefault(keyBind, keyBind);
|
||||
StringJoiner keyText = new StringJoiner(" + ");
|
||||
String hashText = getKeyHashName(keyBind);
|
||||
int keyID = unHashKey(keyBind);
|
||||
|
||||
if (!hashText.isEmpty()) {
|
||||
keyText.add(hashText);
|
||||
}
|
||||
|
||||
if (keyID != Keyboard.CHAR_NONE || hashText.isEmpty()) {
|
||||
keyText.add(Keyboard.getKeyName(keyID));
|
||||
}
|
||||
|
||||
return keyText.toString();
|
||||
}
|
||||
|
||||
public static int unHashKey(int keyBind) {
|
||||
return keyBind & ~(CTRL_HASH | SHIFT_HASH | ALT_HASH);
|
||||
public static String getKeyName(int keyBind, int mouseBind) {
|
||||
return KeyManager.getKeyName(keyBind, mouseBind);
|
||||
}
|
||||
|
||||
public static void playClickSound() {
|
||||
@@ -522,4 +563,80 @@ public class NEIClientUtils extends NEIServerUtils {
|
||||
reportErrorBuffered(e, buffer, "null");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
|
||||
GL11.glColor4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertex(left, top + height, 0.0D);
|
||||
tessellator.addVertex(left + width, top + height, 0.0D);
|
||||
tessellator.addVertex(left + width, top, 0.0D);
|
||||
tessellator.addVertex(left, top, 0.0D);
|
||||
tessellator.draw();
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
if (is2DTexture) GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
if (!isBlend) GL11.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
public static void drawNEIOverlayText(String text, Rectangle4i rect, float scale, int color, boolean shadow,
|
||||
Alignment alignment) {
|
||||
final float screenScale = mc().currentScreen.width * 1f / mc().displayWidth;
|
||||
final double smallTextScale = Math
|
||||
.max(screenScale, (Math.max(scale, 1f) * (GuiDraw.fontRenderer.getUnicodeFlag() ? 3F / 4F : 1F / 2F)));
|
||||
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
final int width = GuiDraw.fontRenderer.getStringWidth(text);
|
||||
final float partW = rect.w / 2f;
|
||||
final float partH = rect.h / 2f;
|
||||
final double offsetX = Math
|
||||
.ceil(rect.x + partW + partW * alignment.x - (width / 2f * (alignment.x + 1)) * smallTextScale);
|
||||
final double offsetY = Math.ceil(
|
||||
rect.y + partH
|
||||
+ partH * alignment.y
|
||||
- (GuiDraw.fontRenderer.FONT_HEIGHT / 2f * (alignment.y + 1)) * smallTextScale);
|
||||
|
||||
GL11.glTranslated(offsetX, offsetY, 0);
|
||||
GL11.glScaled(smallTextScale, smallTextScale, 1);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
public static void drawNEIOverlayText(String text, int x, int y) {
|
||||
drawNEIOverlayText(text, new Rectangle4i(x, y, 16, 16), 0.5f, 0xFDD835, false, Alignment.TopLeft);
|
||||
}
|
||||
|
||||
public static void gl2DRenderContext(Runnable callback) {
|
||||
boolean isLighting = GL11.glGetBoolean(GL11.GL_LIGHTING);
|
||||
boolean isDepthTest = GL11.glGetBoolean(GL11.GL_DEPTH_TEST);
|
||||
boolean isAlphaTest = GL11.glGetBoolean(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
|
||||
callback.run();
|
||||
|
||||
if (isLighting) GL11.glEnable(GL11.GL_LIGHTING);
|
||||
if (isDepthTest) GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
if (!isAlphaTest) GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,35 @@
|
||||
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;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.IInfiniteItemHandler;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.commands.CommandBookmarkAdd;
|
||||
import codechicken.nei.commands.CommandRecipeId;
|
||||
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;
|
||||
|
||||
public class NEIController implements IContainerSlotClickHandler, IContainerInputHandler {
|
||||
|
||||
@@ -39,6 +48,8 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
public static void load() {
|
||||
GuiContainerManager.addSlotClickHandler(instance);
|
||||
GuiContainerManager.addInputHandler(instance);
|
||||
|
||||
if (CommonUtils.isClient()) registerClientCommands();
|
||||
}
|
||||
|
||||
public static void load(GuiContainer gui) {
|
||||
@@ -56,32 +67,48 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
return gui.field_147007_t && gui.field_147008_s.size() > 1;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static void registerClientCommands() {
|
||||
ClientCommandHandler.instance.registerCommand(new CommandBookmarkAdd());
|
||||
ClientCommandHandler.instance.registerCommand(new CommandRecipeId());
|
||||
ClientCommandHandler.instance.registerCommand(new CommandUntranslator());
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -122,7 +149,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == 1 && slot instanceof SlotCrafting) // right click
|
||||
if (button == 1 && modifier != 2 && slot instanceof SlotCrafting) // right click
|
||||
{
|
||||
for (int i1 = 0; i1 < 64; i1++) // click this slot 64 times
|
||||
manager.handleSlotClick(slot.slotNumber, button, 0);
|
||||
@@ -134,26 +161,40 @@ 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 (slotIndex >= 0 && NEIClientUtils.shiftKey()
|
||||
&& NEIClientUtils.getHeldItem() != null
|
||||
&& !slot.getHasStack()) {
|
||||
ItemStack held = NEIClientUtils.getHeldItem();
|
||||
manager.handleSlotClick(slot.slotNumber, button, 0);
|
||||
if (slot.isItemValid(held) && !ItemInfo.fastTransferExemptions.contains(slot.getClass()))
|
||||
fastTransferManager.performMassTransfer(gui, pickedUpFromSlot, slotIndex, held);
|
||||
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();
|
||||
slot = gui.getSlotAtPosition(mouse.x, mouse.y);
|
||||
|
||||
return true;
|
||||
}
|
||||
if (slot != null) {
|
||||
slotIndex = slot.slotNumber;
|
||||
}
|
||||
}
|
||||
|
||||
if (slotIndex == -999 && NEIClientUtils.shiftKey() && button == 0) {
|
||||
fastTransferManager.throwAll(gui, pickedUpFromSlot);
|
||||
return true;
|
||||
if (slotIndex >= 0 && NEIClientUtils.getHeldItem() != null && !slot.getHasStack()) {
|
||||
final ItemStack held = NEIClientUtils.getHeldItem();
|
||||
manager.handleSlotClick(slot.slotNumber, button, 0);
|
||||
|
||||
if (slot.isItemValid(held) && !ItemInfo.fastTransferExemptions.contains(slot.getClass())) {
|
||||
fastTransferManager.performMassTransfer(gui, pickedUpFromSlot, slotIndex, held);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (slotIndex == -999 && button == 0) {
|
||||
fastTransferManager.throwAll(gui, pickedUpFromSlot);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -220,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);
|
||||
|
||||
@@ -7,8 +7,7 @@ import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
@@ -18,6 +17,7 @@ import codechicken.core.launch.CodeChickenCorePlugin;
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
import codechicken.nei.asm.NEICorePlugin;
|
||||
import codechicken.nei.config.IMCHandler;
|
||||
import codechicken.nei.guihook.HideousLinkedList;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import cpw.mods.fml.client.FMLFileResourcePack;
|
||||
import cpw.mods.fml.client.FMLFolderResourcePack;
|
||||
@@ -25,25 +25,30 @@ import cpw.mods.fml.common.DummyModContainer;
|
||||
import cpw.mods.fml.common.LoadController;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModMetadata;
|
||||
import cpw.mods.fml.common.discovery.ASMDataTable;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent;
|
||||
import cpw.mods.fml.common.versioning.ArtifactVersion;
|
||||
import cpw.mods.fml.common.versioning.VersionParser;
|
||||
import cpw.mods.fml.common.versioning.VersionRange;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class NEIModContainer extends DummyModContainer {
|
||||
|
||||
public static LinkedList<IConfigureNEI> plugins = new LinkedList<>();
|
||||
public static final LinkedList<IConfigureNEI> plugins = new HideousLinkedList<>(new CopyOnWriteArrayList<>());
|
||||
|
||||
private static boolean gregTech5Loaded;
|
||||
private static boolean gtnhLibLoaded;
|
||||
private static boolean cubicChunksLoaded;
|
||||
|
||||
private static ASMDataTable asmDataTable;
|
||||
|
||||
public NEIModContainer() {
|
||||
super(getModMetadata());
|
||||
loadMetadata();
|
||||
}
|
||||
|
||||
private static ModMetadata getModMetadata() {
|
||||
@@ -51,7 +56,7 @@ public class NEIModContainer extends DummyModContainer {
|
||||
modMetadata.name = "NotEnoughItems";
|
||||
modMetadata.modId = "NotEnoughItems";
|
||||
modMetadata.version = Tags.VERSION;
|
||||
modMetadata.authorList = Arrays.asList("ChickenBones", "mitchej123");
|
||||
modMetadata.authorList = Arrays.asList("ChickenBones", "mitchej123", "SLPrime");
|
||||
modMetadata.url = "https://github.com/GTNewHorizons/NotEnoughItems";
|
||||
modMetadata.description = "Recipe Viewer, Inventory Manager, Item Spawner, Cheats and more; GTNH Version includes many enhancements.";
|
||||
return modMetadata;
|
||||
@@ -65,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<>();
|
||||
@@ -76,36 +85,12 @@ 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.6.0,)"));
|
||||
deps.add(VersionParser.parseVersionReference("gtnhlib@[0.9.6,)"));
|
||||
return deps;
|
||||
}
|
||||
|
||||
private String description;
|
||||
|
||||
private void loadMetadata() {
|
||||
description = super.getMetadata().description.replace("Supporters:", EnumChatFormatting.AQUA + "Supporters:");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModMetadata getMetadata() {
|
||||
StringBuilder s_plugins = new StringBuilder();
|
||||
if (plugins.size() == 0) {
|
||||
s_plugins.append(EnumChatFormatting.RED).append("No installed plugins.");
|
||||
} else {
|
||||
s_plugins.append(EnumChatFormatting.GREEN).append("Installed plugins: ");
|
||||
for (int i = 0; i < plugins.size(); i++) {
|
||||
if (i > 0) s_plugins.append(", ");
|
||||
IConfigureNEI plugin = plugins.get(i);
|
||||
s_plugins.append(plugin.getName()).append(" ").append(plugin.getVersion());
|
||||
}
|
||||
s_plugins.append(".");
|
||||
}
|
||||
|
||||
ModMetadata meta = super.getMetadata();
|
||||
meta.description = description.replace("<plugins>", s_plugins.toString());
|
||||
return meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBus(EventBus bus, LoadController controller) {
|
||||
bus.register(this);
|
||||
@@ -116,13 +101,18 @@ 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();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void init(FMLInitializationEvent event) {
|
||||
if (CommonUtils.isClient()) ClientHandler.load();
|
||||
|
||||
if (CommonUtils.isClient()) {
|
||||
ClientHandler.load();
|
||||
IMCForNEI.IMCSender();
|
||||
}
|
||||
ServerHandler.load();
|
||||
}
|
||||
|
||||
@@ -139,9 +129,15 @@ public class NEIModContainer extends DummyModContainer {
|
||||
GuiRecipeTab.loadHandlerInfo();
|
||||
ClientHandler.loadPluginsList();
|
||||
ClientHandler.loadHandlerOrdering();
|
||||
asmDataTable = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerAboutToStart(FMLServerAboutToStartEvent event) {
|
||||
NEIServerConfig.resetFirstLoad();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleIMCMessages(FMLInterModComms.IMCEvent event) {
|
||||
IMCHandler.processIMC(event.getMessages());
|
||||
@@ -161,4 +157,8 @@ public class NEIModContainer extends DummyModContainer {
|
||||
public Class<?> getCustomResourcePackClass() {
|
||||
return getSource().isDirectory() ? FMLFolderResourcePack.class : FMLFileResourcePack.class;
|
||||
}
|
||||
|
||||
public static ASMDataTable getAsmDataTable() {
|
||||
return asmDataTable;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -88,9 +89,17 @@ public class NEISPH implements IServerPacketHandler {
|
||||
case C2S.SET_DUMMY_SLOT:
|
||||
handleDummySlotSet(sender, packet);
|
||||
break;
|
||||
case C2S.SEND_CHAT_ITEM_LINK:
|
||||
handleSendChatItemLink(sender, packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
|
||||
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
|
||||
NEIServerUtils.sendChatItemLink(sender, payloadTag);
|
||||
}
|
||||
|
||||
private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) {
|
||||
int slotNumber = packet.readShort();
|
||||
ItemStack stack = packet.readItemStack(true);
|
||||
|
||||
@@ -18,9 +18,10 @@ import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.JsonToNBT;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.permission.PermissionLevel;
|
||||
import net.minecraftforge.permission.PermissionManager;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -35,24 +36,38 @@ import codechicken.lib.packet.PacketCustom;
|
||||
|
||||
public class NEIServerConfig {
|
||||
|
||||
private static MinecraftServer server;
|
||||
|
||||
private static boolean firstLoad;
|
||||
public static Logger logger = LogManager.getLogger("NotEnoughItems");
|
||||
public static File saveDir;
|
||||
public static ConfigFile serverConfig;
|
||||
public static Map<Integer, NBTTagCompound> dimTags = new HashMap<>();
|
||||
public static HashMap<String, PlayerSave> playerSaves = new HashMap<>();
|
||||
public static ItemStackMap<Set<String>> bannedItems = new ItemStackMap<>();
|
||||
private static boolean doesPermAPIExist = false;
|
||||
|
||||
public static void resetFirstLoad() {
|
||||
firstLoad = true;
|
||||
}
|
||||
|
||||
public static void load(World world) {
|
||||
if (MinecraftServer.getServer() != server) {
|
||||
if (firstLoad) {
|
||||
firstLoad = false;
|
||||
logger.debug("Loading NEI Server");
|
||||
server = MinecraftServer.getServer();
|
||||
saveDir = new File(DimensionManager.getCurrentSaveRootDirectory(), "NEI");
|
||||
|
||||
dimTags.clear();
|
||||
loadConfig();
|
||||
loadBannedItems();
|
||||
|
||||
try {
|
||||
Class.forName("net.minecraftforge.permission.PermissionManager");
|
||||
doesPermAPIExist = true;
|
||||
|
||||
NEIActions.baseActions().forEach(
|
||||
action -> {
|
||||
PermissionManager.registerPermission("nei.permissions." + action, PermissionLevel.OP);
|
||||
});
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
}
|
||||
loadWorld(world);
|
||||
}
|
||||
@@ -93,6 +108,7 @@ public class NEIServerConfig {
|
||||
setDefaultFeature("save-state");
|
||||
setDefaultFeature("item");
|
||||
setDefaultFeature("delete");
|
||||
setDefaultFeature("autocraft", "ALL");
|
||||
setDefaultFeature("notify-item", "CONSOLE, OP");
|
||||
}
|
||||
|
||||
@@ -121,7 +137,12 @@ public class NEIServerConfig {
|
||||
}
|
||||
|
||||
public static boolean canPlayerPerformAction(String playername, String name) {
|
||||
return isPlayerInList(playername, getPlayerList("permissions." + NEIActions.base(name)), true);
|
||||
String node = "permissions." + NEIActions.base(name);
|
||||
boolean hasPerm = doesPermAPIExist
|
||||
? PermissionManager.checkPermission(ServerUtils.getPlayer(playername), "nei." + node)
|
||||
: false;
|
||||
|
||||
return hasPerm || isPlayerInList(playername, getPlayerList(node), true);
|
||||
}
|
||||
|
||||
public static boolean isPlayerInList(String playername, Set<String> list, boolean allowCards) {
|
||||
|
||||
@@ -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;
|
||||
@@ -17,6 +18,7 @@ import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.event.ClickEvent;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -27,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;
|
||||
@@ -43,12 +46,30 @@ 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;
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper;
|
||||
|
||||
public class NEIServerUtils {
|
||||
|
||||
private static class GTMetaBaseItemHolder {
|
||||
|
||||
private static final Class<?> gtMetaBaseItem = loadMetaBaseItem();
|
||||
|
||||
private static Class<?> loadMetaBaseItem() {
|
||||
try {
|
||||
final ClassLoader loader = NEIServerUtils.class.getClassLoader();
|
||||
return ReflectionHelper
|
||||
.getClass(loader, "gregtech.api.items.MetaBaseItem", "gregtech.api.items.GT_MetaBase_Item");
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRaining(World world) {
|
||||
return world.getWorldInfo().isRaining();
|
||||
}
|
||||
@@ -56,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)
|
||||
@@ -69,6 +90,48 @@ public class NEIServerUtils {
|
||||
player.extinguish();
|
||||
}
|
||||
|
||||
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) {
|
||||
return world.getWorldInfo().getWorldTime();
|
||||
}
|
||||
@@ -146,7 +209,7 @@ public class NEIServerUtils {
|
||||
|
||||
/**
|
||||
* NBT-friendly version of {@link #areStacksSameType(ItemStack, ItemStack)}
|
||||
*
|
||||
*
|
||||
* @param stack1 The {@link ItemStack} being compared.
|
||||
* @param stack2 The {@link ItemStack} to compare to.
|
||||
* @return whether the two items are the same in terms of itemID, damage and NBT.
|
||||
@@ -154,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());
|
||||
}
|
||||
|
||||
@@ -177,19 +240,33 @@ public class NEIServerUtils {
|
||||
|
||||
/**
|
||||
* NBT-friendly version of {@link #areStacksSameTypeCrafting(ItemStack, ItemStack)}
|
||||
*
|
||||
*
|
||||
* @param stack1 The {@link ItemStack} being compared.
|
||||
* @param stack2 The {@link ItemStack} to compare to.
|
||||
* @return whether the two items are the same from the perspective of a crafting inventory.
|
||||
*/
|
||||
public static boolean areStacksSameTypeCraftingWithNBT(ItemStack stack1, ItemStack stack2) {
|
||||
return stack1 != null && stack2 != null
|
||||
&& stack1.getItem() == stack2.getItem()
|
||||
&& (stack1.getItemDamage() == stack2.getItemDamage()
|
||||
|| stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE
|
||||
|| stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE
|
||||
|| stack1.getItem().isDamageable())
|
||||
&& NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound());
|
||||
|
||||
if (NEIServerUtils.areStacksSameTypeCrafting(stack2, stack1)) {
|
||||
if (NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound())) return true;
|
||||
if (isItemTool(stack1)
|
||||
&& (stack1.stackTagCompound == null || stack1.stackTagCompound.hasKey("GT.ToolStats"))
|
||||
&& (stack2.stackTagCompound == null || stack2.stackTagCompound.hasKey("GT.ToolStats")))
|
||||
return true;
|
||||
return stack1.getMaxStackSize() == 1 && stack2.getMaxStackSize() == 1
|
||||
&& (stack1.stackTagCompound == null ^ stack2.stackTagCompound == null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static boolean isItemTool(ItemStack stack) {
|
||||
return GTMetaBaseItemHolder.gtMetaBaseItem != null
|
||||
&& GTMetaBaseItemHolder.gtMetaBaseItem.isInstance(stack.getItem());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,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) {
|
||||
|
||||
@@ -23,6 +23,7 @@ public final class PacketIDs {
|
||||
public static final int SET_CREATIVE_PLUS_MODE = 23;
|
||||
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 class S2C {
|
||||
@@ -36,5 +37,6 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemPanel.ItemPanelSlot;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
import codechicken.nei.api.GuiInfo;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.GuiUsageRecipe;
|
||||
|
||||
public abstract class PanelWidget extends Widget implements IContainerTooltipHandler {
|
||||
public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ? extends ItemsGrid.MouseContext>>
|
||||
extends Widget {
|
||||
|
||||
protected static final int PADDING = 2;
|
||||
|
||||
@@ -27,20 +27,20 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
public Button pageNext;
|
||||
public Label pageLabel;
|
||||
|
||||
protected ItemsGrid grid;
|
||||
protected T grid;
|
||||
|
||||
public ArrayList<ItemStack> getItems() {
|
||||
return grid.getItems();
|
||||
return this.grid.getItems();
|
||||
}
|
||||
|
||||
public ItemsGrid getGrid() {
|
||||
return grid;
|
||||
public T getGrid() {
|
||||
return this.grid;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
pageLabel = new Label("0/0", true);
|
||||
|
||||
pagePrev = new Button("Prev") {
|
||||
pagePrev = new Button("<") {
|
||||
|
||||
public boolean onButtonPress(boolean rightclick) {
|
||||
|
||||
@@ -54,11 +54,12 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderLabel() {
|
||||
return "<";
|
||||
public void draw(int mousex, int mousey) {
|
||||
this.state = grid.getNumPages() <= 1 ? 2 : 0;
|
||||
super.draw(mousex, mousey);
|
||||
}
|
||||
};
|
||||
pageNext = new Button("Next") {
|
||||
pageNext = new Button(">") {
|
||||
|
||||
public boolean onButtonPress(boolean rightclick) {
|
||||
|
||||
@@ -72,61 +73,34 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRenderLabel() {
|
||||
return ">";
|
||||
public void draw(int mousex, int mousey) {
|
||||
this.state = grid.getNumPages() <= 1 ? 2 : 0;
|
||||
super.draw(mousex, mousey);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract String getLabelText();
|
||||
|
||||
protected abstract String getPositioningSettingName();
|
||||
public abstract Rectangle4i calculateBounds(GuiContainer gui);
|
||||
|
||||
public abstract int getMarginLeft(GuiContainer gui);
|
||||
|
||||
public abstract int getMarginTop(GuiContainer gui);
|
||||
|
||||
public abstract int getWidth(GuiContainer gui);
|
||||
|
||||
public abstract int getHeight(GuiContainer gui);
|
||||
protected static int snapDownToMultiple(int value, int multiple) {
|
||||
return (value / multiple) * multiple;
|
||||
}
|
||||
|
||||
public void resize(GuiContainer gui) {
|
||||
final Rectangle4i margin = new Rectangle4i(
|
||||
getMarginLeft(gui),
|
||||
getMarginTop(gui),
|
||||
getWidth(gui),
|
||||
getHeight(gui));
|
||||
final Rectangle4i bounds = calculateBounds(gui);
|
||||
|
||||
final int minWidth = 5 * ItemsGrid.SLOT_SIZE;
|
||||
final int minHeight = 9 * ItemsGrid.SLOT_SIZE;
|
||||
|
||||
final String settingName = getPositioningSettingName();
|
||||
int paddingLeft = (int) Math
|
||||
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".left").getIntValue() / 100000.0);
|
||||
int paddingTop = (int) Math
|
||||
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".top").getIntValue() / 100000.0);
|
||||
int paddingRight = (int) Math
|
||||
.ceil(margin.w * NEIClientConfig.getSetting(settingName + ".right").getIntValue() / 100000.0);
|
||||
int paddingBottom = (int) Math
|
||||
.ceil(margin.h * NEIClientConfig.getSetting(settingName + ".bottom").getIntValue() / 100000.0);
|
||||
|
||||
int deltaHeight = Math.min(0, margin.h - paddingTop - paddingBottom - minHeight) / 2;
|
||||
|
||||
paddingLeft = Math.min(paddingLeft, Math.max(0, margin.w - paddingRight - minWidth));
|
||||
paddingRight = Math.min(paddingRight, Math.max(0, margin.w - paddingLeft - minWidth));
|
||||
paddingTop = Math.min(margin.h - minHeight, Math.max(0, paddingTop + deltaHeight));
|
||||
paddingBottom = Math.min(margin.h - paddingTop - minHeight, Math.max(0, paddingBottom - deltaHeight));
|
||||
|
||||
x = margin.x + paddingLeft;
|
||||
y = margin.y + paddingTop;
|
||||
w = margin.w - paddingLeft - paddingRight;
|
||||
h = margin.h - paddingTop - paddingBottom;
|
||||
x = bounds.x;
|
||||
y = bounds.y;
|
||||
w = bounds.w;
|
||||
h = bounds.h;
|
||||
|
||||
final int header = resizeHeader(gui);
|
||||
final int footer = resizeFooter(gui);
|
||||
|
||||
grid.setGridSize(x, y + header, w, h - header - footer);
|
||||
grid.refresh(gui);
|
||||
this.grid.setGridSize(x, y + header, w, h - header - footer);
|
||||
this.grid.refresh(gui);
|
||||
}
|
||||
|
||||
protected int resizeHeader(GuiContainer gui) {
|
||||
@@ -171,22 +145,21 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
protected abstract int resizeFooter(GuiContainer gui);
|
||||
|
||||
public void setVisible() {
|
||||
if (grid.getPerPage() > 0 && !grid.isEmpty()) {
|
||||
if (this.grid.getPerPage() > 0 && !this.grid.isEmpty()) {
|
||||
LayoutManager.addWidget(pagePrev);
|
||||
LayoutManager.addWidget(pageNext);
|
||||
LayoutManager.addWidget(pageLabel);
|
||||
grid.setVisible();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
grid.update();
|
||||
this.grid.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
grid.draw(mousex, mousey);
|
||||
this.grid.draw(mousex, mousey);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,37 +173,33 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
|
||||
if (mouseDownSlot >= grid.realItems.size()) {
|
||||
if (this.grid.getSlotBySlotIndex(mouseDownSlot) == null) {
|
||||
mouseDownSlot = -1;
|
||||
}
|
||||
|
||||
if (mouseDownSlot >= 0 && draggedStack == null
|
||||
&& NEIClientUtils.getHeldItem() == null
|
||||
&& NEIClientConfig.hasSMPCounterPart()) {
|
||||
ItemPanelSlot mouseOverSlot = getSlotMouseOver(mousex, mousey);
|
||||
ItemsGridSlot mouseOverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) {
|
||||
draggedStack = getDraggedStackWithQuantity(mouseDownSlot);
|
||||
draggedStack = getDraggedStackWithQuantity(this.grid.getSlotBySlotIndex(mouseDownSlot).getItemStack());
|
||||
mouseDownSlot = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ItemStack getDraggedStackWithQuantity(int mouseDownSlot);
|
||||
protected abstract ItemStack getDraggedStackWithQuantity(ItemStack itemStack);
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mousex, int mousey, int button) {
|
||||
if (handleClickExt(mousex, mousey, button)) return true;
|
||||
|
||||
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
if (hoverSlot != null) {
|
||||
|
||||
if (button == 2) {
|
||||
|
||||
if (hoverSlot.item != null) {
|
||||
draggedStack = getDraggedStackWithQuantity(hoverSlot.slotIndex);
|
||||
}
|
||||
|
||||
draggedStack = getDraggedStackWithQuantity(hoverSlot.getItemStack());
|
||||
} else {
|
||||
mouseDownSlot = hoverSlot.slotIndex;
|
||||
}
|
||||
@@ -245,7 +214,7 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
public boolean handleClickExt(int mouseX, int mouseY, int button) {
|
||||
|
||||
if (ItemPanels.itemPanel.draggedStack != null && ItemPanels.bookmarkPanel.contains(mouseX, mouseY)) {
|
||||
ItemPanels.bookmarkPanel.addOrRemoveItem(ItemPanels.itemPanel.draggedStack, null, null, false, true);
|
||||
ItemPanels.bookmarkPanel.addItem(ItemPanels.itemPanel.draggedStack);
|
||||
ItemPanels.itemPanel.draggedStack = null;
|
||||
return true;
|
||||
}
|
||||
@@ -311,25 +280,17 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
|
||||
@Override
|
||||
public void mouseUp(int mousex, int mousey, int button) {
|
||||
ItemPanelSlot hoverSlot = getSlotMouseOver(mousex, mousey);
|
||||
final ItemsGridSlot hoverSlot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
|
||||
if (hoverSlot != null && hoverSlot.slotIndex == mouseDownSlot && draggedStack == null) {
|
||||
ItemStack item = hoverSlot.item.copy();
|
||||
|
||||
if (NEIController.manager.window instanceof GuiRecipe || NEIClientUtils.shiftKey()
|
||||
|| !NEIClientConfig.canCheatItem(item)) {
|
||||
|
||||
if (button == 0) {
|
||||
GuiCraftingRecipe.openRecipeGui("item", item);
|
||||
} else if (button == 1) {
|
||||
GuiUsageRecipe.openRecipeGui("item", item);
|
||||
}
|
||||
|
||||
mouseDownSlot = -1;
|
||||
return;
|
||||
|| !NEIClientConfig.canCheatItem(hoverSlot.getItemStack())) {
|
||||
ShortcutInputHandler.handleMouseClick(hoverSlot.getItemStack());
|
||||
} else {
|
||||
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.getItemStack()), button, -1);
|
||||
}
|
||||
|
||||
NEIClientUtils.cheatItem(getDraggedStackWithQuantity(hoverSlot.slotIndex), button, -1);
|
||||
}
|
||||
|
||||
mouseDownSlot = -1;
|
||||
@@ -339,20 +300,22 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
public boolean onMouseWheel(int i, int mousex, int mousey) {
|
||||
if (!contains(mousex, mousey)) return false;
|
||||
|
||||
grid.shiftPage(-i);
|
||||
this.grid.shiftPage(-i);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyID, char keyChar) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
if (!contains(mouse.x, mouse.y)) return false;
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.next")) {
|
||||
grid.shiftPage(1);
|
||||
if (KeyManager.isKeyDown("gui.next")) {
|
||||
this.grid.shiftPage(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.prev")) {
|
||||
grid.shiftPage(-1);
|
||||
if (KeyManager.isKeyDown("gui.prev")) {
|
||||
this.grid.shiftPage(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -361,41 +324,28 @@ public abstract class PanelWidget extends Widget implements IContainerTooltipHan
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mousex, int mousey) {
|
||||
ItemPanelSlot slot = getSlotMouseOver(mousex, mousey);
|
||||
return slot == null ? null : slot.item;
|
||||
final ItemsGridSlot slot = this.grid.getSlotMouseOver(mousex, mousey);
|
||||
return slot == null ? null : slot.getItemStack();
|
||||
}
|
||||
|
||||
public ItemPanelSlot getSlotMouseOver(int mousex, int mousey) {
|
||||
return grid.getSlotMouseOver(mousex, mousey);
|
||||
public ItemStack getStackMouseOverWithQuantity(int mousex, int mousey) {
|
||||
final ItemStack hoverSlot = getStackMouseOver(mousex, mousey);
|
||||
return hoverSlot != null ? getDraggedStackWithQuantity(hoverSlot) : null;
|
||||
}
|
||||
|
||||
abstract ItemsGridSlot getSlotMouseOver(int mousex, int mousey);
|
||||
|
||||
public int getPage() {
|
||||
return grid.getPage();
|
||||
return this.grid.getPage();
|
||||
}
|
||||
|
||||
public int getNumPages() {
|
||||
return grid.getNumPages();
|
||||
return this.grid.getNumPages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(int px, int py) {
|
||||
return grid.contains(px, py);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(GuiContainer gui, int mousex, int mousey, List<String> currenttip) {
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemDisplayName(GuiContainer gui, ItemStack itemstack, List<String> currenttip) {
|
||||
return currenttip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(GuiContainer gui, ItemStack itemstack, int mousex, int mousey,
|
||||
List<String> currenttip) {
|
||||
return currenttip;
|
||||
return this.grid.contains(px, py);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,12 +24,12 @@ public class PopupInputHandler implements IContainerInputHandler {
|
||||
@Override
|
||||
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.enchant") && canPerformAction("enchant")) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2,31 +2,54 @@ package codechicken.nei;
|
||||
|
||||
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 codechicken.nei.ItemList.AllMultiItemFilter;
|
||||
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);
|
||||
@@ -81,10 +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.asList(items), 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() {
|
||||
@@ -92,40 +163,42 @@ public class PositionedStack {
|
||||
}
|
||||
|
||||
public List<ItemStack> getFilteredPermutations(ItemFilter additionalFilter) {
|
||||
List<ItemStack> items = Arrays.asList(this.items).stream().filter(getItemFilter(additionalFilter)::matches)
|
||||
.collect(Collectors.toList());
|
||||
List<ItemStack> items = new ArrayList<>(Arrays.asList(this.items));
|
||||
|
||||
if (items.isEmpty()) {
|
||||
items.addAll(
|
||||
Arrays.asList(this.items).stream().filter(item -> !ItemInfo.isHidden(item))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
if (items.isEmpty()) {
|
||||
items.addAll(Arrays.asList(this.items));
|
||||
}
|
||||
items = filteringPermutations(items, item -> !ItemInfo.isHidden(item));
|
||||
items = filteringPermutations(items, PresetsList.getItemFilter());
|
||||
items = filteringPermutations(items, GuiRecipe.getSearchItemFilter());
|
||||
items = filteringPermutations(items, additionalFilter);
|
||||
|
||||
items.sort(Comparator.comparing(FavoriteRecipes::containsManual).reversed());
|
||||
return items;
|
||||
}
|
||||
|
||||
private ItemFilter getItemFilter(ItemFilter additionalFilter) {
|
||||
return new AllMultiItemFilter(
|
||||
additionalFilter,
|
||||
item -> !ItemInfo.isHidden(item),
|
||||
PresetsList.getItemFilter(),
|
||||
GuiRecipe.getSearchItemFilter());
|
||||
private List<ItemStack> filteringPermutations(List<ItemStack> items, ItemFilter filter) {
|
||||
if (filter == null) return items;
|
||||
final List<ItemStack> filteredItems = items.stream().filter(filter::matches).collect(Collectors.toList());
|
||||
return filteredItems.isEmpty() ? items : filteredItems;
|
||||
}
|
||||
|
||||
public boolean setPermutationToRender(ItemStack ingredient) {
|
||||
public int getPermutationIndex(ItemStack stack) {
|
||||
|
||||
for (int index = 0; index < this.items.length; index++) {
|
||||
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(this.items[index], ingredient)) {
|
||||
setPermutationToRender(index);
|
||||
return true;
|
||||
if (NEIServerUtils.areStacksSameType(items[index], stack)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean setPermutationToRender(ItemStack ingredient) {
|
||||
final int stackIndex = getPermutationIndex(ingredient);
|
||||
|
||||
if (stackIndex >= 0) {
|
||||
setPermutationToRender(stackIndex);
|
||||
}
|
||||
|
||||
return stackIndex >= 0;
|
||||
}
|
||||
|
||||
public void setPermutationToRender(int index) {
|
||||
@@ -138,6 +211,12 @@ public class PositionedStack {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(int mx, int my) {
|
||||
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) {
|
||||
for (ItemStack item : items) if (NEIServerUtils.areStacksSameTypeCrafting(item, ingredient)) return true;
|
||||
|
||||
@@ -148,7 +227,7 @@ public class PositionedStack {
|
||||
* NBT-friendly version of {@link #contains(ItemStack)}
|
||||
*/
|
||||
public boolean containsWithNBT(ItemStack ingredient) {
|
||||
for (ItemStack item : items) if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(item, ingredient)) return true;
|
||||
for (ItemStack item : items) if (StackInfo.equalItemAndNBT(item, ingredient, true)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -163,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import codechicken.core.CommonUtils;
|
||||
import codechicken.nei.SubsetWidget.SubsetTag;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
@@ -33,6 +34,7 @@ import codechicken.nei.util.NBTJson;
|
||||
public class PresetsList {
|
||||
|
||||
public enum PresetMode {
|
||||
SUBSET,
|
||||
HIDE,
|
||||
REMOVE,
|
||||
GROUP;
|
||||
@@ -66,11 +68,11 @@ public class PresetsList {
|
||||
}
|
||||
}
|
||||
|
||||
protected static class RecipesFilter implements IRecipeFilterProvider, IRecipeFilter {
|
||||
protected static class PresetRecipeFilter implements IRecipeFilterProvider, IRecipeFilter {
|
||||
|
||||
public Set<String> cache;
|
||||
|
||||
public IRecipeFilter getFilter() {
|
||||
public IRecipeFilter getRecipeFilter() {
|
||||
|
||||
if (cache == null) {
|
||||
cache = PresetsList.presets.stream().filter(p -> p.enabled && p.mode == PresetMode.REMOVE)
|
||||
@@ -81,17 +83,20 @@ public class PresetsList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||
List<PositionedStack> others) {
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex) {
|
||||
|
||||
if (matchPositionedStack(ingredients, false)) {
|
||||
if (matchPositionedStack(handler.getIngredientStacks(recipeIndex), false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PositionedStack result = handler.getResultStack(recipeIndex);
|
||||
|
||||
if (result != null && matchPositionedStack(result)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
|
||||
|
||||
if (!others.isEmpty() && matchPositionedStack(others, true)) {
|
||||
return true;
|
||||
}
|
||||
@@ -143,7 +148,7 @@ public class PresetsList {
|
||||
}
|
||||
|
||||
public static final List<Preset> presets = new ArrayList<>();
|
||||
protected static RecipesFilter recipeFilter = new RecipesFilter();
|
||||
protected static PresetRecipeFilter recipeFilter = new PresetRecipeFilter();
|
||||
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
|
||||
|
||||
static {
|
||||
@@ -158,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)) {
|
||||
@@ -216,6 +219,8 @@ public class PresetsList {
|
||||
if (!preset.items.isEmpty()) {
|
||||
presets.add(preset);
|
||||
}
|
||||
|
||||
updateSubsets();
|
||||
}
|
||||
|
||||
public static void savePresets() {
|
||||
@@ -236,14 +241,29 @@ public class PresetsList {
|
||||
try (FileOutputStream output = new FileOutputStream(presetsFile)) {
|
||||
IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
NEIClientConfig.logger.error("Filed to save presets list to file {}", presetsFile, e);
|
||||
NEIClientConfig.logger.error("Failed to save presets list to file {}", presetsFile, e);
|
||||
}
|
||||
|
||||
recipeFilter.cache = null;
|
||||
itemFilter.cache = null;
|
||||
updateSubsets();
|
||||
CollapsibleItems.saveStates();
|
||||
CollapsibleItems.load();
|
||||
LayoutManager.markItemsDirty();
|
||||
ItemList.refreshItems.restart();
|
||||
}
|
||||
|
||||
private static void updateSubsets() {
|
||||
SubsetWidget.removeTag("Presets");
|
||||
|
||||
for (Preset preset : PresetsList.presets) {
|
||||
if (preset.enabled && preset.mode == PresetMode.SUBSET) {
|
||||
SubsetWidget.addTag(
|
||||
new SubsetTag(
|
||||
"Presets." + preset.name,
|
||||
stack -> preset.items.contains(Preset.getIdentifier(stack))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,10 +3,13 @@ package codechicken.nei;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import codechicken.nei.SearchField.GuiSearchField;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.util.TextHistory;
|
||||
|
||||
public abstract class RecipeSearchField extends TextField {
|
||||
public abstract class RecipeSearchField extends TextField implements ItemFilterProvider, IRecipeFilterProvider {
|
||||
|
||||
private static final TextHistory history = new TextHistory();
|
||||
|
||||
@@ -17,9 +20,12 @@ public abstract class RecipeSearchField extends TextField {
|
||||
|
||||
@Override
|
||||
protected void initInternalTextField() {
|
||||
field = new GuiSearchField();
|
||||
final GuiSearchField field = new GuiSearchField();
|
||||
field.setMaxStringLength(maxSearchLength);
|
||||
field.setCursorPositionZero();
|
||||
field.setSkipRecipeTokens(true);
|
||||
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
@@ -44,11 +50,11 @@ public abstract class RecipeSearchField extends TextField {
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
}
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
}
|
||||
}
|
||||
@@ -58,10 +64,16 @@ public abstract class RecipeSearchField extends TextField {
|
||||
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemFilter getFilter() {
|
||||
return ((GuiSearchField) field).getFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRecipeFilter getRecipeFilter() {
|
||||
return ((GuiSearchField) field).getRecipeFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocus(boolean focus) {
|
||||
final boolean previousFocus = field.isFocused();
|
||||
|
||||
@@ -15,7 +15,8 @@ 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;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -83,14 +84,15 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuiSearchField extends FormattedTextField {
|
||||
public static class GuiSearchField extends FormattedTextField implements ItemFilterProvider, IRecipeFilterProvider {
|
||||
|
||||
protected final SearchTokenParser searchParser;
|
||||
protected boolean skipRecipeTokens = false;
|
||||
|
||||
public GuiSearchField() {
|
||||
this(SearchField.searchParser);
|
||||
@@ -100,14 +102,30 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
super(Minecraft.getMinecraft().fontRenderer, 0, 0, 0, 0);
|
||||
this.searchParser = searchParser;
|
||||
setFormatter(new SearchTextFormatter(searchParser));
|
||||
setDisabledTextColour(0xFF303030);
|
||||
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
|
||||
}
|
||||
|
||||
public void setSkipRecipeTokens(boolean skipRecipeTokens) {
|
||||
this.skipRecipeTokens = skipRecipeTokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemFilter getFilter() {
|
||||
return getFilter(getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRecipeFilter getRecipeFilter() {
|
||||
return getRecipeFilter(getText());
|
||||
}
|
||||
|
||||
public ItemFilter getFilter(String filterText) {
|
||||
return this.searchParser.getFilter(filterText);
|
||||
return this.searchParser.getFilter(filterText, this.skipRecipeTokens);
|
||||
}
|
||||
|
||||
public IRecipeFilter getRecipeFilter(String filterText) {
|
||||
return this.searchParser.getRecipeFilter(filterText);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -203,7 +221,6 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
public void onTextChange(String oldText) {
|
||||
final String newText = text();
|
||||
if (!newText.equals(oldText)) {
|
||||
if (newText.length() > 0) NEIClientConfig.logger.debug("Searching for " + newText);
|
||||
NEIClientConfig.setSearchExpression(newText);
|
||||
ItemList.updateFilter.restart();
|
||||
}
|
||||
@@ -212,14 +229,14 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
|
||||
if (isVisible() && NEIClientConfig.isKeyHashDown("gui.search")) {
|
||||
if (isVisible() && KeyManager.isHashDown("gui.search")) {
|
||||
setFocus(true);
|
||||
}
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getprevioussearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
|
||||
}
|
||||
|
||||
if (focused() && NEIClientConfig.isKeyHashDown("gui.getnextsearch")) {
|
||||
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
|
||||
handleNavigateHistory(TextHistory.Direction.NEXT);
|
||||
}
|
||||
}
|
||||
@@ -270,7 +287,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// regex and extended+
|
||||
if (!search.isEmpty()) {
|
||||
try {
|
||||
return Pattern.compile(search, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
@@ -281,7 +298,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
}
|
||||
|
||||
public static String getEscapedSearchText(ItemStack stack) {
|
||||
final FluidStack fluidStack = StackInfo.isFluidContainer(stack) ? null : StackInfo.getFluid(stack);
|
||||
final FluidStack fluidStack = StackInfo.getFluid(stack);
|
||||
String displayName;
|
||||
|
||||
if (fluidStack != null) {
|
||||
@@ -296,6 +313,9 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
public static String getEscapedSearchText(String text) {
|
||||
text = EnumChatFormatting.getTextWithoutFormattingCodes(text);
|
||||
|
||||
boolean addQuotes = text.contains(" ")
|
||||
&& NEIClientConfig.getBooleanSetting("inventory.search.quoteDropItemName");
|
||||
|
||||
switch (NEIClientConfig.getIntSetting("inventory.search.patternMode")) {
|
||||
case 1:
|
||||
text = text.replaceAll("[\\?|\\*]", "\\\\$0");
|
||||
@@ -303,11 +323,20 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
||||
case 2:
|
||||
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
|
||||
break;
|
||||
case 3:
|
||||
// In extended+ quotes require only quotes to be escaped
|
||||
if (addQuotes) {
|
||||
text = text.replaceAll("\"", "\\\\$0");
|
||||
} else {
|
||||
String prefixes = searchParser.getPrefixes();
|
||||
text = text.replaceAll("[-<>^{}|/\\\\\"" + Pattern.quote(prefixes) + "]", "\\\\$0");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (text.contains(" ") && NEIClientConfig.getBooleanSetting("inventory.search.quoteDropItemName")) {
|
||||
if (addQuotes) {
|
||||
text = "\"" + text + "\"";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +1,96 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import codechicken.nei.FormattedTextField.TextFormatter;
|
||||
import codechicken.nei.SearchTokenParser.SearchToken;
|
||||
import codechicken.nei.search.SearchExpressionFormatVisitor;
|
||||
import codechicken.nei.search.SearchExpressionUtils;
|
||||
import codechicken.nei.search.SearchToken;
|
||||
|
||||
public class SearchTextFormatter implements TextFormatter {
|
||||
|
||||
protected final SearchTokenParser searchParser;
|
||||
protected String contextToken = "";
|
||||
|
||||
public SearchTextFormatter(SearchTokenParser searchParser) {
|
||||
this.searchParser = searchParser;
|
||||
}
|
||||
|
||||
public void setContextToken(String contextToken) {
|
||||
this.contextToken = contextToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(String text) {
|
||||
final String[] parts = (text + "| ").split("\\|");
|
||||
StringJoiner formattedText = new StringJoiner(EnumChatFormatting.GRAY + "|");
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
if (patternMode == 3) {
|
||||
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||
if (spaceModeEnabled == 1) {
|
||||
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
||||
}
|
||||
final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser);
|
||||
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
|
||||
} else {
|
||||
final StringBuilder formattedText = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
final String filterText = parts[i];
|
||||
final List<SearchToken> tokens = searchParser.splitSearchText(filterText);
|
||||
StringBuilder formattedPart = new StringBuilder();
|
||||
int startIndex = 0;
|
||||
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
|
||||
final String filterText = orQuery[1];
|
||||
|
||||
for (SearchToken token : tokens) {
|
||||
formattedPart.append(filterText.substring(startIndex, token.start));
|
||||
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
|
||||
|
||||
if (token.firstChar != null) {
|
||||
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
|
||||
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
|
||||
|
||||
if (!contextPart[0].isEmpty()) {
|
||||
formattedText.append(
|
||||
SearchExpressionUtils.HIGHLIGHTS.RECIPE + contextPart[0].substring(0, 1)
|
||||
+ EnumChatFormatting.RESET);
|
||||
contextPart[1] = contextPart[0].substring(1) + contextPart[1];
|
||||
}
|
||||
|
||||
if (contextPart[1].isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int startIndex = 0;
|
||||
|
||||
for (SearchToken token : searchParser.splitSearchText(contextPart[1])) {
|
||||
formattedText.append(contextPart[1].substring(startIndex, token.start));
|
||||
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;
|
||||
|
||||
if (token.firstChar != null) {
|
||||
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
|
||||
}
|
||||
|
||||
if (token.ignore != null) {
|
||||
formattedText
|
||||
.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + String.valueOf(token.ignore));
|
||||
}
|
||||
|
||||
if (token.firstChar != null) {
|
||||
formattedText.append(tokenColor + String.valueOf(token.firstChar));
|
||||
}
|
||||
|
||||
if (token.quotes) {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||
}
|
||||
|
||||
if (!token.rawText.isEmpty()) {
|
||||
formattedText.append(tokenColor + token.rawText);
|
||||
}
|
||||
|
||||
if (token.quotes) {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||
}
|
||||
|
||||
startIndex = token.end;
|
||||
}
|
||||
|
||||
formattedText.append(contextPart[1].substring(startIndex, contextPart[1].length()));
|
||||
}
|
||||
|
||||
if (token.ignore) {
|
||||
formattedPart.append(EnumChatFormatting.BLUE + "-");
|
||||
}
|
||||
|
||||
if (token.firstChar != null) {
|
||||
formattedPart.append(tokenColor + String.valueOf(token.firstChar));
|
||||
}
|
||||
|
||||
if (token.quotes) {
|
||||
formattedPart.append(EnumChatFormatting.GOLD + "\"");
|
||||
}
|
||||
|
||||
if (!token.rawText.isEmpty()) {
|
||||
formattedPart.append(tokenColor + token.rawText);
|
||||
}
|
||||
|
||||
if (token.quotes) {
|
||||
formattedPart.append(EnumChatFormatting.GOLD + "\"");
|
||||
}
|
||||
|
||||
startIndex = token.end;
|
||||
}
|
||||
|
||||
formattedPart.append(filterText.substring(startIndex, filterText.length()));
|
||||
formattedText.add(formattedPart);
|
||||
return formattedText.toString();
|
||||
}
|
||||
|
||||
return formattedText.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.Language;
|
||||
@@ -18,9 +15,16 @@ import net.minecraft.util.EnumChatFormatting;
|
||||
import codechicken.nei.ItemList.AllMultiItemFilter;
|
||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
||||
import codechicken.nei.ItemList.EverythingItemFilter;
|
||||
import codechicken.nei.ItemList.NegatedItemFilter;
|
||||
import codechicken.nei.ItemList.NothingItemFilter;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.filter.AllMultiRecipeFilter;
|
||||
import codechicken.nei.filter.AnyMultiRecipeFilter;
|
||||
import codechicken.nei.filter.RecipeFilter;
|
||||
import codechicken.nei.filter.RecipeFilter.FilterContext;
|
||||
import codechicken.nei.search.ItemFilterVisitor;
|
||||
import codechicken.nei.search.RecipeFilterVisitor;
|
||||
import codechicken.nei.search.SearchExpressionUtils;
|
||||
import codechicken.nei.search.SearchToken;
|
||||
|
||||
public class SearchTokenParser {
|
||||
|
||||
@@ -39,20 +43,6 @@ public class SearchTokenParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SearchToken {
|
||||
|
||||
public boolean ignore = false;
|
||||
public boolean quotes = false;
|
||||
public Character firstChar = null;
|
||||
|
||||
public String[] words;
|
||||
|
||||
public String rawText = "";
|
||||
public int start = 0;
|
||||
public int end = 0;
|
||||
|
||||
}
|
||||
|
||||
public static interface ISearchParserProvider {
|
||||
|
||||
public ItemFilter getFilter(String searchText);
|
||||
@@ -69,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 {
|
||||
@@ -83,7 +79,7 @@ public class SearchTokenParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static class IsRegisteredItemFilter implements ItemFilter {
|
||||
public static class IsRegisteredItemFilter implements ItemFilter {
|
||||
|
||||
public ItemFilter filter;
|
||||
|
||||
@@ -99,10 +95,14 @@ public class SearchTokenParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static final Pattern SPACE_PATTERN = Pattern.compile(" ");
|
||||
|
||||
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
|
||||
protected final LRUCache<String, IRecipeFilter> recipesCache = new LRUCache<>(20);
|
||||
protected final List<ISearchParserProvider> searchProviders;
|
||||
protected final ProvidersCache providersCache = new ProvidersCache();
|
||||
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
|
||||
protected String prefixes = null;
|
||||
|
||||
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
|
||||
this.searchProviders = searchProviders;
|
||||
@@ -114,196 +114,384 @@ public class SearchTokenParser {
|
||||
|
||||
public void addProvider(ISearchParserProvider provider) {
|
||||
this.searchProviders.add(provider);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
public synchronized void clearCache() {
|
||||
this.providersCache.clear();
|
||||
this.filtersCache.clear();
|
||||
this.recipesCache.clear();
|
||||
this.prefixes = null;
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
this.filtersCache.clear();
|
||||
}
|
||||
|
||||
protected List<ISearchParserProvider> getProviders() {
|
||||
public List<ISearchParserProvider> getProviders() {
|
||||
Language currentLanguage = Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage();
|
||||
|
||||
if (!currentLanguage.getLanguageCode().equals(providersCache.languageCode)) {
|
||||
Map<Character, ISearchParserProvider> providers = new HashMap<>();
|
||||
final List<ISearchParserProvider> providersList = new ArrayList<>();
|
||||
final Map<Character, ISearchParserProvider> providers = new HashMap<>();
|
||||
|
||||
for (int index = this.searchProviders.size() - 1; index >= 0; index--) {
|
||||
ISearchParserProvider provider = this.searchProviders.get(index);
|
||||
|
||||
if (!providers.containsKey(provider.getPrefix())
|
||||
&& provider.getMatchingLanguages().contains(currentLanguage)) {
|
||||
providers.put(provider.getPrefix(), provider);
|
||||
if (provider.getMatchingLanguages().contains(currentLanguage)) {
|
||||
if (provider.getSearchMode() == SearchMode.PREFIX && !providers.containsKey(provider.getPrefix())) {
|
||||
providers.put(provider.getPrefix(), provider);
|
||||
} else if (provider.getSearchMode() == SearchMode.ALWAYS) {
|
||||
providersList.add(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
providersCache.providers = new ArrayList<>(providers.values());
|
||||
providersList.addAll(providers.values());
|
||||
providersCache.languageCode = currentLanguage.getLanguageCode();
|
||||
providersCache.providers = providersList;
|
||||
}
|
||||
|
||||
return providersCache.providers;
|
||||
}
|
||||
|
||||
public boolean hasRedefinedPrefix(char ch) {
|
||||
return getPrefixes().indexOf(ch) >= 0;
|
||||
}
|
||||
|
||||
public ISearchParserProvider getProvider(char ch) {
|
||||
return getProviders().stream()
|
||||
.filter(
|
||||
provider -> provider.getSearchMode() == SearchMode.PREFIX
|
||||
&& getRedefinedPrefix(provider.getPrefix()) == ch)
|
||||
.findFirst().orElse(null);
|
||||
if (!hasRedefinedPrefix(ch)) {
|
||||
return null;
|
||||
}
|
||||
for (ISearchParserProvider provider : getProviders()) {
|
||||
if (provider.getSearchMode() == SearchMode.PREFIX && getRedefinedPrefix(provider.getPrefix()) == ch) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ItemFilter> getAlwaysProvidersFilters(String searchText) {
|
||||
final List<ItemFilter> filters = new ArrayList<>();
|
||||
for (ISearchParserProvider provider : getProviders()) {
|
||||
if (provider.getSearchMode() == SearchMode.ALWAYS) {
|
||||
filters.add(provider.getFilter(searchText));
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
public synchronized ItemFilter getFilter(String filterText) {
|
||||
return getFilter(filterText, false);
|
||||
}
|
||||
|
||||
public synchronized ItemFilter getFilter(String rawText, boolean skipRecipeTokens) {
|
||||
final String filterText = EnumChatFormatting.getTextWithoutFormattingCodes(rawText).toLowerCase();
|
||||
|
||||
if (filterText.isEmpty()) {
|
||||
return new EverythingItemFilter();
|
||||
}
|
||||
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
|
||||
if (patternMode != 3) {
|
||||
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
|
||||
final List<ItemFilter> searchTokens = new ArrayList<>();
|
||||
|
||||
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
|
||||
|
||||
if (subQuery[1].isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<ItemFilter> tokens = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!contextQuery[0].isEmpty()) {
|
||||
contextQuery[0] = contextQuery[0].substring(1);
|
||||
}
|
||||
|
||||
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
|
||||
final ItemFilter result = token.getFilter(this);
|
||||
|
||||
if (result != null) {
|
||||
tokens.add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.size() == 1) {
|
||||
searchTokens.add(tokens.get(0));
|
||||
} else if (!tokens.isEmpty()) {
|
||||
searchTokens.add(new AllMultiItemFilter(tokens));
|
||||
}
|
||||
}
|
||||
|
||||
if (searchTokens.isEmpty()) {
|
||||
return new EverythingItemFilter();
|
||||
} else if (searchTokens.size() == 1) {
|
||||
return new IsRegisteredItemFilter(searchTokens.get(0));
|
||||
} else {
|
||||
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
||||
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||
|
||||
if (spaceMode == 1) {
|
||||
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
||||
}
|
||||
if (skipRecipeTokens) {
|
||||
return new IsRegisteredItemFilter(
|
||||
SearchExpressionUtils.visitRecipeSearchExpression(text, new ItemFilterVisitor(this)));
|
||||
} else {
|
||||
return new IsRegisteredItemFilter(
|
||||
SearchExpressionUtils.visitSearchExpression(text, new ItemFilterVisitor(this)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
|
||||
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
||||
|
||||
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
||||
final String[] parts = text.split("\\|");
|
||||
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText).filter(s -> s != null)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
if (filterText.isEmpty()) {
|
||||
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
|
||||
}
|
||||
|
||||
if (searchTokens.isEmpty()) {
|
||||
return new EverythingItemFilter();
|
||||
} else if (searchTokens.size() == 1) {
|
||||
return new IsRegisteredItemFilter(searchTokens.get(0));
|
||||
} else {
|
||||
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
|
||||
}
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
|
||||
});
|
||||
if (patternMode != 3) {
|
||||
return this.recipesCache.computeIfAbsent(filterText, input -> {
|
||||
final List<IRecipeFilter> searchTokens = new ArrayList<>();
|
||||
|
||||
for (String[] orQueryPart : splitByDelimiters(input, "|", false)) {
|
||||
|
||||
if (orQueryPart[1].isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<IRecipeFilter> contextList = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<IRecipeFilter> tokens = new ArrayList<>();
|
||||
FilterContext context = FilterContext.ANY;
|
||||
|
||||
if (!contextQuery[0].isEmpty()) {
|
||||
context = FilterContext.fromChar(contextQuery[0].charAt(0));
|
||||
contextQuery[0] = contextQuery[0].substring(1);
|
||||
}
|
||||
|
||||
for (SearchToken token : splitSearchText(contextQuery[0] + contextQuery[1])) {
|
||||
final ItemFilter result = token.getFilter(this);
|
||||
if (result != null) {
|
||||
tokens.add(
|
||||
new RecipeFilter(context, token.ignore == null || token.ignore != '!', result));
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.size() == 1) {
|
||||
contextList.add(tokens.get(0));
|
||||
} else if (!tokens.isEmpty()) {
|
||||
contextList.add(new AllMultiRecipeFilter(tokens));
|
||||
}
|
||||
}
|
||||
|
||||
if (contextList.size() == 1) {
|
||||
searchTokens.add(contextList.get(0));
|
||||
} else if (!contextList.isEmpty()) {
|
||||
searchTokens.add(new AllMultiRecipeFilter(contextList));
|
||||
}
|
||||
}
|
||||
|
||||
if (searchTokens.isEmpty()) {
|
||||
return new RecipeFilter(FilterContext.ANY, true, new EverythingItemFilter());
|
||||
} else if (searchTokens.size() == 1) {
|
||||
return searchTokens.get(0);
|
||||
} else {
|
||||
return new AnyMultiRecipeFilter(searchTokens);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
return this.recipesCache.computeIfAbsent(filterText, text -> {
|
||||
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||
|
||||
if (spaceMode == 1) {
|
||||
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
||||
}
|
||||
|
||||
return SearchExpressionUtils.visitRecipeSearchExpression(text, new RecipeFilterVisitor(this));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<SearchToken> splitSearchText(String filterText) {
|
||||
|
||||
if (filterText.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<SearchToken> tokens = new ArrayList<>();
|
||||
final String prefixes = getPrefixes();
|
||||
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||
// The regular expression first tries to match a string that starts with a space or the beginning of the string,
|
||||
// followed by a sequence of characters that do not contain special characters -"@#$%&, and ends with a space
|
||||
// and the characters -"@#$%& or the end of the string.
|
||||
final String patternPart1 = "(?<tokenA>(^|\\s+)(?:[^" + Pattern.quote(" -\"" + prefixes)
|
||||
+ "]).+?(?=\\s+["
|
||||
+ Pattern.quote("-\"" + prefixes)
|
||||
+ "]|$))";
|
||||
// If the first condition is not met, it tries to match a sequence of - characters, followed by @#$%&
|
||||
// characters, then either a quoted string or non-space characters.
|
||||
final String patternPart2 = "((?<ignore>-*)(?<firstChar>[" + Pattern.quote(prefixes)
|
||||
+ "]*)(?<tokenB>\\\".*?(?:\\\"|$)|\\S+\\s*))";
|
||||
final Pattern pattern = Pattern.compile(spaceMode == 0 ? patternPart2 : (patternPart1 + "|" + patternPart2));
|
||||
final Matcher filterMatcher = pattern.matcher(filterText);
|
||||
final String prefixes = getPrefixes();
|
||||
SearchToken lastToken = null;
|
||||
int lastEnd = 0;
|
||||
|
||||
while (filterMatcher.find()) {
|
||||
String firstChar = filterMatcher.group("firstChar");
|
||||
SearchToken token = new SearchToken();
|
||||
token.start = filterMatcher.start();
|
||||
token.end = filterMatcher.end();
|
||||
token.ignore = "-".equals(filterMatcher.group("ignore"));
|
||||
token.rawText = spaceMode == 0 ? null : filterMatcher.group("tokenA");
|
||||
for (String[] part : splitByDelimiters(filterText, "-!" + prefixes, true)) {
|
||||
final SearchToken token = createToken(lastEnd, part, prefixes);
|
||||
final int length = part[0].length() + part[1].length();
|
||||
|
||||
if (firstChar != null && !firstChar.isEmpty()) {
|
||||
token.firstChar = firstChar.charAt(0);
|
||||
if (token.isSimpleFilter() && token.rawText.isEmpty()) {
|
||||
lastEnd += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.rawText == null) { // spaceMode == 0
|
||||
token.rawText = filterMatcher.group("tokenB");
|
||||
token.quotes = token.rawText.length() > 1 && token.rawText.startsWith("\"")
|
||||
&& token.rawText.endsWith("\"");
|
||||
if (spaceMode == 0) { // and
|
||||
tokens.add(lastToken = token);
|
||||
} else if (spaceMode == 1) { // space
|
||||
|
||||
if (token.quotes) {
|
||||
token.rawText = token.rawText.substring(1, token.rawText.length() - 1);
|
||||
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
|
||||
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
|
||||
lastToken.words[0] = lastToken.rawText;
|
||||
lastToken.end = token.end;
|
||||
} else {
|
||||
tokens.add(lastToken = token);
|
||||
}
|
||||
|
||||
} else if (spaceMode == 2) { // and-x
|
||||
|
||||
if (lastToken != null && lastToken.isSimpleFilter() && token.isSimpleFilter()) {
|
||||
final String[] newWords = new String[lastToken.words.length + token.words.length];
|
||||
|
||||
System.arraycopy(lastToken.words, 0, newWords, 0, lastToken.words.length);
|
||||
System.arraycopy(token.words, 0, newWords, lastToken.words.length, token.words.length);
|
||||
|
||||
lastToken.words = newWords;
|
||||
lastToken.rawText = filterText.substring(lastToken.start, token.end).trim();
|
||||
lastToken.end = token.end;
|
||||
} else {
|
||||
tokens.add(lastToken = token);
|
||||
}
|
||||
|
||||
token.words = new String[] { token.rawText.trim() };
|
||||
} else if (spaceMode == 2) {
|
||||
token.words = token.rawText.trim().split("\\s+");
|
||||
} else {
|
||||
token.words = new String[] { token.rawText.trim() };
|
||||
}
|
||||
|
||||
tokens.add(token);
|
||||
lastEnd += length;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private String getPrefixes() {
|
||||
StringBuilder prefixes = new StringBuilder().append('\0');
|
||||
private SearchToken createToken(int lastEnd, String[] part, String prefixes) {
|
||||
final SearchToken token = new SearchToken();
|
||||
String pref = part[0];
|
||||
String query = part[1].trim();
|
||||
|
||||
for (ISearchParserProvider provider : getProviders()) {
|
||||
if (provider.getSearchMode() == SearchMode.PREFIX) {
|
||||
prefixes.append(getRedefinedPrefix(provider.getPrefix()));
|
||||
token.start = lastEnd;
|
||||
token.end = lastEnd + pref.length() + query.length();
|
||||
|
||||
if (pref.startsWith("-")) {
|
||||
token.ignore = '-';
|
||||
pref = pref.substring(1);
|
||||
} else if (pref.startsWith("!")) {
|
||||
token.ignore = '!';
|
||||
pref = pref.substring(1);
|
||||
}
|
||||
|
||||
if (!pref.isEmpty()) {
|
||||
char firstChar = pref.charAt(0);
|
||||
|
||||
if (prefixes.indexOf(firstChar) >= 0) {
|
||||
token.firstChar = firstChar;
|
||||
pref = pref.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return prefixes.toString();
|
||||
String text = pref + query;
|
||||
token.quotes = text.length() > 1 && text.startsWith("\"") && text.endsWith("\"");
|
||||
|
||||
if (token.quotes) {
|
||||
text = text.substring(1, text.length() - 1);
|
||||
}
|
||||
|
||||
token.rawText = text;
|
||||
token.words = new String[] { token.quotes ? text.replaceAll("\\\\\"", "\"") : text };
|
||||
return token;
|
||||
}
|
||||
|
||||
public List<String[]> splitByDelimiters(String input, String delimiters, boolean space) {
|
||||
|
||||
if (delimiters == null || delimiters.isEmpty()) {
|
||||
return Collections.singletonList(new String[] { "", input });
|
||||
}
|
||||
|
||||
final List<String[]> tokens = new ArrayList<>();
|
||||
final int length = input.length();
|
||||
boolean insideQuotes = false;
|
||||
String token = "";
|
||||
int lastEnd = 0;
|
||||
int index = 0;
|
||||
|
||||
while (index < length) {
|
||||
final char ch = input.charAt(index);
|
||||
|
||||
if (!insideQuotes && delimiters.indexOf(ch) >= 0
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ')) {
|
||||
|
||||
if (lastEnd == index) {
|
||||
token += String.valueOf(ch);
|
||||
} else {
|
||||
tokens.add(new String[] { token, input.substring(lastEnd, index) });
|
||||
token = String.valueOf(ch);
|
||||
}
|
||||
|
||||
lastEnd = index + 1;
|
||||
} else if (!insideQuotes && space && ch != ' ' && (index > 0 && input.charAt(index - 1) == ' ')) {
|
||||
tokens.add(new String[] { token, input.substring(lastEnd, index) });
|
||||
token = "";
|
||||
|
||||
lastEnd = index;
|
||||
}
|
||||
|
||||
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
|
||||
insideQuotes = !insideQuotes;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
tokens.add(new String[] { token, input.substring(lastEnd) });
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public String getPrefixes() {
|
||||
if (this.prefixes == null) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (ISearchParserProvider provider : getProviders()) {
|
||||
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
|
||||
builder.append(getRedefinedPrefix(provider.getPrefix()));
|
||||
}
|
||||
}
|
||||
|
||||
this.prefixes = builder.toString();
|
||||
}
|
||||
|
||||
return this.prefixes;
|
||||
}
|
||||
|
||||
public char getRedefinedPrefix(char prefix) {
|
||||
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
||||
}
|
||||
|
||||
private ItemFilter parseSearchText(String filterText) {
|
||||
|
||||
if (filterText.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
|
||||
final List<SearchToken> tokens = splitSearchText(filterText);
|
||||
|
||||
for (SearchToken token : tokens) {
|
||||
if (!token.rawText.isEmpty()) {
|
||||
ItemFilter result = parseToken(token);
|
||||
|
||||
if (token.ignore) {
|
||||
searchTokens.filters.add(new NegatedItemFilter(result));
|
||||
} else {
|
||||
searchTokens.filters.add(result);
|
||||
}
|
||||
} else if (!token.ignore) {
|
||||
searchTokens.filters.add(new NothingItemFilter());
|
||||
}
|
||||
}
|
||||
|
||||
return searchTokens;
|
||||
}
|
||||
|
||||
private ItemFilter parseToken(SearchToken token) {
|
||||
final ISearchParserProvider provider = token.firstChar == null ? null : this.getProvider(token.firstChar);
|
||||
|
||||
if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
|
||||
final List<ItemFilter> filters = new ArrayList<>();
|
||||
|
||||
for (ISearchParserProvider _provider : getProviders()) {
|
||||
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
|
||||
AllMultiItemFilter filter = generateFilters(_provider, token.words);
|
||||
|
||||
if (!filter.filters.isEmpty()) {
|
||||
filters.add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
|
||||
} else {
|
||||
return generateFilters(provider, token.words);
|
||||
}
|
||||
}
|
||||
|
||||
private AllMultiItemFilter generateFilters(ISearchParserProvider provider, String[] words) {
|
||||
final AllMultiItemFilter filters = new AllMultiItemFilter();
|
||||
|
||||
for (String work : words) {
|
||||
final ItemFilter filter = provider.getFilter(work);
|
||||
|
||||
if (filter != null) {
|
||||
filters.filters.add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
@@ -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,13 +19,12 @@ 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 {
|
||||
|
||||
private static ServerHandler instance;
|
||||
|
||||
public static void load() {
|
||||
instance = new ServerHandler();
|
||||
ServerHandler instance = new ServerHandler();
|
||||
|
||||
PacketCustom.assignHandler(NEICPH.channel, new NEISPH());
|
||||
FMLCommonHandler.instance().bus().register(instance);
|
||||
@@ -33,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
|
||||
@@ -48,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);
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.boss.BossStatus;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.IItemRenderer;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -27,21 +26,16 @@ public class SpawnerRenderer implements IItemRenderer {
|
||||
|
||||
public void renderInventoryItem(RenderBlocks render, ItemStack item) {
|
||||
int meta = item.getItemDamage();
|
||||
|
||||
if (meta == 0) meta = ItemMobSpawner.idPig;
|
||||
|
||||
String bossName = BossStatus.bossName;
|
||||
int bossTimeout = BossStatus.statusBarTime;
|
||||
boolean bossHasColorModifier = BossStatus.hasColorModifier;
|
||||
Entity entity = ItemMobSpawner.getEntity(meta);
|
||||
try {
|
||||
World world = NEIClientUtils.mc().theWorld;
|
||||
ItemMobSpawner.loadSpawners(world);
|
||||
CCRenderState.changeTexture(TextureMap.locationBlocksTexture);
|
||||
render.renderBlockAsItem(Blocks.mob_spawner, 0, 1F);
|
||||
entity.setWorld(render.minecraftRB.theWorld);
|
||||
GL11.glPushMatrix();
|
||||
|
||||
Entity entity = ItemMobSpawner.getEntity(meta);
|
||||
entity.setWorld(world);
|
||||
float f1 = 0.4375F;
|
||||
if (entity.getShadowSize() > 1.5) f1 = 0.1F;
|
||||
GL11.glRotatef((float) (ClientUtils.getRenderTime() * 10), 0.0F, 1.0F, 0.0F);
|
||||
@@ -51,7 +45,6 @@ public class SpawnerRenderer implements IItemRenderer {
|
||||
entity.setLocationAndAngles(0, 0, 0, 0.0F, 0.0F);
|
||||
RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 0);
|
||||
GL11.glPopMatrix();
|
||||
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
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;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import codechicken.core.gui.GuiScrollSlot;
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
@@ -32,17 +36,21 @@ import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.util.NBTJson;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public class SubsetWidget extends Button implements ItemFilterProvider, IContainerTooltipHandler {
|
||||
public class SubsetWidget extends Button implements ItemFilterProvider {
|
||||
|
||||
protected static final int SLOT_HEIGHT = 18;
|
||||
protected static final int MARGIN = 2;
|
||||
protected static final char PREFIX = '%';
|
||||
|
||||
protected static ReentrantReadWriteLock hiddenItemLock = new ReentrantReadWriteLock();
|
||||
protected static Lock hiddenWriteLock = hiddenItemLock.writeLock();
|
||||
protected static Lock hiddenReadLock = hiddenItemLock.readLock();
|
||||
private static final Object hiddenLock = new Object();
|
||||
|
||||
public static class SubsetTag {
|
||||
|
||||
@@ -152,7 +160,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
// 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();
|
||||
@@ -185,7 +193,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
public String name() {
|
||||
int idx = this.fullname.indexOf('.');
|
||||
int idx = this.fullname.lastIndexOf('.');
|
||||
return idx < 0 ? this.fullname : this.fullname.substring(idx + 1);
|
||||
}
|
||||
|
||||
@@ -198,7 +206,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
this.state = 2;
|
||||
this.items.clear();
|
||||
this.children.clear();
|
||||
this.calculatedWidth = 0;
|
||||
this.calculatedWidth = -1;
|
||||
}
|
||||
|
||||
public void updateVisiblity(int mx, int my) {
|
||||
@@ -253,6 +261,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -355,16 +367,31 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
private static class DefaultParserProvider implements ISearchParserProvider {
|
||||
|
||||
public ItemFilter getFilter(String searchText) {
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
Pattern pattern = null;
|
||||
if (patternMode != 3) {
|
||||
searchText = searchText.replaceAll("\\s+", "").toLowerCase();
|
||||
} else {
|
||||
pattern = SearchField.getPattern(searchText, patternMode);
|
||||
}
|
||||
final AnyMultiItemFilter filter = new AnyMultiItemFilter();
|
||||
final String pathPart = searchText.replaceAll("\\s+", "").toLowerCase();
|
||||
final Set<ItemStack> filteredItems = new HashSet<>();
|
||||
final List<SubsetTag> snapshot;
|
||||
|
||||
for (SubsetTag tag : tags.values()) {
|
||||
if (tag.filter != null && tag.path.contains(pathPart)) {
|
||||
filter.filters.add(tag.filter);
|
||||
synchronized (tags) {
|
||||
snapshot = new ArrayList<>(tags.values());
|
||||
}
|
||||
|
||||
for (SubsetTag tag : snapshot) {
|
||||
if (tag.filter != null && matches(tag.path, searchText, pattern)) {
|
||||
synchronized (tag.items) {
|
||||
filteredItems.addAll(tag.items);
|
||||
filter.filters.add(tag.filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
return stack -> filteredItems.contains(stack) || !ItemList.items.contains(stack) && filter.matches(stack);
|
||||
}
|
||||
|
||||
public char getPrefix() {
|
||||
@@ -376,15 +403,25 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (pattern != null) {
|
||||
return pattern.matcher(name).find();
|
||||
} else {
|
||||
return name.contains(searchText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("");
|
||||
protected static boolean enableSearchBySubsets = false;
|
||||
@@ -407,24 +444,31 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
updateHiddenItems();
|
||||
SearchField.searchParser.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeTag(String path) {
|
||||
updateState.stop();
|
||||
|
||||
synchronized (tags) {
|
||||
tags.keySet().removeIf(
|
||||
parentpath -> parentpath.equals(path.toLowerCase())
|
||||
|| parentpath.startsWith(path.toLowerCase() + "."));
|
||||
updateHiddenItems();
|
||||
SearchField.searchParser.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isHidden(ItemStack item) {
|
||||
try {
|
||||
if (hiddenReadLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
return hiddenItems.contains(item);
|
||||
} finally {
|
||||
hiddenReadLock.unlock();
|
||||
}
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain read lock in 'isHidden'");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
final String guid = StackInfo.getItemStackGUID(item);
|
||||
synchronized (hiddenLock) {
|
||||
return hiddenItems.contains(guid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -438,90 +482,50 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
public static void showOnly(SubsetTag tag) {
|
||||
try {
|
||||
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(getItems(root, new ArrayList<>()));
|
||||
hiddenItems.removeAll(getItems(tag, new ArrayList<>()));
|
||||
} finally {
|
||||
hiddenWriteLock.unlock();
|
||||
}
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain write lock in 'showOnly'");
|
||||
}
|
||||
final List<String> allItems = toGUIDs(getItems(root, new ArrayList<>()));
|
||||
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
|
||||
|
||||
calculateVisibility();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
synchronized (hiddenLock) {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(allItems);
|
||||
hiddenItems.removeAll(tagItems);
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
}
|
||||
|
||||
public static void setHidden(SubsetTag tag, boolean hidden) {
|
||||
try {
|
||||
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
List<ItemStack> tagItems = getItems(tag, new ArrayList<>());
|
||||
final List<String> tagItems = toGUIDs(getItems(tag, new ArrayList<>()));
|
||||
|
||||
if (hidden) {
|
||||
hiddenItems.addAll(tagItems);
|
||||
} else {
|
||||
hiddenItems.removeAll(tagItems);
|
||||
}
|
||||
|
||||
} finally {
|
||||
hiddenWriteLock.unlock();
|
||||
}
|
||||
synchronized (hiddenLock) {
|
||||
if (hidden) {
|
||||
hiddenItems.addAll(tagItems);
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'");
|
||||
hiddenItems.removeAll(tagItems);
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
}
|
||||
|
||||
public static void setHidden(ItemStack item, boolean hidden) {
|
||||
try {
|
||||
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
final String guid = StackInfo.getItemStackGUID(item);
|
||||
|
||||
if (hidden) {
|
||||
hiddenItems.add(item);
|
||||
} else {
|
||||
hiddenItems.remove(item);
|
||||
}
|
||||
|
||||
} finally {
|
||||
hiddenWriteLock.unlock();
|
||||
}
|
||||
synchronized (hiddenLock) {
|
||||
if (hidden) {
|
||||
hiddenItems.add(guid);
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain write lock in 'setHidden'");
|
||||
hiddenItems.remove(guid);
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
calculateVisibility();
|
||||
}
|
||||
|
||||
public static void unhideAll() {
|
||||
try {
|
||||
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
hiddenItems.clear();
|
||||
} finally {
|
||||
hiddenWriteLock.unlock();
|
||||
}
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain write lock in 'unhideAll'");
|
||||
}
|
||||
|
||||
calculateVisibility();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
synchronized (hiddenLock) {
|
||||
hiddenItems.clear();
|
||||
}
|
||||
calculateVisibility();
|
||||
}
|
||||
|
||||
public static void updateHiddenItems() {
|
||||
@@ -530,43 +534,106 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadCustomSubsets() {
|
||||
File dir = new File(NEIClientConfig.configDir, "subsets");
|
||||
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Stream.of(dir.listFiles()).filter(file -> !file.isDirectory()).forEach(file -> {
|
||||
ClientHandler.loadSettingsFile(
|
||||
"subsets/" + file.getName(),
|
||||
lines -> parseFile(file.getName(), lines.collect(Collectors.toList())));
|
||||
});
|
||||
}
|
||||
|
||||
private static void parseFile(String resource, List<String> itemStrings) {
|
||||
final JsonParser parser = new JsonParser();
|
||||
final int dotIndex = resource.lastIndexOf('.');
|
||||
final String subsetNamespace = dotIndex > 0 ? resource.substring(0, dotIndex) : resource;
|
||||
SubsetTag processedTag = new SubsetTag(subsetNamespace, new ItemStackSet());
|
||||
|
||||
for (String itemStr : itemStrings) {
|
||||
try {
|
||||
if (itemStr.startsWith("; ")) {
|
||||
addTag(processedTag);
|
||||
|
||||
final String handlerName = itemStr.substring(2);
|
||||
final IRecipeHandler recipeHandler = Stream
|
||||
.concat(
|
||||
GuiCraftingRecipe.craftinghandlers.stream(),
|
||||
GuiCraftingRecipe.serialCraftingHandlers.stream())
|
||||
.filter(handler -> handlerName.equals(getHandlerName(handler))).findFirst().orElse(null);
|
||||
String recipeName = handlerName;
|
||||
|
||||
if (recipeHandler != null) {
|
||||
recipeName = recipeHandler.getRecipeName().trim();
|
||||
}
|
||||
|
||||
processedTag = new SubsetTag(
|
||||
subsetNamespace + "." + recipeName.replace(".", ""),
|
||||
new ItemStackSet());
|
||||
} else {
|
||||
final NBTBase nbt = NBTJson.toNbt(parser.parse(itemStr));
|
||||
|
||||
if (nbt instanceof NBTTagCompound tag) {
|
||||
((ItemStackSet) processedTag.filter).add(StackInfo.loadFromNBT(tag));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Expected NBTTagCompound but got " + nbt.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger.error("Failed to load custom subset items from json string:\n{}", itemStr);
|
||||
}
|
||||
}
|
||||
|
||||
addTag(processedTag);
|
||||
}
|
||||
|
||||
private static String getHandlerName(ICraftingHandler handler) {
|
||||
return GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (hiddenWriteLock.tryLock(5, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(itemList);
|
||||
} finally {
|
||||
hiddenWriteLock.unlock();
|
||||
}
|
||||
} else {
|
||||
NEIClientConfig.logger.error("Unable to obtain second write lock in 'loadHidden'");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
synchronized (hiddenLock) {
|
||||
hiddenItems.clear();
|
||||
hiddenItems.addAll(guids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void saveHidden() {
|
||||
if (NEIClientConfig.world == null) return;
|
||||
|
||||
final NBTTagList list = new NBTTagList();
|
||||
for (ItemStack stack : hiddenItems.values()) {
|
||||
list.appendTag(stack.writeToNBT(new NBTTagCompound()));
|
||||
synchronized (hiddenLock) {
|
||||
for (String guid : hiddenItems) {
|
||||
list.appendTag(new NBTTagString(guid));
|
||||
}
|
||||
}
|
||||
|
||||
NEIClientConfig.world.nbt.setTag("hiddenItems", list);
|
||||
@@ -584,14 +651,22 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
public void execute() {
|
||||
|
||||
try {
|
||||
List<SubsetTag> list = new ArrayList<>(tags.values());
|
||||
final List<SubsetTag> list;
|
||||
|
||||
synchronized (tags) {
|
||||
list = new ArrayList<>(tags.values());
|
||||
}
|
||||
|
||||
root.clearCache();
|
||||
|
||||
list.parallelStream().forEach(tag -> {
|
||||
tag.clearCache();
|
||||
ItemList.items.stream().filter(tag.filter::matches).map(ItemStack::copy)
|
||||
.collect(Collectors.toCollection(() -> tag.items));
|
||||
|
||||
if (!(tag.filter instanceof NothingItemFilter)) {
|
||||
ItemList.items.stream().filter(tag.filter::matches)
|
||||
.collect(Collectors.toCollection(() -> tag.items));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
for (SubsetTag tag : list) {
|
||||
@@ -615,18 +690,19 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
SearchField.searchParser.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,15 +714,17 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
@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 {
|
||||
return EnumChatFormatting.DARK_PURPLE + String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX))
|
||||
+ EnumChatFormatting.RESET;
|
||||
return String.valueOf(SearchField.searchParser.getRedefinedPrefix(PREFIX));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -684,7 +762,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
|
||||
root.resize(screen, parent, dropRight);
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GuiContainerManager.enable2DRender();
|
||||
GuiContainerManager.drawItems.zLevel += 100;
|
||||
|
||||
@@ -835,27 +913,27 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
|
||||
if (SubsetWidget.hoverStack != null) {
|
||||
|
||||
if (NEIClientConfig.canCheatItem(SubsetWidget.hoverStack)) {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.translate("subsets.item.cheat.key"),
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("subsets.item.cheat"));
|
||||
}
|
||||
|
||||
if (SubsetWidget.enableSearchBySubsets) {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.translate("subsets.item.search.key"),
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("subsets.item.search"));
|
||||
}
|
||||
|
||||
hotkeys.put(
|
||||
NEIClientUtils.translate("subsets.item.show.key"),
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("subsets.item.show"));
|
||||
hotkeys.put(
|
||||
NEIClientUtils.translate("subsets.item.hide.key"),
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("subsets.item.hide"));
|
||||
} else if (SubsetWidget.hoverTag != null) {
|
||||
hotkeys.put(
|
||||
@@ -864,12 +942,16 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
||||
|
||||
if (SubsetWidget.enableSearchBySubsets) {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.translate("subsets.tag.search.key"),
|
||||
NEIClientUtils.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("subsets.tag.search"));
|
||||
}
|
||||
|
||||
hotkeys.put(NEIClientUtils.translate("subsets.tag.show.key"), NEIClientUtils.translate("subsets.tag.show"));
|
||||
hotkeys.put(NEIClientUtils.translate("subsets.tag.hide.key"), NEIClientUtils.translate("subsets.tag.hide"));
|
||||
hotkeys.put(
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("subsets.tag.show"));
|
||||
hotkeys.put(
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("subsets.tag.hide"));
|
||||
}
|
||||
|
||||
return hotkeys;
|
||||
|
||||
@@ -89,7 +89,7 @@ public abstract class TextField extends Widget {
|
||||
onTextChange(oldText);
|
||||
}
|
||||
|
||||
return handled;
|
||||
return handled || keyID == Keyboard.KEY_SPACE;
|
||||
}
|
||||
|
||||
public abstract void onTextChange(String oldText);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
@@ -14,8 +15,6 @@ public abstract class Widget {
|
||||
|
||||
public void postDraw(int mx, int my) {}
|
||||
|
||||
public void postDrawTooltips(int mx, int my, List<String> tooltip) {}
|
||||
|
||||
public boolean handleClick(int mx, int my, int button) {
|
||||
return true;
|
||||
}
|
||||
@@ -38,6 +37,10 @@ public abstract class Widget {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
public void update() {}
|
||||
|
||||
public Rectangle4i bounds() {
|
||||
@@ -58,6 +61,14 @@ public abstract class Widget {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public boolean isFocused() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loseFocus() {}
|
||||
|
||||
public void gainFocus() {}
|
||||
@@ -67,4 +78,5 @@ public abstract class Widget {
|
||||
public int z;
|
||||
public int w;
|
||||
public int h;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package codechicken.nei;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
|
||||
public class WidgetContainer extends Widget {
|
||||
|
||||
protected final List<Widget> widgets = new ArrayList<>();
|
||||
protected boolean isFocused = false;
|
||||
|
||||
public void addWidget(Widget widget) {
|
||||
this.widgets.add(widget);
|
||||
}
|
||||
|
||||
public void setWidgets(List<Widget> widgets) {
|
||||
final boolean isFocused = this.isFocused;
|
||||
clear();
|
||||
loseFocus();
|
||||
|
||||
for (Widget widget : widgets) {
|
||||
addWidget(widget);
|
||||
}
|
||||
|
||||
this.isFocused = isFocused;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.widgets.clear();
|
||||
this.isFocused = false;
|
||||
}
|
||||
|
||||
public List<Widget> getWidgets() {
|
||||
return Collections.unmodifiableList(this.widgets);
|
||||
}
|
||||
|
||||
public boolean removeIf(Predicate<Widget> filter) {
|
||||
return this.widgets.removeIf(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mousex, int mousey) {
|
||||
drawWidgets(mousex, mousey);
|
||||
}
|
||||
|
||||
protected void drawWidgets(int mousex, int mousey) {
|
||||
final Rectangle4i bounds = bounds();
|
||||
|
||||
for (Widget widget : this.widgets) {
|
||||
if (widget.bounds().intersects(bounds)) {
|
||||
widget.draw(mousex, mousey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
|
||||
for (Widget widget : this.widgets) {
|
||||
widget.update();
|
||||
}
|
||||
|
||||
Collections.sort(this.widgets, Comparator.comparingInt((Widget widget) -> widget.z));
|
||||
}
|
||||
|
||||
public Widget getWidgetUnderMouse(int mousex, int mousey) {
|
||||
|
||||
if (!bounds().contains(mousex, mousey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.contains(mousex, mousey)) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int scroll, int mousex, int mousey) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
return widget != null && widget.onMouseWheel(scroll, mousex, mousey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mousex, int mousey, int button) {
|
||||
|
||||
if (contains(mousex, mousey)) {
|
||||
gainFocus();
|
||||
}
|
||||
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
return widget != null && widget.handleClick(mousex, mousey, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClickExt(int mousex, int mousey, int button) {
|
||||
loseFocus();
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (!widget.contains(mousex, mousey) && widget.handleClickExt(mousex, mousey, button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(int mousex, int mousey, int button, long heldTime) {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.mouseDragged(mousex, mousey, button, heldTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseUp(int mousex, int mousey, int button) {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.mouseUp(mousex, mousey, button);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyCode, char keyChar) {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.handleKeyPress(keyCode, keyChar)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.lastKeyTyped(keyID, keyChar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return this.isFocused;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loseFocus() {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
if (widget.isFocused()) {
|
||||
widget.loseFocus();
|
||||
}
|
||||
}
|
||||
|
||||
this.isFocused = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gainFocus() {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final Widget widget = getWidgetUnderMouse(mouse.x, mouse.y);
|
||||
|
||||
if (widget != null) {
|
||||
|
||||
if (!widget.isFocused()) {
|
||||
loseFocus();
|
||||
widget.gainFocus();
|
||||
}
|
||||
|
||||
} else {
|
||||
loseFocus();
|
||||
}
|
||||
|
||||
this.isFocused = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mousex, int mousey) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (widget != null) {
|
||||
return widget.getStackMouseOver(mousex, mousey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDraw(int mousex, int mousey) {
|
||||
|
||||
for (Widget widget : this.widgets) {
|
||||
widget.postDraw(mousex, mousey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClick(int mousex, int mousey) {
|
||||
|
||||
for (int index = this.widgets.size() - 1; index >= 0; index--) {
|
||||
final Widget widget = this.widgets.get(index);
|
||||
widget.onGuiClick(mousex, mousey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(int mousex, int mousey, List<String> tooltip) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (widget != null) {
|
||||
return widget.handleTooltip(mousex, mousey, tooltip);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mousex, int mousey, Map<String, String> hotkeys) {
|
||||
final Widget widget = getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (widget != null) {
|
||||
return widget.handleHotkeys(mousex, mousey, hotkeys);
|
||||
}
|
||||
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,20 +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) mobOverlay = (mobOverlay + 1) % 2;
|
||||
if (KeyManager.keyStates.get("world.chunkoverlay").down) {
|
||||
if (KeyManager.isPressed("world.moboverlay")) {
|
||||
mobOverlay = (mobOverlay + 1) % 2;
|
||||
NEIClientUtils
|
||||
.printChatMessage(new ChatComponentText(NEIClientUtils.translate("chat.moboverlay." + mobOverlay)));
|
||||
}
|
||||
|
||||
if (KeyManager.isPressed("world.chunkoverlay")) {
|
||||
chunkOverlay = (chunkOverlay + 1) % (NEIModContainer.isGT5Loaded() ? 4 : 3);
|
||||
// 0-2 (or 3 with gt)
|
||||
NEIClientUtils.printChatMessage(
|
||||
@@ -49,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;
|
||||
|
||||
@@ -62,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) {
|
||||
@@ -73,9 +88,11 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
}
|
||||
|
||||
World world = entity.worldObj;
|
||||
int worldHeight = world.getHeight();
|
||||
|
||||
int x1 = (int) entity.posX;
|
||||
int z1 = (int) entity.posZ;
|
||||
int y1 = (int) MathHelper.clip(entity.posY, 16, world.getHeight() - 16);
|
||||
int y1 = (int) MathHelper.clip(entity.posY, 16, worldHeight - 16);
|
||||
|
||||
for (int i = 0; i <= 32; i++) {
|
||||
int x = x1 - 16 + i;
|
||||
@@ -94,6 +111,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
|
||||
for (int k = 0; k <= 32; k++) {
|
||||
int y = y1 - 16 + k;
|
||||
|
||||
if (y >= worldHeight) {
|
||||
mobSpawnCache[bufIndex + k] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (y > maxHeight) {
|
||||
mobSpawnCache[bufIndex + k] = -1;
|
||||
break;
|
||||
@@ -104,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) {
|
||||
@@ -159,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,6 +211,10 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
|
||||
private static final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(0, 0, 0, 0, 0, 0);
|
||||
|
||||
private static byte getSpawnMode(Chunk chunk, int x, int y, int z) {
|
||||
if (y >= chunk.worldObj.getHeight()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15) >= 8
|
||||
|| !SpawnerAnimals.canCreatureTypeSpawnAtLocation(EnumCreatureType.monster, chunk.worldObj, x, y, z)) {
|
||||
return 0;
|
||||
@@ -199,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);
|
||||
@@ -217,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;
|
||||
@@ -250,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++) {
|
||||
@@ -361,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,14 +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;
|
||||
|
||||
@@ -19,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;
|
||||
@@ -31,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;
|
||||
@@ -145,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);
|
||||
@@ -164,12 +167,32 @@ public class API {
|
||||
* Add or replace the name normally shown on the item tooltip
|
||||
*/
|
||||
public static void setOverrideName(ItemStack item, String name) {
|
||||
if (!name.equals(ItemInfo.nameOverrides.get(item))) {
|
||||
ItemInfo.nameOverrides.put(item, name);
|
||||
String existing = ItemInfo.nameOverrides.get(item);
|
||||
if (!Objects.equals(name, existing)) {
|
||||
if (name == null) {
|
||||
ItemInfo.nameOverrides.remove(item);
|
||||
} else {
|
||||
ItemInfo.nameOverrides.put(item, name);
|
||||
}
|
||||
LayoutManager.markItemsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias name that this item can be found by in the search field, in addition to its display name.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -200,20 +223,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) {
|
||||
@@ -362,6 +377,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
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.inventory.Slot;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
|
||||
@@ -23,7 +24,7 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer {
|
||||
|
||||
@Override
|
||||
public void renderOverlay(GuiContainerManager gui, Slot slot) {
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(770, 1);
|
||||
|
||||
@@ -38,11 +39,7 @@ public class DefaultOverlayRenderer implements IRecipeOverlayRenderer {
|
||||
}
|
||||
|
||||
private static void drawHover(int x, int y) {
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
drawRect(x, y, 16, 16, 0x66555555);
|
||||
GL11.glPopAttrib();
|
||||
NEIClientUtils.gl2DRenderContext(() -> drawRect(x, y, 16, 16, 0x66555555));
|
||||
}
|
||||
|
||||
final IStackPositioner positioner;
|
||||
|
||||
@@ -16,7 +16,6 @@ import codechicken.nei.NEIDummySlotHandler;
|
||||
import codechicken.nei.NEIPotionGuiHandler;
|
||||
import codechicken.nei.recipe.CheatItemHandler;
|
||||
import codechicken.nei.recipe.FillFluidContainerHandler;
|
||||
import codechicken.nei.recipe.GuiRecipeCatalyst;
|
||||
import codechicken.nei.recipe.SearchInputDropHandler;
|
||||
|
||||
public class GuiInfo {
|
||||
@@ -31,7 +30,6 @@ public class GuiInfo {
|
||||
API.registerNEIGuiHandler(new NEICreativeGuiHandler());
|
||||
API.registerNEIGuiHandler(new NEIChestGuiHandler());
|
||||
API.registerNEIGuiHandler(new NEIDummySlotHandler());
|
||||
API.registerNEIGuiHandler(new GuiRecipeCatalyst(null));
|
||||
API.registerNEIGuiHandler(new SearchInputDropHandler());
|
||||
API.registerNEIGuiHandler(new FillFluidContainerHandler());
|
||||
API.registerNEIGuiHandler(new CheatItemHandler());
|
||||
@@ -53,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public interface IBookmarkContainerHandler {
|
||||
|
||||
default List<ItemStack> getStorageStacks(GuiContainer guiContainer) {
|
||||
final List<Slot> slots = guiContainer.inventorySlots.inventorySlots;
|
||||
final List<ItemStack> stacks = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < slots.size() - 4 * 9; i++) {
|
||||
if (slots.get(i).getHasStack()) {
|
||||
stacks.add(slots.get(i).getStack().copy());
|
||||
}
|
||||
}
|
||||
|
||||
return stacks;
|
||||
}
|
||||
|
||||
void pullBookmarkItemsFromContainer(GuiContainer guiContainer, ArrayList<ItemStack> realItems);
|
||||
}
|
||||
@@ -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,44 +1,48 @@
|
||||
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;
|
||||
|
||||
public interface IOverlayHandler {
|
||||
|
||||
void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean shift);
|
||||
void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean maxTransfer);
|
||||
|
||||
default int transferRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, int multiplier) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default boolean canFillCraftingGrid(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean craft(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, int multiplier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean canCraft(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) {
|
||||
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.toCollection(ArrayList::new));
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public interface IRecipeFilter {
|
||||
|
||||
public static interface IRecipeFilterProvider {
|
||||
|
||||
public IRecipeFilter getFilter();
|
||||
public IRecipeFilter getRecipeFilter();
|
||||
}
|
||||
|
||||
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||
List<PositionedStack> others);
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex);
|
||||
|
||||
}
|
||||
@@ -6,9 +6,26 @@ import net.minecraftforge.fluids.FluidStack;
|
||||
|
||||
public interface IStackStringifyHandler {
|
||||
|
||||
public NBTTagCompound convertItemStackToNBT(ItemStack stack, boolean saveStackSize);
|
||||
default NBTTagCompound convertItemStackToNBT(ItemStack stack, boolean saveStackSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ItemStack convertNBTToItemStack(NBTTagCompound nbtTag);
|
||||
default ItemStack convertNBTToItemStack(NBTTagCompound nbtTag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default FluidStack getFluid(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default ItemStack normalizeRecipeQueryStack(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default void pauseItemDamageSound(boolean pause) {}
|
||||
|
||||
default boolean isFluidDisplayItem(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public FluidStack getFluid(ItemStack stack);
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
@@ -34,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;
|
||||
@@ -58,12 +61,15 @@ 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;
|
||||
import codechicken.nei.search.ModNameFilter;
|
||||
import codechicken.nei.search.OreDictionaryFilter;
|
||||
import codechicken.nei.search.TooltipFilter;
|
||||
import codechicken.nei.util.ItemUntranslator;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
@@ -74,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();
|
||||
@@ -107,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);
|
||||
}
|
||||
@@ -121,14 +136,15 @@ public class ItemInfo {
|
||||
|
||||
public static void preInit() {
|
||||
addMobSpawnerItem();
|
||||
setupFluidDisplayItemClient();
|
||||
}
|
||||
|
||||
public static void load(World world) {
|
||||
public static void load() {
|
||||
addVanillaBlockProperties();
|
||||
addDefaultDropDowns();
|
||||
searchItems();
|
||||
parseModItems();
|
||||
ItemMobSpawner.loadSpawners(world);
|
||||
ItemMobSpawner.loadSpawners();
|
||||
addSpawnEggs();
|
||||
addInfiniteHandlers();
|
||||
addInputHandlers();
|
||||
@@ -136,6 +152,10 @@ public class ItemInfo {
|
||||
addSearchProviders();
|
||||
RecipeItemInputHandler.load();
|
||||
PresetsList.load();
|
||||
|
||||
if (NEIClientConfig.enableItemUntranslator()) {
|
||||
ItemUntranslator.getInstance().load();
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSearchProviders() {
|
||||
@@ -268,7 +288,8 @@ public class ItemInfo {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parseModItems() {
|
||||
HashMap<String, ItemStackSet> modSubsets = new HashMap<>();
|
||||
final Map<String, ItemStackSet> modSubsets = new HashMap<>();
|
||||
|
||||
for (Item item : (Iterable<Item>) Item.itemRegistry) {
|
||||
UniqueIdentifier ident = null;
|
||||
try {
|
||||
@@ -280,14 +301,12 @@ public class ItemInfo {
|
||||
continue;
|
||||
}
|
||||
|
||||
String modId = ident.modId;
|
||||
itemOwners.put(item, modId);
|
||||
ItemStackSet itemset = modSubsets.get(modId);
|
||||
if (itemset == null) modSubsets.put(modId, itemset = new ItemStackSet());
|
||||
itemset.with(item);
|
||||
itemOwners.put(item, ident.modId);
|
||||
modSubsets.computeIfAbsent(ident.modId, i -> new ItemStackSet()).with(item);
|
||||
}
|
||||
|
||||
API.addSubset("Mod.Minecraft", modSubsets.remove("minecraft"));
|
||||
|
||||
for (Entry<String, ItemStackSet> entry : modSubsets.entrySet()) {
|
||||
ModContainer mc = FMLCommonHandler.instance().findContainerFor(entry.getKey());
|
||||
if (mc == null) NEIClientConfig.logger.error("Missing container for " + entry.getKey());
|
||||
@@ -303,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());
|
||||
@@ -323,44 +347,29 @@ public class ItemInfo {
|
||||
}
|
||||
|
||||
private static void addDefaultDropDowns() {
|
||||
final Item mob_spawner = Item.getItemFromBlock(Blocks.mob_spawner);
|
||||
API.addSubset("Items", item -> Block.getBlockFromItem(item.getItem()) == Blocks.air);
|
||||
API.addSubset("Blocks", item -> Block.getBlockFromItem(item.getItem()) != Blocks.air);
|
||||
API.addSubset("Blocks.MobSpawners", ItemStackSet.of(Blocks.mob_spawner));
|
||||
API.addSubset("Blocks.MobSpawners", item -> item.getItem() == mob_spawner);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void searchItems() {
|
||||
ItemStackSet tools = new ItemStackSet();
|
||||
ItemStackSet picks = new ItemStackSet();
|
||||
ItemStackSet shovels = new ItemStackSet();
|
||||
ItemStackSet axes = new ItemStackSet();
|
||||
ItemStackSet hoes = new ItemStackSet();
|
||||
ItemStackSet swords = new ItemStackSet();
|
||||
ItemStackSet chest = new ItemStackSet();
|
||||
ItemStackSet helmets = new ItemStackSet();
|
||||
ItemStackSet legs = new ItemStackSet();
|
||||
ItemStackSet boots = new ItemStackSet();
|
||||
ItemStackSet other = new ItemStackSet();
|
||||
ItemStackSet ranged = new ItemStackSet();
|
||||
ItemStackSet food = new ItemStackSet();
|
||||
ItemStackSet potioningredients = new ItemStackSet();
|
||||
|
||||
ArrayList<ItemStackSet> creativeTabRanges = new ArrayList<>(CreativeTabs.creativeTabArray.length);
|
||||
List<ItemStack> stackList = new LinkedList<>();
|
||||
final Map<Integer, ItemStackSet> creativeTabRanges = new HashMap<>();
|
||||
final List<ItemStack> stackList = new LinkedList<>();
|
||||
|
||||
for (Item item : (Iterable<Item>) Item.itemRegistry) {
|
||||
if (item == null) continue;
|
||||
|
||||
for (CreativeTabs itemTab : item.getCreativeTabs()) {
|
||||
if (itemTab != null) {
|
||||
while (itemTab.getTabIndex() >= creativeTabRanges.size()) creativeTabRanges.add(null);
|
||||
ItemStackSet set = creativeTabRanges.get(itemTab.getTabIndex());
|
||||
if (set == null) creativeTabRanges.set(itemTab.getTabIndex(), set = new ItemStackSet());
|
||||
final ItemStackSet itemset = creativeTabRanges
|
||||
.computeIfAbsent(itemTab.getTabIndex(), i -> new ItemStackSet());
|
||||
|
||||
try {
|
||||
stackList.clear();
|
||||
item.getSubItems(item, itemTab, stackList);
|
||||
for (ItemStack stack : stackList) set.add(stack);
|
||||
itemset.addAll(stackList);
|
||||
} catch (Exception e) {
|
||||
NEIClientConfig.logger
|
||||
.error("Error loading sub-items for: " + item + ". Tab: " + itemTab.getTabLabel(), e);
|
||||
@@ -368,41 +377,13 @@ public class ItemInfo {
|
||||
}
|
||||
}
|
||||
|
||||
if (item.isDamageable()) {
|
||||
tools.with(item);
|
||||
if (item instanceof ItemPickaxe) picks.with(item);
|
||||
else if (item instanceof ItemSpade) shovels.with(item);
|
||||
else if (item instanceof ItemAxe) axes.with(item);
|
||||
else if (item instanceof ItemHoe) hoes.with(item);
|
||||
else if (item instanceof ItemSword) swords.with(item);
|
||||
else if (item instanceof ItemArmor) switch (((ItemArmor) item).armorType) {
|
||||
case 0:
|
||||
helmets.with(item);
|
||||
break;
|
||||
case 1:
|
||||
chest.with(item);
|
||||
break;
|
||||
case 2:
|
||||
legs.with(item);
|
||||
break;
|
||||
case 3:
|
||||
boots.with(item);
|
||||
break;
|
||||
}
|
||||
else if (item == Items.arrow || item == Items.bow) ranged.with(item);
|
||||
else if (item == Items.fishing_rod || item == Items.flint_and_steel || item == Items.shears)
|
||||
other.with(item);
|
||||
}
|
||||
|
||||
if (item instanceof ItemFood) food.with(item);
|
||||
|
||||
try {
|
||||
LinkedList<ItemStack> subItems = new LinkedList<>();
|
||||
final List<ItemStack> subItems = new LinkedList<>();
|
||||
item.getSubItems(item, null, subItems);
|
||||
|
||||
for (ItemStack stack : subItems) {
|
||||
if (item.isPotionIngredient(stack) && item.getPotionEffect(stack) != null) {
|
||||
BrewingRecipeHandler.ingredients.add(stack);
|
||||
potioningredients.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,25 +391,34 @@ public class ItemInfo {
|
||||
NEIClientConfig.logger.error("Error loading brewing ingredients for: " + item, e);
|
||||
}
|
||||
}
|
||||
API.addSubset("Items.Tools.Pickaxes", picks);
|
||||
API.addSubset("Items.Tools.Shovels", shovels);
|
||||
API.addSubset("Items.Tools.Axes", axes);
|
||||
API.addSubset("Items.Tools.Hoes", hoes);
|
||||
API.addSubset("Items.Tools.Other", other);
|
||||
API.addSubset("Items.Weapons.Swords", swords);
|
||||
API.addSubset("Items.Weapons.Ranged", ranged);
|
||||
API.addSubset("Items.Armor.Chestplates", chest);
|
||||
API.addSubset("Items.Armor.Leggings", legs);
|
||||
API.addSubset("Items.Armor.Helmets", helmets);
|
||||
API.addSubset("Items.Armor.Boots", boots);
|
||||
API.addSubset("Items.Food", food);
|
||||
API.addSubset("Items.Potions.Ingredients", potioningredients);
|
||||
|
||||
API.addSubset("Items.Tools.Pickaxes", item -> item.getItem() instanceof ItemPickaxe);
|
||||
API.addSubset("Items.Tools.Shovels", item -> item.getItem() instanceof ItemSpade);
|
||||
API.addSubset("Items.Tools.Axes", item -> item.getItem() instanceof ItemAxe);
|
||||
API.addSubset("Items.Tools.Hoes", item -> item.getItem() instanceof ItemHoe);
|
||||
API.addSubset(
|
||||
"Items.Tools.Other",
|
||||
item -> item.getItem() == Items.fishing_rod || item.getItem() == Items.flint_and_steel
|
||||
|| item.getItem() == Items.shears);
|
||||
API.addSubset("Items.Weapons.Swords", item -> item.getItem() instanceof ItemSword);
|
||||
API.addSubset("Items.Weapons.Ranged", item -> item.getItem() == Items.arrow || item.getItem() == Items.bow);
|
||||
API.addSubset("Items.Armor.Helmets", item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 0);
|
||||
API.addSubset(
|
||||
"Items.Armor.Chestplates",
|
||||
item -> item.getItem() instanceof ItemArmor armor && armor.armorType == 1);
|
||||
API.addSubset(
|
||||
"Items.Armor.Leggings",
|
||||
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) {
|
||||
if (tab.getTabIndex() >= creativeTabRanges.size()) continue;
|
||||
ItemStackSet set = creativeTabRanges.get(tab.getTabIndex());
|
||||
if (set != null && !set.isEmpty())
|
||||
API.addSubset("CreativeTabs." + I18n.format(tab.getTranslatedTabLabel()), set);
|
||||
final ItemStackSet itemset = creativeTabRanges.get(tab.getTabIndex());
|
||||
if (itemset != null && !itemset.isEmpty()) {
|
||||
API.addSubset("CreativeTabs." + I18n.format(tab.getTranslatedTabLabel()), itemset);
|
||||
}
|
||||
}
|
||||
|
||||
BrewingRecipeHandler.searchPotions();
|
||||
@@ -451,30 +441,38 @@ public class ItemInfo {
|
||||
Block mouseoverBlock = world.getBlock(x, y, z);
|
||||
|
||||
ArrayList<ItemStack> items = new ArrayList<>();
|
||||
|
||||
ArrayList<IHighlightHandler> handlers = new ArrayList<>();
|
||||
|
||||
if (highlightIdentifiers.containsKey(null)) handlers.addAll(highlightIdentifiers.get(null));
|
||||
if (highlightIdentifiers.containsKey(mouseoverBlock)) handlers.addAll(highlightIdentifiers.get(mouseoverBlock));
|
||||
|
||||
for (IHighlightHandler ident : handlers) {
|
||||
ItemStack item = ident.identifyHighlight(world, player, hit);
|
||||
final ItemStack item = ident.identifyHighlight(world, player, hit);
|
||||
if (item != null) items.add(item);
|
||||
}
|
||||
|
||||
if (items.size() > 0) return items;
|
||||
if (!items.isEmpty()) return items;
|
||||
|
||||
ItemStack pick = mouseoverBlock.getPickBlock(hit, world, x, y, z, player);
|
||||
if (pick != null) items.add(pick);
|
||||
try {
|
||||
final ItemStack pick = mouseoverBlock.getPickBlock(hit, world, x, y, z, player);
|
||||
if (pick != null) items.add(pick);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (!items.isEmpty()) return items;
|
||||
|
||||
if (mouseoverBlock instanceof IShearable shearable) {
|
||||
if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z)) {
|
||||
items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (!items.isEmpty()) return items;
|
||||
|
||||
try {
|
||||
items.addAll(mouseoverBlock.getDrops(world, x, y, z, world.getBlockMetadata(x, y, z), 0));
|
||||
} catch (Exception ignored) {}
|
||||
if (mouseoverBlock instanceof IShearable) {
|
||||
IShearable shearable = (IShearable) mouseoverBlock;
|
||||
if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z))
|
||||
items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0));
|
||||
}
|
||||
|
||||
if (items.size() == 0) items.add(0, new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z)));
|
||||
if (items.isEmpty()) items.add(new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z)));
|
||||
|
||||
return items;
|
||||
}
|
||||
@@ -487,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();
|
||||
|
||||
@@ -1,104 +1,179 @@
|
||||
package codechicken.nei.api;
|
||||
|
||||
import static codechicken.lib.gui.GuiDraw.getMousePosition;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import codechicken.nei.ItemPanel.ItemPanelSlot;
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
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.recipe.BookmarkRecipeId;
|
||||
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.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.recipe.chain.RecipeChainMath;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public abstract class ShortcutInputHandler {
|
||||
|
||||
private static class HotkeysCache {
|
||||
|
||||
public int mousex;
|
||||
public int mousey;
|
||||
public ItemStack stack;
|
||||
public int groupId;
|
||||
public Map<String, String> hotkeys = new HashMap<>();
|
||||
|
||||
public boolean matches(int mousex, int mousey, ItemStack stack, int groupId) {
|
||||
return Math.abs(this.mousex - mousex) < 16 && Math.abs(this.mousey - mousey) < 16
|
||||
&& (this.stack == null ? stack == null && this.groupId == groupId
|
||||
: stack != null && NEIClientUtils.areStacksSameTypeWithNBT(stack, this.stack));
|
||||
}
|
||||
|
||||
public void update(int mousex, int mousey, ItemStack stack, int groupId, Map<String, String> hotkeys) {
|
||||
this.mousex = mousex;
|
||||
this.mousey = mousey;
|
||||
this.stack = stack != null ? stack.copy() : null;
|
||||
this.groupId = groupId;
|
||||
this.hotkeys = hotkeys;
|
||||
}
|
||||
}
|
||||
|
||||
private static HotkeysCache hotkeysCache = new HotkeysCache();
|
||||
|
||||
public static boolean handleKeyEvent(ItemStack stackover) {
|
||||
|
||||
if (!NEIClientConfig.isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.overlay_hide")) {
|
||||
return hideOverlayRecipe();
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items")) {
|
||||
return ItemPanels.bookmarkPanel.pullBookmarkItems(ItemPanels.bookmarkPanel.getHoveredGroupId(true), false);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_pull_items_ingredients")) {
|
||||
return ItemPanels.bookmarkPanel.pullBookmarkItems(ItemPanels.bookmarkPanel.getHoveredGroupId(true), true);
|
||||
}
|
||||
|
||||
if (stackover == null && NEIClientConfig.isKeyHashDown("gui.remove_recipe")) {
|
||||
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
|
||||
|
||||
if (groupId != -1) {
|
||||
ItemPanels.bookmarkPanel.removeGroup(groupId);
|
||||
return true;
|
||||
}
|
||||
if (KeyManager.isKeyDown("gui.overlay") && stackover == null && LayoutManager.overlayRenderer != null) {
|
||||
LayoutManager.overlayRenderer = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stackover == null) {
|
||||
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
|
||||
|
||||
if (groupId != -1) {
|
||||
|
||||
if (KeyManager.isHashDown("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH)) {
|
||||
ItemPanels.bookmarkPanel.removeGroup(groupId);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (KeyManager.isKeyDown("bookmark.pull_items")) {
|
||||
return ItemPanels.bookmarkPanel.pullBookmarkItems(groupId, NEIClientUtils.shiftKey());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (math != null) {
|
||||
|
||||
if (!NEIClientUtils.controlKey()) {
|
||||
autocraftingIgnoreInventory(math);
|
||||
}
|
||||
|
||||
AutoCraftingManager.runProcessing(math);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
stackover = stackover.copy();
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.overlay")) {
|
||||
return openOverlayRecipe(stackover, false);
|
||||
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_oredict")) {
|
||||
if (KeyManager.isHashDown("copy.identifier", NEIClientUtils.CTRL_HASH)) {
|
||||
return copyItemStackID(stackover);
|
||||
}
|
||||
|
||||
if (KeyManager.isHashDown("copy.oredict", NEIClientUtils.CTRL_HASH)) {
|
||||
return copyItemStackOreDictionary(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.overlay_use")) {
|
||||
return openOverlayRecipe(stackover, true);
|
||||
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.bookmark")) {
|
||||
return saveRecipeInBookmark(stackover, false, false);
|
||||
if (KeyManager.isHashDown("bookmark.favorite", NEIClientUtils.SHIFT_HASH)) {
|
||||
return saveFavoriteTree(stackover);
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe")) {
|
||||
return saveRecipeInBookmark(stackover, true, false);
|
||||
if (KeyManager.isKeyDown("bookmark.add")) {
|
||||
return saveRecipeInBookmark(stackover, NEIClientUtils.shiftKey(), NEIClientUtils.controlKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_count")) {
|
||||
return saveRecipeInBookmark(stackover, false, true);
|
||||
if (KeyManager.isKeyDown("bookmark.pull_items")) {
|
||||
return pullRecipeItems(stackover, NEIClientUtils.shiftKey());
|
||||
}
|
||||
|
||||
if (NEIClientConfig.isKeyHashDown("gui.bookmark_recipe_count")) {
|
||||
return saveRecipeInBookmark(stackover, true, true);
|
||||
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
|
||||
&& NEIClientUtils.shiftKey()) {
|
||||
return runAutoCrafting(stackover, !NEIClientUtils.controlKey());
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -114,28 +189,59 @@ public abstract class ShortcutInputHandler {
|
||||
final int button = Mouse.getEventButton();
|
||||
|
||||
if (button == 0) {
|
||||
return GuiCraftingRecipe.openRecipeGui("item", stackover);
|
||||
return GuiCraftingRecipe.openRecipeGui("item", stackover.copy());
|
||||
} else if (button == 1) {
|
||||
return GuiUsageRecipe.openRecipeGui("item", stackover);
|
||||
return GuiUsageRecipe.openRecipeGui("item", stackover.copy());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hideOverlayRecipe() {
|
||||
private static boolean copyItemStackName(ItemStack stackover) {
|
||||
GuiScreen.setClipboardString(SearchField.getEscapedSearchText(stackover));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LayoutManager.overlayRenderer != null) {
|
||||
LayoutManager.overlayRenderer = null;
|
||||
private static boolean copyItemStackID(ItemStack stackover) {
|
||||
if (stackover == null || stackover.getItem() == null) return false;
|
||||
GuiScreen.setClipboardString(
|
||||
stackover.getItem().delegate.name()
|
||||
+ (stackover.getItemDamage() != 0 ? "/" + stackover.getItemDamage() : ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean sendRecipeInChatLink(ItemStack stackover, boolean saveIngredients) {
|
||||
final Point mousePos = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousePos.x, mousePos.y);
|
||||
|
||||
if (slot != null && saveIngredients && slot.getType() == BookmarkItemType.RESULT) {
|
||||
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(slot.getBookmarkItem()).toNBT());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
|
||||
|
||||
private static boolean copyItemStackName(ItemStack stackover) {
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||
.setContents(new StringSelection(stackover.getDisplayName()), null);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -153,56 +259,506 @@ public abstract class ShortcutInputHandler {
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
}
|
||||
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(builder.toString()), null);
|
||||
GuiScreen.setClipboardString(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean openOverlayRecipe(ItemStack stackover, boolean shift) {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
|
||||
if (gui == null || gui instanceof GuiRecipe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Point mouseover = getMousePosition();
|
||||
ItemPanelSlot panelSlot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
BookmarkRecipeId recipeId = null;
|
||||
|
||||
if (panelSlot != null) {
|
||||
recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(panelSlot.slotIndex);
|
||||
} else {
|
||||
recipeId = ItemPanels.bookmarkPanel.getBookmarkRecipeId(stackover);
|
||||
}
|
||||
private static boolean runAutoCrafting(ItemStack stackover, boolean ignoreInventory) {
|
||||
final RecipeId recipeId = GuiCraftingRecipe.getRecipeId(NEIClientUtils.getGuiContainer(), stackover);
|
||||
|
||||
if (recipeId != null) {
|
||||
return GuiCraftingRecipe.overlayRecipe(stackover, recipeId, shift);
|
||||
final Point mouseover = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
RecipeChainMath math = null;
|
||||
|
||||
if (slot == null) {
|
||||
final Recipe recipe = Recipe.of(recipeId);
|
||||
if (recipe != null) {
|
||||
math = RecipeChainMath.of(recipe, 1);
|
||||
}
|
||||
} else if (slot.getRecipeId() != null && slot.getType() != BookmarkItemType.INGREDIENT) {
|
||||
|
||||
if (slot.getGroup().crafting == null) {
|
||||
final Recipe recipe = Recipe.of(slot.getRecipeId());
|
||||
if (recipe != null) {
|
||||
math = RecipeChainMath.of(recipe, Math.max(1, slot.getMultiplier()));
|
||||
}
|
||||
} else {
|
||||
math = ItemPanels.bookmarkPanel.getGrid()
|
||||
.createRecipeChainMath(slot.getGroupId(), slot.getRecipeId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (math != null) {
|
||||
|
||||
if (ignoreInventory) {
|
||||
autocraftingIgnoreInventory(math);
|
||||
}
|
||||
|
||||
AutoCraftingManager.runProcessing(math);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean saveRecipeInBookmark(ItemStack stack, boolean saveIngredients, boolean saveStackSize) {
|
||||
private static boolean openOverlayRecipe(ItemStack stackover) {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
final RecipeId recipeId = GuiCraftingRecipe.getRecipeId(gui, stackover);
|
||||
|
||||
if (stack != null) {
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
List<PositionedStack> ingredients = null;
|
||||
String handlerName = "";
|
||||
if (recipeId != null) {
|
||||
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId);
|
||||
|
||||
if (gui instanceof GuiRecipe) {
|
||||
ingredients = ((GuiRecipe<?>) gui).getFocusedRecipeIngredients();
|
||||
handlerName = ((GuiRecipe<?>) gui).getHandlerName();
|
||||
stack.stackSize = ((GuiRecipe<?>) gui).prepareFocusedRecipeResultStackSize(stack);
|
||||
if (handlerRef != null) {
|
||||
|
||||
if (NEIClientUtils.shiftKey() && handlerRef.canFillCraftingGrid(gui)) {
|
||||
int multiplier = 0;
|
||||
|
||||
if (NEIClientUtils.controlKey()) {
|
||||
final Point mouseover = GuiDraw.getMousePosition();
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel
|
||||
.getSlotMouseOver(mouseover.x, mouseover.y);
|
||||
multiplier = slot != null ? (int) Math.min(64, slot.getMultiplier()) : 1;
|
||||
}
|
||||
|
||||
handlerRef.fillCraftingGrid(gui, multiplier);
|
||||
} else if (!NEIClientUtils.shiftKey() && handlerRef.canUseOverlayRenderer(gui)) {
|
||||
handlerRef.useOverlayRenderer(gui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean saveFavoriteTree(ItemStack stackover) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
|
||||
if (ItemPanels.itemPanel.contains(mouse.x, mouse.y)
|
||||
|| ItemPanels.itemPanel.historyPanel.contains(mouse.x, mouse.y)) {
|
||||
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(FavoriteRecipes.getFavorite(stackover));
|
||||
|
||||
if (handlerRef != null) {
|
||||
final GuiFavoriteButton button = new GuiFavoriteButton(handlerRef, 0, 0);
|
||||
button.saveRecipeInBookmark();
|
||||
return true;
|
||||
}
|
||||
|
||||
ItemPanels.bookmarkPanel.addOrRemoveItem(stack, handlerName, ingredients, saveIngredients, saveStackSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Map<String, String> handleHotkeys(GuiContainer gui, int mousex, int mousey, ItemStack stack) {
|
||||
return new HashMap<>();
|
||||
private static boolean saveRecipeInBookmark(ItemStack stackover, boolean saveIngredients, boolean saveStackSize) {
|
||||
final Point mousePos = GuiDraw.getMousePosition();
|
||||
|
||||
// If no slot was removed from the bookmark panel on click:
|
||||
if (!ItemPanels.bookmarkPanel.removeSlot(mousePos.x, mousePos.y, saveIngredients)) {
|
||||
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
|
||||
|
||||
// --- Case 1: saving a recipe (with ingredients) ---
|
||||
if (recipe != null && saveIngredients) {
|
||||
|
||||
// If the recipe is not already bookmarked, add it
|
||||
if (!ItemPanels.bookmarkPanel.removeRecipe(recipe.getRecipeId(), BookmarkGrid.DEFAULT_GROUP_ID)) {
|
||||
final Recipe singleRecipe = Recipe.of(
|
||||
Arrays.asList(recipe.getResult(stackover)),
|
||||
recipe.getHandlerName(),
|
||||
recipe.getIngredients());
|
||||
singleRecipe.setCustomRecipeId(recipe.getRecipeId());
|
||||
|
||||
ItemPanels.bookmarkPanel.addRecipe(
|
||||
singleRecipe,
|
||||
// Determine if stack size should be saved
|
||||
saveStackSize
|
||||
|| NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkRecipeWithCount")
|
||||
? 1
|
||||
: 0,
|
||||
BookmarkGrid.DEFAULT_GROUP_ID);
|
||||
}
|
||||
|
||||
// --- Case 2: saving an item only ---
|
||||
} else {
|
||||
final RecipeId recipeId = recipe != null ? recipe.getRecipeId() : null;
|
||||
|
||||
// Determine if the item is associated with a recipe and should be saved with it
|
||||
final boolean existsRecipe = recipeId != null
|
||||
&& ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID)
|
||||
|| NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkItemsWithRecipe");
|
||||
|
||||
// If the item was not removed from the bookmarks, add it
|
||||
if (!ItemPanels.bookmarkPanel
|
||||
.removeItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID)) {
|
||||
|
||||
// Determine whether to preserve the item count
|
||||
saveStackSize = saveStackSize || saveIngredients
|
||||
|| ItemPanels.bookmarkPanel.existsRecipe(recipeId, BookmarkGrid.DEFAULT_GROUP_ID);
|
||||
|
||||
if (!saveStackSize) {
|
||||
stackover = StackInfo.withAmount(stackover, 0);
|
||||
} else if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
|
||||
}
|
||||
|
||||
ItemPanels.bookmarkPanel
|
||||
.addItem(stackover, existsRecipe ? recipeId : null, BookmarkGrid.DEFAULT_GROUP_ID);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
recipe = guiRecipe.getFocusedRecipe();
|
||||
}
|
||||
|
||||
return recipe;
|
||||
}
|
||||
|
||||
private static boolean pullRecipeItems(ItemStack stackover, boolean shift) {
|
||||
final Point mousePos = GuiDraw.getMousePosition();
|
||||
|
||||
if (ItemPanels.bookmarkPanel.contains(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemPanels.bookmarkPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y);
|
||||
} else if (ItemPanels.itemPanel.contains(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemPanels.itemPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y);
|
||||
} else if (ItemPanels.itemPanel.historyPanel.contains(mousePos.x, mousePos.y)) {
|
||||
stackover = ItemPanels.itemPanel.historyPanel.getStackMouseOverWithQuantity(mousePos.x, mousePos.y);
|
||||
} else {
|
||||
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
|
||||
}
|
||||
|
||||
final RecipeChainMath math = RecipeChainMath
|
||||
.of(Arrays.asList(BookmarkItem.of(-3, stackover)), Collections.emptySet());
|
||||
return ItemPanels.bookmarkPanel.pullBookmarkItems(math, shift);
|
||||
}
|
||||
|
||||
public static Map<String, String> handleHotkeys(int mousex, int mousey, ItemStack stack) {
|
||||
final int groupId = ItemPanels.bookmarkPanel.getHoveredGroupId(true);
|
||||
|
||||
if (hotkeysCache.matches(mousex, mousey, stack, groupId)) {
|
||||
return hotkeysCache.hotkeys;
|
||||
}
|
||||
|
||||
final GuiContainer gui = NEIClientUtils.getGuiContainer();
|
||||
final Map<String, String> hotkeys = new HashMap<>();
|
||||
|
||||
if (groupId != -1) {
|
||||
final boolean isCraftingMode = ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId);
|
||||
|
||||
hotkeys.put(
|
||||
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(
|
||||
KeyManager.getKeyName("bookmark.pull_items"),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
|
||||
}
|
||||
|
||||
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(
|
||||
KeyManager.getKeyName("gui.craft_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.craft_all"));
|
||||
}
|
||||
|
||||
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousex, mousey);
|
||||
final RecipeId recipeId = getHotkeyRecipeId(gui, mousex, mousey, stack, slot);
|
||||
|
||||
if (slot != null) {
|
||||
final BookmarkGroup group = slot.getGroup();
|
||||
|
||||
if (group.crafting != null && group.collapsed) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.remove_recipe"));
|
||||
} else if (slot.getRecipeId() == null || slot.getType() == BookmarkItemType.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(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
|
||||
if (slot.getType() == BookmarkItemType.INGREDIENT) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_item"));
|
||||
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
final BookmarkItem item = slot.getBookmarkItem();
|
||||
|
||||
if (ItemPanels.bookmarkPanel.getGrid().createChainItems(groupId).values().stream()
|
||||
.noneMatch(
|
||||
m -> m.type == BookmarkItemType.RESULT && !m.equals(item)
|
||||
&& item.equalsRecipe(m))) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add"),
|
||||
NEIClientUtils.translate("bookmark.remove_item"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.remove_recipe"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.pull_items"),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.pull_items", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("bookmark.group.pull_items_shift"));
|
||||
}
|
||||
|
||||
} else {
|
||||
hotkeys.put(KeyManager.getKeyName("bookmark.add"), NEIClientUtils.translate("bookmark.add_item"));
|
||||
|
||||
if (StackInfo.getAmount(stack) > 0) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.add_item_with_count"));
|
||||
}
|
||||
|
||||
if (recipeId != null) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("bookmark.add_item_with_recipe_and_count"));
|
||||
hotkeys.put(
|
||||
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(
|
||||
KeyManager.getKeyName("bookmark.favorite", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("recipe.favorite.bookmark_recipe"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hotkeys.put(KeyManager.getKeyName("recipe.recipe"), NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(KeyManager.getKeyName("recipe.usage"), NEIClientUtils.translate("itempanel.open_usage"));
|
||||
|
||||
hotkeys.put(
|
||||
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(
|
||||
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName(NEIClientUtils.SHIFT_HASH, NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("itempanel.open_usage"));
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName(NEIClientUtils.CTRL_HASH, NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("itempanel.infinite_item"));
|
||||
} else {
|
||||
hotkeys.put(
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_LMB),
|
||||
NEIClientUtils.translate("itempanel.open_crafting"));
|
||||
hotkeys.put(
|
||||
NEIMouseUtils.getKeyName(NEIMouseUtils.MOUSE_BTN_RMB),
|
||||
NEIClientUtils.translate("itempanel.open_usage"));
|
||||
}
|
||||
|
||||
if (recipeId != null) {
|
||||
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId);
|
||||
|
||||
if (handlerRef != null) {
|
||||
|
||||
if (handlerRef.canUseOverlayRenderer(gui)) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("gui.overlay"),
|
||||
NEIClientUtils.translate("itempanel.overlay_recipe"));
|
||||
}
|
||||
|
||||
if (handlerRef.canFillCraftingGrid(gui)) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("gui.overlay", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("itempanel.fill_crafting_grid"));
|
||||
|
||||
if (handlerRef.allowedTransferAlghoritm(gui)) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName(
|
||||
"gui.overlay",
|
||||
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.fill_crafting_grid_quantity"));
|
||||
}
|
||||
}
|
||||
|
||||
if (NEIClientConfig.autocraftingEnabled()) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName(
|
||||
"gui.craft_items",
|
||||
NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
|
||||
NEIClientUtils.translate("itempanel.craft_missing"));
|
||||
hotkeys.put(
|
||||
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);
|
||||
|
||||
return hotkeys;
|
||||
}
|
||||
|
||||
private static void autocraftingIgnoreInventory(RecipeChainMath math) {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final List<ItemStack> playerInventory = AutoCraftingManager.getInventoryItems(guiContainer).values();
|
||||
final RecipeId rootRecipeId = math.createMasterRoot();
|
||||
|
||||
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) {
|
||||
if (NEIClientUtils.areStacksSameTypeCraftingWithNBT(stack, item.itemStack)) {
|
||||
amount += StackInfo.getAmount(stack);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,7 +56,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 +83,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(
|
||||
@@ -105,6 +105,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(
|
||||
@@ -413,7 +440,7 @@ public class NEITransformer implements IClassTransformer {
|
||||
if (name.equals("net.minecraftforge.oredict.OreDictionary")) {
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
cr.accept(new OreDictionaryVisitor(ASM5, cw), ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
cr.accept(new OreDictionaryVisitor(ASM5, cw), 0);
|
||||
bytes = cw.toByteArray();
|
||||
}
|
||||
bytes = transformSubclasses(name, bytes);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 281 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user