diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index f9261da11..fa935090e 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -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 diff --git a/src/generated/resources/assets/create/blockstates/data_gatherer.json b/src/generated/resources/assets/create/blockstates/data_gatherer.json new file mode 100644 index 000000000..376895f94 --- /dev/null +++ b/src/generated/resources/assets/create/blockstates/data_gatherer.json @@ -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 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/blockstates/display_board.json b/src/generated/resources/assets/create/blockstates/display_board.json new file mode 100644 index 000000000..82092b62f --- /dev/null +++ b/src/generated/resources/assets/create/blockstates/display_board.json @@ -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 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/blockstates/pulse_extender.json b/src/generated/resources/assets/create/blockstates/pulse_extender.json index e90e9f51f..75748fbcd 100644 --- a/src/generated/resources/assets/create/blockstates/pulse_extender.json +++ b/src/generated/resources/assets/create/blockstates/pulse_extender.json @@ -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 } } } \ No newline at end of file diff --git a/src/generated/resources/assets/create/blockstates/pulse_repeater.json b/src/generated/resources/assets/create/blockstates/pulse_repeater.json index 701e2a96d..2088b496f 100644 --- a/src/generated/resources/assets/create/blockstates/pulse_repeater.json +++ b/src/generated/resources/assets/create/blockstates/pulse_repeater.json @@ -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 } } } \ No newline at end of file diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json index 4e7ab7945..90068a952 100644 --- a/src/generated/resources/assets/create/lang/en_ud.json +++ b/src/generated/resources/assets/create/lang/en_ud.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 3756d5e26..486ee255d 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/de_de.json b/src/generated/resources/assets/create/lang/unfinished/de_de.json index 67ccc5289..992bcb4e4 100644 --- a/src/generated/resources/assets/create/lang/unfinished/de_de.json +++ b/src/generated/resources/assets/create/lang/unfinished/de_de.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_cl.json b/src/generated/resources/assets/create/lang/unfinished/es_cl.json index 69ffb4b80..71bdcb32e 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_cl.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_cl.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_es.json b/src/generated/resources/assets/create/lang/unfinished/es_es.json index 5054bb66f..adfc1f91b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_es.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_es.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json index 4c8474e95..e2ab6e628 100644 --- a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json +++ b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/it_it.json b/src/generated/resources/assets/create/lang/unfinished/it_it.json index 7e7725ed1..73afd2e7a 100644 --- a/src/generated/resources/assets/create/lang/unfinished/it_it.json +++ b/src/generated/resources/assets/create/lang/unfinished/it_it.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json index c9dadff7b..c8be16959 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json +++ b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json @@ -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": "マウスでクリックまたはドラッグしてください", diff --git a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json index 4b0cb918c..8dd045877 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json +++ b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json index 9d8a854bc..a971d7ad5 100644 --- a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json +++ b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json index 3ac5f2eb7..077b7e1f8 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json +++ b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json @@ -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ą", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_br.json b/src/generated/resources/assets/create/lang/unfinished/pt_br.json index 456f3b091..5786a6f4b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_br.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_br.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json index fd12fa591..9ae782826 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json @@ -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", diff --git a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json index 03358ed8c..f5858014b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json +++ b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json @@ -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": "Кликни и тащи с помощью мыши", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json index 33007213a..b87e165ef 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json @@ -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": "点击拖拽你的鼠标", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json index a14ba5e6f..f7beb88cc 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json @@ -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": "點擊拖拽你的滑鼠", diff --git a/src/generated/resources/assets/create/models/item/data_gatherer.json b/src/generated/resources/assets/create/models/item/data_gatherer.json new file mode 100644 index 000000000..1d77a8539 --- /dev/null +++ b/src/generated/resources/assets/create/models/item/data_gatherer.json @@ -0,0 +1,3 @@ +{ + "parent": "create:block/data_gatherer/block" +} \ No newline at end of file diff --git a/src/generated/resources/assets/create/models/item/display_board.json b/src/generated/resources/assets/create/models/item/display_board.json new file mode 100644 index 000000000..ddc5cb06f --- /dev/null +++ b/src/generated/resources/assets/create/models/item/display_board.json @@ -0,0 +1,3 @@ +{ + "parent": "create:block/display_board/item" +} \ No newline at end of file diff --git a/src/generated/resources/data/create/loot_tables/blocks/data_gatherer.json b/src/generated/resources/data/create/loot_tables/blocks/data_gatherer.json new file mode 100644 index 000000000..80db2642a --- /dev/null +++ b/src/generated/resources/data/create/loot_tables/blocks/data_gatherer.json @@ -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" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/loot_tables/blocks/display_board.json b/src/generated/resources/data/create/loot_tables/blocks/display_board.json new file mode 100644 index 000000000..78a378160 --- /dev/null +++ b/src/generated/resources/data/create/loot_tables/blocks/display_board.json @@ -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" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index d96280c7e..069b61897 100644 --- a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -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", diff --git a/src/main/java/com/simibubi/create/AllBlockPartials.java b/src/main/java/com/simibubi/create/AllBlockPartials.java index f5412541b..dbb8e1a36 100644 --- a/src/main/java/com/simibubi/create/AllBlockPartials.java +++ b/src/main/java/com/simibubi/create/AllBlockPartials.java @@ -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 BLAZES = new EnumMap<>(BlazeBurnerBlock.HeatLevel.class); - public static final Map METAL_GIRDER_BRACKETS = - new EnumMap<>(Direction.class); + public static final Map METAL_GIRDER_BRACKETS = new EnumMap<>(Direction.class); public static final Map TOOLBOX_LIDS = new EnumMap<>(DyeColor.class); static { diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index 27efa6e41..1676759d4 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -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 MYSTERIOUS_CUCKOO_CLOCK = @@ -528,7 +545,9 @@ public class AllBlocks { public static final BlockEntry 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 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 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 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 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 ORANGE_NIXIE_TUBE = REGISTRATE.block("nixie_tube", p -> new NixieTubeBlock(p, DyeColor.ORANGE)) .initialProperties(SharedProperties::softMetal) diff --git a/src/main/java/com/simibubi/create/AllShapes.java b/src/main/java/com/simibubi/create/AllShapes.java index f67677bc6..c3f9ee2cd 100644 --- a/src/main/java/com/simibubi/create/AllShapes.java +++ b/src/main/java/com/simibubi/create/AllShapes.java @@ -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(), diff --git a/src/main/java/com/simibubi/create/AllTileEntities.java b/src/main/java/com/simibubi/create/AllTileEntities.java index a1edc09bf..2c21ed6f6 100644 --- a/src/main/java/com/simibubi/create/AllTileEntities.java +++ b/src/main/java/com/simibubi/create/AllTileEntities.java @@ -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 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 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 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 STEAM_ENGINE = Create.registrate() .tileEntity("steam_engine", SteamEngineTileEntity::new) .validBlocks(AllBlocks.STEAM_ENGINE) .renderer(() -> SteamEngineRenderer::new) .register(); - + public static final BlockEntityEntry 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 DATA_GATHERER = Create.registrate() + .tileEntity("data_gatherer", DataGathererTileEntity::new) + .validBlocks(AllBlocks.DATA_GATHERER) .register(); public static final BlockEntityEntry STOCKPILE_SWITCH = Create.registrate() @@ -706,20 +721,18 @@ public class AllTileEntities { .renderer(() -> BrassDiodeRenderer::new) .register(); - public static final BlockEntityEntry 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 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 LECTERN_CONTROLLER = - Create.registrate() - .tileEntity("lectern_controller", LecternControllerTileEntity::new) - .validBlocks(AllBlocks.LECTERN_CONTROLLER) - .renderer(() -> LecternControllerRenderer::new) - .register(); + public static final BlockEntityEntry LECTERN_CONTROLLER = Create.registrate() + .tileEntity("lectern_controller", LecternControllerTileEntity::new) + .validBlocks(AllBlocks.LECTERN_CONTROLLER) + .renderer(() -> LecternControllerRenderer::new) + .register(); // Curiosities public static final BlockEntityEntry COPPER_BACKTANK = Create.registrate() @@ -766,7 +779,14 @@ public class AllTileEntities { .renderer(() -> StationRenderer::new) .validBlocks(AllBlocks.TRACK_STATION) .register(); - + + public static final BlockEntityEntry FLAP_DISPLAY = Create.registrate() + .tileEntity("flap_display", FlapDisplayTileEntity::new) + .instance(() -> MechanicalCrafterInstance::new) + .renderer(() -> FlapDisplayRenderer::new) + .validBlocks(AllBlocks.FLAP_DISPLAY) + .register(); + public static final BlockEntityEntry TRACK_SIGNAL = Create.registrate() .tileEntity("track_signal", SignalTileEntity::new) .renderer(() -> SignalRenderer::new) diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index 3cecd12fe..fa35898f1 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -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(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/BeltPressingCallbacks.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/BeltPressingCallbacks.java index 1af77a41f..def61353d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/BeltPressingCallbacks.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/BeltPressingCallbacks.java @@ -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 recipe = pressTe.getRecipe(transported.stack); - pressTe.pressedItems.clear(); - pressTe.pressedItems.add(transported.stack); - - if (!recipe.isPresent()) + behaviour.particleItems.clear(); + ArrayList 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 collect = InWorldProcessing - .applyRecipeOn(bulk ? transported.stack : ItemHandlerHelper.copyStackWithSize(transported.stack, 1), - recipe.get()) - .stream() + List 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; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java index 43e315100..44302ce7c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressRenderer.java @@ -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 diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java index b96180260..d18cffdeb 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/MechanicalPressTileEntity.java @@ -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 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 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 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 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 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 recipe = getRecipe(item); - if (!recipe.isPresent()) - continue; + @Override + public boolean tryProcessInWorld(ItemEntity itemEntity, boolean simulate) { + ItemStack item = itemEntity.getItem(); + Optional 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 outputList, boolean simulate) { + Optional 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 boolean matchStaticFilters(Recipe 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; - } - } - } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java index ff842943e..7cb472d97 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java @@ -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(); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingBehaviour.java new file mode 100644 index 000000000..5297f1bb1 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressingBehaviour.java @@ -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 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 outputList, boolean simulate); + + public boolean tryProcessInWorld(ItemEntity itemEntity, boolean simulate); + + public boolean canProcessInBulk(); + + public void onPressingCompleted(); + + public int getParticleAmount(); + + public float getKineticSpeed(); + } + + public 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; + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java index 582cf6014..c70ff7ba0 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java @@ -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)) diff --git a/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java index 29ae93931..9d0a7fef4 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java +++ b/src/main/java/com/simibubi/create/content/curiosities/girder/GirderBlock.java @@ -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; diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/AllDataGathererBehaviours.java b/src/main/java/com/simibubi/create/content/logistics/block/data/AllDataGathererBehaviours.java new file mode 100644 index 000000000..54f828fbf --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/AllDataGathererBehaviours.java @@ -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 + + GATHERER_BEHAVIOURS = new HashMap<>(); + + public static final HashMap> + + SOURCES_BY_BLOCK = new HashMap<>(), SOURCES_BY_TILE = new HashMap<>(); + + public static final HashMap + + 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 NonNullConsumer 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 > NonNullConsumer 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 sourcesOf(LevelAccessor level, BlockPos pos) { + BlockState blockState = level.getBlockState(pos); + BlockEntity blockEntity = level.getBlockEntity(pos); + + List sourcesOfBlock = sourcesOf(blockState); + List 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 sourcesOf(BlockState state) { + return sourcesOf(state.getBlock()); + } + + public static List sourcesOf(BlockEntity tileEntity) { + return SOURCES_BY_TILE.getOrDefault(tileEntity.getType() + .getRegistryName(), Collections.emptyList()); + } + + public static List 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); + } +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBehaviour.java new file mode 100644 index 000000000..5bc21a0bd --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBehaviour.java @@ -0,0 +1,9 @@ +package com.simibubi.create.content.logistics.block.data; + +import net.minecraft.resources.ResourceLocation; + +public abstract class DataGathererBehaviour { + + public ResourceLocation id; + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlock.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlock.java new file mode 100644 index 000000000..edd8f1938 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlock.java @@ -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 { + + 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 void sendToGatherers(LevelAccessor level, BlockPos pos, + BiConsumer callback, Class 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 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 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 getTileEntityClass() { + return DataGathererTileEntity.class; + } + + @Override + public BlockEntityType getTileEntityType() { + return AllTileEntities.DATA_GATHERER.get(); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlockItem.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlockItem.java new file mode 100644 index 000000000..8ff837821 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererBlockItem.java @@ -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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererConfigurationPacket.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererConfigurationPacket.java new file mode 100644 index 000000000..3f5a55828 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererConfigurationPacket.java @@ -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 { + + 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(); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererContext.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererContext.java new file mode 100644 index 000000000..813fe3fb7 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererContext.java @@ -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(); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererScreen.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererScreen.java new file mode 100644 index 000000000..85f7e9dfd --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererScreen.java @@ -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 sources; + DataGathererTarget target; + + ScrollInput sourceTypeSelector; + Label sourceTypeLabel; + ScrollInput targetLineSelector; + Label targetLineLabel; + + Couple>> 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 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> targetSet; + private int x; + private int y; + + public LineBuilder(Set> targetSet, int x, int y) { + this.targetSet = targetSet; + this.x = x; + this.y = y; + } + + public LineBuilder addScrollInput(int x, int width, BiConsumer 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 inputTransform, String dataKey) { + SelectionScrollInput input = new SelectionScrollInput(x + this.x, y - 4, width, 18); + addScrollInput(input, inputTransform, dataKey); + return this; + } + + private void addScrollInput(T input, BiConsumer 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 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 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 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_); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererTileEntity.java new file mode 100644 index 000000000..f80948689 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/DataGathererTileEntity.java @@ -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 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 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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/AccumulatedItemCountDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/AccumulatedItemCountDataSource.java new file mode 100644 index 000000000..b28ca53bc --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/AccumulatedItemCountDataSource.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/DataGathererSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/DataGathererSource.java new file mode 100644 index 000000000..5964f11a9 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/DataGathererSource.java @@ -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 EMPTY = ImmutableList.of(new TextComponent("")); + public static final MutableComponent EMPTY_LINE = new TextComponent(""); + public static final MutableComponent WHITESPACE = new TextComponent(" "); + + public abstract List 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> flapDisplayText = provideFlapDisplayText(context, stats); + fddt.acceptFlapText(line, flapDisplayText, context); + return; + } + + List 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> 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) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/DeathCounterDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/DeathCounterDataSource.java new file mode 100644 index 000000000..d40d3ec49 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/DeathCounterDataSource.java @@ -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"); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/FillLevelDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/FillLevelDataSource.java new file mode 100644 index 000000000..0b63a0a2d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/FillLevelDataSource.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemCountDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemCountDataSource.java new file mode 100644 index 000000000..242195704 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemCountDataSource.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemListDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemListDataSource.java new file mode 100644 index 000000000..fdfbb0274 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemListDataSource.java @@ -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> provideEntries(DataGathererContext context, int maxRows) { + BlockEntity sourceTE = context.getSourceTE(); + if (!(sourceTE instanceof ContentObserverTileEntity cote)) + return new ArrayList>().stream(); + + InvManipulationBehaviour invManipulationBehaviour = cote.getBehaviour(InvManipulationBehaviour.TYPE); + FilteringBehaviour filteringBehaviour = cote.getBehaviour(FilteringBehaviour.TYPE); + IItemHandler handler = invManipulationBehaviour.getInventory(); + + if (handler == null) + return new ArrayList>().stream(); + + return new CountedItemStackList(handler, filteringBehaviour).getTopNames(maxRows); + } + + @Override + protected String getTranslationKey() { + return "list_items"; + } + + @Override + protected boolean valueFirst() { + return true; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemNameDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemNameDataSource.java new file mode 100644 index 000000000..f766cdc00 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemNameDataSource.java @@ -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 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"; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemThoughputDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemThoughputDataSource.java new file mode 100644 index 000000000..138490835 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ItemThoughputDataSource.java @@ -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"; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/NixieTubeDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/NixieTubeDataSource.java new file mode 100644 index 000000000..a5b91d9f3 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/NixieTubeDataSource.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/NumericSingleLineDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/NumericSingleLineDataSource.java new file mode 100644 index 000000000..44d3e360f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/NumericSingleLineDataSource.java @@ -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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ScoreboardDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ScoreboardDataSource.java new file mode 100644 index 000000000..b63adc95c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ScoreboardDataSource.java @@ -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> provideEntries(DataGathererContext context, int maxRows) { + Level level = context.te() + .getLevel(); + if (!(level instanceof ServerLevel sLevel)) + return new ArrayList>().stream(); + + String name = context.sourceConfig() + .getString("Objective"); + + return showScoreboard(sLevel, name, maxRows); + } + + protected Stream> 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> 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/SingleLineDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/SingleLineDataSource.java new file mode 100644 index 000000000..837a78b50 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/SingleLineDataSource.java @@ -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 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> 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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/StatTrackingDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StatTrackingDataSource.java new file mode 100644 index 000000000..95c4e9bef --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StatTrackingDataSource.java @@ -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> provideEntries(DataGathererContext context, int maxRows) { + Level level = context.te() + .getLevel(); + if (!(level instanceof ServerLevel sLevel)) + return new ArrayList>().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) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/StationSummaryDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StationSummaryDataSource.java new file mode 100644 index 000000000..8f67d193b --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StationSummaryDataSource.java @@ -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 EMPTY_ENTRY_4 = + ImmutableList.of(WHITESPACE, new TextComponent(" . "), WHITESPACE, WHITESPACE); + protected static final List EMPTY_ENTRY_5 = + ImmutableList.of(WHITESPACE, new TextComponent(" . "), WHITESPACE, WHITESPACE, WHITESPACE); + + @Override + public List provideText(DataGathererContext context, DataTargetStats stats) { + return EMPTY; + } + + @Override + public List> provideFlapDisplayText(DataGathererContext context, DataTargetStats stats) { + String filter = context.sourceConfig() + .getString("Filter"); + boolean hasPlatform = filter.contains("*"); + + List> list = new ArrayList<>(); + GlobalTrainDisplayData.prepare(filter, stats.maxRows()) + .forEach(prediction -> { + List 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 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"); + + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/StopWatchDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StopWatchDataSource.java new file mode 100644 index 000000000..a3bc0082a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/StopWatchDataSource.java @@ -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"; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/TimeOfDayDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/TimeOfDayDataSource.java new file mode 100644 index 000000000..06dcd14af --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/TimeOfDayDataSource.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/source/ValueListDataSource.java b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ValueListDataSource.java new file mode 100644 index 000000000..4e71a6ae2 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/source/ValueListDataSource.java @@ -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> provideEntries(DataGathererContext context, int maxRows); + + protected abstract boolean valueFirst(); + + @Override + public List provideText(DataGathererContext context, DataTargetStats stats) { + boolean isBook = context.getTargetTE() instanceof LecternBlockEntity; + + List 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 condensePages(List list) { + List 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> 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 createComponentsFromEntry(DataGathererContext context, + IntAttached entry) { + int number = entry.getFirst(); + MutableComponent name = entry.getSecond() + .append(WHITESPACE); + + if (shortenNumbers(context)) { + Couple 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 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"); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataGathererTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataGathererTarget.java new file mode 100644 index 000000000..e5a38e571 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataGathererTarget.java @@ -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 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataTargetStats.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataTargetStats.java new file mode 100644 index 000000000..af4c19c89 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/DataTargetStats.java @@ -0,0 +1,5 @@ +package com.simibubi.create.content.logistics.block.data.target; + +public record DataTargetStats(int maxRows, int maxColumns, DataGathererTarget type) { + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/FlapDisplayDataTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/FlapDisplayDataTarget.java new file mode 100644 index 000000000..aba3989fc --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/FlapDisplayDataTarget.java @@ -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 text, DataGathererContext context) {} + + public void acceptFlapText(int line, List> text, DataGathererContext context) { + FlapDisplayTileEntity controller = getController(context); + if (controller == null) + return; + if (!controller.isSpeedRequirementFulfilled()) + return; + + DataGathererSource source = context.te().activeSource; + List 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 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)); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/LecternDataTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/LecternDataTarget.java new file mode 100644 index 000000000..7d2d79f3d --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/LecternDataTarget.java @@ -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 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/NixieTubeDataTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/NixieTubeDataTarget.java new file mode 100644 index 000000000..a516db76c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/NixieTubeDataTarget.java @@ -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 start = new MutableObject<>(null); + MutableObject 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)); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/SignDataTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/SignDataTarget.java new file mode 100644 index 000000000..ab771a23a --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/SignDataTarget.java @@ -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 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); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/data/target/SingleLineDataTarget.java b/src/main/java/com/simibubi/create/content/logistics/block/data/target/SingleLineDataTarget.java new file mode 100644 index 000000000..59248dd91 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/block/data/target/SingleLineDataTarget.java @@ -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 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); + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTargetHandler.java b/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTargetHandler.java index 725752fc3..c1cf6d8b9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTargetHandler.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTargetHandler.java @@ -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(); diff --git a/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeBlock.java b/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeBlock.java index f36c07932..be5b927d8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeBlock.java @@ -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 { 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 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 diff --git a/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeGenerator.java b/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeGenerator.java index 6f52f2d1c..a1ee76859 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeGenerator.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/diodes/BrassDiodeGenerator.java @@ -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); } diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java index 715e12796..6d72a9c41 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeBlock.java @@ -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 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 diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeRenderer.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeRenderer.java index efc153b6a..530213259 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeRenderer.java @@ -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 { - private Random r = new Random(); - - public static final Map> DYE_TABLE = new ImmutableMap.Builder>() - - // 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 couple = DYE_TABLE.get(color); + Couple couple = DyeHelper.DYE_TABLE.get(color); int brightColor = couple.getFirst(); int darkColor = couple.getSecond(); int flickeringBrightColor = Color.mixColors(brightColor, darkColor, flicker / 4); diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeTileEntity.java index dc84b5677..141d74a20 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/NixieTubeTileEntity.java @@ -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 EMPTY = Couple.create("", ""); - private boolean hasCustomText; private int redstoneStrength; - private JsonElement rawCustomText; - private int customTextIndex; - private Component parsedCustomText; + private Optional customText; + private int nixieIndex; private Couple displayedStrings; private WeakReference 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 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 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 behaviours) {} diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/StockpileSwitchTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/StockpileSwitchTileEntity.java index 7381a2bed..45d11b7b1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/StockpileSwitchTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/StockpileSwitchTileEntity.java @@ -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() { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java index c0cecc5ec..71c1f257e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java @@ -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); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java index 4593f30d1..7456211f8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java @@ -50,6 +50,7 @@ public class Navigation { public GlobalStation destination; public double distanceToDestination; + public double distanceStartedAt; public boolean destinationBehindTrain; List> currentPath; @@ -135,7 +136,8 @@ public class Navigation { MutableObject> 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), diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java new file mode 100644 index 000000000..5a5fcb92c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayBlock.java @@ -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, 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 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 getTileEntityClass() { + return FlapDisplayTileEntity.class; + } + + @Override + public BlockEntityType 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 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)); + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayLayout.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayLayout.java new file mode 100644 index 000000000..192467846 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayLayout.java @@ -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 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 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 getSections() { + return sections; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayRenderer.java new file mode 100644 index 000000000..a1608d554 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayRenderer.java @@ -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 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 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplaySection.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplaySection.java new file mode 100644 index 000000000..be4a4d621 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplaySection.java @@ -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 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(";")); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayTileEntity.java new file mode 100644 index 000000000..6d651bd87 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/FlapDisplayTileEntity.java @@ -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 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 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 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 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 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 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 behaviours) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/display/GlobalTrainDisplayData.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/GlobalTrainDisplayData.java new file mode 100644 index 000000000..605d6c03f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/display/GlobalTrainDisplayData.java @@ -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> 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 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 { + 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; + } + + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java index bcde5a8d9..5d3a69338 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java @@ -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 conditionProgress; List conditionContext; + int ticksInTransit; + List 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> 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 submitPredictions() { + Collection 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 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 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]); + } } } diff --git a/src/main/java/com/simibubi/create/content/palettes/ConnectedPillarBlock.java b/src/main/java/com/simibubi/create/content/palettes/ConnectedPillarBlock.java index a2a1988ac..4eef69e8f 100644 --- a/src/main/java/com/simibubi/create/content/palettes/ConnectedPillarBlock.java +++ b/src/main/java/com/simibubi/create/content/palettes/ConnectedPillarBlock.java @@ -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); } diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 845b548b0..08f3df72d 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -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 diff --git a/src/main/java/com/simibubi/create/foundation/config/CLogistics.java b/src/main/java/com/simibubi/create/foundation/config/CLogistics.java index 2ea464037..028f24118 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CLogistics.java +++ b/src/main/java/com/simibubi/create/foundation/config/CLogistics.java @@ -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."; diff --git a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java index 922655f5d..0a9c72953 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java +++ b/src/main/java/com/simibubi/create/foundation/gui/AllGuiTextures.java @@ -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), diff --git a/src/main/java/com/simibubi/create/foundation/gui/widget/AbstractSimiWidget.java b/src/main/java/com/simibubi/create/foundation/gui/widget/AbstractSimiWidget.java index e45297ae1..85cdd4ae1 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/widget/AbstractSimiWidget.java +++ b/src/main/java/com/simibubi/create/foundation/gui/widget/AbstractSimiWidget.java @@ -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 toolTip = new LinkedList<>(); diff --git a/src/main/java/com/simibubi/create/foundation/gui/widget/IconButton.java b/src/main/java/com/simibubi/create/foundation/gui/widget/IconButton.java index d85e75e70..366eb4177 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/widget/IconButton.java +++ b/src/main/java/com/simibubi/create/foundation/gui/widget/IconButton.java @@ -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; diff --git a/src/main/java/com/simibubi/create/foundation/gui/widget/ScrollInput.java b/src/main/java/com/simibubi/create/foundation/gui/widget/ScrollInput.java index 8517da162..7d8be1727 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/widget/ScrollInput.java +++ b/src/main/java/com/simibubi/create/foundation/gui/widget/ScrollInput.java @@ -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 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)); } diff --git a/src/main/java/com/simibubi/create/foundation/gui/widget/SelectionScrollInput.java b/src/main/java/com/simibubi/create/foundation/gui/widget/SelectionScrollInput.java index 940315f4e..c307d7844 100644 --- a/src/main/java/com/simibubi/create/foundation/gui/widget/SelectionScrollInput.java +++ b/src/main/java/com/simibubi/create/foundation/gui/widget/SelectionScrollInput.java @@ -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 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)); } } diff --git a/src/main/java/com/simibubi/create/foundation/gui/widget/TooltipArea.java b/src/main/java/com/simibubi/create/foundation/gui/widget/TooltipArea.java new file mode 100644 index 000000000..db256b4a4 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/gui/widget/TooltipArea.java @@ -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 tooltip) { + this.toolTip = tooltip; + return this; + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/item/CountedItemStackList.java b/src/main/java/com/simibubi/create/foundation/item/CountedItemStackList.java new file mode 100644 index 000000000..e4c683603 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/item/CountedItemStackList.java @@ -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> 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> 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 stackSet = getOrCreateItemSet(stack); + for (ItemStackEntry entry : stackSet) { + if (!entry.matches(stack)) + continue; + entry.grow(amount); + return; + } + stackSet.add(new ItemStackEntry(stack, amount)); + } + + private Set getOrCreateItemSet(ItemStack stack) { + if (!items.containsKey(stack.getItem())) + items.put(stack.getItem(), new HashSet<>()); + return getItemSet(stack); + } + + private Set getItemSet(ItemStack stack) { + return items.get(stack.getItem()); + } + + public static class ItemStackEntry extends IntAttached { + + 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(); + } + + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index 63f570650..6af6c64f6 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -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), diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/BeltScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/BeltScenes.java index 7e42ff7ab..edaa54e0f 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/BeltScenes.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/BeltScenes.java @@ -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 spout = scene.world.showIndependentSection(util.select.position(depotPos.above(2) - .west()), Direction.SOUTH); + ElementLink 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 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); diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/ProcessingScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/ProcessingScenes.java index f72b00fd2..dab04a9a6 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/ProcessingScenes.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/ProcessingScenes.java @@ -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 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 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 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 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); diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java index a7a333a82..0c9f4151c 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java @@ -28,8 +28,8 @@ public abstract class SmartTileEntity extends CachedRenderBBTileEntity implement private final Map, 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; diff --git a/src/main/java/com/simibubi/create/foundation/utility/DyeHelper.java b/src/main/java/com/simibubi/create/foundation/utility/DyeHelper.java index d302c110e..b9d690b98 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/DyeHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/DyeHelper.java @@ -1,5 +1,9 @@ package com.simibubi.create.foundation.utility; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Blocks; @@ -44,4 +48,29 @@ public class DyeHelper { } } + public static final Map> DYE_TABLE = new ImmutableMap.Builder>() + + // 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(); + } diff --git a/src/main/java/com/simibubi/create/foundation/utility/DynamicComponent.java b/src/main/java/com/simibubi/create/foundation/utility/DynamicComponent.java new file mode 100644 index 000000000..25d982d7a --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/DynamicComponent.java @@ -0,0 +1,96 @@ +package com.simibubi.create.foundation.utility; + +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.Create; + +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +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.world.level.Level; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; + +public class DynamicComponent { + + private JsonElement rawCustomText; + private Component parsedCustomText; + + public DynamicComponent() {} + + public void displayCustomText(Level level, BlockPos pos, String tagElement) { + if (tagElement == null) + return; + + rawCustomText = getJsonFromString(tagElement); + parsedCustomText = parseCustomText(level, pos, rawCustomText); + } + + public boolean sameAs(String tagElement) { + return isValid() && rawCustomText.equals(getJsonFromString(tagElement)); + } + + public boolean isValid() { + return parsedCustomText != null && rawCustomText != null; + } + + public String resolve() { + return parsedCustomText.getString(); + } + + public MutableComponent get() { + return parsedCustomText == null ? TextComponent.EMPTY.copy() : parsedCustomText.copy(); + } + + public void read(Level level, BlockPos pos, CompoundTag nbt) { + rawCustomText = getJsonFromString(nbt.getString("RawCustomText")); + try { + parsedCustomText = Component.Serializer.fromJson(nbt.getString("CustomText")); + } catch (JsonParseException e) { + parsedCustomText = null; + } + } + + public void write(CompoundTag nbt) { + if (!isValid()) + return; + + nbt.putString("RawCustomText", rawCustomText.toString()); + nbt.putString("CustomText", Component.Serializer.toJson(parsedCustomText)); + } + + public static JsonElement getJsonFromString(String string) { + try { + return JsonParser.parseString(string); + } catch (JsonParseException e) { + return null; + } + } + + public static Component parseCustomText(Level level, BlockPos pos, JsonElement customText) { + if (!(level instanceof ServerLevel serverLevel)) + return null; + try { + return ComponentUtils.updateForEntity(getCommandSource(serverLevel, pos), + Component.Serializer.fromJson(customText), null, 0); + } catch (JsonParseException e) { + return null; + } catch (CommandSyntaxException e) { + return null; + } + } + + public static CommandSourceStack getCommandSource(ServerLevel level, BlockPos pos) { + return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, level, 2, Create.ID, + new TextComponent(Create.ID), level.getServer(), null); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/IntAttached.java b/src/main/java/com/simibubi/create/foundation/utility/IntAttached.java index f49436c92..884258200 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/IntAttached.java +++ b/src/main/java/com/simibubi/create/foundation/utility/IntAttached.java @@ -1,5 +1,6 @@ package com.simibubi.create.foundation.utility; +import java.util.Comparator; import java.util.function.Function; import net.minecraft.nbt.CompoundTag; @@ -49,6 +50,10 @@ public class IntAttached extends Pair { return nbt; } + public static Comparator> comparator() { + return (i1, i2) -> Integer.compare(i2.getFirst(), i1.getFirst()); + } + public static IntAttached read(CompoundTag nbt, Function deserializer) { return IntAttached.with(nbt.getInt("Location"), deserializer.apply(nbt.getCompound("Item"))); } diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index f682b6224..24ce619d4 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -656,6 +656,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": "\u2588;\u2592", + "create.gui.config.overlay1": "Hi :)", "create.gui.config.overlay2": "This is a sample overlay", "create.gui.config.overlay3": "Click or drag with your mouse", diff --git a/src/main/resources/assets/create/models/block/data_gatherer/block.json b/src/main/resources/assets/create/models/block/data_gatherer/block.json new file mode 100644 index 000000000..34509d57f --- /dev/null +++ b/src/main/resources/assets/create/models/block/data_gatherer/block.json @@ -0,0 +1,111 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "create:block/data_gatherer", + "2": "create:block/smooth_dark_log_top", + "3": "create:block/data_gatherer_side", + "particle": "create:block/brass_casing" + }, + "elements": [ + { + "from": [9, 6, 3], + "to": [13, 9, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [14.5, 0, 12.5, 1.5], "texture": "#0"}, + "east": {"uv": [14.5, 6, 16, 1.5], "rotation": 90, "texture": "#0"}, + "south": {"uv": [12.5, 0, 14.5, 1.5], "texture": "#0"}, + "west": {"uv": [14.5, 6, 16, 1.5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [14.5, 1.5, 12.5, 6], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [1, 1, 1], + "to": [15, 6, 15], + "faces": { + "north": {"uv": [0, 0, 14, 5], "texture": "#3"}, + "east": {"uv": [7.5, 8.5, 10, 15.5], "rotation": 90, "texture": "#0"}, + "south": {"uv": [0, 0, 14, 5], "texture": "#3"}, + "west": {"uv": [7.5, 8.5, 10, 15.5], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 6, 7, 13], "texture": "#0"}, + "down": {"uv": [1, 1, 15, 15], "texture": "#2"} + } + }, + { + "from": [2, -1, 2], + "to": [14, 1, 14], + "faces": { + "north": {"uv": [0.5, 14.5, 6.5, 15.5], "texture": "#0"}, + "east": {"uv": [0.5, 14.5, 6.5, 15.5], "texture": "#0"}, + "south": {"uv": [0.5, 14.5, 6.5, 15.5], "texture": "#0"}, + "west": {"uv": [0.5, 14.5, 6.5, 15.5], "texture": "#0"}, + "down": {"uv": [0, 0, 6, 6], "texture": "#0"} + } + }, + { + "from": [3, 5, 3], + "to": [7, 7, 7], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [13.5, 10, 11.5, 11], "texture": "#0"}, + "east": {"uv": [13.5, 10, 11.5, 11], "texture": "#0"}, + "south": {"uv": [13.5, 10, 11.5, 11], "texture": "#0"}, + "west": {"uv": [13.5, 10, 11.5, 11], "texture": "#0"}, + "up": {"uv": [13.5, 8, 11.5, 10], "texture": "#0"} + } + }, + { + "from": [3, 6, 8], + "to": [7, 8, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [13.5, 8, 11.5, 9], "texture": "#0"}, + "east": {"uv": [13.5, 8, 11.5, 9], "texture": "#0"}, + "south": {"uv": [13.5, 8, 11.5, 9], "texture": "#0"}, + "west": {"uv": [13.5, 8, 11.5, 9], "texture": "#0"}, + "up": {"uv": [13.5, 6, 11.5, 8], "texture": "#0"} + } + }, + { + "from": [2.5, 8, 7.5], + "to": [7.5, 13, 12.5], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [16, 8.5, 13.5, 11], "texture": "#0"}, + "east": {"uv": [16, 8.5, 13.5, 11], "texture": "#0"}, + "south": {"uv": [16, 8.5, 13.5, 11], "texture": "#0"}, + "west": {"uv": [16, 8.5, 13.5, 11], "texture": "#0"}, + "up": {"uv": [16, 6, 13.5, 8.5], "texture": "#0"}, + "down": {"uv": [16, 11, 13.5, 13.5], "texture": "#0"} + } + }, + { + "from": [2.5, 8, 9], + "to": [7.5, 13, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [6, 7.5, 19]}, + "faces": { + "south": {"uv": [16, 13.5, 13.5, 16], "texture": "#0"} + } + }, + { + "from": [2.5, 8, 10], + "to": [7.5, 13, 11], + "rotation": {"angle": 0, "axis": "y", "origin": [16, 0, 0]}, + "faces": { + "north": {"uv": [16, 13.5, 13.5, 16], "texture": "#0"} + } + } + ], + "groups": [ + 0, + 1, + 2, + { + "name": "group", + "origin": [16, 0, 0], + "color": 0, + "children": [3, 4, 5, 6, 7] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/data_gatherer/block_powered.json b/src/main/resources/assets/create/models/block/data_gatherer/block_powered.json new file mode 100644 index 000000000..94ef70b7b --- /dev/null +++ b/src/main/resources/assets/create/models/block/data_gatherer/block_powered.json @@ -0,0 +1,6 @@ +{ + "parent": "create:block/data_gatherer/block", + "textures": { + "3": "create:block/data_gatherer_side_powered" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/display_board/block.json b/src/main/resources/assets/create/models/block/display_board/block.json new file mode 100644 index 000000000..c8ee0f1a3 --- /dev/null +++ b/src/main/resources/assets/create/models/block/display_board/block.json @@ -0,0 +1,58 @@ +{ + "credit": "Made with Blockbench", + "parent": "create:block/large_wheels", + "textures": { + "2": "create:block/flap_display_front", + "3": "create:block/dark_metal_block", + "4": "create:block/flap_display_side", + "particle": "create:block/flap_display_front" + }, + "elements": [ + { + "from": [0, 0, 3], + "to": [16, 16, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 6]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, + "east": {"uv": [13, 0, 16, 16], "texture": "#4"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "west": {"uv": [0, 0, 3, 16], "texture": "#4"}, + "up": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [0, 0, 10], + "to": [16, 16, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 6]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "east": {"uv": [10, 0, 13, 16], "texture": "#4"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "west": {"uv": [10, 0, 13, 16], "texture": "#4"}, + "up": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [0, 0, 6], + "to": [16, 16, 10], + "faces": { + "east": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "west": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "up": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [15.95, 0.05, 6], + "to": [0.05, 15.95, 10], + "faces": { + "east": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "west": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "up": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/block/display_board/item.json b/src/main/resources/assets/create/models/block/display_board/item.json new file mode 100644 index 000000000..e4bdba618 --- /dev/null +++ b/src/main/resources/assets/create/models/block/display_board/item.json @@ -0,0 +1,155 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "2": "create:block/flap_display_front", + "3": "create:block/dark_metal_block", + "4": "create:block/flap_display_side", + "1_2": "create:block/cogwheel", + "particle": "create:block/flap_display_front" + }, + "elements": [ + { + "name": "Gear", + "from": [-1, 6.5, 6.5], + "to": [17, 9.5, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 6, 16, 7.5], "rotation": 180, "texture": "#1_2"}, + "east": {"uv": [5, 8, 6.5, 9.5], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [7, 6, 16, 7.5], "texture": "#1_2"}, + "west": {"uv": [5, 8, 6.5, 9.5], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [7, 8, 16, 9.5], "texture": "#1_2"}, + "down": {"uv": [7, 8, 16, 9.5], "rotation": 180, "texture": "#1_2"} + } + }, + { + "name": "Gear2", + "from": [-1, 6.5, 6.5], + "to": [17, 9.5, 9.5], + "rotation": {"angle": 45, "axis": "z", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 6, 16, 7.5], "rotation": 180, "texture": "#1_2"}, + "east": {"uv": [5, 8, 6.5, 9.5], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [7, 6, 16, 7.5], "texture": "#1_2"}, + "west": {"uv": [5, 8, 6.5, 9.5], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [7, 8, 16, 9.5], "texture": "#1_2"}, + "down": {"uv": [7, 8, 16, 9.5], "rotation": 180, "texture": "#1_2"} + } + }, + { + "name": "Gear3", + "from": [-1, 6.5, 6.5], + "to": [17, 9.5, 9.5], + "rotation": {"angle": -45, "axis": "z", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 6, 16, 7.5], "rotation": 180, "texture": "#1_2"}, + "east": {"uv": [5, 8, 6.5, 9.5], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [7, 6, 16, 7.5], "texture": "#1_2"}, + "west": {"uv": [5, 8, 6.5, 9.5], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [7, 8, 16, 9.5], "texture": "#1_2"}, + "down": {"uv": [7, 8, 16, 9.5], "rotation": 180, "texture": "#1_2"} + } + }, + { + "name": "Gear4", + "from": [6.5, -1, 6.5], + "to": [9.5, 17, 9.5], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [7, 6, 16, 7.5], "rotation": 270, "texture": "#1_2"}, + "east": {"uv": [7, 8, 16, 9.5], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [7, 6, 16, 7.5], "rotation": 90, "texture": "#1_2"}, + "west": {"uv": [7, 8, 16, 9.5], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [5, 8, 6.5, 9.5], "texture": "#1_2"}, + "down": {"uv": [5, 8, 6.5, 9.5], "rotation": 180, "texture": "#1_2"} + } + }, + { + "name": "GearCaseInner", + "from": [2, 2, 6.55], + "to": [14, 14, 9.45], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [4, 0, 10, 6], "rotation": 180, "texture": "#1_2"}, + "east": {"uv": [0, 6, 6, 7.5], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [4, 0, 10, 6], "texture": "#1_2"}, + "west": {"uv": [0, 6, 6, 7.5], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [0, 6, 6, 7.5], "texture": "#1_2"}, + "down": {"uv": [0, 6, 6, 7.5], "rotation": 180, "texture": "#1_2"} + } + }, + { + "name": "GearCaseOuter", + "from": [4, 4, 6], + "to": [12, 12, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [0, 0, 4, 4], "rotation": 180, "texture": "#1_2"}, + "east": {"uv": [0, 4, 4, 6], "rotation": 90, "texture": "#1_2"}, + "south": {"uv": [0, 0, 4, 4], "texture": "#1_2"}, + "west": {"uv": [0, 4, 4, 6], "rotation": 270, "texture": "#1_2"}, + "up": {"uv": [0, 4, 4, 6], "texture": "#1_2"}, + "down": {"uv": [0, 4, 4, 6], "rotation": 180, "texture": "#1_2"} + } + }, + { + "from": [0, 0, 3], + "to": [16, 16, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 6]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, + "east": {"uv": [13, 0, 16, 16], "texture": "#4"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "west": {"uv": [0, 0, 3, 16], "texture": "#4"}, + "up": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [0, 0, 10], + "to": [16, 16, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 6]}, + "faces": { + "north": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "east": {"uv": [0, 0, 3, 16], "texture": "#4"}, + "south": {"uv": [0, 0, 16, 16], "texture": "#3"}, + "west": {"uv": [0, 0, 3, 16], "texture": "#4"}, + "up": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [7, 0, 10, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [0, 0, 6], + "to": [16, 16, 10], + "faces": { + "east": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "west": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "up": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"} + } + }, + { + "from": [15.95, 0.05, 6], + "to": [0.05, 15.95, 10], + "faces": { + "east": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "west": {"uv": [3, 0, 7, 16], "texture": "#4"}, + "up": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"}, + "down": {"uv": [3, 0, 7, 16], "rotation": 90, "texture": "#4"} + } + } + ], + "groups": [ + { + "name": "cogwheel_shaftless", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2, 3, 4, 5] + }, + 6, + 7, + 8, + 9 + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/data_gatherer.png b/src/main/resources/assets/create/textures/block/data_gatherer.png new file mode 100644 index 000000000..5a150a2bc Binary files /dev/null and b/src/main/resources/assets/create/textures/block/data_gatherer.png differ diff --git a/src/main/resources/assets/create/textures/block/data_gatherer_side.png b/src/main/resources/assets/create/textures/block/data_gatherer_side.png new file mode 100644 index 000000000..dceb4df21 Binary files /dev/null and b/src/main/resources/assets/create/textures/block/data_gatherer_side.png differ diff --git a/src/main/resources/assets/create/textures/block/data_gatherer_side_powered.png b/src/main/resources/assets/create/textures/block/data_gatherer_side_powered.png new file mode 100644 index 000000000..1766fbbcb Binary files /dev/null and b/src/main/resources/assets/create/textures/block/data_gatherer_side_powered.png differ diff --git a/src/main/resources/assets/create/textures/block/flap_display_front.png b/src/main/resources/assets/create/textures/block/flap_display_front.png new file mode 100644 index 000000000..88763675f Binary files /dev/null and b/src/main/resources/assets/create/textures/block/flap_display_front.png differ diff --git a/src/main/resources/assets/create/textures/block/flap_display_side.png b/src/main/resources/assets/create/textures/block/flap_display_side.png new file mode 100644 index 000000000..421c45166 Binary files /dev/null and b/src/main/resources/assets/create/textures/block/flap_display_side.png differ diff --git a/src/main/resources/assets/create/textures/gui/data_gatherer.png b/src/main/resources/assets/create/textures/gui/data_gatherer.png new file mode 100644 index 000000000..ee4b20f36 Binary files /dev/null and b/src/main/resources/assets/create/textures/gui/data_gatherer.png differ