Compare commits

...

201 Commits

Author SHA1 Message Date
ddab73bad1
Merge branch '1.21' into worldgen-placement-providers 2025-03-29 18:20:37 +07:00
3e5f47c9a3
Fix matter reconstructor being a downgrade regarding energy values compared to replicator 2025-03-29 17:12:49 +07:00
f57ccafcfa
Fix player not getting advancements for items picked up into exopack inventory 2025-03-29 17:06:01 +07:00
f82c7977fa
Fix being unable to drain sub 1-xp point liquid xp 2025-03-29 14:11:17 +07:00
640aeabb07
Invalidate recipe caches on resources reload 2025-03-29 14:06:38 +07:00
c98a1573aa
ooprs!!1 2025-03-29 13:47:39 +07:00
072d2187f8
Specify default cache size to 16384 entries, and bump cache size for matter entangler 2025-03-29 13:39:56 +07:00
2d1c9184f4
Fix platform declaration clash 2025-03-29 13:37:27 +07:00
c93fa7bb7a
Weaken type argument in AutomationFilters 2025-03-29 13:37:14 +07:00
4db52b83c4
Since /reload reloads everything, we can cache isIncomplete 2025-03-29 13:34:43 +07:00
571c7bc339
Passing Int.MAX_VALUE to simple container slot factory will make its slots ignore max stack size completely 2025-03-29 13:33:07 +07:00
f5e21f5582
Fix filter deserialization 2025-03-29 12:41:55 +07:00
8454db6785
Some fixes for item filters 2025-03-29 12:07:32 +07:00
78fad5d3cc
Initial implementation for improved item filters 2025-03-29 10:55:32 +07:00
3e593748f7
Remove dummy filtered slot implementation since it is no longer required 2025-03-27 07:17:28 +07:00
3fab525345
Merge branch '1.21' into new-container-api 2025-03-26 23:00:26 +07:00
af24f391df
Fix blocks without menu still behaving like being interacted with clientside 2025-03-26 23:00:18 +07:00
10421570be
forgor about gauge updates when container contents changes in matter capacitor bank 2025-03-26 22:51:42 +07:00
4d51ed5210
Optimize battery bank 2025-03-26 22:50:13 +07:00
39a996f79f
Fix matter capacitor bank not marking chunks as dirty 2025-03-26 22:40:44 +07:00
a26840f670
sumOfDecimal 2025-03-26 22:38:27 +07:00
5281a5aeee
Remove jvm overloads from matter capacitor bank menu 2025-03-26 22:38:18 +07:00
750f941525
Optimize matter capacitor bank 2025-03-26 22:38:02 +07:00
7405989b97
Merge branch '1.21' into new-container-api 2025-03-26 22:21:11 +07:00
140bdab59c
Fix swapped out condition checks in nextDecimal 2025-03-26 22:21:02 +07:00
c2f20a76f6
Merge branch '1.21' into new-container-api 2025-03-26 22:14:39 +07:00
a3ed886900
Fix wrong matter recycler receive values 2025-03-26 22:11:37 +07:00
1087e755ff
And now, proper random decimals implementation 2025-03-26 22:09:40 +07:00
c965bcdc82
Move decimal sampling to random utils 2025-03-26 19:36:52 +07:00
5663b262eb
Move random extensions to RandomUtils file 2025-03-26 19:34:22 +07:00
e93c766edb
Move stuff from "Decimal" to appropriate places 2025-03-26 19:16:30 +07:00
85dde24e59
Update energy counter menu code 2025-03-26 19:06:39 +07:00
4fc646a13a
Energy counter "pull" mode 2025-03-26 18:52:26 +07:00
9f8e7693a8
Add IEnhancedContainer specific optimization 2025-03-23 01:28:06 +07:00
65bbc56014
Merge branch '1.21' into new-container-api 2025-03-23 01:25:48 +07:00
524c36aade
oops 2025-03-22 19:09:59 +07:00
faba736bd7
Considerably improve upgrade container performance 2025-03-22 17:10:26 +07:00
17dfa953db
damn 2025-03-22 15:28:11 +07:00
d17e64f335
Fix slotted container has empty bitmap after deserialization 2025-03-22 15:10:17 +07:00
eed3b04555
Make matter bottler actually consume energy 2025-03-22 15:07:05 +07:00
6cf218a184
Considerably buff matter bottler 2025-03-22 15:06:56 +07:00
45181c9e55
Merge branch '1.21' into new-container-api 2025-03-22 00:31:35 +07:00
01d3cbf7b2
Add "Smart exchange" button to quickstack controls 2025-03-21 21:58:56 +07:00
be2011cf96
Merge branch '1.21' into new-container-api 2025-03-21 21:24:26 +07:00
9413ae5f2b
Shrink effect list panel so it no longer overlaps "quick stack" controls 2025-03-21 21:17:50 +07:00
dc73ac7b55
Merge branch '1.21' into new-container-api 2025-03-21 21:11:30 +07:00
a6361f10f2
Final improvements to quick stack controls 2025-03-21 18:35:25 +07:00
f16b003cdf
Merge branch '1.21' into new-container-api 2025-03-21 18:18:18 +07:00
07e0c73479
Fixes for quick stack controls 2025-03-21 15:19:07 +07:00
2547478914
Merge branch '1.21' into new-container-api 2025-03-21 15:11:17 +07:00
dcff861e71
More quickstack controls 2025-03-21 14:31:59 +07:00
9be8596a5f
Merge branch '1.21' into new-container-api 2025-03-21 14:05:50 +07:00
29c51617b9
Merge branch '1.21' into new-container-api 2025-03-21 13:52:56 +07:00
70ed0cfcba
Merge branch '1.21' into new-container-api 2025-03-21 10:46:03 +07:00
65bf4dc9d0
Merge branch '1.21' into new-container-api 2025-03-21 08:07:52 +07:00
5b1ae12f85
Prioritize slots with filter or items across all chests 2025-03-19 16:50:43 +07:00
85c4aa4dc4
Prioritize non-filtered slots when taking items first 2025-03-19 11:29:47 +07:00
bd4622fc0d
Ignore slot filters when taking from chests 2025-03-19 10:58:50 +07:00
36f6a12395
Fix quick move to nearby chests treating every chest equally
and possibly creating situation where items of one type of one of chests go into empty slots of different (unrelated) chest(s)
2025-03-18 22:55:17 +07:00
06a621d370
Prioritize chests closer to player first 2025-03-18 22:47:26 +07:00
d6f53946d9
"Quick stack to nearby chests" prototype 2025-03-18 22:37:44 +07:00
091ffc8c9f
Merge branch '1.21' into new-container-api 2025-03-18 18:59:00 +07:00
f7a4623830
Provide convenient unit packet registration 2025-03-18 18:56:23 +07:00
c9dca76870
Provide Unit (singleton) codecs 2025-03-18 18:55:55 +07:00
ef2602895b
Declare VanillaMenuTypes#register as internal 2025-03-18 18:41:09 +07:00
0a0b0e3819
Merge branch '1.21' into new-container-api 2025-03-18 18:40:39 +07:00
8f40717ff6
Change quickmove slot priority to consider hotbar slots last, to match vanilla behavior 2025-03-17 23:15:54 +07:00
d48dbf3ec9
Merge branch '1.21' into new-container-api 2025-03-17 23:02:13 +07:00
a84df28cf8
Rearrange icons in quickmove 2025-03-17 22:28:02 +07:00
03c64425a9
Add take all, restock, full restock buttons 2025-03-17 21:08:10 +07:00
d41d331e0f
Merge branch '1.21' into new-container-api 2025-03-17 11:24:15 +07:00
d89d5b9672
Add "quick move" to/from storage graphics 2025-03-17 11:23:34 +07:00
ee4b12e687
Improved "quick move" code in menus 2025-03-16 23:41:33 +07:00
95f19bc18f
Merge branch '1.21' into new-container-api 2025-03-16 00:31:14 +07:00
d5a9632c97
Move SortInput to upper level 2025-03-15 22:26:47 +07:00
cf32fd9d2a
Extract moveItemStackToSlots static context 2025-03-15 17:43:41 +07:00
427b5f9179
Merge branch '1.21' into new-container-api 2025-03-15 13:23:43 +07:00
fd9960bd86
Remove HandlerFilter 2025-03-15 11:38:26 +07:00
7c6d58b782
Merge branch '1.21' into new-container-api 2025-03-15 10:15:31 +07:00
5d15425fa4
Merge branch '1.21' into new-container-api 2025-03-15 10:06:27 +07:00
0efc520782
Implement faster lookup methods for Slotted Container as well 2025-03-15 00:18:02 +07:00
8b38504a26
Derive hasEmptySlots, isEmpty, countItem from other methods 2025-03-14 23:10:25 +07:00
69d9aaab50
Fast implementation of some methods in EnhancedContainer 2025-03-14 22:15:58 +07:00
448041fe2e
More specialized versions of iterators in IEnhancedContainer 2025-03-14 22:09:32 +07:00
54012cf136
Bump kommons 2025-03-14 21:06:23 +07:00
70c5382e9d
Always use slotted addItem implementation in containers 2025-03-14 19:45:28 +07:00
01215d647c
Cache hasFilterableSlots in CombinedContainer 2025-03-14 19:40:01 +07:00
3a5f37bd76
Specify argument names explicitly 2025-03-14 19:32:17 +07:00
4ab667b37e
Ooprs!!1 2025-03-14 19:32:04 +07:00
2943026dd5
Add generic param to CombinedContainer 2025-03-14 19:06:49 +07:00
90a22a093d
Don't create filter syncher for IPlayerInventorySlot 2025-03-14 18:57:14 +07:00
c43be6eb62
Remove IContainer 2025-03-14 18:40:10 +07:00
59eab74b44
Remove IMatteryContainer 2025-03-14 18:38:28 +07:00
bdeb740df0
Remove ShadowContainer and MatteryContainer 2025-03-14 18:35:22 +07:00
621661c9fe
Remove MatteryCraftingContainer usage 2025-03-14 18:34:19 +07:00
d0904da1db
Remove remaining MatteryContainer usages 2025-03-14 18:32:28 +07:00
05bdff6a37
Remove unused class 2025-03-14 18:31:01 +07:00
b076d29560
Declare IEnhancedContainer as having generic parameter Slot 2025-03-14 18:29:51 +07:00
a2263c5725
Update MatteryPlayer to use Enhanced containers instead of Mattery containers 2025-03-14 18:11:39 +07:00
d57371ca13
Merge branch '1.21' into new-container-api 2025-03-14 17:47:09 +07:00
305ddbd0d4
Merge branch 'player-improvements' into new-container-api 2025-03-14 12:14:36 +07:00
b42a503096
Merge branch '1.21' into new-container-api 2025-03-14 12:14:06 +07:00
ade2c0499d
Move Item Monitor to new container API 2025-03-12 18:42:04 +07:00
e699147f9f
Merge ChargeMenuSlot into BatteryMenuSlot 2025-03-12 16:49:19 +07:00
4dad60dfbb
Move Matter Hatch to Slotted Container 2025-03-12 16:47:45 +07:00
55c3c16172
Update Energy Interface to use SlottedContainer 2025-03-11 21:10:19 +07:00
1455e12da3
Fix TooltipList not using proper container class 2025-03-11 21:08:31 +07:00
566538f8a7
Update Drive Viewer to use EnhancedContainer 2025-03-11 20:59:11 +07:00
c08ea8a43d
Update Driver Rack to use SlottedContainer 2025-03-11 20:50:59 +07:00
1f8cd2cfe3
Update Plate press to use SlottedContainer 2025-03-11 16:50:48 +07:00
a2b2acd3f0
Merge branch '1.21' into new-container-api 2025-03-11 11:39:30 +07:00
8d58dfff14
Merge branch '1.21' into new-container-api 2025-03-11 11:09:04 +07:00
5e1ae7e77d
Handle negative experience stored in essence capsules 2025-03-10 15:30:50 +07:00
17e4856b10
Use SlottedContainer in Essense Storage 2025-03-10 15:29:50 +07:00
847ec81928
Use moveEnergy in Energy Servo 2025-03-10 09:58:42 +07:00
5e23300ddf
Merge branch '1.21' into new-container-api 2025-03-09 22:37:57 +07:00
6d14e6a396
Merge branch '1.21' into new-container-api 2025-03-09 22:01:13 +07:00
bb77ecddb0
Merge branch '1.21' into new-container-api 2025-03-08 17:45:16 +07:00
00830c2986
Merge branch '1.21' into new-container-api 2025-03-08 17:41:20 +07:00
b4eb0f7056
Merge branch '1.21' into new-container-api 2025-03-07 21:01:13 +07:00
101bf52113
Update Energy servo to use SlottedContainer, as well as make it behave better with automation 2025-03-07 19:53:09 +07:00
54c2964deb
Update Energy servo to use SlottedContainer, merge EnergyContainerInputMenuSlot into BatteryMenuSlot 2025-03-07 19:17:34 +07:00
762926d2cc
Update Cobblestone generator to use SlottedContainer 2025-03-07 18:48:52 +07:00
4107ec2313
Update Chemical generator to use SlottedContainer 2025-03-07 18:37:43 +07:00
af4123990f
Merge branch '1.21' into new-container-api 2025-03-07 17:32:10 +07:00
45651d81b5
Merge branch '1.21' into new-container-api 2025-03-07 11:46:21 +07:00
ebc49b03c4
Replace shulker box gui 2025-03-07 11:45:35 +07:00
b4977ea6ae
Move Battery bank to slotted container 2025-03-07 10:26:09 +07:00
51be2c89f4
Make battery slot be filterable 2025-03-07 10:24:50 +07:00
d88da8f244
Automation-forbidden slots are now prohibited to be extracted from 2025-03-07 10:15:53 +07:00
bbf4e752e7
Fix (dis)chargeable handler filters not allowing to extract invalid items 2025-03-07 10:14:06 +07:00
f011c1a912
Allow bottler slots to be filtered 2025-03-07 09:38:22 +07:00
e3a11b3b9e
Update menus to use UserFilteredSlot 2025-03-07 09:34:05 +07:00
784930e396
Update AbstractProcessingMachineScreen to properly account for UserFilteredMenuSlot 2025-03-07 09:26:54 +07:00
3e086dcacf
Switch Powered furnaces to SlottedContainer, make them reject items which are not used in any recipe (in automation) 2025-03-07 09:23:26 +07:00
fbd34f3414
SimpleCache helper function 2025-03-06 22:36:55 +07:00
67fc8f99f6
ooprs!! 2025-03-06 22:30:42 +07:00
06f8d8838a
More efficient pattern storage patternCapacity and storedPatterns implementations 2025-03-06 22:02:09 +07:00
d1a9825ea8
Update Pattern storage to use SlottedContainer 2025-03-06 22:00:41 +07:00
caa8d1bb90
Remove conflicting methods from mattery container 2025-03-06 21:58:50 +07:00
6c42af9f30
Provide stream() method in enhanced container 2025-03-06 21:58:08 +07:00
16d492073b
Merge branch '1.21' into new-container-api 2025-03-06 21:48:48 +07:00
cfd64fac07
Update Matter scanner to use SlottedContainer 2025-03-06 21:48:05 +07:00
8967d3aafa
Make CombinedItemHandler aware that SlottedContainer does not need to be rechecked 2025-03-06 20:41:58 +07:00
e0e1ba864d
Update Matter replicator to use Slotted Container 2025-03-06 20:41:39 +07:00
e8ba5916b1
Update Matter recycler to use Slotted Container 2025-03-06 20:31:28 +07:00
f8e9a67994
Update Matter Reconstructor to use slotted container 2025-03-06 20:00:26 +07:00
bc2cfccffe
Provide direct set operator 2025-03-06 18:22:04 +07:00
a73ad5484d
Merge branch '1.21' into new-container-api 2025-03-06 18:20:40 +07:00
84d4ed78ea
Don't scramble xoshiro on fork since seed is already enough random 2025-03-06 16:49:10 +07:00
7a4ce4f394
Use EnhancedContainer in makeUpgradeSlots 2025-03-06 16:38:20 +07:00
c31549792e
More specific methods in enhanced container interface 2025-03-06 16:37:30 +07:00
5422f0fec8
Merge branch '1.21' into new-container-api 2025-03-06 16:14:41 +07:00
c376a99da0
Make Upgrade container inherit EnhancedContainer instead 2025-03-06 16:11:37 +07:00
9806e1ec52
Revert "Update Upgrade Container to use SlottedContainer"
This reverts commit 861b94a30a.
2025-03-06 16:06:21 +07:00
de79019f56
Implement EnhancedContainer 2025-03-06 16:06:04 +07:00
efbc93e16a
Make IAutomatedContainer implement IItemHandlerModifiable 2025-03-05 19:01:24 +07:00
861b94a30a
Update Upgrade Container to use SlottedContainer 2025-03-05 16:22:39 +07:00
83d11d531d
Cache recipes in Matter Entangler 2025-03-05 15:23:27 +07:00
45a32dce3c
Merge branch '1.21' into new-container-api 2025-03-05 13:20:27 +07:00
3910e38add
Move Matter Entangler to Slotted container 2025-03-05 13:17:57 +07:00
5942ab2126
Implement IEnhancedCraftingContainer 2025-03-05 13:07:39 +07:00
8696d268ad
Make IEnhancedContainer implement StackedContentsCompatible 2025-03-05 13:03:42 +07:00
8aae573110
Merge branch '1.21' into new-container-api 2025-03-05 10:51:52 +07:00
892beddfc6
Merge branch '1.21' into new-container-api 2025-03-05 09:49:31 +07:00
14e6ee4916
Merge branch '1.21' into new-container-api 2025-03-03 22:29:17 +07:00
d558252db9
Update Matter Decomposer to use SlottedContainer 2025-03-03 20:14:20 +07:00
7f9af78734
Update MatteryPoweredBlockEntity battery container to use SlottedContainer 2025-03-03 19:55:52 +07:00
d76f2fe015
lol 2025-03-03 19:54:22 +07:00
70bfa114c8
Use IEnhancedContainer in setChanged 2025-03-03 19:47:45 +07:00
a5b96fa4c4
Update bottler to use slotted container 2025-03-03 18:33:24 +07:00
cff5988767
Merge branch '1.21' into new-container-api 2025-03-03 18:03:28 +07:00
6e7b90a8e8
Move Matter capacitor bank to slotted container 2025-03-03 15:00:30 +07:00
edb3aa657d
Merge branch '1.21' into new-container-api 2025-03-03 14:44:54 +07:00
6e5e64baeb
Merge branch '1.21' into new-container-api 2025-03-01 19:17:29 +07:00
3ed935001b
Move Painter to SlottedContainer 2025-03-01 19:16:40 +07:00
4292713874
Oopsie 2025-03-01 13:26:34 +07:00
1b5476de40
Mark chunk dirty when grill fuel ticks 2025-03-01 12:22:24 +07:00
bf7c9277ee
Update getMaxStackSize in MatteryMenuSlot to use IEnhancedContainer 2025-03-01 12:19:06 +07:00
a6891362d9
Remove no longer needed operator 2025-03-01 12:15:33 +07:00
2b2f78e528
Fix Grill not properly saving cook progress when being deserialized from disk 2025-03-01 12:15:19 +07:00
454783a99a
KOptional codec 2025-03-01 12:11:53 +07:00
d2ff43946e
Update grill to use SlottedContainer 2025-03-01 12:00:33 +07:00
abf74e4a2b
Merge branch '1.21' into new-container-api 2025-03-01 11:28:16 +07:00
66a4adecf9
Move Fluid tank to SlottedContainer, and make it accept slot filters 2025-03-01 00:06:07 +07:00
1702f95370
Rename "MatteryMenu" to "MatteryMenuSlot", along with all other slot classes to include "menu" in their name 2025-02-28 22:11:11 +07:00
f79b49d422
Move CargoCrate to SlottedContainer
implement necessary changes to MatteryMenu, MatterySlot and panels
to reflect networking slot filters as part of container state, and not as part of menu slot
2025-02-28 21:57:28 +07:00
30263bf30e
Make CombinedContainer temporarily implement old IMatteryContainer so it no longer crashes 2025-02-28 20:29:20 +07:00
ffdd44357d
Make it compile again so migration can be done incrementally 2025-02-28 20:16:23 +07:00
5e8ab76f49
Update MatteryContainer to fix compilation error 2025-02-28 15:38:37 +07:00
9f6b9ad85b
Some helper functions, split "Filtered" into own subclass with own "Simple" factory 2025-02-28 15:23:08 +07:00
ab1446b682
Move SlottedContainer to subpackage 2025-02-28 09:10:03 +07:00
6764af0dcc
Misc improvements for CombinedContainer 2025-02-28 08:38:09 +07:00
6747d5f471
Further split ISlottedContainer into IAutomatedContainer 2025-02-27 23:40:29 +07:00
124a1b3db6
Get rid of "slot" and "container" in IContainerSlot since they were bad design choices 2025-02-27 21:55:03 +07:00
a0b04fc1f4
Extracting 'lost' items from SlottedContainer 2025-02-27 21:17:28 +07:00
8a4abb90bd
Forgot to switch IntSortedSet to IntSet 2025-02-27 21:08:00 +07:00
fc1d9d6448
Make SlottedContainerBuilder be embedded inside SlottedContainer 2025-02-27 20:41:56 +07:00
b39c82e79a
Merge branch '1.21' into new-container-api 2025-02-27 19:41:42 +07:00
b1b6843400
Merge branch '1.21' into new-container-api 2025-02-27 19:24:54 +07:00
ca6ff30414
Initial code for new containers 2025-02-27 19:19:33 +07:00
188 changed files with 5800 additions and 3488 deletions

View File

@ -22,7 +22,7 @@ mixin_version=0.8.5
neogradle.subsystems.parchment.minecraftVersion=1.21.1 neogradle.subsystems.parchment.minecraftVersion=1.21.1
neogradle.subsystems.parchment.mappingsVersion=2024.11.17 neogradle.subsystems.parchment.mappingsVersion=2024.11.17
kommons_version=3.5.2 kommons_version=3.6.0
caffeine_cache_version=3.1.5 caffeine_cache_version=3.1.5
jei_version=19.16.4.171 jei_version=19.16.4.171

View File

@ -631,6 +631,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Switch input facing") add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Switch input facing")
add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "I/O Limit. -1 means no limit") add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "I/O Limit. -1 means no limit")
add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Display this information on block's screen") add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Display this information on block's screen")
add(MBlocks.ENERGY_COUNTER[null]!!, "do_pull", "Pull energy from input side and push to output")
add(MBlocks.ENERGY_COUNTER[null]!!, "dont_pull", "Don't pull energy")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Chemical Generator") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Chemical Generator")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Generates power by burning solid fuels") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Generates power by burning solid fuels")
@ -987,6 +989,18 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
gui("quickmove_from.restock", "Restock from storage")
gui("quickmove_from.restock_with_move", "Quickstack from storage")
gui("quickmove_from.move", "Take all")
gui("quickmove_to.restock", "Restock to storage")
gui("quickmove_to.restock_with_move", "Quickstack to storage")
gui("quickmove_to.move", "Deposit all")
gui("quickmove.exchange", "Smart exchange with storage")
gui("quickmove.exchange.desc", "Filtered slots get restocked, everything else gets quickstacked from Exopack")
gui("quickmove_hint", "Right click to show all variants")
gui("exopack.accept_wireless_charge", "Accept wireless charging") gui("exopack.accept_wireless_charge", "Accept wireless charging")
gui("exopack.dont_accept_wireless_charge", "Do not accept wireless charging") gui("exopack.dont_accept_wireless_charge", "Do not accept wireless charging")

View File

@ -635,6 +635,8 @@ private fun blocks(provider: MatteryLanguageProvider) {
add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Сменить сторону входа") add(MBlocks.ENERGY_COUNTER[null]!!, "switch", "Сменить сторону входа")
add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "Лимит ввода/вывода. -1 для отключения лимитов") add(MBlocks.ENERGY_COUNTER[null]!!, "limit", "Лимит ввода/вывода. -1 для отключения лимитов")
add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Отображать эти данные на экране блока") add(MBlocks.ENERGY_COUNTER[null]!!, "display_this", "Отображать эти данные на экране блока")
add(MBlocks.ENERGY_COUNTER[null]!!, "do_pull", "Забирать энергию на входе и выталкивать её на выходе")
add(MBlocks.ENERGY_COUNTER[null]!!, "dont_pull", "Не забирать энергию на входе")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Химический генератор") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "Химический генератор")
addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Генерирует энергию сжигая твёрдое топливо") addBlock(MBlocks.CHEMICAL_GENERATOR.values, "desc", "Генерирует энергию сжигая твёрдое топливо")
@ -980,6 +982,18 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) {
with(provider.russian) { with(provider.russian) {
gui("quickmove_from.restock", "Быстрое пополнение из хранилища")
gui("quickmove_from.restock_with_move", "Быстрое складирование из хранилища")
gui("quickmove_from.move", "Взять всё")
gui("quickmove_to.restock", "Быстрое пополнение в хранилище")
gui("quickmove_to.restock_with_move", "Быстрое складирование в хранилище")
gui("quickmove_to.move", "Переместить всё")
gui("quickmove.exchange", "Умный обмен с хранилищем")
gui("quickmove.exchange.desc", "Слоты с фильтрами заполняются до максимума, всё остальное быстро складируется из экзопака")
gui("quickmove_hint", "Правый клик чтоб увидеть все варианты")
gui("exopack.accept_wireless_charge", "Принимать заряд от беспроводных зарядников") gui("exopack.accept_wireless_charge", "Принимать заряд от беспроводных зарядников")
gui("exopack.dont_accept_wireless_charge", "Не принимать заряд от беспроводных зарядников") gui("exopack.dont_accept_wireless_charge", "Не принимать заряд от беспроводных зарядников")

View File

@ -15,7 +15,6 @@ import ru.dbotthepony.mc.otm.capability.matter.IReplicationTaskProvider;
import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage; import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage;
import ru.dbotthepony.mc.otm.graph.matter.MatterNode; import ru.dbotthepony.mc.otm.graph.matter.MatterNode;
import ru.dbotthepony.mc.otm.graph.storage.StorageNode; import ru.dbotthepony.mc.otm.graph.storage.StorageNode;
import ru.dbotthepony.mc.otm.storage.StorageStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -70,4 +69,8 @@ public class MatteryCapability {
@Nonnull @Nonnull
@NotNull @NotNull
public static final ItemCapability<IMatteryUpgrade, Void> UPGRADE = ItemCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "machine_upgrade"), IMatteryUpgrade.class); public static final ItemCapability<IMatteryUpgrade, Void> UPGRADE = ItemCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "machine_upgrade"), IMatteryUpgrade.class);
@Nonnull
@NotNull
public static final BlockCapability<IQuickStackContainer, Void> QUICK_STACK_CONTAINER = BlockCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "quick_stack_container"), IQuickStackContainer.class);
} }

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryShulkerBoxMenu;
@Mixin(ShulkerBoxBlockEntity.class)
public abstract class ShulkerBoxBlockEntityMixin {
@Overwrite(remap = false)
public AbstractContainerMenu createMenu(int p_59312_, Inventory p_59313_) {
return new MatteryShulkerBoxMenu(p_59312_, p_59313_, (ShulkerBoxBlockEntity) (Object) this);
}
}

View File

@ -12,6 +12,8 @@ import ru.dbotthepony.mc.otm.player.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.DevChestBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.AbstractPoweredFurnaceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.PlatePressBlockEntity
import ru.dbotthepony.mc.otm.player.MatteryPlayer import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.capability.drive.DrivePool import ru.dbotthepony.mc.otm.capability.drive.DrivePool
import ru.dbotthepony.mc.otm.client.AndroidAbilityKeyMapping import ru.dbotthepony.mc.otm.client.AndroidAbilityKeyMapping
@ -41,6 +43,7 @@ import ru.dbotthepony.mc.otm.client.render.blockentity.MatterBatteryBankRenderer
import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded import ru.dbotthepony.mc.otm.compat.curios.isCuriosLoaded
import ru.dbotthepony.mc.otm.compat.curios.onCuriosSlotModifiersUpdated import ru.dbotthepony.mc.otm.compat.curios.onCuriosSlotModifiersUpdated
import ru.dbotthepony.mc.otm.compat.vanilla.MatteryChestMenu import ru.dbotthepony.mc.otm.compat.vanilla.MatteryChestMenu
import ru.dbotthepony.mc.otm.compat.vanilla.VanillaMenuTypes
import ru.dbotthepony.mc.otm.config.PlayerConfig import ru.dbotthepony.mc.otm.config.PlayerConfig
import ru.dbotthepony.mc.otm.config.CablesConfig import ru.dbotthepony.mc.otm.config.CablesConfig
import ru.dbotthepony.mc.otm.config.ClientConfig import ru.dbotthepony.mc.otm.config.ClientConfig
@ -49,6 +52,7 @@ import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.ServerConfig import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.config.ToolsConfig import ru.dbotthepony.mc.otm.config.ToolsConfig
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.data.FlywheelMaterials import ru.dbotthepony.mc.otm.data.FlywheelMaterials
import ru.dbotthepony.mc.otm.data.world.BooleanProvider import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.DecimalProvider import ru.dbotthepony.mc.otm.data.world.DecimalProvider
@ -131,7 +135,7 @@ object OverdriveThatMatters {
MLootNumberProviders.register(MOD_BUS) MLootNumberProviders.register(MOD_BUS)
StorageStack.register(MOD_BUS) StorageStack.register(MOD_BUS)
MatteryChestMenu.register(MOD_BUS) VanillaMenuTypes.register(MOD_BUS)
MCreativeTabs.initialize(MOD_BUS) MCreativeTabs.initialize(MOD_BUS)
@ -146,6 +150,7 @@ object OverdriveThatMatters {
AbstractRegistryAction.register(MOD_BUS) AbstractRegistryAction.register(MOD_BUS)
IMatterFunction.register(MOD_BUS) IMatterFunction.register(MOD_BUS)
ItemFilter.register(MOD_BUS)
MRegistry.initialize(MOD_BUS) MRegistry.initialize(MOD_BUS)
MatterManager.initialize(MOD_BUS) MatterManager.initialize(MOD_BUS)
@ -247,6 +252,9 @@ object OverdriveThatMatters {
FORGE_BUS.addListener(EventPriority.NORMAL, MStructureTags::registerVillagerTrades) FORGE_BUS.addListener(EventPriority.NORMAL, MStructureTags::registerVillagerTrades)
FORGE_BUS.addListener(EventPriority.LOWEST, PlatePressBlockEntity::onReload)
FORGE_BUS.addListener(EventPriority.LOWEST, AbstractPoweredFurnaceBlockEntity.Companion::onReload)
if (isCuriosLoaded) { if (isCuriosLoaded) {
FORGE_BUS.addListener(EventPriority.NORMAL, ::onCuriosSlotModifiersUpdated) FORGE_BUS.addListener(EventPriority.NORMAL, ::onCuriosSlotModifiersUpdated)
} }

View File

@ -16,7 +16,6 @@ import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource import net.minecraft.util.RandomSource
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.LivingEntity
@ -36,8 +35,6 @@ import net.minecraft.world.phys.shapes.VoxelShape
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion
import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.TooltipList import ru.dbotthepony.mc.otm.core.TooltipList
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -149,6 +146,9 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
return getShapeForEachState(ArrayList(stateDefinition.properties), mapper) return getShapeForEachState(ArrayList(stateDefinition.properties), mapper)
} }
protected open val neverOpensAMenu: Boolean
get() = false
override fun useWithoutItem( override fun useWithoutItem(
blockState: BlockState, blockState: BlockState,
level: Level, level: Level,
@ -156,18 +156,17 @@ open class MatteryBlock(properties: Properties = DEFAULT_PROPERTIES) : Block(pro
ply: Player, ply: Player,
blockHitResult: BlockHitResult blockHitResult: BlockHitResult
): InteractionResult { ): InteractionResult {
if (this is EntityBlock && !level.isClientSide) { if (!neverOpensAMenu && this is EntityBlock) {
val tile = level.getBlockEntity(blockPos) val tile = level.getBlockEntity(blockPos)
if (tile is MenuProvider) { if (tile is MenuProvider) {
ply.openMenu(tile) if (!level.isClientSide)
return InteractionResult.CONSUME ply.openMenu(tile)
return InteractionResult.sidedSuccess(level.isClientSide)
} }
} }
if (this is EntityBlock && level.isClientSide)
return InteractionResult.SUCCESS
return super.useWithoutItem(blockState, level, blockPos, ply, blockHitResult) return super.useWithoutItem(blockState, level, blockPos, ply, blockHitResult)
} }

View File

@ -117,6 +117,10 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
experience = (nbt?.asDouble ?: 0.0).coerceAtLeast(0.0) experience = (nbt?.asDouble ?: 0.0).coerceAtLeast(0.0)
} }
private val liquidXPMilliBuckets: Int get() {
return (experience * XP_TO_LIQUID_RATIO).toInt()
}
override fun getTanks(): Int { override fun getTanks(): Int {
return 1 return 1
} }
@ -125,7 +129,7 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
if (tank != 0) if (tank != 0)
return FluidStack.EMPTY return FluidStack.EMPTY
return FluidStack(MFluids.LIQUID_XP, (experience * XP_TO_LIQUID_RATIO).toInt()) return FluidStack(MFluids.LIQUID_XP, liquidXPMilliBuckets)
} }
override fun getTankCapacity(tank: Int): Int { override fun getTankCapacity(tank: Int): Int {
@ -148,13 +152,13 @@ class ExperienceStorage(val maxExperience: DoubleSupplier = DoubleSupplier { Dou
} }
override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack { override fun drain(maxDrain: Int, action: IFluidHandler.FluidAction): FluidStack {
val actualDrain = maxDrain.coerceAtMost((experience * XP_TO_LIQUID_RATIO).toInt()).let { it / XP_TO_LIQUID_RATIO * XP_TO_LIQUID_RATIO } val actualDrain = maxDrain.coerceAtMost(liquidXPMilliBuckets)
if (actualDrain <= 0) if (actualDrain <= 0)
return FluidStack.EMPTY return FluidStack.EMPTY
if (action.execute()) if (action.execute())
experience -= actualDrain / XP_TO_LIQUID_RATIO experience -= actualDrain.toDouble() / XP_TO_LIQUID_RATIO
return FluidStack(MFluids.LIQUID_XP, actualDrain) return FluidStack(MFluids.LIQUID_XP, actualDrain)
} }

View File

@ -6,13 +6,12 @@ import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.extractEnergy import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) { abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) {
val batteryContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val batteryContainer = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val batteryItemHandler = batteryContainer.handler(HandlerFilter.Dischargeable)
open val energy: IMatteryEnergyStorage? open val energy: IMatteryEnergyStorage?
get() = null get() = null

View File

@ -17,12 +17,16 @@ import net.minecraft.world.level.gameevent.GameEvent
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder import ru.dbotthepony.mc.otm.core.util.BlockLootTableHolder
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
@ -30,20 +34,28 @@ class CargoCrateBlockEntity(
p_155229_: BlockPos, p_155229_: BlockPos,
p_155230_: BlockState p_155230_: BlockState
) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_) { ) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_) {
val container = MatteryContainer(this::setChanged, CAPACITY).also(::addDroppableContainer) private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super<FilteredContainerSlot>.canAutomationPlaceItem(itemStack) && loot.lootTable == null
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && loot.lootTable == null
}
}
val container = SlottedContainer.Builder()
.add(CAPACITY, ::Slot)
.onChanged(::setChanged)
.build()
.also(::addDroppableContainer)
init {
exposeSideless(MatteryCapability.QUICK_STACK_CONTAINER, IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot)))
}
private var interactingPlayers = 0 private var interactingPlayers = 0
val handler = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return loot.lootTable == null
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return loot.lootTable == null
}
})
override fun dropItems( override fun dropItems(
oldBlockState: BlockState, oldBlockState: BlockState,
level: ServerLevel, level: ServerLevel,
@ -81,7 +93,7 @@ class CargoCrateBlockEntity(
} }
init { init {
exposeGlobally(Capabilities.ItemHandler.BLOCK, handler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
savetablesLevel.stateful(::container, INVENTORY_KEY) savetablesLevel.stateful(::container, INVENTORY_KEY)
} }

View File

@ -18,9 +18,12 @@ import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.capability.fluid.BlockMatteryFluidHandler import ru.dbotthepony.mc.otm.capability.fluid.BlockMatteryFluidHandler
import ru.dbotthepony.mc.otm.capability.moveFluid import ru.dbotthepony.mc.otm.capability.moveFluid
import ru.dbotthepony.mc.otm.config.ItemsConfig import ru.dbotthepony.mc.otm.config.ItemsConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilter
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.isNotSameAs import ru.dbotthepony.mc.otm.core.isNotSameAs
import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu
@ -37,28 +40,42 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
} }
}), FluidStack.OPTIONAL_STREAM_CODEC.wrap())) }), FluidStack.OPTIONAL_STREAM_CODEC.wrap()))
val fillInput = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) private inner class FillSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val drainInput = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
val output = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) if (!super.canAutomationPlaceItem(itemStack))
return false
if (fluid.isEmpty) {
return itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
return itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.fill(fluid[0], IFluidHandler.FluidAction.SIMULATE) > 0 } ?: false
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && !canAutomationPlaceItem(item)
}
}
val inputContainer = SlottedContainer.Builder()
.add(DRAIN_TAG, AutomationFilters.DRAINABLE_FLUID_CONTAINERS.filteredProvider)
.add(FILL_TAG, ::FillSlot)
.onChanged(::markDirtyFast)
.build()
.also(::addDroppableContainer)
val outputContainer = SlottedContainer.Builder()
.add(AutomationFilters.ONLY_OUT.simpleProvider)
.onChanged(::markDirtyFast)
.build()
.also(::addDroppableContainer)
private val fillSlot = inputContainer[FILL_TAG]
private val drainSlot = inputContainer[DRAIN_TAG]
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = CombinedItemHandler( input = inputContainer,
drainInput.handler(HandlerFilter.DrainableFluidContainers), output = outputContainer,
fillInput.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (fluid.isEmpty) {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.fill(fluid[0], IFluidHandler.FluidAction.SIMULATE) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !canInsert(slot, stack)
}
})
),
output = output.handler(HandlerFilter.OnlyOut),
frontDefault = ItemHandlerMode.INPUT_OUTPUT, frontDefault = ItemHandlerMode.INPUT_OUTPUT,
backDefault = ItemHandlerMode.INPUT_OUTPUT, backDefault = ItemHandlerMode.INPUT_OUTPUT,
leftDefault = ItemHandlerMode.INPUT_OUTPUT, leftDefault = ItemHandlerMode.INPUT_OUTPUT,
@ -71,20 +88,19 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
init { init {
savetables.stateful(::fluid, FLUID_KEY) savetables.stateful(::fluid, FLUID_KEY)
savetables.stateful(::fillInput) savetables.stateful(::inputContainer)
savetables.stateful(::drainInput) savetables.stateful(::outputContainer)
savetables.stateful(::output)
} }
private fun drainItem() { private fun drainItem() {
val item = drainInput[0] val item = drainSlot.item
if (item.isNotEmpty) { if (item.isNotEmpty) {
val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM) val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM)
if (cap == null) { if (cap == null) {
if (output.consumeItem(item, simulate = false)) { if (outputContainer.consumeItem(item, simulate = false)) {
drainInput.setChanged(0) drainSlot.setChanged()
} }
return return
@ -95,17 +111,17 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
val moved0 = moveFluid(source = cap, destination = fluid) val moved0 = moveFluid(source = cap, destination = fluid)
if (moved0.isNotEmpty) { if (moved0.isNotEmpty) {
drainInput[0] = cap.container drainSlot.item = cap.container
if (output.consumeItem(drainInput[0], simulate = false)) { if (outputContainer.consumeItem(drainSlot.item, simulate = false)) {
drainInput.setChanged(0) drainSlot.setChanged()
} }
} }
} else { } else {
val moved0 = moveFluid(source = cap, destination = fluid, actuallyFill = false) val moved0 = moveFluid(source = cap, destination = fluid, actuallyFill = false)
if (moved0.isNotEmpty) { if (moved0.isNotEmpty) {
if (output.consumeItem(cap.container, simulate = true)) { if (outputContainer.consumeItem(cap.container, simulate = true)) {
val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException() val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException()
val moved1 = moveFluid(source = cap1, destination = fluid) val moved1 = moveFluid(source = cap1, destination = fluid)
@ -114,9 +130,9 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!") LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!")
} else { } else {
item.count-- item.count--
drainInput.setChanged(0) drainSlot.setChanged()
if (!output.consumeItem(cap1.container, simulate = false)) { if (!outputContainer.consumeItem(cap1.container, simulate = false)) {
LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!") LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!")
(level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container)) (level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container))
} }
@ -129,14 +145,14 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
} }
private fun fillItem() { private fun fillItem() {
val item = fillInput[0] val item = fillSlot.item
if (item.isNotEmpty) { if (item.isNotEmpty) {
val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM) val cap = (if (item.count == 1) item else item.copyWithCount(1)).getCapability(Capabilities.FluidHandler.ITEM)
if (cap == null) { if (cap == null) {
if (output.consumeItem(item, simulate = false)) { if (outputContainer.consumeItem(item, simulate = false)) {
fillInput.setChanged(0) fillSlot.setChanged()
} }
return return
@ -147,17 +163,17 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
val moved0 = moveFluid(source = fluid, destination = cap) val moved0 = moveFluid(source = fluid, destination = cap)
if (moved0.isNotEmpty) { if (moved0.isNotEmpty) {
fillInput[0] = cap.container fillSlot.item = cap.container
if (output.consumeItem(fillInput[0], simulate = false)) { if (outputContainer.consumeItem(fillSlot.item, simulate = false)) {
fillInput.setChanged(0) fillSlot.setChanged()
} }
} }
} else { } else {
val moved0 = moveFluid(source = fluid, destination = cap, actuallyDrain = false) val moved0 = moveFluid(source = fluid, destination = cap, actuallyDrain = false)
if (moved0.isNotEmpty) { if (moved0.isNotEmpty) {
if (output.consumeItem(cap.container, simulate = true)) { if (outputContainer.consumeItem(cap.container, simulate = true)) {
val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException() val cap1 = item.copyWithCount(1).getCapability(Capabilities.FluidHandler.ITEM) ?: throw ConcurrentModificationException()
val moved1 = moveFluid(source = fluid, destination = cap1) val moved1 = moveFluid(source = fluid, destination = cap1)
@ -166,9 +182,9 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!") LOGGER.error("Error moving fluids in Fluid tank at $blockPos: moved $moved0 during simulation from ${cap.container}, moved $moved1 from ${cap1.container} during execution. This is likely a bug in OTM or other mod!")
} else { } else {
item.count-- item.count--
fillInput.setChanged(0) fillSlot.setChanged()
if (!output.consumeItem(cap1.container, simulate = false)) { if (!outputContainer.consumeItem(cap1.container, simulate = false)) {
LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!") LOGGER.error("Unable to insert ${cap1.container} into output slot of Fluid tank at $blockPos, popping item in world instead to avoid item loss! This is likely a bug in OTM or other mod!")
(level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container)) (level as? ServerLevel)?.addFreshEntity(ItemEntity(level!!, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), cap1.container))
} }
@ -194,5 +210,8 @@ class FluidTankBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
companion object { companion object {
const val FLUID_KEY = "fluid" const val FLUID_KEY = "fluid"
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
private val FILL_TAG = SlottedContainer.tag<FilteredContainerSlot>()
private val DRAIN_TAG = SlottedContainer.tag<FilteredContainerSlot>()
} }
} }

View File

@ -6,6 +6,7 @@ import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Containers import net.minecraft.world.Containers
import net.minecraft.world.MenuProvider import net.minecraft.world.MenuProvider
@ -19,12 +20,16 @@ import net.minecraft.world.item.crafting.SmokingRecipe
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.util.Delegate import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.mc.otm.block.IBlockWithCustomName import ru.dbotthepony.mc.otm.block.IBlockWithCustomName
import ru.dbotthepony.mc.otm.block.decorative.GrillBlock import ru.dbotthepony.mc.otm.block.decorative.GrillBlock
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -38,47 +43,45 @@ import ru.dbotthepony.mc.otm.menu.decorative.GrillMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.GRILL, blockPos, blockState), MenuProvider, IBlockWithCustomName { class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.GRILL, blockPos, blockState), MenuProvider, IBlockWithCustomName {
val fuelSlot = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, 1) { val fuelSlot = SlottedContainer.simple(1, FUEL_SLOT_PROVIDER, ::markDirtyFast)
override fun getMaxStackSize(): Int {
return 4
}
}
val inputSlots = object : MatteryContainer(this@GrillBlockEntity::markDirtyFast, SLOTS) { private inner class InputSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun getMaxStackSize(): Int { override val maxStackSize: Int
return 1 get() = 1
}
private fun clearSlot(slot: Int) {
private fun clearSlot() {
inputProgress[slot] = 0 inputProgress[slot] = 0
outputs[slot] = null outputs[slot] = null
outputsRecipeNames[slot] = null
activeSlots.rem(slot) activeSlots.rem(slot)
} }
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override fun notifyChanged(old: ItemStack) {
super.setChanged(slot, new, old) super.notifyChanged(old)
if (new.isEmpty || new.count > 1) { if (item.isEmpty || item.count > 1) {
clearSlot(slot) clearSlot()
} else { } else {
val level = level val level = level
if (level == null) { if (level == null) {
clearSlot(slot) clearSlot()
return return
} }
val input = SingleRecipeInput(new) val input = SingleRecipeInput(item)
val result = level.recipeManager val result = level.recipeManager
.byType(RecipeType.SMOKING) .byType(RecipeType.SMOKING)
.firstOrNull { it.value.matches(input, level) } .firstOrNull { it.value.matches(input, level) }
if (result == null) { if (result == null) {
clearSlot(slot) clearSlot()
} else { } else {
if (outputs[slot] != result.value) if (outputsRecipeNames[slot] != result.id)
inputProgress[slot] = 0 inputProgress[slot] = 0
outputsRecipeNames[slot] = result.id
outputs[slot] = result.value outputs[slot] = result.value
activeSlots.add(slot) activeSlots.add(slot)
} }
@ -86,16 +89,18 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
} }
} }
val inputSlots = SlottedContainer.simple(SLOTS, ::InputSlot, ::markDirtyFast)
override var customDisplayName: Component? = null override var customDisplayName: Component? = null
override fun createMenu(p_39954_: Int, p_39955_: Inventory, p_39956_: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
return GrillMenu(p_39954_, p_39955_, this) return GrillMenu(containerID, inventory, this)
} }
override fun getDisplayName(): Component { override fun getDisplayName(): Component {
return customDisplayName ?: level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos") return customDisplayName ?: level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos")
} }
private val outputsRecipeNames = arrayOfNulls<ResourceLocation>(SLOTS)
private val outputs = arrayOfNulls<SmokingRecipe>(SLOTS) private val outputs = arrayOfNulls<SmokingRecipe>(SLOTS)
private val activeSlots = IntArrayList() private val activeSlots = IntArrayList()
private val inputProgress = IntArray(SLOTS) private val inputProgress = IntArray(SLOTS)
@ -132,9 +137,12 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
savetables.stateful(::fuelSlot) savetables.stateful(::fuelSlot)
savetables.stateful(::experience) savetables.stateful(::experience)
outputsRecipeNames.fill(null)
// TODO: could have been done as list (and get more space efficient storage), but we use an array, oh well // TODO: could have been done as list (and get more space efficient storage), but we use an array, oh well
for (i in inputProgress.indices) { for (i in inputProgress.indices) {
savetables.codec(Delegate.Of({ inputProgress[i] }, { inputProgress[i] = it }), progressCodec, "inputProgress${i}") savetables.codec(Delegate.Of({ inputProgress[i] }, { inputProgress[i] = it }), progressCodec, "inputProgress${i}")
savetables.codecNullable(Delegate.Of({ outputsRecipeNames[i] }, { outputsRecipeNames[i] = it }), ResourceLocation.CODEC, "recipeName${i}")
} }
// addDroppableContainer(inputSlots) // addDroppableContainer(inputSlots)
@ -207,5 +215,6 @@ class GrillBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBloc
const val SLOTS = 6 const val SLOTS = 6
private val progressCodec = Codec.INT.minRange(0) private val progressCodec = Codec.INT.minRange(0)
val FUEL_SLOT_PROVIDER = ContainerSlot.Simple(maxStackSize = 4, filter = AutomationFilters.CHEMICAL_FUEL)
} }
} }

View File

@ -18,8 +18,8 @@ import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.fluids.FluidStack import net.neoforged.neoforge.fluids.FluidStack
import net.neoforged.neoforge.fluids.capability.IFluidHandler import net.neoforged.neoforge.fluids.capability.IFluidHandler
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.immutableMap import ru.dbotthepony.mc.otm.core.immutableMap
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -34,7 +34,39 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
return PainterMenu(containerID, inventory, this) return PainterMenu(containerID, inventory, this)
} }
val dyeInput = MatteryContainer(this::markDirtyFast, 1) private inner class InputSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
if (waterStored() < MAX_WATER_STORAGE) {
itemStack.getCapability(Capabilities.FluidHandler.ITEM)?.let {
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.SIMULATE)
if (drain.isNotEmpty) {
return true
}
}
}
val dye = DyeColor.getColor(itemStack) ?: return false
return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun modifyAutomationPlaceCount(itemStack: ItemStack): Int {
if (itemStack.getCapability(Capabilities.FluidHandler.ITEM) != null)
return 1
val dye = DyeColor.getColor(itemStack) ?: return 0
return itemStack.count.coerceAtMost((MAX_STORAGE - dyeStored(dye)) / HUE_PER_ITEM - item.count)
}
}
val dyeInput = SlottedContainer.simple(1, ::InputSlot, ::markDirtyFast)
// null - water // null - water
val dyeStored = Object2IntArrayMap<DyeColor?>() val dyeStored = Object2IntArrayMap<DyeColor?>()
@ -108,34 +140,7 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
markDirtyFast() markDirtyFast()
} }
val config = ConfigurableItemHandler(input = dyeInput.handler(object : HandlerFilter { val config = ConfigurableItemHandler(input = dyeInput)
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (waterStored() < MAX_WATER_STORAGE) {
stack.getCapability(Capabilities.FluidHandler.ITEM)?.let {
val drain = it.drain(FluidStack(Fluids.WATER, MAX_WATER_STORAGE - waterStored()), IFluidHandler.FluidAction.SIMULATE)
if (drain.isNotEmpty) {
return true
}
}
}
val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return false
return dyeStored(dye) + HUE_PER_ITEM <= MAX_STORAGE
}
override fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int {
if (!ItemStack.isSameItemSameComponents(stack, existing))
return super.modifyInsertCount(slot, stack, existing, simulate)
val dye = DyeColor.entries.firstOrNull { stack.`is`(it.tag) } ?: return 0
return stack.count.coerceAtMost((MAX_STORAGE - dyeStored(dye)) / HUE_PER_ITEM - existing.count)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}))
fun waterStored(): Int { fun waterStored(): Int {
return dyeStored.getInt(null) return dyeStored.getInt(null)

View File

@ -21,39 +21,37 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterBottlerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import java.util.function.BooleanSupplier import java.util.function.BooleanSupplier
class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) : class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
MatteryPoweredBlockEntity(MBlockEntities.MATTER_BOTTLER, blockPos, blockState) { MatteryPoweredBlockEntity(MBlockEntities.MATTER_BOTTLER, blockPos, blockState) {
val upgrades = UpgradeContainer(3, UpgradeType.BASIC_MATTER, BooleanSupplier { blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE }, ::markDirtyFast) val upgrades = UpgradeContainer(3, UpgradeType.BASIC_MATTER, BooleanSupplier { this.blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE }, ::markDirtyFast)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::markDirtyFast, upgrades.transform(MachinesConfig.MatterBottler.VALUES))) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::markDirtyFast, upgrades.transform(MachinesConfig.MatterBottler.VALUES)))
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
private inner class Container : MatteryContainer(3) { private inner class BottlingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
init { override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
addDroppableContainer(this) return super.canAutomationPlaceItem(itemStack) && isBottling && itemStack.getCapability(MatteryCapability.MATTER_ITEM)?.receiveMatterChecked(Decimal.ONE, true)?.isPositive == true
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
markDirtyFast()
updateBlockState()
} }
} }
val unbottling: MatteryContainer = Container() private inner class UnBottlingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val bottling: MatteryContainer = Container() override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && !isBottling && itemStack.getCapability(MatteryCapability.MATTER_ITEM)?.extractMatterChecked(Decimal.ONE, true)?.isPositive == true
}
}
val unbottling = SlottedContainer.simple(3, ::UnBottlingSlot, ::updateBlockState)
val bottling = SlottedContainer.simple(3, ::BottlingSlot, ::updateBlockState)
var isBottling: Boolean = true var isBottling: Boolean = true
set(value) { set(value) {
@ -63,11 +61,11 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
this.markDirtyFast() this.markDirtyFast()
if (value) { if (value) {
inputHandler.parent = bottlingHandler inputHandler.parent = bottling
outputHandler.parent = unbottlingHandler outputHandler.parent = unbottling
} else { } else {
inputHandler.parent = unbottlingHandler inputHandler.parent = unbottling
outputHandler.parent = bottlingHandler outputHandler.parent = bottling
} }
updateBlockState() updateBlockState()
@ -79,25 +77,13 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
this.markDirtyFast() this.markDirtyFast()
} }
val bottlingHandler = bottling.handler(object : HandlerFilter { private val inputHandler = ProxiedItemHandler(bottling)
override fun canInsert(slot: Int, stack: ItemStack): Boolean { private val outputHandler = ProxiedItemHandler(unbottling)
return isBottling && stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { it.matterFlow.input && it.missingMatter.isPositive } ?: false
}
})
val unbottlingHandler = unbottling.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return !isBottling && stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { it.matterFlow.output && it.storedMatter.isPositive } ?: false
}
})
private val inputHandler = ProxiedItemHandler(bottlingHandler)
private val outputHandler = ProxiedItemHandler(unbottlingHandler)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputHandler, input = inputHandler,
output = outputHandler, output = outputHandler,
battery = batteryItemHandler battery = batteryContainer
) )
val matter: ProfiledMatterStorage<MatterStorageImpl> = ProfiledMatterStorage(object : MatterStorageImpl(this@MatterBottlerBlockEntity::markDirtyFast, FlowDirection.BI_DIRECTIONAL, upgrades.matterCapacity(MachinesConfig.MatterBottler.VALUES::matterCapacity)) { val matter: ProfiledMatterStorage<MatterStorageImpl> = ProfiledMatterStorage(object : MatterStorageImpl(this@MatterBottlerBlockEntity::markDirtyFast, FlowDirection.BI_DIRECTIONAL, upgrades.matterCapacity(MachinesConfig.MatterBottler.VALUES::matterCapacity)) {
@ -141,12 +127,12 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
matterNode.isValid = false matterNode.isValid = false
} }
private fun updateBlockState(container: MatteryContainer) { private fun updateBlockState(container: SlottedContainer) {
val level = level as? ServerLevel ?: return val level = level as? ServerLevel ?: return
var state = blockState var state = blockState
for (i in 0 .. 2) { for ((i, slot) in container.slotIterator().withIndex()) {
val desired = !container.getItem(i).isEmpty && container.getItem(i).getCapability(MatteryCapability.MATTER_ITEM) != null val desired = !slot.isEmpty && slot.item.getCapability(MatteryCapability.MATTER_ITEM) != null
if (state.getValue(MatterBottlerBlock.SLOT_PROPERTIES[i]) != desired) { if (state.getValue(MatterBottlerBlock.SLOT_PROPERTIES[i]) != desired) {
state = state.setValue(MatterBottlerBlock.SLOT_PROPERTIES[i], desired) state = state.setValue(MatterBottlerBlock.SLOT_PROPERTIES[i], desired)
@ -154,26 +140,156 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
} }
if (state !== blockState) { if (state !== blockState) {
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS) level.setBlock(blockPos, state, Block.UPDATE_ALL)
} }
} }
private fun updateBlockState() { private fun updateBlockState() {
markDirtyFast()
if (isBottling) if (isBottling)
updateBlockState(bottling) updateBlockState(bottling)
else else
updateBlockState(unbottling) updateBlockState(unbottling)
} }
private val workerState by countingLazy(blockStateChangesCounter) {
this.blockState.getValue(WorkerState.SEMI_WORKER_STATE)
}
private fun blockstateToWorking() { private fun blockstateToWorking() {
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.WORKING) { if (workerState !== WorkerState.WORKING) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING), Block.UPDATE_CLIENTS) level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING), Block.UPDATE_ALL)
} }
} }
private fun blockstateToIdle() { private fun blockstateToIdle() {
if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) !== WorkerState.IDLE) { if (workerState !== WorkerState.IDLE) {
level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS) level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_ALL)
}
}
private fun tickBottling() {
var isWorking = false
var hasCapacitors = false
for (slot in bottling.slotIterator()) {
val item = slot.item
val capability = item.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
if (!capability.receiveMatterChecked(Decimal.LONG_MAX_VALUE, true).isPositive) {
unbottling.consumeItem(item, false)
slot.setChanged()
continue
}
hasCapacitors = true
initialCapacity = initialCapacity ?: capability.storedMatter
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
if (matter.storedMatter < rate) {
val toExtract = matter.missingMatter
.coerceAtMost(rate * 200)
.coerceAtMost(capability.missingMatter - matter.storedMatter)
matter.receiveMatter(matterNode.graph.extractMatter(toExtract, false), false)
}
if (matter.storedMatter.isPositive) {
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.extractMatter(capability.receiveMatter(rate.coerceAtMost(matter.storedMatter), true), true) / rate
val minRatio = minOf(matterRatio, energyRatio)
if (minRatio > Decimal.ZERO) {
isWorking = true
energy.extractEnergy(energyRate * minRatio, false)
matter.extractMatter(capability.receiveMatter(rate * minRatio, false), false)
workProgress = ((capability.storedMatter - initialCapacity!!) / capability.maxStoredMatter).toFloat()
slot.setChanged()
}
} else {
if (spitItemsWhenCantWork) {
unbottling.consumeItem(item, false)
slot.setChanged()
}
}
break
}
if (isWorking) {
blockstateToWorking()
} else {
blockstateToIdle()
}
if (!hasCapacitors) {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
initialCapacity = null
workProgress = 0f
}
}
private fun tickUnbottling() {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
if (!matter.missingMatter.isPositive) {
if (spitItemsWhenCantWork) {
for (slot in unbottling.slotIterator()) {
bottling.consumeItem(slot.item, false)
slot.setChanged()
}
}
blockstateToIdle()
return
}
var any = false
var hasCapacitors = false
for (slot in unbottling.slotIterator()) {
val item = slot.item
val it = item.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
if (!it.extractMatterChecked(Decimal.LONG_MAX_VALUE, true).isPositive) {
bottling.consumeItem(item, false)
slot.setChanged()
continue
}
initialCapacity = initialCapacity ?: it.storedMatter
hasCapacitors = true
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
val energyRate = MachinesConfig.MatterBottler.VALUES.energyConsumption * (1.0 + upgrades.speedBonus)
val energyRatio = if (energyRate <= Decimal.ZERO) Decimal.ONE else energy.extractEnergy(energyRate, true) / energyRate
val matterRatio = matter.receiveMatter(it.extractMatterChecked(rate, true), true) / rate
val minRatio = minOf(energyRatio, matterRatio)
if (minRatio > Decimal.ZERO) {
any = true
energy.extractEnergy(energyRate * energyRatio, false)
matter.receiveMatter(it.extractMatterChecked(rate * minRatio, false), false)
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
break
}
if (any) {
blockstateToWorking()
} else {
blockstateToIdle()
}
if (!hasCapacitors) {
initialCapacity = null
workProgress = 0f
} }
} }
@ -186,105 +302,9 @@ class MatterBottlerBlockEntity(blockPos: BlockPos, blockState: BlockState) :
} }
if (isBottling) { if (isBottling) {
var any = false tickBottling()
var idle = false
for (slot in bottling.slotIterator()) {
val item = slot.item
item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (!it.missingMatter.isPositive) {
unbottling.consumeItem(item, false)
slot.setChanged()
} else {
any = true
initialCapacity = initialCapacity ?: it.storedMatter
val rate = MachinesConfig.MatterBottler.RATE * (1.0 + upgrades.speedBonus)
if (matter.storedMatter < rate) {
matter.receiveMatter(matterNode.graph.extractMatter(matter.missingMatter
.coerceAtMost(rate * 200)
.coerceAtMost(it.missingMatter - matter.storedMatter), false), false)
}
if (matter.storedMatter.isPositive) {
matter.extractMatter(it.receiveMatter(rate.coerceAtMost(matter.storedMatter), false), false)
if (!it.missingMatter.isPositive) {
initialCapacity = null
workProgress = 0f
} else {
workProgress = ((it.storedMatter - initialCapacity!!) / it.maxStoredMatter).toFloat()
}
} else {
idle = true
if (spitItemsWhenCantWork) {
unbottling.consumeItem(item, false)
slot.setChanged()
}
}
}
}
if (any) {
break
}
}
if (any && !idle) {
blockstateToWorking()
} else {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false)
blockstateToIdle()
}
} else { } else {
matter.extractMatter(matterNode.graph.receiveMatter(matter.storedMatter, false), false) tickUnbottling()
if (!matter.missingMatter.isPositive) {
if (spitItemsWhenCantWork) {
for (slot in unbottling.slotIterator()) {
bottling.consumeItem(slot.item, false)
slot.setChanged()
}
}
blockstateToIdle()
return
}
var any = false
for (slot in unbottling.slotIterator()) {
val item = slot.item
item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (!it.storedMatter.isPositive) {
bottling.consumeItem(item, false)
slot.setChanged()
} else {
any = true
initialCapacity = initialCapacity ?: it.storedMatter
matter.receiveMatter(it.extractMatter(MachinesConfig.MatterBottler.RATE, false), false)
if (!it.storedMatter.isPositive) {
initialCapacity = null
workProgress = 0f
} else {
workProgress = 1f - (it.storedMatter / initialCapacity!!).toFloat()
}
}
}
if (any) {
break
}
}
if (any) {
blockstateToWorking()
} else {
blockstateToIdle()
}
} }
} }
} }

View File

@ -16,16 +16,15 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu import ru.dbotthepony.mc.otm.menu.matter.MatterCapacitorBankMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity( class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterStorage {
MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterStorage {
var gaugeLevel by syncher.float() var gaugeLevel by syncher.float()
private set private set
@ -40,10 +39,9 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO var summ = Decimal.ZERO
for (stack in container) for (stack in container)
if (!stack.isEmpty) stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { summ += it.storedMatter
summ += it.storedMatter }
}
return summ return summ
} }
@ -51,19 +49,35 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
override val maxStoredMatter: Decimal override val maxStoredMatter: Decimal get() {
get() {
var summ = Decimal.ZERO var summ = Decimal.ZERO
for (stack in container) for (stack in container)
if (!stack.isEmpty) stack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { summ += it.maxStoredMatter
summ += it.maxStoredMatter }
}
return summ return summ
} }
private fun updateGaugeLevel() {
var stored = Decimal.ZERO
var maxStored = Decimal.ZERO
for (stack in container) {
val cap = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
stored += cap.storedMatter
maxStored += cap.maxStoredMatter
}
gaugeLevel = stored.percentage(maxStored)
}
override fun tick() {
super.tick()
updateGaugeLevel()
}
override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal { override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
if (!howMuch.isPositive) if (!howMuch.isPositive)
return Decimal.ZERO return Decimal.ZERO
@ -73,21 +87,18 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO var summ = Decimal.ZERO
for (stack in container) { for (stack in container) {
if (!stack.isEmpty) { val it = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { val diff = it.receiveMatterChecked(howMuch, simulate)
val diff = it.receiveMatterChecked(howMuch, simulate) summ += diff
summ += diff howMuch -= diff
howMuch -= diff
}
if (howMuch.isZero) { if (howMuch.isZero) {
break break
}
} }
} }
if (summ.isPositive && !simulate) { if (!simulate && !summ.isZero) {
gaugeLevel = storedMatter.percentage(maxStoredMatter) markDirtyFast()
} }
return summ return summ
@ -102,21 +113,18 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
var summ = Decimal.ZERO var summ = Decimal.ZERO
for (stack in container) { for (stack in container) {
if (!stack.isEmpty) { val it = stack.getCapability(MatteryCapability.MATTER_ITEM) ?: continue
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { val diff = it.extractMatterChecked(howMuch, simulate)
val diff = it.extractMatterChecked(howMuch, simulate) summ += diff
summ += diff howMuch -= diff
howMuch -= diff
}
if (howMuch.isZero) { if (howMuch.isZero) {
break break
}
} }
} }
if (summ.isPositive && !simulate) { if (!simulate && !summ.isZero) {
gaugeLevel = storedMatter.percentage(maxStoredMatter) markDirtyFast()
} }
return summ return summ
@ -125,31 +133,38 @@ class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState)
override val matterFlow: FlowDirection override val matterFlow: FlowDirection
get() = FlowDirection.BI_DIRECTIONAL get() = FlowDirection.BI_DIRECTIONAL
val container = object : MatteryContainer(::markDirtyFast, BatteryBankBlockEntity.CAPACITY) { private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override fun notifyChanged(old: ItemStack) {
super.setChanged(slot, new, old) super.notifyChanged(old)
capacitorStatus[slot].value = new.getCapability(MatteryCapability.MATTER_ITEM) != null
gaugeLevel = storedMatter.percentage(maxStoredMatter) capacitorStatus[slot].value = item.getCapability(MatteryCapability.MATTER_ITEM) != null
} }
override fun getMaxStackSize(): Int = 1 override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
}.also(::addDroppableContainer) return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(MatteryCapability.MATTER_ITEM) != null
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM) != null
} }
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { override fun canAutomationTakeItem(desired: Int): Boolean {
stack.getCapability(MatteryCapability.MATTER_ITEM)?.let { item.getCapability(MatteryCapability.MATTER_ITEM)?.let {
if (it.storedMatter.isPositive) { if (it.storedMatter.isPositive) {
return false return false
} }
} }
return true return super.canAutomationTakeItem(desired)
} }
}))
override val maxStackSize: Int
get() = 1
}
private fun containerUpdated() {
markDirtyFast()
updateGaugeLevel()
}
val container = SlottedContainer.simple(BatteryBankBlockEntity.CAPACITY, ::Slot, ::containerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
val capacitorStatus = immutableList(BatteryBankBlockEntity.CAPACITY) { val capacitorStatus = immutableList(BatteryBankBlockEntity.CAPACITY) {
syncher.boolean(false) syncher.boolean(false)

View File

@ -21,8 +21,10 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
@ -72,22 +74,23 @@ class MatterDecomposerBlockEntity(pos: BlockPos, state: BlockState)
savetables.stateful(::matter, MATTER_STORAGE_KEY) savetables.stateful(::matter, MATTER_STORAGE_KEY)
} }
private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && MatterManager.canDecompose(itemStack)
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
// вход, выход // вход, выход
val inputContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val inputContainer = SlottedContainer.simple(1, ::InputSlot, ::markDirtyFast).also(::addDroppableContainer)
val outputContainer = MatteryContainer(::markDirtyFast, 2).also(::addDroppableContainer) val outputContainer = SlottedContainer.simple(2, AutomationFilters.ONLY_OUT.simpleProvider, ::markDirtyFast).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputContainer.handler(object : HandlerFilter { input = inputContainer,
override fun canInsert(slot: Int, stack: ItemStack): Boolean { output = outputContainer
return MatterManager.canDecompose(stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}),
output = outputContainer.handler(HandlerFilter.OnlyOut)
) )
init { init {

View File

@ -8,6 +8,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.CraftingInput import net.minecraft.world.item.crafting.CraftingInput
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
@ -24,19 +25,28 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.forEach
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.MatterNode import ru.dbotthepony.mc.otm.graph.matter.MatterNode
import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterEntanglerMenu
import ru.dbotthepony.mc.otm.recipe.IMatterEntanglerRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>( class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterEntanglerBlockEntity.Job>(MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
MBlockEntities.MATTER_ENTANGLER, blockPos, blockState, Job.CODEC) {
class Job(itemStack: ItemStack, val matter: Decimal, ticks: Double, experience: Float) : ItemJob(itemStack, ticks, MachinesConfig.MATTER_ENTANGLER.energyConsumption, experience = experience) { class Job(itemStack: ItemStack, val matter: Decimal, ticks: Double, experience: Float) : ItemJob(itemStack, ticks, MachinesConfig.MATTER_ENTANGLER.energyConsumption, experience = experience) {
val matterPerTick = matter / ticks val matterPerTick = matter / ticks
@ -60,36 +70,67 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
val experience = ExperienceStorage(MachinesConfig.MATTER_ENTANGLER::maxExperienceStored).also(::addNeighbourListener) val experience = ExperienceStorage(MachinesConfig.MATTER_ENTANGLER::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val inputs = object : MatteryCraftingContainer(::itemContainerUpdated, 3, 3) { private var recipeCache: Collection<IMatterEntanglerRecipe> = listOf()
override fun getMaxStackSize(): Int { private var seenRecipeManager: RecipeManager? = null
return 1
private fun getRecipes(): Collection<IMatterEntanglerRecipe> {
val level = level!!
val manager = level.recipeManager
if (seenRecipeManager !== manager) {
seenRecipeManager = manager
val input = inputs.asPositionedCraftInput()
recipeCache = manager.byType(MRecipes.MATTER_ENTANGLER)
.iterator()
.map { it.value }
.filter { it.preemptivelyMatches(input, level, 3, 3) }
.toList()
} }
return recipeCache
} }
val output = object : MatteryContainer(::itemContainerUpdated, 1) { private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int { // may get stalled on /reload command for up to a minute
return Int.MAX_VALUE // shouldn't cause major issues through, since /reload is not something you frequently be executing
val insertCache = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1))
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return false
return insertCache.get(itemStack.asKey()) {
val list = container.toList()
list[slot] = itemStack
val shadow = CraftingInput.ofPositioned(3, 3, list)
getRecipes().any { it.preemptivelyMatches(shadow, level, 3, 3) }
}
} }
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override val maxStackSize: Int
get() = 1
} }
private fun inputsChanged() {
inputs.slotIterator().forEach { (it as InputSlot).insertCache.invalidateAll() }
seenRecipeManager = null
recipeCache = listOf()
setChanged()
}
val inputs = IEnhancedCraftingContainer.Wrapper(SlottedContainer.simple(3 * 3, ::InputSlot, ::inputsChanged), 3, 3)
val output = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.unlimitedSimpleProvider, ::markDirtyFast)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputs.handler(object : HandlerFilter { input = inputs.parent,
override fun canInsert(slot: Int, stack: ItemStack): Boolean { output = output
val list = inputs.toList()
list[slot] = stack
val shadow = CraftingInput.ofPositioned(3, 3, list)
return (level ?: return false)
.recipeManager
.byType(MRecipes.MATTER_ENTANGLER)
.any { it.value.preemptivelyMatches(shadow, level!!, 3, 3) }
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}),
output = output.handler(HandlerFilter.OnlyOut)
) )
init { init {
@ -98,7 +139,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
savetables.stateful(::energy, ENERGY_KEY) savetables.stateful(::energy, ENERGY_KEY)
savetables.stateful(::matter, MATTER_STORAGE_KEY) savetables.stateful(::matter, MATTER_STORAGE_KEY)
savetables.stateful(::upgrades) savetables.stateful(::upgrades)
savetables.stateful(::inputs) savetables.stateful(inputs::parent, "inputs")
savetables.stateful(::output) savetables.stateful(::output)
savetables.stateful(::experience) savetables.stateful(::experience)
@ -124,7 +165,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
val required = status.job.matterPerTick * status.ticksAdvanced val required = status.job.matterPerTick * status.ticksAdvanced
if (matter.storedMatter < required) { if (matter.storedMatter < required) {
matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceAtLeast(Decimal.TEN).coerceAtMost(matter.missingMatter), false), false) matter.receiveMatter(node.graph.extractMatter(status.job.matterPerTick.coerceIn(Decimal.TEN, matter.missingMatter), false), false)
} }
status.scale(matter.extractMatter(required, false) / required) status.scale(matter.extractMatter(required, false) / required)
@ -150,7 +191,7 @@ class MatterEntanglerBlockEntity(blockPos: BlockPos, blockState: BlockState) : M
if (!energy.batteryLevel.isPositive) if (!energy.batteryLevel.isPositive)
return JobContainer.noEnergy() return JobContainer.noEnergy()
val inputs = CraftingInput.of(3, 3, inputs.toList()) val inputs = this.inputs.asCraftInput()
val recipe = (level ?: return JobContainer.failure()) val recipe = (level ?: return JobContainer.failure())
.recipeManager .recipeManager

View File

@ -24,9 +24,9 @@ import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.UpgradeContainer import ru.dbotthepony.mc.otm.container.UpgradeContainer
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.registryName
@ -37,9 +37,35 @@ import ru.dbotthepony.mc.otm.menu.matter.MatterReconstructorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import java.util.function.BooleanSupplier import java.util.function.BooleanSupplier
class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity( class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.MATTER_RECONSTRUCTOR, blockPos, blockState) {
MBlockEntities.MATTER_RECONSTRUCTOR, blockPos, blockState) { private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val repairContainer = MatteryContainer(::containerChanged, 1).also(::addDroppableContainer) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack) || !itemStack.isRepairable || !itemStack.isDamaged) {
return false
}
if (MachinesConfig.MatterReconstructor.ALLOW_TO_SKIP_ANVIL && !MachinesConfig.MatterReconstructor.ONLY_ANVIL && MatterManager.get(itemStack.item).hasMatterValue) {
return true
}
return matterNode.graph
.patterns
.filter { itemStack.item.isValidRepairItem(itemStack, ItemStack(it.item, 1)) }
.findFirst().orElse(null).let {
if (it == null) {
IMatterValue.ZERO
} else {
MatterManager.get(it.item)
}
}.hasMatterValue
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && (progressPerTick <= 0.0 || matterPerTick <= Decimal.ZERO)
}
}
val repairContainer = SlottedContainer.simple(1, ::Slot, ::rescan).also(::addDroppableContainer)
private var matterPerTick = Decimal.ZERO private var matterPerTick = Decimal.ZERO
private var progressPerTick = 0.0 private var progressPerTick = 0.0
@ -67,15 +93,15 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
} }
override fun onPatternAdded(state: PatternState) { override fun onPatternAdded(state: PatternState) {
containerChanged() rescan()
} }
override fun onPatternRemoved(state: PatternState) { override fun onPatternRemoved(state: PatternState) {
containerChanged() rescan()
} }
override fun onPatternUpdated(newState: PatternState, oldState: PatternState) { override fun onPatternUpdated(newState: PatternState, oldState: PatternState) {
containerChanged() rescan()
} }
} }
@ -99,34 +125,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
inputOutput = repairContainer.handler(object : HandlerFilter { inputOutput = repairContainer
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
if (!stack.isRepairable || !stack.isDamaged) {
return false
}
if (MachinesConfig.MatterReconstructor.ALLOW_TO_SKIP_ANVIL && !MachinesConfig.MatterReconstructor.ONLY_ANVIL) {
if (MatterManager.get(stack.item).hasMatterValue) {
return true
}
}
return matterNode.graph
.patterns
.filter { stack.item.isValidRepairItem(stack, ItemStack(it.item, 1)) }
.findFirst().orElse(null).let {
if (it == null) {
IMatterValue.ZERO
} else {
MatterManager.get(it.item)
}
}.hasMatterValue
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return progressPerTick <= 0.0 || matterPerTick <= Decimal.ZERO
}
})
) )
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
@ -145,7 +144,7 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
private var changeset = 0 private var changeset = 0
private fun containerChanged() { private fun rescan() {
matterPerTick = Decimal.ZERO matterPerTick = Decimal.ZERO
progressPerTick = 0.0 progressPerTick = 0.0
@ -196,7 +195,6 @@ class MatterReconstructorBlockEntity(blockPos: BlockPos, blockState: BlockState)
} }
} }
@Suppress("name_shadowing")
val matter = MatterManager.get(found.item) * 2 val matter = MatterManager.get(found.item) * 2
progressPerTick = (item.maxDamage / matter.complexity) / MachinesConfig.MatterReconstructor.DIVISOR progressPerTick = (item.maxDamage / matter.complexity) / MachinesConfig.MatterReconstructor.DIVISOR

View File

@ -21,9 +21,10 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nextDecimal
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.graph.matter.MatterGraph import ru.dbotthepony.mc.otm.graph.matter.MatterGraph
import ru.dbotthepony.mc.otm.item.matter.MatterDustItem import ru.dbotthepony.mc.otm.item.matter.MatterDustItem
@ -33,9 +34,7 @@ import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.data.codec.minRange import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) {
: MatteryWorkerBlockEntity<MatterRecyclerBlockEntity.RecyclerJob>(MBlockEntities.MATTER_RECYCLER, blockPos, blockState, RecyclerJob.CODEC) {
class RecyclerJob(ticks: Double, powerUsage: Decimal, var totalMatter: Decimal) : Job(ticks, powerUsage) { class RecyclerJob(ticks: Double, powerUsage: Decimal, var totalMatter: Decimal) : Job(ticks, powerUsage) {
companion object { companion object {
val CODEC: Codec<RecyclerJob> by lazy { val CODEC: Codec<RecyclerJob> by lazy {
@ -48,20 +47,22 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
override val upgrades = makeUpgrades(3, UpgradeType.BASIC_MATTER) override val upgrades = makeUpgrades(3, UpgradeType.BASIC_MATTER)
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.OUTPUT, upgrades.matterCapacity(MachinesConfig.MatterRecycler.VALUES::matterCapacity))) val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.OUTPUT, upgrades.matterCapacity(MachinesConfig.MatterRecycler.VALUES::matterCapacity)))
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer) private class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.item is MatterDustItem
}
}
val container = SlottedContainer.simple(1, ::Slot, ::itemContainerUpdated).also(::addDroppableContainer)
val matterNode = SimpleMatterNode(matter = matter) val matterNode = SimpleMatterNode(matter = matter)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MatterRecycler.VALUES))) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MatterRecycler.VALUES)))
val itemConfig = ConfigurableItemHandler(input = container.handler(object : HandlerFilter { val itemConfig = ConfigurableItemHandler(container)
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.item is MatterDustItem
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}))
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
@ -137,7 +138,7 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
else if (matter.receiveMatter(toReceive, true) != toReceive) else if (matter.receiveMatter(toReceive, true) != toReceive)
return status.noMatter() return status.noMatter()
matter.receiveMatter(toReceive * 0.4 + level!!.otmRandom.nextDouble() * 0.6, false) matter.receiveMatter(toReceive * level!!.otmRandom.nextDecimal(BASE_RECEIVE, Decimal.ONE), false)
job.totalMatter -= toReceive job.totalMatter -= toReceive
} }
@ -151,4 +152,8 @@ class MatterRecyclerBlockEntity(blockPos: BlockPos, blockState: BlockState)
matter.extractMatter(received, false) matter.extractMatter(received, false)
} }
} }
companion object {
private val BASE_RECEIVE = Decimal("0.4")
}
} }

View File

@ -25,8 +25,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
@ -71,11 +71,11 @@ class MatterReplicatorBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
override val upgrades = makeUpgrades(3, UpgradeType.REPLICATOR) override val upgrades = makeUpgrades(3, UpgradeType.REPLICATOR)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_REPLICATOR))) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_REPLICATOR)))
val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.INPUT, upgrades.matterCapacity(MachinesConfig.MATTER_REPLICATOR::matterCapacity))) val matter = ProfiledMatterStorage(MatterStorageImpl(::matterLevelUpdated, FlowDirection.INPUT, upgrades.matterCapacity(MachinesConfig.MATTER_REPLICATOR::matterCapacity)))
val outputContainer = MatteryContainer(::itemContainerUpdated, 3).also(::addDroppableContainer) val outputContainer = SlottedContainer.simple(3, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val dustContainer = MatteryContainer(::itemContainerUpdated, 2).also(::addDroppableContainer) val dustContainer = SlottedContainer.simple(2, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler(output = CombinedItemHandler(outputContainer.handler(HandlerFilter.OnlyOut), dustContainer.handler(HandlerFilter.OnlyOut))) val itemConfig = ConfigurableItemHandler(output = CombinedItemHandler(outputContainer, dustContainer))
val matterNode = object : MatterNode() { val matterNode = object : MatterNode() {
override fun getMatterHandler(): IMatterStorage { override fun getMatterHandler(): IMatterStorage {

View File

@ -20,8 +20,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.matter.PatternState import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.matter.MatterScannerMenu import ru.dbotthepony.mc.otm.menu.matter.MatterScannerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.graph.matter.MatterNode import ru.dbotthepony.mc.otm.graph.matter.MatterNode
@ -34,19 +34,20 @@ class MatterScannerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryWorkerBlockEntity<ItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ItemJob.CODEC) { MatteryWorkerBlockEntity<ItemJob>(MBlockEntities.MATTER_SCANNER, p_155229_, p_155230_, ItemJob.CODEC) {
override val upgrades = makeUpgrades(2, UpgradeType.BASIC) override val upgrades = makeUpgrades(2, UpgradeType.BASIC)
val container = MatteryContainer(::itemContainerUpdated, 1).also(::addDroppableContainer)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_SCANNER))) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(::energyLevelUpdated, upgrades.transform(MachinesConfig.MATTER_SCANNER)))
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(object : HandlerFilter { private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun canInsert(slot: Int, stack: ItemStack): Boolean { override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return MatterManager.canDecompose(stack) return super.canAutomationPlaceItem(itemStack) && MatterManager.canDecompose(itemStack)
} }
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean { override fun canAutomationTakeItem(desired: Int): Boolean {
return jobEventLoops[0].isIdling return jobEventLoops[0].isIdling && super.canAutomationTakeItem(desired)
} }
})) }
val container = SlottedContainer.simple(1, ::Slot, ::itemContainerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val matterNode = object : MatterNode() { val matterNode = object : MatterNode() {

View File

@ -1,8 +1,8 @@
package ru.dbotthepony.mc.otm.block.entity.matter package ru.dbotthepony.mc.otm.block.entity.matter
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.container.MatteryContainer
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock import ru.dbotthepony.mc.otm.block.matter.PatternStorageBlock
@ -14,7 +14,8 @@ import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.collect.filterNotNull import ru.dbotthepony.mc.otm.core.collect.filterNotNull
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.filterNotNull import ru.dbotthepony.mc.otm.core.filterNotNull
@ -23,53 +24,48 @@ import ru.dbotthepony.mc.otm.graph.matter.SimpleMatterNode
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import java.util.stream.Stream import java.util.stream.Stream
class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IPatternStorage {
MatteryDeviceBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IPatternStorage {
val matterNode = SimpleMatterNode(patterns = this) val matterNode = SimpleMatterNode(patterns = this)
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 8) { private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override val maxStackSize: Int
if (!ItemStack.isSameItemSameComponents(new, old)) { get() = 1
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(MatteryCapability.PATTERN_ITEM) != null
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
override fun notifyChanged(old: ItemStack) {
if (!ItemStack.isSameItemSameComponents(item, old)) {
if (!old.isEmpty) { if (!old.isEmpty) {
old.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage -> old.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage ->
cap.patterns.forEach { matterNode.graph.onPatternRemoved(it) } cap.patterns.forEach { matterNode.graph.onPatternRemoved(it) }
} }
} }
if (!new.isEmpty) { if (!item.isEmpty) {
new.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage -> item.getCapability(MatteryCapability.PATTERN_ITEM)?.let { cap: IPatternStorage ->
cap.patterns.forEach { matterNode.graph.onPatternAdded(it) } cap.patterns.forEach { matterNode.graph.onPatternAdded(it) }
} }
} }
updateBlockstate() val level = level
if (level is ServerLevel) {
level.setBlock(blockPos, blockState.setValue(PatternStorageBlock.PATTERN_STORAGE_DISKS_PROPS[slot], item.getCapability(MatteryCapability.PATTERN_ITEM) != null), Block.UPDATE_CLIENTS)
}
} }
super.setChanged(slot, new, old) super.notifyChanged(old)
}
override fun getMaxStackSize(): Int = 1
}.also(::addDroppableContainer)
private fun updateBlockstate() {
val level = level ?: return
var state = blockState
for (i in 0..7) {
state = state.setValue(
PatternStorageBlock.PATTERN_STORAGE_DISKS_PROPS[i],
this.container.getItem(i).getCapability(MatteryCapability.PATTERN_ITEM) != null
)
}
if (state !== blockState) {
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
} }
} }
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(HandlerFilter.IsPattern.and(HandlerFilter.OnlyIn))) val container = SlottedContainer.simple(2 * 4, ::Slot, ::markDirtyFast).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(inputOutput = container)
override fun setLevel(level: Level) { override fun setLevel(level: Level) {
super.setLevel(level) super.setLevel(level)
@ -96,20 +92,12 @@ class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
} }
override val patternCapacity: Int get() { override val patternCapacity: Int get() {
var stored = 0L val stored = container.sumOf { it.getCapability(MatteryCapability.PATTERN_ITEM)?.patternCapacity?.toLong() ?: 0L }
for (pattern in this.container.iterator().map { it.getCapability(MatteryCapability.PATTERN_ITEM) }.filterNotNull())
stored += pattern.patternCapacity.toLong()
return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt() return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt()
} }
override val storedPatterns: Int get() { override val storedPatterns: Int get() {
var stored = 0L val stored = container.sumOf { it.getCapability(MatteryCapability.PATTERN_ITEM)?.storedPatterns?.toLong() ?: 0L }
for (pattern in this.container.iterator().map { it.getCapability(MatteryCapability.PATTERN_ITEM) }.filterNotNull())
stored += pattern.storedPatterns.toLong()
return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt() return if (stored > Int.MAX_VALUE) Int.MAX_VALUE else stored.toInt()
} }

View File

@ -14,7 +14,8 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.storage.optics.priority import ru.dbotthepony.mc.otm.storage.optics.priority
@ -45,19 +46,21 @@ class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : Mattery
markDirtyFast() markDirtyFast()
} }
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 4) { private inner class Slot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override fun notifyChanged(old: ItemStack) {
super.setChanged(slot, new, old) super.notifyChanged(old)
old.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let { old.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let {
cell.removeStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode)) cell.removeStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode))
} }
new.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let { item.getCapability(MatteryCapability.CONDENSATION_DRIVE)?.let {
cell.addStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode)) cell.addStorageComponent(it.priority(::insertPriority, ::extractPriority).powered(energy).flow(::mode))
} }
} }
}.also(::addDroppableContainer) }
val container = SlottedContainer.simple(4, ::Slot, ::markDirtyFast).also(::addDroppableContainer)
init { init {
savetables.stateful(::energy, ENERGY_KEY) savetables.stateful(::energy, ENERGY_KEY)

View File

@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
@ -29,15 +29,16 @@ class DriveViewerBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER)) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER))
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val container: MatteryContainer = object : MatteryContainer(::markDirtyFast, 1) { val container: EnhancedContainer.Simple = object : EnhancedContainer.Simple(1) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.setChanged(slot, new, old) super.notifySlotChanged(slot, old)
markDirtyFast()
val level = level val level = level
if (level is ServerLevel) { if (level is ServerLevel) {
tickList.once { tickList.once {
val isPresent = new.getCapability(MatteryCapability.CONDENSATION_DRIVE) != null val isPresent = get(slot).getCapability(MatteryCapability.CONDENSATION_DRIVE) != null
var state = this@DriveViewerBlockEntity.blockState.setValue(DriveViewerBlock.DRIVE_PRESENT, isPresent) var state = this@DriveViewerBlockEntity.blockState.setValue(DriveViewerBlock.DRIVE_PRESENT, isPresent)
if (!isPresent) { if (!isPresent) {

View File

@ -29,12 +29,14 @@ import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets8 import ru.dbotthepony.mc.otm.client.render.Widgets8
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.MatteryCraftingContainer import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.IEnhancedCraftingContainer
import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.collect.map import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.collect.toList import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapString import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
@ -94,42 +96,42 @@ class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, IItemMonitorPla
enum class IngredientPriority(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting { enum class IngredientPriority(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
// Refill everything from system // Refill everything from system
SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system"), lazy { Widgets8.WHITE_ARROW_DOWN }, UVWindingOrder.FLIP) { SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system"), lazy { Widgets8.WHITE_ARROW_DOWN }, UVWindingOrder.FLIP) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean { override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(id, view) return takeOne(id, view)
} }
}, },
// Refill everything from player's inventory // Refill everything from player's inventory
INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory"), lazy { Widgets8.WHITE_ARROW_DOWN }) { INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory"), lazy { Widgets8.WHITE_ARROW_DOWN }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean { override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(inventory, item) return takeOne(inventory, item)
} }
}, },
// Refill everything from system, if can't refill from player's inventory // Refill everything from system, if can't refill from player's inventory
SYSTEM_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.system_first"), lazy { Widgets8.ARROW_SIDEWAYS }, UVWindingOrder.FLIP) { SYSTEM_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.system_first"), lazy { Widgets8.ARROW_SIDEWAYS }, UVWindingOrder.FLIP) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean { override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(id, view) || takeOne(inventory, item) return takeOne(id, view) || takeOne(inventory, item)
} }
}, },
// Refill everything from player's inventory, if can't refill from system // Refill everything from player's inventory, if can't refill from system
INVENTORY_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory_first"), lazy { Widgets8.ARROW_SIDEWAYS }) { INVENTORY_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory_first"), lazy { Widgets8.ARROW_SIDEWAYS }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean { override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return takeOne(inventory, item) || takeOne(id, view) return takeOne(inventory, item) || takeOne(id, view)
} }
}, },
// Do not refill (?) // Do not refill (?)
DO_NOT(TranslatableComponent("otm.gui.item_monitor.refill_source.do_not"), lazy { Widgets8.MINUS }) { DO_NOT(TranslatableComponent("otm.gui.item_monitor.refill_source.do_not"), lazy { Widgets8.MINUS }) {
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean { override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean {
return false return false
} }
}; };
override val icon: IGUIRenderable by icon override val icon: IGUIRenderable by icon
abstract fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean abstract fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer<*>?, id: UUID?): Boolean
} }
enum class ResultTarget(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting { enum class ResultTarget(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
@ -216,17 +218,13 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount.getInt(ply) fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount.getInt(ply)
fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply] fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply]
// a lot of code is hardcoded to take CraftingContainer as it's input val craftingGrid = IEnhancedCraftingContainer.Wrapper(EnhancedContainer.WithListener(3 * 3) {
// hence we are forced to work around this by providing proxy container markDirtyFast()
val craftingGrid = object : MatteryCraftingContainer(::markDirtyFast, 3, 3) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
markDirtyFast()
if (!inProcessOfCraft) { if (!inProcessOfCraft) {
scanCraftingGrid(false) scanCraftingGrid(false)
}
} }
}.also(::addDroppableContainer) }, 3, 3).also(::addDroppableContainer)
private fun scanCraftingGrid(justCheckForRecipeChange: Boolean): Boolean { private fun scanCraftingGrid(justCheckForRecipeChange: Boolean): Boolean {
val level = level ?: return false val level = level ?: return false
@ -346,8 +344,8 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
check(residue.size == craftingGrid.containerSize) { "Container and residue list sizes mismatch: ${residue.size} != ${craftingGrid.containerSize}" } check(residue.size == craftingGrid.containerSize) { "Container and residue list sizes mismatch: ${residue.size} != ${craftingGrid.containerSize}" }
val combinedInventory = craftingPlayer.matteryPlayer?.inventoryAndExopack val combinedInventory = craftingPlayer.matteryPlayer.inventoryAndExopack
val copy = craftingGrid.iterator(false).map { it.copy() }.toList() val copy = craftingGrid.parent.copyToList()
// удаляем по одному предмету из сетки крафта // удаляем по одному предмету из сетки крафта
for (slot in 0 until craftingGrid.containerSize) { for (slot in 0 until craftingGrid.containerSize) {
@ -381,7 +379,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
remaining = poweredView?.insertStack(ItemStorageStack(remaining), false)?.toItemStack() ?: remaining remaining = poweredView?.insertStack(ItemStorageStack(remaining), false)?.toItemStack() ?: remaining
} }
remaining = combinedInventory?.addItem(remaining, false) ?: remaining remaining = combinedInventory.addItem(remaining, false)
if (remaining.isNotEmpty) { if (remaining.isNotEmpty) {
craftingPlayer.spawnAtLocation(remaining) craftingPlayer.spawnAtLocation(remaining)
@ -425,7 +423,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
} }
}) })
nbt["crafting_grid"] = craftingGrid.serializeNBT(registry) nbt["crafting_grid"] = craftingGrid.parent.serializeNBT(registry)
} }
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -438,7 +436,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
check(this.settings.put(UUID.fromString(key), ItemMonitorPlayerSettings().also { it.deserializeNBT(registry, settings.getCompound(key)) }) == null) check(this.settings.put(UUID.fromString(key), ItemMonitorPlayerSettings().also { it.deserializeNBT(registry, settings.getCompound(key)) }) == null)
} }
craftingGrid.deserializeNBT(registry, nbt["crafting_grid"]) nbt.map("crafting_grid", craftingGrid.parent::deserializeNBT, registry)
} }
fun getSettings(ply: ServerPlayer): ItemMonitorPlayerSettings { fun getSettings(ply: ServerPlayer): ItemMonitorPlayerSettings {

View File

@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilterSet
import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.isPositive import ru.dbotthepony.mc.otm.core.math.isPositive
@ -123,7 +123,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
}) })
} }
var filter = ItemFilter(MAX_FILTERS) var filter = ItemFilterSet.EMPTY
set(value) { set(value) {
field = value field = value
component?.scan() component?.scan()
@ -131,7 +131,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
} }
init { init {
savetablesConfig.codec(::filter, ItemFilter.CODEC, FILTER_KEY, Supplier { ItemFilter(MAX_FILTERS) }) savetablesConfig.codec(::filter, ItemFilterSet.CODEC, FILTER_KEY, Supplier { ItemFilterSet.EMPTY })
} }
override fun setLevel(level: Level) { override fun setLevel(level: Level) {
@ -348,7 +348,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
} }
fun scan(slot: Int) { fun scan(slot: Int) {
val current = parent[slot].let { if (it.isEmpty || !filter.match(it)) null else it } val current = parent[slot].let { if (it.isEmpty || !filter.test(it)) null else it }
val last = slot2itemStack[slot] val last = slot2itemStack[slot]
if (current == null && last != null) { if (current == null && last != null) {
@ -374,7 +374,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
} }
override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack { override fun insertStack(stack: ItemStorageStack, simulate: Boolean): ItemStorageStack {
if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.match(stack.toItemStack()) || !mode.input) if (redstoneControl.isBlockedByRedstone || energy.batteryLevel.isZero || !filter.test(stack.toItemStack()) || !mode.input)
return stack return stack
val required = StorageStack.ITEMS.energyPerInsert(stack) val required = StorageStack.ITEMS.energyPerInsert(stack)

View File

@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.ItemFilter import ru.dbotthepony.mc.otm.container.ItemFilterSet
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
@ -98,7 +98,7 @@ abstract class AbstractStorageImportExport(
protected val target = CapabilityCache(RelativeSide.FRONT, Capabilities.ItemHandler.BLOCK) protected val target = CapabilityCache(RelativeSide.FRONT, Capabilities.ItemHandler.BLOCK)
var filter: ItemFilter = ItemFilter(MAX_FILTERS) var filter: ItemFilterSet = ItemFilterSet.EMPTY
set(value) { set(value) {
if (value != field) { if (value != field) {
field = value field = value
@ -112,7 +112,7 @@ abstract class AbstractStorageImportExport(
} }
init { init {
savetablesConfig.codec(::filter, ItemFilter.CODEC, FILTER_KEY, Supplier { ItemFilter(MAX_FILTERS) }) savetablesConfig.codec(::filter, ItemFilterSet.CODEC, FILTER_KEY, Supplier { ItemFilterSet.EMPTY })
} }
companion object { companion object {
@ -168,7 +168,7 @@ class StorageImporterBlockEntity(
} }
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack { override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (redstoneControl.isBlockedByRedstone || !filter.match(stack)) if (redstoneControl.isBlockedByRedstone || !filter.test(stack))
return stack return stack
return acceptItem(stack, simulate) return acceptItem(stack, simulate)
@ -183,7 +183,7 @@ class StorageImporterBlockEntity(
} }
override fun isItemValid(slot: Int, stack: ItemStack): Boolean { override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return filter.match(stack) return filter.test(stack)
} }
override fun tick() { override fun tick() {
@ -205,7 +205,7 @@ class StorageImporterBlockEntity(
val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true) val extracted = target.extractItem(lastSlot, MAX_MOVE_PER_OPERATION, true)
if (extracted.isEmpty || !filter.match(extracted)) { if (extracted.isEmpty || !filter.test(extracted)) {
lastSlot++ lastSlot++
} else { } else {
val leftover = acceptItem(extracted, true) val leftover = acceptItem(extracted, true)
@ -244,7 +244,7 @@ class StorageExporterBlockEntity(blockPos: BlockPos, blockState: BlockState) :
} }
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) { override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
if (filter.match(stack.toItemStack())) { if (filter.test(stack.toItemStack())) {
relevantTuples.add(id) relevantTuples.add(id)
} }
} }

View File

@ -16,6 +16,7 @@ import net.minecraft.world.item.crafting.SmokingRecipe
import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.event.AddReloadListenerEvent
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
@ -29,33 +30,60 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.config.WorkerBalanceValues import ru.dbotthepony.mc.otm.config.WorkerBalanceValues
import ru.dbotthepony.mc.otm.container.CombinedContainer import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.balance import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.core.collect.maybe import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu import ru.dbotthepony.mc.otm.menu.tech.PoweredFurnaceMenu
import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe import ru.dbotthepony.mc.otm.recipe.MatteryCookingRecipe
import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe import ru.dbotthepony.mc.otm.recipe.MicrowaveRecipe
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : MatteryCookingRecipe>( sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : MatteryCookingRecipe>(
type: BlockEntityType<*>, type: BlockEntityType<*>,
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState, blockState: BlockState,
val recipeType: RecipeType<P>,
val secondaryRecipeType: RecipeType<S>?,
val config: WorkerBalanceValues,
maxJobs: Int = 2 maxJobs: Int = 2
) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, maxJobs) { ) : MatteryWorkerBlockEntity<ItemJob>(type, blockPos, blockState, ItemJob.CODEC, maxJobs) {
abstract val recipeType: RecipeType<P>
abstract val secondaryRecipeType: RecipeType<S>?
abstract val config: WorkerBalanceValues
final override val upgrades = makeUpgrades(2, UpgradeType.BASIC_PROCESSING) final override val upgrades = makeUpgrades(2, UpgradeType.BASIC_PROCESSING)
final override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config))) final override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(config)))
val inputs = MatteryContainer(this::itemContainerUpdated, maxJobs) private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
val outputs = MatteryContainer(this::itemContainerUpdated, maxJobs) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return true
return acceptableItems.get(itemStack.asKey()) {
val input = SingleRecipeInput(itemStack)
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null && level.recipeManager.byType(secondaryRecipeType).any { it.value.matches(input, level) })
return@get true
return@get level.recipeManager.byType(recipeType).any { it.value.matches(input, level) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
val inputs = SlottedContainer.simple(maxJobs, ::InputSlot, ::itemContainerUpdated)
val outputs = SlottedContainer.simple(maxJobs, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated)
init { init {
addDroppableContainer(inputs) addDroppableContainer(inputs)
@ -76,9 +104,9 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
val experience = ExperienceStorage(config::maxExperienceStored).also(::addNeighbourListener) val experience = ExperienceStorage(config::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputs.handler(HandlerFilter.OnlyIn), input = inputs,
output = outputs.handler(HandlerFilter.OnlyOut), output = outputs,
battery = batteryItemHandler battery = batteryContainer
) )
init { init {
@ -127,13 +155,13 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
return JobContainer.noItem() return JobContainer.noItem()
val level = level as? ServerLevel ?: return JobContainer.failure() val level = level as? ServerLevel ?: return JobContainer.failure()
val secondaryRecipeType = secondaryRecipeType
if (secondaryRecipeType != null) { if (secondaryRecipeType != null) {
val recipe = level.recipeManager val recipe = level.recipeManager
.byType(secondaryRecipeType) .byType(secondaryRecipeType)
.iterator() .firstOrNull { it.value.matches(SingleRecipeInput(inputs[id]), level) }
.filter { it.value.matches(SingleRecipeInput(inputs[id]), level) } ?.value
.maybe()?.value
if (recipe != null) { if (recipe != null) {
val toProcess = inputs[id].count.coerceAtMost(1 + upgrades.processingItems) val toProcess = inputs[id].count.coerceAtMost(1 + upgrades.processingItems)
@ -172,24 +200,51 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
)) ))
}.orElse(JobContainer.noItem()) }.orElse(JobContainer.noItem())
} }
companion object {
// shared by all furnace instances, so cache should be large enough
private val acceptableItems = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1))
internal fun onReload(event: AddReloadListenerEvent) {
acceptableItems.invalidateAll()
}
}
} }
class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>( class PoweredFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmeltingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_FURNACE, blockPos, blockState) {
MBlockEntities.POWERED_FURNACE, blockPos, blockState, RecipeType.SMELTING, null, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<SmeltingRecipe>
get() = RecipeType.SMELTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.furnace(containerID, inventory, this) return PoweredFurnaceMenu.furnace(containerID, inventory, this)
} }
} }
class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>( class PoweredBlastFurnaceBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<BlastingRecipe, MatteryCookingRecipe>(MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState) {
MBlockEntities.POWERED_BLAST_FURNACE, blockPos, blockState, RecipeType.BLASTING, null, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<BlastingRecipe>
get() = RecipeType.BLASTING
override val secondaryRecipeType: RecipeType<MatteryCookingRecipe>?
get() = null
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.blasting(containerID, inventory, this) return PoweredFurnaceMenu.blasting(containerID, inventory, this)
} }
} }
class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>( class PoweredSmokerBlockEntity(blockPos: BlockPos, blockState: BlockState) : AbstractPoweredFurnaceBlockEntity<SmokingRecipe, MicrowaveRecipe>(MBlockEntities.POWERED_SMOKER, blockPos, blockState) {
MBlockEntities.POWERED_SMOKER, blockPos, blockState, RecipeType.SMOKING, MRecipes.MICROWAVE, MachinesConfig.POWERED_FURNACE) { override val recipeType: RecipeType<SmokingRecipe>
get() = RecipeType.SMOKING
override val secondaryRecipeType: RecipeType<MicrowaveRecipe>
get() = MRecipes.MICROWAVE
override val config: WorkerBalanceValues
get() = MachinesConfig.POWERED_FURNACE
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return PoweredFurnaceMenu.smoking(containerID, inventory, this) return PoweredFurnaceMenu.smoking(containerID, inventory, this)
} }

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity.tech package ru.dbotthepony.mc.otm.block.entity.tech
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
@ -12,6 +13,7 @@ import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kommons.util.value import ru.dbotthepony.kommons.util.value
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy import ru.dbotthepony.mc.otm.capability.energy
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
@ -19,9 +21,12 @@ import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.matteryEnergy import ru.dbotthepony.mc.otm.capability.matteryEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.transcieveEnergy import ru.dbotthepony.mc.otm.capability.transcieveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotRange
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.immutableList import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.shuffle import ru.dbotthepony.mc.otm.core.shuffle
@ -33,31 +38,73 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
var gaugeLevel by syncher.float() var gaugeLevel by syncher.float()
private set private set
// 6 на 2 private var containerSlotIndices = IntArray(0)
val container: MatteryContainer = object : MatteryContainer(::setChanged, CAPACITY) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) { private inner class Slot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
super.setChanged(slot, new, old) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
batteryStatus[slot].value = new.getCapability(Capabilities.EnergyStorage.ITEM) != null return super.canAutomationPlaceItem(itemStack) && itemStack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canExtract() && it.extractEnergy(Int.MAX_VALUE, true) > 0 } == true
gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
} }
override fun getMaxStackSize(): Int = 1 override fun canAutomationTakeItem(desired: Int): Boolean {
}.also(::addDroppableContainer) return super.canAutomationTakeItem(desired) && item.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canExtract() || it.extractEnergy(Int.MAX_VALUE, true) <= 0 } != false
}
val batteryStatus = immutableList(CAPACITY) { override fun notifyChanged(old: ItemStack) {
syncher.boolean(false) super.notifyChanged(old)
batteryStatus[slot].value = item.getCapability(Capabilities.EnergyStorage.ITEM) != null
updateGaugeLevel()
}
override val maxStackSize: Int
get() = 1
} }
val itemConfig = ConfigurableItemHandler(inputOutput = container.handler(HandlerFilter.Dischargeable)) private fun containerUpdated() {
markDirtyFast()
val newSlots = IntArrayList()
for (i in 0 until container.containerSize) {
val stack = container[i]
if (stack.isNotEmpty && stack.energy != null)
newSlots.add(i)
}
containerSlotIndices = newSlots.toIntArray()
}
// 6 на 2
val container = SlottedContainer.simple(CAPACITY, ::Slot, ::containerUpdated).also(::addDroppableContainer)
val batteryStatus = immutableList(CAPACITY) { syncher.boolean(false) }
val itemConfig = ConfigurableItemHandler(inputOutput = container)
init { init {
savetables.stateful(::container, INVENTORY_KEY) savetables.stateful(::container, INVENTORY_KEY)
} }
private val containerSlotIndices = IntArray(CAPACITY) { it } private fun updateGaugeLevel() {
var stored = Decimal.ZERO
var maxStored = Decimal.ZERO
for (stack in container) {
val cap = stack.energy ?: continue
stored += cap.energyStoredMattery
maxStored += cap.maxEnergyStoredMattery
}
gaugeLevel = stored.percentage(maxStored)
}
override fun tick() {
super.tick()
updateGaugeLevel()
}
private fun distributeEnergy(isReceiving: Boolean, howMuch: Decimal, simulate: Boolean): Decimal { private fun distributeEnergy(isReceiving: Boolean, howMuch: Decimal, simulate: Boolean): Decimal {
if (!howMuch.isPositive) if (!howMuch.isPositive || containerSlotIndices.isEmpty())
return Decimal.ZERO return Decimal.ZERO
containerSlotIndices.shuffle(level!!.otmRandom) containerSlotIndices.shuffle(level!!.otmRandom)
@ -84,7 +131,6 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
if (!simulate && !summ.isZero) { if (!simulate && !summ.isZero) {
markDirtyFast() markDirtyFast()
gaugeLevel = batteryLevel.percentage(maxBatteryLevel)
} }
return summ return summ
@ -108,13 +154,9 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
get() { get() {
var result = Decimal.ZERO var result = Decimal.ZERO
for (i in 0 until container.containerSize) { for (stack in container) {
val stack = container.getItem(i) stack.energy?.let {
result += it.energyStoredMattery
if (!stack.isEmpty) {
stack.energy?.let {
result += it.energyStoredMattery
}
} }
} }
@ -128,13 +170,9 @@ class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
get() { get() {
var result = Decimal.ZERO var result = Decimal.ZERO
for (i in 0 until container.containerSize) { for (stack in container) {
val stack = container.getItem(i) stack.energy?.let {
result += it.maxEnergyStoredMattery
if (!stack.isEmpty) {
stack.energy?.let {
result += it.maxEnergyStoredMattery
}
} }
} }

View File

@ -12,8 +12,8 @@ import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
@ -23,20 +23,16 @@ class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDe
return ChemicalGeneratorMenu(containerID, inventory, this) return ChemicalGeneratorMenu(containerID, inventory, this)
} }
val batteryContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val batteryContainer = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val residueContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val residueContainer = SlottedContainer.simple(1, AutomationFilters.ONLY_OUT.simpleProvider, ::markDirtyFast).also(::addDroppableContainer)
val fuelContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val fuelContainer = SlottedContainer.simple(1, AutomationFilters.CHEMICAL_FUEL.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val batteryItemHandler = batteryContainer.handler(HandlerFilter.Chargeable)
val residueItemHandler = residueContainer.handler(HandlerFilter.OnlyOut)
val fuelItemHandler = fuelContainer.handler(HandlerFilter.ChemicalFuel)
val energy = ProfiledEnergyStorage(GeneratorEnergyStorage(::markDirtyFast, MachinesConfig.ChemicalGenerator.VALUES::energyCapacity, MachinesConfig.ChemicalGenerator.VALUES::energyThroughput)) val energy = ProfiledEnergyStorage(GeneratorEnergyStorage(::markDirtyFast, MachinesConfig.ChemicalGenerator.VALUES::energyCapacity, MachinesConfig.ChemicalGenerator.VALUES::energyThroughput))
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = fuelItemHandler, input = fuelContainer,
output = residueItemHandler, output = residueContainer,
battery = batteryItemHandler, battery = batteryContainer,
backDefault = ItemHandlerMode.BATTERY) backDefault = ItemHandlerMode.BATTERY)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)

View File

@ -11,8 +11,8 @@ import ru.dbotthepony.mc.otm.block.entity.ItemJob
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus import ru.dbotthepony.mc.otm.block.entity.JobStatus
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -22,8 +22,8 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
return CobblerMenu(containerID, inventory, this) return CobblerMenu(containerID, inventory, this)
} }
val container = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE).also(::addDroppableContainer) val container = SlottedContainer.simple(CONTAINER_SIZE, AutomationFilters.ONLY_OUT.simpleProvider, ::itemContainerUpdated).also(::addDroppableContainer)
val itemConfig = ConfigurableItemHandler(output = container.handler(HandlerFilter.OnlyOut)) val itemConfig = ConfigurableItemHandler(output = container)
init { init {
savetables.stateful(::container, INVENTORY_KEY) savetables.stateful(::container, INVENTORY_KEY)

View File

@ -22,7 +22,7 @@ import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
import ru.dbotthepony.mc.otm.core.math.BlockRotation import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.mapPresent import ru.dbotthepony.mc.otm.core.nbt.mapPresent
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.countingLazy import ru.dbotthepony.mc.otm.core.util.countingLazy
@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) {
var passed by syncher.decimal() var passed by syncher.decimal()
var pullEnergyFromInput by syncher.boolean()
override val blockRotation: BlockRotation by countingLazy(blockStateChangesCounter) { override val blockRotation: BlockRotation by countingLazy(blockStateChangesCounter) {
BlockRotation.of(blockState[EnergyCounterBlock.INPUT_DIRECTION]) BlockRotation.of(blockState[EnergyCounterBlock.INPUT_DIRECTION])
@ -86,10 +87,8 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
savetables.stateful(::history1h) savetables.stateful(::history1h)
savetables.stateful(::history6h) savetables.stateful(::history6h)
savetables.stateful(::history24h) savetables.stateful(::history24h)
}
override fun saveLevel(nbt: CompoundTag, registry: HolderLookup.Provider) { savetablesConfig.bool(::pullEnergyFromInput)
super.saveLevel(nbt, registry)
} }
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -267,6 +266,14 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
override fun tick() { override fun tick() {
super.tick() super.tick()
if (pullEnergyFromInput) {
val input = inputCapability
val output = outputCapability
if (input != null && output != null)
thisTick += moveEnergy(source = input, destination = output, simulate = false)
}
lastTick = thisTick lastTick = thisTick
charts.forEach { it.add(thisTick) } charts.forEach { it.add(thisTick) }
thisTick = Decimal.ZERO thisTick = Decimal.ZERO

View File

@ -16,8 +16,8 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
@ -32,13 +32,12 @@ class EnergyHatchBlockEntity(
) : MatteryDeviceBlockEntity(type, blockPos, blockState) { ) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val energy = ProfiledEnergyStorage(BlockEnergyStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), capacity)) val energy = ProfiledEnergyStorage(BlockEnergyStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), capacity))
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) { val container = SlottedContainer.simple(
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int { CAPACITY,
return 1 if (isInput) AutomationFilters.DISCHARGABLE.limitedFilteredProvider else AutomationFilters.CHARGEABLE.limitedFilteredProvider,
} ::markDirtyFast
}.also(::addDroppableContainer) ).also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>() private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()
init { init {
@ -47,7 +46,7 @@ class EnergyHatchBlockEntity(
// it would cause a lot of frustration if hatches accept stuff only though one face // it would cause a lot of frustration if hatches accept stuff only though one face
exposeEnergyGlobally(energy) exposeEnergyGlobally(energy)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
if (!isInput) { if (!isInput) {
for (side in RelativeSide.entries) { for (side in RelativeSide.entries) {

View File

@ -18,8 +18,8 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess
@ -56,13 +56,11 @@ class EnergyInterfaceBlockEntity(
targets.invalidate() targets.invalidate()
} }
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) { val container = SlottedContainer.simple(
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int { CAPACITY,
return 1 if (isInput) AutomationFilters.DISCHARGABLE.limitedFilteredProvider else AutomationFilters.CHARGEABLE.limitedFilteredProvider,
} ::markDirtyFast).also(::addDroppableContainer)
}.also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>() private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()
override fun setRemoved() { override fun setRemoved() {
@ -75,7 +73,7 @@ class EnergyInterfaceBlockEntity(
// it would cause a lot of frustration if hatches accept stuff only though one face // it would cause a lot of frustration if hatches accept stuff only though one face
exposeEnergyGlobally(energy) exposeEnergyGlobally(energy)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
if (!isInput) { if (!isInput) {
for (side in RelativeSide.entries) { for (side in RelativeSide.entries) {

View File

@ -14,16 +14,19 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energyStoredMattery import ru.dbotthepony.mc.otm.capability.energyStoredMattery
import ru.dbotthepony.mc.otm.capability.extractEnergy import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery import ru.dbotthepony.mc.otm.capability.maxEnergyStoredMattery
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.container.slotted.and
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState) { class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState) {
val discharge = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val discharge = SlottedContainer.simple(1, AutomationFilters.DISCHARGABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val charge = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) val charge = SlottedContainer.simple(1, AutomationFilters.CHARGEABLE.filteredProvider, ::markDirtyFast).also(::addDroppableContainer)
val energy: ProfiledEnergyStorage<IMatteryEnergyStorage> = ProfiledEnergyStorage(object : IMatteryEnergyStorage { val energy: ProfiledEnergyStorage<IMatteryEnergyStorage> = ProfiledEnergyStorage(object : IMatteryEnergyStorage {
override val energyFlow: FlowDirection get() { override val energyFlow: FlowDirection get() {
@ -61,11 +64,7 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
}) })
val energyConfig = ConfigurableEnergy(energy, possibleModes = FlowDirection.BI_DIRECTIONAL) val energyConfig = ConfigurableEnergy(energy, possibleModes = FlowDirection.BI_DIRECTIONAL)
val itemConfig = ConfigurableItemHandler(input = charge, output = discharge)
val itemConfig = ConfigurableItemHandler(
input = charge.handler(HandlerFilter.OnlyIn.and(HandlerFilter.Chargeable)),
output = discharge.handler(HandlerFilter.OnlyOut.and(HandlerFilter.Dischargeable))
)
init { init {
savetables.stateful(::charge) savetables.stateful(::charge)
@ -89,19 +88,7 @@ class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
val chargeEnergy = charge.energy ?: return val chargeEnergy = charge.energy ?: return
val dischargeEnergy = discharge.energy ?: return val dischargeEnergy = discharge.energy ?: return
val extracted = dischargeEnergy.extractEnergy(Decimal.LONG_MAX_VALUE, true) moveEnergy(source = dischargeEnergy, destination = chargeEnergy, amount = Decimal.LONG_MAX_VALUE, simulate = false)
if (extracted.isPositive) {
val received = chargeEnergy.receiveEnergy(extracted, true)
if (received.isPositive) {
val extracted2 = dischargeEnergy.extractEnergy(received, false)
if (extracted2 == received) {
chargeEnergy.receiveEnergy(dischargeEnergy.extractEnergy(received, false), false)
}
}
}
} }
} }
} }

View File

@ -23,8 +23,10 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock import ru.dbotthepony.mc.otm.block.tech.EssenceStorageBlock
import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler import ru.dbotthepony.mc.otm.capability.item.CombinedItemHandler
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.ContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid import ru.dbotthepony.mc.otm.core.getEntitiesInEllipsoid
import ru.dbotthepony.mc.otm.core.lookupOrThrow import ru.dbotthepony.mc.otm.core.lookupOrThrow
import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.math.Vector
@ -36,6 +38,7 @@ import ru.dbotthepony.mc.otm.menu.tech.EssenceStorageMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes
import ru.dbotthepony.mc.otm.registry.game.MFluids import ru.dbotthepony.mc.otm.registry.game.MFluids
import kotlin.math.max
class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ESSENCE_STORAGE, blockPos, blockState), IFluidHandler { class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ESSENCE_STORAGE, blockPos, blockState), IFluidHandler {
var experienceStored = 0L var experienceStored = 0L
@ -45,9 +48,29 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
markDirtyFast() markDirtyFast()
} }
val capsuleContainer = MatteryContainer(::markDirtyFast, 1) private class CapsuleSlot(container: SlottedContainer, slot: Int) : ContainerSlot(container, slot) {
val servoContainer = MatteryContainer(::markDirtyFast, 1) override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
val mendingContainer = MatteryContainer(::markDirtyFast, 1).also(::addDroppableContainer) return super.canAutomationPlaceItem(itemStack) && itemStack.item is EssenceCapsuleItem
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
private inner class MendingSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && itemStack.isDamaged && itemStack.getEnchantmentLevel(mending!!) > 0
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && (!item.isDamaged || experienceStored <= 0)
}
}
val capsuleContainer = SlottedContainer.simple(1, ::CapsuleSlot, ::markDirtyFast)
val servoContainer = EnhancedContainer.WithListener(1, ::markDirtyFast)
val mendingContainer = SlottedContainer.simple(1, ::MendingSlot, ::markDirtyFast).also(::addDroppableContainer)
private var mending: Holder<Enchantment>? = null private var mending: Holder<Enchantment>? = null
@ -63,22 +86,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
} }
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
inputOutput = CombinedItemHandler( inputOutput = CombinedItemHandler(capsuleContainer, mendingContainer)
capsuleContainer.handler(HandlerFilter.OnlyIn.and(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.item is EssenceCapsuleItem
}
})),
mendingContainer.handler(object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.isDamaged && stack.getEnchantmentLevel(mending!!) > 0
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !stack.isDamaged || experienceStored <= 0
}
})
)
) )
override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) { override fun loadAdditional(nbt: CompoundTag, registry: HolderLookup.Provider) {
@ -160,7 +168,7 @@ class EssenceStorageBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
val capsule = capsuleContainer[0] val capsule = capsuleContainer[0]
if (!capsule.isEmpty && capsule.has(MDataComponentTypes.EXPERIENCE)) { if (!capsule.isEmpty && capsule.has(MDataComponentTypes.EXPERIENCE)) {
experienceStored += capsule.get(MDataComponentTypes.EXPERIENCE)!! * capsule.count experienceStored += max(capsule.get(MDataComponentTypes.EXPERIENCE)!! * capsule.count, 0L)
capsuleContainer.clearContent() capsuleContainer.clearContent()
} }

View File

@ -9,8 +9,8 @@ import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.ItemHatchMenu import ru.dbotthepony.mc.otm.menu.tech.ItemHatchMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
@ -21,12 +21,11 @@ class ItemHatchBlockEntity(
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState blockState: BlockState
) : MatteryDeviceBlockEntity(type, blockPos, blockState) { ) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val container = MatteryContainer(this::markDirtyFast, CAPACITY).also(::addDroppableContainer) val container = SlottedContainer.simple(CAPACITY, if (isInput) AutomationFilters.ONLY_IN.filteredProvider else AutomationFilters.ONLY_OUT.filteredProvider, this::markDirtyFast).also(::addDroppableContainer)
val itemHandler = container.handler(if (isInput) HandlerFilter.OnlyIn else HandlerFilter.OnlyOut)
init { init {
savetables.stateful(::container, INVENTORY_KEY) savetables.stateful(::container, INVENTORY_KEY)
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
} }
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu { override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {

View File

@ -15,8 +15,8 @@ import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.capability.moveMatter import ru.dbotthepony.mc.otm.capability.moveMatter
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
@ -28,26 +28,15 @@ class MatterHatchBlockEntity(
blockPos: BlockPos, blockPos: BlockPos,
blockState: BlockState blockState: BlockState
) : MatteryDeviceBlockEntity(type, blockPos, blockState) { ) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
val container = object : MatteryContainer(::markDirtyFast, CAPACITY) { val container = SlottedContainer.simple(CAPACITY, if (isInput) AutomationFilters.MATTER_PROVIDERS.limitedFilteredProvider else AutomationFilters.MATTER_CONSUMERS.limitedFilteredProvider, ::markDirtyFast).also(::addDroppableContainer)
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return 1
}
}.also(::addDroppableContainer)
val matter = ProfiledMatterStorage(MatterStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), MachinesConfig::MATTER_HATCH)) val matter = ProfiledMatterStorage(MatterStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), MachinesConfig::MATTER_HATCH))
val itemHandler = if (isInput) {
container.handler(HandlerFilter.MatterProviders)
} else {
container.handler(HandlerFilter.MatterConsumers)
}
init { init {
savetables.stateful(::container, INVENTORY_KEY) savetables.stateful(::container, INVENTORY_KEY)
savetables.stateful(::matter, MATTER_STORAGE_KEY) savetables.stateful(::matter, MATTER_STORAGE_KEY)
// it would cause a lot of frustration if hatches accept stuff only though one face // it would cause a lot of frustration if hatches accept stuff only though one face
exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler) exposeGlobally(Capabilities.ItemHandler.BLOCK, container)
exposeGlobally(MatteryCapability.MATTER_BLOCK, matter) exposeGlobally(MatteryCapability.MATTER_BLOCK, matter)
} }

View File

@ -5,8 +5,11 @@ import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.SingleRecipeInput
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.event.AddReloadListenerEvent
import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage import ru.dbotthepony.mc.otm.block.entity.ExperienceStorage
import ru.dbotthepony.mc.otm.block.entity.JobContainer import ru.dbotthepony.mc.otm.block.entity.JobContainer
import ru.dbotthepony.mc.otm.block.entity.JobStatus import ru.dbotthepony.mc.otm.block.entity.JobStatus
@ -16,15 +19,18 @@ import ru.dbotthepony.mc.otm.capability.UpgradeType
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.config.MachinesConfig import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.balance import ru.dbotthepony.mc.otm.container.balance
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.container.slotted.AutomationFilters
import ru.dbotthepony.mc.otm.core.collect.maybe import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.SimpleCache
import ru.dbotthepony.mc.otm.core.otmRandom import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.asKey
import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu import ru.dbotthepony.mc.otm.menu.tech.PlatePressMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MRecipes import ru.dbotthepony.mc.otm.registry.game.MRecipes
import java.time.Duration
class PlatePressBlockEntity( class PlatePressBlockEntity(
blockPos: BlockPos, blockPos: BlockPos,
@ -33,14 +39,36 @@ class PlatePressBlockEntity(
) : MatteryWorkerBlockEntity<ItemJob>(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, blockPos, blockState, ItemJob.CODEC, if (isTwin) 2 else 1) { ) : MatteryWorkerBlockEntity<ItemJob>(if (isTwin) MBlockEntities.TWIN_PLATE_PRESS else MBlockEntities.PLATE_PRESS, blockPos, blockState, ItemJob.CODEC, if (isTwin) 2 else 1) {
override val upgrades = makeUpgrades(if (isTwin) 4 else 3, UpgradeType.BASIC_PROCESSING) override val upgrades = makeUpgrades(if (isTwin) 4 else 3, UpgradeType.BASIC_PROCESSING)
override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(MachinesConfig.PLATE_PRESS))) override val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyLevelUpdated, upgrades.transform(MachinesConfig.PLATE_PRESS)))
val inputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer)
val outputContainer = MatteryContainer(this::itemContainerUpdated, if (isTwin) 2 else 1).also(::addDroppableContainer) private inner class InputSlot(container: SlottedContainer, slot: Int) : FilteredContainerSlot(container, slot) {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
if (!super.canAutomationPlaceItem(itemStack))
return false
val level = level ?: return true
val input = SingleRecipeInput(itemStack)
return cache.get(itemStack.asKey()) {
return@get level.recipeManager
.byType(MRecipes.PLATE_PRESS)
.any { it.value.matches(input, level) }
}
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return false
}
}
val inputContainer = SlottedContainer.simple(if (isTwin) 2 else 1, ::InputSlot, this::itemContainerUpdated).also(::addDroppableContainer)
val outputContainer = SlottedContainer.simple(if (isTwin) 2 else 1, AutomationFilters.ONLY_OUT.simpleProvider, this::itemContainerUpdated).also(::addDroppableContainer)
val experience = ExperienceStorage(MachinesConfig.PLATE_PRESS::maxExperienceStored).also(::addNeighbourListener) val experience = ExperienceStorage(MachinesConfig.PLATE_PRESS::maxExperienceStored).also(::addNeighbourListener)
val energyConfig = ConfigurableEnergy(energy) val energyConfig = ConfigurableEnergy(energy)
val itemConfig = ConfigurableItemHandler( val itemConfig = ConfigurableItemHandler(
input = inputContainer.handler(HandlerFilter.OnlyIn), input = inputContainer,
output = outputContainer.handler(HandlerFilter.OnlyOut), output = outputContainer,
) )
init { init {
@ -76,9 +104,7 @@ class PlatePressBlockEntity(
val recipe = level.recipeManager val recipe = level.recipeManager
.byType(MRecipes.PLATE_PRESS) .byType(MRecipes.PLATE_PRESS)
.iterator() .firstOrNull { it.value.matches(inputContainer, id) }?.value ?: return JobContainer.noItem()
.filter { it.value.matches(inputContainer, id) }
.maybe()?.value ?: return JobContainer.noItem()
val toProcess = inputContainer[id].count.coerceAtMost(1 + upgrades.processingItems) val toProcess = inputContainer[id].count.coerceAtMost(1 + upgrades.processingItems)
@ -100,4 +126,12 @@ class PlatePressBlockEntity(
super.tick() super.tick()
} }
companion object {
private val cache = SimpleCache<ItemStackKey, Boolean>(Duration.ofMinutes(1L))
internal fun onReload(event: AddReloadListenerEvent) {
cache.invalidateAll()
}
}
} }

View File

@ -146,13 +146,13 @@ val ItemStack.matteryEnergy: IMatteryEnergyStorage? get() {
} }
fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> { fun Player.items(includeCosmetics: Boolean = true): Iterator<ItemStack> {
val matteryPlayer = matteryPlayer ?: return emptyIterator() val matteryPlayer = matteryPlayer
val iterators = ArrayList<Iterator<ItemStack>>() val iterators = ArrayList<Iterator<ItemStack>>()
iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item }) iterators.add(matteryPlayer.wrappedInventory.slotIterator().filter { !it.filter.denyAll }.map { it.item })
if (matteryPlayer.hasExopack) { if (matteryPlayer.hasExopack) {
iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.isForbiddenForAutomation }.map { it.item }) iterators.add(matteryPlayer.exopackContainer.slotIterator().filter { !it.filter.denyAll }.map { it.item })
iterators.add(matteryPlayer.exopackEnergy.parent.iterator()) iterators.add(matteryPlayer.exopackEnergy.parent.iterator())
iterators.add(matteryPlayer.exopackChargeSlots.iterator()) iterators.add(matteryPlayer.exopackChargeSlots.iterator())
} }
@ -185,10 +185,8 @@ fun Player.awareItemsStream(includeCosmetics: Boolean = false): Stream<out Aware
val streams = ArrayList<Stream<out AwareItemStack>>() val streams = ArrayList<Stream<out AwareItemStack>>()
streams.add(inventory.awareStream()) streams.add(inventory.awareStream())
matteryPlayer?.let { if (matteryPlayer.hasExopack) {
if (it.hasExopack) { streams.add(matteryPlayer.exopackContainer.awareStream())
streams.add(it.exopackContainer.awareStream())
}
} }
if (isCuriosLoaded) { if (isCuriosLoaded) {

View File

@ -136,7 +136,7 @@ enum class FlowDirection(val input: Boolean, val output: Boolean, val translatio
*/ */
@JvmStatic @JvmStatic
fun input(flag: Boolean): FlowDirection { fun input(flag: Boolean): FlowDirection {
return of(flag, !flag) return if (flag) INPUT else OUTPUT
} }
/** /**

View File

@ -0,0 +1,14 @@
package ru.dbotthepony.mc.otm.capability
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.inventory.Slot
interface IQuickStackContainer {
fun getSlotsFor(player: ServerPlayer): Collection<Slot>
class Simple(private val slots: Collection<Slot>) : IQuickStackContainer {
override fun getSlotsFor(player: ServerPlayer): Collection<Slot> {
return slots
}
}
}

View File

@ -15,7 +15,7 @@ import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.extractEnergy import ru.dbotthepony.mc.otm.capability.extractEnergy
import ru.dbotthepony.mc.otm.capability.receiveEnergy import ru.dbotthepony.mc.otm.capability.receiveEnergy
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.registry.StatNames import ru.dbotthepony.mc.otm.registry.StatNames
import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger import ru.dbotthepony.mc.otm.triggers.AndroidBatteryTrigger

View File

@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.capability.item
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler import net.neoforged.neoforge.items.IItemHandler
import ru.dbotthepony.mc.otm.container.ContainerHandler import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import java.util.stream.Stream import java.util.stream.Stream
class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHandler { class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHandler {
@ -11,7 +11,7 @@ class CombinedItemHandler(val handlers: ImmutableList<IItemHandler>) : IItemHand
constructor(handlers: Collection<IItemHandler>) : this(ImmutableList.copyOf(handlers)) constructor(handlers: Collection<IItemHandler>) : this(ImmutableList.copyOf(handlers))
constructor(vararg handlers: IItemHandler) : this(ImmutableList.copyOf(handlers)) constructor(vararg handlers: IItemHandler) : this(ImmutableList.copyOf(handlers))
private val needsChecking = handlers.any { it !is ContainerHandler } private val needsChecking = handlers.any { it !is SlottedContainer }
private val lastSizes = IntArray(this.handlers.size) private val lastSizes = IntArray(this.handlers.size)
private var totalSize = 0 private var totalSize = 0
private val mappings = ArrayList<Mapping>() private val mappings = ArrayList<Mapping>()

View File

@ -25,8 +25,11 @@ import ru.dbotthepony.mc.otm.client.setMousePos
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.network.ExopackMenuOpen import ru.dbotthepony.mc.otm.network.ExopackMenuOpen
import ru.dbotthepony.mc.otm.network.QuickStackPacket
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
import java.util.function.IntConsumer
@MouseTweaksDisableWheelTweak @MouseTweaksDisableWheelTweak
class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<ExopackInventoryMenu>(menu, TranslatableComponent("otm.gui.exopack")) { class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<ExopackInventoryMenu>(menu, TranslatableComponent("otm.gui.exopack")) {
@ -281,7 +284,9 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
curios.x = x curios.x = x
} }
EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 6).tick() EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 4).tick()
QuickStackControlsPanel(this, frame, x = -19f, y = 96f + 18f)
return frame return frame
} }

View File

@ -10,7 +10,6 @@ import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.client.event.ContainerScreenEvent import net.neoforged.neoforge.client.event.ContainerScreenEvent
import net.neoforged.neoforge.common.NeoForge import net.neoforged.neoforge.common.NeoForge
@ -52,7 +51,7 @@ import ru.dbotthepony.mc.otm.core.math.component1
import ru.dbotthepony.mc.otm.core.math.component2 import ru.dbotthepony.mc.otm.core.math.component2
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.* import java.util.*
@ -364,8 +363,6 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean { override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return false return false
} }
override var slotFilter: Item? by slot.filter!!
} }
} }
@ -425,7 +422,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
matter: LevelGaugeWidget? = null, matter: LevelGaugeWidget? = null,
profiledMatter: ProfiledLevelGaugeWidget<*>? = null, profiledMatter: ProfiledLevelGaugeWidget<*>? = null,
patterns: LevelGaugeWidget? = null, patterns: LevelGaugeWidget? = null,
batterySlot: MatterySlot? = null, batterySlot: MatteryMenuSlot? = null,
) { ) {
var bars = 0 var bars = 0
if (energy != null) bars++ if (energy != null) bars++

View File

@ -2,11 +2,13 @@ package ru.dbotthepony.mc.otm.client.screen.decorative
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) { class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Component) : MatteryScreen<CargoCrateMenu>(menu, inventory, title) {
@ -20,12 +22,19 @@ class CargoCrateScreen(menu: CargoCrateMenu, inventory: Inventory, title: Compon
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6) val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots) for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot) UserFilteredSlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)
controls.sortingButtons(menu.sort) controls.sortingButtons(menu.sort)
val leftControls = DeviceControls(this, frame)
leftControls.dockOnLeft = true
leftControls.quickMoveButtons(menu.quickMoveFromStorage)
val leftInventoryControls = DeviceControls(this, inventoryFrame!!)
leftInventoryControls.dockOnLeft = true
leftInventoryControls.quickMoveButtons(menu.quickMoveToStorage, menu.quickMoveFromStorage)
return frame return frame
} }
} }

View File

@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.FluidGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.FluidGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu import ru.dbotthepony.mc.otm.menu.decorative.FluidTankMenu
@ -25,8 +26,8 @@ class FluidTankScreen(menu: FluidTankMenu, inventory: Inventory, title: Componen
val s = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 30f) val s = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 30f)
SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 55f, winding = UVWindingOrder.FLOP) SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND, x = 30f, y = 55f, winding = UVWindingOrder.FLOP)
SlotPanel(this, frame, menu.fillInput, x = 30f + s.width + 4f, y = 28f) UserFilteredSlotPanel(this, frame, menu.fillInput, x = 30f + s.width + 4f, y = 28f)
SlotPanel(this, frame, menu.drainInput, x = 30f + s.width + 4f, y = 53f) UserFilteredSlotPanel(this, frame, menu.drainInput, x = 30f + s.width + 4f, y = 53f)
SlotPanel(this, frame, menu.output, x = 30f + s.width + 4f + 20f, y = 53f) SlotPanel(this, frame, menu.output, x = 30f + s.width + 4f + 20f, y = 53f)

View File

@ -8,6 +8,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
@ -25,7 +26,7 @@ class MatterDecomposerScreen(p_97741_: MatterDecomposerMenu, p_97742_: Inventory
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE) BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
SlotPanel(this, frame, menu.input, 122f, PROGRESS_SLOT_TOP) UserFilteredSlotPanel(this, frame, menu.input, 122f, PROGRESS_SLOT_TOP)
ProgressGaugePanel(this, frame, menu.progressWidget, 96f, PROGRESS_ARROW_TOP).also { it.flop = true } ProgressGaugePanel(this, frame, menu.progressWidget, 96f, PROGRESS_ARROW_TOP).also { it.flop = true }
SlotPanel(this, frame, menu.outputMain, 74f, PROGRESS_SLOT_TOP) SlotPanel(this, frame, menu.outputMain, 74f, PROGRESS_SLOT_TOP)
SlotPanel(this, frame, menu.outputStacking, 56f, PROGRESS_SLOT_TOP) SlotPanel(this, frame, menu.outputStacking, 56f, PROGRESS_SLOT_TOP)

View File

@ -11,6 +11,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.compat.jei.MatterEntanglerRecipeCategory import ru.dbotthepony.mc.otm.compat.jei.MatterEntanglerRecipeCategory
@ -30,7 +31,7 @@ class MatterEntanglerScreen(menu: MatterEntanglerMenu, inventory: Inventory, tit
it.dockResize = DockResizeMode.NONE it.dockResize = DockResizeMode.NONE
for (slot in menu.inputs) for (slot in menu.inputs)
SlotPanel(this, it, slot) UserFilteredSlotPanel(this, it, slot)
} }
ProgressGaugePanel(this, frame, menu.progress).also { ProgressGaugePanel(this, frame, menu.progress).also {

View File

@ -10,9 +10,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.PlayerEquipmentPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel import ru.dbotthepony.mc.otm.client.screen.panels.makeCuriosPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.MatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
@ -27,7 +25,7 @@ class MatterReconstructorScreen(menu: MatterReconstructorMenu, inventory: Invent
BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE) BatterySlotPanel(this, frame, menu.batterySlot, LEFT_MARGIN, SLOT_TOP_UNDER_GAUGE)
SlotPanel(this, frame, menu.slot, 66f, PROGRESS_SLOT_TOP) UserFilteredSlotPanel(this, frame, menu.slot, 66f, PROGRESS_SLOT_TOP)
ProgressGaugePanel(this, frame, menu.progress, 37f, PROGRESS_ARROW_TOP) ProgressGaugePanel(this, frame, menu.progress, 37f, PROGRESS_ARROW_TOP)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig, upgrades = menu.upgrades) makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig, upgrades = menu.upgrades)

View File

@ -62,7 +62,7 @@ open class EffectListPanel<out S : Screen> @JvmOverloads constructor(
init { init {
scroll.visible = false scroll.visible = false
//scissor = true scissor = true
} }
open inner class EffectSquare( open inner class EffectSquare(

View File

@ -60,7 +60,7 @@ open class PlayerEquipmentPanel<S : MatteryScreen<*>>(
parent: EditablePanel<*>?, parent: EditablePanel<*>?,
x: Float = 0f, x: Float = 0f,
y: Float = 0f, y: Float = 0f,
val armorSlots: List<PlayerSlot<MatteryMenu.EquipmentSlot, Slot>> val armorSlots: List<PlayerSlot<MatteryMenu.EquipmentMenuSlot, Slot>>
) : EditablePanel<S>(screen, parent, x, y, height = HEIGHT, width = WIDTH) { ) : EditablePanel<S>(screen, parent, x, y, height = HEIGHT, width = WIDTH) {
val armorSlotsStrip = EditablePanel(screen, this, width = AbstractSlotPanel.SIZE) val armorSlotsStrip = EditablePanel(screen, this, width = AbstractSlotPanel.SIZE)
val entityPanel = EntityRendererPanel(screen, this, minecraft.player!!) val entityPanel = EntityRendererPanel(screen, this, minecraft.player!!)

View File

@ -0,0 +1,120 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.neoforged.neoforge.network.PacketDistributor
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.network.QuickStackPacket
import java.util.function.IntConsumer
class QuickStackControlsPanel<out S : Screen>(
screen: S,
parent: EditablePanel<*>? = null,
x: Float = 0f,
y: Float = 0f
) : EditablePanel<S>(screen, parent, x, y, 18f, 18f) {
private val grid = GridPanel(screen, this, columns = 1, rows = 1)
private val buttons = ArrayList<EditablePanel<S>>()
init {
grid.dock = Dock.FILL
grid.layout = GridPanel.Layout.TOP_RIGHT
grid.columnMajorOrder = true
// so row with main button contains only that button (visibly)
val fill = EditablePanel(screen, grid, width = 18f, height = 18f)
fill.childrenOrder = 999
fill.visible = false
fill.dockMargin = DockProperty.bottomRight(1f)
buttons.add(fill)
}
private inner class MainButton : ButtonPanel<S>(screen, grid, width = 18f, height = 18f) {
init {
dockMargin = DockProperty.bottom(1f)
childrenOrder = -100000
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange"))
tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange.desc").withStyle(ChatFormatting.GRAY))
tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.quickmove_hint").withStyle(ChatFormatting.GRAY))
}
override var isDisabled: Boolean
get() = minecraft.player!!.isSpectator
set(value) {}
override val icon: IGUIRenderable
get() = Widgets18.SMART_STORAGE_EXCHANGE
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || value == InputConstants.MOUSE_BUTTON_RIGHT
}
override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
PacketDistributor.sendToServer(QuickStackPacket(QuickMoveInput.Mode.RESTOCK_WITH_MOVE, true))
PacketDistributor.sendToServer(QuickStackPacket(QuickMoveInput.Mode.RESTOCK, false))
} else {
buttons.forEach { it.visible = !it.visible }
if (buttons[0].visible) {
grid.columns = 2
grid.rows = 4
} else {
grid.columns = 1
grid.rows = 1
}
this@QuickStackControlsPanel.sizeToContents()
}
}
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
return false
}
override fun sizeToContents() {
val oldWidth = width
super.sizeToContents()
x -= width - oldWidth
}
init {
for ((i, mode) in QuickMoveInput.Mode.entries.withIndex()) {
val button = ButtonPanel.square18(
screen, grid,
mode.iconFromStorage,
onPress = IntConsumer { PacketDistributor.sendToServer(QuickStackPacket(mode, false)) })
button.childrenOrder = 1000 + i
button.dockMargin = DockProperty.bottomRight(1f)
button.visible = false
button.tooltips.add(mode.textFromStorage)
buttons.add(button)
}
for ((i, mode) in QuickMoveInput.Mode.entries.withIndex()) {
val button = ButtonPanel.square18(
screen, grid,
mode.iconToStorage,
onPress = IntConsumer { PacketDistributor.sendToServer(QuickStackPacket(mode, true)) })
button.childrenOrder = i
button.dockMargin = DockProperty.bottom(1f)
button.visible = false
button.tooltips.add(mode.textToStorage)
buttons.add(button)
}
MainButton()
}
}

View File

@ -2,12 +2,11 @@ package ru.dbotthepony.mc.otm.client.screen.panels.button
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.util.Delegate import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.util.value import ru.dbotthepony.kommons.util.value
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
@ -21,7 +20,6 @@ import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.sprites.AbstractMatterySprite import ru.dbotthepony.mc.otm.client.render.sprites.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.ItemStackIcon import ru.dbotthepony.mc.otm.client.render.ItemStackIcon
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.Dock import ru.dbotthepony.mc.otm.client.screen.panels.Dock
@ -38,8 +36,9 @@ import ru.dbotthepony.mc.otm.core.immutableList
import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.util.ItemStackSorter import ru.dbotthepony.mc.otm.core.util.ItemStackSorter
import ru.dbotthepony.mc.otm.core.util.getLevelFromXp import ru.dbotthepony.mc.otm.core.util.getLevelFromXp
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.SortInput
import ru.dbotthepony.mc.otm.menu.UpgradeSlots import ru.dbotthepony.mc.otm.menu.UpgradeSlots
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
@ -388,13 +387,17 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P { fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
buttons.add(button) if (button !in buttons) {
button.parent = this buttons.add(button)
alignButtons() button.parent = this
alignButtons()
}
return button return button
} }
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P, after: EditablePanel<*>): P { fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P, after: EditablePanel<*>): P {
buttons.remove(button)
val index = buttons.indexOf(after) val index = buttons.indexOf(after)
if (index == -1) throw NoSuchElementException("Unknown panel to add button after: $after") if (index == -1) throw NoSuchElementException("Unknown panel to add button after: $after")
buttons.add(index + 1, button) buttons.add(index + 1, button)
@ -404,9 +407,12 @@ class DeviceControls<out S : MatteryScreen<*>>(
} }
fun <P : EditablePanel<@UnsafeVariance S>> prependButton(button: P): P { fun <P : EditablePanel<@UnsafeVariance S>> prependButton(button: P): P {
buttons.add(0, button) if (button !in buttons) {
button.parent = this buttons.add(0, button)
alignButtons() button.parent = this
alignButtons()
}
return button return button
} }
@ -452,9 +458,74 @@ class DeviceControls<out S : MatteryScreen<*>>(
return result return result
} }
fun sortingButtons(input: MatteryMenu.SortInput, unfoldableSettings: Boolean = true) { abstract inner class FoldableButtonPanel() : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) {
object : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) { init {
var buttons: List<EditablePanel<*>>? = null addButton(this)
}
private var buttons: List<EditablePanel<S>>? = null
fun makeButtons() {
if (buttons == null) {
buttons = doMakeButtons().also {
it.forEach { addButton(it, this) }
}
}
}
fun removeButtons() {
if (buttons != null) {
buttons!!.forEach { it.remove() }
buttons = null
}
}
override fun test(value: Int): Boolean {
return value == InputConstants.MOUSE_BUTTON_LEFT || hasExtraButtons && value == InputConstants.MOUSE_BUTTON_RIGHT
}
fun switchButtons() {
if (buttons == null)
makeButtons()
else
removeButtons()
}
protected abstract val hasExtraButtons: Boolean
protected abstract fun performAction()
protected abstract fun doMakeButtons(): List<EditablePanel<S>>
final override fun onClick(mouseButton: Int) {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
performAction()
} else {
switchButtons()
}
}
}
fun sortingButtons(input: SortInput, unfoldableSettings: Boolean = true) {
object : FoldableButtonPanel() {
override fun performAction() {
input.clientInput()
}
override var isDisabled: Boolean
get() { return !input.input.test(minecraft.player ?: return false) }
set(value) {}
override val hasExtraButtons: Boolean
get() = unfoldableSettings
override fun doMakeButtons(): List<EditablePanel<S>> {
return sortingButtons(Delegate.Of(input.settings::isAscending), Delegate.Of(input.settings::sorting), ItemStackSorter.DEFAULT) {
for (v in ItemStackSorter.entries) {
add(v, v.icon, v.title)
}
finish()
}
}
init { init {
tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now")) tooltips.add(TranslatableComponent("otm.gui.sorting.sort_now"))
@ -462,50 +533,75 @@ class DeviceControls<out S : MatteryScreen<*>>(
if (unfoldableSettings) { if (unfoldableSettings) {
tooltips.add(TextComponent("")) tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.sorting.sort_settings").withStyle(ChatFormatting.GRAY)) tooltips.add(TranslatableComponent("otm.gui.sorting.sort_settings").withStyle(ChatFormatting.GRAY))
} } else {
addButton(this)
if (!unfoldableSettings) {
makeButtons() makeButtons()
} }
} }
override val icon: IGUIRenderable override val icon: IGUIRenderable
get() = Widgets18.SORT_NOW get() = Widgets18.SORT_NOW
}
}
override fun test(value: Int): Boolean { fun quickMoveButtons(
return value == InputConstants.MOUSE_BUTTON_LEFT || unfoldableSettings && value == InputConstants.MOUSE_BUTTON_RIGHT buttons: Map<QuickMoveInput.Mode, QuickMoveInput>,
fromStorage: Map<QuickMoveInput.Mode, QuickMoveInput>? = null
) {
object : FoldableButtonPanel() {
override fun performAction() {
if (fromStorage == null) {
buttons[QuickMoveInput.Mode.MOVE]!!.clientInput()
} else {
buttons[QuickMoveInput.Mode.RESTOCK_WITH_MOVE]!!.clientInput()
fromStorage[QuickMoveInput.Mode.RESTOCK]!!.clientInput()
}
} }
override var isDisabled: Boolean override var isDisabled: Boolean
get() { return !input.input.test(minecraft.player ?: return false) } get() { return !buttons.values.first().input.test(minecraft.player ?: return false) }
set(value) {} set(value) {}
private fun makeButtons() { override val hasExtraButtons: Boolean
buttons = sortingButtons(Delegate.Of(input.settings::isAscending), Delegate.Of(input.settings::sorting), ItemStackSorter.DEFAULT) { get() = true
for (v in ItemStackSorter.entries) {
add(v, v.icon, v.title) override fun doMakeButtons(): List<EditablePanel<S>> {
var stream = buttons.entries
.stream()
if (fromStorage == null)
stream = stream.filter { (m) -> m != QuickMoveInput.Mode.MOVE }
return stream
.map { (m, b) ->
square18(
screen,
this,
icon = if (fromStorage == null) m.iconFromStorage else m.iconToStorage,
onPress = { b.clientInput() }
).also {
if (fromStorage == null)
it.tooltips.add(m.textFromStorage)
else
it.tooltips.add(m.textToStorage)
}
} }
.toList()
finish()
}
buttons!!.forEach { removeButton(it) }
buttons!!.forEach { addButton(it as EditablePanel<S>, this) }
} }
override fun onClick(mouseButton: Int) { init {
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) { if (fromStorage == null)
input.clientInput() tooltips.add(QuickMoveInput.Mode.MOVE.textFromStorage)
} else { else {
if (buttons == null) { tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange"))
makeButtons() tooltips.add(TranslatableComponent("otm.gui.quickmove.exchange.desc").withStyle(ChatFormatting.GRAY))
} else {
buttons!!.forEach { it.remove() }
buttons = null
}
} }
tooltips.add(TextComponent(""))
tooltips.add(TranslatableComponent("otm.gui.quickmove_hint").withStyle(ChatFormatting.GRAY))
} }
override val icon: IGUIRenderable
get() = if (fromStorage == null) QuickMoveInput.Mode.MOVE.iconFromStorage else Widgets18.SMART_STORAGE_EXCHANGE
} }
} }
@ -571,7 +667,7 @@ class DeviceControls<out S : MatteryScreen<*>>(
val grid = GridPanel(screen, frame, columns = columns, rows = rows) val grid = GridPanel(screen, frame, columns = columns, rows = rows)
for (slot in upgrades.slots) { for (slot in upgrades.slots) {
object : SlotPanel<S, MatterySlot>(screen, grid, slot) { object : SlotPanel<S, MatteryMenuSlot>(screen, grid, slot) {
override val cursorType: CursorType override val cursorType: CursorType
get() = if (upgrades.areLocked.get()) CursorType.NOT_ALLOWED else super.cursorType get() = if (upgrades.areLocked.get()) CursorType.NOT_ALLOWED else super.cursorType
} }

View File

@ -4,22 +4,36 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.util.Delegate import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.container.ItemFilter
open class FilterSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constructor( open class FilterSlotPanel<out S : MatteryScreen<*>>(
screen: S, screen: S,
parent: EditablePanel<*>?, parent: EditablePanel<*>?,
val slot: Delegate<ItemStack>, val slot: Delegate<ItemFilter>,
x: Float = 0f, x: Float = 0f,
y: Float = 0f, y: Float = 0f,
width: Float = SIZE, width: Float = SIZE,
height: Float = SIZE height: Float = SIZE
) : AbstractSlotPanel<S>(screen, parent, x, y, width, height) { ) : AbstractSlotPanel<S>(screen, parent, x, y, width, height) {
private var lastFilteredItemDisplayUpdate = System.nanoTime()
private var filteredItemDisplayIndex = 0
override val itemStack: ItemStack get() { override val itemStack: ItemStack get() {
return slot.get() val items = slot.get().displayItems
if (items.isEmpty())
return ItemStack.EMPTY
if (System.nanoTime() - lastFilteredItemDisplayUpdate >= 1_000_000_000L || filteredItemDisplayIndex !in items.indices) {
lastFilteredItemDisplayUpdate = System.nanoTime()
filteredItemDisplayIndex = random.nextInt(items.size)
}
return items.asList()[filteredItemDisplayIndex].asItemStack()
} }
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
slot.accept(screen.menu.carried) slot.accept(ItemFilter.item(screen.menu.carried))
return true return true
} }
} }

View File

@ -18,10 +18,6 @@ open class InventorySlotPanel<out S : MatteryScreen<*>, out T : MatteryMenu.Inve
x: Float = 0f, x: Float = 0f,
y: Float = 0f, y: Float = 0f,
) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE) { ) : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, SIZE, SIZE) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
override fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
if (slot.chargeFlag?.get() == true) { if (slot.chargeFlag?.get() == true) {
Widgets18.CHARGE_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height) Widgets18.CHARGE_SLOT_BACKGROUND.render(graphics, 0f, 0f, width, height)

View File

@ -5,9 +5,13 @@ package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.Slot import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.Widgets18 import ru.dbotthepony.mc.otm.client.render.Widgets18
@ -15,6 +19,14 @@ import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.compat.itemborders.isItemBordersLoaded import ru.dbotthepony.mc.otm.compat.itemborders.isItemBordersLoaded
import ru.dbotthepony.mc.otm.compat.itemborders.renderSlotBorder import ru.dbotthepony.mc.otm.compat.itemborders.renderSlotBorder
import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemStackKey
import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import javax.annotation.Nonnull import javax.annotation.Nonnull
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -52,6 +64,49 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
} }
} }
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
private var lastFilteredItemDisplayUpdate = System.nanoTime()
private var filteredItemDisplayIndex = 0
private fun selectRandomItemFromFilter(filter: ItemFilter): ItemStack {
val items = filter.displayItems
if (items.isEmpty()) {
return ItemStack.EMPTY
} else {
if (System.nanoTime() - lastFilteredItemDisplayUpdate >= 1_000_000_000L || filteredItemDisplayIndex !in items.indices) {
lastFilteredItemDisplayUpdate = System.nanoTime()
filteredItemDisplayIndex = random.nextInt(items.size)
}
return items.asList()[filteredItemDisplayIndex].asItemStack()
}
}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
val containerSlot = slot.container.containerSlotOrNull(slot.slotIndex)
if (containerSlot is IFilteredContainerSlot) {
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (containerSlot.filter.denyAll) {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
} else if (!containerSlot.filter.allowAll) {
val itemStack = selectRandomItemFromFilter(containerSlot.filter)
if (itemStack.isNotEmpty) {
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
}
}
}
}
override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
slot.x = absoluteX.roundToInt() - screen.guiLeft slot.x = absoluteX.roundToInt() - screen.guiLeft
slot.y = absoluteY.roundToInt() - screen.guiTop slot.y = absoluteY.roundToInt() - screen.guiTop
@ -120,10 +175,58 @@ open class SlotPanel<out S : MatteryScreen<*>, out T : Slot>(
} }
} }
override fun innerRenderTooltips(@Nonnull graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean { override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
val slot = slot.container.containerSlotOrNull(slot.containerSlot) as? IFilteredContainerSlot
if (isHovered && slot?.filter != null && slot.filter.hasRules && itemStack.isEmpty) {
val itemstack = selectRandomItemFromFilter(slot.filter)
val text: List<Component>
if (itemstack.isEmpty) {
text = listOf(
TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY),
TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY)
)
} else {
text = getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
}
}
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
text,
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slot?.filter?.denyAll == true && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
// no op, screen does it for us (completely) // no op, screen does it for us (completely)
return false return false
} }
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
}
} }
fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel( fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel(
@ -134,7 +237,7 @@ fun <S : MatteryScreen<*>, T : Slot> BatterySlotPanel(
y: Float = 0f, y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE, width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE, height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.BATTERY_SLOT_BACKGROUND } ) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.BATTERY_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> EquipmentBatterySlotPanel( fun <S : MatteryScreen<*>, T : Slot> EquipmentBatterySlotPanel(
screen: S, screen: S,
@ -144,7 +247,7 @@ fun <S : MatteryScreen<*>, T : Slot> EquipmentBatterySlotPanel(
y: Float = 0f, y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE, width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE, height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.EQUIPMENT_BATTERY_SLOT_BACKGROUND } ) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.EQUIPMENT_BATTERY_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> PatternSlotPanel( fun <S : MatteryScreen<*>, T : Slot> PatternSlotPanel(
screen: S, screen: S,
@ -154,7 +257,7 @@ fun <S : MatteryScreen<*>, T : Slot> PatternSlotPanel(
y: Float = 0f, y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE, width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE, height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.PATTERN_SLOT_BACKGROUND } ) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.PATTERN_SLOT_BACKGROUND }
fun <S : MatteryScreen<*>, T : Slot> MatterCapacitorSlotPanel( fun <S : MatteryScreen<*>, T : Slot> MatterCapacitorSlotPanel(
screen: S, screen: S,
@ -164,4 +267,4 @@ fun <S : MatteryScreen<*>, T : Slot> MatterCapacitorSlotPanel(
y: Float = 0f, y: Float = 0f,
width: Float = AbstractSlotPanel.SIZE, width: Float = AbstractSlotPanel.SIZE,
height: Float = AbstractSlotPanel.SIZE, height: Float = AbstractSlotPanel.SIZE,
) = SlotPanel(screen, parent, slot, x, y, width, height).also { it.slotBackgroundEmpty = Widgets18.MATTER_CAPACITOR_SLOT_BACKGROUND } ) = (if (slot is UserFilteredMenuSlot) UserFilteredSlotPanel(screen, parent, slot, x, y, width, height) else SlotPanel(screen, parent, slot, x, y, width, height)).also { it.slotBackgroundEmpty = Widgets18.MATTER_CAPACITOR_SLOT_BACKGROUND }

View File

@ -1,28 +1,17 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.platform.InputConstants import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.isCtrlDown import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.container.IFilteredContainerSlot
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.kommons.util.Delegate import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.menu.UserFilteredSlot
abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>( open class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : UserFilteredMenuSlot>(
screen: S, screen: S,
parent: EditablePanel<*>?, parent: EditablePanel<*>?,
slot: T, slot: T,
@ -31,77 +20,22 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
width: Float = SIZE, width: Float = SIZE,
height: Float = SIZE, height: Float = SIZE,
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) { ) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
abstract var slotFilter: Item?
protected open fun renderBackgroundBeforeFilter(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {}
override fun renderSlotBackground(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(graphics, mouseX, mouseY, partialTick)
renderBackgroundBeforeFilter(graphics, mouseX, mouseY, partialTick)
if (slotFilter != null) {
if (slotFilter !== Items.AIR) {
val itemStack = ItemStack(slotFilter!!, 1)
screen.renderItemStack(graphics, itemStack, null)
clearDepth(graphics)
graphics.renderRect(0f, 0f, width, height, color = SLOT_FILTER_COLOR)
} else {
graphics.renderRect(0f, 0f, width, height, color = SLOT_BLOCK_COLOR)
}
}
}
override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) {
val itemstack = ItemStack(slotFilter!!, 1)
graphics.renderComponentTooltip(
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: font,
getItemStackTooltip(itemstack).toMutableList().also {
it.add(0, TranslatableComponent("otm.gui.slot_filter.filtered").withStyle(ChatFormatting.GRAY))
it.add(1, TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
it.add(2, TextComponent(""))
},
mouseX.toInt(),
mouseY.toInt(),
itemstack
)
return true
} else if (isHovered && slotFilter === Items.AIR && itemStack.isEmpty) {
graphics.renderComponentTooltip(
font,
ArrayList<Component>().also {
it.add(TranslatableComponent("otm.gui.slot_filter.forbidden").withStyle(ChatFormatting.GRAY))
it.add(TranslatableComponent("otm.gui.slot_filter.hint").withStyle(ChatFormatting.GRAY))
},
mouseX.toInt(),
mouseY.toInt()
)
return true
}
return super.innerRenderTooltips(graphics, mouseX, mouseY, partialTick)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
val filterInput = slot.filterInput ?: return super.mouseClickedInner(x, y, button)
val containerSlot = slot.containerSlot() as IFilteredContainerSlot
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) { if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
if (slotFilter === null) { if (containerSlot.filter.allowAll) {
if (screen.menu.carried.isEmpty) { if (screen.menu.carried.isEmpty) {
slotFilter = slot.item.item filterInput.accept(ItemFilter.item(slot.item.item))
} else { } else {
slotFilter = screen.menu.carried.item filterInput.accept(ItemFilter.item(screen.menu.carried.item))
} }
} else { } else {
slotFilter = null filterInput.accept(ItemFilter.EMPTY)
} }
playGuiClickSound() playGuiClickSound()
return true return true
} else { } else {
return super.mouseClickedInner(x, y, button) return super.mouseClickedInner(x, y, button)
@ -115,40 +49,4 @@ abstract class UserFilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
return super.mouseReleasedInner(x, y, button) return super.mouseReleasedInner(x, y, button)
} }
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
val SLOT_BLOCK_COLOR = RGBAColor(219, 113, 113, 150)
fun <S : MatteryScreen<*>, T : Slot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
filter: Delegate<Item?>
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item? by filter
}
}
fun <S : MatteryScreen<*>, T : UserFilteredSlot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
): UserFilteredSlotPanel<S, T> {
return object : UserFilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height) {
override var slotFilter: Item?
get() = slot.filter?.get()
set(value) { slot.filter?.accept(value) }
}
}
}
} }

View File

@ -66,14 +66,12 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
settings.add(filterGrid) settings.add(filterGrid)
for (i in 0 until PortableCondensationDriveItem.MAX_FILTERS) { for (i in 0 until PortableCondensationDriveItem.MAX_FILTERS) {
FilterSlotPanel(this, filterGrid, menu.driveFilterSlots[i], 0f, 0f) FilterSlotPanel(this, filterGrid, menu.driveFilter.slots[i], 0f, 0f)
} }
settings.add(EditablePanel(this, frame, width = 90f).also { settings.add(EditablePanel(this, frame, width = 90f).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT
BooleanButtonPanel.Checkbox(this, it, menu.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist")).also { it.dockTop = 20f; it.dock = Dock.TOP } BooleanButtonPanel.Checkbox(this, it, menu.driveFilter.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist")).also { it.dockTop = 20f; it.dock = Dock.TOP }
BooleanButtonPanel.Checkbox(this, it, menu.matchTag, TranslatableComponent("otm.gui.filter.match_tag")).also { it.dockTop = 4f; it.dock = Dock.TOP }
BooleanButtonPanel.Checkbox(this, it, menu.matchComponents, TranslatableComponent("otm.gui.filter.match_nbt")).also { it.dockTop = 4f; it.dock = Dock.TOP }
}) })
frame.CustomTab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE))) frame.CustomTab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE)))

View File

@ -34,18 +34,6 @@ class StorageImporterExporterScreen(menu: StorageImporterExporterMenu, inventory
it.childrenOrder = -1 it.childrenOrder = -1
} }
BooleanButtonPanel.Checkbox(this, right, menu.filter.matchComponents, TranslatableComponent("otm.gui.filter.match_nbt")).also {
it.dock = Dock.BOTTOM
it.dockTop = 2f
it.childrenOrder = -2
}
BooleanButtonPanel.Checkbox(this, right, menu.filter.matchTag, TranslatableComponent("otm.gui.filter.match_tag")).also {
it.dock = Dock.BOTTOM
it.dockTop = 2f
it.childrenOrder = -3
}
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig) makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig)
return frame return frame

View File

@ -12,9 +12,11 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.compat.jei.isJeiLoaded import ru.dbotthepony.mc.otm.compat.jei.isJeiLoaded
import ru.dbotthepony.mc.otm.menu.UserFilteredMenuSlot
import ru.dbotthepony.mc.otm.menu.tech.AbstractProcessingMachineMenu import ru.dbotthepony.mc.otm.menu.tech.AbstractProcessingMachineMenu
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -50,9 +52,14 @@ open class AbstractProcessingMachineScreen<M : AbstractProcessingMachineMenu>(me
row.dock = Dock.TOP row.dock = Dock.TOP
row.dockBottom = 2f row.dockBottom = 2f
SlotPanel(this, row, input).also { if (input is UserFilteredMenuSlot)
it.dock = Dock.LEFT UserFilteredSlotPanel(this, row, input).also {
} it.dock = Dock.LEFT
}
else
SlotPanel(this, row, input).also {
it.dock = Dock.LEFT
}
progressPanels.add(ProgressGaugePanel(this, row, progress).also { progressPanels.add(ProgressGaugePanel(this, row, progress).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
@ -35,7 +36,7 @@ class ChemicalGeneratorScreen(menu: ChemicalGeneratorMenu, inventory: Inventory,
progress.setRecipeType { listOf(RecipeTypes.FUELING) } progress.setRecipeType { listOf(RecipeTypes.FUELING) }
SlotPanel(this, frame, menu.residueSlot, 56f, PROGRESS_SLOT_TOP) SlotPanel(this, frame, menu.residueSlot, 56f, PROGRESS_SLOT_TOP)
SlotPanel(this, frame, menu.fuelSlot, 104f, PROGRESS_SLOT_TOP) UserFilteredSlotPanel(this, frame, menu.fuelSlot, 104f, PROGRESS_SLOT_TOP)
makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig) makeDeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, itemConfig = menu.itemConfig, energyConfig = menu.energyConfig)

View File

@ -111,7 +111,19 @@ class EnergyCounterScreen(menu: EnergyCounterMenu, inventory: Inventory, title:
} }
} }
makeDeviceControls(this, frame, redstoneConfig = menu.redstone) val controls = makeDeviceControls(this, frame, redstoneConfig = menu.redstone)
controls.addButton(
BooleanButtonPanel.square18(
this,
controls,
menu.pullEnergyFromInput,
iconActive = Widgets18.LEFT_CONTROLS.push,
iconInactive = Widgets18.LEFT_CONTROLS.output,
tooltipActive = TranslatableComponent("block.overdrive_that_matters.energy_counter.do_pull"),
tooltipInactive = TranslatableComponent("block.overdrive_that_matters.energy_counter.dont_pull")
)
)
return frame return frame
} }

View File

@ -21,6 +21,7 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls import ru.dbotthepony.mc.otm.client.screen.panels.button.makeDeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.UserFilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HorizontalStripPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.HorizontalStripPanel
import ru.dbotthepony.mc.otm.core.ResourceLocation import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
@ -279,7 +280,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
get() = SET_EXACT get() = SET_EXACT
override fun onClick(mouseButton: Int) { override fun onClick(mouseButton: Int) {
val player = minecraft!!.player!! ?: return val player = minecraft!!.player!!
if (player.experienceLevel == customDispense) { if (player.experienceLevel == customDispense) {
if (player.experienceProgress > 0f) { if (player.experienceProgress > 0f) {
@ -297,7 +298,7 @@ class EssenceStorageScreen(menu: EssenceStorageMenu, inventory: Inventory, title
set(_) {} set(_) {}
} }
SlotPanel(this, inputs, menu.mendingSlot).also { UserFilteredSlotPanel(this, inputs, menu.mendingSlot).also {
it.dock = Dock.LEFT it.dock = Dock.LEFT
it.tooltips.add(TranslatableComponent("enchantment.minecraft.mending").withStyle(ChatFormatting.GRAY)) it.tooltips.add(TranslatableComponent("enchantment.minecraft.mending").withStyle(ChatFormatting.GRAY))
} }

View File

@ -20,7 +20,7 @@ class ItemHatchScreen(menu: ItemHatchMenu, inventory: Inventory, title: Componen
val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6) val grid = GridPanel(this, frame, 8f, 18f, 9f * 18f, 6f * 18f, 9, 6)
for (slot in menu.storageSlots) for (slot in menu.storageSlots)
UserFilteredSlotPanel.of(this, grid, slot) UserFilteredSlotPanel(this, grid, slot)
if (menu.isInput) { if (menu.isInput) {
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)

View File

@ -6,7 +6,6 @@ import lain.mods.cos.impl.client.PlayerRenderHandler
import lain.mods.cos.impl.client.gui.GuiCosArmorInventory import lain.mods.cos.impl.client.gui.GuiCosArmorInventory
import lain.mods.cos.impl.network.payload.PayloadSetSkinArmor import lain.mods.cos.impl.network.payload.PayloadSetSkinArmor
import net.minecraft.client.gui.screens.Screen import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container import net.minecraft.world.Container
@ -20,9 +19,6 @@ import net.neoforged.neoforge.network.PacketDistributor
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.minecraft import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.sprites.MatterySprite
import ru.dbotthepony.mc.otm.client.render.sprites.sprite import ru.dbotthepony.mc.otm.client.render.sprites.sprite
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
@ -32,7 +28,7 @@ import ru.dbotthepony.mc.otm.container.util.awareStream
import ru.dbotthepony.mc.otm.container.util.iterator import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.core.collect.AwareItemStack import ru.dbotthepony.mc.otm.core.collect.AwareItemStack
import ru.dbotthepony.mc.otm.core.collect.emptyIterator import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import java.util.stream.Stream import java.util.stream.Stream
val isCosmeticArmorLoaded by lazy { val isCosmeticArmorLoaded by lazy {
@ -47,7 +43,7 @@ val Player.cosmeticArmorSlots: Map<EquipmentSlot, Slot>? get() {
return cosmeticArmorSlotsImpl return cosmeticArmorSlotsImpl
} }
private class CosmeticSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatterySlot(container, when (slot) { private class CosmeticMenuSlot(container: Container, private val slot: EquipmentSlot, private val player: Player) : MatteryMenuSlot(container, when (slot) {
EquipmentSlot.FEET -> 0 EquipmentSlot.FEET -> 0
EquipmentSlot.LEGS -> 1 EquipmentSlot.LEGS -> 1
EquipmentSlot.CHEST -> 2 EquipmentSlot.CHEST -> 2
@ -127,10 +123,10 @@ private val Player.cosmeticArmorSlotsImpl: Map<EquipmentSlot, Slot>? get() {
} }
return mapOf( return mapOf(
EquipmentSlot.HEAD to CosmeticSlot(container, EquipmentSlot.HEAD, this), EquipmentSlot.HEAD to CosmeticMenuSlot(container, EquipmentSlot.HEAD, this),
EquipmentSlot.CHEST to CosmeticSlot(container, EquipmentSlot.CHEST, this), EquipmentSlot.CHEST to CosmeticMenuSlot(container, EquipmentSlot.CHEST, this),
EquipmentSlot.LEGS to CosmeticSlot(container, EquipmentSlot.LEGS, this), EquipmentSlot.LEGS to CosmeticMenuSlot(container, EquipmentSlot.LEGS, this),
EquipmentSlot.FEET to CosmeticSlot(container, EquipmentSlot.FEET, this), EquipmentSlot.FEET to CosmeticMenuSlot(container, EquipmentSlot.FEET, this),
) )
} }

View File

@ -13,8 +13,8 @@ import ru.dbotthepony.mc.otm.compat.jade.JadeUids
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.getCapability import ru.dbotthepony.mc.otm.core.getCapability
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.math.putDecimal import ru.dbotthepony.mc.otm.core.nbt.putDecimal
import ru.dbotthepony.mc.otm.core.util.formatMatter import ru.dbotthepony.mc.otm.core.util.formatMatter
import snownee.jade.api.BlockAccessor import snownee.jade.api.BlockAccessor
import snownee.jade.api.IBlockComponentProvider import snownee.jade.api.IBlockComponentProvider

View File

@ -13,8 +13,8 @@ import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.capability.IProfiledStorage import ru.dbotthepony.mc.otm.capability.IProfiledStorage
import ru.dbotthepony.mc.otm.core.getCapability import ru.dbotthepony.mc.otm.core.getCapability
import ru.dbotthepony.mc.otm.core.math.getDecimal import ru.dbotthepony.mc.otm.core.nbt.getDecimal
import ru.dbotthepony.mc.otm.core.math.putDecimal import ru.dbotthepony.mc.otm.core.nbt.putDecimal
import ru.dbotthepony.mc.otm.core.util.formatPower import ru.dbotthepony.mc.otm.core.util.formatPower
import snownee.jade.api.* import snownee.jade.api.*
import snownee.jade.api.config.IPluginConfig import snownee.jade.api.config.IPluginConfig

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.MenuType
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.SortInput
abstract class AbstractVanillaChestMenu(
type: MenuType<*>,
containerId: Int,
inventory: Inventory,
val container: Container
) : MatteryMenu(type, containerId, inventory) {
abstract val rows: Int
abstract val columns: Int
abstract val containerSlots: List<MatteryMenuSlot>
abstract val quickMoveToStorage: Map<QuickMoveInput.Mode, QuickMoveInput>
abstract val quickMoveFromStorage: Map<QuickMoveInput.Mode, QuickMoveInput>
val sort = SortInput(this, container, playerSortSettings)
override fun stillValid(player: Player): Boolean {
return container.stillValid(player)
}
override fun removed(player: Player) {
super.removed(player)
container.stopOpen(player)
}
}

View File

@ -22,7 +22,7 @@ import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.player.matteryPlayer import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.core.ResourceLocation import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.menu.MatteryMenu import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import java.util.* import java.util.*
private val menuConfigurations = WeakHashMap<AbstractContainerMenu, MenuConfiguration>() private val menuConfigurations = WeakHashMap<AbstractContainerMenu, MenuConfiguration>()
@ -47,9 +47,9 @@ private class MenuConfiguration(
for (i in 0 .. 8) { for (i in 0 .. 8) {
if (matteryPlayer.exopackContainer.containerSize > i + offset) { if (matteryPlayer.exopackContainer.containerSize > i + offset) {
row.add(MatterySlot(matteryPlayer.exopackContainer, i + offset)) row.add(MatteryMenuSlot(matteryPlayer.exopackContainer, i + offset))
} else { } else {
row.add(FakeSlot()) row.add(FakeMenuSlot())
} }
} }
@ -150,7 +150,7 @@ private class MenuConfiguration(
} }
} }
private class FakeSlot : MatterySlot(SimpleContainer(1), 0, 0, 0) { private class FakeMenuSlot : MatteryMenuSlot(SimpleContainer(1), 0, 0, 0) {
override fun mayPickup(player: Player): Boolean { override fun mayPickup(player: Player): Boolean {
return false return false
} }

View File

@ -1,118 +1,78 @@
package ru.dbotthepony.mc.otm.compat.vanilla package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType import net.minecraft.world.inventory.MenuType
import net.neoforged.bus.api.IEventBus import ru.dbotthepony.mc.otm.container.EnhancedContainer
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.MatterySlot
import ru.dbotthepony.mc.otm.menu.makeSlots import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
class MatteryChestMenu( class MatteryChestMenu(
type: MenuType<*>, containerId: Int, type: MenuType<*>, containerId: Int,
inventory: Inventory, val rows: Int, val columns: Int, inventory: Inventory, override val rows: Int, override val columns: Int,
val container: Container = SimpleContainer(rows * columns) container: Container = EnhancedContainer.Simple(rows * columns)
) : MatteryMenu(type, containerId, inventory) { ) : AbstractVanillaChestMenu(type, containerId, inventory, container) {
val chestSlots = makeSlots(container, ::MatterySlot) override val containerSlots = makeSlots(container, ::MatteryMenuSlot)
val sort = SortInput(container, playerSortSettings)
init { init {
require(rows * columns == container.containerSize) { "Provided container $container has different dimensions than specified rows x columns: ${container.containerSize} vs $rows x $columns (${rows * columns})" } require(rows * columns == container.containerSize) { "Provided container $container has different dimensions than specified rows x columns: ${container.containerSize} vs $rows x $columns (${rows * columns})" }
container.startOpen(player) container.startOpen(player)
addStorageSlot(chestSlots) addStorageSlot(containerSlots)
addInventorySlots() addInventorySlots()
} }
override fun stillValid(player: Player): Boolean { override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
return container.stillValid(player) override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
}
override fun removed(player: Player) {
super.removed(player)
container.stopOpen(player)
}
companion object { companion object {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
private val GENERIC_9x1 by registrar.register("generic_9x1") { MenuType(::c9x1, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x2 by registrar.register("generic_9x2") { MenuType(::c9x2, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x3 by registrar.register("generic_9x3") { MenuType(::c9x3, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x4 by registrar.register("generic_9x4") { MenuType(::c9x4, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x5 by registrar.register("generic_9x5") { MenuType(::c9x5, FeatureFlags.VANILLA_SET) }
private val GENERIC_9x6 by registrar.register("generic_9x6") { MenuType(::c9x6, FeatureFlags.VANILLA_SET) }
private val GENERIC_3x3 by registrar.register("generic_3x3") { MenuType(::c3x3, FeatureFlags.VANILLA_SET) }
private val HOPPER by registrar.register("hopper") { MenuType(::hopper, FeatureFlags.VANILLA_SET) }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x1(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9)): MatteryChestMenu { fun c9x1(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x1, containerId, inventory, 1, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x1, containerId, inventory, 1, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x2(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 2)): MatteryChestMenu { fun c9x2(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 2)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x2, containerId, inventory, 2, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x2, containerId, inventory, 2, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 3)): MatteryChestMenu { fun c9x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 3)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x3, containerId, inventory, 3, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x3, containerId, inventory, 3, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x4(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 4)): MatteryChestMenu { fun c9x4(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 4)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x4, containerId, inventory, 4, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x4, containerId, inventory, 4, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x5(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 5)): MatteryChestMenu { fun c9x5(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 5)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x5, containerId, inventory, 5, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x5, containerId, inventory, 5, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c9x6(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 6)): MatteryChestMenu { fun c9x6(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(9 * 6)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_9x6, containerId, inventory, 6, 9, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_9x6, containerId, inventory, 6, 9, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun c3x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(3 * 3)): MatteryChestMenu { fun c3x3(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(3 * 3)): MatteryChestMenu {
return MatteryChestMenu(GENERIC_3x3, containerId, inventory, 3, 3, container) return MatteryChestMenu(VanillaMenuTypes.GENERIC_3x3, containerId, inventory, 3, 3, container)
} }
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun hopper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(5)): MatteryChestMenu { fun hopper(containerId: Int, inventory: Inventory, container: Container = SimpleContainer(5)): MatteryChestMenu {
return MatteryChestMenu(HOPPER, containerId, inventory, 1, 5, container) return MatteryChestMenu(VanillaMenuTypes.HOPPER, containerId, inventory, 1, 5, container)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(GENERIC_9x1, ::MatteryChestScreen)
event.register(GENERIC_9x2, ::MatteryChestScreen)
event.register(GENERIC_9x3, ::MatteryChestScreen)
event.register(GENERIC_9x4, ::MatteryChestScreen)
event.register(GENERIC_9x5, ::MatteryChestScreen)
event.register(GENERIC_9x6, ::MatteryChestScreen)
event.register(GENERIC_3x3, ::MatteryChestScreen)
event.register(HOPPER, ::MatteryChestScreen)
} }
} }
} }

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.EnhancedContainer
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.menu.makeSlots
class MatteryShulkerBoxMenu(
containerId: Int,
inventory: Inventory,
container: Container = EnhancedContainer.Simple(27)
) : AbstractVanillaChestMenu(VanillaMenuTypes.SHULKER_BOX, containerId, inventory, container) {
override val containerSlots = makeSlots(container, ::Slot)
override val rows: Int
get() = 3
override val columns: Int
get() = 9
init {
container.startOpen(player)
addStorageSlot(containerSlots)
addInventorySlots()
}
override val quickMoveToStorage = QuickMoveInput.create(this, playerCombinedInventorySlots, containerSlots)
override val quickMoveFromStorage = QuickMoveInput.create(this, containerSlots, playerInventorySlots, false)
class Slot(container: Container, slot: Int) : MatteryMenuSlot(container, slot) {
override fun mayPlace(stack: ItemStack): Boolean {
return super.mayPlace(stack) && stack.item.canFitInsideContainerItems()
}
}
}

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.mc.otm.client.render.RenderGravity import ru.dbotthepony.mc.otm.client.render.RenderGravity
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.Dock import ru.dbotthepony.mc.otm.client.screen.panels.Dock
@ -10,8 +11,9 @@ import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
class MatteryChestScreen(menu: MatteryChestMenu, inventory: Inventory, title: Component) : MatteryScreen<MatteryChestMenu>(menu, inventory, title) { class VanillaChestScreen(menu: AbstractVanillaChestMenu, inventory: Inventory, title: Component) : MatteryScreen<AbstractVanillaChestMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> { override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel.padded(this, AbstractSlotPanel.SIZE * menu.columns.coerceAtLeast(9), AbstractSlotPanel.SIZE * menu.rows + 4f, title) val frame = FramePanel.padded(this, AbstractSlotPanel.SIZE * menu.columns.coerceAtLeast(9), AbstractSlotPanel.SIZE * menu.rows + 4f, title)
@ -22,12 +24,20 @@ class MatteryChestScreen(menu: MatteryChestMenu, inventory: Inventory, title: Co
grid.dock = Dock.FILL grid.dock = Dock.FILL
grid.gravity = RenderGravity.BOTTOM_CENTER grid.gravity = RenderGravity.BOTTOM_CENTER
for (slot in menu.chestSlots) for (slot in menu.containerSlots)
SlotPanel(this, grid, slot) SlotPanel(this, grid, slot)
val controls = DeviceControls(this, frame) val controls = DeviceControls(this, frame)
controls.sortingButtons(menu.sort) controls.sortingButtons(menu.sort)
val leftControls = DeviceControls(this, frame)
leftControls.dockOnLeft = true
leftControls.quickMoveButtons(menu.quickMoveFromStorage)
val leftInventoryControls = DeviceControls(this, inventoryFrame!!)
leftInventoryControls.dockOnLeft = true
leftInventoryControls.quickMoveButtons(menu.quickMoveToStorage, menu.quickMoveFromStorage)
return frame return frame
} }
} }

View File

@ -0,0 +1,71 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.capabilities.IBlockCapabilityProvider
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object VanillaMenuTypes {
private val registrar = MDeferredRegister(Registries.MENU, OverdriveThatMatters.MOD_ID)
val GENERIC_9x1 by registrar.register("generic_9x1") { MenuType(MatteryChestMenu::c9x1, FeatureFlags.VANILLA_SET) }
val GENERIC_9x2 by registrar.register("generic_9x2") { MenuType(MatteryChestMenu::c9x2, FeatureFlags.VANILLA_SET) }
val GENERIC_9x3 by registrar.register("generic_9x3") { MenuType(MatteryChestMenu::c9x3, FeatureFlags.VANILLA_SET) }
val GENERIC_9x4 by registrar.register("generic_9x4") { MenuType(MatteryChestMenu::c9x4, FeatureFlags.VANILLA_SET) }
val GENERIC_9x5 by registrar.register("generic_9x5") { MenuType(MatteryChestMenu::c9x5, FeatureFlags.VANILLA_SET) }
val GENERIC_9x6 by registrar.register("generic_9x6") { MenuType(MatteryChestMenu::c9x6, FeatureFlags.VANILLA_SET) }
val GENERIC_3x3 by registrar.register("generic_3x3") { MenuType(MatteryChestMenu::c3x3, FeatureFlags.VANILLA_SET) }
val HOPPER by registrar.register("hopper") { MenuType(MatteryChestMenu::hopper, FeatureFlags.VANILLA_SET) }
val SHULKER_BOX by registrar.register("shulker_box") { MenuType(::MatteryShulkerBoxMenu, FeatureFlags.VANILLA_SET) }
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
val container = blockEntity as? Container ?: return null
return IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot))
}
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
event.registerBlock(
MatteryCapability.QUICK_STACK_CONTAINER,
::provider,
Blocks.CHEST,
Blocks.TRAPPED_CHEST,
Blocks.SHULKER_BOX,
Blocks.BARREL,
)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
bus.addListener(this::registerCapabilities)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {
event.register(GENERIC_9x1, ::VanillaChestScreen)
event.register(GENERIC_9x2, ::VanillaChestScreen)
event.register(GENERIC_9x3, ::VanillaChestScreen)
event.register(GENERIC_9x4, ::VanillaChestScreen)
event.register(GENERIC_9x5, ::VanillaChestScreen)
event.register(GENERIC_9x6, ::VanillaChestScreen)
event.register(GENERIC_3x3, ::VanillaChestScreen)
event.register(HOPPER, ::VanillaChestScreen)
event.register(SHULKER_BOX, ::VanillaChestScreen)
}
}

View File

@ -7,7 +7,6 @@ import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.util.WriteOnce import ru.dbotthepony.mc.otm.core.util.WriteOnce
abstract class AbstractConfig(private val configName: String, private val type: ModConfig.Type = ModConfig.Type.SERVER) { abstract class AbstractConfig(private val configName: String, private val type: ModConfig.Type = ModConfig.Type.SERVER) {

View File

@ -3,7 +3,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object CablesConfig : AbstractConfig("cables") { object CablesConfig : AbstractConfig("cables") {
enum class E(throughput: Decimal) { enum class E(throughput: Decimal) {

View File

@ -0,0 +1,58 @@
package ru.dbotthepony.mc.otm.config
import net.neoforged.neoforge.common.ModConfigSpec
import ru.dbotthepony.mc.otm.core.math.Decimal
class DecimalConfigValue(
parent: ModConfigSpec.ConfigValue<String>,
val minimum: Decimal? = null,
val maximum: Decimal? = null,
) : ObservedConfigValue<Decimal>(parent) {
override fun fromString(value: String): Decimal? {
try {
val parsed = Decimal(value)
if (minimum != null && minimum > parsed) {
return minimum
} else if (maximum != null && maximum < parsed) {
return maximum
}
return parsed
} catch (err: java.lang.NumberFormatException) {
return null
}
}
override fun toString(value: Decimal): Pair<String, Decimal> {
if (minimum != null && minimum > value) {
return minimum.toString() to minimum
} else if (maximum != null && maximum < value) {
return maximum.toString() to maximum
}
return value.toString() to value
}
}
private fun ModConfigSpec.Builder.commentRange(minimum: Decimal?, maximum: Decimal?) {
if (minimum != null && maximum != null) {
comment("Range: $minimum ~ $maximum")
} else if (minimum != null) {
comment("Range: >= $minimum")
} else if (maximum != null) {
comment("Range: <= $maximum")
}
}
fun ModConfigSpec.Builder.defineDecimal(path: String, defaultValue: Decimal, minimum: Decimal? = null, maximum: Decimal? = null): DecimalConfigValue {
commentRange(minimum, maximum)
comment("Default: $defaultValue")
return DecimalConfigValue(define(path, defaultValue.toString()), minimum, maximum)
}
fun ModConfigSpec.Builder.defineDecimal(path: List<String>, defaultValue: Decimal, minimum: Decimal? = null, maximum: Decimal? = null): DecimalConfigValue {
commentRange(minimum, maximum)
comment("Default: $defaultValue")
return DecimalConfigValue(define(path, defaultValue.toString()), minimum, maximum)
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object ExopackConfig : AbstractConfig("exopack") { object ExopackConfig : AbstractConfig("exopack") {
val ENERGY_CAPACITY by builder val ENERGY_CAPACITY by builder

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
object ItemsConfig : AbstractConfig("items") { object ItemsConfig : AbstractConfig("items") {

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.registry.MNames import ru.dbotthepony.mc.otm.registry.MNames
object MachinesConfig : AbstractConfig("machines") { object MachinesConfig : AbstractConfig("machines") {
@ -60,7 +59,7 @@ object MachinesConfig : AbstractConfig("machines") {
private val MATTER_BOTTLER = workerValues( private val MATTER_BOTTLER = workerValues(
MNames.MATTER_BOTTLER, MNames.MATTER_BOTTLER,
energyStorage = Decimal(40_000), energyStorage = Decimal(40_000),
energyConsumption = Decimal(10), energyConsumption = Decimal(40),
energyThroughput = Decimal(200), energyThroughput = Decimal(200),
matterCapacity = Decimal(400), matterCapacity = Decimal(400),
workTimeMultiplier = null workTimeMultiplier = null
@ -85,7 +84,7 @@ object MachinesConfig : AbstractConfig("machines") {
object MatterBottler { object MatterBottler {
val VALUES by ::MATTER_BOTTLER val VALUES by ::MATTER_BOTTLER
val RATE by builder.comment("Matter transferred per tick").defineDecimal("RATE", Decimal("2.0"), Decimal.ONE_TENTH) val RATE by builder.comment("Matter transferred per tick").defineDecimal("RATE", Decimal("10.0"), Decimal.ONE_TENTH)
} }
object MatterRecycler { object MatterRecycler {
@ -111,8 +110,8 @@ object MachinesConfig : AbstractConfig("machines") {
private val MATTER_RECONSTRUCTOR = workerValues( private val MATTER_RECONSTRUCTOR = workerValues(
MNames.MATTER_RECONSTRUCTOR, MNames.MATTER_RECONSTRUCTOR,
energyStorage = Decimal(100_000), energyStorage = Decimal(200_000),
energyThroughput = Decimal(1000), energyThroughput = Decimal(4_000),
energyConsumption = Decimal(400), energyConsumption = Decimal(400),
matterCapacity = Decimal(200), matterCapacity = Decimal(200),
workTimeMultiplier = null, workTimeMultiplier = null,

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object PlayerConfig : AbstractConfig("player") { object PlayerConfig : AbstractConfig("player") {
init { init {

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.config
import ru.dbotthepony.kommons.util.getValue import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
object ServerConfig : AbstractConfig("misc") { object ServerConfig : AbstractConfig("misc") {
val LABORATORY_LAMP_LIGHT_LENGTH: Int by builder.comment("In blocks").defineInRange("LABORATORY_LAMP_LIGHT_LENGTH", 6, 1, 128) val LABORATORY_LAMP_LIGHT_LENGTH: Int by builder.comment("In blocks").defineInRange("LABORATORY_LAMP_LIGHT_LENGTH", 6, 1, 128)

View File

@ -0,0 +1,61 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.collect.iterateClearBits
import ru.dbotthepony.kommons.collect.iterateSetBits
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.map
import java.util.*
abstract class BitmapTrackingContainer<out S : IContainerSlot> : IEnhancedContainer<S> {
protected val bitmap = BitSet()
final override fun isEmpty(): Boolean {
return bitmap.isEmpty
}
final override fun nextEmptySlot(startIndex: Int): Int {
if (startIndex >= containerSize)
return -1
else if (startIndex < 0)
return bitmap.nextClearBit(0)
else
return bitmap.nextClearBit(startIndex)
}
final override fun nextNonEmptySlot(startIndex: Int): Int {
if (startIndex >= containerSize)
return -1
else if (startIndex < 0)
return bitmap.nextSetBit(0)
else
return bitmap.nextSetBit(startIndex)
}
final override fun iterator(): Iterator<ItemStack> {
return bitmap.iterateSetBits(containerSize).map { this[it] }
}
final override fun nonEmptySlotIndexIterator(): IntIterator {
return bitmap.iterateSetBits(containerSize)
}
final override fun emptySlotIndexIterator(): IntIterator {
return bitmap.iterateClearBits(containerSize)
}
final override fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty())
return bitmap.iterateClearBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1)
return super.emptySlotIndexIterator(allowedSlots)
}
final override fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
if (allowedSlots is IntRange2Set && allowedSlots.isNotEmpty())
return bitmap.iterateSetBits(allowedSlots.firstInt(), allowedSlots.lastInt() + 1)
return super.nonEmptySlotIndexIterator(allowedSlots)
}
}

View File

@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.ints.IntArrayList
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
@ -22,39 +23,36 @@ import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.stream import ru.dbotthepony.mc.otm.core.stream
import java.util.stream.Stream import java.util.stream.Stream
class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IMatteryContainer { class CombinedContainer<S : IContainerSlot>(containers: Stream<Pair<IEnhancedContainer<S>, Iterable<Int>>>) : ISlottedContainer<S> {
constructor(vararg containers: Container) : this(containers.stream().map { it to (0 until it.containerSize) }) constructor(vararg containers: IEnhancedContainer<S>) : this(containers.stream().map { it to (0 until it.containerSize) })
constructor(containers: Collection<Container>) : this(containers.stream().map { it to (0 until it.containerSize) }) constructor(containers: Collection<IEnhancedContainer<S>>) : this(containers.stream().map { it to (0 until it.containerSize) })
private inner class Slot(override val slot: Int, val outer: IContainerSlot) : IContainerSlot by outer { private val slots: ImmutableList<S>
override val container: Container private val slotsMap: ImmutableMap<Container, List<S>>
get() = this@CombinedContainer
}
private val slots: ImmutableList<Slot>
private val slotsMap: ImmutableMap<Container, List<IContainerSlot>>
private val containers: ImmutableSet<Container> private val containers: ImmutableSet<Container>
private val fullCoverage: ImmutableList<Container> private val fullCoverage: ImmutableList<Container>
private val notFullCoverage: ImmutableMap<Container, List<IContainerSlot>> private val notFullCoverage: ImmutableMap<Container, List<S>>
init { init {
val list = ImmutableList.Builder<Slot>() val list = ImmutableList.Builder<S>()
var i = 0
val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>() val validationMap = Reference2ObjectOpenHashMap<Container, IntSet>()
val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<IContainerSlot>>() val slotsMap = Reference2ObjectOpenHashMap<Container, ArrayList<S>>()
var i = 0
for ((container, slots) in containers) { for ((container, slots) in containers) {
val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntAVLTreeSet() }) val validator = validationMap.computeIfAbsent(container, Object2ObjectFunction { IntOpenHashSet() })
val slotList = slotsMap.computeIfAbsent(container, Object2ObjectFunction { ArrayList() }) val slotList = slotsMap.computeIfAbsent(container, Object2ObjectFunction { ArrayList() })
for (slot in slots) { for (slot in slots) {
if (validator.add(slot)) { if (validator.add(slot)) {
val slotObj = container.containerSlot(slot) val slotObj = container.containerSlot(slot)
list.add(Slot(i++, slotObj)) list.add(slotObj)
slotList.add(slotObj) slotList.add(slotObj)
} else { } else {
throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot") throw IllegalArgumentException("Duplicate mapping for $container at $i for slot $slot")
} }
i++
} }
} }
@ -78,6 +76,8 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
.collect(ImmutableMap.toImmutableMap({ it.key }, { it.value })) .collect(ImmutableMap.toImmutableMap({ it.key }, { it.value }))
} }
override val hasFilterableSlots: Boolean = super.hasFilterableSlots
override fun clearContent() { override fun clearContent() {
for (container in fullCoverage) { for (container in fullCoverage) {
container.clearContent() container.clearContent()
@ -85,20 +85,7 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
for (slots in notFullCoverage.values) { for (slots in notFullCoverage.values) {
for (slot in slots) { for (slot in slots) {
slot.item = ItemStack.EMPTY slot.remove()
}
}
}
override fun clearSlotFilters() {
for (container in fullCoverage) {
if (container is IMatteryContainer)
container.clearSlotFilters()
}
for (slots in notFullCoverage.values) {
for (slot in slots) {
slot.setFilter()
} }
} }
} }
@ -108,37 +95,7 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
} }
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean {
if (fullCoverage.isNotEmpty()) return fullCoverage.all { it.isEmpty } && notFullCoverage.values.all { it.all { it.isEmpty } }
for (container in fullCoverage)
if (!container.isEmpty)
return false
if (notFullCoverage.isNotEmpty())
for (slots in notFullCoverage.values)
for (slot in slots)
if (!slot.isEmpty)
return false
return true
}
override fun getItem(slot: Int): ItemStack {
// do not violate contract of getItem not throwing exceptions when index is invalid
return slots.getOrNull(slot)?.item ?: ItemStack.EMPTY
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.outer.container.removeItem(data.outer.slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
val data = slots.getOrNull(slot) ?: return ItemStack.EMPTY
return data.outer.container.removeItemNoUpdate(data.outer.slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
slots.getOrNull(slot)?.item = itemStack
} }
override fun setChanged() { override fun setChanged() {
@ -148,82 +105,87 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
} }
override fun stillValid(player: Player): Boolean { override fun stillValid(player: Player): Boolean {
for (container in containers) return containers.all { it.stillValid(player) }
if (!container.stillValid(player))
return false
return true
} }
override fun iterator(nonEmpty: Boolean): Iterator<ItemStack> { override fun iterator(): Iterator<ItemStack> {
if (notFullCoverage.isEmpty()) if (notFullCoverage.isEmpty())
return fullCoverage.iterator().flatMap { it.iterator(nonEmpty) } return fullCoverage.iterator().flatMap { it.iterator() }
return concatIterators( return concatIterators(
fullCoverage.iterator().flatMap { it.iterator(nonEmpty) }, fullCoverage.iterator().flatMap { it.iterator() },
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.let { if (nonEmpty) it.filter { it.isNotEmpty } else it } notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
) )
} }
override fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot> { override fun slotIterator(): Iterator<S> {
if (nonEmpty)
return slots.iterator().filter { it.isNotEmpty }
return slots.iterator() return slots.iterator()
} }
override fun containerSlot(slot: Int): IContainerSlot { override fun containerSlot(slot: Int): S {
return slots[slot] return slots[slot]
} }
override fun getSlotFilter(slot: Int): Item? {
return slots[slot].getFilter()
}
override fun setChanged(slot: Int) { override fun setChanged(slot: Int) {
slots[slot].setChanged() slots[slot].setChanged()
} }
override fun setSlotFilter(slot: Int, filter: Item?): Boolean { class Builder<S : IContainerSlot> {
return slots[slot].setFilter(filter) private val values = ArrayList<Pair<IEnhancedContainer<S>, Iterable<Int>>>()
}
class Builder { fun add(container: Container): Builder<S> {
private val values = ArrayList<Pair<Container, Iterable<Int>>>() return add(IEnhancedContainer.wrap(container))
}
fun add(container: Container): Builder { fun add(container: Container, slots: Iterator<Int>): Builder<S> {
return add(IEnhancedContainer.wrap(container), slots)
}
fun add(container: Container, slot: Int): Builder<S> {
return add(IEnhancedContainer.wrap(container), slot)
}
fun add(container: Container, from: Int, to: Int): Builder<S> {
return add(IEnhancedContainer.wrap(container), from, to)
}
fun add(container: Container, slots: Iterable<Int>): Builder<S> {
return add(IEnhancedContainer.wrap(container), slots)
}
fun add(container: IEnhancedContainer<S>): Builder<S> {
values.add(container to container.slotRange) values.add(container to container.slotRange)
return this return this
} }
fun add(container: Container, slots: Iterator<Int>): Builder { fun add(container: IEnhancedContainer<S>, slots: Iterator<Int>): Builder<S> {
values.add(container to IntArrayList(slots)) values.add(container to IntArrayList(slots))
return this return this
} }
fun add(container: Container, slot: Int): Builder { fun add(container: IEnhancedContainer<S>, slot: Int): Builder<S> {
values.add(container to intArrayOf(slot).asIterable()) values.add(container to intArrayOf(slot).asIterable())
return this return this
} }
fun add(container: Container, from: Int, to: Int): Builder { fun add(container: IEnhancedContainer<S>, from: Int, to: Int): Builder<S> {
values.add(container to (from .. to)) values.add(container to (from .. to))
return this return this
} }
fun add(container: Container, slots: Iterable<Int>): Builder { fun add(container: IEnhancedContainer<S>, slots: Iterable<Int>): Builder<S> {
values.add(container to slots) values.add(container to slots)
return this return this
} }
fun build(): CombinedContainer { fun build(): CombinedContainer<S> {
return CombinedContainer(values.stream()) return CombinedContainer(values.stream())
} }
} }
companion object { companion object {
fun fromMenuSlots(slots: Iterator<net.minecraft.world.inventory.Slot>): CombinedContainer { fun fromMenuSlots(slots: Iterator<net.minecraft.world.inventory.Slot>): CombinedContainer<IContainerSlot> {
val builder = Builder() val builder = Builder<IContainerSlot>()
for (slot in slots) { for (slot in slots) {
builder.add(slot.container, slot.slotIndex) builder.add(slot.container, slot.slotIndex)

View File

@ -1,89 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
class ContainerHandler(
private val container: IMatteryContainer,
private val filter: HandlerFilter = HandlerFilter.Both,
) : IItemHandler {
override fun getSlots() = container.containerSize
override fun getStackInSlot(slot: Int) = container[slot]
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
if (!container.testSlotFilter(slot, stack) || !filter.canInsert(slot, stack))
return stack
filter.preInsert(slot, stack, simulate)
val localStack = container[slot]
var amount = filter.modifyInsertCount(slot, stack, localStack, simulate)
if (amount <= 0)
return stack
if (localStack.isEmpty) {
amount = stack.count.coerceAtMost(container.getMaxStackSize(slot, stack)).coerceAtMost(amount)
if (!simulate) {
container.setItem(slot, stack.copyWithCount(amount))
}
if (stack.count <= amount) {
return ItemStack.EMPTY
} else {
return stack.copyWithCount(stack.count - amount)
}
} else if (localStack.isStackable && container.getMaxStackSize(slot, localStack) > localStack.count && ItemStack.isSameItemSameComponents(localStack, stack)) {
val newCount = container.getMaxStackSize(slot, localStack).coerceAtMost(localStack.count + stack.count.coerceAtMost(amount))
val diff = newCount - localStack.count
if (diff != 0) {
if (!simulate) {
localStack.grow(diff)
container.setChanged(slot)
}
val copy = stack.copy()
copy.shrink(diff)
return copy
}
}
return stack
}
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
if (amount <= 0 || container.isSlotForbiddenForAutomation(slot))
return ItemStack.EMPTY
val localStack = container.getItem(slot)
if (localStack.isEmpty) return ItemStack.EMPTY
@Suppress("name_shadowing")
val amount = filter.modifyExtractCount(slot, amount, simulate)
if (amount <= 0) return ItemStack.EMPTY
if (!filter.canExtract(slot, amount, localStack)) return ItemStack.EMPTY
filter.preExtract(slot, amount, simulate)
val minimal = amount.coerceAtMost(localStack.count)
val copy = localStack.copy()
copy.count = minimal
if (!simulate) {
localStack.shrink(minimal)
container.setChanged(slot)
}
return copy
}
override fun getSlotLimit(slot: Int): Int {
return container.maxStackSize
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return container.testSlotFilter(slot, stack) && filter.canInsert(slot, stack)
}
}

View File

@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.IntIterator
import it.unimi.dsi.fastutil.ints.IntList import it.unimi.dsi.fastutil.ints.IntList
import it.unimi.dsi.fastutil.ints.IntOpenHashSet import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.ints.IntSet
import it.unimi.dsi.fastutil.ints.IntSortedSet
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
@ -22,6 +23,7 @@ import ru.dbotthepony.mc.otm.container.util.ItemStackHashStrategy
import ru.dbotthepony.mc.otm.container.util.containerSlot import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.toList import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -38,89 +40,12 @@ inline operator fun Container.get(index: Int): ItemStack = getItem(index)
@Suppress("nothing_to_inline") @Suppress("nothing_to_inline")
inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index) inline operator fun IFluidHandler.get(index: Int) = getFluidInTank(index)
val Container.slotRange: IntIterable get() { val Container.slotRange: IntRange2Set get() {
return IntIterable { return IntRange2Set.openEnded(0, containerSize)
val i = (0 until containerSize).iterator()
object : IntIterator {
override fun hasNext(): Boolean {
return i.hasNext()
}
override fun remove() {
throw UnsupportedOperationException()
}
override fun nextInt(): Int {
return i.nextInt()
}
}
}
} }
fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange): ItemStack { fun Container.addItem(stack: ItemStack, simulate: Boolean, slots: IntSet = slotRange): ItemStack {
if (this is IMatteryContainer) { return IEnhancedContainer.wrap(this).addItem(stack, simulate, slots)
return this.addItem(stack, simulate, slots)
}
if (stack.isEmpty)
return stack
val copy = stack.copy()
// двигаем в одинаковые слоты
var i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (ItemStack.isSameItemSameComponents(this[slot], copy)) {
val slotStack = this[slot]
val slotLimit = maxStackSize.coerceAtMost(slotStack.maxStackSize)
if (slotStack.count < slotLimit) {
val newCount = (slotStack.count + copy.count).coerceAtMost(slotLimit)
val diff = newCount - slotStack.count
if (!simulate) {
slotStack.count = newCount
setChanged()
}
copy.shrink(diff)
if (copy.isEmpty) {
return copy
}
}
}
}
// двигаем в пустые слоты
i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (this[slot].isEmpty) {
val diff = copy.count.coerceAtMost(maxStackSize.coerceAtMost(copy.maxStackSize))
if (!simulate) {
val copyToPut = copy.copy()
copyToPut.count = diff
this[slot] = copyToPut
setChanged()
}
copy.shrink(diff)
if (copy.isEmpty) {
return copy
}
}
}
return copy
} }
fun Container.vanishCursedItems() { fun Container.vanishCursedItems() {
@ -131,7 +56,7 @@ fun Container.vanishCursedItems() {
} }
} }
fun Container.balance(slots: IntSet, checkForEmpty: Boolean = true) { fun Container.balance(slots: IntCollection, checkForEmpty: Boolean = true) {
if (slots.isEmpty() || checkForEmpty && !slots.any { getItem(it).isNotEmpty }) return if (slots.isEmpty() || checkForEmpty && !slots.any { getItem(it).isNotEmpty }) return
val empty = IntArrayList() val empty = IntArrayList()
@ -312,12 +237,18 @@ fun Container.sortWithIndices(sortedSlots: IntCollection) {
if (value in 0 until containerSize && seen.add(value)) { if (value in 0 until containerSize && seen.add(value)) {
val slot = containerSlot(value) val slot = containerSlot(value)
if ( val condition: Boolean
slot.isNotEmpty &&
!slot.isForbiddenForAutomation && if (slot is IFilteredContainerSlot) {
slot.item.count <= slot.getMaxStackSize() && condition = slot.isNotEmpty &&
(!slot.hasFilter || slot.getFilter() != slot.item.item || slot.getMaxStackSize() > 1) !slot.filter.denyAll &&
) { slot.item.count <= slot.maxStackSize(slot.item) &&
(slot.filter.allowAll || !slot.filter.test(slot.item) || slot.maxStackSize(slot.item) > 1)
} else {
condition = slot.isNotEmpty && slot.item.count <= slot.maxStackSize(slot.item)
}
if (condition) {
valid.add(slot) valid.add(slot)
} }
} }
@ -335,21 +266,24 @@ fun Container.computeSortedIndices(comparator: Comparator<ItemStack> = ItemStack
if (isEmpty) if (isEmpty)
return IntList.of() return IntList.of()
val slots = slotIterator().filter { !it.isForbiddenForAutomation && it.getMaxStackSize() >= it.item.count }.toList() val slots = slotIterator()
.withIndex()
.filter { (_, it) -> (it !is IFilteredContainerSlot || !it.filter.denyAll) && it.maxStackSize(it.item) >= it.item.count }
.toList()
if (slots.isEmpty()) if (slots.isEmpty())
return IntList.of() return IntList.of()
val items = Object2ObjectOpenCustomHashMap<ItemStack, Pair<ItemStack, IntList>>(ItemStackHashStrategy) val items = Object2ObjectOpenCustomHashMap<ItemStack, Pair<ItemStack, IntList>>(ItemStackHashStrategy)
slots.forEach { slots.forEach { (index, it) ->
val get = items[it.item] val get = items[it.item]
if (get == null) { if (get == null) {
items[it.item] = it.item.copy() to IntArrayList().also { s -> s.add(it.slot) } items[it.item] = it.item.copy() to IntArrayList().also { s -> s.add(index) }
} else { } else {
get.first.count += it.item.count get.first.count += it.item.count
get.second.add(it.slot) get.second.add(index)
} }
} }

View File

@ -1,81 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.function.Predicate
import java.util.function.Supplier
/**
* because mods tend to do crazy shit
*/
class DynamicallyProxiedContainer(private val toProxy: Supplier<Container>) : IContainer {
override fun clearContent() {
return toProxy.get().clearContent()
}
override fun getContainerSize(): Int {
return toProxy.get().containerSize
}
override fun isEmpty(): Boolean {
return toProxy.get().isEmpty
}
override fun getItem(slot: Int): ItemStack {
return toProxy.get().getItem(slot)
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return toProxy.get().removeItem(slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return toProxy.get().removeItemNoUpdate(slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
return toProxy.get().setItem(slot, itemStack)
}
override fun setChanged() {
return toProxy.get().setChanged()
}
override fun stillValid(player: Player): Boolean {
return toProxy.get().stillValid(player)
}
override fun getMaxStackSize(): Int {
return toProxy.get().getMaxStackSize()
}
override fun startOpen(player: Player) {
toProxy.get().startOpen(player)
}
override fun stopOpen(player: Player) {
toProxy.get().stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return toProxy.get().canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return toProxy.get().countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return toProxy.get().hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return toProxy.get().hasAnyMatching(predicate)
}
}

View File

@ -0,0 +1,212 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import net.minecraft.core.HolderLookup.Provider
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.resources.RegistryOps
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.common.util.INBTSerializable
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.collect.iterateClearBits
import ru.dbotthepony.kommons.collect.iterateSetBits
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.collect.IntRange2Set
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.BitSet
/**
* Flexible base implementation of [IEnhancedContainer], designed to be inherited, or used as-is
* if no specific logic is required (this implementation is more efficient than one provided by [SlottedContainer.simple]).
*
* This is supposed to be counterpart to [SimpleContainer] of Minecraft itself, with more features
* and improved performance (inside [IEnhancedContainer] defined methods).
*/
abstract class EnhancedContainer<out S : IContainerSlot>(private val size: Int) : BitmapTrackingContainer<S>(), INBTSerializable<CompoundTag> {
private val items = Array(size) { ItemStack.EMPTY }
private val observedItems = Array(size) { ItemStack.EMPTY }
protected open fun notifySlotChanged(slot: Int, old: ItemStack) {}
private fun observeSlotChanges(slot: Int) {
if (items[slot].count != observedItems[slot].count || !ItemStack.isSameItemSameComponents(items[slot], observedItems[slot])) {
notifySlotChanged(slot, observedItems[slot])
if (items[slot].isEmpty) {
items[slot] = ItemStack.EMPTY
observedItems[slot] = ItemStack.EMPTY
bitmap[slot] = false
} else {
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = items[slot].copy()
bitmap[slot] = true
}
}
}
override fun setChanged(slot: Int) {
observeSlotChanges(slot)
}
override fun clearContent() {
items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
}
override fun setChanged() {
for (slot in 0 until size)
observeSlotChanges(slot)
}
final override fun getContainerSize(): Int {
return size
}
final override fun getItem(slot: Int): ItemStack {
return items[slot]
}
final override fun removeItem(slot: Int, amount: Int): ItemStack {
val item = items[slot]
if (item.isEmpty)
return ItemStack.EMPTY
else if (item.count >= amount)
return removeItemNoUpdate(slot)
else {
val split = item.split(amount)
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = item.copy()
return split
}
}
final override fun removeItemNoUpdate(slot: Int): ItemStack {
val item = items[slot]
if (item.isEmpty)
return ItemStack.EMPTY
items[slot] = ItemStack.EMPTY
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = ItemStack.EMPTY
bitmap[slot] = false
return item
}
final override fun setItem(slot: Int, itemStack: ItemStack) {
if (itemStack.count != items[slot].count || !ItemStack.isSameItemSameComponents(itemStack, items[slot])) {
items[slot] = itemStack
notifySlotChanged(slot, observedItems[slot])
observedItems[slot] = itemStack.copy()
bitmap[slot] = itemStack.isNotEmpty
}
}
override fun stillValid(player: Player): Boolean {
return true
}
/**
* Called from inside [serializeNBT] per slot, return true if you have written something into [nbt].
* If returning false, and slot does not contain an item, tag entry is discarded
*/
protected open fun attachSlotData(provider: Provider, slot: Int, nbt: CompoundTag, ops: RegistryOps<Tag>): Boolean {
return false
}
protected open fun loadSlotData(provider: Provider, slot: Int, nbt: CompoundTag, ops: RegistryOps<Tag>) {
}
override fun serializeNBT(provider: Provider): CompoundTag {
val ops = provider.createSerializationContext(NbtOps.INSTANCE)
return CompoundTag().also {
it["items"] = ListTag().also {
for (i in 0 until size) {
val tag = CompoundTag()
var attached = attachSlotData(provider, i, tag, ops)
if (items[i].isNotEmpty) {
attached = true
tag["item"] = ItemStack.OPTIONAL_CODEC.encodeStart(ops, items[i])
.getOrThrow { RuntimeException("Unable to serialize item ${items[i]} at slot $i: $it") }
}
tag["slot"] = i
if (attached) {
it.add(tag)
}
}
}
}
}
override fun deserializeNBT(provider: Provider, nbt: CompoundTag) {
val copy = observedItems.copyOf()
items.fill(ItemStack.EMPTY)
observedItems.fill(ItemStack.EMPTY)
bitmap.clear()
val seenSlots = IntOpenHashSet()
val ops = provider.createSerializationContext(NbtOps.INSTANCE)
if ("items" in nbt) {
for (element in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) {
element as CompoundTag
if ("slot" !in element) continue
val slot = element.getInt("slot")
if (!seenSlots.add(slot)) continue
if ("item" in element) {
ItemStack.OPTIONAL_CODEC.decode(ops, element["item"])
.map { it.first }
.ifError { LOGGER.error("Failed to deserialize item stack in slot $slot: ${it.message()}") }
.ifSuccess {
if (it.isNotEmpty) {
items[slot] = it
observedItems[slot] = it.copy()
bitmap[slot] = true
if (it.count != copy[slot].count || !ItemStack.isSameItemSameComponents(it, copy[slot]))
notifySlotChanged(slot, copy[slot])
}
}
}
loadSlotData(provider, slot, element, ops)
}
}
}
open class Simple(size: Int) : EnhancedContainer<IContainerSlot>(size) {
private val slots by lazy(LazyThreadSafetyMode.PUBLICATION) { Array(size) { IContainerSlot.Simple(it, this) } }
override fun containerSlot(slot: Int): IContainerSlot {
return slots[slot]
}
}
open class WithListener(size: Int, private val listener: Runnable) : Simple(size) {
override fun notifySlotChanged(slot: Int, old: ItemStack) {
super.notifySlotChanged(slot, old)
listener.run()
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -1,153 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.fluid.stream
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
interface HandlerFilter {
fun canInsert(slot: Int, stack: ItemStack): Boolean {
return true
}
fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return true
}
fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {}
fun preExtract(slot: Int, amount: Int, simulate: Boolean) {}
fun modifyInsertCount(slot: Int, stack: ItemStack, existing: ItemStack, simulate: Boolean): Int {
return stack.count
}
fun modifyExtractCount(slot: Int, amount: Int, simulate: Boolean): Int {
return amount
}
fun and(other: HandlerFilter): HandlerFilter {
return object : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return this@HandlerFilter.canInsert(slot, stack) && other.canInsert(slot, stack)
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return this@HandlerFilter.canExtract(slot, amount, stack) && other.canExtract(slot, amount, stack)
}
override fun preInsert(slot: Int, stack: ItemStack, simulate: Boolean) {
this@HandlerFilter.preInsert(slot, stack, simulate)
other.preInsert(slot, stack, simulate)
}
override fun preExtract(slot: Int, amount: Int, simulate: Boolean) {
this@HandlerFilter.preExtract(slot, amount, simulate)
other.preExtract(slot, amount, simulate)
}
}
}
object FluidContainers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.tanks > 0 } ?: false
}
}
object DrainableFluidContainers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.FluidHandler.ITEM)?.let { it.stream().anyMatch { it.isNotEmpty } } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return !canInsert(slot, stack)
}
}
object OnlyIn : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return true
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return false
}
}
object OnlyOut : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return true
}
}
object Both : HandlerFilter
object Dischargeable : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canExtract() && it.extractEnergy(Int.MAX_VALUE, true) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canExtract() || it.extractEnergy(Int.MAX_VALUE, true) <= 0 } ?: false
}
}
object Chargeable : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { it.canReceive() && it.receiveEnergy(Int.MAX_VALUE, true) > 0 } ?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(Capabilities.EnergyStorage.ITEM)?.let { !it.canReceive() || it.receiveEnergy(Int.MAX_VALUE, true) <= 0 } ?: false
}
}
object ChemicalFuel : HandlerFilter {
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getBurnTime(null) <= 0
}
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getBurnTime(null) > 0
}
}
object IsPattern : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.PATTERN_ITEM) != null
}
}
object MatterProviders : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { it.matterFlow.output && it.extractMatterChecked(Decimal.POSITIVE_INFINITY, true) > Decimal.ZERO }
?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { !it.matterFlow.output || it.extractMatterChecked(Decimal.POSITIVE_INFINITY, true) <= Decimal.ZERO }
?: false
}
}
object MatterConsumers : HandlerFilter {
override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { it.matterFlow.input && it.receiveMatterChecked(Decimal.POSITIVE_INFINITY, true) > Decimal.ZERO }
?: false
}
override fun canExtract(slot: Int, amount: Int, stack: ItemStack): Boolean {
return stack.getCapability(MatteryCapability.MATTER_ITEM)
?.let { !it.matterFlow.input || it.receiveMatterChecked(Decimal.POSITIVE_INFINITY, true) <= Decimal.ZERO }
?: false
}
}
}

View File

@ -0,0 +1,44 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
import net.neoforged.neoforge.items.IItemHandlerModifiable
/**
* Reinforced [ISlottedContainer] which slots are [IAutomatedContainerSlot]s, which
* subsequently allow this container to implement [IItemHandler]
*/
interface IAutomatedContainer<out S : IAutomatedContainerSlot> : ISlottedContainer<S>, IItemHandlerModifiable {
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return containerSlot(slot).canAutomationPlaceItem(itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return containerSlot(slot).canAutomationTakeItem()
}
override fun getSlots() = containerSize
override fun getStackInSlot(slot: Int) = containerSlot(slot).item
override fun insertItem(slot: Int, stack: ItemStack, simulate: Boolean): ItemStack {
return containerSlot(slot).insertItem(stack, simulate)
}
override fun extractItem(slot: Int, amount: Int, simulate: Boolean): ItemStack {
return containerSlot(slot).extractItem(amount, simulate)
}
override fun getSlotLimit(slot: Int): Int {
return containerSlot(slot).maxStackSize
}
override fun setStackInSlot(slot: Int, stack: ItemStack) {
setItem(slot, stack)
}
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return canPlaceItem(slot, stack)
}
}

View File

@ -0,0 +1,93 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.neoforged.neoforge.items.IItemHandler
/**
* Slot of [IAutomatedContainer], with additional methods to implement interaction behavior for both for players and mechanisms
*/
interface IAutomatedContainerSlot : IContainerSlot {
fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return true
}
fun canAutomationTakeItem(desired: Int = item.count): Boolean {
return true
}
fun modifyAutomationPlaceCount(itemStack: ItemStack): Int {
return itemStack.count
}
fun modifyAutomationExtractionCount(desired: Int): Int {
return desired
}
/**
* Slot-specific implementation for [IItemHandler.insertItem]
*/
fun insertItem(stack: ItemStack, simulate: Boolean): ItemStack {
if (!canAutomationPlaceItem(stack))
return stack
var amount = modifyAutomationPlaceCount(stack)
if (amount <= 0)
return stack
if (item.isEmpty) {
amount = stack.count.coerceAtMost(maxStackSize(stack)).coerceAtMost(amount)
if (!simulate) {
item = stack.copyWithCount(amount)
}
if (stack.count <= amount) {
return ItemStack.EMPTY
} else {
return stack.copyWithCount(stack.count - amount)
}
} else if (item.isStackable && maxStackSize(item) > item.count && ItemStack.isSameItemSameComponents(item, stack)) {
val newCount = maxStackSize(item).coerceAtMost(item.count + stack.count.coerceAtMost(amount))
val diff = newCount - item.count
if (diff != 0) {
if (!simulate) {
item.grow(diff)
setChanged()
}
val copy = stack.copy()
copy.shrink(diff)
return copy
}
}
return stack
}
fun extractItem(amount: Int, simulate: Boolean): ItemStack {
if (amount <= 0 || !canAutomationTakeItem(amount) || item.isEmpty)
return ItemStack.EMPTY
@Suppress("name_shadowing")
val amount = modifyAutomationExtractionCount(amount)
if (amount <= 0 || !canAutomationTakeItem(amount)) return ItemStack.EMPTY
val minimal = amount.coerceAtMost(item.count)
val copy = item.copy()
copy.count = minimal
if (!simulate) {
if (item.count == minimal) {
item = ItemStack.EMPTY
} else {
item.shrink(minimal)
}
setChanged()
}
return copy
}
}

View File

@ -1,95 +0,0 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.function.Predicate
// passthrough all default methods to fix Kotlin bug related to implementation delegation not properly working on Java interfaces
// and also to give params proper names
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
interface IContainer : Container {
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return super.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return super.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return super.hasAnyMatching(predicate)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun isEmpty(): Boolean
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
override fun stillValid(player: Player): Boolean
companion object {
fun wrap(container: Container): IContainer {
if (container is IContainer)
return container
else
return object : IContainer, Container by container {
override fun getMaxStackSize(): Int {
return container.getMaxStackSize()
}
override fun startOpen(player: Player) {
container.startOpen(player)
}
override fun stopOpen(player: Player) {
container.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return container.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return container.canTakeItem(container, slot, itemStack)
}
override fun countItem(item: Item): Int {
return container.countItem(item)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return container.hasAnyOf(items)
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return container.hasAnyMatching(predicate)
}
}
}
}
}

View File

@ -1,9 +1,7 @@
package ru.dbotthepony.mc.otm.container package ru.dbotthepony.mc.otm.container
import net.minecraft.world.Container import net.minecraft.world.Container
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import ru.dbotthepony.kommons.util.Delegate import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.mc.otm.core.isNotEmpty import ru.dbotthepony.mc.otm.core.isNotEmpty
@ -12,57 +10,64 @@ import ru.dbotthepony.mc.otm.core.isNotEmpty
* for Player interaction. * for Player interaction.
*/ */
interface IContainerSlot : Delegate<ItemStack> { interface IContainerSlot : Delegate<ItemStack> {
val slot: Int fun setChanged()
val container: Container var item: ItemStack
operator fun component1() = slot
operator fun component2() = item
fun getMaxStackSize(item: ItemStack = this.item): Int {
return container.maxStackSize
}
val isForbiddenForAutomation: Boolean get() {
return getFilter() === Items.AIR
}
fun getFilter(): Item?
/** /**
* @return whenever the filter was set. Returns false only if container can't be filtered. * Max stack size regardless of item
*
* Prefer to use ItemStack version instead
*/ */
fun setFilter(filter: Item? = null): Boolean val maxStackSize: Int
val hasFilter: Boolean /**
get() = getFilter() != null * Max amount of [item] that can be stored in this slot.
*
fun remove() { * This may be larger or smaller than value returned [ItemStack.getMaxStackSize],
container[slot] = ItemStack.EMPTY * and as such returned value by this method should be ground truth
*/
fun maxStackSize(item: ItemStack): Int {
return maxStackSize.coerceAtMost(item.maxStackSize)
} }
fun remove(count: Int): ItemStack { fun remove(): ItemStack
return container.removeItem(slot, count) fun remove(count: Int): ItemStack
}
override fun get(): ItemStack { override fun get(): ItemStack {
return container[slot] return item
} }
override fun accept(t: ItemStack) { override fun accept(t: ItemStack) {
container[slot] = t item = t
} }
fun setChanged() {
container.setChanged()
}
var item: ItemStack
get() = container[slot]
set(value) { container[slot] = value }
val isEmpty: Boolean val isEmpty: Boolean
get() = container[slot].isEmpty get() = item.isEmpty
val isNotEmpty: Boolean val isNotEmpty: Boolean
get() = container[slot].isNotEmpty get() = item.isNotEmpty
open class Simple(protected val slot: Int, protected val container: Container) : IContainerSlot {
override fun setChanged() {
container.setChanged()
}
override var item: ItemStack
get() = container[slot]
set(value) { container[slot] = value }
override val maxStackSize: Int
get() = container.maxStackSize
override fun remove(count: Int): ItemStack {
return container.removeItem(slot, count)
}
override fun remove(): ItemStack {
return container.removeItemNoUpdate(slot)
}
override fun maxStackSize(item: ItemStack): Int {
return maxStackSize.coerceAtMost(item.maxStackSize)
}
}
} }

View File

@ -0,0 +1,477 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntCollection
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.StackedContentsCompatible
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.RecipeInput
import ru.dbotthepony.mc.otm.core.collect.any
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
import java.util.function.Predicate
import java.util.stream.Stream
import java.util.stream.StreamSupport
/**
* "Backward-compatible" enhanced container interface, where all methods can be derived/emulated from existing [Container] code
*
* This is useful because it allows to interact with actually enhanced and regular containers through unified interface,
* and actual implementations of this interface are likely to provide efficient method implementations in place of derived/emulated ones.
*/
interface IEnhancedContainer<out S : IContainerSlot> : Container, RecipeInput, Iterable<ItemStack>, StackedContentsCompatible {
// https://youtrack.jetbrains.com/issue/KT-55080/Change-the-behavior-of-inheritance-delegation-to-delegates-implementing-Java-interfaces-with-default-methods
override fun getMaxStackSize(): Int {
return super.getMaxStackSize()
}
override fun startOpen(player: Player) {
super.startOpen(player)
}
override fun stopOpen(player: Player) {
super.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return super.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return super.canTakeItem(container, slot, itemStack)
}
override fun clearContent()
override fun getContainerSize(): Int
override fun getItem(slot: Int): ItemStack
override fun removeItem(slot: Int, amount: Int): ItemStack
override fun removeItemNoUpdate(slot: Int): ItemStack
override fun setItem(slot: Int, itemStack: ItemStack)
override fun setChanged()
// provide non-ambiguous get and set operators
operator fun get(slot: Int): ItemStack {
return getItem(slot)
}
operator fun set(slot: Int, value: ItemStack) {
setItem(slot, value)
}
fun containerSlot(slot: Int): S
override fun fillStackedContents(contents: StackedContents) {
forEach { contents.accountStack(it) }
}
/**
* Returns iterator over **all** slots this container has
*/
fun slotIterator(): Iterator<S> {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(): Iterator<S> {
return nonEmptySlotIndexIterator().map { containerSlot(it) }
}
fun emptySlotIterator(): Iterator<S> {
return emptySlotIndexIterator().map { containerSlot(it) }
}
fun nonEmptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return nonEmptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
fun emptySlotIterator(allowedSlots: IntCollection): Iterator<S> {
return emptySlotIndexIterator(allowedSlots).map { containerSlot(it) }
}
private fun slotIndexWalker(allowedSlots: IntCollection, predicate: Predicate<ItemStack>): IntIterator {
return object : IntIterator() {
private val parent = allowedSlots.intIterator()
private var foundNext = false
private var next = -1
private fun findNext() {
if (!foundNext) {
foundNext = true
next = -1
while (parent.hasNext()) {
val i = parent.nextInt()
if (predicate.test(this@IEnhancedContainer[i])) {
next = i
break
}
}
}
}
override fun nextInt(): Int {
findNext()
if (next == -1)
throw NoSuchElementException()
foundNext = false
val next = next
this.next = -1
return next
}
override fun hasNext(): Boolean {
findNext()
return next != -1
}
}
}
fun emptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isEmpty }
}
fun nonEmptySlotIndexIterator(allowedSlots: IntCollection): IntIterator {
return slotIndexWalker(allowedSlots) { it.isNotEmpty }
}
fun emptySlotIndexIterator(): IntIterator {
return emptySlotIndexIterator(slotRange)
}
fun nonEmptySlotIndexIterator(): IntIterator {
return nonEmptySlotIndexIterator(slotRange)
}
fun slotIndexWithItemIterator(item: Item): IntIterator {
return slotIndexWithItemIterator(item, slotRange)
}
fun slotIndexWithItemIterator(item: Item, allowedSlots: IntCollection): IntIterator {
val parent = nonEmptySlotIndexIterator(allowedSlots).filter { this[it].isNotEmpty && this[it].item === item }
return object : IntIterator() {
override fun nextInt(): Int {
return parent.next()
}
override fun hasNext(): Boolean {
return parent.hasNext()
}
}
}
fun slotWithItemIterator(item: Item): Iterator<S> {
return slotWithItemIterator(item, slotRange)
}
fun slotWithItemIterator(item: Item, allowedSlots: IntCollection): Iterator<S> {
return slotIndexWithItemIterator(item, allowedSlots).map { containerSlot(it) }
}
fun nextEmptySlot(startIndex: Int): Int {
for (i in startIndex until containerSize) {
if (this[i].isEmpty) {
return i
}
}
return -1
}
fun nextNonEmptySlot(startIndex: Int): Int {
for (i in startIndex until containerSize) {
if (this[i].isNotEmpty) {
return i
}
}
return -1
}
fun nextSlotWithItem(startIndex: Int, item: Item): Int {
var find = nextNonEmptySlot(startIndex)
while (find != -1 && this[find].item !== item)
find = nextNonEmptySlot(find + 1)
return find
}
fun setChanged(slot: Int) {
return setChanged()
}
fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return maxStackSize
}
/**
* Returns iterator over **non-empty** [ItemStack]s inside this container
*/
override fun iterator(): Iterator<ItemStack> {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
}
override fun isEmpty(): Boolean {
return nextNonEmptySlot(0) != -1
}
val hasEmptySlots: Boolean get() {
return nextEmptySlot(0) != -1
}
override fun size(): Int {
return containerSize
}
override fun countItem(item: Item): Int {
var count = 0
for (slot in slotWithItemIterator(item))
count += slot.item.count
return count
}
override fun hasAnyOf(items: Set<Item>): Boolean {
if (Items.AIR in items && hasEmptySlots)
return true
return iterator().any { it.item in items }
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
if (predicate.test(ItemStack.EMPTY) && hasEmptySlots)
return true
return iterator().any(predicate)
}
fun toList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(containerSize)
for (i in 0 until containerSize) {
list.add(this[i])
}
return list
}
fun copyToList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(containerSize)
for (i in 0 until containerSize) {
list.add(this[i].copy())
}
return list
}
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntCollection, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
if (stack.isEmpty || slots.isEmpty())
return stack
// двигаем в одинаковые слоты
for (slot in slotWithItemIterator(stack.item, slots)) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = (ignoreFilters || !slot.filter.denyAll) &&
ItemStack.isSameItemSameComponents(slot.item, stack) &&
(ignoreFilters || !filterPass && slot.filter.allowAll || filterPass && !slot.filter.allowAll && slot.filter.test(stack))
} else {
condition = (ignoreFilters || !filterPass) && ItemStack.isSameItemSameComponents(slot.item, stack)
}
if (condition) {
val slotLimit = slot.maxStackSize(slot.item)
if (slot.item.count < slotLimit) {
val newCount = (slot.item.count + stack.count).coerceAtMost(slotLimit)
val diff = newCount - slot.item.count
if (!simulate) {
slot.item.count = newCount
slot.setChanged()
if (popTime != null) {
slot.item.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty)
return ItemStack.EMPTY
}
}
}
if (!onlyIntoExisting) {
for (slot in emptySlotIterator(slots)) {
val condition: Boolean
if (slot is IFilteredContainerSlot) {
condition = (ignoreFilters || !slot.filter.denyAll) &&
(ignoreFilters || !filterPass && slot.filter.allowAll || filterPass && !slot.filter.allowAll && slot.filter.test(stack))
} else {
condition = ignoreFilters || !filterPass
}
if (condition) {
val diff = stack.count.coerceAtMost(slot.maxStackSize(stack))
if (!simulate) {
val copyToPut = stack.copy()
copyToPut.count = diff
slot.item = copyToPut
if (popTime != null) {
copyToPut.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty)
return ItemStack.EMPTY
}
}
}
return stack
}
/**
* Hint used internally by [IEnhancedContainer] to potentially speed up default method implementations
*/
val hasFilterableSlots: Boolean
get() = false
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack {
if (stack.isEmpty || slots.isEmpty())
return stack
if (ignoreFilters || !hasFilterableSlots) {
return addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = true)
} else {
var copy = addItem(stack.copy(), simulate, filterPass = true, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
copy = addItem(copy, simulate, filterPass = false, slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = false)
return copy
}
}
/**
* Unlike [addItem], modifies original [stack]
*
* @return Whenever [stack] was modified
*/
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (stack.isEmpty)
return false
val result = addItem(stack, simulate = simulate, slots = slots, onlyIntoExisting = onlyIntoExisting, popTime = popTime, ignoreFilters = ignoreFilters)
if (!simulate) stack.count = result.count
return result.count != stack.count
}
fun fullyAddItem(stack: ItemStack, slots: IntCollection = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (!addItem(stack, simulate = true, slots = slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty)
return false
return addItem(stack, simulate = false, slots = slots, popTime = popTime, onlyIntoExisting = onlyIntoExisting, ignoreFilters = ignoreFilters).isEmpty
}
fun stream(): Stream<ItemStack> {
return StreamSupport.stream(spliterator(), false)
}
private class Wrapper(private val parent: Container) : IEnhancedContainer<IContainerSlot> {
override fun containerSlot(slot: Int): IContainerSlot {
return IContainerSlot.Simple(slot, parent)
}
override fun clearContent() {
return parent.clearContent()
}
override fun setChanged() {
return parent.setChanged()
}
override fun getContainerSize(): Int {
return parent.containerSize
}
override fun getItem(slot: Int): ItemStack {
return parent.getItem(slot)
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return parent.removeItem(slot, amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return parent.removeItemNoUpdate(slot)
}
override fun setItem(slot: Int, itemStack: ItemStack) {
return parent.setItem(slot, itemStack)
}
override fun stillValid(player: Player): Boolean {
return parent.stillValid(player)
}
override fun getMaxStackSize(): Int {
return parent.maxStackSize
}
override fun startOpen(player: Player) {
parent.startOpen(player)
}
override fun stopOpen(player: Player) {
parent.stopOpen(player)
}
override fun canPlaceItem(slot: Int, itemStack: ItemStack): Boolean {
return parent.canPlaceItem(slot, itemStack)
}
override fun canTakeItem(container: Container, slot: Int, itemStack: ItemStack): Boolean {
return parent.canTakeItem(container, slot, itemStack)
}
override fun isEmpty(): Boolean {
return parent.isEmpty
}
override fun hasAnyMatching(predicate: Predicate<ItemStack>): Boolean {
return parent.hasAnyMatching(predicate)
}
override fun hasAnyOf(items: Set<Item>): Boolean {
return parent.hasAnyOf(items)
}
override fun countItem(item: Item): Int {
return parent.countItem(item)
}
}
companion object {
fun wrap(other: Container): IEnhancedContainer<*> {
if (other is IEnhancedContainer<*>)
return other
return Wrapper(other)
}
}
}

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.entity.player.StackedContents
import net.minecraft.world.inventory.CraftingContainer
import net.minecraft.world.item.ItemStack
interface IEnhancedCraftingContainer<out S : IContainerSlot> : IEnhancedContainer<S>, CraftingContainer {
override fun getItems(): MutableList<ItemStack> {
return toList()
}
override fun fillStackedContents(contents: StackedContents) {
forEach { contents.accountSimpleStack(it) }
}
class Wrapper<out C : IEnhancedContainer<S>, out S : IContainerSlot>(val parent: C, private val width: Int, private val height: Int) : IEnhancedCraftingContainer<S>, IEnhancedContainer<S> by parent {
init {
require(width * height == parent.containerSize) { "Crafting container dimensions ($width x $height) do not match container size provided (${parent.containerSize})" }
}
override fun getWidth(): Int {
return width
}
override fun getHeight(): Int {
return height
}
override fun fillStackedContents(contents: StackedContents) {
super<IEnhancedCraftingContainer>.fillStackedContents(contents)
}
}
}

View File

@ -0,0 +1,14 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
interface IFilteredAutomatedContainerSlot : IFilteredContainerSlot, IAutomatedContainerSlot {
override fun canAutomationPlaceItem(itemStack: ItemStack): Boolean {
return super.canAutomationPlaceItem(itemStack) && filter.test(itemStack)
}
override fun canAutomationTakeItem(desired: Int): Boolean {
return super.canAutomationTakeItem(desired) && !filter.denyAll
}
}

View File

@ -0,0 +1,5 @@
package ru.dbotthepony.mc.otm.container
interface IFilteredContainerSlot : IContainerSlot {
var filter: ItemFilter
}

View File

@ -1,255 +0,0 @@
package ru.dbotthepony.mc.otm.container
import it.unimi.dsi.fastutil.ints.IntIterable
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.RecipeInput
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
interface IMatteryContainer : IContainer, RecipeInput, Iterable<ItemStack> {
fun getSlotFilter(slot: Int): Item?
/**
* @return whenever the filter was set. Returns false only if container can't be filtered.
*/
fun setSlotFilter(slot: Int, filter: Item? = null): Boolean {
return false
}
fun clearSlotFilters()
override fun isEmpty(): Boolean
fun setChanged(slot: Int)
override fun size(): Int {
return containerSize
}
/**
* Iterates over non-empty itemstacks of this container
*/
override fun iterator(): Iterator<ItemStack> {
return iterator(true)
}
/**
* Iterates non-empty slots of this container
*/
fun slotIterator(): Iterator<IContainerSlot> {
return slotIterator(true)
}
fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (nonEmpty) {
return (0 until containerSize).iterator().map { this[it] }.filter { it.isNotEmpty }
} else {
return (0 until containerSize).iterator().map { this[it] }
}
}
open class ContainerSlot(override val slot: Int, override val container: IMatteryContainer) : IContainerSlot {
override val isForbiddenForAutomation: Boolean
get() = container.isSlotForbiddenForAutomation(slot)
override fun getFilter(): Item? {
return container.getSlotFilter(slot)
}
override fun setFilter(filter: Item?): Boolean {
return container.setSlotFilter(slot, filter)
}
override fun getMaxStackSize(item: ItemStack): Int {
return container.getMaxStackSize(slot, item)
}
override fun setChanged() {
container.setChanged(slot)
}
}
/**
* Iterates either non-empty slots of container or all slots of container
*/
fun slotIterator(nonEmpty: Boolean): Iterator<IContainerSlot> {
if (nonEmpty) {
return (0 until containerSize).iterator().filter { this[it].isNotEmpty }.map { containerSlot(it) }
} else {
return (0 until containerSize).iterator().map { containerSlot(it) }
}
}
fun containerSlot(slot: Int): IContainerSlot {
return ContainerSlot(slot, this)
}
fun hasSlotFilter(slot: Int) = getSlotFilter(slot) !== null
fun isSlotForbiddenForAutomation(slot: Int) = getSlotFilter(slot) === Items.AIR
fun testSlotFilter(slot: Int, itemStack: ItemStack): Boolean {
return testSlotFilter(slot, itemStack.item)
}
fun testSlotFilter(slot: Int, item: Item): Boolean {
if (getSlotFilter(slot) == null) {
return true
} else {
return getSlotFilter(slot) === item
}
}
fun getMaxStackSize(slot: Int, itemStack: ItemStack) = maxStackSize.coerceAtMost(itemStack.maxStackSize)
private fun addItem(stack: ItemStack, simulate: Boolean, filterPass: Boolean, slots: IntIterable, onlyIntoExisting: Boolean, popTime: Int?, ignoreFilters: Boolean): ItemStack {
if (stack.isEmpty)
return stack
// двигаем в одинаковые слоты
var i = slots.intIterator()
while (i.hasNext()) {
val slot = i.nextInt()
if (
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
ItemStack.isSameItemSameComponents(getItem(slot), stack) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val slotStack = getItem(slot)
val slotLimit = getMaxStackSize(slot, slotStack)
if (slotStack.count < slotLimit) {
val newCount = (slotStack.count + stack.count).coerceAtMost(slotLimit)
val diff = newCount - slotStack.count
if (!simulate) {
slotStack.count = newCount
setChanged(slot)
if (popTime != null) {
slotStack.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
if (!onlyIntoExisting) {
i = slots.intIterator()
// двигаем в пустые слоты
while (i.hasNext()) {
val slot = i.nextInt()
if (
getItem(slot).isEmpty &&
(ignoreFilters || !isSlotForbiddenForAutomation(slot)) &&
(ignoreFilters || !filterPass && !hasSlotFilter(slot) || filterPass && hasSlotFilter(slot) && testSlotFilter(slot, stack))
) {
val diff = stack.count.coerceAtMost(getMaxStackSize(slot, stack))
if (!simulate) {
val copyToPut = stack.copy()
copyToPut.count = diff
setItem(slot, copyToPut)
if (popTime != null) {
copyToPut.popTime = popTime
}
}
stack.shrink(diff)
if (stack.isEmpty) {
return stack
}
}
}
}
return stack
}
fun addItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): ItemStack {
if (stack.isEmpty)
return stack
if (ignoreFilters) {
return addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, true)
} else {
var copy = addItem(stack.copy(), simulate, true, slots, onlyIntoExisting, popTime, false)
copy = addItem(copy, simulate, false, slots, onlyIntoExisting, popTime, false)
return copy
}
}
fun handler(filter: HandlerFilter = HandlerFilter.Both): ContainerHandler {
return ContainerHandler(this, filter)
}
/**
* Unlike [addItem], modifies original [stack]
*
* @return Whenever [stack] was modified
*/
fun consumeItem(stack: ItemStack, simulate: Boolean, slots: IntIterable = slotRange, onlyIntoExisting: Boolean = false, popTime: Int? = null, ignoreFilters: Boolean = false): Boolean {
if (stack.isEmpty)
return false
val result = addItem(stack, simulate, slots, onlyIntoExisting, popTime, ignoreFilters)
if (result.count != stack.count) {
if (!simulate) {
stack.count = result.count
}
return true
}
return false
}
fun fullyAddItem(stack: ItemStack, slots: IntIterable = slotRange, ignoreFilters: Boolean = false): Boolean {
if (!addItem(stack, true, slots, ignoreFilters).isEmpty)
return false
return addItem(stack, false, slots, ignoreFilters).isEmpty
}
fun toList(): MutableList<ItemStack> {
val list = ArrayList<ItemStack>(size())
for (i in 0 until size()) {
list.add(this[i])
}
return list
}
fun shrink(slot: Int, amount: Int): Boolean {
if (slot < 0 || slot > size())
return false
val item = this[slot]
if (item.isEmpty)
return false
if (item.count <= amount) {
this[slot] = ItemStack.EMPTY
} else {
item.shrink(amount)
setChanged(slot)
}
return true
}
}

View File

@ -0,0 +1,36 @@
package ru.dbotthepony.mc.otm.container
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.kommons.collect.any
/**
* Skeletal implementation for containers which revolve around [IContainerSlot]
*/
interface ISlottedContainer<out S : IContainerSlot> : IEnhancedContainer<S> {
override fun setChanged(slot: Int) {
containerSlot(slot).setChanged()
}
override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
return containerSlot(slot).maxStackSize(itemStack)
}
override fun getItem(slot: Int): ItemStack {
return containerSlot(slot).item
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
return containerSlot(slot).remove(amount)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
return containerSlot(slot).remove()
}
override fun setItem(slot: Int, itemStack: ItemStack) {
containerSlot(slot).item = itemStack
}
override val hasFilterableSlots: Boolean
get() = slotIterator().any { it is IFilteredContainerSlot }
}

Some files were not shown because too many files have changed in this diff Show More