- Added the Display Board
- Added the Data Gatherer
- Nixie Tubes no longer animate text from "dynamic" vanilla text components
- Trains now keep travel time statistics for prediction purposes
- Created "Data-target" behaviour for Signs, Display Boards, Lecterns and Nixie Tubes
- Created "Data-source" behaviour for Train Stations, Clocks, Nixie Tubes, Content Observers, Stockpile Switches, Respawn Anchors, Depots, Belts, Belt Tunnels and Command Blocks
This commit is contained in:
simibubi 2022-04-05 19:04:04 +02:00
parent 9d8803d280
commit 39b51821aa
114 changed files with 7231 additions and 743 deletions

View file

@ -188,11 +188,13 @@ b496452f2f7dbbba385e1fc10b560ec266e4b5e7 assets/create/blockstates/cyan_sail.jso
2c04d57e56849f243aec8a1e769574d24daac1e9 assets/create/blockstates/cyan_valve_handle.json
1726b1b9e04a0634e7e1fdcf1cf4cc898efc5c2f assets/create/blockstates/dark_oak_window.json
50d4627d8e8b5adade12de764ab528ddacfa9ea5 assets/create/blockstates/dark_oak_window_pane.json
990cdcce80e6e4ea3055c2572c5fafe3ee795b0f assets/create/blockstates/data_gatherer.json
de0116bf32a26d697a3b999044d6fb0b1b98320e assets/create/blockstates/deepslate_pillar.json
c3317a94c509d4bb2fe9c6f64a072334421998d3 assets/create/blockstates/deepslate_zinc_ore.json
ac85f55d82d96fc15750e6b954297cfd1e00d04d assets/create/blockstates/deployer.json
3660f44309279a0347347f23ce7444c6ed98cafd assets/create/blockstates/depot.json
0270b68550e19720d0cdc9e44f63618908628192 assets/create/blockstates/diorite_pillar.json
62cc543abb242836570d07d619fcdb4c79c75db4 assets/create/blockstates/display_board.json
30b3422bfee9878c92521429b2536d3e0313cedb assets/create/blockstates/dripstone_pillar.json
35fc68eb1d031d28ad09b7b603e64ae459634179 assets/create/blockstates/encased_chain_drive.json
7b2b836649e729feafa60972bf95e3afb2143131 assets/create/blockstates/encased_fan.json
@ -382,8 +384,8 @@ bc91f1cbdf0c2f9867edb36652bda43aa614e414 assets/create/blockstates/polished_cut_
c650bece9b333daba3174f944e6ebd41a010c850 assets/create/blockstates/powered_shaft.json
e8b0a401c10d1ba67ed71ba31bd5f9bc28571b65 assets/create/blockstates/powered_toggle_latch.json
3a739f9d4276828d83f2d2750bf3227c87bcd438 assets/create/blockstates/pulley_magnet.json
dee3cdef860bb92d439ecaaec4300b42208b025c assets/create/blockstates/pulse_extender.json
638eb675fe3c464a0ab265c37f7d37fdf6440323 assets/create/blockstates/pulse_repeater.json
2a825cb867b9738b34d80b4151c55f76eb95e2ef assets/create/blockstates/pulse_extender.json
9035c427fdb0035312f617196e65a1e3814bd0fa assets/create/blockstates/pulse_repeater.json
ea8200550190eb65d8631c7842e06d99274b3c79 assets/create/blockstates/purple_nixie_tube.json
d06cd9a1101b18d306a786320aab12018b1325d6 assets/create/blockstates/purple_sail.json
92957119abd5fbcca36a113b2a80255fd70fc303 assets/create/blockstates/purple_seat.json
@ -540,22 +542,22 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
6801fa1f466f172700e573e5b8ee8ee5f9ca4583 assets/create/blockstates/yellow_valve_handle.json
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
03a6020bfac9e4f979252abc8a631e6aafaa6b8b assets/create/lang/en_ud.json
f30503db6d2841ebc7c59bf0a79b680a765613cd assets/create/lang/en_us.json
3730041212c67067395b57218f26a403a18016c9 assets/create/lang/unfinished/de_de.json
f1dd81b07e832b0f3187b2f52fdf3cdbea41fec1 assets/create/lang/unfinished/es_cl.json
afc637fd921e0f17ec2b70d93b7a474d347a6a18 assets/create/lang/unfinished/es_es.json
7258250074ffe3e50fe4803a2effe4f2b7a94c4e assets/create/lang/unfinished/fr_fr.json
91cd404b62a87dd820cc5602622c88c65a5d5dbb assets/create/lang/unfinished/it_it.json
7716adb1099e6b164d07390679b3a1b77397048e assets/create/lang/unfinished/ja_jp.json
4b2b3ff736dbbfc58803432463e11850d651e631 assets/create/lang/unfinished/ko_kr.json
23ec8c0fbb3d82e92397ef5857b2f57e2f72ae40 assets/create/lang/unfinished/nl_nl.json
e8ef3db7b90bbc2a91bd104228c81b54bca638e7 assets/create/lang/unfinished/pl_pl.json
0213870418b26af7f67c794973144594918fa114 assets/create/lang/unfinished/pt_br.json
c0fccd4886228185a61af35535f3a92944d64c98 assets/create/lang/unfinished/pt_pt.json
2cf778412184edac79cf457adb57f68475d7bcf0 assets/create/lang/unfinished/ru_ru.json
feab3d65b3b95ecb6730d5d45fd672e4e2545ee3 assets/create/lang/unfinished/zh_cn.json
eee611a3eecfa457050d522195f76e95a35dca81 assets/create/lang/unfinished/zh_tw.json
dde2b8043a318bc88b6d381f9de6bfd557c0f079 assets/create/lang/en_ud.json
0ea900a1d36f0b19568f8183844a64016c539dcf assets/create/lang/en_us.json
c9977bb0eb8912a4260e414d8868628904d847a0 assets/create/lang/unfinished/de_de.json
4d30c4754b8494fbbe62340b7c67ba3d2ada6721 assets/create/lang/unfinished/es_cl.json
29c05f8ed2ec05e5bd4c73f6678946041c4e8293 assets/create/lang/unfinished/es_es.json
027514a78a5f233c2d579694fa3c6a2c57ce3272 assets/create/lang/unfinished/fr_fr.json
f354377f09ebc8ee7863dc24266215fc77003c11 assets/create/lang/unfinished/it_it.json
8e8d4fe60ca6386e14c31cb4a24274cd21e92212 assets/create/lang/unfinished/ja_jp.json
39194f6899360e4a13f0d3feac07ab25b5c18256 assets/create/lang/unfinished/ko_kr.json
4b067fe0830950e7cc6615c8f466686b5aefee7f assets/create/lang/unfinished/nl_nl.json
44fd3bc93eb6f2dce30a7f52a765662fb1093791 assets/create/lang/unfinished/pl_pl.json
410f0132633089e26db9d7b70c49fc3c39541cd6 assets/create/lang/unfinished/pt_br.json
bec4373054c1e0d2cd803572f8491e7597e292c2 assets/create/lang/unfinished/pt_pt.json
3ff78cccb260071cf5d55af9ace4f7ed1eadb46b assets/create/lang/unfinished/ru_ru.json
9ea4b032f852a55d9359fb931f5eb91243d87e36 assets/create/lang/unfinished/zh_cn.json
33a7cf2b3b6014b5714fdb5f63de49474f23d2aa assets/create/lang/unfinished/zh_tw.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
@ -1783,11 +1785,13 @@ b1eace1d7fe80e2b8dc0d844621f0d485f34cbf8 assets/create/models/item/cut_veridium_
523cd531eadaadc45fb356ca58b99a8fe206c3a7 assets/create/models/item/cyan_valve_handle.json
f786a43e296d9f10d7c302fe3ae9cddf4ba9984e assets/create/models/item/dark_oak_window.json
515d55b1ce18543fdb44b194901040fd29e75818 assets/create/models/item/dark_oak_window_pane.json
2faefc40f532f8480916f6d6a9c4a15e40deecef assets/create/models/item/data_gatherer.json
422c5ab12029ffdf37b315a158168d71725bd334 assets/create/models/item/deepslate_pillar.json
0f220a538e6a083debf68b4f1135d5f3ae4a3918 assets/create/models/item/deepslate_zinc_ore.json
2104c1276259ab67b94f3d4fe97e14b6bc6941ac assets/create/models/item/deployer.json
84d87f715efab45dc7bcb2e3c0870ed56fa20ee9 assets/create/models/item/depot.json
e0ecc0a20cf9dd54ccfc48e0041d5220b2c8316e assets/create/models/item/diorite_pillar.json
0ff9267a39783dce5e0aa59e78088c64337ad6ee assets/create/models/item/display_board.json
6006f88e56d74a3fd75a9dddb25af39075e0482b assets/create/models/item/diving_boots.json
df8cfe7e8eb527329094396e11222e9097e309d7 assets/create/models/item/diving_helmet.json
4b2af721dccfcf4e5b5a7b0f64f295d7cfd27f69 assets/create/models/item/dough.json
@ -3478,11 +3482,13 @@ db7ea40bfd36b5fa864453716256aba748435e36 data/create/loot_tables/blocks/cyan_sea
8854c95ee0d78abfb0393f7b4185618dc9aecba3 data/create/loot_tables/blocks/cyan_valve_handle.json
0d17705688109e9cf81c99ef559b1183b0e6053c data/create/loot_tables/blocks/dark_oak_window.json
636acaf2ebbbd790f8efe45a98cd036ffe848407 data/create/loot_tables/blocks/dark_oak_window_pane.json
c9dad60d36dde6522729fe433cb5022a00c12ce1 data/create/loot_tables/blocks/data_gatherer.json
18f29fec67600edc66b25217017b5b618efb1f4b data/create/loot_tables/blocks/deepslate_pillar.json
841d6010e87a4bf4e35e8ccc411e5d7f513b484a data/create/loot_tables/blocks/deepslate_zinc_ore.json
12a02b7737557a81281369826907b7e75076b8a0 data/create/loot_tables/blocks/deployer.json
b6118279802f1a27e6e0c3d0feca86f0792f85df data/create/loot_tables/blocks/depot.json
48eba3e521b190fedfb6e7580bdb10bcb3f290bd data/create/loot_tables/blocks/diorite_pillar.json
2a8d81a07e9d209349264787eee93a0b973d2510 data/create/loot_tables/blocks/display_board.json
7ab5f0aa32d6641999943636766c806a1d59e1d2 data/create/loot_tables/blocks/dripstone_pillar.json
2186860c4a0cb47a66bdfdefcde302c599cddeea data/create/loot_tables/blocks/encased_chain_drive.json
7fcc15674a7583b965441fb079b8997e4244a4ff data/create/loot_tables/blocks/encased_fan.json
@ -5292,7 +5298,7 @@ ff1900963bc4cd8ceffa78d58ef1952ceacb2fb7 data/forge/tags/items/storage_blocks/br
69f596fcb065e26b02ce246760432b5174191b76 data/minecraft/tags/blocks/impermeable.json
2db7759fe036160c14c6ed19a68604ca16f4de60 data/minecraft/tags/blocks/lush_ground_replaceable.json
02f7a9df2f9e154749266e7ac59c37aa076a3390 data/minecraft/tags/blocks/mineable/axe.json
a462e6c5caf03b7d11e61ef775724472fdd53570 data/minecraft/tags/blocks/mineable/pickaxe.json
20500209238916508e88a8219e7f3b342bb16461 data/minecraft/tags/blocks/mineable/pickaxe.json
2db7759fe036160c14c6ed19a68604ca16f4de60 data/minecraft/tags/blocks/moss_replaceable.json
e157c1d3af30e409e34bbefbe15a037e6e1c8daa data/minecraft/tags/blocks/needs_iron_tool.json
a08f67865337f62601c5e333b4011382d10020e4 data/minecraft/tags/blocks/needs_stone_tool.json

View file

@ -0,0 +1,56 @@
{
"variants": {
"facing=down,powered=false": {
"model": "create:block/data_gatherer/block",
"x": 180
},
"facing=up,powered=false": {
"model": "create:block/data_gatherer/block"
},
"facing=north,powered=false": {
"model": "create:block/data_gatherer/block",
"x": 90
},
"facing=south,powered=false": {
"model": "create:block/data_gatherer/block",
"x": 90,
"y": 180
},
"facing=west,powered=false": {
"model": "create:block/data_gatherer/block",
"x": 90,
"y": 270
},
"facing=east,powered=false": {
"model": "create:block/data_gatherer/block",
"x": 90,
"y": 90
},
"facing=down,powered=true": {
"model": "create:block/data_gatherer/block_powered",
"x": 180
},
"facing=up,powered=true": {
"model": "create:block/data_gatherer/block_powered"
},
"facing=north,powered=true": {
"model": "create:block/data_gatherer/block_powered",
"x": 90
},
"facing=south,powered=true": {
"model": "create:block/data_gatherer/block_powered",
"x": 90,
"y": 180
},
"facing=west,powered=true": {
"model": "create:block/data_gatherer/block_powered",
"x": 90,
"y": 270
},
"facing=east,powered=true": {
"model": "create:block/data_gatherer/block_powered",
"x": 90,
"y": 90
}
}
}

View file

@ -0,0 +1,124 @@
{
"variants": {
"down=false,facing=north,up=false,waterlogged=false": {
"model": "create:block/display_board/block"
},
"down=true,facing=north,up=false,waterlogged=false": {
"model": "create:block/display_board/block"
},
"down=false,facing=south,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 180
},
"down=true,facing=south,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 180
},
"down=false,facing=west,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 270
},
"down=true,facing=west,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 270
},
"down=false,facing=east,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 90
},
"down=true,facing=east,up=false,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 90
},
"down=false,facing=north,up=true,waterlogged=false": {
"model": "create:block/display_board/block"
},
"down=true,facing=north,up=true,waterlogged=false": {
"model": "create:block/display_board/block"
},
"down=false,facing=south,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 180
},
"down=true,facing=south,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 180
},
"down=false,facing=west,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 270
},
"down=true,facing=west,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 270
},
"down=false,facing=east,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 90
},
"down=true,facing=east,up=true,waterlogged=false": {
"model": "create:block/display_board/block",
"y": 90
},
"down=false,facing=north,up=false,waterlogged=true": {
"model": "create:block/display_board/block"
},
"down=true,facing=north,up=false,waterlogged=true": {
"model": "create:block/display_board/block"
},
"down=false,facing=south,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 180
},
"down=true,facing=south,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 180
},
"down=false,facing=west,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 270
},
"down=true,facing=west,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 270
},
"down=false,facing=east,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 90
},
"down=true,facing=east,up=false,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 90
},
"down=false,facing=north,up=true,waterlogged=true": {
"model": "create:block/display_board/block"
},
"down=true,facing=north,up=true,waterlogged=true": {
"model": "create:block/display_board/block"
},
"down=false,facing=south,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 180
},
"down=true,facing=south,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 180
},
"down=false,facing=west,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 270
},
"down=true,facing=west,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 270
},
"down=false,facing=east,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 90
},
"down=true,facing=east,up=true,waterlogged=true": {
"model": "create:block/display_board/block",
"y": 90
}
}
}

View file

@ -1,64 +1,124 @@
{
"variants": {
"facing=north,powered=false,powering=false": {
"facing=north,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_extender",
"y": 180
},
"facing=south,powered=false,powering=false": {
"facing=south,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_extender"
},
"facing=west,powered=false,powering=false": {
"facing=west,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_extender",
"y": 90
},
"facing=east,powered=false,powering=false": {
"facing=east,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_extender",
"y": 270
},
"facing=north,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 180
},
"facing=south,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered"
},
"facing=west,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 90
},
"facing=east,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 270
},
"facing=north,powered=false,powering=true": {
"facing=north,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_extender_powering",
"y": 180
},
"facing=south,powered=false,powering=true": {
"facing=south,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_extender_powering"
},
"facing=west,powered=false,powering=true": {
"facing=west,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_extender_powering",
"y": 90
},
"facing=east,powered=false,powering=true": {
"facing=east,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_extender_powering",
"y": 270
},
"facing=north,powered=true,powering=true": {
"facing=north,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 180
},
"facing=south,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered"
},
"facing=west,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 90
},
"facing=east,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered",
"y": 270
},
"facing=north,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered_powering",
"y": 180
},
"facing=south,powered=true,powering=true": {
"facing=south,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered_powering"
},
"facing=west,powered=true,powering=true": {
"facing=west,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered_powering",
"y": 90
},
"facing=east,powered=true,powering=true": {
"facing=east,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_extender_powered_powering",
"y": 270
},
"facing=north,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_extender_powering",
"y": 180
},
"facing=south,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_extender_powering"
},
"facing=west,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_extender_powering",
"y": 90
},
"facing=east,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_extender_powering",
"y": 270
},
"facing=north,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_extender",
"y": 180
},
"facing=south,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_extender"
},
"facing=west,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_extender",
"y": 90
},
"facing=east,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_extender",
"y": 270
},
"facing=north,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered_powering",
"y": 180
},
"facing=south,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered_powering"
},
"facing=west,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered_powering",
"y": 90
},
"facing=east,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered_powering",
"y": 270
},
"facing=north,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered",
"y": 180
},
"facing=south,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered"
},
"facing=west,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered",
"y": 90
},
"facing=east,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_extender_powered",
"y": 270
}
}
}

View file

@ -1,64 +1,124 @@
{
"variants": {
"facing=north,powered=false,powering=false": {
"facing=north,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_repeater",
"y": 180
},
"facing=south,powered=false,powering=false": {
"facing=south,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_repeater"
},
"facing=west,powered=false,powering=false": {
"facing=west,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_repeater",
"y": 90
},
"facing=east,powered=false,powering=false": {
"facing=east,inverted=false,powered=false,powering=false": {
"model": "create:block/diodes/pulse_repeater",
"y": 270
},
"facing=north,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 180
},
"facing=south,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered"
},
"facing=west,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 90
},
"facing=east,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 270
},
"facing=north,powered=false,powering=true": {
"facing=north,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_repeater_powering",
"y": 180
},
"facing=south,powered=false,powering=true": {
"facing=south,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_repeater_powering"
},
"facing=west,powered=false,powering=true": {
"facing=west,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_repeater_powering",
"y": 90
},
"facing=east,powered=false,powering=true": {
"facing=east,inverted=true,powered=false,powering=false": {
"model": "create:block/pulse_repeater_powering",
"y": 270
},
"facing=north,powered=true,powering=true": {
"facing=north,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 180
},
"facing=south,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered"
},
"facing=west,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 90
},
"facing=east,inverted=false,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered",
"y": 270
},
"facing=north,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 180
},
"facing=south,powered=true,powering=true": {
"facing=south,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered_powering"
},
"facing=west,powered=true,powering=true": {
"facing=west,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 90
},
"facing=east,powered=true,powering=true": {
"facing=east,inverted=true,powered=true,powering=false": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 270
},
"facing=north,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_repeater_powering",
"y": 180
},
"facing=south,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_repeater_powering"
},
"facing=west,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_repeater_powering",
"y": 90
},
"facing=east,inverted=false,powered=false,powering=true": {
"model": "create:block/pulse_repeater_powering",
"y": 270
},
"facing=north,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_repeater",
"y": 180
},
"facing=south,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_repeater"
},
"facing=west,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_repeater",
"y": 90
},
"facing=east,inverted=true,powered=false,powering=true": {
"model": "create:block/diodes/pulse_repeater",
"y": 270
},
"facing=north,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 180
},
"facing=south,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered_powering"
},
"facing=west,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 90
},
"facing=east,inverted=false,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered_powering",
"y": 270
},
"facing=north,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered",
"y": 180
},
"facing=south,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered"
},
"facing=west,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered",
"y": 90
},
"facing=east,inverted=true,powered=true,powering=true": {
"model": "create:block/pulse_repeater_powered",
"y": 270
}
}
}

View file

@ -189,11 +189,13 @@
"block.create.cyan_valve_handle": "\u01DD\u05DFpu\u0250H \u01DD\u028C\u05DF\u0250\u039B u\u0250\u028E\u0186",
"block.create.dark_oak_window": "\u028Dopu\u0131M \u029E\u0250O \u029E\u0279\u0250\u15E1",
"block.create.dark_oak_window_pane": "\u01DDu\u0250\u0500 \u028Dopu\u0131M \u029E\u0250O \u029E\u0279\u0250\u15E1",
"block.create.data_gatherer": "\u0279\u01DD\u0279\u01DD\u0265\u0287\u0250\u2141 \u0250\u0287\u0250\u15E1",
"block.create.deepslate_pillar": "\u0279\u0250\u05DF\u05DF\u0131\u0500 \u01DD\u0287\u0250\u05DFsd\u01DD\u01DD\u15E1",
"block.create.deepslate_zinc_ore": "\u01DD\u0279O \u0254u\u0131Z \u01DD\u0287\u0250\u05DFsd\u01DD\u01DD\u15E1",
"block.create.deployer": "\u0279\u01DD\u028Eo\u05DFd\u01DD\u15E1",
"block.create.depot": "\u0287od\u01DD\u15E1",
"block.create.diorite_pillar": "\u0279\u0250\u05DF\u05DF\u0131\u0500 \u01DD\u0287\u0131\u0279o\u0131\u15E1",
"block.create.display_board": "p\u0279\u0250o\u15FA \u028E\u0250\u05DFds\u0131\u15E1",
"block.create.dripstone_pillar": "\u0279\u0250\u05DF\u05DF\u0131\u0500 \u01DDuo\u0287sd\u0131\u0279\u15E1",
"block.create.encased_chain_drive": "\u01DD\u028C\u0131\u0279\u15E1 u\u0131\u0250\u0265\u0186 p\u01DDs\u0250\u0254u\u018E",
"block.create.encased_fan": "u\u0250\u2132 p\u01DDs\u0250\u0254u\u018E",

View file

@ -192,11 +192,13 @@
"block.create.cyan_valve_handle": "Cyan Valve Handle",
"block.create.dark_oak_window": "Dark Oak Window",
"block.create.dark_oak_window_pane": "Dark Oak Window Pane",
"block.create.data_gatherer": "Data Gatherer",
"block.create.deepslate_pillar": "Deepslate Pillar",
"block.create.deepslate_zinc_ore": "Deepslate Zinc Ore",
"block.create.deployer": "Deployer",
"block.create.depot": "Depot",
"block.create.diorite_pillar": "Diorite Pillar",
"block.create.display_board": "Display Board",
"block.create.dripstone_pillar": "Dripstone Pillar",
"block.create.encased_chain_drive": "Encased Chain Drive",
"block.create.encased_fan": "Encased Fan",
@ -1433,6 +1435,72 @@
"create.contraption.controls.stop_controlling": "Stopped controlling contraption",
"create.contraption.controls.approach_station": "Hold %1$s to approach %2$s",
"create.data_gatherer.set": "Targeted position selected",
"create.data_gatherer.success": "Successfully bound to targeted position",
"create.data_gatherer.clear": "Cleared position selection",
"create.data_gatherer.too_far": "Targeted position is too far from here",
"create.data_gatherer.invalid": "Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "Data Gatherer",
"create.data_gatherer.no_source": "Not a Data Source",
"create.data_gatherer.no_target": "Not a Data Target",
"create.data_gatherer.reading_from": "Read from:",
"create.data_gatherer.writing_to": "Send to:",
"create.data_gatherer.attached_side": "Block on attached side",
"create.data_gatherer.targeted_location": "Block in targeted location",
"create.data_gatherer.view_compatible": "Click to view all Compatible",
"create.data_gatherer.information_type": "Type of Information",
"create.data_gatherer.display_on": "Write data to:",
"create.data_gatherer.display_on_multiline": "Start writing at:",
"create.data_source.label": "Attached Label",
"create.data_source.combine_item_names": "Combine Item Names",
"create.data_source.count_items": "Amount of matching Items",
"create.data_source.list_items": "List matching Items",
"create.data_source.nixie_tube": "Copy Nixie Tubes",
"create.data_source.fill_level": "Container Fill Level",
"create.data_source.fill_level.display": "Display Format",
"create.data_source.fill_level.percent": "Percent",
"create.data_source.fill_level.progress_bar": "Progress Bar",
"create.data_source.value_list.display": "Value Display",
"create.data_source.value_list.shortened": "Shortened",
"create.data_source.value_list.full_number": "Full Number",
"create.data_source.value_list.thousand": "k",
"create.data_source.value_list.million": "m",
"create.data_source.player_deaths": "Player Deaths",
"create.data_source.scoreboard": "Scoreboard",
"create.data_source.scoreboard.objective": "Objective ID",
"create.data_source.scoreboard.objective_not_found": "'%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "Player Deaths",
"create.data_source.time_of_day": "Time of Day",
"create.data_source.stop_watch": "Stopwatch",
"create.data_source.time.format": "Time Format",
"create.data_source.time.12_hour": "12-hour",
"create.data_source.time.24_hour": "24-hour",
"create.data_source.accumulate_items": "Accumulate Item Count",
"create.data_source.item_throughput": "Item Throughput",
"create.data_source.item_throughput.interval": "Interval",
"create.data_source.item_throughput.interval.second": "per Second",
"create.data_source.item_throughput.interval.minute": "per Minute",
"create.data_source.item_throughput.interval.hour": "per Hour",
"create.data_source.station_summary": "Train Station Summary",
"create.data_source.station_summary.filter": "Station name filter",
"create.data_source.station_summary.train_name_column": "Train column size",
"create.data_source.station_summary.platform_column": "Platform column size",
"create.data_source.station_summary.now": "now",
"create.data_source.station_summary.minutes": " min",
"create.data_source.station_summary.seconds": "%1$ss",
"create.data_target.line": "Line %1$s",
"create.data_target.page": "Page %1$s",
"create.data_target.single_line": "Single Line",
"create.flap_display.cycles.alphabet": " ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": " ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": " ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": " ;K;M",
"create.flap_display.cycles.instant": " ; ",
"create.flap_display.cycles.pixel": "█;▒",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1439",
"_": "Missing Localizations: 1503",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Türkiser Ventilgriff",
"block.create.dark_oak_window": "Schwarzeichenholzfenster",
"block.create.dark_oak_window_pane": "Schwarzeichenholzfensterscheibe",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Einsatzgerät",
"block.create.depot": "Depot",
"block.create.diorite_pillar": "Dioritsäule",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Ummantelter Kettenriemen",
"block.create.encased_fan": "Ummantelter Lüfter",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "Dies ist ein Beispiel Overlay",
"create.gui.config.overlay3": "Klicke oder ziehe mit deiner Maus",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 450",
"_": "Missing Localizations: 514",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Válvula Cian",
"block.create.dark_oak_window": "Ventana de Roble Oscuro",
"block.create.dark_oak_window_pane": "Panel de Ventana de Roble Oscuro",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Desplegador",
"block.create.depot": "Depósito",
"block.create.diorite_pillar": "Pilar de Diorita",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Conductor en Cadena Encubierto",
"block.create.encased_fan": "Ventilador Encubierto",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Hola :)",
"create.gui.config.overlay2": "Este es un overlay de ejemplo",
"create.gui.config.overlay3": "Haz clic o arrastra con el mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 121",
"_": "Missing Localizations: 185",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Asa de válvula cian",
"block.create.dark_oak_window": "Ventana de roble oscuro",
"block.create.dark_oak_window_pane": "Panel de ventana de roble oscuro",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "Pilar de pizarra profunda",
"block.create.deepslate_zinc_ore": "Mena de cinc de pizarra profunda",
"block.create.deployer": "Desplegador",
"block.create.depot": "Depósito",
"block.create.diorite_pillar": "Pilar de diorita",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "Pilar de espeleotema",
"block.create.encased_chain_drive": "Cadena de transmisión revestida",
"block.create.encased_fan": "Ventilador revestido",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Hola :)",
"create.gui.config.overlay2": "Esta es una muestra de la superposición",
"create.gui.config.overlay3": "Haga clic o arrastre con el ratón",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1701",
"_": "Missing Localizations: 1765",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Vanne cyan",
"block.create.dark_oak_window": "fenêtre en chêne sombre",
"block.create.dark_oak_window_pane": "Vitre en chêne sombre",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Déployeur",
"block.create.depot": "Dépot",
"block.create.diorite_pillar": "UNLOCALIZED: Diorite Pillar",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Chaine de transmission",
"block.create.encased_fan": "Ventilateur enchâssé",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1390",
"_": "Missing Localizations: 1454",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Maniglia per valvola ciano",
"block.create.dark_oak_window": "Finestra di quercia scura",
"block.create.dark_oak_window_pane": "Pannello di finestra di quercia scura",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Installatore",
"block.create.depot": "Deposito",
"block.create.diorite_pillar": "Pilastro di diorite",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Trasmissione a catena incassata",
"block.create.encased_fan": "Ventilatore incassato",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Ciao :)",
"create.gui.config.overlay2": "Questo overlay è di esempio",
"create.gui.config.overlay3": "Cliccalo o trascinalo col mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 116",
"_": "Missing Localizations: 180",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "水色のバルブハンドル",
"block.create.dark_oak_window": "ダークオークの窓",
"block.create.dark_oak_window_pane": "ダークオークの板窓",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "深層岩の柱",
"block.create.deepslate_zinc_ore": "深層亜鉛鉱石",
"block.create.deployer": "デプロイヤー",
"block.create.depot": "デポ",
"block.create.diorite_pillar": "閃緑岩の柱",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "鍾乳石の柱",
"block.create.encased_chain_drive": "ケース入りチェーンドライブ",
"block.create.encased_fan": "ケース入りファン",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "やぁ(・∀・)",
"create.gui.config.overlay2": "これはオーバーレイのサンプルです",
"create.gui.config.overlay3": "マウスでクリックまたはドラッグしてください",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 116",
"_": "Missing Localizations: 180",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "청록색 밸브 손잡이",
"block.create.dark_oak_window": "짙은 참나무 유리창",
"block.create.dark_oak_window_pane": "짙은 참나무 유리판",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "심층암 기둥",
"block.create.deepslate_zinc_ore": "심층암 아연 광석",
"block.create.deployer": "기계 손",
"block.create.depot": "아이템 거치대",
"block.create.diorite_pillar": "섬록암 기둥",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "점적석 기둥",
"block.create.encased_chain_drive": "체인 드라이브",
"block.create.encased_fan": "선풍기",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 2054",
"_": "Missing Localizations: 2118",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "UNLOCALIZED: Cyan Valve Handle",
"block.create.dark_oak_window": "UNLOCALIZED: Dark Oak Window",
"block.create.dark_oak_window_pane": "UNLOCALIZED: Dark Oak Window Pane",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "UNLOCALIZED: Deployer",
"block.create.depot": "UNLOCALIZED: Depot",
"block.create.diorite_pillar": "UNLOCALIZED: Diorite Pillar",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "UNLOCALIZED: Encased Chain Drive",
"block.create.encased_fan": "Omhulsde Ventilator",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 489",
"_": "Missing Localizations: 553",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Błękitne pokrętło",
"block.create.dark_oak_window": "Ciemnodębowe okno",
"block.create.dark_oak_window_pane": "Ciemnodębowa szyba okienna",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Aplikator",
"block.create.depot": "Składnica",
"block.create.diorite_pillar": "Diorytowy filar",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Izolowany przekaźnik łańcuchowy",
"block.create.encased_fan": "Izolowany wiatrak",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Cześć :)",
"create.gui.config.overlay2": "To jest przykładowa nakładka",
"create.gui.config.overlay3": "Kliknij lub przeciągnij myszką",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1301",
"_": "Missing Localizations: 1365",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Válvula ciano",
"block.create.dark_oak_window": "Janela de carvalho escuro",
"block.create.dark_oak_window_pane": "Vidraça de carvalho escuro",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "Pilar de ardósia",
"block.create.deepslate_zinc_ore": "Minério de zinco de ardósia",
"block.create.deployer": "Implantador",
"block.create.depot": "Depósito",
"block.create.diorite_pillar": "Pilar de diorito",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "Pilar de espeleotema",
"block.create.encased_chain_drive": "Correia Revestida",
"block.create.encased_fan": "Ventilador Revestida",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1673",
"_": "Missing Localizations: 1737",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Manopla de válvula ciano",
"block.create.dark_oak_window": "UNLOCALIZED: Dark Oak Window",
"block.create.dark_oak_window_pane": "UNLOCALIZED: Dark Oak Window Pane",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "Deepslate zinc ore",
"block.create.deployer": "Implantador",
"block.create.depot": "Depósito",
"block.create.diorite_pillar": "UNLOCALIZED: Diorite Pillar",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Correia Revestida",
"block.create.encased_fan": "Ventilador Revestida",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 494",
"_": "Missing Localizations: 558",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "Бирюзовый ручной вентиль",
"block.create.dark_oak_window": "Окно из тёмного дуба",
"block.create.dark_oak_window_pane": "Панель из окна из тёмного дуб",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "Автономный активатор",
"block.create.depot": "Депо",
"block.create.diorite_pillar": "Диоритовая колонна",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "Цепной привод в корпусе",
"block.create.encased_fan": "Вентилятор в корпусе",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Привет :)",
"create.gui.config.overlay2": "Это образец оверлея",
"create.gui.config.overlay3": "Кликни и тащи с помощью мыши",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 116",
"_": "Missing Localizations: 180",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "青色阀门手轮",
"block.create.dark_oak_window": "深色橡木窗户",
"block.create.dark_oak_window_pane": "深色橡木窗户板",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "深板岩柱",
"block.create.deepslate_zinc_ore": "深层锌矿石",
"block.create.deployer": "机械手",
"block.create.depot": "置物台",
"block.create.diorite_pillar": "闪长岩柱",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "滴水石柱",
"block.create.encased_chain_drive": "链式传动箱",
"block.create.encased_fan": "鼓风机",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "这是一个实例层",
"create.gui.config.overlay3": "点击拖拽你的鼠标",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 508",
"_": "Missing Localizations: 572",
"_": "->------------------------] Game Elements [------------------------<-",
@ -193,11 +193,13 @@
"block.create.cyan_valve_handle": "藍綠色閥門開關",
"block.create.dark_oak_window": "黑橡木窗戶",
"block.create.dark_oak_window_pane": "黑橡木窗戶片",
"block.create.data_gatherer": "UNLOCALIZED: Data Gatherer",
"block.create.deepslate_pillar": "UNLOCALIZED: Deepslate Pillar",
"block.create.deepslate_zinc_ore": "UNLOCALIZED: Deepslate Zinc Ore",
"block.create.deployer": "機器手",
"block.create.depot": "置物臺",
"block.create.diorite_pillar": "豎紋閃長岩",
"block.create.display_board": "UNLOCALIZED: Display Board",
"block.create.dripstone_pillar": "UNLOCALIZED: Dripstone Pillar",
"block.create.encased_chain_drive": "鏈式傳動箱",
"block.create.encased_fan": "鼓風機",
@ -1434,6 +1436,72 @@
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",
"create.data_gatherer.set": "UNLOCALIZED: Targeted position selected",
"create.data_gatherer.success": "UNLOCALIZED: Successfully bound to targeted position",
"create.data_gatherer.clear": "UNLOCALIZED: Cleared position selection",
"create.data_gatherer.too_far": "UNLOCALIZED: Targeted position is too far from here",
"create.data_gatherer.invalid": "UNLOCALIZED: Gatherer has no valid target, try placing it again",
"create.data_gatherer.title": "UNLOCALIZED: Data Gatherer",
"create.data_gatherer.no_source": "UNLOCALIZED: Not a Data Source",
"create.data_gatherer.no_target": "UNLOCALIZED: Not a Data Target",
"create.data_gatherer.reading_from": "UNLOCALIZED: Read from:",
"create.data_gatherer.writing_to": "UNLOCALIZED: Send to:",
"create.data_gatherer.attached_side": "UNLOCALIZED: Block on attached side",
"create.data_gatherer.targeted_location": "UNLOCALIZED: Block in targeted location",
"create.data_gatherer.view_compatible": "UNLOCALIZED: Click to view all Compatible",
"create.data_gatherer.information_type": "UNLOCALIZED: Type of Information",
"create.data_gatherer.display_on": "UNLOCALIZED: Write data to:",
"create.data_gatherer.display_on_multiline": "UNLOCALIZED: Start writing at:",
"create.data_source.label": "UNLOCALIZED: Attached Label",
"create.data_source.combine_item_names": "UNLOCALIZED: Combine Item Names",
"create.data_source.count_items": "UNLOCALIZED: Amount of matching Items",
"create.data_source.list_items": "UNLOCALIZED: List matching Items",
"create.data_source.nixie_tube": "UNLOCALIZED: Copy Nixie Tubes",
"create.data_source.fill_level": "UNLOCALIZED: Container Fill Level",
"create.data_source.fill_level.display": "UNLOCALIZED: Display Format",
"create.data_source.fill_level.percent": "UNLOCALIZED: Percent",
"create.data_source.fill_level.progress_bar": "UNLOCALIZED: Progress Bar",
"create.data_source.value_list.display": "UNLOCALIZED: Value Display",
"create.data_source.value_list.shortened": "UNLOCALIZED: Shortened",
"create.data_source.value_list.full_number": "UNLOCALIZED: Full Number",
"create.data_source.value_list.thousand": "UNLOCALIZED: k",
"create.data_source.value_list.million": "UNLOCALIZED: m",
"create.data_source.player_deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.scoreboard": "UNLOCALIZED: Scoreboard",
"create.data_source.scoreboard.objective": "UNLOCALIZED: Objective ID",
"create.data_source.scoreboard.objective_not_found": "UNLOCALIZED: '%1$s' not found",
"create.data_source.scoreboard.objective.deaths": "UNLOCALIZED: Player Deaths",
"create.data_source.time_of_day": "UNLOCALIZED: Time of Day",
"create.data_source.stop_watch": "UNLOCALIZED: Stopwatch",
"create.data_source.time.format": "UNLOCALIZED: Time Format",
"create.data_source.time.12_hour": "UNLOCALIZED: 12-hour",
"create.data_source.time.24_hour": "UNLOCALIZED: 24-hour",
"create.data_source.accumulate_items": "UNLOCALIZED: Accumulate Item Count",
"create.data_source.item_throughput": "UNLOCALIZED: Item Throughput",
"create.data_source.item_throughput.interval": "UNLOCALIZED: Interval",
"create.data_source.item_throughput.interval.second": "UNLOCALIZED: per Second",
"create.data_source.item_throughput.interval.minute": "UNLOCALIZED: per Minute",
"create.data_source.item_throughput.interval.hour": "UNLOCALIZED: per Hour",
"create.data_source.station_summary": "UNLOCALIZED: Train Station Summary",
"create.data_source.station_summary.filter": "UNLOCALIZED: Station name filter",
"create.data_source.station_summary.train_name_column": "UNLOCALIZED: Train column size",
"create.data_source.station_summary.platform_column": "UNLOCALIZED: Platform column size",
"create.data_source.station_summary.now": "UNLOCALIZED: now",
"create.data_source.station_summary.minutes": "UNLOCALIZED: min",
"create.data_source.station_summary.seconds": "UNLOCALIZED: %1$ss",
"create.data_target.line": "UNLOCALIZED: Line %1$s",
"create.data_target.page": "UNLOCALIZED: Page %1$s",
"create.data_target.single_line": "UNLOCALIZED: Single Line",
"create.flap_display.cycles.alphabet": "UNLOCALIZED: ;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;X;Y;Z",
"create.flap_display.cycles.numeric": "UNLOCALIZED: ;0;1;2;3;4;5;6;7;8;9",
"create.flap_display.cycles.arrival_time": "UNLOCALIZED: ; min;now;15s;30s;45s",
"create.flap_display.cycles.shortened_numbers": "UNLOCALIZED: ;K;M",
"create.flap_display.cycles.instant": "UNLOCALIZED: ; ",
"create.flap_display.cycles.pixel": "UNLOCALIZED: █;▒",
"create.gui.config.overlay1": "嗨 :)",
"create.gui.config.overlay2": "這是一個實例層",
"create.gui.config.overlay3": "點擊拖拽你的滑鼠",

View file

@ -0,0 +1,3 @@
{
"parent": "create:block/data_gatherer/block"
}

View file

@ -0,0 +1,3 @@
{
"parent": "create:block/display_board/item"
}

View file

@ -0,0 +1,20 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1.0,
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "create:data_gatherer"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View file

@ -0,0 +1,20 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1.0,
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "create:display_board"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View file

@ -121,6 +121,7 @@
"create:content_observer",
"create:stockpile_switch",
"create:creative_crate",
"create:display_board",
"create:nixie_tube",
"create:white_nixie_tube",
"create:magenta_nixie_tube",

View file

@ -110,55 +110,40 @@ public class AllBlockPartials {
COPPER_BACKTANK_SHAFT = block("copper_backtank/block_shaft_input"),
COPPER_BACKTANK_COGS = block("copper_backtank/block_cogs"),
TRACK_SEGMENT_LEFT = block("track/segment_left"),
TRACK_SEGMENT_RIGHT = block("track/segment_right"),
TRACK_TIE = block("track/tie"),
GIRDER_SEGMENT_TOP = block("metal_girder/segment_top"),
TRACK_SEGMENT_LEFT = block("track/segment_left"), TRACK_SEGMENT_RIGHT = block("track/segment_right"),
TRACK_TIE = block("track/tie"), GIRDER_SEGMENT_TOP = block("metal_girder/segment_top"),
GIRDER_SEGMENT_MIDDLE = block("metal_girder/segment_middle"),
GIRDER_SEGMENT_BOTTOM = block("metal_girder/segment_bottom"),
TRACK_STATION_OVERLAY = block("track_overlay/station"),
TRACK_SIGNAL_OVERLAY = block("track_overlay/signal"),
TRACK_STATION_OVERLAY = block("track_overlay/station"), TRACK_SIGNAL_OVERLAY = block("track_overlay/signal"),
TRACK_ASSEMBLING_OVERLAY = block("track_overlay/assembling"),
TRACK_SIGNAL_DUAL_OVERLAY = block("track_overlay/signal_dual"),
BOGEY_FRAME = block("track/bogey/bogey_frame"),
SMALL_BOGEY_WHEELS = block("track/bogey/bogey_wheel"),
BOGEY_PIN = block("track/bogey/bogey_drive_wheel_pin"),
BOGEY_PISTON = block("track/bogey/bogey_drive_piston"),
BOGEY_DRIVE = block("track/bogey/bogey_drive"),
LARGE_BOGEY_WHEELS = block("track/bogey/bogey_drive_wheel"),
BOGEY_FRAME = block("track/bogey/bogey_frame"), SMALL_BOGEY_WHEELS = block("track/bogey/bogey_wheel"),
BOGEY_PIN = block("track/bogey/bogey_drive_wheel_pin"), BOGEY_PISTON = block("track/bogey/bogey_drive_piston"),
BOGEY_DRIVE = block("track/bogey/bogey_drive"), LARGE_BOGEY_WHEELS = block("track/bogey/bogey_drive_wheel"),
TRAIN_COUPLING_HEAD = block("track/bogey/coupling_head"),
TRAIN_COUPLING_CABLE = block("track/bogey/coupling_cable"),
TRAIN_CONTROLS_COVER = block("controls/train/cover"),
TRAIN_CONTROLS_LEVER = block("controls/train/lever"),
ENGINE_PISTON = block("steam_engine/piston"),
ENGINE_LINKAGE = block("steam_engine/linkage"),
ENGINE_CONNECTOR = block("steam_engine/shaft_connector"),
BOILER_GAUGE = block("steam_engine/gauge"),
SIGNAL_ON = block("track_signal/indicator_on"),
SIGNAL_OFF = block("track_signal/indicator_off"),
SIGNAL_PANEL = block("track_signal/panel"),
SIGNAL_WHITE_CUBE = block("track_signal/white_cube"),
SIGNAL_WHITE_GLOW = block("track_signal/white_glow"),
SIGNAL_WHITE = block("track_signal/white_tube"),
SIGNAL_RED_CUBE = block("track_signal/red_cube"),
SIGNAL_RED_GLOW = block("track_signal/red_glow"),
SIGNAL_RED = block("track_signal/red_tube"),
SIGNAL_YELLOW_CUBE = block("track_signal/yellow_cube"),
SIGNAL_YELLOW_GLOW = block("track_signal/yellow_glow"),
SIGNAL_YELLOW = block("track_signal/yellow_tube"),
TRAIN_CONTROLS_COVER = block("controls/train/cover"), TRAIN_CONTROLS_LEVER = block("controls/train/lever"),
ENGINE_PISTON = block("steam_engine/piston"), ENGINE_LINKAGE = block("steam_engine/linkage"),
ENGINE_CONNECTOR = block("steam_engine/shaft_connector"), BOILER_GAUGE = block("steam_engine/gauge"),
SIGNAL_ON = block("track_signal/indicator_on"), SIGNAL_OFF = block("track_signal/indicator_off"),
SIGNAL_PANEL = block("track_signal/panel"), SIGNAL_WHITE_CUBE = block("track_signal/white_cube"),
SIGNAL_WHITE_GLOW = block("track_signal/white_glow"), SIGNAL_WHITE = block("track_signal/white_tube"),
SIGNAL_RED_CUBE = block("track_signal/red_cube"), SIGNAL_RED_GLOW = block("track_signal/red_glow"),
SIGNAL_RED = block("track_signal/red_tube"), SIGNAL_YELLOW_CUBE = block("track_signal/yellow_cube"),
SIGNAL_YELLOW_GLOW = block("track_signal/yellow_glow"), SIGNAL_YELLOW = block("track_signal/yellow_tube"),
CRAFTING_BLUEPRINT_1x1 = entity("crafting_blueprint_small"),
CRAFTING_BLUEPRINT_2x2 = entity("crafting_blueprint_medium"),
CRAFTING_BLUEPRINT_3x3 = entity("crafting_blueprint_large"),
COUPLING_ATTACHMENT = entity("minecart_coupling/attachment"), COUPLING_RING = entity("minecart_coupling/ring"),
COUPLING_CONNECTOR = entity("minecart_coupling/connector")
@ -169,8 +154,7 @@ public class AllBlockPartials {
public static final Map<BlazeBurnerBlock.HeatLevel, PartialModel> BLAZES =
new EnumMap<>(BlazeBurnerBlock.HeatLevel.class);
public static final Map<Direction, PartialModel> METAL_GIRDER_BRACKETS =
new EnumMap<>(Direction.class);
public static final Map<Direction, PartialModel> METAL_GIRDER_BRACKETS = new EnumMap<>(Direction.class);
public static final Map<DyeColor, PartialModel> TOOLBOX_LIDS = new EnumMap<>(DyeColor.class);
static {

View file

@ -7,6 +7,7 @@ import static com.simibubi.create.AllTags.axeOrPickaxe;
import static com.simibubi.create.AllTags.pickaxeOnly;
import static com.simibubi.create.AllTags.tagBlockAndItem;
import static com.simibubi.create.content.AllSections.SCHEMATICS;
import static com.simibubi.create.content.logistics.block.data.AllDataGathererBehaviours.assignDataBehaviour;
import static com.simibubi.create.foundation.data.BlockStateGen.axisBlock;
import static com.simibubi.create.foundation.data.CreateRegistrate.connectedTextures;
import static com.simibubi.create.foundation.data.ModelGen.customItemModel;
@ -142,6 +143,18 @@ import com.simibubi.create.content.logistics.block.chute.ChuteBlock;
import com.simibubi.create.content.logistics.block.chute.ChuteGenerator;
import com.simibubi.create.content.logistics.block.chute.ChuteItem;
import com.simibubi.create.content.logistics.block.chute.SmartChuteBlock;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.content.logistics.block.data.DataGathererBlockItem;
import com.simibubi.create.content.logistics.block.data.source.AccumulatedItemCountDataSource;
import com.simibubi.create.content.logistics.block.data.source.FillLevelDataSource;
import com.simibubi.create.content.logistics.block.data.source.ItemCountDataSource;
import com.simibubi.create.content.logistics.block.data.source.ItemListDataSource;
import com.simibubi.create.content.logistics.block.data.source.ItemNameDataSource;
import com.simibubi.create.content.logistics.block.data.source.ItemThoughputDataSource;
import com.simibubi.create.content.logistics.block.data.source.StationSummaryDataSource;
import com.simibubi.create.content.logistics.block.data.source.StopWatchDataSource;
import com.simibubi.create.content.logistics.block.data.source.TimeOfDayDataSource;
import com.simibubi.create.content.logistics.block.data.target.FlapDisplayDataTarget;
import com.simibubi.create.content.logistics.block.depot.DepotBlock;
import com.simibubi.create.content.logistics.block.depot.EjectorBlock;
import com.simibubi.create.content.logistics.block.depot.EjectorItem;
@ -175,6 +188,7 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultCTBehaviour;
import com.simibubi.create.content.logistics.block.vault.ItemVaultItem;
import com.simibubi.create.content.logistics.item.LecternControllerBlock;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlock;
@ -426,6 +440,7 @@ public class AllBlocks {
.transform(axeOrPickaxe())
.blockstate(new BeltGenerator()::generate)
.transform(BlockStressDefaults.setImpact(0))
.onRegister(assignDataBehaviour(new ItemNameDataSource(), "combine_item_names"))
.onRegister(CreateRegistrate.blockModel(() -> BeltModel::new))
.register();
@ -495,6 +510,8 @@ public class AllBlocks {
REGISTRATE.block("cuckoo_clock", CuckooClockBlock::regular)
.transform(axeOrPickaxe())
.transform(BuilderTransformers.cuckooClock())
.onRegister(assignDataBehaviour(new TimeOfDayDataSource(), "time_of_day"))
.onRegister(assignDataBehaviour(new StopWatchDataSource(), "stop_watch"))
.register();
public static final BlockEntry<CuckooClockBlock> MYSTERIOUS_CUCKOO_CLOCK =
@ -528,7 +545,9 @@ public class AllBlocks {
public static final BlockEntry<CrushingWheelControllerBlock> CRUSHING_WHEEL_CONTROLLER =
REGISTRATE.block("crushing_wheel_controller", CrushingWheelControllerBlock::new)
.initialProperties(SharedProperties.CRUSHING_WHEEL_CONTROLLER_MATERIAL)
.properties(p -> p.noOcclusion().noDrops().air())
.properties(p -> p.noOcclusion()
.noDrops()
.air())
.blockstate((c, p) -> p.getVariantBuilder(c.get())
.forAllStatesExcept(state -> ConfiguredModel.builder()
.modelFile(p.models()
@ -606,6 +625,7 @@ public class AllBlocks {
.initialProperties(SharedProperties::stone)
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.simpleBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p)))
.onRegister(assignDataBehaviour(new ItemNameDataSource(), "combine_item_names"))
.item()
.transform(customItemModel("_", "block"))
.register();
@ -617,6 +637,7 @@ public class AllBlocks {
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.horizontalBlock(c.getEntry(), AssetLookup.partialBaseModel(c, p), 180))
.transform(BlockStressDefaults.setImpact(2.0))
.onRegister(assignDataBehaviour(new ItemNameDataSource(), "combine_item_names"))
.item(EjectorItem::new)
.transform(customItemModel())
.register();
@ -1330,6 +1351,7 @@ public class AllBlocks {
.blockstate((c, p) -> p.horizontalBlock(c.get(),
s -> s.getValue(StationBlock.ASSEMBLING) ? AssetLookup.partialBaseModel(c, p, "assembling")
: AssetLookup.partialBaseModel(c, p)))
.onRegister(assignDataBehaviour(new StationSummaryDataSource(), "station_summary"))
.lang("Train Station")
.item(TrackTargetingBlockItem::new)
.transform(customItemModel("_", "block"))
@ -1430,11 +1452,13 @@ public class AllBlocks {
public static final BlockEntry<BeltTunnelBlock> ANDESITE_TUNNEL =
REGISTRATE.block("andesite_tunnel", BeltTunnelBlock::new)
.transform(BuilderTransformers.beltTunnel("andesite", new ResourceLocation("block/polished_andesite")))
.onRegister(assignDataBehaviour(new AccumulatedItemCountDataSource(), "accumulate_items"))
.register();
public static final BlockEntry<BrassTunnelBlock> BRASS_TUNNEL =
REGISTRATE.block("brass_tunnel", BrassTunnelBlock::new)
.transform(BuilderTransformers.beltTunnel("brass", Create.asResource("block/brass_block")))
.onRegister(assignDataBehaviour(new ItemThoughputDataSource(), "item_throughput"))
.onRegister(connectedTextures(BrassTunnelCTBehaviour::new))
.register();
@ -1443,6 +1467,8 @@ public class AllBlocks {
.initialProperties(SharedProperties::stone)
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.horizontalBlock(c.get(), AssetLookup.forPowered(c, p)))
.onRegister(assignDataBehaviour(new ItemCountDataSource(), "count_items"))
.onRegister(assignDataBehaviour(new ItemListDataSource(), "list_items"))
.item()
.transform(customItemModel("_", "block"))
.register();
@ -1453,6 +1479,7 @@ public class AllBlocks {
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.horizontalBlock(c.get(),
AssetLookup.withIndicator(c, p, $ -> AssetLookup.standardModel(c, p), StockpileSwitchBlock.INDICATOR)))
.onRegister(assignDataBehaviour(new FillLevelDataSource(), "fill_level"))
.simpleItem()
.register();
@ -1462,6 +1489,28 @@ public class AllBlocks {
.tag(AllBlockTags.SAFE_NBT.tag)
.register();
public static final BlockEntry<DataGathererBlock> DATA_GATHERER =
REGISTRATE.block("data_gatherer", DataGathererBlock::new)
.initialProperties(SharedProperties::softMetal)
.addLayer(() -> RenderType::translucent)
.blockstate((c, p) -> p.directionalBlock(c.get(), AssetLookup.forPowered(c, p)))
.item(DataGathererBlockItem::new)
.transform(customItemModel("_", "block"))
.register();
public static final BlockEntry<FlapDisplayBlock> FLAP_DISPLAY =
REGISTRATE.block("display_board", FlapDisplayBlock::new)
.initialProperties(SharedProperties::softMetal)
.addLayer(() -> RenderType::cutoutMipped)
.transform(pickaxeOnly())
.transform(BlockStressDefaults.setImpact(0))
.blockstate((c, p) -> p.horizontalBlock(c.get(), AssetLookup.partialBaseModel(c, p)))
.onRegister(assignDataBehaviour(new FlapDisplayDataTarget()))
.lang("Display Board")
.item()
.transform(customItemModel())
.register();
public static final BlockEntry<NixieTubeBlock> ORANGE_NIXIE_TUBE =
REGISTRATE.block("nixie_tube", p -> new NixieTubeBlock(p, DyeColor.ORANGE))
.initialProperties(SharedProperties::softMetal)

View file

@ -130,6 +130,11 @@ public class AllShapes {
NIXIE_TUBE_WALL = shape(5, 9, 0, 11, 15, 12).add(5, 1, 0, 11, 7, 12)
.forHorizontal(Direction.SOUTH),
FLAP_DISPLAY = shape(0, 0, 3, 16, 16, 13).forHorizontal(Direction.SOUTH),
DATA_GATHERER = shape(1, 0, 1, 15, 6, 15).add(3, 5, 3, 13, 9, 13)
.forDirectional(),
STEAM_ENGINE = shape(1, 0, 1, 15, 3, 15).add(3, 0, 3, 13, 15, 13)
.add(1, 5, 4, 15, 13, 12)
.forHorizontalAxis(),

View file

@ -1,5 +1,7 @@
package com.simibubi.create;
import static com.simibubi.create.content.logistics.block.data.AllDataGathererBehaviours.assignDataBehaviourTE;
import com.simibubi.create.content.contraptions.base.CutoutRotatingInstance;
import com.simibubi.create.content.contraptions.base.HalfShaftInstance;
import com.simibubi.create.content.contraptions.base.HorizontalHalfShaftInstance;
@ -143,6 +145,9 @@ import com.simibubi.create.content.logistics.block.chute.ChuteRenderer;
import com.simibubi.create.content.logistics.block.chute.ChuteTileEntity;
import com.simibubi.create.content.logistics.block.chute.SmartChuteRenderer;
import com.simibubi.create.content.logistics.block.chute.SmartChuteTileEntity;
import com.simibubi.create.content.logistics.block.data.DataGathererTileEntity;
import com.simibubi.create.content.logistics.block.data.source.NixieTubeDataSource;
import com.simibubi.create.content.logistics.block.data.target.NixieTubeDataTarget;
import com.simibubi.create.content.logistics.block.depot.DepotRenderer;
import com.simibubi.create.content.logistics.block.depot.DepotTileEntity;
import com.simibubi.create.content.logistics.block.depot.EjectorInstance;
@ -171,6 +176,8 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity;
import com.simibubi.create.content.logistics.item.LecternControllerRenderer;
import com.simibubi.create.content.logistics.item.LecternControllerTileEntity;
import com.simibubi.create.content.logistics.trains.IBogeyTileEntityRenderer;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayRenderer;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalRenderer;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationRenderer;
@ -287,10 +294,10 @@ public class AllTileEntities {
.register();
public static final BlockEntityEntry<HandCrankTileEntity> HAND_CRANK = Create.registrate()
.tileEntity("hand_crank", HandCrankTileEntity::new)
.instance(() -> HandCrankInstance::new)
.validBlocks(AllBlocks.HAND_CRANK, AllBlocks.COPPER_VALVE_HANDLE)
.validBlocks(AllBlocks.DYED_VALVE_HANDLES.toArray())
.tileEntity("hand_crank", HandCrankTileEntity::new)
.instance(() -> HandCrankInstance::new)
.validBlocks(AllBlocks.HAND_CRANK, AllBlocks.COPPER_VALVE_HANDLE)
.validBlocks(AllBlocks.DYED_VALVE_HANDLES.toArray())
.renderer(() -> HandCrankRenderer::new)
.register();
@ -503,19 +510,20 @@ public class AllTileEntities {
.renderer(() -> PortableStorageInterfaceRenderer::new)
.register();
public static final BlockEntityEntry<PortableFluidInterfaceTileEntity> PORTABLE_FLUID_INTERFACE = Create.registrate()
.tileEntity("portable_fluid_interface", PortableFluidInterfaceTileEntity::new)
.instance(() -> PSIInstance::new)
.validBlocks(AllBlocks.PORTABLE_FLUID_INTERFACE)
.renderer(() -> PortableStorageInterfaceRenderer::new)
.register();
public static final BlockEntityEntry<PortableFluidInterfaceTileEntity> PORTABLE_FLUID_INTERFACE =
Create.registrate()
.tileEntity("portable_fluid_interface", PortableFluidInterfaceTileEntity::new)
.instance(() -> PSIInstance::new)
.validBlocks(AllBlocks.PORTABLE_FLUID_INTERFACE)
.renderer(() -> PortableStorageInterfaceRenderer::new)
.register();
public static final BlockEntityEntry<SteamEngineTileEntity> STEAM_ENGINE = Create.registrate()
.tileEntity("steam_engine", SteamEngineTileEntity::new)
.validBlocks(AllBlocks.STEAM_ENGINE)
.renderer(() -> SteamEngineRenderer::new)
.register();
public static final BlockEntityEntry<PoweredShaftTileEntity> POWERED_SHAFT = Create.registrate()
.tileEntity("powered_shaft", PoweredShaftTileEntity::new)
.instance(() -> SingleRotatingInstance::new, false)
@ -658,6 +666,13 @@ public class AllTileEntities {
.validBlocks(AllBlocks.ORANGE_NIXIE_TUBE)
.validBlocks(AllBlocks.NIXIE_TUBES.toArray())
.renderer(() -> NixieTubeRenderer::new)
.onRegister(assignDataBehaviourTE(new NixieTubeDataTarget()))
.onRegister(assignDataBehaviourTE(new NixieTubeDataSource()))
.register();
public static final BlockEntityEntry<DataGathererTileEntity> DATA_GATHERER = Create.registrate()
.tileEntity("data_gatherer", DataGathererTileEntity::new)
.validBlocks(AllBlocks.DATA_GATHERER)
.register();
public static final BlockEntityEntry<StockpileSwitchTileEntity> STOCKPILE_SWITCH = Create.registrate()
@ -706,20 +721,18 @@ public class AllTileEntities {
.renderer(() -> BrassDiodeRenderer::new)
.register();
public static final BlockEntityEntry<PulseRepeaterTileEntity> PULSE_REPEATER =
Create.registrate()
.tileEntity("pulse_repeater", PulseRepeaterTileEntity::new)
.instance(() -> BrassDiodeInstance::new, false)
.validBlocks(AllBlocks.PULSE_REPEATER)
.renderer(() -> BrassDiodeRenderer::new)
.register();
public static final BlockEntityEntry<PulseRepeaterTileEntity> PULSE_REPEATER = Create.registrate()
.tileEntity("pulse_repeater", PulseRepeaterTileEntity::new)
.instance(() -> BrassDiodeInstance::new, false)
.validBlocks(AllBlocks.PULSE_REPEATER)
.renderer(() -> BrassDiodeRenderer::new)
.register();
public static final BlockEntityEntry<LecternControllerTileEntity> LECTERN_CONTROLLER =
Create.registrate()
.tileEntity("lectern_controller", LecternControllerTileEntity::new)
.validBlocks(AllBlocks.LECTERN_CONTROLLER)
.renderer(() -> LecternControllerRenderer::new)
.register();
public static final BlockEntityEntry<LecternControllerTileEntity> LECTERN_CONTROLLER = Create.registrate()
.tileEntity("lectern_controller", LecternControllerTileEntity::new)
.validBlocks(AllBlocks.LECTERN_CONTROLLER)
.renderer(() -> LecternControllerRenderer::new)
.register();
// Curiosities
public static final BlockEntityEntry<CopperBacktankTileEntity> COPPER_BACKTANK = Create.registrate()
@ -766,7 +779,14 @@ public class AllTileEntities {
.renderer(() -> StationRenderer::new)
.validBlocks(AllBlocks.TRACK_STATION)
.register();
public static final BlockEntityEntry<FlapDisplayTileEntity> FLAP_DISPLAY = Create.registrate()
.tileEntity("flap_display", FlapDisplayTileEntity::new)
.instance(() -> MechanicalCrafterInstance::new)
.renderer(() -> FlapDisplayRenderer::new)
.validBlocks(AllBlocks.FLAP_DISPLAY)
.register();
public static final BlockEntityEntry<SignalTileEntity> TRACK_SIGNAL = Create.registrate()
.tileEntity("track_signal", SignalTileEntity::new)
.renderer(() -> SignalRenderer::new)

View file

@ -13,6 +13,7 @@ import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineInteractions;
import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes;
import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler;
import com.simibubi.create.content.logistics.block.data.AllDataGathererBehaviours;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.palettes.AllPaletteBlocks;
import com.simibubi.create.content.palettes.PalettesItemGroup;
@ -96,6 +97,7 @@ public class Create {
AllEntityTypes.register();
AllTileEntities.register();
AllMovementBehaviours.register();
AllDataGathererBehaviours.register();
AllInteractionBehaviours.register();
AllWorldFeatures.register();
AllEnchantments.register();

View file

@ -3,61 +3,53 @@ package com.simibubi.create.content.contraptions.components.press;
import static com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour.ProcessingResult.HOLD;
import static com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour.ProcessingResult.PASS;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity.Mode;
import com.simibubi.create.content.contraptions.processing.InWorldProcessing;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.relays.belt.BeltHelper;
import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour.ProcessingResult;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour.TransportedResult;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraft.world.item.ItemStack;
public class BeltPressingCallbacks {
static ProcessingResult onItemReceived(TransportedItemStack transported,
TransportedItemStackHandlerBehaviour handler, MechanicalPressTileEntity press) {
if (press.getSpeed() == 0)
TransportedItemStackHandlerBehaviour handler, PressingBehaviour behaviour) {
if (behaviour.specifics.getKineticSpeed() == 0)
return PASS;
if (press.running)
if (behaviour.running)
return HOLD;
if (!press.getRecipe(transported.stack)
.isPresent())
if (!behaviour.specifics.tryProcessOnBelt(transported, null, true))
return PASS;
press.start(Mode.BELT);
behaviour.start(Mode.BELT);
return HOLD;
}
static ProcessingResult whenItemHeld(TransportedItemStack transported, TransportedItemStackHandlerBehaviour handler,
MechanicalPressTileEntity pressTe) {
PressingBehaviour behaviour) {
if (pressTe.getSpeed() == 0)
if (behaviour.specifics.getKineticSpeed() == 0)
return PASS;
if (!pressTe.running)
if (!behaviour.running)
return PASS;
if (pressTe.runningTicks != MechanicalPressTileEntity.CYCLE / 2)
if (behaviour.runningTicks != PressingBehaviour.CYCLE / 2)
return HOLD;
Optional<PressingRecipe> recipe = pressTe.getRecipe(transported.stack);
pressTe.pressedItems.clear();
pressTe.pressedItems.add(transported.stack);
if (!recipe.isPresent())
behaviour.particleItems.clear();
ArrayList<ItemStack> results = new ArrayList<>();
if (!behaviour.specifics.tryProcessOnBelt(transported, results, false))
return PASS;
boolean bulk = MechanicalPressTileEntity.canProcessInBulk() || transported.stack.getCount() == 1;
boolean bulk = behaviour.specifics.canProcessInBulk() || transported.stack.getCount() == 1;
List<TransportedItemStack> collect = InWorldProcessing
.applyRecipeOn(bulk ? transported.stack : ItemHandlerHelper.copyStackWithSize(transported.stack, 1),
recipe.get())
.stream()
List<TransportedItemStack> collect = results.stream()
.map(stack -> {
TransportedItemStack copy = transported.copy();
boolean centered = BeltHelper.isItemUpright(stack);
@ -84,8 +76,7 @@ public class BeltPressingCallbacks {
handler.handleProcessingOnItem(transported, TransportedResult.convertToAndLeaveHeld(collect, left));
}
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, pressTe.getLevel(), pressTe.getBlockPos(), 4);
pressTe.sendData();
behaviour.tileEntity.sendData();
return HOLD;
}

View file

@ -28,18 +28,22 @@ public class MechanicalPressRenderer extends KineticTileEntityRenderer {
@Override
protected void renderSafe(KineticTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer,
int light, int overlay) {
int light, int overlay) {
super.renderSafe(te, partialTicks, ms, buffer, light, overlay);
if (Backend.canUseInstancing(te.getLevel())) return;
if (Backend.canUseInstancing(te.getLevel()))
return;
BlockState blockState = te.getBlockState();
float renderedHeadOffset = ((MechanicalPressTileEntity) te).getRenderedHeadOffset(partialTicks);
PressingBehaviour pressingBehaviour = ((MechanicalPressTileEntity) te).getPressingBehaviour();
float renderedHeadOffset =
pressingBehaviour.getRenderedHeadOffset(partialTicks) * pressingBehaviour.mode.headOffset;
SuperByteBuffer headRender = CachedBufferer.partialFacing(AllBlockPartials.MECHANICAL_PRESS_HEAD, blockState, blockState.getValue(HORIZONTAL_FACING));
SuperByteBuffer headRender = CachedBufferer.partialFacing(AllBlockPartials.MECHANICAL_PRESS_HEAD, blockState,
blockState.getValue(HORIZONTAL_FACING));
headRender.translate(0, -renderedHeadOffset, 0)
.light(light)
.renderInto(ms, buffer.getBuffer(RenderType.solid()));
.light(light)
.renderInto(ms, buffer.getBuffer(RenderType.solid()));
}
@Override

View file

@ -1,44 +1,34 @@
package com.simibubi.create.content.contraptions.components.press;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCraftingRecipe;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.PressingBehaviourSpecifics;
import com.simibubi.create.content.contraptions.itemAssembly.SequencedAssemblyRecipe;
import com.simibubi.create.content.contraptions.processing.BasinOperatingTileEntity;
import com.simibubi.create.content.contraptions.processing.BasinTileEntity;
import com.simibubi.create.content.contraptions.processing.InWorldProcessing;
import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.advancement.ITriggerable;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.item.SmartInventory;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.Container;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
@ -47,64 +37,14 @@ import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
public class MechanicalPressTileEntity extends BasinOperatingTileEntity implements PressingBehaviourSpecifics {
private static final Object compressingRecipesKey = new Object();
public List<ItemStack> pressedItems = new ArrayList<>();
public BeltProcessingBehaviour processingBehaviour;
public int prevRunningTicks;
public int runningTicks;
static final int CYCLE = 240;
static final int ENTITY_SCAN = 10;
int entityScanCooldown;
public boolean running;
public Mode mode;
public boolean finished;
public PressingBehaviour pressingBehaviour;
public MechanicalPressTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
mode = Mode.WORLD;
entityScanCooldown = ENTITY_SCAN;
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
processingBehaviour =
new BeltProcessingBehaviour(this).whenItemEnters((s, i) -> BeltPressingCallbacks.onItemReceived(s, i, this))
.whileItemHeld((s, i) -> BeltPressingCallbacks.whenItemHeld(s, i, this));
behaviours.add(processingBehaviour);
}
@Override
protected void read(CompoundTag compound, boolean clientPacket) {
running = compound.getBoolean("Running");
mode = Mode.values()[compound.getInt("Mode")];
finished = compound.getBoolean("Finished");
prevRunningTicks = runningTicks = compound.getInt("Ticks");
super.read(compound, clientPacket);
if (clientPacket) {
NBTHelper.iterateCompoundList(compound.getList("ParticleItems", Tag.TAG_COMPOUND),
c -> pressedItems.add(ItemStack.of(c)));
spawnParticles();
}
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
compound.putBoolean("Running", running);
compound.putInt("Mode", mode.ordinal());
compound.putBoolean("Finished", finished);
compound.putInt("Ticks", runningTicks);
super.write(compound, clientPacket);
if (clientPacket) {
compound.put("ParticleItems", NBTHelper.writeCompoundList(pressedItems, ItemStack::serializeNBT));
pressedItems.clear();
}
}
@Override
@ -113,119 +53,21 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
.expandTowards(0, 1, 0);
}
public float getRenderedHeadOffset(float partialTicks) {
if (!running)
return 0;
int runningTicks = Math.abs(this.runningTicks);
float ticks = Mth.lerp(partialTicks, prevRunningTicks, runningTicks);
if (runningTicks < (CYCLE * 2) / 3)
return (float) Mth.clamp(Math.pow(ticks / CYCLE * 2, 3), 0, 1) * mode.headOffset;
return Mth.clamp((CYCLE - ticks) / CYCLE * 3, 0, 1) * mode.headOffset;
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
pressingBehaviour = new PressingBehaviour(this);
behaviours.add(pressingBehaviour);
}
public void start(Mode mode) {
this.mode = mode;
running = true;
prevRunningTicks = 0;
runningTicks = 0;
pressedItems.clear();
sendData();
}
public boolean inWorld() {
return mode == Mode.WORLD;
}
public boolean onBasin() {
return mode == Mode.BASIN;
public PressingBehaviour getPressingBehaviour() {
return pressingBehaviour;
}
@Override
public void tick() {
super.tick();
if (!running || level == null) {
if (hasLevel() && !level.isClientSide) {
if (getSpeed() == 0)
return;
if (entityScanCooldown > 0)
entityScanCooldown--;
if (entityScanCooldown <= 0) {
entityScanCooldown = ENTITY_SCAN;
if (TileEntityBehaviour.get(level, worldPosition.below(2), TransportedItemStackHandlerBehaviour.TYPE) != null)
return;
if (AllBlocks.BASIN.has(level.getBlockState(worldPosition.below(2))))
return;
for (ItemEntity itemEntity : level.getEntitiesOfClass(ItemEntity.class,
new AABB(worldPosition.below()).deflate(.125f))) {
if (!itemEntity.isAlive() || !itemEntity.isOnGround())
continue;
ItemStack stack = itemEntity.getItem();
Optional<PressingRecipe> recipe = getRecipe(stack);
if (!recipe.isPresent())
continue;
start(Mode.WORLD);
return;
}
}
}
return;
}
if (level.isClientSide && runningTicks == -CYCLE / 2) {
prevRunningTicks = CYCLE / 2;
return;
}
if (runningTicks == CYCLE / 2 && getSpeed() != 0) {
if (inWorld())
applyPressingInWorld();
if (onBasin())
applyCompactingOnBasin();
if (level.getBlockState(worldPosition.below(2)).getSoundType() == SoundType.WOOL)
AllSoundEvents.MECHANICAL_PRESS_ACTIVATION_ON_BELT.playOnServer(level, worldPosition);
else
AllSoundEvents.MECHANICAL_PRESS_ACTIVATION.playOnServer(level, worldPosition, .5f, .75f + (Math.abs(getSpeed()) / 1024f));
if (!level.isClientSide)
sendData();
}
if (!level.isClientSide && runningTicks > CYCLE) {
finished = true;
running = false;
if (onBasin() && matchBasinRecipe(currentRecipe)
&& getBasin().filter(BasinTileEntity::canContinueProcessing)
.isPresent())
startProcessingBasin();
else
basinChecker.scheduleUpdate();
pressedItems.clear();
sendData();
return;
}
prevRunningTicks = runningTicks;
runningTicks += getRunningTickSpeed();
if (prevRunningTicks < CYCLE / 2 && runningTicks >= CYCLE / 2) {
runningTicks = CYCLE / 2;
// Pause the ticks until a packet is received
if (level.isClientSide && !isVirtual())
runningTicks = -(CYCLE / 2);
}
}
protected void applyCompactingOnBasin() {
if (level.isClientSide)
return;
pressedItems.clear();
public boolean tryProcessInBasin(boolean simulate) {
applyBasinRecipe();
Optional<BasinTileEntity> basin = getBasin();
if (basin.isPresent()) {
SmartInventory inputs = basin.get()
@ -234,99 +76,63 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
ItemStack stackInSlot = inputs.getItem(slot);
if (stackInSlot.isEmpty())
continue;
pressedItems.add(stackInSlot);
pressingBehaviour.particleItems.add(stackInSlot);
}
}
sendData();
return true;
}
protected void applyPressingInWorld() {
AABB bb = new AABB(worldPosition.below(1));
boolean bulk = canProcessInBulk();
pressedItems.clear();
if (level.isClientSide)
return;
for (Entity entity : level.getEntities(null, bb)) {
if (!(entity instanceof ItemEntity))
continue;
if (!entity.isAlive() || !entity.isOnGround())
continue;
ItemEntity itemEntity = (ItemEntity) entity;
ItemStack item = itemEntity.getItem();
pressedItems.add(item);
sendData();
Optional<PressingRecipe> recipe = getRecipe(item);
if (!recipe.isPresent())
continue;
@Override
public boolean tryProcessInWorld(ItemEntity itemEntity, boolean simulate) {
ItemStack item = itemEntity.getItem();
Optional<PressingRecipe> recipe = getRecipe(item);
if (!recipe.isPresent())
return false;
if (simulate)
return true;
if (bulk || item.getCount() == 1) {
InWorldProcessing.applyRecipeOn(itemEntity, recipe.get());
} else {
for (ItemStack result : InWorldProcessing.applyRecipeOn(ItemHandlerHelper.copyStackWithSize(item, 1),
recipe.get())) {
ItemEntity created =
new ItemEntity(level, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), result);
created.setDefaultPickUpDelay();
created.setDeltaMovement(VecHelper.offsetRandomly(Vec3.ZERO, Create.RANDOM, .05f));
level.addFreshEntity(created);
}
item.shrink(1);
pressingBehaviour.particleItems.add(item);
if (canProcessInBulk() || item.getCount() == 1) {
InWorldProcessing.applyRecipeOn(itemEntity, recipe.get());
} else {
for (ItemStack result : InWorldProcessing.applyRecipeOn(ItemHandlerHelper.copyStackWithSize(item, 1),
recipe.get())) {
ItemEntity created =
new ItemEntity(level, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), result);
created.setDefaultPickUpDelay();
created.setDeltaMovement(VecHelper.offsetRandomly(Vec3.ZERO, Create.RANDOM, .05f));
level.addFreshEntity(created);
}
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, level, worldPosition, 4);
entityScanCooldown = 0;
if (!bulk)
break;
item.shrink(1);
}
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, level, worldPosition, 4);
return true;
}
public static boolean canProcessInBulk() {
return AllConfigs.SERVER.recipes.bulkPressing.get();
@Override
public boolean tryProcessOnBelt(TransportedItemStack input, List<ItemStack> outputList, boolean simulate) {
Optional<PressingRecipe> recipe = getRecipe(input.stack);
if (!recipe.isPresent())
return false;
if (simulate)
return true;
pressingBehaviour.particleItems.add(input.stack);
outputList.addAll(InWorldProcessing.applyRecipeOn(
canProcessInBulk() ? input.stack : ItemHandlerHelper.copyStackWithSize(input.stack, 1), recipe.get()));
AllTriggers.triggerForNearbyPlayers(AllTriggers.BONK, level, worldPosition, 4);
return true;
}
public int getRunningTickSpeed() {
if (getSpeed() == 0)
return 0;
return (int) Mth.lerp(Mth.clamp(Math.abs(getSpeed()) / 512f, 0, 1), 1, 60);
}
protected void spawnParticles() {
if (pressedItems.isEmpty())
return;
if (mode == Mode.BASIN)
pressedItems.forEach(stack -> makeCompactingParticleEffect(VecHelper.getCenterOf(worldPosition.below(2)), stack));
if (mode == Mode.BELT)
pressedItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(worldPosition.below(2))
.add(0, 8 / 16f, 0), stack));
if (mode == Mode.WORLD)
pressedItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(worldPosition.below(1))
.add(0, -1 / 4f, 0), stack));
pressedItems.clear();
}
public void makePressingParticleEffect(Vec3 pos, ItemStack stack) {
if (level == null || !level.isClientSide)
return;
for (int i = 0; i < 20; i++) {
Vec3 motion = VecHelper.offsetRandomly(Vec3.ZERO, level.random, .125f)
.multiply(1, 0, 1);
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, stack), pos.x, pos.y - .25f, pos.z, motion.x,
motion.y + .125f, motion.z);
}
}
public void makeCompactingParticleEffect(Vec3 pos, ItemStack stack) {
if (level == null || !level.isClientSide)
return;
for (int i = 0; i < 20; i++) {
Vec3 motion = VecHelper.offsetRandomly(Vec3.ZERO, level.random, .175f)
.multiply(1, 0, 1);
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, stack), pos.x, pos.y, pos.z, motion.x,
motion.y + .25f, motion.z);
}
@Override
public void onPressingCompleted() {
if (pressingBehaviour.onBasin() && matchBasinRecipe(currentRecipe)
&& getBasin().filter(BasinTileEntity::canContinueProcessing)
.isPresent())
startProcessingBasin();
else
basinChecker.scheduleUpdate();
}
private static final RecipeWrapper pressingInv = new RecipeWrapper(new ItemStackHandler(1));
@ -350,35 +156,50 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
@Override
protected <C extends Container> boolean matchStaticFilters(Recipe<C> recipe) {
return (recipe instanceof CraftingRecipe && !(recipe instanceof MechanicalCraftingRecipe)
&& canCompress(recipe) && !AllRecipeTypes.shouldIgnoreInAutomation(recipe))
return (recipe instanceof CraftingRecipe && !(recipe instanceof MechanicalCraftingRecipe) && canCompress(recipe)
&& !AllRecipeTypes.shouldIgnoreInAutomation(recipe))
|| recipe.getType() == AllRecipeTypes.COMPACTING.getType();
}
@Override
public float getKineticSpeed() {
return getSpeed();
}
@Override
public boolean canProcessInBulk() {
return AllConfigs.SERVER.recipes.bulkPressing.get();
}
@Override
protected Object getRecipeCacheKey() {
return compressingRecipesKey;
}
@Override
public int getParticleAmount() {
return 15;
}
@Override
public void startProcessingBasin() {
if (running && runningTicks <= CYCLE / 2)
if (pressingBehaviour.running && pressingBehaviour.runningTicks <= PressingBehaviour.CYCLE / 2)
return;
super.startProcessingBasin();
start(Mode.BASIN);
pressingBehaviour.start(Mode.BASIN);
}
@Override
protected void onBasinRemoved() {
pressedItems.clear();
running = false;
runningTicks = 0;
pressingBehaviour.particleItems.clear();
pressingBehaviour.running = false;
pressingBehaviour.runningTicks = 0;
sendData();
}
@Override
protected boolean isRunning() {
return running;
return pressingBehaviour.running;
}
@Override
@ -386,16 +207,4 @@ public class MechanicalPressTileEntity extends BasinOperatingTileEntity {
return Optional.of(AllTriggers.PRESS_COMPACT);
}
public enum Mode {
WORLD(1), BELT(19f / 16f), BASIN(22f / 16f)
;
float headOffset;
Mode(float headOffset) {
this.headOffset = headOffset;
}
}
}

View file

@ -13,51 +13,54 @@ import com.simibubi.create.foundation.utility.AnimationTickHolder;
public class PressInstance extends ShaftInstance implements DynamicInstance {
private final OrientedData pressHead;
private final MechanicalPressTileEntity press;
private final OrientedData pressHead;
private final MechanicalPressTileEntity press;
public PressInstance(MaterialManager dispatcher, MechanicalPressTileEntity tile) {
super(dispatcher, tile);
press = tile;
public PressInstance(MaterialManager dispatcher, MechanicalPressTileEntity tile) {
super(dispatcher, tile);
press = tile;
pressHead = dispatcher.defaultSolid()
.material(Materials.ORIENTED)
.getModel(AllBlockPartials.MECHANICAL_PRESS_HEAD, blockState)
.createInstance();
pressHead = dispatcher.defaultSolid()
.material(Materials.ORIENTED)
.getModel(AllBlockPartials.MECHANICAL_PRESS_HEAD, blockState)
.createInstance();
Quaternion q = Vector3f.YP.rotationDegrees(AngleHelper.horizontalAngle(blockState.getValue(MechanicalPressBlock.HORIZONTAL_FACING)));
Quaternion q = Vector3f.YP
.rotationDegrees(AngleHelper.horizontalAngle(blockState.getValue(MechanicalPressBlock.HORIZONTAL_FACING)));
pressHead.setRotation(q);
pressHead.setRotation(q);
transformModels();
}
transformModels();
}
@Override
public void beginFrame() {
transformModels();
}
@Override
public void beginFrame() {
transformModels();
}
private void transformModels() {
float renderedHeadOffset = getRenderedHeadOffset(press);
private void transformModels() {
float renderedHeadOffset = getRenderedHeadOffset(press);
pressHead.setPosition(getInstancePosition())
.nudge(0, -renderedHeadOffset, 0);
}
pressHead.setPosition(getInstancePosition())
.nudge(0, -renderedHeadOffset, 0);
}
private float getRenderedHeadOffset(MechanicalPressTileEntity press) {
return press.getRenderedHeadOffset(AnimationTickHolder.getPartialTicks());
}
private float getRenderedHeadOffset(MechanicalPressTileEntity press) {
PressingBehaviour pressingBehaviour = press.getPressingBehaviour();
return pressingBehaviour.getRenderedHeadOffset(AnimationTickHolder.getPartialTicks())
* pressingBehaviour.mode.headOffset;
}
@Override
public void updateLight() {
super.updateLight();
@Override
public void updateLight() {
super.updateLight();
relight(pos, pressHead);
}
relight(pos, pressHead);
}
@Override
public void remove() {
super.remove();
pressHead.delete();
}
@Override
public void remove() {
super.remove();
pressHead.delete();
}
}

View file

@ -0,0 +1,305 @@
package com.simibubi.create.content.contraptions.components.press;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class PressingBehaviour extends BeltProcessingBehaviour {
public static final int CYCLE = 240;
public static final int ENTITY_SCAN = 10;
public List<ItemStack> particleItems = new ArrayList<>();
public PressingBehaviourSpecifics specifics;
public int prevRunningTicks;
public int runningTicks;
public boolean running;
public boolean finished;
public Mode mode;
int entityScanCooldown;
public interface PressingBehaviourSpecifics {
public boolean tryProcessInBasin(boolean simulate);
public boolean tryProcessOnBelt(TransportedItemStack input, List<ItemStack> outputList, boolean simulate);
public boolean tryProcessInWorld(ItemEntity itemEntity, boolean simulate);
public boolean canProcessInBulk();
public void onPressingCompleted();
public int getParticleAmount();
public float getKineticSpeed();
}
public <T extends SmartTileEntity & PressingBehaviourSpecifics> PressingBehaviour(T te) {
super(te);
this.specifics = te;
mode = Mode.WORLD;
entityScanCooldown = ENTITY_SCAN;
whenItemEnters((s, i) -> BeltPressingCallbacks.onItemReceived(s, i, this));
whileItemHeld((s, i) -> BeltPressingCallbacks.whenItemHeld(s, i, this));
}
@Override
public void read(CompoundTag compound, boolean clientPacket) {
running = compound.getBoolean("Running");
mode = Mode.values()[compound.getInt("Mode")];
finished = compound.getBoolean("Finished");
prevRunningTicks = runningTicks = compound.getInt("Ticks");
super.read(compound, clientPacket);
if (clientPacket) {
NBTHelper.iterateCompoundList(compound.getList("ParticleItems", Tag.TAG_COMPOUND),
c -> particleItems.add(ItemStack.of(c)));
spawnParticles();
}
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
compound.putBoolean("Running", running);
compound.putInt("Mode", mode.ordinal());
compound.putBoolean("Finished", finished);
compound.putInt("Ticks", runningTicks);
super.write(compound, clientPacket);
if (clientPacket) {
compound.put("ParticleItems", NBTHelper.writeCompoundList(particleItems, ItemStack::serializeNBT));
particleItems.clear();
}
}
public float getRenderedHeadOffset(float partialTicks) {
if (!running)
return 0;
int runningTicks = Math.abs(this.runningTicks);
float ticks = Mth.lerp(partialTicks, prevRunningTicks, runningTicks);
if (runningTicks < (CYCLE * 2) / 3)
return (float) Mth.clamp(Math.pow(ticks / CYCLE * 2, 3), 0, 1);
return Mth.clamp((CYCLE - ticks) / CYCLE * 3, 0, 1);
}
public void start(Mode mode) {
this.mode = mode;
running = true;
prevRunningTicks = 0;
runningTicks = 0;
particleItems.clear();
tileEntity.sendData();
}
public boolean inWorld() {
return mode == Mode.WORLD;
}
public boolean onBasin() {
return mode == Mode.BASIN;
}
@Override
public void tick() {
super.tick();
Level level = getWorld();
BlockPos worldPosition = getPos();
if (!running || level == null) {
if (level != null && !level.isClientSide) {
if (specifics.getKineticSpeed() == 0)
return;
if (entityScanCooldown > 0)
entityScanCooldown--;
if (entityScanCooldown <= 0) {
entityScanCooldown = ENTITY_SCAN;
if (TileEntityBehaviour.get(level, worldPosition.below(2),
TransportedItemStackHandlerBehaviour.TYPE) != null)
return;
if (AllBlocks.BASIN.has(level.getBlockState(worldPosition.below(2))))
return;
for (ItemEntity itemEntity : level.getEntitiesOfClass(ItemEntity.class,
new AABB(worldPosition.below()).deflate(.125f))) {
if (!itemEntity.isAlive() || !itemEntity.isOnGround())
continue;
if (!specifics.tryProcessInWorld(itemEntity, true))
continue;
start(Mode.WORLD);
return;
}
}
}
return;
}
if (level.isClientSide && runningTicks == -CYCLE / 2) {
prevRunningTicks = CYCLE / 2;
return;
}
if (runningTicks == CYCLE / 2 && specifics.getKineticSpeed() != 0) {
if (inWorld())
applyInWorld();
if (onBasin())
applyOnBasin();
if (level.getBlockState(worldPosition.below(2))
.getSoundType() == SoundType.WOOL)
AllSoundEvents.MECHANICAL_PRESS_ACTIVATION_ON_BELT.playOnServer(level, worldPosition);
else
AllSoundEvents.MECHANICAL_PRESS_ACTIVATION.playOnServer(level, worldPosition, .5f,
.75f + (Math.abs(specifics.getKineticSpeed()) / 1024f));
if (!level.isClientSide)
tileEntity.sendData();
}
if (!level.isClientSide && runningTicks > CYCLE) {
finished = true;
running = false;
particleItems.clear();
specifics.onPressingCompleted();
tileEntity.sendData();
return;
}
prevRunningTicks = runningTicks;
runningTicks += getRunningTickSpeed();
if (prevRunningTicks < CYCLE / 2 && runningTicks >= CYCLE / 2) {
runningTicks = CYCLE / 2;
// Pause the ticks until a packet is received
if (level.isClientSide && !tileEntity.isVirtual())
runningTicks = -(CYCLE / 2);
}
}
protected void applyOnBasin() {
Level level = getWorld();
if (level.isClientSide)
return;
particleItems.clear();
if (specifics.tryProcessInBasin(false))
tileEntity.sendData();
}
protected void applyInWorld() {
Level level = getWorld();
BlockPos worldPosition = getPos();
AABB bb = new AABB(worldPosition.below(1));
boolean bulk = specifics.canProcessInBulk();
particleItems.clear();
if (level.isClientSide)
return;
for (Entity entity : level.getEntities(null, bb)) {
if (!(entity instanceof ItemEntity itemEntity))
continue;
if (!entity.isAlive() || !entity.isOnGround())
continue;
entityScanCooldown = 0;
if (specifics.tryProcessInWorld(itemEntity, false))
tileEntity.sendData();
if (!bulk)
break;
}
}
public int getRunningTickSpeed() {
float speed = specifics.getKineticSpeed();
if (speed == 0)
return 0;
return (int) Mth.lerp(Mth.clamp(Math.abs(speed) / 512f, 0, 1), 1, 60);
}
protected void spawnParticles() {
if (particleItems.isEmpty())
return;
BlockPos worldPosition = getPos();
if (mode == Mode.BASIN)
particleItems
.forEach(stack -> makeCompactingParticleEffect(VecHelper.getCenterOf(worldPosition.below(2)), stack));
if (mode == Mode.BELT)
particleItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(worldPosition.below(2))
.add(0, 8 / 16f, 0), stack));
if (mode == Mode.WORLD)
particleItems.forEach(stack -> makePressingParticleEffect(VecHelper.getCenterOf(worldPosition.below(1))
.add(0, -1 / 4f, 0), stack));
particleItems.clear();
}
public void makePressingParticleEffect(Vec3 pos, ItemStack stack) {
makePressingParticleEffect(pos, stack, specifics.getParticleAmount());
}
public void makePressingParticleEffect(Vec3 pos, ItemStack stack, int amount) {
Level level = getWorld();
if (level == null || !level.isClientSide)
return;
for (int i = 0; i < amount; i++) {
Vec3 motion = VecHelper.offsetRandomly(Vec3.ZERO, level.random, .125f)
.multiply(1, 0, 1);
motion = motion.add(0, amount != 1 ? 0.125f : 1 / 16f, 0);
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, stack), pos.x, pos.y - .25f, pos.z, motion.x,
motion.y, motion.z);
}
}
public void makeCompactingParticleEffect(Vec3 pos, ItemStack stack) {
Level level = getWorld();
if (level == null || !level.isClientSide)
return;
for (int i = 0; i < 20; i++) {
Vec3 motion = VecHelper.offsetRandomly(Vec3.ZERO, level.random, .175f)
.multiply(1, 0, 1);
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, stack), pos.x, pos.y, pos.z, motion.x,
motion.y + .25f, motion.z);
}
}
public enum Mode {
WORLD(1), BELT(19f / 16f), BASIN(22f / 16f);
public float headOffset;
Mode(float headOffset) {
this.headOffset = headOffset;
}
}
}

View file

@ -9,6 +9,8 @@ import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelTileEntity;
import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelTileEntity;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.content.logistics.block.data.source.AccumulatedItemCountDataSource;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
@ -44,6 +46,7 @@ public class BeltTunnelInteractionHandler {
boolean onServer = !world.isClientSide || beltInventory.belt.isVirtual();
boolean removed = false;
BeltTunnelTileEntity nextTunnel = getTunnelOnSegment(beltInventory, upcomingSegment);
int transferred = current.stack.getCount();
if (nextTunnel instanceof BrassTunnelTileEntity) {
BrassTunnelTileEntity brassTunnel = (BrassTunnelTileEntity) nextTunnel;
@ -82,7 +85,8 @@ public class BeltTunnelInteractionHandler {
continue;
ItemStack toinsert = ItemHandlerHelper.copyStackWithSize(current.stack, 1);
if (!behaviour.handleInsertion(toinsert, d, false).isEmpty())
if (!behaviour.handleInsertion(toinsert, d, false)
.isEmpty())
return true;
if (onServer)
flapTunnel(beltInventory, upcomingSegment, d, false);
@ -98,6 +102,10 @@ public class BeltTunnelInteractionHandler {
if (onServer) {
flapTunnel(beltInventory, currentSegment, movementFacing, false);
flapTunnel(beltInventory, upcomingSegment, movementFacing.getOpposite(), true);
if (nextTunnel != null)
DataGathererBlock.sendToGatherers(world, nextTunnel.getBlockPos(),
(dgte, b) -> b.itemReceived(dgte, transferred), AccumulatedItemCountDataSource.class);
}
if (removed)
@ -140,7 +148,8 @@ public class BeltTunnelInteractionHandler {
public static BeltTunnelTileEntity getTunnelOnPosition(Level world, BlockPos pos) {
pos = pos.above();
if (!(world.getBlockState(pos).getBlock() instanceof BeltTunnelBlock))
if (!(world.getBlockState(pos)
.getBlock() instanceof BeltTunnelBlock))
return null;
BlockEntity te = world.getBlockEntity(pos);
if (te == null || !(te instanceof BeltTunnelTileEntity))

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileE
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.block.chute.AbstractChuteBlock;
import com.simibubi.create.content.logistics.block.redstone.NixieTubeBlock;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackShape;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
@ -203,6 +204,8 @@ public class GirderBlock extends Block implements SimpleWaterloggedBlock, IWrenc
state = state.setValue(updateProperty, true);
else if (sideState.getBlock() instanceof NixieTubeBlock && NixieTubeBlock.getFacing(sideState) == d)
state = state.setValue(updateProperty, true);
else if (sideState.getBlock() instanceof FlapDisplayBlock)
state = state.setValue(updateProperty, true);
else if (isFacingBracket(level, pos, d))
state = state.setValue(updateProperty, true);
return state;

View file

@ -0,0 +1,185 @@
package com.simibubi.create.content.logistics.block.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.content.logistics.block.data.source.DeathCounterDataSource;
import com.simibubi.create.content.logistics.block.data.source.ScoreboardDataSource;
import com.simibubi.create.content.logistics.block.data.target.DataGathererTarget;
import com.simibubi.create.content.logistics.block.data.target.LecternDataTarget;
import com.simibubi.create.content.logistics.block.data.target.SignDataTarget;
import com.tterrag.registrate.util.nullness.NonNullConsumer;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
public class AllDataGathererBehaviours {
public static final HashMap<ResourceLocation, DataGathererBehaviour>
GATHERER_BEHAVIOURS = new HashMap<>();
public static final HashMap<ResourceLocation, List<DataGathererSource>>
SOURCES_BY_BLOCK = new HashMap<>(), SOURCES_BY_TILE = new HashMap<>();
public static final HashMap<ResourceLocation, DataGathererTarget>
TARGETS_BY_BLOCK = new HashMap<>(), TARGETS_BY_TILE = new HashMap<>();
public static DataGathererBehaviour register(ResourceLocation id, DataGathererBehaviour behaviour) {
if (GATHERER_BEHAVIOURS.containsKey(id))
Create.LOGGER.warn("Data Gatherer Behaviour for " + id.toString() + " was overridden");
behaviour.id = id;
GATHERER_BEHAVIOURS.put(id, behaviour);
return behaviour;
}
public static void assign(DataGathererBehaviour behaviour, Block block) {
assignBlock(behaviour, block.getRegistryName());
}
public static void assign(DataGathererBehaviour behaviour, BlockEntityType<?> teType) {
assignTileEntity(behaviour, teType.getRegistryName());
}
public static void assignBlock(DataGathererBehaviour behaviour, ResourceLocation blockId) {
if (behaviour instanceof DataGathererSource source)
SOURCES_BY_BLOCK.computeIfAbsent(blockId, r -> new ArrayList<>())
.add(source);
if (behaviour instanceof DataGathererTarget target)
TARGETS_BY_BLOCK.put(blockId, target);
}
public static void assignTileEntity(DataGathererBehaviour behaviour, ResourceLocation tileId) {
if (behaviour instanceof DataGathererSource source)
SOURCES_BY_TILE.computeIfAbsent(tileId, r -> new ArrayList<>())
.add(source);
if (behaviour instanceof DataGathererTarget target)
TARGETS_BY_TILE.put(tileId, target);
}
public static <B extends Block> NonNullConsumer<? super B> assignDataBehaviour(DataGathererBehaviour behaviour,
String... suffix) {
return b -> {
ResourceLocation registryName = b.getRegistryName();
String idSuffix = behaviour instanceof DataGathererSource ? "_source" : "_target";
if (suffix.length > 0)
idSuffix += "_" + suffix[0];
assignBlock(register(new ResourceLocation(registryName.getNamespace(), registryName.getPath() + idSuffix),
behaviour), registryName);
};
}
public static <B extends BlockEntityType<?>> NonNullConsumer<? super B> assignDataBehaviourTE(
DataGathererBehaviour behaviour, String... suffix) {
return b -> {
ResourceLocation registryName = b.getRegistryName();
String idSuffix = behaviour instanceof DataGathererSource ? "_source" : "_target";
if (suffix.length > 0)
idSuffix += "_" + suffix[0];
assignTileEntity(
register(new ResourceLocation(registryName.getNamespace(), registryName.getPath() + idSuffix),
behaviour),
registryName);
};
}
//
@Nullable
public static DataGathererSource getSource(ResourceLocation resourceLocation) {
DataGathererBehaviour available = GATHERER_BEHAVIOURS.getOrDefault(resourceLocation, null);
if (available instanceof DataGathererSource source)
return source;
return null;
}
@Nullable
public static DataGathererTarget getTarget(ResourceLocation resourceLocation) {
DataGathererBehaviour available = GATHERER_BEHAVIOURS.getOrDefault(resourceLocation, null);
if (available instanceof DataGathererTarget target)
return target;
return null;
}
//
@Nullable
public static List<DataGathererSource> sourcesOf(LevelAccessor level, BlockPos pos) {
BlockState blockState = level.getBlockState(pos);
BlockEntity blockEntity = level.getBlockEntity(pos);
List<DataGathererSource> sourcesOfBlock = sourcesOf(blockState);
List<DataGathererSource> sourcesOfTE = blockEntity == null ? Collections.emptyList() : sourcesOf(blockEntity);
if (sourcesOfTE.isEmpty())
return sourcesOfBlock;
return sourcesOfTE;
}
@Nullable
public static DataGathererTarget targetOf(LevelAccessor level, BlockPos pos) {
BlockState blockState = level.getBlockState(pos);
BlockEntity blockEntity = level.getBlockEntity(pos);
DataGathererTarget targetOfBlock = targetOf(blockState);
DataGathererTarget targetOfTE = blockEntity == null ? null : targetOf(blockEntity);
if (targetOfTE == null)
return targetOfBlock;
return targetOfTE;
}
public static List<DataGathererSource> sourcesOf(BlockState state) {
return sourcesOf(state.getBlock());
}
public static List<DataGathererSource> sourcesOf(BlockEntity tileEntity) {
return SOURCES_BY_TILE.getOrDefault(tileEntity.getType()
.getRegistryName(), Collections.emptyList());
}
public static List<DataGathererSource> sourcesOf(Block block) {
return SOURCES_BY_BLOCK.getOrDefault(block.getRegistryName(), Collections.emptyList());
}
@Nullable
public static DataGathererTarget targetOf(BlockState state) {
return targetOf(state.getBlock());
}
@Nullable
public static DataGathererTarget targetOf(BlockEntity tileEntity) {
return TARGETS_BY_TILE.get(tileEntity.getType()
.getRegistryName());
}
@Nullable
public static DataGathererTarget targetOf(Block block) {
return TARGETS_BY_BLOCK.get(block.getRegistryName());
}
//
public static void register() {
assign(register(Create.asResource("sign_data_target"), new SignDataTarget()), BlockEntityType.SIGN);
assign(register(Create.asResource("lectern_data_target"), new LecternDataTarget()), BlockEntityType.LECTERN);
assign(register(Create.asResource("death_count_data_source"), new DeathCounterDataSource()),
Blocks.RESPAWN_ANCHOR);
assign(register(Create.asResource("scoreboard_data_source"), new ScoreboardDataSource()),
BlockEntityType.COMMAND_BLOCK);
}
}

View file

@ -0,0 +1,9 @@
package com.simibubi.create.content.logistics.block.data;
import net.minecraft.resources.ResourceLocation;
public abstract class DataGathererBehaviour {
public ResourceLocation id;
}

View file

@ -0,0 +1,165 @@
package com.simibubi.create.content.logistics.block.data;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.block.WrenchableDirectionalBlock;
import com.simibubi.create.foundation.gui.ScreenOpener;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class DataGathererBlock extends WrenchableDirectionalBlock implements ITE<DataGathererTileEntity> {
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
public DataGathererBlock(Properties p_i48415_1_) {
super(p_i48415_1_);
registerDefaultState(defaultBlockState().setValue(POWERED, false));
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState placed = super.getStateForPlacement(context);
placed = placed.setValue(FACING, context.getClickedFace());
return placed.setValue(POWERED, shouldBePowered(placed, context.getLevel(), context.getClickedPos()));
}
public static void notifyGatherers(LevelAccessor level, BlockPos pos) {
forEachAttachedGatherer(level, pos, DataGathererTileEntity::updateGatheredData);
}
@SuppressWarnings("unchecked")
public static <T extends DataGathererSource> void sendToGatherers(LevelAccessor level, BlockPos pos,
BiConsumer<DataGathererTileEntity, T> callback, Class<T> type) {
forEachAttachedGatherer(level, pos, dgte -> {
if (type.isInstance(dgte.activeSource))
callback.accept(dgte, (T) dgte.activeSource);
});
}
private static void forEachAttachedGatherer(LevelAccessor level, BlockPos pos,
Consumer<DataGathererTileEntity> callback) {
for (Direction d : Iterate.directions) {
BlockPos offsetPos = pos.relative(d);
BlockState blockState = level.getBlockState(offsetPos);
if (!AllBlocks.DATA_GATHERER.has(blockState))
continue;
BlockEntity blockEntity = level.getBlockEntity(offsetPos);
if (!(blockEntity instanceof DataGathererTileEntity dgte))
continue;
if (dgte.activeSource == null)
continue;
if (dgte.getDirection() != d.getOpposite())
continue;
callback.accept(dgte);
}
}
@Override
public void neighborChanged(BlockState state, Level worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) {
if (worldIn.isClientSide)
return;
boolean powered = shouldBePowered(state, worldIn, pos);
boolean previouslyPowered = state.getValue(POWERED);
if (previouslyPowered != powered) {
worldIn.setBlock(pos, state.cycle(POWERED), 2);
if (!powered)
withTileEntityDo(worldIn, pos, DataGathererTileEntity::onNoLongerPowered);
}
}
private boolean shouldBePowered(BlockState state, Level worldIn, BlockPos pos) {
boolean powered = false;
for (Direction d : Iterate.directions) {
if (d.getOpposite() == state.getValue(FACING))
continue;
if (worldIn.getSignal(pos.relative(d), d) == 0)
continue;
powered = true;
break;
}
return powered;
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder.add(POWERED));
}
@Override
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand,
BlockHitResult pHit) {
if (pPlayer == null)
return InteractionResult.PASS;
if (pPlayer.isSteppingCarefully())
return InteractionResult.PASS;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> withTileEntityDo(pLevel, pPos, te -> this.displayScreen(te, pPlayer)));
return InteractionResult.SUCCESS;
}
@OnlyIn(value = Dist.CLIENT)
protected void displayScreen(DataGathererTileEntity te, Player player) {
if (!(player instanceof LocalPlayer))
return;
if (te.targetOffset.equals(BlockPos.ZERO)) {
player.displayClientMessage(Lang.translate("data_gatherer.invalid"), true);
return;
}
ScreenOpener.open(new DataGathererScreen(te));
}
@Override
public boolean isPathfindable(BlockState pState, BlockGetter pLevel, BlockPos pPos, PathComputationType pType) {
return false;
}
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return AllShapes.DATA_GATHERER.get(pState.getValue(FACING));
}
@Override
public Class<DataGathererTileEntity> getTileEntityClass() {
return DataGathererTileEntity.class;
}
@Override
public BlockEntityType<? extends DataGathererTileEntity> getTileEntityType() {
return AllTileEntities.DATA_GATHERER.get();
}
}

View file

@ -0,0 +1,150 @@
package com.simibubi.create.content.logistics.block.data;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.block.data.target.DataGathererTarget;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.Event.Result;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class DataGathererBlockItem extends BlockItem {
public DataGathererBlockItem(Block pBlock, Properties pProperties) {
super(pBlock, pProperties);
}
@SubscribeEvent
public static void gathererItemAlwaysPlacesWhenUsed(PlayerInteractEvent.RightClickBlock event) {
ItemStack usedItem = event.getItemStack();
if (usedItem.getItem() instanceof DataGathererBlockItem) {
if (AllBlocks.DATA_GATHERER.has(event.getWorld()
.getBlockState(event.getPos())))
return;
event.setUseBlock(Result.DENY);
}
}
@Override
public InteractionResult useOn(UseOnContext pContext) {
ItemStack stack = pContext.getItemInHand();
BlockPos pos = pContext.getClickedPos();
Level level = pContext.getLevel();
BlockState state = level.getBlockState(pos);
Player player = pContext.getPlayer();
if (player == null)
return InteractionResult.FAIL;
if (player.isSteppingCarefully() && stack.hasTag()) {
if (level.isClientSide)
return InteractionResult.SUCCESS;
player.displayClientMessage(Lang.translate("data_gatherer.clear"), true);
stack.setTag(null);
return InteractionResult.SUCCESS;
}
if (!stack.hasTag()) {
if (level.isClientSide)
return InteractionResult.SUCCESS;
CompoundTag stackTag = stack.getOrCreateTag();
stackTag.put("SelectedPos", NbtUtils.writeBlockPos(pos));
player.displayClientMessage(Lang.translate("data_gatherer.set"), true);
stack.setTag(stackTag);
return InteractionResult.SUCCESS;
}
CompoundTag tag = stack.getTag();
CompoundTag teTag = new CompoundTag();
BlockPos selectedPos = NbtUtils.readBlockPos(tag.getCompound("SelectedPos"));
BlockPos placedPos = pos.relative(pContext.getClickedFace(), state.getMaterial()
.isReplaceable() ? 0 : 1);
if (!selectedPos.closerThan(placedPos, AllConfigs.SERVER.logistics.dataGathererRange.get())) {
player.displayClientMessage(Lang.translate("data_gatherer.too_far")
.withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
teTag.put("TargetOffset", NbtUtils.writeBlockPos(selectedPos.subtract(placedPos)));
tag.put("BlockEntityTag", teTag);
InteractionResult useOn = super.useOn(pContext);
if (level.isClientSide || useOn == InteractionResult.FAIL)
return useOn;
ItemStack itemInHand = player.getItemInHand(pContext.getHand());
if (!itemInHand.isEmpty())
itemInHand.setTag(null);
player.displayClientMessage(Lang.translate("data_gatherer.success")
.withStyle(ChatFormatting.GREEN), true);
return useOn;
}
private static BlockPos lastShownPos = null;
private static AABB lastShownAABB = null;
@OnlyIn(Dist.CLIENT)
public static void clientTick() {
Player player = Minecraft.getInstance().player;
if (player == null)
return;
ItemStack heldItemMainhand = player.getMainHandItem();
if (!(heldItemMainhand.getItem() instanceof DataGathererBlockItem))
return;
if (!heldItemMainhand.hasTag())
return;
CompoundTag stackTag = heldItemMainhand.getOrCreateTag();
if (!stackTag.contains("SelectedPos"))
return;
BlockPos selectedPos = NbtUtils.readBlockPos(stackTag.getCompound("SelectedPos"));
if (!selectedPos.equals(lastShownPos)) {
lastShownAABB = getBounds(selectedPos);
lastShownPos = selectedPos;
}
CreateClient.OUTLINER.showAABB("target", lastShownAABB)
.colored(0xffcb74)
.lineWidth(1 / 16f);
}
@OnlyIn(Dist.CLIENT)
private static AABB getBounds(BlockPos pos) {
Level world = Minecraft.getInstance().level;
DataGathererTarget target = AllDataGathererBehaviours.targetOf(world, pos);
if (target != null)
return target.getMultiblockBounds(world, pos);
BlockState state = world.getBlockState(pos);
VoxelShape shape = state.getShape(world, pos);
return shape.isEmpty() ? new AABB(BlockPos.ZERO)
: shape.bounds()
.move(pos);
}
}

View file

@ -0,0 +1,66 @@
package com.simibubi.create.content.logistics.block.data;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.foundation.networking.TileEntityConfigurationPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
public class DataGathererConfigurationPacket extends TileEntityConfigurationPacket<DataGathererTileEntity> {
private CompoundTag configData;
private int targetLine;
public DataGathererConfigurationPacket(BlockPos pos, CompoundTag configData, int targetLine) {
super(pos);
this.configData = configData;
this.targetLine = targetLine;
}
public DataGathererConfigurationPacket(FriendlyByteBuf buffer) {
super(buffer);
}
@Override
protected void writeSettings(FriendlyByteBuf buffer) {
buffer.writeNbt(configData);
buffer.writeInt(targetLine);
}
@Override
protected void readSettings(FriendlyByteBuf buffer) {
configData = buffer.readNbt();
targetLine = buffer.readInt();
}
@Override
protected void applySettings(DataGathererTileEntity te) {
te.targetLine = targetLine;
if (!configData.contains("Id")) {
te.notifyUpdate();
return;
}
ResourceLocation id = new ResourceLocation(configData.getString("Id"));
DataGathererSource source = AllDataGathererBehaviours.getSource(id);
if (source == null) {
te.notifyUpdate();
return;
}
if (te.activeSource == null || te.activeSource != source) {
te.activeSource = source;
te.setSourceConfig(configData.copy());
} else {
te.getSourceConfig()
.merge(configData);
}
te.updateGatheredData();
te.notifyUpdate();
}
}

View file

@ -0,0 +1,48 @@
package com.simibubi.create.content.logistics.block.data;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
public class DataGathererContext {
private LevelAccessor level;
private DataGathererTileEntity te;
public Object flapDisplayContext;
public DataGathererContext(LevelAccessor level, DataGathererTileEntity te) {
this.level = level;
this.te = te;
}
public LevelAccessor level() {
return level;
}
public DataGathererTileEntity te() {
return te;
}
public BlockEntity getSourceTE() {
return level.getBlockEntity(getSourcePos());
}
public BlockPos getSourcePos() {
return te.getSourcePosition();
}
public BlockEntity getTargetTE() {
return level.getBlockEntity(getTargetPos());
}
public BlockPos getTargetPos() {
return te.getTargetPosition();
}
public CompoundTag sourceConfig() {
return te.getSourceConfig();
}
}

View file

@ -0,0 +1,382 @@
package com.simibubi.create.content.logistics.block.data;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.content.logistics.block.data.source.SingleLineDataSource;
import com.simibubi.create.content.logistics.block.data.target.DataGathererTarget;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.UIRenderHelper;
import com.simibubi.create.foundation.gui.element.GuiGameElement;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.gui.widget.Label;
import com.simibubi.create.foundation.gui.widget.ScrollInput;
import com.simibubi.create.foundation.gui.widget.SelectionScrollInput;
import com.simibubi.create.foundation.gui.widget.TooltipArea;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
public class DataGathererScreen extends AbstractSimiScreen {
private static final ItemStack FALLBACK = new ItemStack(Items.BARRIER);
private AllGuiTextures background;
private DataGathererTileEntity te;
private IconButton confirmButton;
BlockState sourceState;
BlockState targetState;
ItemStack sourceIcon = FALLBACK;
ItemStack targetIcon = FALLBACK;
List<DataGathererSource> sources;
DataGathererTarget target;
ScrollInput sourceTypeSelector;
Label sourceTypeLabel;
ScrollInput targetLineSelector;
Label targetLineLabel;
Couple<Set<Pair<AbstractWidget, String>>> configWidgets;
public DataGathererScreen(DataGathererTileEntity te) {
this.background = AllGuiTextures.DATA_GATHERER;
this.te = te;
sources = Collections.emptyList();
configWidgets = Couple.create(HashSet::new);
target = null;
}
@Override
protected void init() {
setWindowSize(background.width, background.height);
super.init();
clearWidgets();
int x = guiLeft;
int y = guiTop;
if (sourceState == null || targetState == null)
initGathererOptions();
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(this::onClose);
addRenderableWidget(confirmButton);
}
@Override
public void tick() {
super.tick();
if (sourceState != null && sourceState.getBlock() != minecraft.level.getBlockState(te.getSourcePosition())
.getBlock()
|| targetState != null && targetState.getBlock() != minecraft.level.getBlockState(te.getTargetPosition())
.getBlock())
initGathererOptions();
}
private void initGathererOptions() {
sourceState = minecraft.level.getBlockState(te.getSourcePosition());
targetState = minecraft.level.getBlockState(te.getTargetPosition());
Item asItem;
int x = guiLeft;
int y = guiTop;
Block sourceBlock = sourceState.getBlock();
Block targetBlock = targetState.getBlock();
asItem = sourceBlock.asItem();
sourceIcon = asItem == null || asItem == Items.AIR ? FALLBACK : new ItemStack(asItem);
asItem = targetBlock.asItem();
targetIcon = asItem == null || asItem == Items.AIR ? FALLBACK : new ItemStack(asItem);
sources = AllDataGathererBehaviours.sourcesOf(minecraft.level, te.getSourcePosition());
target = AllDataGathererBehaviours.targetOf(minecraft.level, te.getTargetPosition());
removeWidget(targetLineSelector);
removeWidget(targetLineLabel);
removeWidget(sourceTypeSelector);
removeWidget(sourceTypeLabel);
configWidgets.forEach(s -> s.forEach(p -> removeWidget(p.getFirst())));
targetLineSelector = null;
sourceTypeSelector = null;
if (target != null) {
DataTargetStats stats = target.provideStats(new DataGathererContext(minecraft.level, te));
int rows = stats.maxRows();
int startIndex = Math.min(te.targetLine, rows);
targetLineLabel = new Label(x + 65, y + 109, TextComponent.EMPTY).withShadow();
targetLineLabel.text = target.getLineOptionText(startIndex);
if (rows > 1) {
targetLineSelector = new ScrollInput(x + 61, y + 105, 135, 16).withRange(0, rows)
.titled(Lang.translate("data_gatherer.display_on"))
.inverted()
.calling(i -> targetLineLabel.text = target.getLineOptionText(i))
.setState(startIndex);
addRenderableWidget(targetLineSelector);
}
addRenderableWidget(targetLineLabel);
}
if (!sources.isEmpty()) {
int startIndex = Math.max(sources.indexOf(te.activeSource), 0);
sourceTypeLabel = new Label(x + 65, y + 30, TextComponent.EMPTY).withShadow();
sourceTypeLabel.text = sources.get(startIndex)
.getName();
if (sources.size() > 1) {
List<Component> options = sources.stream()
.map(DataGathererSource::getName)
.toList();
sourceTypeSelector = new SelectionScrollInput(x + 61, y + 26, 135, 16).forOptions(options)
.writingTo(sourceTypeLabel)
.titled(Lang.translate("data_gatherer.information_type"))
.calling(this::initGathererSourceSubOptions)
.setState(startIndex);
sourceTypeSelector.onChanged();
addRenderableWidget(sourceTypeSelector);
} else
initGathererSourceSubOptions(0);
addRenderableWidget(sourceTypeLabel);
}
}
private void initGathererSourceSubOptions(int i) {
DataGathererSource source = sources.get(i);
source.populateData(new DataGathererContext(te.getLevel(), te));
if (targetLineSelector != null)
targetLineSelector
.titled(source instanceof SingleLineDataSource ? Lang.translate("data_gatherer.display_on")
: Lang.translate("data_gatherer.display_on_multiline"));
configWidgets.forEach(s -> {
s.forEach(p -> removeWidget(p.getFirst()));
s.clear();
});
DataGathererContext context = new DataGathererContext(minecraft.level, te);
configWidgets.forEachWithContext((s, first) -> source.initConfigurationWidgets(context,
new LineBuilder(s, guiLeft + 60, guiTop + (first ? 51 : 72)), first));
configWidgets.forEach(s -> s.forEach(p -> {
loadValue(te.getSourceConfig(), p);
if (p.getFirst() instanceof TooltipArea)
addRenderableOnly(p.getFirst());
else
addRenderableWidget(p.getFirst());
}));
}
public class LineBuilder {
private Set<Pair<AbstractWidget, String>> targetSet;
private int x;
private int y;
public LineBuilder(Set<Pair<AbstractWidget, String>> targetSet, int x, int y) {
this.targetSet = targetSet;
this.x = x;
this.y = y;
}
public LineBuilder addScrollInput(int x, int width, BiConsumer<ScrollInput, Label> inputTransform,
String dataKey) {
ScrollInput input = new ScrollInput(x + this.x, y - 4, width, 18);
addScrollInput(input, inputTransform, dataKey);
return this;
}
public LineBuilder addSelectionScrollInput(int x, int width,
BiConsumer<SelectionScrollInput, Label> inputTransform, String dataKey) {
SelectionScrollInput input = new SelectionScrollInput(x + this.x, y - 4, width, 18);
addScrollInput(input, inputTransform, dataKey);
return this;
}
private <T extends ScrollInput> void addScrollInput(T input, BiConsumer<T, Label> inputTransform,
String dataKey) {
Label label = new Label(input.x + 5, y, TextComponent.EMPTY);
label.withShadow();
inputTransform.accept(input, label);
input.writingTo(label);
targetSet.add(Pair.of(label, "Dummy"));
targetSet.add(Pair.of(input, dataKey));
}
public LineBuilder addTextInput(int x, int width, BiConsumer<EditBox, TooltipArea> inputTransform,
String dataKey) {
EditBox input = new EditBox(font, x + this.x + 5, y, width - 9, 8, TextComponent.EMPTY);
input.setBordered(false);
input.setTextColor(0xffffff);
input.changeFocus(false);
input.mouseClicked(0, 0, 0);
TooltipArea tooltipArea = new TooltipArea(this.x + x, y - 4, width, 18);
inputTransform.accept(input, tooltipArea);
targetSet.add(Pair.of(input, dataKey));
targetSet.add(Pair.of(tooltipArea, "Dummy"));
return this;
}
}
private void saveValue(CompoundTag data, Pair<AbstractWidget, String> widget) {
AbstractWidget w = widget.getFirst();
String key = widget.getSecond();
if (w instanceof EditBox eb)
data.putString(key, eb.getValue());
if (w instanceof ScrollInput si)
data.putInt(key, si.getState());
}
private void loadValue(CompoundTag data, Pair<AbstractWidget, String> widget) {
AbstractWidget w = widget.getFirst();
String key = widget.getSecond();
if (!data.contains(key))
return;
if (w instanceof EditBox eb)
eb.setValue(data.getString(key));
if (w instanceof ScrollInput si)
si.setState(data.getInt(key));
}
@Override
public void onClose() {
super.onClose();
CompoundTag sourceData = new CompoundTag();
if (!sources.isEmpty()) {
sourceData.putString("Id",
sources.get(sourceTypeSelector == null ? 0 : sourceTypeSelector.getState()).id.toString());
configWidgets.forEach(s -> s.forEach(p -> saveValue(sourceData, p)));
}
AllPackets.channel.sendToServer(new DataGathererConfigurationPacket(te.getBlockPos(), sourceData,
targetLineSelector == null ? 0 : targetLineSelector.getState()));
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
int y = guiTop;
background.render(ms, x, y, this);
MutableComponent header = Lang.translate("data_gatherer.title");
font.draw(ms, header, x + background.width / 2 - font.width(header) / 2, y + 4, 0x442000);
if (sources.isEmpty())
font.drawShadow(ms, Lang.translate("data_gatherer.no_source"), x + 65, y + 30, 0xD3D3D3);
if (target == null)
font.drawShadow(ms, Lang.translate("data_gatherer.no_target"), x + 65, y + 109, 0xD3D3D3);
if (!sourceIcon.isEmpty())
minecraft.getItemRenderer()
.renderGuiItem(sourceIcon, x + 37, y + 26);
if (!targetIcon.isEmpty())
minecraft.getItemRenderer()
.renderGuiItem(targetIcon, x + 37, y + 105);
configWidgets.forEachWithContext((s, first) -> s.forEach(p -> {
if (p.getSecond()
.equals("Dummy"))
return;
renderWidgetBG(ms, p.getFirst(), first);
}));
ms.pushPose();
TransformStack.cast(ms)
.pushPose()
.translate(x + background.width + 4, y + background.height + 4, 100)
.scale(40)
.rotateX(-22)
.rotateY(63);
GuiGameElement.of(te.getBlockState()
.setValue(DataGathererBlock.FACING, Direction.UP))
.render(ms);
ms.popPose();
}
protected void renderWidgetBG(PoseStack ms, AbstractWidget aw, boolean firstLine) {
int x = aw.x;
int width = aw.getWidth();
int y = guiTop + (firstLine ? 46 : 67);
if (aw instanceof EditBox) {
x -= 5;
width += 9;
}
UIRenderHelper.drawStretched(ms, x, y, width, 18, -100, AllGuiTextures.DATA_AREA);
AllGuiTextures.DATA_AREA_START.render(ms, x, y);
AllGuiTextures.DATA_AREA_END.render(ms, x + width - 2, y);
}
@Override
protected void renderWindowForeground(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
super.renderWindowForeground(ms, mouseX, mouseY, partialTicks);
int mX = mouseX - guiLeft;
int mY = mouseY - guiTop;
if (sourceState != null && mX >= 33 && mX < 53 && mY >= 24 && mY < 44) {
renderComponentTooltip(ms,
ImmutableList.of(Lang.translate("data_gatherer.reading_from"), sourceState.getBlock()
.getName()
.withStyle(s -> s.withColor(sources.isEmpty() ? 0xF68989 : 0xF2C16D)),
Lang.translate("data_gatherer.attached_side"), Lang.translate("data_gatherer.view_compatible")
.withStyle(ChatFormatting.GRAY)),
mouseX, mouseY);
}
if (targetState != null && mX >= 33 && mX < 53 && mY >= 102 && mY < 122) {
renderComponentTooltip(ms,
ImmutableList.of(Lang.translate("data_gatherer.writing_to"), targetState.getBlock()
.getName()
.withStyle(s -> s.withColor(target == null ? 0xF68989 : 0xF2C16D)),
Lang.translate("data_gatherer.targeted_location"), Lang.translate("data_gatherer.view_compatible")
.withStyle(ChatFormatting.GRAY)),
mouseX, mouseY);
}
}
@Override
protected void removeWidget(GuiEventListener p_169412_) {
if (p_169412_ != null)
super.removeWidget(p_169412_);
}
}

View file

@ -0,0 +1,172 @@
package com.simibubi.create.content.logistics.block.data;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.content.logistics.block.data.target.DataGathererTarget;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
public class DataGathererTileEntity extends SmartTileEntity {
protected BlockPos targetOffset;
public DataGathererSource activeSource;
private CompoundTag sourceConfig;
public DataGathererTarget activeTarget;
public int targetLine;
public int refreshTicks;
public DataGathererTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
targetOffset = BlockPos.ZERO;
sourceConfig = new CompoundTag();
targetLine = 0;
}
@Override
public void tick() {
super.tick();
if (activeSource == null)
return;
if (level.isClientSide)
return;
refreshTicks++;
if (refreshTicks < activeSource.getPassiveRefreshTicks())
return;
tickSource();
}
public void tickSource() {
refreshTicks = 0;
if (getBlockState().getOptionalValue(DataGathererBlock.POWERED)
.orElse(true))
return;
if (!level.isClientSide)
updateGatheredData();
}
public void onNoLongerPowered() {
if (activeSource == null)
return;
refreshTicks = 0;
activeSource.onSignalReset(new DataGathererContext(level, this));
updateGatheredData();
}
public void updateGatheredData() {
BlockPos sourcePosition = getSourcePosition();
BlockPos targetPosition = getTargetPosition();
if (!level.isAreaLoaded(targetPosition, 1) || !level.isAreaLoaded(sourcePosition, 1))
return;
DataGathererTarget target = AllDataGathererBehaviours.targetOf(level, targetPosition);
List<DataGathererSource> sources = AllDataGathererBehaviours.sourcesOf(level, sourcePosition);
boolean notify = false;
if (activeTarget != target) {
activeTarget = target;
notify = true;
}
if (activeSource != null && !sources.contains(activeSource)) {
activeSource = null;
sourceConfig = new CompoundTag();
notify = true;
}
if (notify)
notifyUpdate();
if (activeSource == null || activeTarget == null)
return;
DataGathererContext context = new DataGathererContext(level, this);
activeSource.transferData(context, activeTarget, targetLine);
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
@Override
public void writeSafe(CompoundTag tag, boolean clientPacket) {
super.writeSafe(tag, clientPacket);
writeGatheredData(tag, clientPacket);
}
@Override
protected void write(CompoundTag tag, boolean clientPacket) {
super.write(tag, clientPacket);
writeGatheredData(tag, clientPacket);
if (clientPacket && activeTarget != null)
tag.putString("TargetType", activeTarget.id.toString());
}
private void writeGatheredData(CompoundTag tag, boolean clientPacket) {
tag.put("TargetOffset", NbtUtils.writeBlockPos(targetOffset));
tag.putInt("TargetLine", targetLine);
if (activeSource != null) {
CompoundTag data = sourceConfig.copy();
data.putString("Id", activeSource.id.toString());
tag.put("Source", data);
}
}
@Override
protected void read(CompoundTag tag, boolean clientPacket) {
super.read(tag, clientPacket);
targetOffset = NbtUtils.readBlockPos(tag.getCompound("TargetOffset"));
targetLine = tag.getInt("TargetLine");
if (clientPacket && tag.contains("TargetType"))
activeTarget = AllDataGathererBehaviours.getTarget(new ResourceLocation(tag.getString("TargetType")));
if (!tag.contains("Source"))
return;
CompoundTag data = tag.getCompound("Source");
activeSource = AllDataGathererBehaviours.getSource(new ResourceLocation(data.getString("Id")));
sourceConfig = new CompoundTag();
if (activeSource != null)
sourceConfig = data.copy();
}
public void target(BlockPos targetPosition) {
this.targetOffset = targetPosition.subtract(worldPosition);
}
public BlockPos getSourcePosition() {
return worldPosition.relative(getDirection());
}
public CompoundTag getSourceConfig() {
return sourceConfig;
}
public void setSourceConfig(CompoundTag sourceConfig) {
this.sourceConfig = sourceConfig;
}
public Direction getDirection() {
return getBlockState().getOptionalValue(DataGathererBlock.FACING)
.orElse(Direction.UP)
.getOpposite();
}
public BlockPos getTargetPosition() {
return worldPosition.offset(targetOffset);
}
}

View file

@ -0,0 +1,53 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererTileEntity;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
public class AccumulatedItemCountDataSource extends NumericSingleLineDataSource {
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
return new TextComponent(String.valueOf(context.sourceConfig()
.getInt("Collected")));
}
public void itemReceived(DataGathererTileEntity te, int amount) {
if (te.getBlockState()
.getOptionalValue(DataGathererBlock.POWERED)
.orElse(true))
return;
int collected = te.getSourceConfig()
.getInt("Collected");
te.getSourceConfig()
.putInt("Collected", collected + amount);
te.updateGatheredData();
}
@Override
protected String getTranslationKey() {
return "accumulate_items";
}
@Override
public int getPassiveRefreshTicks() {
return 200;
}
@Override
public void onSignalReset(DataGathererContext context) {
context.sourceConfig()
.remove("Collected");
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
}

View file

@ -0,0 +1,76 @@
package com.simibubi.create.content.logistics.block.data.source;
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.block.data.DataGathererBehaviour;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataGathererTarget;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.block.data.target.FlapDisplayDataTarget;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public abstract class DataGathererSource extends DataGathererBehaviour {
public static final List<MutableComponent> EMPTY = ImmutableList.of(new TextComponent(""));
public static final MutableComponent EMPTY_LINE = new TextComponent("");
public static final MutableComponent WHITESPACE = new TextComponent(" ");
public abstract List<MutableComponent> provideText(DataGathererContext context, DataTargetStats stats);
public void transferData(DataGathererContext context, DataGathererTarget activeTarget, int line) {
DataTargetStats stats = activeTarget.provideStats(context);
if (activeTarget instanceof FlapDisplayDataTarget fddt) {
List<List<MutableComponent>> flapDisplayText = provideFlapDisplayText(context, stats);
fddt.acceptFlapText(line, flapDisplayText, context);
return;
}
List<MutableComponent> text = provideText(context, stats);
activeTarget.acceptText(line, text, context);
}
public void onSignalReset(DataGathererContext context) {};
public void populateData(DataGathererContext context) {};
public int getPassiveRefreshTicks() {
return 100;
};
protected String getTranslationKey() {
return id.getPath();
}
public Component getName() {
return new TranslatableComponent(id.getNamespace() + ".data_source." + getTranslationKey());
}
public void loadFlapDisplayLayout(DataGathererContext context, FlapDisplayTileEntity flapDisplay,
FlapDisplayLayout layout) {
if (!layout.isLayout("Default"))
layout.loadDefault(flapDisplay.getMaxCharCount());
}
public List<List<MutableComponent>> provideFlapDisplayText(DataGathererContext context, DataTargetStats stats) {
return provideText(context, stats).stream()
.map(Arrays::asList)
.toList();
}
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {}
}

View file

@ -0,0 +1,32 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stats;
public class DeathCounterDataSource extends StatTrackingDataSource {
@Override
protected int updatedScoreOf(ServerPlayer player) {
return player.getStats()
.getValue(Stats.CUSTOM.get(Stats.DEATHS));
}
@Override
protected String getTranslationKey() {
return "player_deaths";
}
@Override
protected String getObjectiveName() {
return "deaths";
}
@Override
protected Component getObjectiveDisplayName() {
return Lang.translate("data_source.scoreboard.objective.deaths");
}
}

View file

@ -0,0 +1,101 @@
package com.simibubi.create.content.logistics.block.data.source;
import static com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection.WIDE_MONOSPACE;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.block.redstone.StockpileSwitchTileEntity;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class FillLevelDataSource extends NumericSingleLineDataSource {
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
BlockEntity te = context.getSourceTE();
if (!(te instanceof StockpileSwitchTileEntity sste))
return EMPTY_LINE;
float currentLevel = sste.currentLevel;
if (usePercent(context))
return new TextComponent(Mth.clamp((int) (currentLevel * 100), 0, 100) + "%");
String label = context.sourceConfig()
.getString("Label");
int labelSize = label.isEmpty() ? 0 : label.length() + 1;
int length = Math.min(stats.maxColumns() - labelSize, 32);
if (context.getTargetTE() instanceof SignBlockEntity)
length = (int) (length * 6f / 9f);
if (context.getTargetTE() instanceof FlapDisplayTileEntity)
length = sizeForWideChars(length);
int filledLength = (int) (currentLevel * length);
if (length < 1)
return EMPTY_LINE;
StringBuilder s = new StringBuilder();
int emptySpaces = length - filledLength;
for (int i = 0; i < filledLength; i++)
s.append("\u2588");
for (int i = 0; i < emptySpaces; i++)
s.append("\u2592");
return new TextComponent(s.toString());
}
private boolean usePercent(DataGathererContext context) {
return context.sourceConfig()
.getInt("Mode") == 0;
}
@Override
protected String getTranslationKey() {
return "fill_level";
}
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return usePercent(context) ? super.getFlapDisplayLayoutName(context) : "Progress";
}
@Override
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
return usePercent(context) ? super.createSectionForValue(context, size)
: new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "pixel", false, false).wideFlaps();
}
private int sizeForWideChars(int size) {
return (int) (size * FlapDisplaySection.MONOSPACE / WIDE_MONOSPACE);
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
super.initConfigurationWidgets(context, builder, isFirstLine);
if (isFirstLine)
return;
builder.addSelectionScrollInput(0, 120,
(si, l) -> si.forOptions(Lang.translatedOptions("data_source.fill_level", "percent", "progress_bar"))
.titled(Lang.translate("data_source.fill_level.display")),
"Mode");
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
}

View file

@ -0,0 +1,53 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.block.redstone.ContentObserverTileEntity;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InvManipulationBehaviour;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.items.IItemHandler;
public class ItemCountDataSource extends NumericSingleLineDataSource {
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
BlockEntity sourceTE = context.getSourceTE();
if (!(sourceTE instanceof ContentObserverTileEntity cote))
return ZERO;
InvManipulationBehaviour invManipulationBehaviour = cote.getBehaviour(InvManipulationBehaviour.TYPE);
FilteringBehaviour filteringBehaviour = cote.getBehaviour(FilteringBehaviour.TYPE);
IItemHandler handler = invManipulationBehaviour.getInventory();
if (handler == null)
return ZERO;
int collected = 0;
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack stack = handler.extractItem(i, handler.getSlotLimit(i), true);
if (stack.isEmpty())
continue;
if (!filteringBehaviour.test(stack))
continue;
collected += stack.getCount();
}
return new TextComponent(String.valueOf(collected));
}
@Override
protected String getTranslationKey() {
return "count_items";
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
}

View file

@ -0,0 +1,45 @@
package com.simibubi.create.content.logistics.block.data.source;
import java.util.ArrayList;
import java.util.stream.Stream;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.redstone.ContentObserverTileEntity;
import com.simibubi.create.foundation.item.CountedItemStackList;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.inventory.InvManipulationBehaviour;
import com.simibubi.create.foundation.utility.IntAttached;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.items.IItemHandler;
public class ItemListDataSource extends ValueListDataSource {
@Override
protected Stream<IntAttached<MutableComponent>> provideEntries(DataGathererContext context, int maxRows) {
BlockEntity sourceTE = context.getSourceTE();
if (!(sourceTE instanceof ContentObserverTileEntity cote))
return new ArrayList<IntAttached<MutableComponent>>().stream();
InvManipulationBehaviour invManipulationBehaviour = cote.getBehaviour(InvManipulationBehaviour.TYPE);
FilteringBehaviour filteringBehaviour = cote.getBehaviour(FilteringBehaviour.TYPE);
IItemHandler handler = invManipulationBehaviour.getInventory();
if (handler == null)
return new ArrayList<IntAttached<MutableComponent>>().stream();
return new CountedItemStackList(handler, filteringBehaviour).getTopNames(maxRows);
}
@Override
protected String getTranslationKey() {
return "list_items";
}
@Override
protected boolean valueFirst() {
return true;
}
}

View file

@ -0,0 +1,65 @@
package com.simibubi.create.content.logistics.block.data.source;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererTileEntity;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour.TransportedResult;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.ItemStack;
public class ItemNameDataSource extends SingleLineDataSource {
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
DataGathererTileEntity gatherer = context.te();
Direction direction = gatherer.getDirection();
MutableBlockPos pos = gatherer.getSourcePosition()
.mutable();
MutableComponent combined = EMPTY_LINE.copy();
for (int i = 0; i < 32; i++) {
TransportedItemStackHandlerBehaviour behaviour =
TileEntityBehaviour.get(context.level(), pos, TransportedItemStackHandlerBehaviour.TYPE);
pos.move(direction);
if (behaviour == null)
break;
MutableObject<ItemStack> stackHolder = new MutableObject<>();
behaviour.handleCenteredProcessingOnAllItems(.25f, tis -> {
stackHolder.setValue(tis.stack);
return TransportedResult.doNothing();
});
ItemStack stack = stackHolder.getValue();
if (stack != null && !stack.isEmpty())
combined = combined.append(stack.getHoverName());
}
return combined;
}
@Override
protected String getTranslationKey() {
return "combine_item_names";
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return "Number";
}
}

View file

@ -0,0 +1,120 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.DataGathererTileEntity;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class ItemThoughputDataSource extends AccumulatedItemCountDataSource {
static final int POOL_SIZE = 10;
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
CompoundTag conf = context.sourceConfig();
if (conf.contains("Inactive"))
return new TextComponent("0");
double interval = 20 * Math.pow(60, conf.getInt("Interval"));
double rate = conf.getFloat("Rate") * interval;
if (rate > 0) {
long previousTime = conf.getLong("LastReceived");
long gameTime = context.te()
.getLevel()
.getGameTime();
int diff = (int) (gameTime - previousTime);
if (diff > 0) {
// Too long since last item
int lastAmount = conf.getInt("LastReceivedAmount");
double timeBetweenStacks = lastAmount / rate;
if (diff > timeBetweenStacks * 2)
conf.putBoolean("Inactive", true);
}
}
return new TextComponent(IHaveGoggleInformation.format(rate));
}
public void itemReceived(DataGathererTileEntity te, int amount) {
if (te.getBlockState()
.getOptionalValue(DataGathererBlock.POWERED)
.orElse(true))
return;
CompoundTag conf = te.getSourceConfig();
long gameTime = te.getLevel()
.getGameTime();
if (!conf.contains("LastReceived")) {
conf.putLong("LastReceived", gameTime);
return;
}
long previousTime = conf.getLong("LastReceived");
ListTag rates = conf.getList("PrevRates", Tag.TAG_FLOAT);
if (rates.size() != POOL_SIZE) {
rates = new ListTag();
for (int i = 0; i < POOL_SIZE; i++)
rates.add(FloatTag.valueOf(-1));
}
int poolIndex = conf.getInt("Index") % POOL_SIZE;
rates.set(poolIndex, FloatTag.valueOf((float) (amount / (double) (gameTime - previousTime))));
float rate = 0;
int validIntervals = 0;
for (int i = 0; i < POOL_SIZE; i++) {
float pooledRate = rates.getFloat(i);
if (pooledRate >= 0) {
rate += pooledRate;
validIntervals++;
}
}
conf.remove("Rate");
if (validIntervals > 0) {
rate /= validIntervals;
conf.putFloat("Rate", rate);
}
conf.remove("Inactive");
conf.putInt("LastReceivedAmount", amount);
conf.putLong("LastReceived", gameTime);
conf.putInt("Index", poolIndex + 1);
conf.put("PrevRates", rates);
te.updateGatheredData();
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
super.initConfigurationWidgets(context, builder, isFirstLine);
if (isFirstLine)
return;
builder.addSelectionScrollInput(0, 80, (si, l) -> {
si.forOptions(Lang.translatedOptions("data_source.item_throughput.interval", "second", "minute", "hour"))
.titled(Lang.translate("data_source.item_throughput.interval"));
}, "Interval");
}
@Override
protected String getTranslationKey() {
return "item_throughput";
}
}

View file

@ -0,0 +1,60 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.block.data.target.NixieTubeDataTarget;
import com.simibubi.create.content.logistics.block.redstone.NixieTubeTileEntity;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.block.entity.BlockEntity;
public class NixieTubeDataSource extends SingleLineDataSource {
@Override
protected String getTranslationKey() {
return "nixie_tube";
}
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
BlockEntity sourceTE = context.getSourceTE();
if (!(sourceTE instanceof NixieTubeTileEntity nte))
return EMPTY_LINE;
MutableComponent text = nte.getFullText();
try {
String line = text.getString();
Integer.valueOf(line);
context.flapDisplayContext = Boolean.TRUE;
} catch (NumberFormatException e) {
}
return text;
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return !(context.te().activeTarget instanceof NixieTubeDataTarget);
}
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
if (isNumeric(context))
return "Number";
return super.getFlapDisplayLayoutName(context);
}
@Override
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
if (isNumeric(context))
return new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "numeric", false, false);
return super.createSectionForValue(context, size);
}
protected boolean isNumeric(DataGathererContext context) {
return context.flapDisplayContext == Boolean.TRUE;
}
}

View file

@ -0,0 +1,22 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import net.minecraft.network.chat.TextComponent;
public abstract class NumericSingleLineDataSource extends SingleLineDataSource {
protected static final TextComponent ZERO = new TextComponent("0");
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return "Number";
}
@Override
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
return new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "numeric", false, false);
}
}

View file

@ -0,0 +1,78 @@
package com.simibubi.create.content.logistics.block.data.source;
import java.util.ArrayList;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.foundation.utility.IntAttached;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.scores.Objective;
public class ScoreboardDataSource extends ValueListDataSource {
@Override
protected Stream<IntAttached<MutableComponent>> provideEntries(DataGathererContext context, int maxRows) {
Level level = context.te()
.getLevel();
if (!(level instanceof ServerLevel sLevel))
return new ArrayList<IntAttached<MutableComponent>>().stream();
String name = context.sourceConfig()
.getString("Objective");
return showScoreboard(sLevel, name, maxRows);
}
protected Stream<IntAttached<MutableComponent>> showScoreboard(ServerLevel sLevel, String objectiveName,
int maxRows) {
Objective objective = sLevel.getScoreboard()
.getObjective(objectiveName);
if (objective == null)
return notFound(objectiveName).stream();
return sLevel.getScoreboard()
.getPlayerScores(objective)
.stream()
.limit(maxRows)
.map(score -> IntAttached.with(score.getScore(), new TextComponent(score.getOwner()).copy()))
.sorted(IntAttached.comparator());
}
private ImmutableList<IntAttached<MutableComponent>> notFound(String objective) {
return ImmutableList
.of(IntAttached.with(404, Lang.translate("data_source.scoreboard.objective_not_found", objective)));
}
@Override
protected String getTranslationKey() {
return "scoreboard";
}
@Override
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
if (isFirstLine)
builder.addTextInput(0, 137, (e, t) -> {
e.setValue("");
t.withTooltip(ImmutableList.of(Lang.translate("data_source.scoreboard.objective")
.withStyle(s -> s.withColor(0x5391E1)),
Lang.translate("gui.schedule.lmb_edit")
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC)));
}, "Objective");
else
addFullNumberConfig(builder);
}
@Override
protected boolean valueFirst() {
return false;
}
}

View file

@ -0,0 +1,118 @@
package com.simibubi.create.content.logistics.block.data.source;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public abstract class SingleLineDataSource extends DataGathererSource {
protected abstract MutableComponent provideLine(DataGathererContext context, DataTargetStats stats);
protected abstract boolean allowsLabeling(DataGathererContext context);
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
if (isFirstLine && allowsLabeling(context))
addLabelingTextBox(builder);
}
@OnlyIn(Dist.CLIENT)
protected void addLabelingTextBox(LineBuilder builder) {
builder.addTextInput(0, 137, (e, t) -> {
e.setValue("");
t.withTooltip(ImmutableList.of(Lang.translate("data_source.label")
.withStyle(s -> s.withColor(0x5391E1)),
Lang.translate("gui.schedule.lmb_edit")
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC)));
}, "Label");
}
@Override
public List<MutableComponent> provideText(DataGathererContext context, DataTargetStats stats) {
MutableComponent line = provideLine(context, stats);
if (line == EMPTY_LINE)
return EMPTY;
if (allowsLabeling(context)) {
String label = context.sourceConfig()
.getString("Label");
if (!label.isEmpty())
line = new TextComponent(label + " ").append(line);
}
return ImmutableList.of(line);
}
@Override
public List<List<MutableComponent>> provideFlapDisplayText(DataGathererContext context, DataTargetStats stats) {
if (allowsLabeling(context)) {
String label = context.sourceConfig()
.getString("Label");
if (!label.isEmpty())
return ImmutableList.of(ImmutableList.of(new TextComponent(label + " "), provideLine(context, stats)));
}
return super.provideFlapDisplayText(context, stats);
}
@Override
public void loadFlapDisplayLayout(DataGathererContext context, FlapDisplayTileEntity flapDisplay,
FlapDisplayLayout layout) {
String layoutKey = getFlapDisplayLayoutName(context);
if (!allowsLabeling(context)) {
if (!layout.isLayout(layoutKey))
layout.configure(layoutKey,
ImmutableList.of(createSectionForValue(context, flapDisplay.getMaxCharCount())));
return;
}
String label = context.sourceConfig()
.getString("Label");
if (label.isEmpty()) {
if (!layout.isLayout(layoutKey))
layout.configure(layoutKey,
ImmutableList.of(createSectionForValue(context, flapDisplay.getMaxCharCount())));
return;
}
String layoutName = label.length() + "_Labeled_" + layoutKey;
if (layout.isLayout(layoutName))
return;
int maxCharCount = flapDisplay.getMaxCharCount();
FlapDisplaySection labelSection = new FlapDisplaySection(
Math.min(maxCharCount, label.length() + 1) * FlapDisplaySection.MONOSPACE, "alphabet", false, false);
if (label.length() + 1 < maxCharCount)
layout.configure(layoutName,
ImmutableList.of(labelSection, createSectionForValue(context, maxCharCount - label.length() - 1)));
else
layout.configure(layoutName, ImmutableList.of(labelSection));
}
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return "Default";
}
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
return new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "alphabet", false, false);
}
}

View file

@ -0,0 +1,64 @@
package com.simibubi.create.content.logistics.block.data.source;
import java.util.ArrayList;
import java.util.stream.Stream;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.foundation.utility.IntAttached;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public abstract class StatTrackingDataSource extends ScoreboardDataSource {
@Override
protected Stream<IntAttached<MutableComponent>> provideEntries(DataGathererContext context, int maxRows) {
Level level = context.te()
.getLevel();
if (!(level instanceof ServerLevel sLevel))
return new ArrayList<IntAttached<MutableComponent>>().stream();
String name = "create_auto_" + getObjectiveName();
Scoreboard scoreboard = level.getScoreboard();
if (!scoreboard.hasObjective(name))
scoreboard.addObjective(name, ObjectiveCriteria.DUMMY, getObjectiveDisplayName(), RenderType.INTEGER);
Objective objective = scoreboard.getObjective(name);
sLevel.players()
.forEach(s -> scoreboard.getOrCreatePlayerScore(s.getScoreboardName(), objective)
.setScore(updatedScoreOf(s)));
return showScoreboard(sLevel, name, maxRows);
}
protected abstract String getObjectiveName();
protected abstract Component getObjectiveDisplayName();
protected abstract int updatedScoreOf(ServerPlayer player);
@Override
protected boolean valueFirst() {
return false;
}
@Override
protected boolean shortenNumbers(DataGathererContext context) {
return false;
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {}
}

View file

@ -0,0 +1,203 @@
package com.simibubi.create.content.logistics.block.data.source;
import static com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection.MONOSPACE;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.Mth;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class StationSummaryDataSource extends DataGathererSource {
protected static final MutableComponent UNPREDICTABLE = new TextComponent(" ~ ");
protected static final List<MutableComponent> EMPTY_ENTRY_4 =
ImmutableList.of(WHITESPACE, new TextComponent(" . "), WHITESPACE, WHITESPACE);
protected static final List<MutableComponent> EMPTY_ENTRY_5 =
ImmutableList.of(WHITESPACE, new TextComponent(" . "), WHITESPACE, WHITESPACE, WHITESPACE);
@Override
public List<MutableComponent> provideText(DataGathererContext context, DataTargetStats stats) {
return EMPTY;
}
@Override
public List<List<MutableComponent>> provideFlapDisplayText(DataGathererContext context, DataTargetStats stats) {
String filter = context.sourceConfig()
.getString("Filter");
boolean hasPlatform = filter.contains("*");
List<List<MutableComponent>> list = new ArrayList<>();
GlobalTrainDisplayData.prepare(filter, stats.maxRows())
.forEach(prediction -> {
List<MutableComponent> lines = new ArrayList<>();
if (prediction.ticks == -1 || prediction.ticks >= 12000 - 15 * 20) {
lines.add(WHITESPACE);
lines.add(UNPREDICTABLE);
} else if (prediction.ticks < 200) {
lines.add(WHITESPACE);
lines.add(Lang.translate("data_source.station_summary.now"));
} else {
int min = prediction.ticks / 1200;
int sec = (prediction.ticks / 20) % 60;
sec = Mth.ceil(sec / 15f) * 15;
if (sec == 60) {
min++;
sec = 0;
}
lines.add(min > 0 ? new TextComponent(String.valueOf(min)) : WHITESPACE);
lines.add(min > 0 ? Lang.translate("data_source.station_summary.minutes")
: Lang.translate("data_source.station_summary.seconds", sec));
}
lines.add(prediction.train.name.copy());
lines.add(prediction.scheduleTitle);
if (!hasPlatform) {
list.add(lines);
return;
}
String platform = prediction.destination;
for (String string : filter.split("\\*"))
if (!string.isEmpty())
platform = platform.replace(string, "");
platform = platform.replace("*", "?");
lines.add(new TextComponent(platform.trim()));
list.add(lines);
});
int toPad = stats.maxRows() - list.size();
for (int padding = 0; padding < toPad; padding++)
list.add(hasPlatform ? EMPTY_ENTRY_5 : EMPTY_ENTRY_4);
return list;
}
@Override
public void loadFlapDisplayLayout(DataGathererContext context, FlapDisplayTileEntity flapDisplay,
FlapDisplayLayout layout) {
CompoundTag conf = context.sourceConfig();
int columnWidth = conf.getInt("NameColumn");
int columnWidth2 = conf.getInt("PlatformColumn");
boolean hasPlatform = conf.getString("Filter")
.contains("*");
String layoutName = "StationSummary" + columnWidth + hasPlatform + columnWidth2;
if (layout.isLayout(layoutName))
return;
ArrayList<FlapDisplaySection> list = new ArrayList<>();
int timeWidth = 20;
float gapSize = 8f;
float platformWidth = columnWidth2 * MONOSPACE;
// populate
FlapDisplaySection minutes = new FlapDisplaySection(MONOSPACE, "numeric", false, false);
FlapDisplaySection time = new FlapDisplaySection(timeWidth, "arrival_time", true, true);
float totalSize = flapDisplay.xSize * 32f - 4f - gapSize * 2;
totalSize = totalSize - timeWidth - MONOSPACE;
platformWidth = Math.min(platformWidth, totalSize - gapSize);
platformWidth = (int) (platformWidth / MONOSPACE) * MONOSPACE;
if (hasPlatform)
totalSize = totalSize - gapSize - platformWidth;
if (platformWidth == 0 && hasPlatform)
totalSize += gapSize;
int trainNameWidth = (int) ((columnWidth / 100f) * totalSize / MONOSPACE);
int destinationWidth = (int) Math.round((1 - columnWidth / 100f) * totalSize / MONOSPACE);
FlapDisplaySection trainName =
new FlapDisplaySection(trainNameWidth * MONOSPACE, "alphabet", false, trainNameWidth > 0);
FlapDisplaySection destination = new FlapDisplaySection(destinationWidth * MONOSPACE, "alphabet", false,
hasPlatform && destinationWidth > 0 && platformWidth > 0);
FlapDisplaySection platform = new FlapDisplaySection(platformWidth, "numeric", false, false).rightAligned();
list.add(minutes);
list.add(time);
list.add(trainName);
list.add(destination);
if (hasPlatform)
list.add(platform);
layout.configure(layoutName, list);
}
@Override
protected String getTranslationKey() {
return "station_summary";
}
@Override
public void populateData(DataGathererContext context) {
CompoundTag conf = context.sourceConfig();
if (conf.contains("Filter"))
return;
if (!(context.getSourceTE()instanceof StationTileEntity stationTe))
return;
GlobalStation station = stationTe.getStation();
if (station == null)
return;
conf.putString("Filter", station.name);
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
if (isFirstLine) {
builder.addTextInput(0, 137, (e, t) -> {
e.setValue("");
t.withTooltip(ImmutableList.of(Lang.translate("data_source.station_summary.filter")
.withStyle(s -> s.withColor(0x5391E1)),
Lang.translate("gui.schedule.lmb_edit")
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC)));
}, "Filter");
return;
}
builder.addScrollInput(0, 32, (si, l) -> {
si.titled(Lang.translate("data_source.station_summary.train_name_column"))
.withRange(0, 73)
.withShiftStep(12);
si.setState(50);
l.withSuffix("%");
}, "NameColumn");
builder.addScrollInput(36, 22, (si, l) -> {
si.titled(Lang.translate("data_source.station_summary.platform_column"))
.withRange(0, 16)
.withShiftStep(4);
si.setState(3);
}, "PlatformColumn");
}
}

View file

@ -0,0 +1,74 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.content.contraptions.components.clock.CuckooClockTileEntity;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
public class StopWatchDataSource extends SingleLineDataSource {
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
if (!(context.getSourceTE()instanceof CuckooClockTileEntity ccte))
return TimeOfDayDataSource.EMPTY_TIME;
if (ccte.getSpeed() == 0)
return TimeOfDayDataSource.EMPTY_TIME;
if (!context.sourceConfig()
.contains("StartTime"))
onSignalReset(context);
long started = context.sourceConfig()
.getLong("StartTime");
long current = context.te()
.getLevel()
.getGameTime();
int diff = (int) (current - started);
int hours = (diff / 60 / 60 / 20);
int minutes = (diff / 60 / 20) % 60;
int seconds = (diff / 20) % 60;
MutableComponent component = new TextComponent((hours == 0 ? "" : (hours < 10 ? " " : "") + hours + ":")
+ (minutes < 10 ? hours == 0 ? " " : "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds);
return component;
}
@Override
public void onSignalReset(DataGathererContext context) {
context.sourceConfig()
.putLong("StartTime", context.te()
.getLevel()
.getGameTime());
}
@Override
public int getPassiveRefreshTicks() {
return 20;
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return "Instant";
}
@Override
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
return new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "instant", false, false);
}
@Override
protected String getTranslationKey() {
return "stop_watch";
}
}

View file

@ -0,0 +1,91 @@
package com.simibubi.create.content.logistics.block.data.source;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.clock.CuckooClockTileEntity;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class TimeOfDayDataSource extends SingleLineDataSource {
public static final MutableComponent EMPTY_TIME = new TextComponent("--:--");
@Override
protected MutableComponent provideLine(DataGathererContext context, DataTargetStats stats) {
if (!(context.level()instanceof ServerLevel sLevel))
return EMPTY_TIME;
if (!(context.getSourceTE() instanceof CuckooClockTileEntity ccte))
return EMPTY_TIME;
if (ccte.getSpeed() == 0)
return EMPTY_TIME;
boolean c12 = context.sourceConfig()
.getInt("Cycle") == 0;
boolean isNatural = sLevel.dimensionType()
.natural();
int dayTime = (int) (sLevel.getDayTime() % 24000);
int hours = (dayTime / 1000 + 6) % 24;
int minutes = (dayTime % 1000) * 60 / 1000;
MutableComponent suffix = Lang.translate("generic.daytime." + (hours > 11 ? "pm" : "am"));
minutes = minutes / 5 * 5;
if (c12) {
hours %= 12;
if (hours == 0)
hours = 12;
}
if (!isNatural) {
hours = Create.RANDOM.nextInt(70) + 24;
minutes = Create.RANDOM.nextInt(40) + 60;
}
MutableComponent component = new TextComponent(
(hours < 10 ? " " : "") + hours + ":" + (minutes < 10 ? "0" : "") + minutes + (c12 ? " " : ""));
return c12 ? component.append(suffix) : component;
}
@Override
protected String getFlapDisplayLayoutName(DataGathererContext context) {
return "Instant";
}
@Override
protected FlapDisplaySection createSectionForValue(DataGathererContext context, int size) {
return new FlapDisplaySection(size * FlapDisplaySection.MONOSPACE, "instant", false, false);
}
@Override
protected String getTranslationKey() {
return "time_of_day";
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
super.initConfigurationWidgets(context, builder, isFirstLine);
if (isFirstLine)
return;
builder.addSelectionScrollInput(0, 60, (si, l) -> {
si.forOptions(Lang.translatedOptions("data_source.time", "12_hour", "24_hour"))
.titled(Lang.translate("data_source.time.format"));
}, "Cycle");
}
@Override
protected boolean allowsLabeling(DataGathererContext context) {
return true;
}
}

View file

@ -0,0 +1,170 @@
package com.simibubi.create.content.logistics.block.data.source;
import static com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection.MONOSPACE;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.DataGathererScreen.LineBuilder;
import com.simibubi.create.content.logistics.block.data.target.DataTargetStats;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.IntAttached;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public abstract class ValueListDataSource extends DataGathererSource {
protected abstract Stream<IntAttached<MutableComponent>> provideEntries(DataGathererContext context, int maxRows);
protected abstract boolean valueFirst();
@Override
public List<MutableComponent> provideText(DataGathererContext context, DataTargetStats stats) {
boolean isBook = context.getTargetTE() instanceof LecternBlockEntity;
List<MutableComponent> list = provideEntries(context, stats.maxRows() * (isBook ? ENTRIES_PER_PAGE : 1))
.map(e -> createComponentsFromEntry(context, e))
.map(l -> {
MutableComponent combined = l.get(0)
.append(l.get(1));
if (l.size() > 2)
combined.append(l.get(2));
return combined;
})
.toList();
if (isBook)
list = condensePages(list);
return list;
}
static final int ENTRIES_PER_PAGE = 8;
private List<MutableComponent> condensePages(List<MutableComponent> list) {
List<MutableComponent> condensed = new ArrayList<>();
MutableComponent current = null;
for (int i = 0; i < list.size(); i++) {
MutableComponent atIndex = list.get(i);
if (current == null) {
current = atIndex;
continue;
}
current.append(new TextComponent("\n"))
.append(atIndex);
if ((i + 1) % ENTRIES_PER_PAGE == 0) {
condensed.add(current);
current = null;
}
}
if (current != null)
condensed.add(current);
return condensed;
}
@Override
public List<List<MutableComponent>> provideFlapDisplayText(DataGathererContext context, DataTargetStats stats) {
MutableInt highest = new MutableInt(0);
context.flapDisplayContext = highest;
return provideEntries(context, stats.maxRows()).map(e -> {
highest.setValue(Math.max(highest.getValue(), e.getFirst()));
return createComponentsFromEntry(context, e);
})
.toList();
}
protected List<MutableComponent> createComponentsFromEntry(DataGathererContext context,
IntAttached<MutableComponent> entry) {
int number = entry.getFirst();
MutableComponent name = entry.getSecond()
.append(WHITESPACE);
if (shortenNumbers(context)) {
Couple<MutableComponent> shortened = shorten(number);
return valueFirst() ? Arrays.asList(shortened.getFirst(), shortened.getSecond(), name)
: Arrays.asList(name, shortened.getFirst(), shortened.getSecond());
}
MutableComponent formattedNumber = new TextComponent(String.valueOf(number)).append(WHITESPACE);
return valueFirst() ? Arrays.asList(formattedNumber, name) : Arrays.asList(name, formattedNumber);
}
@Override
public void loadFlapDisplayLayout(DataGathererContext context, FlapDisplayTileEntity flapDisplay,
FlapDisplayLayout layout) {
boolean valueFirst = valueFirst();
boolean shortenNumbers = shortenNumbers(context);
int valueFormat = shortenNumbers ? 0
: Math.max(4, 1 + (int) Math.log10(((MutableInt) context.flapDisplayContext).intValue()));
String layoutKey = "ValueList_" + valueFirst + "_" + valueFormat;
if (layout.isLayout(layoutKey))
return;
int maxCharCount = flapDisplay.getMaxCharCount(1);
int numberLength = Math.min(maxCharCount, Math.max(3, valueFormat));
int nameLength = Math.max(maxCharCount - numberLength - (shortenNumbers ? 1 : 0), 0);
FlapDisplaySection name = new FlapDisplaySection(MONOSPACE * nameLength, "alphabet", false, !valueFirst);
FlapDisplaySection value =
new FlapDisplaySection(MONOSPACE * numberLength, "number", false, !shortenNumbers && valueFirst)
.rightAligned();
if (shortenNumbers) {
FlapDisplaySection suffix = new FlapDisplaySection(MONOSPACE, "shortened_numbers", false, valueFirst);
layout.configure(layoutKey,
valueFirst ? Arrays.asList(value, suffix, name) : Arrays.asList(name, value, suffix));
return;
}
layout.configure(layoutKey, valueFirst ? Arrays.asList(value, name) : Arrays.asList(name, value));
}
private Couple<MutableComponent> shorten(int number) {
if (number >= 1000000)
return Couple.create(new TextComponent(String.valueOf(number / 1000000)),
Lang.translate("data_source.value_list.million")
.append(WHITESPACE));
if (number >= 1000)
return Couple.create(new TextComponent(String.valueOf(number / 1000)),
Lang.translate("data_source.value_list.thousand")
.append(WHITESPACE));
return Couple.create(new TextComponent(String.valueOf(number)), WHITESPACE);
}
protected boolean shortenNumbers(DataGathererContext context) {
return context.sourceConfig()
.getInt("Format") == 0;
}
@Override
@OnlyIn(Dist.CLIENT)
public void initConfigurationWidgets(DataGathererContext context, LineBuilder builder, boolean isFirstLine) {
if (isFirstLine)
addFullNumberConfig(builder);
}
@OnlyIn(Dist.CLIENT)
protected void addFullNumberConfig(LineBuilder builder) {
builder.addSelectionScrollInput(0, 75,
(si, l) -> si.forOptions(Lang.translatedOptions("data_source.value_list", "shortened", "full_number"))
.titled(Lang.translate("data_source.value_list.display")),
"Format");
}
}

View file

@ -0,0 +1,71 @@
package com.simibubi.create.content.logistics.block.data.target;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.logistics.block.data.DataGathererBehaviour;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
public abstract class DataGathererTarget extends DataGathererBehaviour {
public abstract void acceptText(int line, List<MutableComponent> text, DataGathererContext context);
public abstract DataTargetStats provideStats(DataGathererContext context);
public AABB getMultiblockBounds(LevelAccessor level, BlockPos pos) {
VoxelShape shape = level.getBlockState(pos)
.getShape(level, pos);
if (shape.isEmpty())
return new AABB(pos);
return shape.bounds()
.move(pos);
}
public Component getLineOptionText(int line) {
return Lang.translate("data_target.line", line + 1);
}
public static void reserve(int line, BlockEntity target, DataGathererContext context) {
if (line == 0)
return;
CompoundTag tag = target.getTileData();
CompoundTag compound = tag.getCompound("DataGatherer");
compound.putLong("Line" + line, context.te()
.getBlockPos()
.asLong());
tag.put("DataGatherer", compound);
}
public boolean isReserved(int line, BlockEntity target, DataGathererContext context) {
CompoundTag tag = target.getTileData();
CompoundTag compound = tag.getCompound("DataGatherer");
if (!compound.contains("Line" + line))
return false;
long l = compound.getLong("Line" + line);
BlockPos reserved = BlockPos.of(l);
if (!reserved.equals(context.te()
.getBlockPos()) && AllBlocks.DATA_GATHERER.has(target.getLevel()
.getBlockState(reserved)))
return true;
compound.remove("Line" + line);
if (compound.isEmpty())
tag.remove("DataGatherer");
return false;
}
}

View file

@ -0,0 +1,5 @@
package com.simibubi.create.content.logistics.block.data.target;
public record DataTargetStats(int maxRows, int maxColumns, DataGathererTarget type) {
}

View file

@ -0,0 +1,109 @@
package com.simibubi.create.content.logistics.block.data.target;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.data.source.DataGathererSource;
import com.simibubi.create.content.logistics.block.data.source.SingleLineDataSource;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class FlapDisplayDataTarget extends DataGathererTarget {
@Override
public void acceptText(int line, List<MutableComponent> text, DataGathererContext context) {}
public void acceptFlapText(int line, List<List<MutableComponent>> text, DataGathererContext context) {
FlapDisplayTileEntity controller = getController(context);
if (controller == null)
return;
if (!controller.isSpeedRequirementFulfilled())
return;
DataGathererSource source = context.te().activeSource;
List<FlapDisplayLayout> lines = controller.getLines();
for (int i = 0; i + line < lines.size(); i++) {
if (i == 0)
reserve(i + line, controller, context);
if (i > 0 && isReserved(i + line, controller, context))
break;
FlapDisplayLayout layout = lines.get(i + line);
if (i >= text.size()) {
if (source instanceof SingleLineDataSource)
break;
controller.applyTextManually(i + line, null);
continue;
}
source.loadFlapDisplayLayout(context, controller, layout);
for (int sectionIndex = 0; sectionIndex < layout.getSections()
.size(); sectionIndex++) {
List<MutableComponent> textLine = text.get(i);
if (textLine.size() <= sectionIndex)
break;
layout.getSections()
.get(sectionIndex)
.setText(textLine.get(sectionIndex));
}
}
controller.sendData();
}
@Override
public boolean isReserved(int line, BlockEntity target, DataGathererContext context) {
return super.isReserved(line, target, context)
|| target instanceof FlapDisplayTileEntity fdte && fdte.manualLines.length > line && fdte.manualLines[line];
}
@Override
public DataTargetStats provideStats(DataGathererContext context) {
FlapDisplayTileEntity controller = getController(context);
if (controller == null)
return new DataTargetStats(1, 1, this);
return new DataTargetStats(controller.ySize * 2, controller.getMaxCharCount(), this);
}
private FlapDisplayTileEntity getController(DataGathererContext context) {
BlockEntity teIn = context.getTargetTE();
if (!(teIn instanceof FlapDisplayTileEntity te))
return null;
return te.getController();
}
@Override
@OnlyIn(Dist.CLIENT)
public AABB getMultiblockBounds(LevelAccessor level, BlockPos pos) {
AABB baseShape = super.getMultiblockBounds(level, pos);
BlockEntity te = level.getBlockEntity(pos);
if (!(te instanceof FlapDisplayTileEntity fdte))
return baseShape;
FlapDisplayTileEntity controller = fdte.getController();
if (controller == null)
return baseShape;
Vec3i normal = controller.getDirection()
.getClockWise()
.getNormal();
return baseShape.move(controller.getBlockPos()
.subtract(pos))
.expandTowards(normal.getX() * (controller.xSize - 1), 1 - controller.ySize,
normal.getZ() * (controller.xSize - 1));
}
}

View file

@ -0,0 +1,85 @@
package com.simibubi.create.content.logistics.block.data.target;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
public class LecternDataTarget extends DataGathererTarget {
@Override
public void acceptText(int line, List<MutableComponent> text, DataGathererContext context) {
BlockEntity te = context.getTargetTE();
if (!(te instanceof LecternBlockEntity lectern))
return;
ItemStack book = lectern.getBook();
if (book.isEmpty())
return;
if (book.is(Items.WRITABLE_BOOK))
lectern.setBook(book = signBook(book));
if (!book.is(Items.WRITTEN_BOOK))
return;
ListTag tag = book.getTag()
.getList("pages", Tag.TAG_STRING);
boolean changed = false;
for (int i = 0; i - line < text.size() && i < 50; i++) {
if (tag.size() <= i)
tag.add(StringTag.valueOf(i < line ? "" : Component.Serializer.toJson(text.get(i - line))));
else if (i >= line) {
if (i - line == 0)
reserve(i, lectern, context);
if (i - line > 0 && isReserved(i - line, lectern, context))
break;
tag.set(i, StringTag.valueOf(Component.Serializer.toJson(text.get(i - line))));
}
changed = true;
}
book.getTag()
.put("pages", tag);
lectern.setBook(book);
if (changed && context.level()instanceof Level level)
level.sendBlockUpdated(context.getTargetPos(), lectern.getBlockState(), lectern.getBlockState(), 2);
}
@Override
public DataTargetStats provideStats(DataGathererContext context) {
return new DataTargetStats(50, 256, this);
}
public Component getLineOptionText(int line) {
return Lang.translate("data_target.page", line + 1);
}
private ItemStack signBook(ItemStack book) {
ItemStack written = new ItemStack(Items.WRITTEN_BOOK);
CompoundTag compoundtag = book.getTag();
if (compoundtag != null)
written.setTag(compoundtag.copy());
written.addTagElement("author", StringTag.valueOf("Data Gatherer"));
written.addTagElement("filtered_title", StringTag.valueOf("Printed Book"));
written.addTagElement("title", StringTag.valueOf("Printed Book"));
return written;
}
}

View file

@ -0,0 +1,60 @@
package com.simibubi.create.content.logistics.block.data.target;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.content.logistics.block.redstone.NixieTubeBlock;
import com.simibubi.create.content.logistics.block.redstone.NixieTubeTileEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class NixieTubeDataTarget extends SingleLineDataTarget {
@Override
protected void acceptLine(MutableComponent text, DataGathererContext context) {
String tagElement = Component.Serializer.toJson(text);
NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), (currentPos, rowPosition) -> {
BlockEntity blockEntity = context.level()
.getBlockEntity(currentPos);
if (blockEntity instanceof NixieTubeTileEntity nixie)
nixie.displayCustomText(tagElement, rowPosition);
});
}
@Override
protected int getWidth(DataGathererContext context) {
MutableInt count = new MutableInt(0);
NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), (currentPos, rowPosition) -> count.add(2));
return count.intValue();
}
@Override
@OnlyIn(Dist.CLIENT)
public AABB getMultiblockBounds(LevelAccessor level, BlockPos pos) {
MutableObject<BlockPos> start = new MutableObject<>(null);
MutableObject<BlockPos> end = new MutableObject<>(null);
NixieTubeBlock.walkNixies(level, pos, (currentPos, rowPosition) -> {
end.setValue(currentPos);
if (start.getValue() == null)
start.setValue(currentPos);
});
BlockPos diffToCurrent = start.getValue()
.subtract(pos);
BlockPos diff = end.getValue()
.subtract(start.getValue());
return super.getMultiblockBounds(level, pos).move(diffToCurrent)
.expandTowards(Vec3.atLowerCornerOf(diff));
}
}

View file

@ -0,0 +1,40 @@
package com.simibubi.create.content.logistics.block.data.target;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
public class SignDataTarget extends DataGathererTarget {
@Override
public void acceptText(int line, List<MutableComponent> text, DataGathererContext context) {
BlockEntity te = context.getTargetTE();
if (!(te instanceof SignBlockEntity sign))
return;
boolean changed = false;
for (int i = 0; i < text.size() && i + line < 4; i++) {
if (i == 0)
reserve(i + line, sign, context);
if (i > 0 && isReserved(i + line, sign, context))
break;
sign.setMessage(i + line, text.get(i));
changed = true;
}
if (changed && context.level()instanceof Level level)
level.sendBlockUpdated(context.getTargetPos(), sign.getBlockState(), sign.getBlockState(), 2);
}
@Override
public DataTargetStats provideStats(DataGathererContext context) {
return new DataTargetStats(4, 15, this);
}
}

View file

@ -0,0 +1,32 @@
package com.simibubi.create.content.logistics.block.data.target;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.DataGathererContext;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
public abstract class SingleLineDataTarget extends DataGathererTarget {
@Override
public final void acceptText(int line, List<MutableComponent> text, DataGathererContext context) {
acceptLine(text.get(0), context);
}
protected abstract void acceptLine(MutableComponent text, DataGathererContext context);
@Override
public final DataTargetStats provideStats(DataGathererContext context) {
return new DataTargetStats(1, getWidth(context), this);
}
@Override
public Component getLineOptionText(int line) {
return Lang.translate("data_target.single_line");
}
protected abstract int getWidth(DataGathererContext context);
}

View file

@ -254,12 +254,12 @@ public class EjectorTargetHandler {
drawOutline(currentSelection);
}
private static void drawOutline(BlockPos selection) {
public static void drawOutline(BlockPos selection) {
Level world = Minecraft.getInstance().level;
if (currentSelection == null)
if (selection == null)
return;
BlockPos pos = currentSelection;
BlockPos pos = selection;
BlockState state = world.getBlockState(pos);
VoxelShape shape = state.getShape(world, pos);
AABB boundingBox = shape.isEmpty() ? new AABB(BlockPos.ZERO) : shape.bounds();

View file

@ -1,37 +1,64 @@
package com.simibubi.create.content.logistics.block.diodes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.ITE;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.BlockHitResult;
public class BrassDiodeBlock extends AbstractDiodeBlock implements ITE<BrassDiodeTileEntity> {
public static final BooleanProperty POWERING = BooleanProperty.create("powering");
public static final BooleanProperty INVERTED = BooleanProperty.create("inverted");
public BrassDiodeBlock(Properties properties) {
super(properties);
registerDefaultState(defaultBlockState().setValue(POWERED, false)
.setValue(POWERING, false));
.setValue(POWERING, false)
.setValue(INVERTED, false));
}
@Override
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player player, InteractionHand pHand,
BlockHitResult pHit) {
if (!player.mayBuild())
return InteractionResult.PASS;
if (player.isShiftKeyDown())
return InteractionResult.PASS;
if (AllItems.WRENCH.isIn(player.getItemInHand(pHand)))
return InteractionResult.PASS;
if (pLevel.isClientSide)
return InteractionResult.SUCCESS;
pLevel.setBlock(pPos, pState.cycle(INVERTED), 3);
float f = !pState.getValue(INVERTED) ? 0.6F : 0.5F;
pLevel.playSound(null, pPos, SoundEvents.LEVER_CLICK, SoundSource.BLOCKS, 0.3F, f);
return InteractionResult.SUCCESS;
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
builder.add(POWERED, POWERING, FACING);
builder.add(POWERED, POWERING, FACING, INVERTED);
super.createBlockStateDefinition(builder);
}
@Override
protected int getOutputSignal(BlockGetter worldIn, BlockPos pos, BlockState state) {
return state.getValue(POWERING) ? 15 : 0;
return state.getValue(POWERING) ^ state.getValue(INVERTED) ? 15 : 0;
}
@Override

View file

@ -33,7 +33,7 @@ public class BrassDiodeGenerator extends AbstractDiodeGenerator {
@Override
protected int getModelIndex(BlockState state) {
return (state.getValue(BrassDiodeBlock.POWERING) ? 2 : 0)
return (state.getValue(BrassDiodeBlock.POWERING) ^ state.getValue(BrassDiodeBlock.INVERTED) ? 2 : 0)
+ (state.getValue(BrassDiodeBlock.POWERED) ? 1 : 0);
}

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.logistics.block.redstone;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.Random;
import java.util.function.BiConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
@ -16,6 +17,8 @@ import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@ -78,7 +81,28 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock
if (!display && dye == null)
return InteractionResult.PASS;
if (world.isClientSide)
return InteractionResult.SUCCESS;
CompoundTag tag = heldItem.getTagElement("display");
String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null;
walkNixies(world, pos, (currentPos, rowPosition) -> {
if (display)
withTileEntityDo(world, currentPos, te -> te.displayCustomText(tagElement, rowPosition));
if (dye != null)
world.setBlockAndUpdate(currentPos, withColor(state, dye));
});
return InteractionResult.SUCCESS;
}
public static void walkNixies(LevelAccessor world, BlockPos start, BiConsumer<BlockPos, Integer> callback) {
BlockState state = world.getBlockState(start);
if (!(state.getBlock() instanceof NixieTubeBlock))
return;
BlockPos currentPos = start;
Direction left = state.getValue(FACING)
.getOpposite();
@ -89,10 +113,6 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock
Direction right = left.getOpposite();
if (world.isClientSide)
return InteractionResult.SUCCESS;
BlockPos currentPos = pos;
while (true) {
BlockPos nextPos = currentPos.relative(left);
if (!areNixieBlocksEqual(world.getBlockState(nextPos), state))
@ -104,20 +124,13 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock
while (true) {
final int rowPosition = index;
if (display)
withTileEntityDo(world, currentPos, te -> te.displayCustomNameOf(heldItem, rowPosition));
if (dye != null)
world.setBlockAndUpdate(currentPos, withColor(state, dye));
callback.accept(currentPos, rowPosition);
BlockPos nextPos = currentPos.relative(right);
if (!areNixieBlocksEqual(world.getBlockState(nextPos), state))
break;
currentPos = nextPos;
index++;
}
return InteractionResult.SUCCESS;
}
@Override

View file

@ -1,9 +1,7 @@
package com.simibubi.create.content.logistics.block.redstone;
import java.util.Map;
import java.util.Random;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlockPartials;
@ -15,6 +13,7 @@ import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.DyeHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.client.Minecraft;
@ -33,32 +32,7 @@ import net.minecraft.world.phys.Vec3;
public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntity> {
private Random r = new Random();
public static final Map<DyeColor, Couple<Integer>> DYE_TABLE = new ImmutableMap.Builder<DyeColor, Couple<Integer>>()
// DyeColor, ( Front RGB, Back RGB )
.put(DyeColor.BLACK, Couple.create(0x45403B, 0x21201F))
.put(DyeColor.RED, Couple.create(0xB13937, 0x632737))
.put(DyeColor.GREEN, Couple.create(0x208A46, 0x1D6045))
.put(DyeColor.BROWN, Couple.create(0xAC855C, 0x68533E))
.put(DyeColor.BLUE, Couple.create(0x5391E1, 0x504B90))
.put(DyeColor.GRAY, Couple.create(0x5D666F, 0x313538))
.put(DyeColor.LIGHT_GRAY, Couple.create(0x95969B, 0x707070))
.put(DyeColor.PURPLE, Couple.create(0x9F54AE, 0x63366C))
.put(DyeColor.CYAN, Couple.create(0x3EABB4, 0x3C7872))
.put(DyeColor.PINK, Couple.create(0xD5A8CB, 0xB86B95))
.put(DyeColor.LIME, Couple.create(0xA3DF55, 0x4FB16F))
.put(DyeColor.YELLOW, Couple.create(0xE6D756, 0xE9AC29))
.put(DyeColor.LIGHT_BLUE, Couple.create(0x69CED2, 0x508AA5))
.put(DyeColor.ORANGE, Couple.create(0xEE9246, 0xD94927))
.put(DyeColor.MAGENTA, Couple.create(0xF062B0, 0xC04488))
.put(DyeColor.WHITE, Couple.create(0xEDEAE5, 0xBBB6B0))
.build();
private static Random r = new Random();
public NixieTubeRenderer(BlockEntityRendererProvider.Context context) {}
@ -107,12 +81,12 @@ public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntit
ms.popPose();
}
private void drawTube(PoseStack ms, MultiBufferSource buffer, String c, float height, DyeColor color) {
public static void drawTube(PoseStack ms, MultiBufferSource buffer, String c, float height, DyeColor color) {
Font fontRenderer = Minecraft.getInstance().font;
float charWidth = fontRenderer.width(c);
float shadowOffset = .5f;
float flicker = r.nextFloat();
Couple<Integer> couple = DYE_TABLE.get(color);
Couple<Integer> couple = DyeHelper.DYE_TABLE.get(color);
int brightColor = couple.getFirst();
int darkColor = couple.getSecond();
int flickeringBrightColor = Color.mixColors(brightColor, darkColor, flicker / 4);

View file

@ -2,46 +2,32 @@ package com.simibubi.create.content.logistics.block.redstone;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity.SignalState;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.DynamicComponent;
import net.minecraft.commands.CommandSource;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
public class NixieTubeTileEntity extends SmartTileEntity {
private static final Couple<String> EMPTY = Couple.create("", "");
private boolean hasCustomText;
private int redstoneStrength;
private JsonElement rawCustomText;
private int customTextIndex;
private Component parsedCustomText;
private Optional<DynamicComponent> customText;
private int nixieIndex;
private Couple<String> displayedStrings;
private WeakReference<SignalTileEntity> cachedSignalTE;
@ -49,7 +35,7 @@ public class NixieTubeTileEntity extends SmartTileEntity {
public NixieTubeTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
hasCustomText = false;
customText = Optional.empty();
redstoneStrength = 0;
cachedSignalTE = new WeakReference<>(null);
}
@ -57,32 +43,23 @@ public class NixieTubeTileEntity extends SmartTileEntity {
@Override
public void tick() {
super.tick();
if (!level.isClientSide)
return;
// Dynamic text components have to be ticked manually and re-sent to the client
if (level instanceof ServerLevel && hasCustomText) {
Couple<String> currentStrings = displayedStrings;
parsedCustomText = parseCustomText();
updateDisplayedStrings();
if (currentStrings == null || !currentStrings.equals(displayedStrings))
sendData();
}
signalState = null;
SignalTileEntity signalTileEntity = cachedSignalTE.get();
if (level.isClientSide) {
signalState = null;
SignalTileEntity signalTileEntity = cachedSignalTE.get();
if (signalTileEntity == null || signalTileEntity.isRemoved()) {
Direction facing = NixieTubeBlock.getFacing(getBlockState());
BlockEntity blockEntity = level.getBlockEntity(worldPosition.relative(facing.getOpposite()));
if (blockEntity instanceof SignalTileEntity signal) {
signalState = signal.getState();
cachedSignalTE = new WeakReference<>(signal);
}
return;
if (signalTileEntity == null || signalTileEntity.isRemoved()) {
Direction facing = NixieTubeBlock.getFacing(getBlockState());
BlockEntity blockEntity = level.getBlockEntity(worldPosition.relative(facing.getOpposite()));
if (blockEntity instanceof SignalTileEntity signal) {
signalState = signal.getState();
cachedSignalTE = new WeakReference<>(signal);
}
signalState = signalTileEntity.getState();
return;
}
signalState = signalTileEntity.getState();
}
@Override
@ -94,7 +71,7 @@ public class NixieTubeTileEntity extends SmartTileEntity {
//
public boolean reactsToRedstone() {
return !hasCustomText;
return customText.isEmpty();
}
public Couple<String> getDisplayedStrings() {
@ -103,40 +80,47 @@ public class NixieTubeTileEntity extends SmartTileEntity {
return displayedStrings;
}
public MutableComponent getFullText() {
return customText.map(DynamicComponent::get)
.orElse(new TextComponent("" + redstoneStrength));
}
public void updateRedstoneStrength(int signalStrength) {
clearCustomText();
redstoneStrength = signalStrength;
DataGathererBlock.notifyGatherers(level, worldPosition);
notifyUpdate();
}
public void displayCustomNameOf(ItemStack stack, int nixiePositionInRow) {
CompoundTag compoundnbt = stack.getTagElement("display");
if (compoundnbt != null && compoundnbt.contains("Name", Tag.TAG_STRING)) {
hasCustomText = true;
rawCustomText = getJsonFromString(compoundnbt.getString("Name"));
customTextIndex = nixiePositionInRow;
parsedCustomText = parseCustomText();
notifyUpdate();
}
public void displayCustomText(String tagElement, int nixiePositionInRow) {
if (tagElement == null)
return;
if (customText.filter(d -> d.sameAs(tagElement))
.isPresent())
return;
DynamicComponent component = customText.orElseGet(DynamicComponent::new);
component.displayCustomText(level, worldPosition, tagElement);
customText = Optional.of(component);
nixieIndex = nixiePositionInRow;
DataGathererBlock.notifyGatherers(level, worldPosition);
notifyUpdate();
}
public void updateDisplayedStrings() {
if (signalState != null)
return;
if (!hasCustomText) {
displayedStrings = Couple.create(redstoneStrength < 10 ? "0" : "1", String.valueOf(redstoneStrength % 10));
} else {
String fullText = parsedCustomText.getString();
int index = customTextIndex * 2;
displayedStrings = Couple.create(charOrEmpty(fullText, index), charOrEmpty(fullText, index + 1));
}
customText.map(DynamicComponent::resolve)
.ifPresentOrElse(
fullText -> displayedStrings =
Couple.create(charOrEmpty(fullText, nixieIndex * 2), charOrEmpty(fullText, nixieIndex * 2 + 1)),
() -> displayedStrings =
Couple.create(redstoneStrength < 10 ? "0" : "1", String.valueOf(redstoneStrength % 10)));
}
public void clearCustomText() {
hasCustomText = false;
rawCustomText = null;
customTextIndex = 0;
parsedCustomText = null;
nixieIndex = 0;
customText = Optional.empty();
}
//
@ -145,34 +129,21 @@ public class NixieTubeTileEntity extends SmartTileEntity {
protected void read(CompoundTag nbt, boolean clientPacket) {
super.read(nbt, clientPacket);
if (nbt.contains("RawCustomText", Tag.TAG_STRING)) {
rawCustomText = getJsonFromString(nbt.getString("RawCustomText"));
// Check if string forms valid JSON
if (rawCustomText != null && !rawCustomText.isJsonNull()) {
Component deserializedComponent = parseCustomText();
// Check if JSON forms valid component
if (deserializedComponent != null) {
try {
// Try to deserialize previously parsed component
parsedCustomText = Component.Serializer.fromJson(nbt.getString("CustomText"));
} catch (JsonParseException e) {
//
}
if (parsedCustomText == null) {
// Use test component to ensure field isn't null
parsedCustomText = deserializedComponent;
}
hasCustomText = true;
customTextIndex = nbt.getInt("CustomTextIndex");
}
if (nbt.contains("CustomText")) {
DynamicComponent component = customText.orElseGet(DynamicComponent::new);
component.read(level, worldPosition, nbt);
if (component.isValid()) {
customText = Optional.of(component);
nixieIndex = nbt.getInt("CustomTextIndex");
} else {
customText = Optional.empty();
nixieIndex = 0;
}
}
if (!hasCustomText) {
clearCustomText();
if (customText.isEmpty())
redstoneStrength = nbt.getInt("RedstoneStrength");
}
if (clientPacket)
updateDisplayedStrings();
}
@ -181,57 +152,18 @@ public class NixieTubeTileEntity extends SmartTileEntity {
protected void write(CompoundTag nbt, boolean clientPacket) {
super.write(nbt, clientPacket);
if (hasCustomText) {
nbt.putString("RawCustomText", rawCustomText.toString());
nbt.putInt("CustomTextIndex", customTextIndex);
nbt.putString("CustomText", Component.Serializer.toJson(parsedCustomText));
} else {
if (customText.isPresent()) {
nbt.putInt("CustomTextIndex", nixieIndex);
customText.get()
.write(nbt);
} else
nbt.putInt("RedstoneStrength", redstoneStrength);
}
}
private JsonElement getJsonFromString(String string) {
try {
return JsonParser.parseString(string);
} catch (JsonParseException e) {
return null;
}
}
private String charOrEmpty(String string, int index) {
return string.length() <= index ? " " : string.substring(index, index + 1);
}
protected Component parseCustomText() {
try {
return parseDynamicComponent(Component.Serializer.fromJson(rawCustomText));
} catch (JsonParseException e) {
return null;
}
}
protected Component parseDynamicComponent(Component customText) {
if (level instanceof ServerLevel) {
try {
return ComponentUtils.updateForEntity(getCommandSource(null), customText, null, 0);
} catch (CommandSyntaxException e) {
//
}
}
return customText;
}
// From SignTileEntity
public CommandSourceStack getCommandSource(@Nullable ServerPlayer p_195539_1_) {
String s = p_195539_1_ == null ? "Nixie Tube"
: p_195539_1_.getName()
.getString();
Component itextcomponent =
(Component) (p_195539_1_ == null ? new TextComponent("Nixie Tube") : p_195539_1_.getDisplayName());
return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(this.worldPosition), Vec2.ZERO,
(ServerLevel) this.level, 2, s, itextcomponent, this.level.getServer(), p_195539_1_);
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.block.redstone;
import java.util.List;
import com.simibubi.create.content.logistics.block.data.DataGathererBlock;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
@ -145,8 +146,10 @@ public class StockpileSwitchTileEntity extends SmartTileEntity {
if (update)
scheduleBlockTick();
if (changed || update)
sendData();
if (changed || update) {
DataGathererBlock.notifyGatherers(level, worldPosition);
notifyUpdate();
}
}
protected void scheduleBlockTick() {

View file

@ -19,6 +19,7 @@ import com.simibubi.create.AllKeys;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainPacket;
import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.foundation.networking.AllPackets;
@ -91,6 +92,7 @@ public class GlobalRailwayManager {
sync = new TrackGraphSync();
movingTrains = new LinkedList<>();
waitingTrains = new LinkedList<>();
GlobalTrainDisplayData.statusByDestination.clear();
}
public void markTracksDirty() {
@ -167,15 +169,19 @@ public class GlobalRailwayManager {
group.trains.clear();
group.reserved = null;
}
for (TrackGraph graph : trackNetworks.values())
graph.tickPoints(true);
tickTrains(level);
for (TrackGraph graph : trackNetworks.values())
graph.tickPoints(false);
GlobalTrainDisplayData.updateTick = level.getGameTime() % 100 == 0;
if (GlobalTrainDisplayData.updateTick)
GlobalTrainDisplayData.refresh();
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K))
// trackNetworks.values()
// .forEach(TrackGraph::debugViewReserved);

View file

@ -50,6 +50,7 @@ public class Navigation {
public GlobalStation destination;
public double distanceToDestination;
public double distanceStartedAt;
public boolean destinationBehindTrain;
List<Couple<TrackNode>> currentPath;
@ -135,7 +136,8 @@ public class Navigation {
MutableObject<Pair<UUID, Boolean>> trackingCrossSignal = new MutableObject<>(null);
waitingForChainedGroups.clear();
// Adding 50 to the distance due to unresolved inaccuracies in TravellingPoint::travel
// Adding 50 to the distance due to unresolved inaccuracies in
// TravellingPoint::travel
signalScout.travel(train.graph, (distanceToDestination + 50) * speedMod, controlSignalScout(),
(distance, couple) -> {
// > scanDistance and not following down a cross signal
@ -358,7 +360,7 @@ public class Navigation {
distanceToDestination = distance;
if (noneFound) {
distanceToDestination = 0;
distanceToDestination = distanceStartedAt = 0;
currentPath = new ArrayList<>();
if (this.destination != null)
cancelNavigation();
@ -370,6 +372,9 @@ public class Navigation {
train.reservedSignalBlocks.clear();
train.navigation.waitingForSignal = null;
if (this.destination == null && !simulate)
distanceStartedAt = distance;
if (this.destination == destination)
return 0;
@ -677,6 +682,7 @@ public class Navigation {
return tag;
tag.putUUID("Destination", destination.id);
tag.putDouble("DistanceToDestination", distanceToDestination);
tag.putDouble("DistanceStartedAt", distanceStartedAt);
tag.putBoolean("BehindTrain", destinationBehindTrain);
tag.put("Path", NBTHelper.writeCompoundList(currentPath, c -> {
CompoundTag nbt = new CompoundTag();
@ -700,6 +706,7 @@ public class Navigation {
if (destination == null)
return;
distanceToDestination = tag.getDouble("DistanceToDestination");
distanceStartedAt = tag.getDouble("DistanceStartedAt");
destinationBehindTrain = tag.getBoolean("BehindTrain");
currentPath.clear();
NBTHelper.iterateCompoundList(tag.getList("Path", Tag.TAG_COMPOUND),

View file

@ -0,0 +1,300 @@
package com.simibubi.create.content.logistics.trains.management.display;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.Random;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.LevelTickAccess;
public class FlapDisplayBlock extends HorizontalKineticBlock
implements ITE<FlapDisplayTileEntity>, IWrenchable, ICogWheel, SimpleWaterloggedBlock {
public static final BooleanProperty UP = BooleanProperty.create("up");
public static final BooleanProperty DOWN = BooleanProperty.create("down");
public FlapDisplayBlock(Properties p_49795_) {
super(p_49795_);
registerDefaultState(defaultBlockState().setValue(UP, false)
.setValue(DOWN, false)
.setValue(WATERLOGGED, false));
}
@Override
protected boolean areStatesKineticallyEquivalent(BlockState oldState, BlockState newState) {
return false;
}
@Override
public Axis getRotationAxis(BlockState state) {
return state.getValue(HORIZONTAL_FACING)
.getAxis();
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder.add(UP, DOWN, WATERLOGGED));
}
@Override
public SpeedLevel getMinimumRequiredSpeedLevel() {
return SpeedLevel.MEDIUM;
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
Direction face = context.getClickedFace();
BlockPos clickedPos = context.getClickedPos();
BlockPos placedOnPos = clickedPos.relative(face.getOpposite());
Level level = context.getLevel();
BlockState blockState = level.getBlockState(placedOnPos);
BlockState stateForPlacement = defaultBlockState();
FluidState ifluidstate = context.getLevel()
.getFluidState(context.getClickedPos());
if ((blockState.getBlock() != this) || (context.getPlayer() != null && context.getPlayer()
.isShiftKeyDown()))
stateForPlacement = super.getStateForPlacement(context);
else {
Direction otherFacing = blockState.getValue(HORIZONTAL_FACING);
stateForPlacement = stateForPlacement.setValue(HORIZONTAL_FACING, otherFacing);
}
return updateColumn(level, clickedPos,
stateForPlacement.setValue(WATERLOGGED, Boolean.valueOf(ifluidstate.getType() == Fluids.WATER)), true);
}
@Override
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult ray) {
if (player.isShiftKeyDown())
return InteractionResult.PASS;
ItemStack heldItem = player.getItemInHand(hand);
FlapDisplayTileEntity flapTe = getTileEntity(world, pos);
if (flapTe == null)
return InteractionResult.PASS;
flapTe = flapTe.getController();
if (flapTe == null)
return InteractionResult.PASS;
double yCoord = ray.getLocation()
.add(Vec3.atLowerCornerOf(ray.getDirection()
.getOpposite()
.getNormal())
.scale(.125f)).y;
int lineIndex = flapTe.getLineIndexAt(yCoord);
if (heldItem.isEmpty()) {
if (!flapTe.isSpeedRequirementFulfilled())
return InteractionResult.PASS;
flapTe.applyTextManually(lineIndex, null);
return InteractionResult.SUCCESS;
}
boolean display = heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName();
DyeColor dye = DyeColor.getColor(heldItem);
if (!display && dye == null)
return InteractionResult.PASS;
if (dye == null && !flapTe.isSpeedRequirementFulfilled())
return InteractionResult.PASS;
if (world.isClientSide)
return InteractionResult.SUCCESS;
CompoundTag tag = heldItem.getTagElement("display");
String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null;
if (display)
flapTe.applyTextManually(lineIndex, tagElement);
if (dye != null)
flapTe.setColour(lineIndex, dye);
return InteractionResult.SUCCESS;
}
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return AllShapes.FLAP_DISPLAY.get(pState.getValue(HORIZONTAL_FACING));
}
@Override
public Class<FlapDisplayTileEntity> getTileEntityClass() {
return FlapDisplayTileEntity.class;
}
@Override
public BlockEntityType<? extends FlapDisplayTileEntity> getTileEntityType() {
return AllTileEntities.FLAP_DISPLAY.get();
}
@Override
public float getParticleTargetRadius() {
return .85f;
}
@Override
public float getParticleInitialRadius() {
return .75f;
}
private BlockState updateColumn(Level level, BlockPos pos, BlockState state, boolean present) {
MutableBlockPos currentPos = new MutableBlockPos();
Axis axis = getConnectionAxis(state);
for (Direction connection : Iterate.directions) {
if (connection.getAxis() == axis)
continue;
boolean connect = true;
Move: for (Direction movement : Iterate.directionsInAxis(axis)) {
currentPos.set(pos);
for (int i = 0; i < 1000; i++) {
if (!level.isAreaLoaded(currentPos, 1))
break;
BlockState other1 = currentPos.equals(pos) ? state : level.getBlockState(currentPos);
BlockState other2 = level.getBlockState(currentPos.relative(connection));
boolean col1 = canConnect(state, other1);
boolean col2 = canConnect(state, other2);
currentPos.move(movement);
if (!col1 && !col2)
break;
if (col1 && col2)
continue;
connect = false;
break Move;
}
}
state = setConnection(state, connection, connect);
}
return state;
}
@Override
public void onPlace(BlockState pState, Level pLevel, BlockPos pPos, BlockState pOldState, boolean pIsMoving) {
if (pOldState.getBlock() == this)
return;
LevelTickAccess<Block> blockTicks = pLevel.getBlockTicks();
if (!blockTicks.hasScheduledTick(pPos, this))
pLevel.scheduleTick(pPos, this, 1);
}
@Override
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, Random pRandom) {
if (pState.getBlock() != this)
return;
BlockPos belowPos =
pPos.relative(Direction.fromAxisAndDirection(getConnectionAxis(pState), AxisDirection.NEGATIVE));
BlockState belowState = pLevel.getBlockState(belowPos);
if (!canConnect(pState, belowState))
KineticTileEntity.switchToBlockState(pLevel, pPos, updateColumn(pLevel, pPos, pState, true));
withTileEntityDo(pLevel, pPos, FlapDisplayTileEntity::updateControllerStatus);
}
@Override
public BlockState updateShape(BlockState state, Direction pDirection, BlockState pNeighborState,
LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pNeighborPos) {
BlockState newState = updatedShapeInner(state, pDirection, pNeighborState, pLevel, pCurrentPos);
if (pLevel instanceof Level level)
KineticTileEntity.switchToBlockState(level, pCurrentPos, newState);
return newState;
}
private BlockState updatedShapeInner(BlockState state, Direction pDirection, BlockState pNeighborState,
LevelAccessor pLevel, BlockPos pCurrentPos) {
if (state.getValue(BlockStateProperties.WATERLOGGED))
pLevel.scheduleTick(pCurrentPos, Fluids.WATER, Fluids.WATER.getTickDelay(pLevel));
if (!canConnect(state, pNeighborState))
return setConnection(state, pDirection, false);
if (pDirection.getAxis() == getConnectionAxis(state))
return withPropertiesOf(pNeighborState).setValue(WATERLOGGED, state.getValue(WATERLOGGED));
return setConnection(state, pDirection, getConnection(pNeighborState, pDirection.getOpposite()));
}
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getSource(false)
: Fluids.EMPTY.defaultFluidState();
}
protected boolean canConnect(BlockState state, BlockState other) {
return other.getBlock() == this && state.getValue(HORIZONTAL_FACING) == other.getValue(HORIZONTAL_FACING);
}
protected Axis getConnectionAxis(BlockState state) {
return state.getValue(HORIZONTAL_FACING)
.getClockWise()
.getAxis();
}
public static boolean getConnection(BlockState state, Direction side) {
BooleanProperty property = side == Direction.DOWN ? DOWN : side == Direction.UP ? UP : null;
return property != null && state.getValue(property);
}
public static BlockState setConnection(BlockState state, Direction side, boolean connect) {
BooleanProperty property = side == Direction.DOWN ? DOWN : side == Direction.UP ? UP : null;
if (property != null)
state = state.setValue(property, connect);
return state;
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
if (pState.hasBlockEntity() && (!pState.is(pNewState.getBlock()) || !pNewState.hasBlockEntity()))
pLevel.removeBlockEntity(pPos);
if (pIsMoving || pNewState.getBlock() == this)
return;
for (Direction d : Iterate.directionsInAxis(getConnectionAxis(pState))) {
BlockPos relative = pPos.relative(d);
BlockState adjacent = pLevel.getBlockState(relative);
if (canConnect(pState, adjacent))
KineticTileEntity.switchToBlockState(pLevel, relative, updateColumn(pLevel, relative, adjacent, false));
}
}
}

View file

@ -0,0 +1,63 @@
package com.simibubi.create.content.logistics.trains.management.display;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.mutable.MutableInt;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
public class FlapDisplayLayout {
List<FlapDisplaySection> sections;
String layoutKey;
public FlapDisplayLayout(int maxCharCount) {
loadDefault(maxCharCount);
}
public void loadDefault(int maxCharCount) {
configure("Default", Arrays
.asList(new FlapDisplaySection(maxCharCount * FlapDisplaySection.MONOSPACE, "alphabet", false, false)));
}
public boolean isLayout(String key) {
return layoutKey.equals(key);
}
public void configure(String layoutKey, List<FlapDisplaySection> sections) {
this.layoutKey = layoutKey;
this.sections = sections;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putString("Key", layoutKey);
tag.put("Sections", NBTHelper.writeCompoundList(sections, FlapDisplaySection::write));
return tag;
};
public void read(CompoundTag tag) {
String prevKey = layoutKey;
layoutKey = tag.getString("Key");
ListTag sectionsTag = tag.getList("Sections", Tag.TAG_COMPOUND);
if (!prevKey.equals(layoutKey)) {
sections = NBTHelper.readCompoundList(sectionsTag, FlapDisplaySection::load);
return;
}
MutableInt index = new MutableInt(0);
NBTHelper.iterateCompoundList(sectionsTag, nbt -> sections.get(index.getAndIncrement())
.update(nbt));
}
public List<FlapDisplaySection> getSections() {
return sections;
}
}

View file

@ -0,0 +1,244 @@
package com.simibubi.create.content.logistics.trains.management.display;
import java.util.List;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.font.GlyphInfo;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.PoseStack.Pose;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.DyeHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.font.FontSet;
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
import net.minecraft.client.gui.font.glyphs.EmptyGlyph;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.util.FormattedCharSink;
import net.minecraft.util.StringDecomposer;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class FlapDisplayRenderer extends KineticTileEntityRenderer {
public FlapDisplayRenderer(BlockEntityRendererProvider.Context context) {
super(context);
}
@Override
protected void renderSafe(KineticTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer,
int light, int overlay) {
super.renderSafe(te, partialTicks, ms, buffer, light, overlay);
Font fontRenderer = Minecraft.getInstance().font;
FontSet fontSet = fontRenderer.getFontSet(Style.DEFAULT_FONT);
float scale = 1 / 32f;
if (!(te instanceof FlapDisplayTileEntity flapTe))
return;
if (!flapTe.isController)
return;
List<FlapDisplayLayout> lines = flapTe.getLines();
ms.pushPose();
TransformStack.cast(ms)
.centre()
.rotateY(AngleHelper.horizontalAngle(te.getBlockState()
.getValue(FlapDisplayBlock.HORIZONTAL_FACING)))
.unCentre()
.translate(0, 0, -3 / 16f);
ms.translate(0, 1, 1);
ms.scale(scale, scale, scale);
ms.scale(1, -1, 1);
ms.translate(0, 0, 1 / 2f);
for (int j = 0; j < lines.size(); j++) {
List<FlapDisplaySection> line = lines.get(j)
.getSections();
int color = flapTe.colour[j] == null ? 0xFF_D3C6BA
: DyeHelper.DYE_TABLE.get(flapTe.colour[j])
.getFirst() | 0xFF_000000;
ms.pushPose();
float w = 0;
for (FlapDisplaySection section : line) {
w += section.getSize() + (section.hasGap ? 8 : 1);
}
ms.translate(flapTe.xSize * 16 - w / 2 + 1, 4, 0);
Pose transform = ms.last();
FlapDisplayRenderOutput renderOutput = new FlapDisplayRenderOutput(buffer, color, transform.pose(), light,
j, !te.isSpeedRequirementFulfilled());
for (int i = 0; i < line.size(); i++) {
FlapDisplaySection section = line.get(i);
renderOutput.nextSection(section);
int ticks = AnimationTickHolder.getTicks();
String text = section.renderCharsIndividually() || !section.spinning[0] ? section.text
: section.cyclingOptions[((ticks / 3) + i * 13) % section.cyclingOptions.length];
StringDecomposer.iterateFormatted(text, Style.EMPTY, renderOutput);
ms.translate(0, 0, -1 / 4f);
renderOutput.finish(0x55000000);
ms.translate(0, 0, 1 / 4f);
ms.translate(section.size + (section.hasGap ? 8 : 1), 0, 0);
}
if (buffer instanceof BufferSource bs) {
BakedGlyph texturedglyph = fontSet.whiteGlyph();
bs.endBatch(texturedglyph.renderType(Font.DisplayMode.NORMAL));
}
ms.popPose();
ms.translate(0, 16, 0);
}
ms.popPose();
}
@OnlyIn(Dist.CLIENT)
class FlapDisplayRenderOutput implements FormattedCharSink {
final MultiBufferSource bufferSource;
final float r, g, b, a;
final Matrix4f pose;
final int light;
final boolean paused;
FlapDisplaySection section;
float x;
private int lineIndex;
public FlapDisplayRenderOutput(MultiBufferSource buffer, int color, Matrix4f pose, int light, int lineIndex,
boolean paused) {
this.bufferSource = buffer;
this.lineIndex = lineIndex;
this.a = (color >> 24 & 255) / 255f;
this.r = (color >> 16 & 255) / 255f;
this.g = (color >> 8 & 255) / 255f;
this.b = (color & 255) / 255f;
this.pose = pose;
this.light = light;
this.paused = paused;
}
public void nextSection(FlapDisplaySection section) {
this.section = section;
x = 0;
}
public boolean accept(int charIndex, Style style, int glyph) {
FontSet fontset = getFontSet();
int ticks = paused ? 0 : AnimationTickHolder.getTicks();
float time = paused ? 0 : AnimationTickHolder.getRenderTime();
float dim = 1;
if (section.renderCharsIndividually() && section.spinning[Math.min(charIndex, section.spinning.length)]) {
float speed = section.spinningTicks > 5 && section.spinningTicks < 20 ? 1.75f : 2.5f;
float cycle = (time / speed) + charIndex * 16.83f + lineIndex * 0.75f;
float partial = cycle % 1;
char cyclingGlyph = section.cyclingOptions[((int) cycle) % section.cyclingOptions.length].charAt(0);
glyph = paused ? cyclingGlyph : partial > 1 / 2f ? partial > 3 / 4f ? '_' : '-' : cyclingGlyph;
dim = 0.75f;
}
GlyphInfo glyphinfo = fontset.getGlyphInfo(glyph);
float glyphWidth = glyphinfo.getAdvance(false);
if (!section.renderCharsIndividually() && section.spinning[0]) {
glyph = ticks % 3 == 0 ? glyphWidth == 6 ? '-' : glyphWidth == 1 ? '\'' : glyph : glyph;
glyph = ticks % 3 == 2 ? glyphWidth == 6 ? '_' : glyphWidth == 1 ? '.' : glyph : glyph;
if (ticks % 3 != 1)
dim = 0.75f;
}
BakedGlyph bakedglyph =
style.isObfuscated() && glyph != 32 ? fontset.getRandomGlyph(glyphinfo) : fontset.getGlyph(glyph);
TextColor textcolor = style.getColor();
float red = this.r * dim;
float green = this.g * dim;
float blue = this.b * dim;
if (textcolor != null) {
int i = textcolor.getValue();
red = (i >> 16 & 255) / 255f;
green = (i >> 8 & 255) / 255f;
blue = (i & 255) / 255f;
}
float standardWidth = section.wideFlaps ? FlapDisplaySection.WIDE_MONOSPACE : FlapDisplaySection.MONOSPACE;
if (section.renderCharsIndividually())
x += (standardWidth - glyphWidth) / 2f;
if (!(bakedglyph instanceof EmptyGlyph)) {
VertexConsumer vertexconsumer = bufferSource.getBuffer(renderTypeOf(bakedglyph));
bakedglyph.render(style.isItalic(), x, 0, pose, vertexconsumer, red, green, blue, a, light);
}
if (section.renderCharsIndividually())
x += standardWidth - (standardWidth - glyphWidth) / 2f;
else
x += glyphWidth;
return true;
}
public float finish(int bgColor) {
if (bgColor == 0)
return x;
float a = (bgColor >> 24 & 255) / 255f;
float r = (bgColor >> 16 & 255) / 255f;
float g = (bgColor >> 8 & 255) / 255f;
float b = (bgColor & 255) / 255f;
BakedGlyph bakedglyph = getFontSet().whiteGlyph();
VertexConsumer vertexconsumer = bufferSource.getBuffer(renderTypeOf(bakedglyph));
bakedglyph.renderEffect(new BakedGlyph.Effect(-1f, 9f, section.size, -2f, 0.01f, r, g, b, a), this.pose,
vertexconsumer, light);
return x;
}
private FontSet getFontSet() {
return Minecraft.getInstance().font.getFontSet(Style.DEFAULT_FONT);
}
private RenderType renderTypeOf(BakedGlyph bakedglyph) {
return bakedglyph.renderType(Font.DisplayMode.NORMAL);
}
}
@Override
protected SuperByteBuffer getRotatedModel(KineticTileEntity te, BlockState state) {
return CachedBufferer.partial(AllBlockPartials.SHAFTLESS_COGWHEEL, state);
}
@Override
public boolean shouldRenderOffScreen(KineticTileEntity pBlockEntity) {
return ((FlapDisplayTileEntity) pBlockEntity).isController;
}
}

View file

@ -0,0 +1,165 @@
package com.simibubi.create.content.logistics.trains.management.display;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import com.google.common.base.Strings;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
public class FlapDisplaySection {
static final Map<String, String[]> LOADED_FLAP_CYCLES = new HashMap<>();
public static final float MONOSPACE = 7;
public static final float WIDE_MONOSPACE = 9;
float size;
boolean singleFlap;
boolean hasGap;
boolean rightAligned;
boolean wideFlaps;
String cycle;
Component component;
// Client
String[] cyclingOptions;
boolean[] spinning;
int spinningTicks;
String text;
public FlapDisplaySection(float width, String cycle, boolean singleFlap, boolean hasGap) {
this.size = width;
this.cycle = cycle;
this.hasGap = hasGap;
this.singleFlap = singleFlap;
this.spinning = new boolean[singleFlap ? 1 : (int) (width / FlapDisplaySection.MONOSPACE)];
this.text = Strings.repeat(" ", spinning.length);
this.component = null;
}
public FlapDisplaySection rightAligned() {
rightAligned = true;
return this;
}
public FlapDisplaySection wideFlaps() {
wideFlaps = true;
return this;
}
public void setText(Component component) {
this.component = component;
}
private void refresh(boolean transition) {
if (component == null)
return;
String newText = component.getString();
if (!singleFlap) {
if (rightAligned)
newText = newText.trim();
newText = newText.toUpperCase(Locale.ROOT);
newText = newText.substring(0, Math.min(spinning.length, newText.length()));
String whitespace = Strings.repeat(" ", spinning.length - newText.length());
newText = rightAligned ? whitespace + newText : newText + whitespace;
if (!text.isEmpty())
for (int i = 0; i < spinning.length; i++)
spinning[i] |= text.charAt(i) != newText.charAt(i);
} else if (!text.isEmpty())
spinning[0] |= !newText.equals(text);
text = newText;
spinningTicks = 0;
}
public void tick() {
if (cyclingOptions == null)
return;
int max = Math.max(4, (int) (cyclingOptions.length * 1.75f));
if (spinningTicks > max)
return;
spinningTicks++;
if (spinningTicks <= max && spinningTicks < 2)
return;
for (int i = 0; i < spinning.length; i++) {
int increasingChance = Mth.clamp(8 - spinningTicks, 1, 10);
boolean continueSpin = Create.RANDOM.nextInt(increasingChance * max / 4) != 0;
continueSpin &= max > 5 || spinningTicks < 2;
spinning[i] &= continueSpin;
if (i > 0 && Create.RANDOM.nextInt(3) > 0)
spinning[i - 1] &= continueSpin;
if (i < spinning.length - 1 && Create.RANDOM.nextInt(3) > 0)
spinning[i + 1] &= continueSpin;
if (spinningTicks > max)
spinning[i] = false;
}
}
public float getSize() {
return size;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putFloat("Width", size);
tag.putString("Cycle", cycle);
if (rightAligned)
NBTHelper.putMarker(tag, "RightAligned");
if (singleFlap)
NBTHelper.putMarker(tag, "SingleFlap");
if (hasGap)
NBTHelper.putMarker(tag, "Gap");
if (wideFlaps)
NBTHelper.putMarker(tag, "Wide");
if (component != null)
tag.putString("Text", Component.Serializer.toJson(component));
return tag;
}
public static FlapDisplaySection load(CompoundTag tag) {
float width = tag.getFloat("Width");
String cycle = tag.getString("Cycle");
boolean singleFlap = tag.contains("SingleFlap");
boolean hasGap = tag.contains("Gap");
FlapDisplaySection section = new FlapDisplaySection(width, cycle, singleFlap, hasGap);
section.cyclingOptions = getFlapCycle(cycle);
section.rightAligned = tag.contains("RightAligned");
section.wideFlaps = tag.contains("Wide");
if (!tag.contains("Text"))
return section;
section.component = Component.Serializer.fromJson(tag.getString("Text"));
section.refresh(false);
return section;
}
public void update(CompoundTag tag) {
component = Component.Serializer.fromJson(tag.getString("Text"));
if (cyclingOptions == null)
cyclingOptions = getFlapCycle(cycle);
refresh(true);
}
public boolean renderCharsIndividually() {
return !singleFlap;
}
public static String[] getFlapCycle(String key) {
return LOADED_FLAP_CYCLES.computeIfAbsent(key, k -> Lang.translate("flap_display.cycles." + key)
.getString()
.split(";"));
}
}

View file

@ -0,0 +1,279 @@
package com.simibubi.create.content.logistics.trains.management.display;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.gson.JsonElement;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.DynamicComponent;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
public class FlapDisplayTileEntity extends KineticTileEntity {
public List<FlapDisplayLayout> lines;
public boolean isController;
public boolean isRunning;
public int xSize, ySize;
public DyeColor[] colour;
public boolean[] manualLines;
public FlapDisplayTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
setLazyTickRate(10);
isController = false;
xSize = 1;
ySize = 1;
colour = new DyeColor[2];
manualLines = new boolean[2];
}
@Override
public void initialize() {
super.initialize();
}
@Override
public void lazyTick() {
super.lazyTick();
updateControllerStatus();
}
public void updateControllerStatus() {
if (level.isClientSide)
return;
BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof FlapDisplayBlock))
return;
Direction leftDirection = blockState.getValue(FlapDisplayBlock.HORIZONTAL_FACING)
.getClockWise();
boolean shouldBeController = !blockState.getValue(FlapDisplayBlock.UP)
&& level.getBlockState(worldPosition.relative(leftDirection)) != blockState;
int newXSize = 1;
int newYSize = 1;
if (shouldBeController) {
for (int xOffset = 1; xOffset < 32; xOffset++) {
if (level.getBlockState(worldPosition.relative(leftDirection.getOpposite(), xOffset)) != blockState)
break;
newXSize++;
}
for (int yOffset = 0; yOffset < 32; yOffset++) {
if (!level.getBlockState(worldPosition.relative(Direction.DOWN, yOffset))
.getOptionalValue(FlapDisplayBlock.DOWN)
.orElse(false))
break;
newYSize++;
}
}
if (isController == shouldBeController && newXSize == xSize && newYSize == ySize)
return;
isController = shouldBeController;
xSize = newXSize;
ySize = newYSize;
colour = Arrays.copyOf(colour, ySize * 2);
manualLines = new boolean[ySize * 2];
lines = null;
sendData();
}
@Override
public void tick() {
super.tick();
isRunning = super.isSpeedRequirementFulfilled();
if (!level.isClientSide || !isRunning)
return;
List<FlapDisplayLayout> lines = getLines();
lines.forEach(l -> l.getSections()
.forEach(FlapDisplaySection::tick));
}
@Override
public boolean isSpeedRequirementFulfilled() {
return isRunning;
}
public void applyTextManually(int lineIndex, String rawComponentText) {
FlapDisplayLayout layout = getLines().get(lineIndex);
if (!layout.isLayout("Default"))
layout.loadDefault(getMaxCharCount());
List<FlapDisplaySection> sections = layout.getSections();
if (rawComponentText == null) {
manualLines[lineIndex] = false;
sections.get(0)
.setText(new TextComponent(""));
notifyUpdate();
return;
}
JsonElement json = DynamicComponent.getJsonFromString(rawComponentText);
if (json == null)
return;
manualLines[lineIndex] = true;
Component text = DynamicComponent.parseCustomText(level, worldPosition, json);
sections.get(0)
.setText(text);
notifyUpdate();
}
public void setColour(int lineIndex, DyeColor color) {
colour[lineIndex] = color == DyeColor.WHITE ? null : color;
notifyUpdate();
}
public List<FlapDisplayLayout> getLines() {
if (lines == null)
initDefaultSections();
return lines;
}
public void initDefaultSections() {
lines = new ArrayList<>();
for (int i = 0; i < ySize * 2; i++)
lines.add(new FlapDisplayLayout(getMaxCharCount()));
}
public int getMaxCharCount() {
return getMaxCharCount(0);
}
public int getMaxCharCount(int gaps) {
return (int) ((xSize * 16f - 2f - 4f * gaps) / 3.5f);
}
@Override
protected void write(CompoundTag tag, boolean clientPacket) {
super.write(tag, clientPacket);
tag.putBoolean("Controller", isController);
tag.putInt("XSize", xSize);
tag.putInt("YSize", ySize);
for (int j = 0; j < manualLines.length; j++)
if (manualLines[j])
NBTHelper.putMarker(tag, "CustomLine" + j);
for (int j = 0; j < colour.length; j++)
if (colour[j] != null)
NBTHelper.writeEnum(tag, "Dye" + j, colour[j]);
List<FlapDisplayLayout> lines = getLines();
for (int i = 0; i < lines.size(); i++)
tag.put("Display" + i, lines.get(i)
.write());
}
@Override
protected void read(CompoundTag tag, boolean clientPacket) {
super.read(tag, clientPacket);
boolean wasActive = isController;
int prevX = xSize;
int prevY = ySize;
isController = tag.getBoolean("Controller");
xSize = tag.getInt("XSize");
ySize = tag.getInt("YSize");
manualLines = new boolean[ySize * 2];
for (int i = 0; i < ySize * 2; i++)
manualLines[i] = tag.contains("CustomLine" + i);
colour = new DyeColor[ySize * 2];
for (int i = 0; i < ySize * 2; i++)
colour[i] = tag.contains("Dye" + i) ? NBTHelper.readEnum(tag, "Dye" + i, DyeColor.class) : null;
if (clientPacket && wasActive != isController || prevX != xSize || prevY != ySize) {
invalidateRenderBoundingBox();
lines = null;
}
List<FlapDisplayLayout> lines = getLines();
for (int i = 0; i < lines.size(); i++)
lines.get(i)
.read(tag.getCompound("Display" + i));
}
public int getLineIndexAt(double yCoord) {
return (int) Mth.clamp(Math.floor(2 * (worldPosition.getY() - yCoord + 1)), 0, ySize * 2);
}
public FlapDisplayTileEntity getController() {
if (isController)
return this;
BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof FlapDisplayBlock))
return null;
MutableBlockPos pos = getBlockPos().mutable();
Direction side = blockState.getValue(FlapDisplayBlock.HORIZONTAL_FACING)
.getClockWise();
for (int i = 0; i < 64; i++) {
BlockState other = level.getBlockState(pos);
if (other.getOptionalValue(FlapDisplayBlock.UP)
.orElse(false)) {
pos.move(Direction.UP);
continue;
}
if (!level.getBlockState(pos.relative(side))
.getOptionalValue(FlapDisplayBlock.UP)
.orElse(true)) {
pos.move(side);
continue;
}
BlockEntity found = level.getBlockEntity(pos);
if (found instanceof FlapDisplayTileEntity flap && flap.isController)
return flap;
break;
}
return null;
}
@Override
protected AABB createRenderBoundingBox() {
AABB aabb = new AABB(worldPosition);
if (!isController)
return aabb;
Vec3i normal = getDirection().getClockWise()
.getNormal();
return aabb.expandTowards(normal.getX() * xSize, -ySize, normal.getZ() * xSize);
}
public Direction getDirection() {
return getBlockState().getOptionalValue(FlapDisplayBlock.HORIZONTAL_FACING)
.orElse(Direction.SOUTH)
.getOpposite();
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
}

View file

@ -0,0 +1,76 @@
package com.simibubi.create.content.logistics.trains.management.display;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import net.minecraft.network.chat.MutableComponent;
public class GlobalTrainDisplayData {
public static final Map<String, Collection<TrainDeparturePrediction>> statusByDestination = new HashMap<>();
public static boolean updateTick = false;
public static void refresh() {
statusByDestination.clear();
for (Train train : Create.RAILWAYS.trains.values()) {
if (train.runtime.paused || train.runtime.getSchedule() == null)
continue;
if (train.derailed || train.graph == null)
continue;
for (TrainDeparturePrediction prediction : train.runtime.submitPredictions())
statusByDestination.computeIfAbsent(prediction.destination, $ -> new ArrayList<>())
.add(prediction);
}
}
public static List<TrainDeparturePrediction> prepare(String filter, int maxLines) {
return statusByDestination.entrySet()
.stream()
.filter(e -> e.getKey()
.matches(filter.replace("*", ".*")))
.flatMap(e -> e.getValue()
.stream())
.sorted()
.limit(maxLines)
.toList();
}
public static class TrainDeparturePrediction implements Comparable<TrainDeparturePrediction> {
public Train train;
public int ticks;
public MutableComponent scheduleTitle;
public String destination;
public TrainDeparturePrediction(Train train, int ticks, MutableComponent scheduleTitle, String destination) {
this.scheduleTitle = scheduleTitle;
this.destination = destination;
this.train = train;
this.ticks = ticks;
}
private int getCompareTicks() {
if (ticks == -1)
return Integer.MAX_VALUE;
if (ticks < 200)
return 0;
return ticks;
}
@Override
public int compareTo(TrainDeparturePrediction o) {
int compare = Integer.compare(getCompareTicks(), o.getCompareTicks());
if (compare == 0)
return train.name.getString()
.compareTo(o.train.name.getString());
return compare;
}
}
}

View file

@ -1,18 +1,25 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.display.GlobalTrainDisplayData.TrainDeparturePrediction;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.condition.TimedWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.destination.FilteredDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.destination.ScheduleDestination;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.config.CTrains;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
public class ScheduleRuntime {
@ -34,6 +41,9 @@ public class ScheduleRuntime {
List<Integer> conditionProgress;
List<CompoundTag> conditionContext;
int ticksInTransit;
List<Integer> predictionTicks;
public ScheduleRuntime(Train train) {
this.train = train;
reset();
@ -44,6 +54,14 @@ public class ScheduleRuntime {
return;
state = State.POST_TRANSIT;
conditionProgress.clear();
if (ticksInTransit > 0) {
int current = predictionTicks.get(currentEntry);
if (current > 0)
ticksInTransit = (current + ticksInTransit) / 2;
predictionTicks.set(currentEntry, ticksInTransit);
}
if (currentEntry >= schedule.entries.size())
return;
List<List<ScheduleWaitCondition>> conditions = schedule.entries.get(currentEntry).conditions;
@ -67,8 +85,10 @@ public class ScheduleRuntime {
return;
if (train.derailed)
return;
if (train.navigation.destination != null)
if (train.navigation.destination != null) {
ticksInTransit++;
return;
}
if (currentEntry >= schedule.entries.size()) {
currentEntry = 0;
if (!schedule.cyclic)
@ -97,8 +117,10 @@ public class ScheduleRuntime {
destinationReached();
return;
}
if (train.navigation.startNavigation(nextStation, Double.MAX_VALUE, false) != -1)
if (train.navigation.startNavigation(nextStation, Double.MAX_VALUE, false) != -1) {
state = State.IN_TRANSIT;
ticksInTransit = 0;
}
}
public void tickConditions(Level level) {
@ -155,6 +177,8 @@ public class ScheduleRuntime {
paused = false;
isAutoSchedule = auto;
train.status.newSchedule();
predictionTicks = new ArrayList<>();
schedule.entries.forEach($ -> predictionTicks.add(-1));
}
public Schedule getSchedule() {
@ -173,6 +197,121 @@ public class ScheduleRuntime {
state = State.PRE_TRANSIT;
conditionProgress = new ArrayList<>();
conditionContext = new ArrayList<>();
predictionTicks = new ArrayList<>();
}
public Collection<TrainDeparturePrediction> submitPredictions() {
Collection<TrainDeparturePrediction> predictions = new ArrayList<>();
int entryCount = schedule.entries.size();
int accumulatedTime = 0;
int current = currentEntry;
// Current
if (state == State.POST_TRANSIT || current >= entryCount) {
GlobalStation currentStation = train.getCurrentStation();
if (currentStation != null)
predictions.add(createPrediction(current, currentStation.name, 0));
int departureTime = estimateStayDuration(current);
if (departureTime == -1)
accumulatedTime = -1;
} else if (train.navigation.destination != null) {
CTrains conf = AllConfigs.SERVER.trains;
double speed = (conf.getTopSpeedMPT() + conf.getTurningTopSpeedMPT()) / 2;
int timeRemaining = (int) (train.navigation.distanceToDestination / speed) * 2;
if (predictionTicks.size() > current && train.navigation.distanceStartedAt != 0) {
float predictedTime = predictionTicks.get(current);
if (predictedTime > 0) {
predictedTime *=
Mth.clamp(train.navigation.distanceToDestination / train.navigation.distanceStartedAt, 0, 1);
timeRemaining = (timeRemaining + (int) predictedTime) / 2;
}
}
accumulatedTime += timeRemaining;
predictions.add(createPrediction(current, train.navigation.destination.name, accumulatedTime));
int departureTime = estimateStayDuration(current);
if (departureTime != -1)
accumulatedTime += departureTime;
if (departureTime == -1)
accumulatedTime = -1;
} else
predictForEntry(current, accumulatedTime, predictions);
// Upcoming
for (int i = 1; i < entryCount; i++) {
int index = (i + current) % entryCount;
if (index == 0 && !schedule.cyclic)
break;
accumulatedTime = predictForEntry(index, accumulatedTime, predictions);
}
return predictions;
}
private int predictForEntry(int index, int accumulatedTime, Collection<TrainDeparturePrediction> predictions) {
ScheduleEntry entry = schedule.entries.get(index);
if (!(entry.destination instanceof FilteredDestination filter))
return accumulatedTime;
if (predictionTicks.size() <= currentEntry)
return accumulatedTime;
if (accumulatedTime == -1) {
predictions.add(createPrediction(index, filter.nameFilter, accumulatedTime));
return -1;
}
int predictedTime = predictionTicks.get(index);
int departureTime = estimateStayDuration(index);
if (predictedTime == -1)
accumulatedTime = -1;
else {
accumulatedTime += predictedTime;
if (departureTime != -1)
accumulatedTime += departureTime;
}
predictions.add(createPrediction(index, filter.nameFilter, accumulatedTime));
if (departureTime == -1)
return -1;
return accumulatedTime;
}
private int estimateStayDuration(int index) {
if (index >= schedule.entries.size()) {
if (!schedule.cyclic)
return 100000;
index = 0;
}
ScheduleEntry scheduleEntry = schedule.entries.get(index);
for (List<ScheduleWaitCondition> list : scheduleEntry.conditions)
for (ScheduleWaitCondition condition : list)
if (condition instanceof TimedWaitCondition wait)
return wait.timeUnit.ticksPer * wait.value;
return 5; // TODO properly ask conditions for time prediction
}
private TrainDeparturePrediction createPrediction(int index, String destination, int time) {
if (++index >= schedule.entries.size()) {
if (!schedule.cyclic)
return new TrainDeparturePrediction(train, time, new TextComponent(" "), destination);
index %= schedule.entries.size();
}
ScheduleEntry scheduleEntry = schedule.entries.get(index);
if (!(scheduleEntry.destination instanceof FilteredDestination fd))
return new TrainDeparturePrediction(train, time, new TextComponent(" "), destination);
String station = fd.nameFilter.replaceAll("\\*", "")
.trim();
return new TrainDeparturePrediction(train, time, new TextComponent(station), destination);
}
public CompoundTag write() {
@ -185,6 +324,7 @@ public class ScheduleRuntime {
NBTHelper.writeEnum(tag, "State", state);
tag.putIntArray("ConditionProgress", conditionProgress);
tag.put("ConditionContext", NBTHelper.writeCompoundList(conditionContext, CompoundTag::copy));
tag.putIntArray("TransitTimes", predictionTicks);
return tag;
}
@ -199,6 +339,14 @@ public class ScheduleRuntime {
for (int i : tag.getIntArray("ConditionProgress"))
conditionProgress.add(i);
NBTHelper.iterateCompoundList(tag.getList("ConditionContext", Tag.TAG_COMPOUND), conditionContext::add);
int[] readTransits = tag.getIntArray("TransitTimes");
if (schedule != null) {
schedule.entries.forEach($ -> predictionTicks.add(-1));
if (readTransits.length == schedule.entries.size())
for (int i = 0; i < readTransits.length; i++)
predictionTicks.set(i, readTransits[i]);
}
}
}

View file

@ -78,7 +78,7 @@ public class ConnectedPillarBlock extends LayeredBlock {
}
return state;
}
@Override
public void onPlace(BlockState pState, Level pLevel, BlockPos pPos, BlockState pOldState, boolean pIsMoving) {
if (pOldState.getBlock() == this)
@ -87,14 +87,15 @@ public class ConnectedPillarBlock extends LayeredBlock {
if (!blockTicks.hasScheduledTick(pPos, this))
pLevel.scheduleTick(pPos, this, 1);
}
@Override
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, Random pRandom) {
if (pState.getBlock() != this)
return;
BlockPos belowPos = pPos.relative(Direction.fromAxisAndDirection(pState.getValue(AXIS), AxisDirection.NEGATIVE));
BlockPos belowPos =
pPos.relative(Direction.fromAxisAndDirection(pState.getValue(AXIS), AxisDirection.NEGATIVE));
BlockState belowState = pLevel.getBlockState(belowPos);
if (!canConnect(pState, belowState))
if (!canConnect(pState, belowState))
pLevel.setBlock(pPos, updateColumn(pLevel, pPos, pState, true), 3);
}

View file

@ -30,6 +30,7 @@ import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler;
import com.simibubi.create.content.curiosities.zapper.ZapperItem;
import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler;
import com.simibubi.create.content.logistics.block.data.DataGathererBlockItem;
import com.simibubi.create.content.logistics.block.depot.EjectorTargetHandler;
import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPointHandler;
import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler;
@ -156,6 +157,7 @@ public class ClientEvents {
TrackPlacement.clientTick();
TrackRemoval.clientTick();
TrainRelocator.clientTick();
DataGathererBlockItem.clientTick();
}
@SubscribeEvent

View file

@ -2,11 +2,13 @@ package com.simibubi.create.foundation.config;
public class CLogistics extends ConfigBase {
public final ConfigInt defaultExtractionLimit = i(64, 1, 64, "defaultExtractionLimit", Comments.defaultExtractionLimit);
public final ConfigInt defaultExtractionLimit =
i(64, 1, 64, "defaultExtractionLimit", Comments.defaultExtractionLimit);
public final ConfigInt defaultExtractionTimer = i(8, 1, "defaultExtractionTimer", Comments.defaultExtractionTimer);
public final ConfigInt psiTimeout = i(20, 1, "psiTimeout", Comments.psiTimeout);
public final ConfigInt mechanicalArmRange = i(5, 1, "mechanicalArmRange", Comments.mechanicalArmRange);
public final ConfigInt linkRange = i(128, 1, "linkRange", Comments.linkRange);
public final ConfigInt dataGathererRange = i(32, 1, "dataGathererRange", Comments.dataGathererRange);
public final ConfigInt vaultCapacity = i(20, 1, "vaultCapacity", Comments.vaultCapacity);
@Override
@ -20,6 +22,8 @@ public class CLogistics extends ConfigBase {
static String defaultExtractionTimer =
"The amount of ticks a funnel waits between item transferrals, when it is not re-activated by redstone.";
static String linkRange = "Maximum possible range in blocks of redstone link connections.";
static String dataGathererRange =
"Maximum possible distance in blocks between data gatherers and their target.";
static String psiTimeout =
"The amount of ticks a portable storage interface waits for transfers until letting contraptions move along.";
static String mechanicalArmRange = "Maximum distance in blocks a Mechanical Arm can reach across.";

View file

@ -59,7 +59,7 @@ public enum AllGuiTextures implements ScreenElement {
TOOLBELT_MAIN_SLOT("widgets", 0, 97, 24, 24),
TOOLBELT_EMPTY_SLOT("widgets", 27, 98, 22, 22),
TOOLBELT_INACTIVE_SLOT("widgets", 52, 98, 22, 22),
TOOLBELT_HOTBAR_OFF("widgets", 0, 130, 20, 24),
TOOLBELT_HOTBAR_ON("widgets", 20, 130, 20, 24),
TOOLBELT_SELECTED_OFF("widgets", 0, 155, 22, 22),
@ -80,7 +80,12 @@ public enum AllGuiTextures implements ScreenElement {
PROJECTOR_FILTER("projector", 0, 36, 162, 22),
PROJECTOR_END("projector", 0, 58, 162, 22),
PROJECTOR_EMPTY("projector", 0, 80, 162, 22),
DATA_GATHERER("data_gatherer", 235, 162),
DATA_AREA_START("data_gatherer", 0, 163, 2, 18),
DATA_AREA("data_gatherer", 3, 163, 1, 18),
DATA_AREA_END("data_gatherer", 5, 163, 2, 18),
SCHEDULE("schedule", 256, 226),
SCHEDULE_CARD_DARK("schedule", 5, 233, 1, 1),
SCHEDULE_CARD_MEDIUM("schedule", 6, 233, 1, 1),
@ -109,7 +114,7 @@ public enum AllGuiTextures implements ScreenElement {
SCHEDULE_EDITOR_INACTIVE_SLOT("schedule_2", 0, 91, 18, 18),
SCHEDULE_EDITOR_SECOND_LINE("schedule_2", 20, 91, 124, 18),
SCHEDULE_EDITOR_DIVIDER("schedule_2", 145, 91, 4, 18),
STATION("schedule_2", 0, 111, 200, 127),
STATION_ASSEMBLING("assemble", 200, 178),
STATION_TEXTBOX_TOP("assemble", 1, 179, 150, 18),

View file

@ -16,6 +16,8 @@ import net.minecraft.network.chat.TextComponent;
public abstract class AbstractSimiWidget extends AbstractWidget implements TickableGuiEventListener {
public static final int HEADER_RGB = 0x5391E1;
protected float z;
protected boolean wasHovered = false;
protected List<Component> toolTip = new LinkedList<>();

View file

@ -5,7 +5,6 @@ import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.element.ScreenElement;
import net.minecraft.network.chat.Component;

View file

@ -20,6 +20,7 @@ public class ScrollInput extends AbstractSimiWidget {
protected final Component scrollToModify = Lang.translate("gui.scrollInput.scrollToModify");
protected final Component shiftScrollsFaster = Lang.translate("gui.scrollInput.shiftScrollsFaster");
protected Label displayLabel;
protected boolean inverted;
protected int min, max;
protected int shiftStep;
@ -37,6 +38,11 @@ public class ScrollInput extends AbstractSimiWidget {
public Function<StepContext, Integer> standardStep() {
return c -> c.shift ? shiftStep : 1;
}
public ScrollInput inverted() {
inverted = true;
return this;
}
public ScrollInput withRange(int min, int max) {
this.min = min;
@ -91,6 +97,9 @@ public class ScrollInput extends AbstractSimiWidget {
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
if (inverted)
delta *= -1;
StepContext context = new StepContext();
context.control = AllKeys.ctrlDown();
context.shift = AllKeys.shiftDown();
@ -134,7 +143,7 @@ public class ScrollInput extends AbstractSimiWidget {
protected void updateTooltip() {
toolTip.clear();
toolTip.add(title.plainCopy().withStyle(ChatFormatting.BLUE));
toolTip.add(title.plainCopy().withStyle(s -> s.withColor(HEADER_RGB)));
toolTip.add(scrollToModify.plainCopy().withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
toolTip.add(shiftScrollsFaster.plainCopy().withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
}

View file

@ -18,6 +18,7 @@ public class SelectionScrollInput extends ScrollInput {
public SelectionScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
super(xIn, yIn, widthIn, heightIn);
options = new ArrayList<>();
inverted();
}
public ScrollInput forOptions(List<? extends Component> options) {
@ -32,15 +33,11 @@ public class SelectionScrollInput extends ScrollInput {
displayLabel.text = options.get(state);
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
return super.mouseScrolled(mouseX, mouseY, -delta);
}
@Override
protected void updateTooltip() {
toolTip.clear();
toolTip.add(title.plainCopy().withStyle(ChatFormatting.BLUE));
toolTip.add(title.plainCopy()
.withStyle(s -> s.withColor(HEADER_RGB)));
int min = Math.min(this.max - 16, state - 7);
int max = Math.max(this.min + 16, state + 8);
min = Math.max(min, this.min);
@ -53,14 +50,21 @@ public class SelectionScrollInput extends ScrollInput {
max++;
for (int i = min; i < max; i++) {
if (i == state)
toolTip.add(TextComponent.EMPTY.plainCopy().append("-> ").append(options.get(i)).withStyle(ChatFormatting.WHITE));
toolTip.add(TextComponent.EMPTY.plainCopy()
.append("-> ")
.append(options.get(i))
.withStyle(ChatFormatting.WHITE));
else
toolTip.add(TextComponent.EMPTY.plainCopy().append("> ").append(options.get(i)).withStyle(ChatFormatting.GRAY));
toolTip.add(TextComponent.EMPTY.plainCopy()
.append("> ")
.append(options.get(i))
.withStyle(ChatFormatting.GRAY));
}
if (max < this.max)
toolTip.add(new TextComponent("> ...").withStyle(ChatFormatting.GRAY));
toolTip.add(scrollToSelect.plainCopy().withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC));
toolTip.add(scrollToSelect.plainCopy()
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC));
}
}

View file

@ -0,0 +1,26 @@
package com.simibubi.create.foundation.gui.widget;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.network.chat.Component;
public class TooltipArea extends AbstractSimiWidget {
public TooltipArea(int x, int y, int width, int height) {
super(x, y, width, height);
}
@Override
public void renderButton(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
if (visible)
isHovered = mouseX >= x && mouseY >= y && mouseX < x + width && mouseY < y + height;
}
public TooltipArea withTooltip(List<Component> tooltip) {
this.toolTip = tooltip;
return this;
}
}

View file

@ -0,0 +1,97 @@
package com.simibubi.create.foundation.item;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.IntAttached;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
public class CountedItemStackList {
Map<Item, Set<ItemStackEntry>> items = new HashMap<>();
public CountedItemStackList(IItemHandler inventory, FilteringBehaviour filteringBehaviour) {
for (int slot = 0; slot < inventory.getSlots(); slot++) {
ItemStack extractItem = inventory.extractItem(slot, inventory.getSlotLimit(slot), true);
if (filteringBehaviour.test(extractItem))
add(extractItem);
}
}
public Stream<IntAttached<MutableComponent>> getTopNames(int limit) {
return items.values()
.stream()
.flatMap(s -> s.stream())
.sorted(IntAttached.comparator())
.limit(limit)
.map(entry -> IntAttached.with(entry.count(), entry.stack()
.getHoverName()
.copy()));
}
public void add(ItemStack stack) {
add(stack, stack.getCount());
}
public void add(ItemStack stack, int amount) {
if (stack.isEmpty())
return;
Set<ItemStackEntry> stackSet = getOrCreateItemSet(stack);
for (ItemStackEntry entry : stackSet) {
if (!entry.matches(stack))
continue;
entry.grow(amount);
return;
}
stackSet.add(new ItemStackEntry(stack, amount));
}
private Set<ItemStackEntry> getOrCreateItemSet(ItemStack stack) {
if (!items.containsKey(stack.getItem()))
items.put(stack.getItem(), new HashSet<>());
return getItemSet(stack);
}
private Set<ItemStackEntry> getItemSet(ItemStack stack) {
return items.get(stack.getItem());
}
public static class ItemStackEntry extends IntAttached<ItemStack> {
public ItemStackEntry(ItemStack stack) {
this(stack, stack.getCount());
}
public ItemStackEntry(ItemStack stack, int amount) {
super(amount, stack);
}
public boolean matches(ItemStack other) {
return ItemHandlerHelper.canItemStacksStack(other, stack());
}
public ItemStack stack() {
return getSecond();
}
public void grow(int amount) {
setFirst(getFirst() + amount);
}
public int count() {
return getFirst();
}
}
}

View file

@ -34,6 +34,7 @@ import com.simibubi.create.content.curiosities.weapons.PotatoCannonPacket;
import com.simibubi.create.content.curiosities.weapons.PotatoProjectileTypeManager;
import com.simibubi.create.content.curiosities.zapper.ZapperBeamPacket;
import com.simibubi.create.content.curiosities.zapper.terrainzapper.ConfigureWorldshaperPacket;
import com.simibubi.create.content.logistics.block.data.DataGathererConfigurationPacket;
import com.simibubi.create.content.logistics.block.depot.EjectorElytraPacket;
import com.simibubi.create.content.logistics.block.depot.EjectorPlacementPacket;
import com.simibubi.create.content.logistics.block.depot.EjectorTriggerPacket;
@ -120,6 +121,7 @@ public enum AllPackets {
RELOCATE_TRAIN(TrainRelocationPacket.class, TrainRelocationPacket::new, PLAY_TO_SERVER),
CONTROLS_INPUT(ControlsInputPacket.class, ControlsInputPacket::new, PLAY_TO_SERVER),
REMOVE_TRACKS(TrackRemovalPacket.class, TrackRemovalPacket::new, PLAY_TO_SERVER),
CONFIGURE_DATA_GATHERER(DataGathererConfigurationPacket.class, DataGathererConfigurationPacket::new, PLAY_TO_SERVER),
// Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),

View file

@ -7,7 +7,7 @@ import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity.Mode;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.fluids.actors.SpoutTileEntity;
import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.content.contraptions.relays.belt.BeltPart;
@ -79,7 +79,8 @@ public class BeltScenes {
scene.idle(7);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, frontEnd, shaftBB.move(frontEnd), 17);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.BLACK, backEndCenter, connectBB.expandTowards(-4, 0, 0), 20);
scene.overlay.chaseBoundingBoxOutline(PonderPalette.BLACK, backEndCenter, connectBB.expandTowards(-4, 0, 0),
20);
scene.idle(20);
scene.world.moveSection(shafts, util.vector.of(0, -2, 0), 0);
@ -534,8 +535,9 @@ public class BeltScenes {
scene.world.removeItemsFromBelt(depotPos);
scene.world.hideSection(util.select.position(depotPos.above(2)), Direction.SOUTH);
scene.idle(20);
ElementLink<WorldSectionElement> spout = scene.world.showIndependentSection(util.select.position(depotPos.above(2)
.west()), Direction.SOUTH);
ElementLink<WorldSectionElement> spout =
scene.world.showIndependentSection(util.select.position(depotPos.above(2)
.west()), Direction.SOUTH);
scene.world.moveSection(spout, util.vector.of(1, 0, 0), 0);
BlockPos pressPos = depotPos.above(2)
@ -546,10 +548,11 @@ public class BeltScenes {
scene.idle(10);
Class<MechanicalPressTileEntity> type = MechanicalPressTileEntity.class;
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BELT));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BELT));
scene.idle(15);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.removeItemsFromBelt(depotPos);
ItemStack sheet = AllItems.COPPER_SHEET.asStack();
scene.world.createItemOnBeltLike(depotPos, Direction.UP, sheet);

View file

@ -7,7 +7,7 @@ import com.simibubi.create.content.contraptions.components.deployer.DeployerTile
import com.simibubi.create.content.contraptions.components.millstone.MillstoneTileEntity;
import com.simibubi.create.content.contraptions.components.mixer.MechanicalMixerTileEntity;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity;
import com.simibubi.create.content.contraptions.components.press.MechanicalPressTileEntity.Mode;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.processing.BasinBlock;
import com.simibubi.create.content.contraptions.processing.BasinTileEntity;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
@ -314,10 +314,11 @@ public class ProcessingScenes {
scene.idle(10);
Class<MechanicalPressTileEntity> type = MechanicalPressTileEntity.class;
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BELT));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BELT));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.removeItemsFromBelt(depotPos);
ItemStack sheet = AllItems.COPPER_SHEET.asStack();
scene.world.createItemOnBeltLike(depotPos, Direction.UP, sheet);
@ -344,7 +345,8 @@ public class ProcessingScenes {
ElementLink<BeltItemElement> ingot2 = scene.world.createItemOnBelt(beltPos, Direction.SOUTH, copper);
scene.idle(15);
scene.world.stallBeltItem(ingot, true);
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BELT));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BELT));
scene.overlay.showText(50)
.pointAt(pressSide)
@ -353,8 +355,8 @@ public class ProcessingScenes {
.text("The Press will hold and process them automatically");
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.removeItemsFromBelt(pressPos.below(2));
ingot = scene.world.createItemOnBelt(pressPos.below(2), Direction.UP, sheet);
scene.world.stallBeltItem(ingot, true);
@ -362,10 +364,11 @@ public class ProcessingScenes {
scene.world.stallBeltItem(ingot, false);
scene.idle(15);
scene.world.stallBeltItem(ingot2, true);
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BELT));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BELT));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makePressingParticleEffect(depotCenter.add(0, 8 / 16f, 0), copper));
scene.world.removeItemsFromBelt(pressPos.below(2));
ingot2 = scene.world.createItemOnBelt(pressPos.below(2), Direction.UP, sheet);
scene.world.stallBeltItem(ingot2, true);
@ -492,10 +495,11 @@ public class ProcessingScenes {
30);
scene.idle(30);
Class<MechanicalPressTileEntity> type = MechanicalPressTileEntity.class;
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BASIN));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BASIN));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makeCompactingParticleEffect(util.vector.centerOf(basin), copper));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makeCompactingParticleEffect(util.vector.centerOf(basin), copper));
scene.world.modifyTileNBT(util.select.position(basin), BasinTileEntity.class, nbt -> {
nbt.put("VisualizedItems",
NBTHelper.writeCompoundList(ImmutableList.of(IntAttached.with(1, copperBlock)), ia -> ia.getValue()
@ -517,10 +521,11 @@ public class ProcessingScenes {
scene.overlay.showControls(new InputWindowElement(util.vector.topOf(basin), Pointing.DOWN).withItem(log), 30);
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BASIN));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BASIN));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makeCompactingParticleEffect(util.vector.centerOf(basin), log));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makeCompactingParticleEffect(util.vector.centerOf(basin), log));
scene.world.modifyTileNBT(util.select.position(basin), BasinTileEntity.class, nbt -> {
nbt.put("VisualizedItems",
NBTHelper.writeCompoundList(ImmutableList.of(IntAttached.with(1, bark)), ia -> ia.getValue()
@ -632,8 +637,8 @@ public class ProcessingScenes {
.withItem(new ItemStack(Items.SOUL_SAND)),
40);
scene.idle(7);
scene.world.modifyBlock(util.grid.at(3, 1, 2), s -> s.setValue(LitBlazeBurnerBlock.FLAME_TYPE, LitBlazeBurnerBlock.FlameType.SOUL),
false);
scene.world.modifyBlock(util.grid.at(3, 1, 2),
s -> s.setValue(LitBlazeBurnerBlock.FLAME_TYPE, LitBlazeBurnerBlock.FlameType.SOUL), false);
scene.overlay.showText(60)
.text("The flame can be transformed using a soul-infused item")
.pointAt(util.vector.blockSurface(center.east()
@ -821,10 +826,11 @@ public class ProcessingScenes {
scene.idle(10);
Class<MechanicalPressTileEntity> type = MechanicalPressTileEntity.class;
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BASIN));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BASIN));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makeCompactingParticleEffect(util.vector.centerOf(basinPos), stack));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makeCompactingParticleEffect(util.vector.centerOf(basinPos), stack));
scene.world.modifyTileNBT(util.select.position(basinPos), BasinTileEntity.class, nbt -> {
nbt.put("VisualizedItems",
NBTHelper.writeCompoundList(ImmutableList.of(IntAttached.with(1, new ItemStack(Blocks.BRICKS))),
@ -859,20 +865,22 @@ public class ProcessingScenes {
scene.overlay.showControls(new InputWindowElement(util.vector.topOf(basinPos), Pointing.RIGHT).withItem(nugget),
30);
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BASIN));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BASIN));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makeCompactingParticleEffect(util.vector.centerOf(basinPos), nugget));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makeCompactingParticleEffect(util.vector.centerOf(basinPos), nugget));
ItemStack ingot = new ItemStack(Items.COPPER_INGOT);
scene.idle(30);
scene.overlay.showControls(new InputWindowElement(util.vector.topOf(basinPos), Pointing.RIGHT).withItem(ingot),
30);
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type, pte -> pte.start(Mode.BASIN));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.start(Mode.BASIN));
scene.idle(30);
scene.world.modifyTileEntity(pressPos, type,
pte -> pte.makeCompactingParticleEffect(util.vector.centerOf(basinPos), ingot));
scene.world.modifyTileEntity(pressPos, type, pte -> pte.getPressingBehaviour()
.makeCompactingParticleEffect(util.vector.centerOf(basinPos), ingot));
ItemStack block = new ItemStack(Items.COPPER_BLOCK);
scene.idle(30);

View file

@ -28,8 +28,8 @@ public abstract class SmartTileEntity extends CachedRenderBBTileEntity implement
private final Map<BehaviourType<?>, TileEntityBehaviour> behaviours = new HashMap<>();
private boolean initialized = false;
private boolean firstNbtRead = true;
private int lazyTickRate;
private int lazyTickCounter;
protected int lazyTickRate;
protected int lazyTickCounter;
// Used for simulating this TE in a client-only setting
private boolean virtualMode;

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