Choowo what's this?

- It trains
This commit is contained in:
simibubi 2022-02-01 01:14:21 +01:00
parent 2a4cfef7a8
commit 576d00d3a0
219 changed files with 34359 additions and 98 deletions

View file

@ -49,6 +49,7 @@ b59324f051f21d8ce1a48a08f4721a61a3c414d6 assets/create/blockstates/chute.json
e5e3757e99c139d67b2a70288466d8a74d818841 assets/create/blockstates/cogwheel.json
36f54136a7756c97f71bc6b47ef4e8e575e72879 assets/create/blockstates/content_observer.json
7d11142092c89ccba3e74e0a3bdd0ccb446d63b5 assets/create/blockstates/controller_rail.json
f292ffa3bc036947b655c0a0ce26a91615c3d40b assets/create/blockstates/controls.json
961b615124ea9a5a5735e8a79f81a702de7da2cf assets/create/blockstates/copper_backtank.json
cabf6b8c59eb0e3d56a0a5a856ca058bb3200882 assets/create/blockstates/copper_casing.json
b3d0dee8f6e14fa6c637e98cc1c6f1ea55b5f0d5 assets/create/blockstates/copper_shingle_slab.json
@ -235,6 +236,7 @@ be3bef7e091d8b50bfc1c6b7275946d1f636aefd assets/create/blockstates/horizontal_fr
10ef455fd61ed1ca831d27bf2b533d05dec9c67d assets/create/blockstates/item_vault.json
5d851c90d23de5087ce546d4bbe509e112b84c49 assets/create/blockstates/jungle_window.json
b15bea757ef981e0ca60f740ca234ee2014eb7b7 assets/create/blockstates/jungle_window_pane.json
f8982f0241f33b2cd55d6261ab6ee38cb83b6d8d assets/create/blockstates/large_bogey.json
f651091db216b009b3379b2f48d56d03481c8675 assets/create/blockstates/large_cogwheel.json
a38184e035c2ebca7471e1714494fea213af259e assets/create/blockstates/layered_andesite.json
2409f04042380a8ad086f9c4f98032e85771c3f3 assets/create/blockstates/layered_asurine.json
@ -416,6 +418,7 @@ e05f2e98984127aa6b601c4e4909e4c8207b5407 assets/create/blockstates/small_asurine
636028cb348cf9a0f060b4232cdb5dc4d26a4d40 assets/create/blockstates/small_asurine_brick_stairs.json
d9b5e23652ca70b29a9142ff8f2efd33dfe74904 assets/create/blockstates/small_asurine_brick_wall.json
6fc5be0d465faa59aebe016d4ecb4d5284507bef assets/create/blockstates/small_asurine_bricks.json
f8982f0241f33b2cd55d6261ab6ee38cb83b6d8d assets/create/blockstates/small_bogey.json
2371c092ecbefa9a844889e0a471714d070569dd assets/create/blockstates/small_calcite_brick_slab.json
134ba0452fc721333177695882c8cef3cb7eca8e assets/create/blockstates/small_calcite_brick_stairs.json
7112ac7498acdd196b05af977e7e12cbab29df14 assets/create/blockstates/small_calcite_brick_wall.json
@ -476,6 +479,8 @@ f385988cb6fa9c48b5d59a6942ec50ed2b60c8bf assets/create/blockstates/stockpile_swi
e815bfd854c2653f10828bb11950f7fb991d7efc assets/create/blockstates/stressometer.json
8b0c2c7ac72529565b3339aa8df7565858100afa assets/create/blockstates/tiled_glass.json
a2454400b1cf9889f70aebdc89c52a1be25f543c assets/create/blockstates/tiled_glass_pane.json
180ab07fdae2bedcf66005c572c8e5c220e6bba2 assets/create/blockstates/track.json
aa08785f906d41933e0dd1086ea7b08f5b93aa24 assets/create/blockstates/track_station.json
29af21c8d82891139d48d69f0393f612f2b6f8f1 assets/create/blockstates/tuff_pillar.json
a8094531617e27a545c4815ab2062bf0ffca3633 assets/create/blockstates/turntable.json
c9bf881ea71aa274b2803142456f1bbed9539076 assets/create/blockstates/veridium.json
@ -530,22 +535,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
64a8442735a333c7444cac12cfb22c454ca06b6b assets/create/lang/en_ud.json
7f4222143f17e12d1e89a08d91ff97555f3c945e assets/create/lang/en_us.json
b375c968109083d7839b80ad347a43b8861a45ac assets/create/lang/unfinished/de_de.json
a8b0d479a7817127bc52569167f32378e11420ce assets/create/lang/unfinished/es_cl.json
dda7983a065205140c8092b47ed7d4111c4c28a2 assets/create/lang/unfinished/es_es.json
8c4ff5b38bae4c8bcc2fec698be98ca26fe09145 assets/create/lang/unfinished/fr_fr.json
42281c85374f9583bfb714cc2a634bf59e5cec19 assets/create/lang/unfinished/it_it.json
2a5261a51c544bd30fed6124fb39e386779b589b assets/create/lang/unfinished/ja_jp.json
69f32419986e38bad60fe437f6ea5c07a3a61cf1 assets/create/lang/unfinished/ko_kr.json
f0c2e7e2cdf82a9e8d1ce2d256982bf2050aa63c assets/create/lang/unfinished/nl_nl.json
c3f46417c4b5e3c0a83f5a1affcf21ecd2d4694f assets/create/lang/unfinished/pl_pl.json
9bdaf0b9b51b35fd10934be46954932d58199a21 assets/create/lang/unfinished/pt_br.json
f96ba0f96eb04753ef4431bdda63049174792d12 assets/create/lang/unfinished/pt_pt.json
34f8b480a8af8e449a3dfb8c978bbceeb934824f assets/create/lang/unfinished/ru_ru.json
78690f57ef5d7dfa40026c6a7558df34203a59b5 assets/create/lang/unfinished/zh_cn.json
1116935a6b993de72ec7e15a8f44d06a7de3ebe7 assets/create/lang/unfinished/zh_tw.json
edf2d5f68bb6fc51fb2a1614c42b2b788fd857e2 assets/create/lang/en_ud.json
1b63bdac0063bbc60ef3c93d8b3f97f40d648ee6 assets/create/lang/en_us.json
9f10830c508416004fe2a745eda7e3b4adf10cf8 assets/create/lang/unfinished/de_de.json
9f83f6ce12ee18acfb3c29591ff79adb4986d1d6 assets/create/lang/unfinished/es_cl.json
58885e0c74b14a186d70b2f5167dfee36a7fbb51 assets/create/lang/unfinished/es_es.json
a0f9aec0a4072fda5fd3768003b8b1038be90bb6 assets/create/lang/unfinished/fr_fr.json
6eae75c25b1c10433d8a14d68a90454a2f2f3f6a assets/create/lang/unfinished/it_it.json
66f7cc9e7976f4c917f4b489878c5ee658ab64a0 assets/create/lang/unfinished/ja_jp.json
775a183ea22a2e209d6c7e752c5b0ae438c1d893 assets/create/lang/unfinished/ko_kr.json
40dbedf8545dd976f6f84eb62ef621d399e7bdea assets/create/lang/unfinished/nl_nl.json
3e7f5a1530cc7fd429229150fb956a2357647542 assets/create/lang/unfinished/pl_pl.json
4aa73bd608ae220216abd601b6875ad5bf16974d assets/create/lang/unfinished/pt_br.json
81e4b0e807cd1754c4c3a05c2797f9a1e78ab757 assets/create/lang/unfinished/pt_pt.json
434eea7907c9b816c293f24172d7424747b4c7b8 assets/create/lang/unfinished/ru_ru.json
82d92da2a6e4cab32086f8872387f8a723294d13 assets/create/lang/unfinished/zh_cn.json
3813edf0e800083810ea4d51af2faa5d81ac7a98 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
@ -1618,6 +1623,7 @@ c1da21be9f1af4f7a2ef4ec9cd92195d65ada316 assets/create/models/item/clockwork_bea
dcb09deae110077bcddf090996b51cc66e9a7de3 assets/create/models/item/cogwheel.json
7717e3b21cff39f497f07687c70c1fa40eaa756d assets/create/models/item/content_observer.json
9dbd63c9e1b09a663fd4b83d76e3ab5967086167 assets/create/models/item/controller_rail.json
9a93b3ccef02cd0abd8106edec954dc0f2269229 assets/create/models/item/controls.json
10397036fc0bb1e18a767cfd7b19b10d805a83fe assets/create/models/item/copper_backtank.json
759bcb5fe7dfdd628716f9b4ff19a5ab00393381 assets/create/models/item/copper_casing.json
751324b03f657f4166460eb10a64dae47cb97bd4 assets/create/models/item/copper_nugget.json
@ -1983,6 +1989,7 @@ ef52b3734a47e96c5f83d60da73110e925737933 assets/create/models/item/refined_radia
acfbf487ee65c2c58d89cb2644e33fda75751fde assets/create/models/item/rotation_speed_controller.json
171c343f7f536008f79ea1d63e0a443d064e9ef1 assets/create/models/item/sail_frame.json
5fa0bfe8642e2614a7e97d27af1a95dd2e012097 assets/create/models/item/sand_paper.json
98a33e62ab6ef0d1f7eec886ce56f9b4bd01ce1c assets/create/models/item/schedule.json
3202829de06e9c532dc8a61c955458648b70d645 assets/create/models/item/schematic.json
3f07bc7d4587d78de463ae2ce236e4f363b923cd assets/create/models/item/schematic_and_quill.json
8dd5caa4d7a0ee45bd9b39e09c4503159933d089 assets/create/models/item/schematic_table.json
@ -2066,6 +2073,8 @@ bab8f78c319b2a79ed55c5d2a94b521ddaa44996 assets/create/models/item/stressometer.
088af343cda8a949f1d950e15e72b51ffca20a1d assets/create/models/item/sweet_roll.json
b1d3d00ff05908feacad06a86800da96cc9bc65d assets/create/models/item/tiled_glass.json
a7d0b746637897209bd86b1a6501ecbfb46d8270 assets/create/models/item/tiled_glass_pane.json
6b5569f25fa2d905729a3f18deb56b6c67c5dfa4 assets/create/models/item/track.json
d6364e9d11915e53dafd8761f016e4b23c7703c8 assets/create/models/item/track_station.json
f8a4fa1ccecb16a3941cc46db7481ed8e8429a5e assets/create/models/item/tree_fertilizer.json
3f6810da54724de551591b46cd5b47a98a4737ef assets/create/models/item/tuff_pillar.json
fb24881c4e92bbb7ffa54a71e0af6b1c66d84829 assets/create/models/item/turntable.json
@ -3322,6 +3331,7 @@ bd4ed53c029fcd6e5da7e43ebe4d15030d3fd9de data/create/loot_tables/blocks/clockwor
982a41e1bccd9a130a2874aff995d4f7da0f0316 data/create/loot_tables/blocks/cogwheel.json
c2b075008849e152f20e8da946e89c9722325df6 data/create/loot_tables/blocks/content_observer.json
28856dc862efc6bcc421d035d26386740458f868 data/create/loot_tables/blocks/controller_rail.json
2c2785e39e1891dff2c50cba93e814b56d935154 data/create/loot_tables/blocks/controls.json
3abf04f6132955275ad490668cd28f481afb4ec2 data/create/loot_tables/blocks/copper_backtank.json
8010db6b2427536c74312c85425b3ba83abc363c data/create/loot_tables/blocks/copper_casing.json
31b3e00f6ab3e593a0d6abd42961b3e5e03fc888 data/create/loot_tables/blocks/copper_shingle_slab.json
@ -3507,6 +3517,7 @@ feed1cd9639de957c9b34212d1c4aef2044d1e31 data/create/loot_tables/blocks/item_dra
af3042824de4aff38e27657ebb084077a7d4f925 data/create/loot_tables/blocks/item_vault.json
db23fee08abdb61fe2f200a5016e41523159feef data/create/loot_tables/blocks/jungle_window.json
67f7d9162d3b86e198ab7faa1ddcfdfce605d10c data/create/loot_tables/blocks/jungle_window_pane.json
5c1df8443043b3fe3b665dba348e2ff188bcbe31 data/create/loot_tables/blocks/large_bogey.json
a70fcfe474ba023adc9c326218c5664fbd7b58f8 data/create/loot_tables/blocks/large_cogwheel.json
81013913c3cc88e2390608420a2911d57414bd2c data/create/loot_tables/blocks/layered_andesite.json
a89d357d3b8d7633bffe12a4197ab19cba891005 data/create/loot_tables/blocks/layered_asurine.json
@ -3688,6 +3699,7 @@ af9c104f3d5863191aedf25c83c647de439c215f data/create/loot_tables/blocks/small_as
56aa727f02da49231e52593f4904eff3a93475ab data/create/loot_tables/blocks/small_asurine_brick_stairs.json
172dc3a9a866895035553e6ff166755a24afd0ff data/create/loot_tables/blocks/small_asurine_brick_wall.json
db97cf328010da9b5f643dbfde12660e9f8a33f8 data/create/loot_tables/blocks/small_asurine_bricks.json
5c1df8443043b3fe3b665dba348e2ff188bcbe31 data/create/loot_tables/blocks/small_bogey.json
9a48f87a53b61188cfddbcf217b251ba13ee08df data/create/loot_tables/blocks/small_calcite_brick_slab.json
542fb74a3b02b5f947dbe9bd9d919b631ffd3ff3 data/create/loot_tables/blocks/small_calcite_brick_stairs.json
8b03b9773f2be01cf639c0fedcf69bb05f9a0f7d data/create/loot_tables/blocks/small_calcite_brick_wall.json
@ -3748,6 +3760,8 @@ ad771358ecd71e2f0c4b71f7244947fc2dc2a1c9 data/create/loot_tables/blocks/spruce_w
da3ceb80799d349b91781b0dd43a02e548045c66 data/create/loot_tables/blocks/stressometer.json
811674fd816503cd78fc4df267dc23f760940e8f data/create/loot_tables/blocks/tiled_glass.json
313344ef4ee67ffd0f7fd44adcb3ad08de571c92 data/create/loot_tables/blocks/tiled_glass_pane.json
e2846b8823918bce402eb361f703ecdc14251ccc data/create/loot_tables/blocks/track.json
4617a11e220dcd0094c29d204fe90c01495c4e9b data/create/loot_tables/blocks/track_station.json
8fbe59bc77b029b802c43fb8a930778dede440ea data/create/loot_tables/blocks/tuff_pillar.json
2419d50b6086e92ab05624fdf38ef2b55c0b0944 data/create/loot_tables/blocks/turntable.json
2706ebca18074fe8d7a988d2c0002c857bff5c2b data/create/loot_tables/blocks/veridium.json
@ -5259,8 +5273,8 @@ ff1900963bc4cd8ceffa78d58ef1952ceacb2fb7 data/forge/tags/items/storage_blocks/br
2db7759fe036160c14c6ed19a68604ca16f4de60 data/minecraft/tags/blocks/dripstone_replaceable_blocks.json
69f596fcb065e26b02ce246760432b5174191b76 data/minecraft/tags/blocks/impermeable.json
2db7759fe036160c14c6ed19a68604ca16f4de60 data/minecraft/tags/blocks/lush_ground_replaceable.json
59681910c06f8b7385c6641a409d352056235506 data/minecraft/tags/blocks/mineable/axe.json
f8c66b3808efb61d2d928748783bb7eb8a1357f9 data/minecraft/tags/blocks/mineable/pickaxe.json
02f7a9df2f9e154749266e7ac59c37aa076a3390 data/minecraft/tags/blocks/mineable/axe.json
b52748d3d434dd40ba10db8e97fa2102d7acb638 data/minecraft/tags/blocks/mineable/pickaxe.json
2db7759fe036160c14c6ed19a68604ca16f4de60 data/minecraft/tags/blocks/moss_replaceable.json
e157c1d3af30e409e34bbefbe15a037e6e1c8daa data/minecraft/tags/blocks/needs_iron_tool.json
a08f67865337f62601c5e333b4011382d10020e4 data/minecraft/tags/blocks/needs_stone_tool.json

View file

@ -0,0 +1,34 @@
{
"variants": {
"facing=north,open=false": {
"model": "create:block/controls/block_closed"
},
"facing=south,open=false": {
"model": "create:block/controls/block_closed",
"y": 180
},
"facing=west,open=false": {
"model": "create:block/controls/block_closed",
"y": 270
},
"facing=east,open=false": {
"model": "create:block/controls/block_closed",
"y": 90
},
"facing=north,open=true": {
"model": "create:block/controls/block_open"
},
"facing=south,open=true": {
"model": "create:block/controls/block_open",
"y": 180
},
"facing=west,open=true": {
"model": "create:block/controls/block_open",
"y": 270
},
"facing=east,open=true": {
"model": "create:block/controls/block_open",
"y": 90
}
}
}

View file

@ -0,0 +1,11 @@
{
"variants": {
"axis=x": {
"model": "create:block/track/bogey/top",
"y": 90
},
"axis=z": {
"model": "create:block/track/bogey/top"
}
}
}

View file

@ -0,0 +1,11 @@
{
"variants": {
"axis=x": {
"model": "create:block/track/bogey/top",
"y": 90
},
"axis=z": {
"model": "create:block/track/bogey/top"
}
}
}

View file

@ -0,0 +1,64 @@
{
"variants": {
"shape=none,turn=false": {
"model": "minecraft:block/air"
},
"shape=zo,turn=false": {
"model": "create:block/track/z_ortho"
},
"shape=xo,turn=false": {
"model": "create:block/track/x_ortho"
},
"shape=pd,turn=false": {
"model": "create:block/track/pos_diag"
},
"shape=nd,turn=false": {
"model": "create:block/track/neg_diag"
},
"shape=an,turn=false": {
"model": "create:block/track/ascending",
"y": 180
},
"shape=as,turn=false": {
"model": "create:block/track/ascending"
},
"shape=ae,turn=false": {
"model": "create:block/track/ascending",
"y": 270
},
"shape=aw,turn=false": {
"model": "create:block/track/ascending",
"y": 90
},
"shape=none,turn=true": {
"model": "minecraft:block/air"
},
"shape=zo,turn=true": {
"model": "create:block/track/z_ortho"
},
"shape=xo,turn=true": {
"model": "create:block/track/x_ortho"
},
"shape=pd,turn=true": {
"model": "create:block/track/pos_diag"
},
"shape=nd,turn=true": {
"model": "create:block/track/neg_diag"
},
"shape=an,turn=true": {
"model": "create:block/track/ascending",
"y": 180
},
"shape=as,turn=true": {
"model": "create:block/track/ascending"
},
"shape=ae,turn=true": {
"model": "create:block/track/ascending",
"y": 270
},
"shape=aw,turn=true": {
"model": "create:block/track/ascending",
"y": 90
}
}
}

View file

@ -0,0 +1,34 @@
{
"variants": {
"assembling=false,facing=north": {
"model": "create:block/track_station/block"
},
"assembling=true,facing=north": {
"model": "create:block/track_station/block_assembling"
},
"assembling=false,facing=south": {
"model": "create:block/track_station/block",
"y": 180
},
"assembling=true,facing=south": {
"model": "create:block/track_station/block_assembling",
"y": 180
},
"assembling=false,facing=west": {
"model": "create:block/track_station/block",
"y": 270
},
"assembling=true,facing=west": {
"model": "create:block/track_station/block_assembling",
"y": 270
},
"assembling=false,facing=east": {
"model": "create:block/track_station/block",
"y": 90
},
"assembling=true,facing=east": {
"model": "create:block/track_station/block_assembling",
"y": 90
}
}
}

View file

@ -50,6 +50,7 @@
"block.create.cogwheel": "\u05DF\u01DD\u01DD\u0265\u028Dbo\u0186",
"block.create.content_observer": "\u0279\u01DD\u028C\u0279\u01DDsqO \u0287u\u01DD\u0287uo\u0186",
"block.create.controller_rail": "\u05DF\u0131\u0250\u1D1A \u0279\u01DD\u05DF\u05DFo\u0279\u0287uo\u0186",
"block.create.controls": "s\u05DFo\u0279\u0287uo\u0186",
"block.create.copper_backtank": "\u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186",
"block.create.copper_casing": "bu\u0131s\u0250\u0186 \u0279\u01DDddo\u0186",
"block.create.copper_shingle_slab": "q\u0250\u05DFS \u01DD\u05DFbu\u0131\u0265S \u0279\u01DDddo\u0186",
@ -236,6 +237,7 @@
"block.create.item_vault": "\u0287\u05DFn\u0250\u039B \u026F\u01DD\u0287I",
"block.create.jungle_window": "\u028Dopu\u0131M \u01DD\u05DFbun\u017F",
"block.create.jungle_window_pane": "\u01DDu\u0250\u0500 \u028Dopu\u0131M \u01DD\u05DFbun\u017F",
"block.create.large_bogey": "\u028E\u01DDbo\u15FA \u01DDb\u0279\u0250\uA780",
"block.create.large_cogwheel": "\u05DF\u01DD\u01DD\u0265\u028Dbo\u0186 \u01DDb\u0279\u0250\uA780",
"block.create.layered_andesite": "\u01DD\u0287\u0131s\u01DDpu\u2C6F p\u01DD\u0279\u01DD\u028E\u0250\uA780",
"block.create.layered_asurine": "\u01DDu\u0131\u0279ns\u2C6F p\u01DD\u0279\u01DD\u028E\u0250\uA780",
@ -417,6 +419,7 @@
"block.create.small_asurine_brick_stairs": "s\u0279\u0131\u0250\u0287S \u029E\u0254\u0131\u0279\u15FA \u01DDu\u0131\u0279ns\u2C6F \u05DF\u05DF\u0250\u026FS",
"block.create.small_asurine_brick_wall": "\u05DF\u05DF\u0250M \u029E\u0254\u0131\u0279\u15FA \u01DDu\u0131\u0279ns\u2C6F \u05DF\u05DF\u0250\u026FS",
"block.create.small_asurine_bricks": "s\u029E\u0254\u0131\u0279\u15FA \u01DDu\u0131\u0279ns\u2C6F \u05DF\u05DF\u0250\u026FS",
"block.create.small_bogey": "\u028E\u01DDbo\u15FA \u05DF\u05DF\u0250\u026FS",
"block.create.small_calcite_brick_slab": "q\u0250\u05DFS \u029E\u0254\u0131\u0279\u15FA \u01DD\u0287\u0131\u0254\u05DF\u0250\u0186 \u05DF\u05DF\u0250\u026FS",
"block.create.small_calcite_brick_stairs": "s\u0279\u0131\u0250\u0287S \u029E\u0254\u0131\u0279\u15FA \u01DD\u0287\u0131\u0254\u05DF\u0250\u0186 \u05DF\u05DF\u0250\u026FS",
"block.create.small_calcite_brick_wall": "\u05DF\u05DF\u0250M \u029E\u0254\u0131\u0279\u15FA \u01DD\u0287\u0131\u0254\u05DF\u0250\u0186 \u05DF\u05DF\u0250\u026FS",
@ -477,6 +480,8 @@
"block.create.stressometer": "\u0279\u01DD\u0287\u01DD\u026Foss\u01DD\u0279\u0287S",
"block.create.tiled_glass": "ss\u0250\u05DF\u2141 p\u01DD\u05DF\u0131\u27D8",
"block.create.tiled_glass_pane": "\u01DDu\u0250\u0500 ss\u0250\u05DF\u2141 p\u01DD\u05DF\u0131\u27D8",
"block.create.track": "\u029E\u0254\u0250\u0279\u27D8",
"block.create.track_station": "uo\u0131\u0287\u0250\u0287S \u029E\u0254\u0250\u0279\u27D8",
"block.create.tuff_pillar": "\u0279\u0250\u05DF\u05DF\u0131\u0500 \u025F\u025Fn\u27D8",
"block.create.turntable": "\u01DD\u05DFq\u0250\u0287u\u0279n\u27D8",
"block.create.veridium": "\u026Fn\u0131p\u0131\u0279\u01DD\u039B",
@ -533,6 +538,7 @@
"block.create.zinc_ore": "\u01DD\u0279O \u0254u\u0131Z",
"enchantment.create.capacity": "\u028E\u0287\u0131\u0254\u0250d\u0250\u0186",
"enchantment.create.potato_recovery": "\u028E\u0279\u01DD\u028Co\u0254\u01DD\u1D1A o\u0287\u0250\u0287o\u0500",
"entity.create.carriage_contraption": "uo\u0131\u0287d\u0250\u0279\u0287uo\u0186 \u01DDb\u0250\u0131\u0279\u0279\u0250\u0186",
"entity.create.contraption": "uo\u0131\u0287d\u0250\u0279\u0287uo\u0186",
"entity.create.crafting_blueprint": "\u0287u\u0131\u0279d\u01DDn\u05DF\u15FA bu\u0131\u0287\u025F\u0250\u0279\u0186",
"entity.create.gantry_contraption": "uo\u0131\u0287d\u0250\u0279\u0287uo\u0186 \u028E\u0279\u0287u\u0250\u2141",
@ -609,6 +615,7 @@
"item.create.refined_radiance": "\u01DD\u0254u\u0250\u0131p\u0250\u1D1A p\u01DDu\u0131\u025F\u01DD\u1D1A",
"item.create.rose_quartz": "z\u0287\u0279\u0250n\u1F49 \u01DDso\u1D1A",
"item.create.sand_paper": "\u0279\u01DDd\u0250\u0500 pu\u0250S",
"item.create.schedule": "\u01DD\u05DFnp\u01DD\u0265\u0254S",
"item.create.schematic": "\u0254\u0131\u0287\u0250\u026F\u01DD\u0265\u0254S",
"item.create.schematic_and_quill": "\u05DF\u05DF\u0131n\u1F49 pu\u2C6F \u0254\u0131\u0287\u0250\u026F\u01DD\u0265\u0254S",
"item.create.shadow_steel": "\u05DF\u01DD\u01DD\u0287S \u028Dop\u0250\u0265S",

View file

@ -53,6 +53,7 @@
"block.create.cogwheel": "Cogwheel",
"block.create.content_observer": "Content Observer",
"block.create.controller_rail": "Controller Rail",
"block.create.controls": "Controls",
"block.create.copper_backtank": "Copper Backtank",
"block.create.copper_casing": "Copper Casing",
"block.create.copper_shingle_slab": "Copper Shingle Slab",
@ -239,6 +240,7 @@
"block.create.item_vault": "Item Vault",
"block.create.jungle_window": "Jungle Window",
"block.create.jungle_window_pane": "Jungle Window Pane",
"block.create.large_bogey": "Large Bogey",
"block.create.large_cogwheel": "Large Cogwheel",
"block.create.layered_andesite": "Layered Andesite",
"block.create.layered_asurine": "Layered Asurine",
@ -420,6 +422,7 @@
"block.create.small_asurine_brick_stairs": "Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "Small Asurine Bricks",
"block.create.small_bogey": "Small Bogey",
"block.create.small_calcite_brick_slab": "Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "Small Calcite Brick Wall",
@ -480,6 +483,8 @@
"block.create.stressometer": "Stressometer",
"block.create.tiled_glass": "Tiled Glass",
"block.create.tiled_glass_pane": "Tiled Glass Pane",
"block.create.track": "Track",
"block.create.track_station": "Track Station",
"block.create.tuff_pillar": "Tuff Pillar",
"block.create.turntable": "Turntable",
"block.create.veridium": "Veridium",
@ -538,6 +543,7 @@
"enchantment.create.capacity": "Capacity",
"enchantment.create.potato_recovery": "Potato Recovery",
"entity.create.carriage_contraption": "Carriage Contraption",
"entity.create.contraption": "Contraption",
"entity.create.crafting_blueprint": "Crafting Blueprint",
"entity.create.gantry_contraption": "Gantry Contraption",
@ -616,6 +622,7 @@
"item.create.refined_radiance": "Refined Radiance",
"item.create.rose_quartz": "Rose Quartz",
"item.create.sand_paper": "Sand Paper",
"item.create.schedule": "Schedule",
"item.create.schematic": "Schematic",
"item.create.schematic_and_quill": "Schematic And Quill",
"item.create.shadow_steel": "Shadow Steel",
@ -863,9 +870,15 @@
"create.generic.length": "Length",
"create.generic.speed": "Speed",
"create.generic.delay": "Delay",
"create.generic.duration": "Duration",
"create.generic.timeUnit": "Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Seconds",
"create.generic.unit.minutes": "Minutes",
"create.generic.daytime.hour": "Hour",
"create.generic.daytime.minute": "Minute",
"create.generic.daytime.pm": "pm",
"create.generic.daytime.am": "am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "°",
@ -1309,6 +1322,60 @@
"create.hint.full_deployer.title": "Deployer Item Overflow",
"create.hint.full_deployer": "It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.schedule.lmb_edit": "Left-Click to Edit",
"create.gui.schedule.rmb_remove": "Right-Click to Remove",
"create.gui.schedule.duplicate": "Duplicate",
"create.gui.schedule.remove_entry": "Remove Stop",
"create.gui.schedule.add_entry": "Add Stop",
"create.gui.schedule.move_up": "Move up",
"create.gui.schedule.move_down": "Move down",
"create.gui.schedule.add_condition": "Add Condition",
"create.gui.schedule.alternative_condition": "Alternative Condition",
"create.schedule.destination_type": "Next Stop:",
"create.schedule.destination.editor": "Destination Editor",
"create.schedule.destination.filtered": "Specific Station",
"create.schedule.destination.filtered_matching": "Station: %1$s",
"create.schedule.destination.filter": "Station Name",
"create.schedule.destination.filter_2": "Use * as a text wildcard",
"create.schedule.destination.filter_3": "Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "Nearest Station",
"create.schedule.destination.redstone": "Station with Redstone Pulse",
"create.schedule.condition_type": "Continue when:",
"create.schedule.condition.editor": "Condition Editor",
"create.schedule.condition.delay": "Scheduled Delay",
"create.schedule.condition.delay_short": "Wait: %1$s",
"create.schedule.condition.idle": "Cargo Inactivity",
"create.schedule.condition.idle_short": "Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "for %1$s",
"create.schedule.condition.unloaded": "Chunk Unloaded",
"create.schedule.condition.powered": "Station Powered",
"create.schedule.condition.time_of_day": "Time of Day",
"create.schedule.condition.time_of_day.scheduled": "Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "%1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "+%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "Train Holds %1$s",
"create.schedule.condition.threshold.greater": "More than",
"create.schedule.condition.threshold.less": "Less than",
"create.schedule.condition.threshold.equal": "Exactly",
"create.schedule.condition.threshold.x_units_of_item": "%1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "Matching Content",
"create.schedule.condition.threshold.item_measure": "Item Measure",
"create.schedule.condition.threshold.items": "Items",
"create.schedule.condition.threshold.stacks": "Stacks",
"create.schedule.condition.threshold.buckets": "Buckets",
"create.schedule.condition.threshold.place_item": "Reference Item",
"create.schedule.condition.threshold.place_item_2": "Filters can be used",
"create.schedule.condition.fluid_threshold": "Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "Item Cargo Condition",
"create.schedule.loop": "Loop Forever",
"create.schedule.loop1": "Schedule starts over",
"create.schedule.loop2": "when completed",
"create.train.unnamed": "Unnamed Train",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1323",
"_": "Missing Localizations: 1387",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Zahnrad",
"block.create.content_observer": "Inhaltsbeobachter",
"block.create.controller_rail": "Steuerungsschiene",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "UNLOCALIZED: Copper Backtank",
"block.create.copper_casing": "Kupferrahmen",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Tropenholzfenster",
"block.create.jungle_window_pane": "Tropenholzfensterscheibe",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Großes Zahnrad",
"block.create.layered_andesite": "Geschichteter Andesit",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Stressometer",
"block.create.tiled_glass": "Glasfliesen",
"block.create.tiled_glass_pane": "Glasfliesenscheibe",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Drehtisch",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "UNLOCALIZED: Capacity",
"enchantment.create.potato_recovery": "UNLOCALIZED: Potato Recovery",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Vorrichtung",
"entity.create.crafting_blueprint": "UNLOCALIZED: Crafting Blueprint",
"entity.create.gantry_contraption": "Portalkran Vorrichtung",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Raffinierter Glanz",
"item.create.rose_quartz": "Rosenquarz",
"item.create.sand_paper": "Schmirgelpapier",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Bauplan",
"item.create.schematic_and_quill": "Bauplan und Feder",
"item.create.shadow_steel": "Schattenstahl",
@ -864,9 +871,15 @@
"create.generic.length": "Länge",
"create.generic.speed": "Geschwindigkeit",
"create.generic.delay": "Verzögerung",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Sekunden",
"create.generic.unit.minutes": "Minuten",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Einsatzgerät Gegenstand Überlauf",
"create.hint.full_deployer": "Es scheint, dieses _Einsatzgerät_ enthält _überflüssige_ _Gegenstände_ die _extrahiert_ werden müssen. Nutze _Trichter_ oder anderes um ihn von seinem Überfluss zu befreien.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "Dies ist ein Beispiel Overlay",
"create.gui.config.overlay3": "Klicke oder ziehe mit deiner Maus",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 334",
"_": "Missing Localizations: 398",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Engranaje",
"block.create.content_observer": "Observador de Contenidos",
"block.create.controller_rail": "Raíl Controlador",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Tanque-Mochila de Cobre",
"block.create.copper_casing": "Cubierta de Cobre",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Ventana de Jungla",
"block.create.jungle_window_pane": "Panel de Ventana de Jungla",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Engranaje Grande",
"block.create.layered_andesite": "Capa de Andesita",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Estresómetro",
"block.create.tiled_glass": "Vidrio Baldosa",
"block.create.tiled_glass_pane": "Panel de Vidrio Baldosa",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Plato Giratorio",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Capacidad",
"enchantment.create.potato_recovery": "Recuperación de papas",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Artefacto",
"entity.create.crafting_blueprint": "Crafteando Planos",
"entity.create.gantry_contraption": "Artefacto de Grúa",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Radiancia Refinada",
"item.create.rose_quartz": "Cuarzo Rosa",
"item.create.sand_paper": "Papel de Arena",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Esquema",
"item.create.schematic_and_quill": "Esquema con Pluma",
"item.create.shadow_steel": "Acero Sombrío",
@ -864,9 +871,15 @@
"create.generic.length": "Largo",
"create.generic.speed": "Velocidad",
"create.generic.delay": "Retraso",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Segundos",
"create.generic.unit.minutes": "Minutos",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "us",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Desbordamiento de objetos del Desplegador",
"create.hint.full_deployer": "Parece que este _Desplegador_ contiene _objetos_ de _exceso_ que requieren ser _extraídos._ Usa una _tolva,_ _tolvogán_ u otros parecidos para librarlo del sobreflujo.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Hola :)",
"create.gui.config.overlay2": "Este es un overlay de ejemplo",
"create.gui.config.overlay3": "Haz clic o arrastra con el mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 334",
"_": "Missing Localizations: 398",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Engranaje pequeño",
"block.create.content_observer": "Observador de contenidos",
"block.create.controller_rail": "Raíl de control",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Depósito trasero de cobre",
"block.create.copper_casing": "Revestidor de caliza",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Ventana de jungla",
"block.create.jungle_window_pane": "Panel de ventana de jungla",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Engranaje grande",
"block.create.layered_andesite": "Andesita estratificada",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Estresómetro",
"block.create.tiled_glass": "Vidrio esmaltado",
"block.create.tiled_glass_pane": "Panel de vidrio esmaltado",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Plataforma giratoria mecánica",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Capacidad",
"enchantment.create.potato_recovery": "Recuperación de patatas",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Artilugio",
"entity.create.crafting_blueprint": "Plano de elaboración",
"entity.create.gantry_contraption": "Artilugio de grúa",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Resplandor refinado",
"item.create.rose_quartz": "Cuarzo rosado",
"item.create.sand_paper": "Papel de lija",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Esquema",
"item.create.schematic_and_quill": "Esquema y pluma",
"item.create.shadow_steel": "Acero sombrío",
@ -864,9 +871,15 @@
"create.generic.length": "Largo",
"create.generic.speed": "Velocidad",
"create.generic.delay": "Retraso",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Segundos",
"create.generic.unit.minutes": "Minutos",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "SU(unidades de estrés)",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Exceso de objetos en el desplegador",
"create.hint.full_deployer": "Parece que este _desplegador_ contiene _exceso_ de objetos que necesitan ser _extraídos._ Usa una _tolva_, _embudo_ u otro medio para liberarlo de su excedente.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Hola :)",
"create.gui.config.overlay2": "Esta es una muestra de la superposición",
"create.gui.config.overlay3": "Haga clic o arrastre con el ratón",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1585",
"_": "Missing Localizations: 1649",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Roue dentée",
"block.create.content_observer": "Observateur de contenu",
"block.create.controller_rail": "Rails controlleurs",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "UNLOCALIZED: Copper Backtank",
"block.create.copper_casing": "Revêtement en cuivre",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "UNLOCALIZED: Jungle Window",
"block.create.jungle_window_pane": "UNLOCALIZED: Jungle Window Pane",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Grande roue dentée",
"block.create.layered_andesite": "UNLOCALIZED: Layered Andesite",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Stressomètre",
"block.create.tiled_glass": "Verre carrelé",
"block.create.tiled_glass_pane": "Vitre carrelé",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Plaque tournante",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "UNLOCALIZED: Capacity",
"enchantment.create.potato_recovery": "UNLOCALIZED: Potato Recovery",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Engin",
"entity.create.crafting_blueprint": "UNLOCALIZED: Crafting Blueprint",
"entity.create.gantry_contraption": "UNLOCALIZED: Gantry Contraption",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Éclat raffiné",
"item.create.rose_quartz": "Quartz rose",
"item.create.sand_paper": "Papier de verre",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Schéma",
"item.create.schematic_and_quill": "Schéma et plume",
"item.create.shadow_steel": "Acier sombre",
@ -864,9 +871,15 @@
"create.generic.length": "Longueur",
"create.generic.speed": "Vitesse",
"create.generic.delay": "Delai",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Secondes",
"create.generic.unit.minutes": "Minutes",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "tr/min",
"create.generic.unit.stress": "us",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1274",
"_": "Missing Localizations: 1338",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Ruota dentata",
"block.create.content_observer": "Osservatore dei contenuti",
"block.create.controller_rail": "Binario di controllo",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "UNLOCALIZED: Copper Backtank",
"block.create.copper_casing": "Involucro di rame",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Finestra della giungla",
"block.create.jungle_window_pane": "Pannello di finestra della giungla",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Ruota dentata grande",
"block.create.layered_andesite": "Andesite stratificata",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Stressometro",
"block.create.tiled_glass": "Vetro piastrellato",
"block.create.tiled_glass_pane": "Pannello di vetro piastrellato",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Piatto",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "UNLOCALIZED: Capacity",
"enchantment.create.potato_recovery": "UNLOCALIZED: Potato Recovery",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Contrazione",
"entity.create.crafting_blueprint": "UNLOCALIZED: Crafting Blueprint",
"entity.create.gantry_contraption": "UNLOCALIZED: Gantry Contraption",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Radiance raffinata",
"item.create.rose_quartz": "Quarzo rosa",
"item.create.sand_paper": "Carta vetrata",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Schematica",
"item.create.schematic_and_quill": "Schematica e penna d'oca",
"item.create.shadow_steel": "Acciaio oscuro",
@ -864,9 +871,15 @@
"create.generic.length": "Lunghezza",
"create.generic.speed": "Velocità",
"create.generic.delay": "Ritardo",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Tick",
"create.generic.unit.seconds": "Secondi",
"create.generic.unit.minutes": "Minuti",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Overflow di oggetti dell'installatore",
"create.hint.full_deployer": "Sembra che questo _installatore_ contenga _oggetti_ _eccessivi_ che necessitano di essere _estratti_. Usa una _tramoggia_, un _imbuto_ o altro per liberarlo dall'overflow.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Ciao :)",
"create.gui.config.overlay2": "Questo overlay è di esempio",
"create.gui.config.overlay3": "Cliccalo o trascinalo col mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 4",
"_": "Missing Localizations: 68",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "歯車",
"block.create.content_observer": "コンテンツオブザーバー",
"block.create.controller_rail": "コントローラーレール",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "銅のバックタンク",
"block.create.copper_casing": "銅ケーシング",
"block.create.copper_shingle_slab": "銅の屋根板のハーフブロック",
@ -240,6 +241,7 @@
"block.create.item_vault": "アイテム保管庫",
"block.create.jungle_window": "ジャングルの窓",
"block.create.jungle_window_pane": "ジャングルの板窓",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "大きな歯車",
"block.create.layered_andesite": "安山岩の組石",
"block.create.layered_asurine": "瑠璃岩の組石",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "小さな瑠璃岩レンガの階段",
"block.create.small_asurine_brick_wall": "小さな瑠璃岩レンガの塀",
"block.create.small_asurine_bricks": "小さな瑠璃岩レンガ",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "小さな方解石レンガのハーフブロック",
"block.create.small_calcite_brick_stairs": "小さな方解石レンガの階段",
"block.create.small_calcite_brick_wall": "小さな方解石レンガの塀",
@ -481,6 +484,8 @@
"block.create.stressometer": "応力メーター",
"block.create.tiled_glass": "タイルガラス",
"block.create.tiled_glass_pane": "タイル板ガラス",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "凝灰岩の柱",
"block.create.turntable": "ターンテーブル",
"block.create.veridium": "翡翠岩",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "容量増加",
"enchantment.create.potato_recovery": "ポテト回収",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "からくり",
"entity.create.crafting_blueprint": "クラフトブループリント",
"entity.create.gantry_contraption": "ガントリーからくり",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "高貴な光輝",
"item.create.rose_quartz": "ローズクォーツ",
"item.create.sand_paper": "紙やすり",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "概略図",
"item.create.schematic_and_quill": "概略図と羽根ペン",
"item.create.shadow_steel": "シャドウスチール",
@ -864,9 +871,15 @@
"create.generic.length": "長さ",
"create.generic.speed": "回転速度",
"create.generic.delay": "遅延",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "ティック",
"create.generic.unit.seconds": "秒",
"create.generic.unit.minutes": "分",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "度",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "デプロイヤーのアイテムが溢れています",
"create.hint.full_deployer": "この_デプロイヤー_には、_搬出_する必要がある余分なアイテムが含まれています。_ ホッパー_や_漏斗_などの手段を利用して、溢れないようにしてください。",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "やぁ(・∀・)",
"create.gui.config.overlay2": "これはオーバーレイのサンプルです",
"create.gui.config.overlay3": "マウスでクリックまたはドラッグしてください",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 6",
"_": "Missing Localizations: 70",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "톱니바퀴",
"block.create.content_observer": "정보 감지기",
"block.create.controller_rail": "방향 레일",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "구리 산소통",
"block.create.copper_casing": "구리 케이스",
"block.create.copper_shingle_slab": "구리 판자 반 블록",
@ -240,6 +241,7 @@
"block.create.item_vault": "아이템 금고",
"block.create.jungle_window": "정글나무 유리창",
"block.create.jungle_window_pane": "정글나무 유리판",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "큰 톱니바퀴",
"block.create.layered_andesite": "이어지는 안산암",
"block.create.layered_asurine": "이어지는 유리암",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "작은 유리암 벽돌 계단",
"block.create.small_asurine_brick_wall": "작은 유리암 벽돌 담장",
"block.create.small_asurine_bricks": "작은 유리암 벽돌",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "작은 방해석 벽돌 반 블록",
"block.create.small_calcite_brick_stairs": "작은 방해석 벽돌 계단",
"block.create.small_calcite_brick_wall": "작은 방해석 벽돌 담장",
@ -481,6 +484,8 @@
"block.create.stressometer": "피로도 계측기",
"block.create.tiled_glass": "타일 유리",
"block.create.tiled_glass_pane": "타일 유리판",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "응회암 기둥",
"block.create.turntable": "돌림판",
"block.create.veridium": "심록암",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "저장량",
"enchantment.create.potato_recovery": "대포알 회수",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "구조물",
"entity.create.crafting_blueprint": "조합 청사진",
"entity.create.gantry_contraption": "갠트리 구조물",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "정제된 광채",
"item.create.rose_quartz": "장밋빛 석영",
"item.create.sand_paper": "사포",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "청사진",
"item.create.schematic_and_quill": "청사진과 깃펜",
"item.create.shadow_steel": "그림자 강철",
@ -864,9 +871,15 @@
"create.generic.length": "길이",
"create.generic.speed": "속도",
"create.generic.delay": "딜레이",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "틱",
"create.generic.unit.seconds": "초",
"create.generic.unit.minutes": "분",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "기계 손 아이템 과적",
"create.hint.full_deployer": "이 _기계 손_은 _배출_할 아이템을 가지고 있습니다. 호퍼 , 퍼널 등을 이용해 아이템을 빼내세요.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1938",
"_": "Missing Localizations: 2002",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Tandwiel",
"block.create.content_observer": "UNLOCALIZED: Content Observer",
"block.create.controller_rail": "UNLOCALIZED: Controller Rail",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "UNLOCALIZED: Copper Backtank",
"block.create.copper_casing": "UNLOCALIZED: Copper Casing",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "UNLOCALIZED: Jungle Window",
"block.create.jungle_window_pane": "UNLOCALIZED: Jungle Window Pane",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Groot Tandwiel",
"block.create.layered_andesite": "UNLOCALIZED: Layered Andesite",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Stressmeter",
"block.create.tiled_glass": "Getegeld Glas",
"block.create.tiled_glass_pane": "Getegeld Glazen Paneel",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Draaischijf",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "UNLOCALIZED: Capacity",
"enchantment.create.potato_recovery": "UNLOCALIZED: Potato Recovery",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "UNLOCALIZED: Contraption",
"entity.create.crafting_blueprint": "UNLOCALIZED: Crafting Blueprint",
"entity.create.gantry_contraption": "UNLOCALIZED: Gantry Contraption",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "UNLOCALIZED: Refined Radiance",
"item.create.rose_quartz": "Roze Kwarts",
"item.create.sand_paper": "UNLOCALIZED: Sand Paper",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Bouwtekening",
"item.create.schematic_and_quill": "Bouwtekening en Veer",
"item.create.shadow_steel": "UNLOCALIZED: Shadow Steel",
@ -864,9 +871,15 @@
"create.generic.length": "UNLOCALIZED: Length",
"create.generic.speed": "Snelheid",
"create.generic.delay": "Vertraging",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Seconden",
"create.generic.unit.minutes": "Minuten",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "UNLOCALIZED: RPM",
"create.generic.unit.stress": "UNLOCALIZED: su",
"create.generic.unit.degrees": "UNLOCALIZED: °",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 373",
"_": "Missing Localizations: 437",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Koło zębate",
"block.create.content_observer": "Detektor zawartości",
"block.create.controller_rail": "Tory sterujące",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Miedziany zbiornik w plecaku",
"block.create.copper_casing": "Miedziana Obudowa",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Dżunglowe okno",
"block.create.jungle_window_pane": "Dżunglowa szyba okienna",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Duże koło zębate",
"block.create.layered_andesite": "Warstwowy andezyt",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Miernik obciążenia",
"block.create.tiled_glass": "Kafelkowane szkło",
"block.create.tiled_glass_pane": "Kafelkowana szyba",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Talerz obrotowy",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Pojameność",
"enchantment.create.potato_recovery": "Odzyskiwanie",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Maszyna",
"entity.create.crafting_blueprint": "Szablon konstruowania",
"entity.create.gantry_contraption": "Maszyna suwnicowa",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Świetlisty materiał",
"item.create.rose_quartz": "Różowy kwarc",
"item.create.sand_paper": "Papier ścierny",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Schemat",
"item.create.schematic_and_quill": "Schemat z piórem",
"item.create.shadow_steel": "Mroczna stal",
@ -864,9 +871,15 @@
"create.generic.length": "Długość",
"create.generic.speed": "Prędkość",
"create.generic.delay": "Opóźnienie",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Tiki",
"create.generic.unit.seconds": "Sekundy",
"create.generic.unit.minutes": "Minuty",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "Ob/min",
"create.generic.unit.stress": "JO",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Nadmiar przedmiotów w aplikatorze",
"create.hint.full_deployer": "Wygląda na to, że ten _aplikator_ zawiera _nadmiar_ _przedmiotów_, które muszą zostać _wyciągnięte_. Użyj _leji_, _lejków_ lub innych sposobów, aby uwolnić od przepełnienia.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Cześć :)",
"create.gui.config.overlay2": "To jest przykładowa nakładka",
"create.gui.config.overlay3": "Kliknij lub przeciągnij myszką",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1557",
"_": "Missing Localizations: 1621",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Roda Dentada",
"block.create.content_observer": "Observador de Conteúdo",
"block.create.controller_rail": "Trilho Controlador",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Tanque Traseiro de Cobre",
"block.create.copper_casing": "Revestimento de Cobre",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "Cofre de itens",
"block.create.jungle_window": "UNLOCALIZED: Jungle Window",
"block.create.jungle_window_pane": "UNLOCALIZED: Jungle Window Pane",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "UNLOCALIZED: Large Cogwheel",
"block.create.layered_andesite": "UNLOCALIZED: Layered Andesite",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Estressómetro",
"block.create.tiled_glass": "Vidro Entalhado",
"block.create.tiled_glass_pane": "Vidraça Entalhada",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Mesa giratória",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Capacidade",
"enchantment.create.potato_recovery": "Recuperação de Batata",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Engenhoca",
"entity.create.crafting_blueprint": "Esquema de Fabricação",
"entity.create.gantry_contraption": "Engenhoca de Pórticolo",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Luz Refinada",
"item.create.rose_quartz": "Quartzo Rosa",
"item.create.sand_paper": "Lixa",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Esquema",
"item.create.schematic_and_quill": "Esquema e pena",
"item.create.shadow_steel": "Aço sombrio",
@ -864,9 +871,15 @@
"create.generic.length": "Comprimento",
"create.generic.speed": "Velocidade",
"create.generic.delay": "Demorada",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Segundos",
"create.generic.unit.minutes": "Minutos",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "us",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1557",
"_": "Missing Localizations: 1621",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Roda Dentada",
"block.create.content_observer": "Observador de Conteúdo",
"block.create.controller_rail": "Trilho Controlador",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Tanque Traseiro de Cobre",
"block.create.copper_casing": "Revestimento de Cobre",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "Cofre de itens",
"block.create.jungle_window": "UNLOCALIZED: Jungle Window",
"block.create.jungle_window_pane": "UNLOCALIZED: Jungle Window Pane",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "UNLOCALIZED: Large Cogwheel",
"block.create.layered_andesite": "UNLOCALIZED: Layered Andesite",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Estressómetro",
"block.create.tiled_glass": "Vidro Entalhado",
"block.create.tiled_glass_pane": "Vidraça Entalhada",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Mesa giratória",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Capacidade",
"enchantment.create.potato_recovery": "Recuperação de Batata",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Engenhoca",
"entity.create.crafting_blueprint": "Esquema de Fabricação",
"entity.create.gantry_contraption": "Engenhoca de Pórticolo",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Luz Refinada",
"item.create.rose_quartz": "Quartzo Rosa",
"item.create.sand_paper": "Lixa",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Esquema",
"item.create.schematic_and_quill": "Esquema e pena",
"item.create.shadow_steel": "Aço sombrio",
@ -864,9 +871,15 @@
"create.generic.length": "Comprimento",
"create.generic.speed": "Velocidade",
"create.generic.delay": "Demorada",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "Segundos",
"create.generic.unit.minutes": "Minutos",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "us",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",
"create.gui.config.overlay3": "UNLOCALIZED: Click or drag with your mouse",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 378",
"_": "Missing Localizations: 442",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "Шестерня",
"block.create.content_observer": "Наблюдатель за содержимым",
"block.create.controller_rail": "Контролирующая рельса",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "Медный баллон",
"block.create.copper_casing": "Медный корпус",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "Окно из тропического дерева",
"block.create.jungle_window_pane": "Панель окна из тропического дерева",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "Большая шестерня",
"block.create.layered_andesite": "Слоистый андезит",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "Стрессометр",
"block.create.tiled_glass": "Плиточное стекло",
"block.create.tiled_glass_pane": "Плиточная стеклянная панель",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "Поворотный стол",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "Вместимость",
"enchantment.create.potato_recovery": "Возобновление картофеля",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "Штуковина",
"entity.create.crafting_blueprint": "Создание чертежа",
"entity.create.gantry_contraption": "Крановая штуковина",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "Изысканное сияние",
"item.create.rose_quartz": "Розовый кварц",
"item.create.sand_paper": "Наждачная бумага",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "Схематика",
"item.create.schematic_and_quill": "Схематика и перо",
"item.create.shadow_steel": "Призрачная сталь",
@ -864,9 +871,15 @@
"create.generic.length": "Длина",
"create.generic.speed": "Скорость",
"create.generic.delay": "Задержка",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "тиков",
"create.generic.unit.seconds": "секунд",
"create.generic.unit.minutes": "минут",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "об./мин.",
"create.generic.unit.stress": "ен",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "Переполнение автономного активатора",
"create.hint.full_deployer": "Похоже, этот _автономный активатор_ содержит _лишние_ _предметы_, которые необходимо _извлечь_. Используйте _воронку_ или _другие способы_, чтобы освободить его от переполнения.",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Привет :)",
"create.gui.config.overlay2": "Это образец оверлея",
"create.gui.config.overlay3": "Кликни и тащи с помощью мыши",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 4",
"_": "Missing Localizations: 68",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "齿轮",
"block.create.content_observer": "物品侦测器",
"block.create.controller_rail": "控制铁轨",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "铜制背罐",
"block.create.copper_casing": "铜机壳",
"block.create.copper_shingle_slab": "铜砖块台阶",
@ -240,6 +241,7 @@
"block.create.item_vault": "物品保险库",
"block.create.jungle_window": "丛林木窗户",
"block.create.jungle_window_pane": "丛林木窗户板",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "大齿轮",
"block.create.layered_andesite": "层叠安山岩",
"block.create.layered_asurine": "层叠皓蓝石",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "皓蓝石小砖块楼梯",
"block.create.small_asurine_brick_wall": "皓蓝石小砖块墙",
"block.create.small_asurine_bricks": "皓蓝石小砖块",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "方解石小砖块台阶",
"block.create.small_calcite_brick_stairs": "方解石小砖块楼梯",
"block.create.small_calcite_brick_wall": "方解石小砖块墙",
@ -481,6 +484,8 @@
"block.create.stressometer": "应力表",
"block.create.tiled_glass": "十字玻璃窗",
"block.create.tiled_glass_pane": "十字玻璃窗户板",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "凝灰岩柱",
"block.create.turntable": "转盘",
"block.create.veridium": "辉绿矿",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "扩容",
"enchantment.create.potato_recovery": "土豆回收",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "装置",
"entity.create.crafting_blueprint": "合成蓝图",
"entity.create.gantry_contraption": "起重机装置",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "光辉石",
"item.create.rose_quartz": "玫瑰石英",
"item.create.sand_paper": "砂纸",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "蓝图",
"item.create.schematic_and_quill": "蓝图与笔",
"item.create.shadow_steel": "暗影钢",
@ -864,9 +871,15 @@
"create.generic.length": "长度",
"create.generic.speed": "速度",
"create.generic.delay": "延时",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "秒",
"create.generic.unit.minutes": "分钟",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "°",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "机械手物品溢出",
"create.hint.full_deployer": "_机械手_包含_过剩的物品_需要被_提取。你需要_使用_料斗__漏斗_或其他方法将溢出释放出来。",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "这是一个实例层",
"create.gui.config.overlay3": "点击拖拽你的鼠标",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 392",
"_": "Missing Localizations: 456",
"_": "->------------------------] Game Elements [------------------------<-",
@ -54,6 +54,7 @@
"block.create.cogwheel": "齒輪",
"block.create.content_observer": "物品偵測器",
"block.create.controller_rail": "控制鐵軌",
"block.create.controls": "UNLOCALIZED: Controls",
"block.create.copper_backtank": "銅製後背包",
"block.create.copper_casing": "銅機殼",
"block.create.copper_shingle_slab": "UNLOCALIZED: Copper Shingle Slab",
@ -240,6 +241,7 @@
"block.create.item_vault": "UNLOCALIZED: Item Vault",
"block.create.jungle_window": "叢林木窗戶",
"block.create.jungle_window_pane": "叢林木窗戶片",
"block.create.large_bogey": "UNLOCALIZED: Large Bogey",
"block.create.large_cogwheel": "大齒輪",
"block.create.layered_andesite": "疊層安山岩",
"block.create.layered_asurine": "UNLOCALIZED: Layered Asurine",
@ -421,6 +423,7 @@
"block.create.small_asurine_brick_stairs": "UNLOCALIZED: Small Asurine Brick Stairs",
"block.create.small_asurine_brick_wall": "UNLOCALIZED: Small Asurine Brick Wall",
"block.create.small_asurine_bricks": "UNLOCALIZED: Small Asurine Bricks",
"block.create.small_bogey": "UNLOCALIZED: Small Bogey",
"block.create.small_calcite_brick_slab": "UNLOCALIZED: Small Calcite Brick Slab",
"block.create.small_calcite_brick_stairs": "UNLOCALIZED: Small Calcite Brick Stairs",
"block.create.small_calcite_brick_wall": "UNLOCALIZED: Small Calcite Brick Wall",
@ -481,6 +484,8 @@
"block.create.stressometer": "動能錶",
"block.create.tiled_glass": "十字玻璃窗",
"block.create.tiled_glass_pane": "十字玻璃窗戶片",
"block.create.track": "UNLOCALIZED: Track",
"block.create.track_station": "UNLOCALIZED: Track Station",
"block.create.tuff_pillar": "UNLOCALIZED: Tuff Pillar",
"block.create.turntable": "轉盤",
"block.create.veridium": "UNLOCALIZED: Veridium",
@ -539,6 +544,7 @@
"enchantment.create.capacity": "容量",
"enchantment.create.potato_recovery": "馬鈴薯恢復",
"entity.create.carriage_contraption": "UNLOCALIZED: Carriage Contraption",
"entity.create.contraption": "結構",
"entity.create.crafting_blueprint": "合成藍圖",
"entity.create.gantry_contraption": "門式結構",
@ -617,6 +623,7 @@
"item.create.refined_radiance": "光輝石",
"item.create.rose_quartz": "玫瑰石英",
"item.create.sand_paper": "砂紙",
"item.create.schedule": "UNLOCALIZED: Schedule",
"item.create.schematic": "藍圖",
"item.create.schematic_and_quill": "藍圖與筆",
"item.create.shadow_steel": "暗影鋼",
@ -864,9 +871,15 @@
"create.generic.length": "長",
"create.generic.speed": "速度",
"create.generic.delay": "延時",
"create.generic.duration": "UNLOCALIZED: Duration",
"create.generic.timeUnit": "UNLOCALIZED: Time Unit",
"create.generic.unit.ticks": "Ticks",
"create.generic.unit.seconds": "秒",
"create.generic.unit.minutes": "分",
"create.generic.daytime.hour": "UNLOCALIZED: Hour",
"create.generic.daytime.minute": "UNLOCALIZED: Minute",
"create.generic.daytime.pm": "UNLOCALIZED: pm",
"create.generic.daytime.am": "UNLOCALIZED: am",
"create.generic.unit.rpm": "RPM",
"create.generic.unit.stress": "su",
"create.generic.unit.degrees": "度",
@ -1310,6 +1323,60 @@
"create.hint.full_deployer.title": "機械手物品溢出",
"create.hint.full_deployer": "_機械手_包含_過剩的物品_需要被_取出._使用漏斗_或其他方法將溢出解決。",
"create.gui.schedule.lmb_edit": "UNLOCALIZED: Left-Click to Edit",
"create.gui.schedule.rmb_remove": "UNLOCALIZED: Right-Click to Remove",
"create.gui.schedule.duplicate": "UNLOCALIZED: Duplicate",
"create.gui.schedule.remove_entry": "UNLOCALIZED: Remove Stop",
"create.gui.schedule.add_entry": "UNLOCALIZED: Add Stop",
"create.gui.schedule.move_up": "UNLOCALIZED: Move up",
"create.gui.schedule.move_down": "UNLOCALIZED: Move down",
"create.gui.schedule.add_condition": "UNLOCALIZED: Add Condition",
"create.gui.schedule.alternative_condition": "UNLOCALIZED: Alternative Condition",
"create.schedule.destination_type": "UNLOCALIZED: Next Stop:",
"create.schedule.destination.editor": "UNLOCALIZED: Destination Editor",
"create.schedule.destination.filtered": "UNLOCALIZED: Specific Station",
"create.schedule.destination.filtered_matching": "UNLOCALIZED: Station: %1$s",
"create.schedule.destination.filter": "UNLOCALIZED: Station Name",
"create.schedule.destination.filter_2": "UNLOCALIZED: Use * as a text wildcard",
"create.schedule.destination.filter_3": "UNLOCALIZED: Example: 'My Station, Platform *'",
"create.schedule.destination.filter_4": "UNLOCALIZED: Train picks nearest unoccupied match",
"create.schedule.destination.nearest": "UNLOCALIZED: Nearest Station",
"create.schedule.destination.redstone": "UNLOCALIZED: Station with Redstone Pulse",
"create.schedule.condition_type": "UNLOCALIZED: Continue when:",
"create.schedule.condition.editor": "UNLOCALIZED: Condition Editor",
"create.schedule.condition.delay": "UNLOCALIZED: Scheduled Delay",
"create.schedule.condition.delay_short": "UNLOCALIZED: Wait: %1$s",
"create.schedule.condition.idle": "UNLOCALIZED: Cargo Inactivity",
"create.schedule.condition.idle_short": "UNLOCALIZED: Cargo Idle: %1$s",
"create.schedule.condition.for_x_time": "UNLOCALIZED: for %1$s",
"create.schedule.condition.unloaded": "UNLOCALIZED: Chunk Unloaded",
"create.schedule.condition.powered": "UNLOCALIZED: Station Powered",
"create.schedule.condition.time_of_day": "UNLOCALIZED: Time of Day",
"create.schedule.condition.time_of_day.scheduled": "UNLOCALIZED: Scheduled Time: %1$s",
"create.schedule.condition.time_of_day.digital_format": "UNLOCALIZED: %1$s:%3$s %4$s",
"create.schedule.condition.time_of_day.grace_period": "UNLOCALIZED: Grace Period",
"create.schedule.condition.time_of_day.grace_period.format": "UNLOCALIZED: +%1$s Hrs.",
"create.schedule.condition.threshold.train_holds": "UNLOCALIZED: Train Holds %1$s",
"create.schedule.condition.threshold.greater": "UNLOCALIZED: More than",
"create.schedule.condition.threshold.less": "UNLOCALIZED: Less than",
"create.schedule.condition.threshold.equal": "UNLOCALIZED: Exactly",
"create.schedule.condition.threshold.x_units_of_item": "UNLOCALIZED: %1$s %2$s of %3$s",
"create.schedule.condition.threshold.matching_content": "UNLOCALIZED: Matching Content",
"create.schedule.condition.threshold.item_measure": "UNLOCALIZED: Item Measure",
"create.schedule.condition.threshold.items": "UNLOCALIZED: Items",
"create.schedule.condition.threshold.stacks": "UNLOCALIZED: Stacks",
"create.schedule.condition.threshold.buckets": "UNLOCALIZED: Buckets",
"create.schedule.condition.threshold.place_item": "UNLOCALIZED: Reference Item",
"create.schedule.condition.threshold.place_item_2": "UNLOCALIZED: Filters can be used",
"create.schedule.condition.fluid_threshold": "UNLOCALIZED: Fluid Cargo Condition",
"create.schedule.condition.item_threshold": "UNLOCALIZED: Item Cargo Condition",
"create.schedule.loop": "UNLOCALIZED: Loop Forever",
"create.schedule.loop1": "UNLOCALIZED: Schedule starts over",
"create.schedule.loop2": "UNLOCALIZED: when completed",
"create.train.unnamed": "UNLOCALIZED: Unnamed Train",
"create.gui.config.overlay1": "嗨 :)",
"create.gui.config.overlay2": "這是一個實例層",
"create.gui.config.overlay3": "點擊拖拽你的滑鼠",

View file

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

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/schedule"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/track"
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -96,6 +96,7 @@
"create:flywheel",
"create:rotation_speed_controller",
"create:mechanical_arm",
"create:track_station",
"create:content_observer",
"create:stockpile_switch",
"create:creative_crate",

View file

@ -104,6 +104,8 @@
"create:furnace_engine",
"create:rotation_speed_controller",
"create:mechanical_arm",
"create:track",
"create:track_station",
"create:item_vault",
"create:andesite_funnel",
"create:andesite_belt_funnel",

View file

@ -17,8 +17,7 @@ public class AllBlockPartials {
public static final PartialModel
SCHEMATICANNON_CONNECTOR = block("schematicannon/connector"),
SCHEMATICANNON_PIPE = block("schematicannon/pipe"),
SCHEMATICANNON_CONNECTOR = block("schematicannon/connector"), SCHEMATICANNON_PIPE = block("schematicannon/pipe"),
SHAFTLESS_COGWHEEL = block("cogwheel_shaftless"), SHAFTLESS_LARGE_COGWHEEL = block("large_cogwheel_shaftless"),
COGWHEEL_SHAFT = block("cogwheel_shaft"), SHAFT_HALF = block("shaft_half"),
@ -31,7 +30,8 @@ public class AllBlockPartials {
ENCASED_FAN_INNER = block("encased_fan/propeller"), HAND_CRANK_HANDLE = block("hand_crank/handle"),
MECHANICAL_PRESS_HEAD = block("mechanical_press/head"), MECHANICAL_MIXER_POLE = block("mechanical_mixer/pole"),
MECHANICAL_MIXER_HEAD = block("mechanical_mixer/head"), MECHANICAL_CRAFTER_LID = block("mechanical_crafter/lid"),
MECHANICAL_MIXER_HEAD = block("mechanical_mixer/head"),
MECHANICAL_CRAFTER_LID = block("mechanical_crafter/lid"),
MECHANICAL_CRAFTER_ARROW = block("mechanical_crafter/arrow"),
MECHANICAL_CRAFTER_BELT_FRAME = block("mechanical_crafter/belt"),
MECHANICAL_CRAFTER_BELT = block("mechanical_crafter/belt_animated"),
@ -45,7 +45,8 @@ public class AllBlockPartials {
GAUGE_HEAD_STRESS = block("gauge/stressometer/head"), BEARING_TOP = block("bearing/top"),
BEARING_TOP_WOODEN = block("bearing/top_wooden"), DRILL_HEAD = block("mechanical_drill/head"),
HARVESTER_BLADE = block("mechanical_harvester/blade"), DEPLOYER_POLE = block("deployer/pole"),
DEPLOYER_HAND_POINTING = block("deployer/hand_pointing"), DEPLOYER_HAND_PUNCHING = block("deployer/hand_punching"),
DEPLOYER_HAND_POINTING = block("deployer/hand_pointing"),
DEPLOYER_HAND_PUNCHING = block("deployer/hand_punching"),
DEPLOYER_HAND_HOLDING = block("deployer/hand_holding"), ANALOG_LEVER_HANDLE = block("analog_lever/handle"),
ANALOG_LEVER_INDICATOR = block("analog_lever/indicator"), FUNNEL_FLAP = block("funnel/flap"),
BELT_FUNNEL_FLAP = block("belt_funnel/flap"), BELT_TUNNEL_FLAP = block("belt_tunnel/flap"),
@ -109,20 +110,43 @@ 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"),
TRACK_STATION_OVERLAY = block("track/station_overlay"),
TRACK_STATION_OVERLAY_DIAGONAL = block("track/station_overlay_diagonal"),
TRACK_STATION_OVERLAY_ASCENDING = block("track/station_overlay_ascending"),
TRACK_ASSEMBLY_OVERLAY = block("track/assembly_overlay"),
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"),
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_ATTACHMENT = entity("minecart_coupling/attachment"), COUPLING_RING = entity("minecart_coupling/ring"),
COUPLING_CONNECTOR = entity("minecart_coupling/connector")
;
public static final Map<FluidTransportBehaviour.AttachmentTypes, Map<Direction, PartialModel>> PIPE_ATTACHMENTS =
new EnumMap<>(FluidTransportBehaviour.AttachmentTypes.class);
public static final Map<BlazeBurnerBlock.HeatLevel, PartialModel> BLAZES = new EnumMap<>(BlazeBurnerBlock.HeatLevel.class);
public static final Map<BlazeBurnerBlock.HeatLevel, PartialModel> BLAZES =
new EnumMap<>(BlazeBurnerBlock.HeatLevel.class);
public static final Map<DyeColor, PartialModel> TOOLBOX_LIDS = new EnumMap<>(DyeColor.class);
static {

View file

@ -58,6 +58,8 @@ import com.simibubi.create.content.contraptions.components.structureMovement.cha
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.RadialChassisBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.StickerBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryCarriageBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsMovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerBlock.MinecartAnchorBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerBlockItem;
@ -161,6 +163,13 @@ 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.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.management.StationBlock;
import com.simibubi.create.content.logistics.trains.management.TrackTargetingBlockItem;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlockItem;
import com.simibubi.create.content.logistics.trains.track.TrackBlockStateGenerator;
import com.simibubi.create.content.schematics.block.SchematicTableBlock;
import com.simibubi.create.content.schematics.block.SchematicannonBlock;
import com.simibubi.create.foundation.block.BlockStressDefaults;
@ -304,14 +313,14 @@ public class AllBlocks {
.transform(axeOrPickaxe())
.register();
public static final BlockEntry<EncasedCogwheelBlock> ANDESITE_ENCASED_COGWHEEL =
REGISTRATE.block("andesite_encased_cogwheel", p -> EncasedCogwheelBlock.andesite(false, p))
.transform(BuilderTransformers.encasedCogwheel("andesite", () -> AllSpriteShifts.ANDESITE_CASING))
.onRegister(CreateRegistrate.connectedTextures(() -> new EncasedCogCTBehaviour(AllSpriteShifts.ANDESITE_CASING,
Couple.create(AllSpriteShifts.ANDESITE_ENCASED_COGWHEEL_SIDE,
AllSpriteShifts.ANDESITE_ENCASED_COGWHEEL_OTHERSIDE))))
.transform(axeOrPickaxe())
.register();
public static final BlockEntry<EncasedCogwheelBlock> ANDESITE_ENCASED_COGWHEEL = REGISTRATE
.block("andesite_encased_cogwheel", p -> EncasedCogwheelBlock.andesite(false, p))
.transform(BuilderTransformers.encasedCogwheel("andesite", () -> AllSpriteShifts.ANDESITE_CASING))
.onRegister(CreateRegistrate.connectedTextures(() -> new EncasedCogCTBehaviour(AllSpriteShifts.ANDESITE_CASING,
Couple.create(AllSpriteShifts.ANDESITE_ENCASED_COGWHEEL_SIDE,
AllSpriteShifts.ANDESITE_ENCASED_COGWHEEL_OTHERSIDE))))
.transform(axeOrPickaxe())
.register();
public static final BlockEntry<EncasedCogwheelBlock> BRASS_ENCASED_COGWHEEL =
REGISTRATE.block("brass_encased_cogwheel", p -> EncasedCogwheelBlock.brass(false, p))
@ -1247,6 +1256,53 @@ public class AllBlocks {
.transform(customItemModel())
.register();
public static final BlockEntry<TrackBlock> TRACK = REGISTRATE.block("track", TrackBlock::new)
.initialProperties(() -> Blocks.RAIL)
.addLayer(() -> RenderType::cutoutMipped)
.transform(pickaxeOnly())
.blockstate(new TrackBlockStateGenerator()::generate)
.item(TrackBlockItem::new)
.model((c, p) -> p.generated(c, Create.asResource("item/" + c.getName())))
.build()
.register();
public static final BlockEntry<StationBlock> TRACK_STATION = REGISTRATE.block("track_station", StationBlock::new)
.initialProperties(SharedProperties::wooden)
.transform(axeOrPickaxe())
.blockstate((c, p) -> p.horizontalBlock(c.get(),
s -> s.getValue(StationBlock.ASSEMBLING) ? AssetLookup.partialBaseModel(c, p, "assembling")
: AssetLookup.partialBaseModel(c, p)))
.item(TrackTargetingBlockItem::new)
.transform(customItemModel("_", "block"))
.register();
public static final BlockEntry<StandardBogeyBlock> SMALL_BOGEY =
REGISTRATE.block("small_bogey", p -> new StandardBogeyBlock(p, false))
.initialProperties(SharedProperties::softMetal)
.properties(p -> p.noOcclusion())
.blockstate((c, p) -> BlockStateGen.horizontalAxisBlock(c, p, s -> p.models()
.getExistingFile(p.modLoc("block/track/bogey/top"))))
.onRegister(b -> IBogeyBlock.register(b.getRegistryName()))
.register();
public static final BlockEntry<StandardBogeyBlock> LARGE_BOGEY =
REGISTRATE.block("large_bogey", p -> new StandardBogeyBlock(p, true))
.initialProperties(SharedProperties::softMetal)
.properties(p -> p.noOcclusion())
.blockstate((c, p) -> BlockStateGen.horizontalAxisBlock(c, p, s -> p.models()
.getExistingFile(p.modLoc("block/track/bogey/top"))))
.onRegister(b -> IBogeyBlock.register(b.getRegistryName()))
.register();
public static final BlockEntry<ControlsBlock> CONTROLS = REGISTRATE.block("controls", ControlsBlock::new)
.initialProperties(SharedProperties::softMetal)
.blockstate((c, p) -> p.horizontalBlock(c.get(),
s -> AssetLookup.partialBaseModel(c, p, s.getValue(ControlsBlock.OPEN) ? "open" : "closed")))
.onRegister(addMovementBehaviour(new ControlsMovementBehaviour()))
.item()
.transform(customItemModel())
.register();
public static final BlockEntry<ItemVaultBlock> ITEM_VAULT = REGISTRATE.block("item_vault", ItemVaultBlock::new)
.initialProperties(SharedProperties::softMetal)
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK)

View file

@ -10,6 +10,8 @@ import com.simibubi.create.content.logistics.item.filter.AttributeFilterContaine
import com.simibubi.create.content.logistics.item.filter.AttributeFilterScreen;
import com.simibubi.create.content.logistics.item.filter.FilterContainer;
import com.simibubi.create.content.logistics.item.filter.FilterScreen;
import com.simibubi.create.content.logistics.trains.management.ScheduleContainer;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
import com.simibubi.create.content.schematics.block.SchematicTableContainer;
import com.simibubi.create.content.schematics.block.SchematicTableScreen;
import com.simibubi.create.content.schematics.block.SchematicannonContainer;
@ -45,6 +47,9 @@ public class AllContainerTypes {
public static final MenuEntry<ToolboxContainer> TOOLBOX =
register("toolbox", ToolboxContainer::new, () -> ToolboxScreen::new);
public static final MenuEntry<ScheduleContainer> SCHEDULE =
register("schedule", ScheduleContainer::new, () -> ScheduleScreen::new);
private static <C extends AbstractContainerMenu, S extends Screen & MenuAccess<C>> MenuEntry<C> register(
String name, ForgeMenuFactory<C> factory, NonNullSupplier<ScreenFactory<C, S>> screenFactory) {

View file

@ -14,6 +14,8 @@ import com.simibubi.create.content.curiosities.tools.BlueprintEntity;
import com.simibubi.create.content.curiosities.tools.BlueprintRenderer;
import com.simibubi.create.content.curiosities.weapons.PotatoProjectileEntity;
import com.simibubi.create.content.curiosities.weapons.PotatoProjectileRenderer;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntityRenderer;
import com.simibubi.create.foundation.data.CreateEntityBuilder;
import com.simibubi.create.foundation.utility.Lang;
import com.tterrag.registrate.util.entry.EntityEntry;
@ -37,6 +39,9 @@ public class AllEntityTypes {
20, 40, false);
public static final EntityEntry<GantryContraptionEntity> GANTRY_CONTRAPTION = contraption("gantry_contraption",
GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false);
public static final EntityEntry<CarriageContraptionEntity> CARRIAGE_CONTRAPTION =
contraption("carriage_contraption", CarriageContraptionEntity::new,
() -> CarriageContraptionEntityRenderer::new, 5, 100, true);
public static final EntityEntry<SuperGlueEntity> SUPER_GLUE =
register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10,
@ -44,8 +49,8 @@ public class AllEntityTypes {
.register();
public static final EntityEntry<BlueprintEntity> CRAFTING_BLUEPRINT =
register("crafting_blueprint", BlueprintEntity::new, () -> BlueprintRenderer::new, MobCategory.MISC,
10, Integer.MAX_VALUE, false, true, BlueprintEntity::build).register();
register("crafting_blueprint", BlueprintEntity::new, () -> BlueprintRenderer::new, MobCategory.MISC, 10,
Integer.MAX_VALUE, false, true, BlueprintEntity::build).register();
public static final EntityEntry<PotatoProjectileEntity> POTATO_PROJECTILE =
register("potato_projectile", PotatoProjectileEntity::new, () -> PotatoProjectileRenderer::new,
@ -57,16 +62,16 @@ public class AllEntityTypes {
//
private static <T extends Entity> EntityEntry<T> contraption(String name, EntityFactory<T> factory,
NonNullSupplier<NonNullFunction<EntityRendererProvider.Context, EntityRenderer<? super T>>> renderer,
int range, int updateFrequency, boolean sendVelocity) {
NonNullSupplier<NonNullFunction<EntityRendererProvider.Context, EntityRenderer<? super T>>> renderer, int range,
int updateFrequency, boolean sendVelocity) {
return register(name, factory, renderer, MobCategory.MISC, range, updateFrequency, sendVelocity, true,
AbstractContraptionEntity::build).register();
}
private static <T extends Entity> CreateEntityBuilder<T, ?> register(String name, EntityFactory<T> factory,
NonNullSupplier<NonNullFunction<EntityRendererProvider.Context, EntityRenderer<? super T>>> renderer,
MobCategory group, int range, int updateFrequency, boolean sendVelocity, boolean immuneToFire,
NonNullConsumer<EntityType.Builder<T>> propertyBuilder) {
NonNullSupplier<NonNullFunction<EntityRendererProvider.Context, EntityRenderer<? super T>>> renderer,
MobCategory group, int range, int updateFrequency, boolean sendVelocity, boolean immuneToFire,
NonNullConsumer<EntityType.Builder<T>> propertyBuilder) {
String id = Lang.asId(name);
return (CreateEntityBuilder<T, ?>) Create.registrate()
.entity(id, factory, group)

View file

@ -41,6 +41,7 @@ import com.simibubi.create.content.curiosities.weapons.PotatoCannonItem;
import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperItem;
import com.simibubi.create.content.logistics.item.LinkedControllerItem;
import com.simibubi.create.content.logistics.item.filter.FilterItem;
import com.simibubi.create.content.logistics.trains.management.ScheduleItem;
import com.simibubi.create.content.schematics.item.SchematicAndQuillItem;
import com.simibubi.create.content.schematics.item.SchematicItem;
import com.simibubi.create.foundation.data.AssetLookup;
@ -327,6 +328,9 @@ public class AllItems {
.model(AssetLookup.existingItemModel())
.register();
public static final ItemEntry<ScheduleItem> SCHEDULE = REGISTRATE.item("schedule", ScheduleItem::new)
.register();
// Schematics
static {

View file

@ -29,6 +29,7 @@ public class AllShapes {
CASING_12PX = shape(0, 0, 0, 16, 12, 16).forDirectional(),
CASING_11PX = shape(0, 0, 0, 16, 11, 16).forDirectional(),
MOTOR_BLOCK = shape(3, 0, 3, 13, 14, 13).forDirectional(),
TRACK = shape(0, 0, 0, 16, 4, 16).forDirectional(),
FOUR_VOXEL_POLE = shape(6, 0, 6, 10, 16, 10).forAxis(), SIX_VOXEL_POLE = shape(5, 0, 5, 11, 16, 11).forAxis(),
EIGHT_VOXEL_POLE = shape(4, 0, 4, 12, 16, 12).forAxis(),
FURNACE_ENGINE = shape(1, 1, 0, 15, 15, 16).add(0, 0, 9, 16, 16, 14)
@ -118,7 +119,12 @@ public class AllShapes {
BELL_DOUBLE_WALL = shape(5, 5, 0, 11, 11, 16).add(3, 1, 3, 13, 13, 13)
.forHorizontal(SOUTH),
BELL_CEILING = shape(0, 5, 5, 16, 16, 11).add(3, 1, 3, 13, 13, 13)
.forHorizontal(SOUTH)
.forHorizontal(SOUTH),
STATION = shape(0, 0, 0, 16, 5, 16).add(2, 4, 0, 14, 16, 4)
.forHorizontal(NORTH),
CONTROLS = shape(0, 0, 4, 16, 8, 16).add(0, 0, 6, 16, 14, 16)
.forHorizontal(NORTH)
;

View file

@ -167,6 +167,12 @@ import com.simibubi.create.content.logistics.block.redstone.StockpileSwitchTileE
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.StationRenderer;
import com.simibubi.create.content.logistics.trains.management.StationTileEntity;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity;
import com.simibubi.create.content.logistics.trains.track.TrackRenderer;
import com.simibubi.create.content.logistics.trains.track.TrackTileEntity;
import com.simibubi.create.content.schematics.block.SchematicTableTileEntity;
import com.simibubi.create.content.schematics.block.SchematicannonInstance;
import com.simibubi.create.content.schematics.block.SchematicannonRenderer;
@ -722,5 +728,23 @@ public class AllTileEntities {
.renderer(() -> ToolboxRenderer::new)
.register();
public static final BlockEntityEntry<TrackTileEntity> TRACK = Create.registrate()
.tileEntity("track", TrackTileEntity::new)
.renderer(() -> TrackRenderer::new)
.validBlocks(AllBlocks.TRACK)
.register();
public static final BlockEntityEntry<StandardBogeyTileEntity> BOGEY = Create.registrate()
.tileEntity("bogey", StandardBogeyTileEntity::new)
.renderer(() -> IBogeyTileEntityRenderer::new)
.validBlocks(AllBlocks.SMALL_BOGEY, AllBlocks.LARGE_BOGEY)
.register();
public static final BlockEntityEntry<StationTileEntity> TRACK_STATION = Create.registrate()
.tileEntity("track_station", StationTileEntity::new)
.renderer(() -> StationRenderer::new)
.validBlocks(AllBlocks.TRACK_STATION)
.register();
public static void register() {}
}

View file

@ -13,6 +13,7 @@ import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineInteractions;
import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes;
import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.palettes.AllPaletteBlocks;
import com.simibubi.create.content.palettes.PalettesItemGroup;
import com.simibubi.create.content.schematics.SchematicProcessor;
@ -71,6 +72,7 @@ public class Create {
public static final ServerSchematicLoader SCHEMATIC_RECEIVER = new ServerSchematicLoader();
public static final RedstoneLinkNetworkHandler REDSTONE_LINK_NETWORK_HANDLER = new RedstoneLinkNetworkHandler();
public static final TorquePropagator TORQUE_PROPAGATOR = new TorquePropagator();
public static final GlobalRailwayManager RAILWAYS = new GlobalRailwayManager();
public static final ServerLagger LAGGER = new ServerLagger();
public static final Random RANDOM = new Random();
@ -115,8 +117,7 @@ public class Create {
modEventBus.addGenericListener(ParticleType.class, AllParticleTypes::register);
modEventBus.addGenericListener(SoundEvent.class, AllSoundEvents::register);
DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
() -> () -> CreateClient.onCtorClient(modEventBus, forgeEventBus));
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> CreateClient.onCtorClient(modEventBus, forgeEventBus));
}
public static void init(final FMLCommonSetupEvent event) {

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.curiosities.weapons.PotatoCannonRenderHandler;
import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.schematics.ClientSchematicLoader;
import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler;
import com.simibubi.create.content.schematics.client.SchematicHandler;
@ -57,6 +58,7 @@ public class CreateClient {
public static final ZapperRenderHandler ZAPPER_RENDER_HANDLER = new ZapperRenderHandler();
public static final PotatoCannonRenderHandler POTATO_CANNON_RENDER_HANDLER = new PotatoCannonRenderHandler();
public static final SoulPulseEffectHandler SOUL_PULSE_EFFECT_HANDLER = new SoulPulseEffectHandler();
public static final GlobalRailwayManager RAILWAYS = new GlobalRailwayManager();
public static final ClientResourceReloadListener RESOURCE_RELOAD_LISTENER = new ClientResourceReloadListener();

View file

@ -73,6 +73,10 @@ public class AssemblyException extends Exception {
public static AssemblyException noPistonPoles() {
return new AssemblyException("noPistonPoles");
}
public static AssemblyException invalidBogeyCount() {
return new AssemblyException("invalidBogeyCount");
}
public static AssemblyException notEnoughSails(int sails) {
return new AssemblyException("not_enough_sails", sails, AllConfigs.SERVER.kinetics.minimumWindmillSails.get());

View file

@ -32,6 +32,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankConnectivit
import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultConnectivityHandler;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.config.ContraptionMovementSetting;
import net.minecraft.core.BlockPos;
@ -340,6 +341,9 @@ public class BlockMovementChecks {
return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
}
if (block instanceof IBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state)
.contains(direction);
return false;
}

View file

@ -41,6 +41,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.cha
import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryCarriageBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.PistonState;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonHeadBlock;
@ -56,6 +57,7 @@ import com.simibubi.create.content.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateTileEntity;
import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import com.simibubi.create.foundation.tileEntity.IMultiTileContainer;
@ -345,6 +347,12 @@ public abstract class Contraption {
frontier.add(attached);
}
// Bogeys tend to have sticky sides
if (state.getBlock() instanceof IBogeyBlock bogey)
for (Direction d : bogey.getStickySurfaces(world, pos, state))
if (!visited.contains(pos.relative(d)))
frontier.add(pos.relative(d));
// Bearings potentially create stabilized sub-contraptions
if (AllBlocks.MECHANICAL_BEARING.has(state))
moveBearing(pos, frontier, visited, state);
@ -603,6 +611,8 @@ public abstract class Contraption {
BlockState blockstate = world.getBlockState(pos);
if (AllBlocks.REDSTONE_CONTACT.has(blockstate))
blockstate = blockstate.setValue(RedstoneContactBlock.POWERED, true);
if (AllBlocks.CONTROLS.has(blockstate))
blockstate = blockstate.setValue(ControlsBlock.OPEN, true);
if (blockstate.getBlock() instanceof ButtonBlock) {
blockstate = blockstate.setValue(ButtonBlock.POWERED, false);
world.scheduleTick(pos, blockstate.getBlock(), -1);

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.gan
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.PistonContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyContraption;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
public class ContraptionType {
@ -23,7 +24,8 @@ public class ContraptionType {
CLOCKWORK = register("clockwork", ClockworkContraption::new),
MOUNTED = register("mounted", MountedContraption::new),
STABILIZED = register("stabilized", StabilizedContraption::new),
GANTRY = register("gantry", GantryContraption::new);
GANTRY = register("gantry", GantryContraption::new),
CARRIAGE = register("carriage", CarriageContraption::new);
Supplier<? extends Contraption> factory;
String id;

View file

@ -0,0 +1,58 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionWorld;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
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.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
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.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class ControlsBlock extends HorizontalDirectionalBlock {
public static final BooleanProperty OPEN = BooleanProperty.create("open");
public ControlsBlock(Properties p_54120_) {
super(p_54120_);
registerDefaultState(defaultBlockState().setValue(OPEN, false));
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> pBuilder) {
super.createBlockStateDefinition(pBuilder.add(FACING, OPEN));
}
@Override
public BlockState updateShape(BlockState pState, Direction pDirection, BlockState pNeighborState,
LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pNeighborPos) {
return pState.setValue(OPEN, pLevel instanceof ContraptionWorld);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
BlockState state = super.getStateForPlacement(pContext);
Direction horizontalDirection = pContext.getHorizontalDirection();
Player player = pContext.getPlayer();
state = state.setValue(FACING, horizontalDirection.getOpposite());
if (player != null && player.isSteppingCarefully())
state = state.setValue(FACING, horizontalDirection);
return state;
}
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return AllShapes.CONTROLS.get(pState.getValue(FACING));
}
}

View file

@ -0,0 +1,21 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls;
import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class ControlsMovementBehaviour extends MovementBehaviour {
@Override
@OnlyIn(Dist.CLIENT)
public void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffer) {
ControlsRenderer.render(context, renderWorld, matrices, buffer);
}
}

View file

@ -0,0 +1,47 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls;
import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
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.Iterate;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
public class ControlsRenderer {
public static void render(MovementContext context, VirtualRenderWorld renderWorld, ContraptionMatrices matrices,
MultiBufferSource buffer) {
BlockState state = context.state;
Direction facing = state.getValue(ControlsBlock.FACING);
SuperByteBuffer cover = CachedBufferer.partial(AllBlockPartials.TRAIN_CONTROLS_COVER, state);
float hAngle = 180 + AngleHelper.horizontalAngle(facing);
cover.transform(matrices.getModel())
.centre()
.rotateY(hAngle)
.unCentre()
.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
for (boolean first : Iterate.trueAndFalse) {
SuperByteBuffer lever = CachedBufferer.partial(AllBlockPartials.TRAIN_CONTROLS_LEVER, state);
lever.transform(matrices.getModel())
.centre()
.rotateY(hAngle)
.unCentre()
.translate(first ? 0 : 6 / 16f, 0, 0)
.light(matrices.getWorld(), ContraptionRenderDispatcher.getContraptionWorldLight(context, renderWorld))
.renderInto(matrices.getViewProjection(), buffer.getBuffer(RenderType.solid()));
}
}
}

View file

@ -0,0 +1,253 @@
package com.simibubi.create.content.logistics.trains;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
public class BezierConnection {
public Couple<BlockPos> tePositions;
public Couple<Boolean> trackEnds;
public Couple<Vec3> starts;
public Couple<Vec3> axes;
public Couple<Vec3> normals;
public boolean primary;
// runtime
Vec3 finish1;
Vec3 finish2;
private boolean resolved;
private double length;
private float[] stepLUT;
private int segments;
private double radius;
private double handleLength;
public BezierConnection(Couple<BlockPos> positions, Couple<Vec3> starts, Couple<Vec3> axes, Couple<Vec3> normals,
Couple<Boolean> targets, boolean primary) {
tePositions = positions;
this.starts = starts;
this.axes = axes;
this.normals = normals;
this.trackEnds = targets;
this.primary = primary;
resolved = false;
}
public BezierConnection secondary() {
return new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), trackEnds.swap(),
false);
}
public BezierConnection(CompoundTag compound) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos),
Couple.deserializeEach(compound.getList("Starts", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.deserializeEach(compound.getList("Axes", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.deserializeEach(compound.getList("Normals", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.create(compound.getBoolean("TrackEnd1"), compound.getBoolean("TrackEnd2")),
compound.getBoolean("Primary"));
}
public CompoundTag write() {
CompoundTag compound = new CompoundTag();
compound.putBoolean("Primary", primary);
compound.putBoolean("TrackEnd1", trackEnds.getFirst());
compound.putBoolean("TrackEnd2", trackEnds.getSecond());
compound.put("Positions", tePositions.serializeEach(NbtUtils::writeBlockPos));
compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound));
compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound));
compound.put("Normals", normals.serializeEach(VecHelper::writeNBTCompound));
return compound;
}
public BezierConnection(FriendlyByteBuf buffer) {
this(Couple.create(buffer::readBlockPos), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(buffer::readBoolean), buffer.readBoolean());
}
public void write(FriendlyByteBuf buffer) {
tePositions.forEach(buffer::writeBlockPos);
starts.forEach(v -> VecHelper.write(v, buffer));
axes.forEach(v -> VecHelper.write(v, buffer));
normals.forEach(v -> VecHelper.write(v, buffer));
trackEnds.forEach(buffer::writeBoolean);
buffer.writeBoolean(primary);
}
public BlockPos getKey() {
return tePositions.getSecond();
}
public boolean isPrimary() {
return primary;
}
// Runtime information
public double getLength() {
resolve();
return length;
}
public float[] getStepLUT() {
resolve();
return stepLUT;
}
public int getSegmentCount() {
resolve();
return segments;
}
public Vec3 getPosition(double t) {
resolve();
return VecHelper.bezier(starts.getFirst(), starts.getSecond(), finish1, finish2, (float) t);
}
public double getRadius() {
resolve();
return radius;
}
public double getHandleLength() {
resolve();
return handleLength;
}
public double incrementT(double currentT, double distance) {
resolve();
double dx =
VecHelper.bezierDerivative(starts.getFirst(), starts.getSecond(), finish1, finish2, (float) currentT)
.length() / getLength();
return currentT + distance / dx;
}
public Vec3 getNormal(double t) {
resolve();
Vec3 end1 = starts.getFirst();
Vec3 end2 = starts.getSecond();
Vec3 fn1 = normals.getFirst();
Vec3 fn2 = normals.getSecond();
Vec3 derivative = VecHelper.bezierDerivative(end1, end2, finish1, finish2, (float) t)
.normalize();
Vec3 faceNormal = fn1.equals(fn2) ? fn1 : VecHelper.slerp((float) t, fn1, fn2);
Vec3 normal = faceNormal.cross(derivative)
.normalize();
return derivative.cross(normal);
}
private void resolve() {
if (resolved)
return;
resolved = true;
Vec3 end1 = starts.getFirst();
Vec3 end2 = starts.getSecond();
Vec3 axis1 = axes.getFirst()
.normalize();
Vec3 axis2 = axes.getSecond()
.normalize();
determineHandles(end1, end2, axis1, axis2);
finish1 = axis1.scale(handleLength)
.add(end1);
finish2 = axis2.scale(handleLength)
.add(end2);
int scanCount = 16;
length = 0;
{
Vec3 previous = end1;
for (int i = 0; i <= scanCount; i++) {
float t = i / (float) scanCount;
Vec3 result = VecHelper.bezier(end1, end2, finish1, finish2, t);
if (previous != null)
length += result.distanceTo(previous);
previous = result;
}
}
segments = (int) (length * 2);
stepLUT = new float[segments + 1];
stepLUT[0] = 1;
float combinedDistance = 0;
// determine step lut
{
Vec3 previous = end1;
for (int i = 0; i <= segments; i++) {
float t = i / (float) segments;
Vec3 result = VecHelper.bezier(end1, end2, finish1, finish2, t);
if (i > 0) {
combinedDistance += result.distanceTo(previous) / length;
stepLUT[i] = (float) (t / combinedDistance);
}
previous = result;
}
}
}
private void determineHandles(Vec3 end1, Vec3 end2, Vec3 axis1, Vec3 axis2) {
Vec3 cross1 = axis1.cross(new Vec3(0, 1, 0));
Vec3 cross2 = axis2.cross(new Vec3(0, 1, 0));
radius = 0;
double a1 = Mth.atan2(-axis2.z, -axis2.x);
double a2 = Mth.atan2(axis1.z, axis1.x);
double angle = a1 - a2;
float circle = 2 * Mth.PI;
angle = (angle + circle) % circle;
if (Math.abs(circle - angle) < Math.abs(angle))
angle = circle - angle;
if (Mth.equal(angle, 0)) {
double[] intersect = VecHelper.intersect(end1, end2, axis1, cross2, Axis.Y);
if (intersect != null) {
double t = Math.abs(intersect[0]);
double u = Math.abs(intersect[1]);
double min = Math.min(t, u);
double max = Math.max(t, u);
if (min > 1.2 && max / min > 1 && max / min < 3) {
handleLength = (max - min);
return;
}
}
handleLength = end2.distanceTo(end1) / 3;
return;
}
double n = circle / angle;
double factor = 4 / 3d * Math.tan(Math.PI / (2 * n));
double[] intersect = VecHelper.intersect(end1, end2, cross1, cross2, Axis.Y);
if (intersect == null) {
handleLength = end2.distanceTo(end1) / 3;
return;
}
radius = Math.abs(intersect[1]);
handleLength = radius * factor;
if (Mth.equal(handleLength, 0))
handleLength = 1;
}
}

View file

@ -0,0 +1,134 @@
package com.simibubi.create.content.logistics.trains;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.Train;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.dimension.DimensionType;
public class GlobalRailwayManager {
public Map<UUID, TrackGraph> trackNetworks;
private TrackSavedData trackData;
public Map<UUID, Train> trains;
public Map<Integer, Carriage> carriageById;
public TrackGraphSync sync;
//
public GlobalRailwayManager() {
cleanUp();
}
public void playerLogin(Player player) {
if (player instanceof ServerPlayer serverPlayer) {
loadTrackData(serverPlayer.getServer());
trackNetworks.values()
.forEach(g -> sync.sendFullGraphTo(g, serverPlayer));
}
}
public void levelLoaded(LevelAccessor level) {
MinecraftServer server = level.getServer();
if (server == null || server.overworld() != level)
return;
cleanUp();
trackData = null;
loadTrackData(server);
}
private void loadTrackData(MinecraftServer server) {
if (trackData != null)
return;
trackData = TrackSavedData.load(server);
trackNetworks = trackData.getTrackNetworks();
}
public void levelUnloaded(LevelAccessor level) {
// MinecraftServer server = level.getServer();
// if (server == null || server.overworld() != level)
// return;
// cleanUp();
}
public void cleanUp() {
trackNetworks = new HashMap<>();
trains = new HashMap<>();
carriageById = new HashMap<>();
sync = new TrackGraphSync();
}
public void markTracksDirty() {
if (trackData != null)
trackData.setDirty();
}
//
public TrackGraph getOrCreateGraph(UUID graphID) {
return trackNetworks.computeIfAbsent(graphID, uid -> new TrackGraph(graphID));
}
public void putGraph(TrackGraph graph) {
trackNetworks.put(graph.id, graph);
markTracksDirty();
}
public void removeGraph(TrackGraph railGraph) {
trackNetworks.remove(railGraph.id);
markTracksDirty();
}
public void updateSplitGraph(TrackGraph graph) {
Set<TrackGraph> disconnected = graph.findDisconnectedGraphs(null);
disconnected.forEach(this::putGraph);
if (!disconnected.isEmpty()) {
sync.graphSplit(graph, disconnected);
markTracksDirty();
}
}
@Nullable
public TrackGraph getGraph(LevelAccessor level, TrackNodeLocation vertex) {
if (trackNetworks == null)
return null;
for (TrackGraph railGraph : trackNetworks.values())
if (railGraph.locateNode(vertex) != null)
return railGraph;
return null;
}
public void tick(Level level) {
ResourceLocation location2 = DimensionType.OVERWORLD_LOCATION.location();
ResourceLocation location = level.dimension()
.location();
if (!location.equals(location2))
return;
for (Train train : trains.values())
train.tick(level);
}
public void clientTick() {
if (KineticDebugger.isActive()) {
trackNetworks.values()
.forEach(TrackGraph::debugViewNodes);
}
}
}

View file

@ -0,0 +1,80 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public interface IBogeyBlock extends IWrenchable {
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public static void register(ResourceLocation block) {
BOGEYS.add(block);
}
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state);
public double getWheelPointSpacing();
public double getWheelRadius();
public boolean allowsSingleBogeyCarriage();
public Vec3 getConnectorAnchorOffset();
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay);
public default Direction getBogeyUpDirection() {
return Direction.UP;
}
public boolean isTrackAxisAlongFirstCoordinate(BlockState state);
@Nullable
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst);
@Override
default BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
Block block = state.getBlock();
int indexOf = BOGEYS.indexOf(block.getRegistryName());
if (indexOf == -1)
return state;
int index = (indexOf + 1) % BOGEYS.size();
Direction bogeyUpDirection = getBogeyUpDirection();
boolean trackAxisAlongFirstCoordinate = isTrackAxisAlongFirstCoordinate(state);
while (index != indexOf) {
ResourceLocation id = BOGEYS.get(index);
Block newBlock = ForgeRegistries.BLOCKS.getValue(id);
if (newBlock instanceof IBogeyBlock bogey) {
BlockState matchingBogey = bogey.getMatchingBogey(bogeyUpDirection, trackAxisAlongFirstCoordinate);
if (matchingBogey != null)
return matchingBogey;
}
index = (index + 1) % BOGEYS.size();
}
return state;
}
}

View file

@ -0,0 +1,23 @@
package com.simibubi.create.content.logistics.trains;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class IBogeyTileEntityRenderer<T extends BlockEntity> extends SafeTileEntityRenderer<T> {
public IBogeyTileEntityRenderer(BlockEntityRendererProvider.Context context) {}
@Override
protected void renderSafe(T te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) {
BlockState blockState = te.getBlockState();
if (blockState.getBlock()instanceof IBogeyBlock bogey)
bogey.render(blockState, 0, ms, partialTicks, buffer, light, overlay);
}
}

View file

@ -0,0 +1,35 @@
package com.simibubi.create.content.logistics.trains;
import com.jozufozu.flywheel.core.PartialModel;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public interface ITrackBlock {
public Vec3 getUpNormal(BlockGetter world, BlockPos pos, BlockState state);
public Vec3 getTrackAxis(BlockGetter world, BlockPos pos, BlockState state);
public Vec3 getCurveStart(BlockGetter world, BlockPos pos, BlockState state, Vec3 axis);
public BlockState getBogeyAnchor(BlockGetter world, BlockPos pos, BlockState state); // should be on bogey side
public boolean trackEquals(BlockState state1, BlockState state2);
@OnlyIn(Dist.CLIENT)
public PartialModel prepareStationOverlay(BlockGetter world, BlockPos pos, BlockState state,
AxisDirection direction, PoseStack transform);
@OnlyIn(Dist.CLIENT)
public PartialModel prepareAssemblyOverlay(BlockGetter world, BlockPos pos, BlockState state, Direction direction,
PoseStack ms);
}

View file

@ -0,0 +1,70 @@
package com.simibubi.create.content.logistics.trains;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
public class TrackEdge {
BezierConnection turn;
public TrackEdge(BezierConnection turn) {
this.turn = turn;
}
public boolean isTurn() {
return turn != null;
}
public BezierConnection getTurn() {
return turn;
}
public Vec3 getDirection(TrackNode node1, TrackNode node2, boolean fromFirst) {
return getPosition(node1, node2, fromFirst ? 0.25f : 1)
.subtract(getPosition(node1, node2, fromFirst ? 0 : 0.75f))
.normalize();
}
public double getLength(TrackNode node1, TrackNode node2) {
return isTurn() ? turn.getLength()
: node1.location.getLocation()
.distanceTo(node2.location.getLocation());
}
public double incrementT(TrackNode node1, TrackNode node2, double currentT, double distance) {
distance = distance / getLength(node1, node2);
return isTurn() ? turn.incrementT(currentT, distance) : currentT + distance;
}
public Vec3 getPosition(TrackNode node1, TrackNode node2, double t) {
return isTurn() ? turn.getPosition(Mth.clamp(t, 0, 1))
: VecHelper.lerp((float) t, node1.location.getLocation(), node2.location.getLocation());
}
public Vec3 getNormal(TrackNode node1, TrackNode node2, double t) {
return isTurn() ? turn.getNormal(Mth.clamp(t, 0, 1)) : node1.getNormal();
}
public void write(FriendlyByteBuf buffer) {
buffer.writeBoolean(isTurn());
if (isTurn())
turn.write(buffer);
}
public static TrackEdge read(FriendlyByteBuf buffer) {
return new TrackEdge(buffer.readBoolean() ? new BezierConnection(buffer) : null);
}
public CompoundTag write() {
return isTurn() ? turn.write() : new CompoundTag();
}
public static TrackEdge read(CompoundTag tag) {
return new TrackEdge(tag.contains("Positions") ? new BezierConnection(tag) : null);
}
}

View file

@ -0,0 +1,375 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.management.GlobalStation;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
public class TrackGraph {
public static final AtomicInteger netIdGenerator = new AtomicInteger();
UUID id;
Color color;
Map<TrackNodeLocation, TrackNode> nodes;
Map<Integer, TrackNode> nodesById;
Map<TrackNode, Map<TrackNode, TrackEdge>> connectionsByNode;
Map<UUID, GlobalStation> stations;
public TrackGraph() {
this(UUID.randomUUID());
}
public TrackGraph(UUID graphID) {
id = graphID;
nodes = new HashMap<>();
nodesById = new HashMap<>();
connectionsByNode = new IdentityHashMap<>();
color = Color.rainbowColor(new Random().nextInt());
stations = new HashMap<>();
}
//
@Nullable
public GlobalStation getStation(UUID id) {
return stations.get(id);
}
public Collection<GlobalStation> getStations() {
return stations.values();
}
public void removeStation(UUID id) {
stations.remove(id);
markDirty();
}
public void addStation(GlobalStation station) {
stations.put(station.id, station);
markDirty();
}
//
public TrackNode locateNode(Vec3 position) {
return locateNode(new TrackNodeLocation(position));
}
public TrackNode locateNode(TrackNodeLocation position) {
return nodes.get(position);
}
public TrackNode getNode(int netId) {
return nodesById.get(netId);
}
public boolean createNode(DiscoveredLocation location) {
if (!createSpecificNode(location, nextNodeId(), location.normal))
return false;
Create.RAILWAYS.sync.nodeAdded(this, nodes.get(location));
markDirty();
return true;
}
public boolean createSpecificNode(TrackNodeLocation location, int netId, Vec3 normal) {
return addNode(new TrackNode(location, netId, normal));
}
public boolean addNode(TrackNode node) {
if (nodes.putIfAbsent(node.getLocation(), node) != null)
return false;
nodesById.put(node.getNetId(), node);
return true;
}
public boolean removeNode(TrackNodeLocation location) {
TrackNode removed = nodes.remove(location);
if (removed == null)
return false;
nodesById.remove(removed.netId);
if (!connectionsByNode.containsKey(removed))
return true;
Map<TrackNode, TrackEdge> connections = connectionsByNode.remove(removed);
for (TrackNode railNode : connections.keySet())
if (connectionsByNode.containsKey(railNode))
connectionsByNode.get(railNode)
.remove(removed);
return true;
}
public static int nextNodeId() {
return netIdGenerator.incrementAndGet();
}
public void transferAll(TrackGraph toOther) {
toOther.nodes.putAll(nodes);
toOther.nodesById.putAll(nodesById);
toOther.connectionsByNode.putAll(connectionsByNode);
nodesById.forEach((id, node) -> Create.RAILWAYS.sync.nodeAdded(toOther, node));
connectionsByNode.forEach(
(node1, map) -> map.forEach((node2, edge) -> Create.RAILWAYS.sync.edgeAdded(toOther, node1, node2, edge)));
markDirty();
nodes.clear();
nodesById.clear();
connectionsByNode.clear();
}
public Set<TrackGraph> findDisconnectedGraphs(@Nullable Map<Integer, UUID> preAssignedIds) {
Set<TrackGraph> dicovered = new HashSet<>();
Set<TrackNodeLocation> vertices = new HashSet<>(nodes.keySet());
List<TrackNodeLocation> frontier = new ArrayList<>();
TrackGraph target = null;
while (!vertices.isEmpty()) {
if (target != null)
dicovered.add(target);
TrackNodeLocation start = vertices.stream()
.findFirst()
.get();
frontier.add(start);
vertices.remove(start);
while (!frontier.isEmpty()) {
TrackNodeLocation current = frontier.remove(0);
TrackNode currentNode = locateNode(current);
Map<TrackNode, TrackEdge> connections = getConnectionsFrom(currentNode);
for (TrackNode connected : connections.keySet())
if (vertices.remove(connected.getLocation()))
frontier.add(connected.getLocation());
if (target != null) {
transfer(currentNode, target);
if (preAssignedIds != null && preAssignedIds.containsKey(currentNode.getNetId()))
target.id = preAssignedIds.get(currentNode.getNetId());
}
}
frontier.clear();
target = new TrackGraph();
}
return dicovered;
}
public void transfer(TrackNode node, TrackGraph target) {
target.addNode(node);
Map<TrackNode, TrackEdge> connections = getConnectionsFrom(node);
if (!connections.isEmpty())
target.connectionsByNode.put(node, connections);
nodes.remove(node.getLocation());
nodesById.remove(node.getNetId());
connectionsByNode.remove(node);
}
public boolean isEmpty() {
return nodes.isEmpty();
}
public Map<TrackNode, TrackEdge> getConnectionsFrom(TrackNode node) {
return connectionsByNode.getOrDefault(node, new HashMap<>());
}
public void connectNodes(TrackNodeLocation location, TrackNodeLocation location2, TrackEdge edge) {
TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2);
TrackEdge edge2 = new TrackEdge(edge.turn != null ? edge.turn.secondary() : null);
putConnection(node1, node2, edge);
putConnection(node2, node1, edge2);
Create.RAILWAYS.sync.edgeAdded(this, node1, node2, edge);
Create.RAILWAYS.sync.edgeAdded(this, node2, node1, edge2);
markDirty();
}
public void disconnectNodes(TrackNode node1, TrackNode node2) {
Map<TrackNode, TrackEdge> map1 = connectionsByNode.get(node1);
Map<TrackNode, TrackEdge> map2 = connectionsByNode.get(node2);
if (map1 != null)
map1.remove(node2);
if (map2 != null)
map2.remove(node1);
}
public void putConnection(TrackNode node1, TrackNode node2, TrackEdge edge) {
connectionsByNode.computeIfAbsent(node1, n -> new IdentityHashMap<>())
.put(node2, edge);
}
public void markDirty() {
Create.RAILWAYS.markTracksDirty();
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id);
tag.putInt("Color", color.getRGB());
Map<TrackNode, Integer> indexTracker = new HashMap<>();
ListTag nodesList = new ListTag();
int i = 0;
for (TrackNode railNode : nodes.values()) {
indexTracker.put(railNode, i);
CompoundTag nodeTag = new CompoundTag();
nodeTag.put("Location", NbtUtils.writeBlockPos(new BlockPos(railNode.getLocation())));
nodeTag.put("Normal", VecHelper.writeNBT(railNode.getNormal()));
nodesList.add(nodeTag);
i++;
}
connectionsByNode.forEach((node1, map) -> {
Integer index1 = indexTracker.get(node1);
if (index1 == null)
return;
CompoundTag nodeTag = (CompoundTag) nodesList.get(index1);
ListTag connectionsList = new ListTag();
map.forEach((node2, edge) -> {
CompoundTag connectionTag = new CompoundTag();
Integer index2 = indexTracker.get(node2);
if (index2 == null)
return;
connectionTag.putInt("To", index2);
connectionTag.put("EdgeData", edge.write());
connectionsList.add(connectionTag);
});
nodeTag.put("Connections", connectionsList);
});
tag.put("Nodes", nodesList);
tag.put("Stations", NBTHelper.writeCompoundList(stations.values(), GlobalStation::write));
return tag;
}
public static TrackGraph read(CompoundTag tag) {
TrackGraph graph = new TrackGraph(tag.getUUID("Id"));
graph.color = new Color(tag.getInt("Color"));
Map<Integer, TrackNode> indexTracker = new HashMap<>();
ListTag nodesList = tag.getList("Nodes", Tag.TAG_COMPOUND);
int i = 0;
for (Tag t : nodesList) {
CompoundTag nodeTag = (CompoundTag) t;
TrackNodeLocation location =
TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(nodeTag.getCompound("Location")));
Vec3 normal = VecHelper.readNBT(nodeTag.getList("Normal", Tag.TAG_DOUBLE));
graph.createSpecificNode(location, nextNodeId(), normal);
indexTracker.put(i, graph.locateNode(location));
i++;
}
i = 0;
for (Tag t : nodesList) {
CompoundTag nodeTag = (CompoundTag) t;
TrackNode node1 = indexTracker.get(i);
i++;
if (!nodeTag.contains("Connections"))
continue;
NBTHelper.iterateCompoundList(nodeTag.getList("Connections", Tag.TAG_COMPOUND), c -> {
TrackNode node2 = indexTracker.get(c.getInt("To"));
TrackEdge edge = TrackEdge.read(c.getCompound("EdgeData"));
graph.putConnection(node1, node2, edge);
});
}
NBTHelper.readCompoundList(tag.getList("Stations", Tag.TAG_COMPOUND), GlobalStation::new)
.forEach(s -> graph.stations.put(s.id, s));
return graph;
}
public void debugViewNodes() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 50)
continue;
Vec3 v1 = location.add(0, 3 / 16f, 0);
Vec3 v2 = v1.add(node.normal.scale(0.75f));
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
.colored(Color.mixColors(Color.WHITE, color, .5f))
.lineWidth(1 / 8f);
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode)
continue;
TrackEdge edge = entry.getValue();
if (!edge.isTurn()) {
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, 0), edge.getPosition(node, other, 1))
.colored(color)
.lineWidth(1 / 16f);
continue;
}
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER.showLine(previous, previous, current)
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
}

View file

@ -0,0 +1,221 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.NetworkEvent.Context;
import net.minecraftforge.network.PacketDistributor;
public class TrackGraphSync {
//
public void nodeAdded(TrackGraph graph, TrackNode node) {
flushPacket(graph.id);
currentPacket.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal()));
}
public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) {
flushPacket(graph.id);
currentPacket.addedEdges.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge));
}
public void nodeRemoved(TrackGraph graph, TrackNode node) {
flushPacket(graph.id);
if (currentPacket.addedNodes.remove(node.getNetId()) == null)
currentPacket.removedNodes.add(node.getNetId());
}
public void graphSplit(TrackGraph graph, Set<TrackGraph> additional) {
flushPacket(graph.id);
additional.forEach(rg -> currentPacket.splitSubGraphs.put(rg.nodesById.keySet()
.stream()
.findFirst()
.get(), rg.id));
}
public void graphRemoved(TrackGraph graph) {
flushPacket(graph.id);
currentPacket.delete = true;
}
public void finish() {
flushPacket(null);
}
//
private RailGraphSyncPacket currentPacket;
public void sendFullGraphTo(TrackGraph graph, ServerPlayer player) {
// TODO ensure packet size limit
RailGraphSyncPacket packet = new RailGraphSyncPacket(graph.id);
for (TrackNode node : graph.nodes.values()) {
packet.addedNodes.put(node.getNetId(), Pair.of(node.getLocation(), node.getNormal()));
if (!graph.connectionsByNode.containsKey(node))
continue;
graph.connectionsByNode.get(node)
.forEach((node2, edge) -> packet.addedEdges
.add(Pair.of(Couple.create(node.getNetId(), node2.getNetId()), edge)));
}
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), packet);
}
private void flushPacket(@Nullable UUID graphId) {
if (currentPacket != null) {
if (currentPacket.graphId.equals(graphId))
return;
AllPackets.channel.send(PacketDistributor.ALL.noArg(), currentPacket);
currentPacket = null;
}
if (graphId != null)
currentPacket = new RailGraphSyncPacket(graphId);
}
public static class RailGraphSyncPacket extends SimplePacketBase {
UUID graphId;
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, TrackEdge>> addedEdges;
List<Integer> removedNodes;
Map<Integer, UUID> splitSubGraphs;
boolean delete;
public RailGraphSyncPacket(UUID graphId) {
this.graphId = graphId;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
removedNodes = new ArrayList<>();
splitSubGraphs = new HashMap<>();
delete = false;
}
public RailGraphSyncPacket(FriendlyByteBuf buffer) {
int size;
graphId = buffer.readUUID();
delete = buffer.readBoolean();
if (delete)
return;
addedNodes = new HashMap<>();
addedEdges = new ArrayList<>();
removedNodes = new ArrayList<>();
splitSubGraphs = new HashMap<>();
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
removedNodes.add(buffer.readVarInt());
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedNodes.put(buffer.readVarInt(),
Pair.of(TrackNodeLocation.fromPackedPos(buffer.readBlockPos()), VecHelper.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
addedEdges.add(Pair.of(Couple.create(buffer::readVarInt), TrackEdge.read(buffer)));
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
splitSubGraphs.put(buffer.readVarInt(), buffer.readUUID());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeUUID(graphId);
buffer.writeBoolean(delete);
if (delete)
return;
buffer.writeVarInt(removedNodes.size());
removedNodes.forEach(buffer::writeVarInt);
buffer.writeVarInt(addedNodes.size());
addedNodes.forEach((node, loc) -> {
buffer.writeVarInt(node);
buffer.writeBlockPos(new BlockPos(loc.getFirst()));
VecHelper.write(loc.getSecond(), buffer);
});
buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> {
pair.getFirst()
.forEach(buffer::writeVarInt);
pair.getSecond()
.write(buffer);
});
buffer.writeVarInt(splitSubGraphs.size());
splitSubGraphs.forEach((node, uuid) -> {
buffer.writeVarInt(node);
buffer.writeUUID(uuid);
});
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
GlobalRailwayManager manager = CreateClient.RAILWAYS;
TrackGraph railGraph = manager.getOrCreateGraph(graphId);
if (delete) {
manager.removeGraph(railGraph);
return;
}
for (int nodeId : removedNodes) {
TrackNode node = railGraph.getNode(nodeId);
if (node != null)
railGraph.removeNode(node.getLocation());
}
for (Entry<Integer, Pair<TrackNodeLocation, Vec3>> entry : addedNodes.entrySet())
railGraph.createSpecificNode(entry.getValue()
.getFirst(), entry.getKey(),
entry.getValue()
.getSecond());
for (Pair<Couple<Integer>, TrackEdge> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()
.map(railGraph::getNode);
if (nodes.getFirst() != null && nodes.getSecond() != null)
railGraph.putConnection(nodes.getFirst(), nodes.getSecond(), pair.getSecond());
}
if (!splitSubGraphs.isEmpty())
railGraph.findDisconnectedGraphs(splitSubGraphs)
.forEach(manager::putGraph);
});
context.get()
.setPacketHandled(true);
}
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.content.logistics.trains;
import net.minecraft.world.phys.Vec3;
public class TrackNode {
int netId;
Vec3 normal;
TrackNodeLocation location;
public TrackNode(TrackNodeLocation location, int netId, Vec3 normal) {
this.location = location;
this.netId = netId;
this.normal = normal;
}
public TrackNodeLocation getLocation() {
return location;
}
public int getNetId() {
return netId;
}
public Vec3 getNormal() {
return normal;
}
}

View file

@ -0,0 +1,72 @@
package com.simibubi.create.content.logistics.trains;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class TrackNodeLocation extends Vec3i {
public TrackNodeLocation(Vec3 vec) {
this(vec.x, vec.y, vec.z);
}
public TrackNodeLocation(double p_121865_, double p_121866_, double p_121867_) {
super(Math.round(p_121865_ * 2), Math.floor(p_121866_ * 2), Math.round(p_121867_ * 2));
}
public static TrackNodeLocation fromPackedPos(BlockPos bufferPos) {
return new TrackNodeLocation(bufferPos);
}
private TrackNodeLocation(BlockPos readBlockPos) {
super(readBlockPos.getX(), readBlockPos.getY(), readBlockPos.getZ());
}
public Vec3 getLocation() {
return new Vec3(getX() / 2f, getY() / 2f, getZ() / 2f);
}
@Override
public boolean equals(Object pOther) {
return super.equals(pOther);
}
@Override
public int hashCode() {
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
}
public static class DiscoveredLocation extends TrackNodeLocation {
BezierConnection turn = null;
Vec3 normal;
public DiscoveredLocation(double p_121865_, double p_121866_, double p_121867_) {
super(p_121865_, p_121866_, p_121867_);
}
public DiscoveredLocation(Vec3 vec) {
super(vec);
}
public DiscoveredLocation viaTurn(BezierConnection turn) {
this.turn = turn;
return this;
}
public DiscoveredLocation withNormal(Vec3 normal) {
this.normal = normal;
return this;
}
public boolean connectedViaTurn() {
return turn != null;
}
public BezierConnection getTurn() {
return turn;
}
}
}

View file

@ -0,0 +1,374 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackBlock.TrackShape;
import com.simibubi.create.content.logistics.trains.track.TrackTileEntity;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class TrackPropagator {
static class FrontierEntry {
BlockPos prevPos;
DiscoveredLocation prevNode;
BlockPos currentPos;
DiscoveredLocation currentNode;
DiscoveredLocation parentNode;
public FrontierEntry(BlockPos previousPos, BlockPos pos, DiscoveredLocation location) {
this(null, previousPos, null, pos, location);
}
public FrontierEntry(DiscoveredLocation parent, BlockPos previousPos, DiscoveredLocation previousNode,
BlockPos pos, DiscoveredLocation location) {
parentNode = parent;
prevPos = previousPos;
prevNode = previousNode;
currentPos = pos;
currentNode = location;
}
}
public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) {
List<Pair<BlockPos, DiscoveredLocation>> ends = getEnds(reader, pos, state, null, false);
TrackGraph foundGraph = null;
GlobalRailwayManager manager = Create.RAILWAYS;
TrackGraphSync sync = manager.sync;
for (Pair<BlockPos, DiscoveredLocation> removedEnd : ends) {
DiscoveredLocation removedLocation = removedEnd.getSecond();
if (foundGraph == null)
foundGraph = manager.getGraph(reader, removedLocation);
if (foundGraph != null) {
TrackNode removedNode = foundGraph.locateNode(removedLocation);
if (removedNode != null) {
foundGraph.removeNode(removedLocation);
sync.nodeRemoved(foundGraph, removedNode);
}
}
}
if (foundGraph != null && foundGraph.isEmpty()) {
manager.removeGraph(foundGraph);
sync.graphRemoved(foundGraph);
}
Set<TrackGraph> toUpdate = new HashSet<>();
for (Pair<BlockPos, DiscoveredLocation> removedEnd : ends) {
BlockPos adjPos = removedEnd.getFirst();
BlockState adjState = reader.getBlockState(adjPos);
if (!getEnds(reader, adjPos, adjState, removedEnd.getSecond(), true).isEmpty())
toUpdate.add(onRailAdded(reader, adjPos, adjState));
}
for (TrackGraph railGraph : toUpdate)
manager.updateSplitGraph(railGraph);
sync.finish();
manager.markTracksDirty();
}
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) {
// 1. Remove all immediately reachable node locations
GlobalRailwayManager manager = Create.RAILWAYS;
TrackGraphSync sync = manager.sync;
List<FrontierEntry> frontier = new ArrayList<>();
Set<DiscoveredLocation> visited = new HashSet<>();
Set<TrackGraph> connectedGraphs = new HashSet<>();
addInitialEndsOf(reader, pos, state, frontier, false);
int emergencyExit = 1000;
while (!frontier.isEmpty()) {
if (emergencyExit-- == 0)
break;
FrontierEntry entry = frontier.remove(0);
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
TrackGraph graph = manager.getGraph(reader, entry.currentNode);
if (graph != null) {
TrackNode node = graph.locateNode(entry.currentNode);
graph.removeNode(entry.currentNode);
sync.nodeRemoved(graph, node);
connectedGraphs.add(graph);
continue;
}
continueSearch(frontier, visited, entry, ends);
}
frontier.clear();
visited.clear();
TrackGraph graph = null;
// Remove empty graphs
for (Iterator<TrackGraph> iterator = connectedGraphs.iterator(); iterator.hasNext();) {
TrackGraph railGraph = iterator.next();
if (!railGraph.isEmpty() || connectedGraphs.size() == 1)
continue;
manager.removeGraph(railGraph);
sync.graphRemoved(railGraph);
iterator.remove();
}
// Merge graphs if more than 1
if (connectedGraphs.size() > 1) {
for (TrackGraph other : connectedGraphs)
if (graph == null)
graph = other;
else {
other.transferAll(graph);
manager.removeGraph(other);
sync.graphRemoved(other);
}
} else if (connectedGraphs.size() == 1) {
graph = connectedGraphs.stream()
.findFirst()
.get();
} else
manager.putGraph(graph = new TrackGraph());
DiscoveredLocation startNode = null;
List<BlockPos> startPositions = new ArrayList<>();
// 2. Find the first graph node candidate nearby
addInitialEndsOf(reader, pos, state, frontier, true);
emergencyExit = 1000;
while (!frontier.isEmpty()) {
if (emergencyExit-- == 0)
break;
FrontierEntry entry = frontier.remove(0);
// CreateClient.OUTLINER
// .showAABB(entry.currentNode, new AABB(entry.currentNode.getLocation(), entry.currentNode.getLocation()
// .add(0, 2, 0)), 120)
// .colored(Color.GREEN)
// .lineWidth(1 / 16f);
// CreateClient.OUTLINER.showAABB(entry.currentPos, new AABB(entry.currentPos).contract(0, 1, 0), 120)
// .colored(0x7777ff)
// .lineWidth(1 / 16f);
// if (entry.prevPos != null) {
// CreateClient.OUTLINER.showAABB(entry.prevPos, new AABB(entry.prevPos).contract(0, 1, 0), 120)
// .colored(0x3333aa)
// .lineWidth(1 / 16f);
// }
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
if (isValidGraphNodeLocation(entry.currentNode, ends)) {
startNode = entry.currentNode;
startPositions.add(entry.prevPos);
startPositions.add(entry.currentPos);
break;
}
continueSearch(frontier, visited, entry, ends);
}
frontier.clear();
if (graph.createNode(startNode))
sync.nodeAdded(graph, graph.locateNode(startNode));
// CreateClient.OUTLINER.showAABB(graph, new AABB(startNode.getLocation(), startNode.getLocation()
// .add(0, 2, 0)), 20)
// .lineWidth(1 / 32f);
for (BlockPos position : startPositions)
frontier.add(new FrontierEntry(startNode, null, null, position, startNode));
// 3. Build up the graph via all connected nodes
emergencyExit = 1000;
while (!frontier.isEmpty()) {
if (emergencyExit-- == 0)
break;
FrontierEntry entry = frontier.remove(0);
DiscoveredLocation parentNode = entry.parentNode;
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
if (isValidGraphNodeLocation(entry.currentNode, ends) && entry.currentNode != startNode) {
boolean nodeIsNew = graph.createNode(entry.currentNode);
if (nodeIsNew)
sync.nodeAdded(graph, graph.locateNode(entry.currentNode));
graph.connectNodes(parentNode, entry.currentNode, new TrackEdge(entry.currentNode.getTurn()));
parentNode = entry.currentNode;
if (!nodeIsNew)
continue;
}
continueSearchWithParent(frontier, entry, parentNode, ends);
}
sync.finish();
manager.markTracksDirty();
return graph;
}
private static void addInitialEndsOf(LevelAccessor reader, BlockPos pos, BlockState state,
List<FrontierEntry> frontier, boolean ignoreTurns) {
for (Pair<BlockPos, DiscoveredLocation> initial : getEnds(reader, pos, state, null, ignoreTurns))
frontier.add(new FrontierEntry(initial.getFirst(), pos, initial.getSecond()));
}
private static List<Pair<BlockPos, DiscoveredLocation>> findReachableEnds(LevelAccessor reader,
FrontierEntry entry) {
BlockState currentState = reader.getBlockState(entry.currentPos);
List<Pair<BlockPos, DiscoveredLocation>> ends = new ArrayList<>();
if (entry.prevNode != null) {
BlockPos prevPos = entry.prevPos;
// PrevPos correction after a turn
if (entry.currentNode.connectedViaTurn()) {
boolean slope = false;
if (currentState.getBlock() instanceof ITrackBlock track)
slope = track.getTrackAxis(reader, entry.currentPos, currentState).y != 0;
BlockPos offset = new BlockPos(VecHelper.getCenterOf(entry.currentPos)
.subtract(entry.currentNode.getLocation()
.add(0, slope ? 0 : .5f, 0))
.scale(-2));
prevPos = entry.currentPos.offset(offset);
}
for (Pair<BlockPos, DiscoveredLocation> pair : getEnds(reader, prevPos, reader.getBlockState(prevPos),
entry.currentNode, false))
if (!pair.getSecond()
.equals(entry.prevNode))
ends.add(pair);
}
ends.addAll(getEnds(reader, entry.currentPos, currentState, entry.currentNode, false));
return ends;
}
private static void continueSearch(List<FrontierEntry> frontier, Set<DiscoveredLocation> visited,
FrontierEntry entry, List<Pair<BlockPos, DiscoveredLocation>> ends) {
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
if (visited.add(pair.getSecond()))
frontier.add(
new FrontierEntry(null, entry.currentPos, entry.currentNode, pair.getFirst(), pair.getSecond()));
}
private static void continueSearchWithParent(List<FrontierEntry> frontier, FrontierEntry entry,
DiscoveredLocation parentNode, List<Pair<BlockPos, DiscoveredLocation>> ends) {
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
frontier.add(
new FrontierEntry(parentNode, entry.currentPos, entry.currentNode, pair.getFirst(), pair.getSecond()));
}
public static boolean isValidGraphNodeLocation(DiscoveredLocation location,
List<Pair<BlockPos, DiscoveredLocation>> next) {
if (next.size() != 1)
return true;
if (location.connectedViaTurn())
return true;
DiscoveredLocation nextLocation = next.iterator()
.next()
.getSecond();
if (nextLocation.connectedViaTurn())
return true;
Vec3 vec = location.getLocation();
boolean centeredX = !Mth.equal(vec.x, Math.round(vec.x));
boolean centeredZ = !Mth.equal(vec.z, Math.round(vec.z));
if (centeredX && !centeredZ)
return ((int) Math.round(vec.z)) % 16 == 0;
return ((int) Math.round(vec.x)) % 16 == 0;
}
// TODO ITrackBlock
public static List<Pair<BlockPos, DiscoveredLocation>> getEnds(LevelReader reader, BlockPos pos, BlockState state,
@Nullable DiscoveredLocation fromEnd, boolean ignoreTurns) {
Vec3 center = VecHelper.getCenterOf(pos);
List<Pair<BlockPos, DiscoveredLocation>> list = new ArrayList<>();
if (!(state.getBlock() instanceof TrackBlock))
return list;
BlockEntity blockEntity = reader.getBlockEntity(pos);
if (state.getValue(TrackBlock.HAS_TURN) && blockEntity instanceof TrackTileEntity && !ignoreTurns) {
TrackTileEntity trackTileEntity = (TrackTileEntity) blockEntity;
trackTileEntity.getConnections()
.forEach(map -> map.forEach((connectedPos, bc) -> addToSet(fromEnd, list,
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get,
bc)));
}
TrackShape shape = state.getValue(TrackBlock.SHAPE);
if (shape != TrackShape.NONE)
addToSet(fromEnd, list, (d, b) -> shape.getAxis()
.scale(b ? d : -d)
.add(center)
.add(0, shape.getAxis().y == 0 ? -.5 : 0, 0), b -> shape.getNormal(), null);
return list;
}
private static void addToSet(DiscoveredLocation fromEnd, List<Pair<BlockPos, DiscoveredLocation>> list,
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
BezierConnection viaTurn) {
DiscoveredLocation firstLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, true));
DiscoveredLocation secondLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, false));
Pair<BlockPos, DiscoveredLocation> firstNode =
Pair.of(new BlockPos(offsetFactory.apply(1.0d, true)), firstLocation.viaTurn(viaTurn)
.withNormal(normalFactory.apply(true)));
Pair<BlockPos, DiscoveredLocation> secondNode =
Pair.of(new BlockPos(offsetFactory.apply(1.0d, false)), secondLocation.viaTurn(viaTurn)
.withNormal(normalFactory.apply(false)));
boolean skipFirst = false;
boolean skipSecond = false;
if (fromEnd != null) {
boolean equalsFirst = firstNode.getSecond()
.equals(fromEnd);
boolean equalsSecond = secondNode.getSecond()
.equals(fromEnd);
// not reachable from this end, crossover rail
if (!equalsFirst && !equalsSecond)
return;
if (equalsFirst)
skipFirst = true;
if (equalsSecond)
skipSecond = true;
}
if (!skipFirst)
list.add(firstNode);
if (!skipSecond)
list.add(secondNode);
}
}

View file

@ -0,0 +1,47 @@
package com.simibubi.create.content.logistics.trains;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.saveddata.SavedData;
public class TrackSavedData extends SavedData {
private Map<UUID, TrackGraph> trackNetworks = new HashMap<>();
@Override
public CompoundTag save(CompoundTag nbt) {
nbt.put("RailGraphs", NBTHelper.writeCompoundList(Create.RAILWAYS.trackNetworks.values(), TrackGraph::write));
return nbt;
}
private static TrackSavedData load(CompoundTag nbt) {
TrackSavedData sd = new TrackSavedData();
sd.trackNetworks = new HashMap<>();
NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> {
TrackGraph graph = TrackGraph.read(c);
sd.trackNetworks.put(graph.id, graph);
});
return sd;
}
public Map<UUID, TrackGraph> getTrackNetworks() {
return trackNetworks;
}
private TrackSavedData() {}
public static TrackSavedData load(MinecraftServer server) {
return server.overworld()
.getDataStorage()
.computeIfAbsent(TrackSavedData::load, TrackSavedData::new, "create_tracks");
}
}

View file

@ -0,0 +1,272 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.entity.MovingPoint.ITrackSelector;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class Carriage {
public static final AtomicInteger netIdGenerator = new AtomicInteger();
public Train train;
public CarriageContraption contraption;
public int bogeySpacing;
public int id;
WeakReference<CarriageContraptionEntity> entity;
Couple<CarriageBogey> bogeys;
public Carriage(CarriageBogey bogey1, @Nullable CarriageBogey bogey2, int bogeySpacing) {
this.bogeySpacing = bogeySpacing;
this.bogeys = Couple.create(bogey1, bogey2);
this.entity = new WeakReference<>(null);
this.id = netIdGenerator.incrementAndGet();
}
public void setTrain(Train train) {
this.train = train;
}
public void setContraption(CarriageContraption contraption) {
this.contraption = contraption;
contraption.setCarriage(this);
}
public double travel(Level level, double distance, @Nullable Function<MovingPoint, ITrackSelector> control) {
Vec3 leadingAnchor = leadingBogey().anchorPosition;
Vec3 trailingAnchor = trailingBogey().anchorPosition;
boolean onTwoBogeys = isOnTwoBogeys();
double stress = onTwoBogeys ? bogeySpacing - leadingAnchor.distanceTo(trailingAnchor) : 0;
// positive stress: points should move apart
// negative stress: points should move closer
double leadingBogeyModifier = 0.5d;
double trailingBogeyModifier = -0.5d;
double leadingPointModifier = 0.5d;
double trailingPointModifier = -0.5d;
MutableObject<MovingPoint> previous = new MutableObject<>();
MutableDouble distanceMoved = new MutableDouble(distance);
bogeys.forEachWithContext((bogey, firstBogey) -> {
if (!firstBogey && !onTwoBogeys)
return;
double bogeyCorrection = stress * (firstBogey ? leadingBogeyModifier : trailingBogeyModifier);
double bogeyStress = bogey.getStress();
bogey.points.forEachWithContext((point, first) -> {
MovingPoint prevPoint = previous.getValue();
ITrackSelector trackSelector =
prevPoint == null ? control == null ? point.random() : control.apply(point)
: point.follow(prevPoint);
double correction = bogeyStress * (first ? leadingPointModifier : trailingPointModifier);
double toMove = distanceMoved.getValue();
double moved = point.travel(toMove, trackSelector);
point.travel(correction + bogeyCorrection, trackSelector);
distanceMoved.setValue(moved);
previous.setValue(point);
});
bogey.updateAnchorPosition();
});
tickEntity(level);
return distanceMoved.getValue();
}
public void createEntity(Level level) {
contraption.startMoving(level);
CarriageContraptionEntity entity = CarriageContraptionEntity.create(level, contraption);
Vec3 pos = leadingBogey().anchorPosition;
entity.setPos(pos);
entity.setInitialOrientation(contraption.getAssemblyDirection()
.getClockWise());
level.addFreshEntity(entity);
this.entity = new WeakReference<>(entity);
}
public ChunkPos getChunk() {
return new ChunkPos(new BlockPos(leadingBogey().anchorPosition));
}
protected void tickEntity(Level level) {
CarriageContraptionEntity entity = this.entity.get();
if (entity == null) {
if (CarriageEntityHandler.isActiveChunk(level, getChunk()))
createEntity(level);
} else {
CarriageEntityHandler.validateCarriageEntity(entity);
if (!entity.isAlive()) {
this.entity.clear();
return;
}
}
entity = this.entity.get();
if (entity == null)
return;
if (!entity.level.isClientSide)
moveEntity(entity);
}
public void moveEntity(CarriageContraptionEntity entity) {
Vec3 positionVec = isOnTwoBogeys() ? leadingBogey().anchorPosition
: leadingBogey().leading()
.getPosition();
Vec3 coupledVec = isOnTwoBogeys() ? trailingBogey().anchorPosition
: leadingBogey().trailing()
.getPosition();
double diffX = positionVec.x - coupledVec.x;
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
entity.setPos(leadingBogey().anchorPosition);
entity.prevYaw = entity.yaw;
entity.prevPitch = entity.pitch;
entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180;
entity.pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI) * -1;
}
public void discardEntity() {
CarriageContraptionEntity entity = this.entity.get();
if (entity == null)
return;
entity.discard();
}
public MovingPoint getLeadingPoint() {
return leadingBogey().leading();
}
public MovingPoint getTrailingPoint() {
return trailingBogey().trailing();
}
public CarriageBogey leadingBogey() {
return bogeys.getFirst();
}
public CarriageBogey trailingBogey() {
return isOnTwoBogeys() ? bogeys.getSecond() : leadingBogey();
}
public boolean isOnTwoBogeys() {
return bogeys.getSecond() != null;
}
public static class CarriageBogey {
IBogeyBlock type;
Couple<MovingPoint> points;
Vec3 anchorPosition;
LerpedFloat wheelAngle;
LerpedFloat yaw;
LerpedFloat pitch;
public Vec3 leadingCouplingAnchor;
public Vec3 trailingCouplingAnchor;
public CarriageBogey(IBogeyBlock type, MovingPoint point, MovingPoint point2) {
this.type = type;
points = Couple.create(point, point2);
wheelAngle = LerpedFloat.angular();
yaw = LerpedFloat.angular();
pitch = LerpedFloat.angular();
updateAnchorPosition();
}
public void updateAngles(double distanceMoved) {
double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius());
Vec3 positionVec = leading().getPosition();
Vec3 coupledVec = trailing().getPosition();
double diffX = positionVec.x - coupledVec.x;
double diffY = positionVec.y - coupledVec.y;
double diffZ = positionVec.z - coupledVec.z;
float yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90;
float xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)));
wheelAngle.setValue((wheelAngle.getValue() - angleDiff) % 360);
pitch.setValue(xRot);
yaw.setValue(-yRot);
}
public MovingPoint leading() {
return points.getFirst();
}
public MovingPoint trailing() {
return points.getSecond();
}
public double getStress() {
return type.getWheelPointSpacing() - leading().getPosition()
.distanceTo(trailing().getPosition());
}
public void updateAnchorPosition() {
anchorPosition = points.getFirst()
.getPosition()
.add(points.getSecond()
.getPosition())
.scale(.5);
}
public void updateCouplingAnchor(Vec3 entityPos, float entityXRot, float entityYRot, int bogeySpacing,
float partialTicks, boolean leading) {
Vec3 thisOffset = type.getConnectorAnchorOffset();
thisOffset = thisOffset.multiply(1, 1, leading ? -1 : 1);
// msr.rotateY(viewYRot + 90)
// .rotateX(-viewXRot)
// .rotateY(180)
// .translate(0, 0, first ? 0 : -bogeySpacing)
// .rotateY(-180)
// .rotateX(viewXRot)
// .rotateY(-viewYRot - 90)
// .rotateY(bogey.yaw.getValue(partialTicks))
// .rotateX(bogey.pitch.getValue(partialTicks))
thisOffset = VecHelper.rotate(thisOffset, pitch.getValue(partialTicks), Axis.X);
thisOffset = VecHelper.rotate(thisOffset, yaw.getValue(partialTicks), Axis.Y);
thisOffset = VecHelper.rotate(thisOffset, -entityYRot - 90, Axis.Y);
thisOffset = VecHelper.rotate(thisOffset, entityXRot, Axis.X);
thisOffset = VecHelper.rotate(thisOffset, -180, Axis.Y);
thisOffset = thisOffset.add(0, 0, leading ? 0 : -bogeySpacing);
thisOffset = VecHelper.rotate(thisOffset, 180, Axis.Y);
thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X);
thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y);
if (leading)
leadingCouplingAnchor = entityPos.add(thisOffset);
else {
trailingCouplingAnchor = entityPos.add(thisOffset);
}
}
}
}

View file

@ -0,0 +1,128 @@
package com.simibubi.create.content.logistics.trains.entity;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionType;
import com.simibubi.create.content.contraptions.components.structureMovement.NonStationaryLighter;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
public class CarriageContraption extends Contraption {
private Direction assemblyDirection;
private boolean controls;
private int bogeys;
private BlockPos secondBogeyPos;
private Carriage carriage;
public int temporaryCarriageIdHolder = -1;
public CarriageContraption() {}
public CarriageContraption(Direction assemblyDirection) {
this.assemblyDirection = assemblyDirection;
this.bogeys = 0;
}
@Override
public boolean assemble(Level world, BlockPos pos) throws AssemblyException {
if (!searchMovedStructure(world, pos, null))
return false;
if (blocks.size() == 0)
return false;
if (bogeys > 2 || bogeys == 0)
throw AssemblyException.invalidBogeyCount();
return true;
}
@Override
protected boolean isAnchoringBlockAt(BlockPos pos) {
return false;
}
@Override
protected Pair<StructureBlockInfo, BlockEntity> capture(Level world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if (blockState.getBlock() instanceof IBogeyBlock) {
bogeys++;
if (bogeys == 2)
secondBogeyPos = pos;
return Pair.of(new StructureBlockInfo(pos, blockState, null), null);
}
if (AllBlocks.CONTROLS.has(blockState))
controls = true;
return super.capture(world, pos);
}
@Override
public CompoundTag writeNBT(boolean spawnPacket) {
CompoundTag tag = super.writeNBT(spawnPacket);
NBTHelper.writeEnum(tag, "AssemblyDirection", getAssemblyDirection());
if (spawnPacket)
tag.putInt("CarriageId", carriage.id);
tag.putBoolean("Controls", hasControls());
return tag;
}
@Override
public void readNBT(Level world, CompoundTag nbt, boolean spawnData) {
assemblyDirection = NBTHelper.readEnum(nbt, "AssemblyDirection", Direction.class);
if (spawnData)
temporaryCarriageIdHolder = nbt.getInt("CarriageId");
controls = nbt.getBoolean("Controls");
super.readNBT(world, nbt, spawnData);
}
@Override
public boolean canBeStabilized(Direction facing, BlockPos localPos) {
return false;
}
@Override
protected ContraptionType getType() {
return ContraptionType.CARRIAGE;
}
@Override
public ContraptionLighter<?> makeLighter() {
return new NonStationaryLighter<>(this);
}
public Direction getAssemblyDirection() {
return assemblyDirection;
}
public void setCarriage(Carriage carriage) {
this.carriage = carriage;
temporaryCarriageIdHolder = carriage.id;
}
public Carriage getCarriage() {
return carriage;
}
public boolean hasControls() {
return controls;
}
public BlockPos getSecondBogeyPos() {
return secondBogeyPos;
}
}

View file

@ -0,0 +1,61 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class CarriageContraptionEntity extends OrientedContraptionEntity {
public CarriageContraptionEntity(EntityType<?> type, Level world) {
super(type, world);
}
@Override
public boolean isControlledByLocalInstance() {
return true;
}
public static CarriageContraptionEntity create(Level world, CarriageContraption contraption) {
CarriageContraptionEntity entity =
new CarriageContraptionEntity(AllEntityTypes.CARRIAGE_CONTRAPTION.get(), world);
entity.setContraption(contraption);
entity.setInitialOrientation(contraption.getAssemblyDirection());
entity.startAtInitialYaw();
return entity;
}
@Override
protected void tickContraption() {
if (!(contraption instanceof CarriageContraption))
return;
int id = ((CarriageContraption) contraption).temporaryCarriageIdHolder;
Carriage carriage = Create.RAILWAYS.carriageById.get(id); // TODO: thread breach
if (carriage == null) {
discard();
return;
}
if (!level.isClientSide)
return;
xo = getX();
yo = getY();
zo = getZ();
carriage.moveEntity(this);
double distanceTo = position().distanceTo(new Vec3(xo, yo, zo));
carriage.bogeys.getFirst()
.updateAngles(distanceTo);
if (carriage.isOnTwoBogeys())
carriage.bogeys.getSecond()
.updateAngles(distanceTo);
}
@Override
public boolean shouldBeSaved() {
return false;
}
}

View file

@ -0,0 +1,72 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntityRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.world.phys.Vec3;
public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer<CarriageContraptionEntity> {
public CarriageContraptionEntityRenderer(EntityRendererProvider.Context context) {
super(context);
}
@Override
public boolean shouldRender(CarriageContraptionEntity entity, Frustum clippingHelper, double cameraX,
double cameraY, double cameraZ) {
if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ))
return false;
return ((CarriageContraption) entity.getContraption()).temporaryCarriageIdHolder != -1;
}
@Override
public void render(CarriageContraptionEntity entity, float yaw, float partialTicks, PoseStack ms,
MultiBufferSource buffers, int overlay) {
super.render(entity, yaw, partialTicks, ms, buffers, overlay);
int id = ((CarriageContraption) entity.getContraption()).temporaryCarriageIdHolder;
Carriage carriage = Create.RAILWAYS.carriageById.get(id);
if (carriage == null)
return;
Vec3 position = entity.getPosition(partialTicks);
ms.pushPose();
carriage.bogeys.forEachWithContext((bogey, first) -> {
if (!first && !carriage.isOnTwoBogeys())
return;
ms.pushPose();
MatrixTransformStack msr = new MatrixTransformStack(ms);
float viewYRot = entity.getViewYRot(partialTicks);
float viewXRot = entity.getViewXRot(partialTicks);
int bogeySpacing = carriage.bogeySpacing;
msr.rotateY(viewYRot + 90)
.rotateX(-viewXRot)
.rotateY(180)
.translate(0, 0, first ? 0 : -bogeySpacing)
.rotateY(-180)
.rotateX(viewXRot)
.rotateY(-viewYRot - 90)
.rotateY(bogey.yaw.getValue(partialTicks))
.rotateX(bogey.pitch.getValue(partialTicks))
.translate(0, .5f, 0);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers,
getPackedLightCoords(entity, partialTicks), overlay);
bogey.updateCouplingAnchor(position, viewXRot, viewYRot, bogeySpacing, partialTicks, first);
if (!carriage.isOnTwoBogeys())
bogey.updateCouplingAnchor(position, viewXRot, viewYRot, bogeySpacing, partialTicks, !first);
ms.popPose();
});
ms.popPose();
}
}

View file

@ -0,0 +1,130 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.Collection;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Carriage.CarriageBogey;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class CarriageCouplingRenderer {
public static void renderAll(PoseStack ms, MultiBufferSource buffer) {
Collection<Train> trains = Create.RAILWAYS.trains.values(); // TODO: thread breach
VertexConsumer vb = buffer.getBuffer(RenderType.solid());
BlockState air = Blocks.AIR.defaultBlockState();
float partialTicks = AnimationTickHolder.getPartialTicks();
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Vec3 camera = cameraEntity.getPosition(partialTicks);
for (Train train : trains) {
List<Carriage> carriages = train.carriages;
for (int i = 0; i < carriages.size() - 1; i++) {
Carriage carriage = carriages.get(i);
CarriageContraptionEntity entity = carriage.entity.get();
Carriage carriage2 = carriages.get(i + 1);
CarriageContraptionEntity entity2 = carriage.entity.get();
if (entity == null || entity2 == null)
continue;
CarriageBogey bogey1 = carriage.trailingBogey();
CarriageBogey bogey2 = carriage2.leadingBogey();
Vec3 anchor = bogey1.trailingCouplingAnchor;
Vec3 anchor2 = bogey2.leadingCouplingAnchor;
if (anchor == null || anchor2 == null)
continue;
if (!anchor.closerThan(camera, 64))
continue;
int lightCoords = getPackedLightCoords(entity, partialTicks);
int lightCoords2 = getPackedLightCoords(entity2, partialTicks);
double diffX = anchor2.x - anchor.x;
double diffY = anchor2.y - anchor.y;
double diffZ = anchor2.z - anchor.z;
float yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90;
float xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)));
Vec3 position = entity.getPosition(partialTicks);
Vec3 position2 = entity2.getPosition(partialTicks);
ms.pushPose();
ms.pushPose();
ms.translate(anchor.x, anchor.y, anchor.z);
CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_HEAD, air)
.rotateY(-yRot)
.rotateX(xRot)
.light(lightCoords)
.renderInto(ms, vb);
float margin = 3 / 16f;
double couplingDistance = train.carriageSpacing.get(i) - 2 * margin
- bogey1.type.getConnectorAnchorOffset().z - bogey2.type.getConnectorAnchorOffset().z;
int couplingSegments = (int) Math.round(couplingDistance * 4);
double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments;
for (int j = 0; j < couplingSegments; j++) {
CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_CABLE, air)
.rotateY(-yRot + 180)
.rotateX(-xRot)
.translate(0, 0, margin + 2 / 16f)
.scale(1, 1, (float) stretch)
.translate(0, 0, j / 4f)
.light(lightCoords)
.renderInto(ms, vb);
}
ms.popPose();
ms.pushPose();
ms.translate(-position.x, -position.y, -position.z);
ms.translate(position2.x, position2.y, position2.z);
ms.translate(anchor2.x, anchor2.y, anchor2.z);
CachedBufferer.partial(AllBlockPartials.TRAIN_COUPLING_HEAD, air)
.rotateY(-yRot + 180)
.rotateX(-xRot)
.light(lightCoords2)
.renderInto(ms, vb);
ms.popPose();
ms.popPose();
}
}
}
public static int getPackedLightCoords(Entity pEntity, float pPartialTicks) {
BlockPos blockpos = new BlockPos(pEntity.getLightProbePosition(pPartialTicks));
return LightTexture.pack(getBlockLightLevel(pEntity, blockpos), getSkyLightLevel(pEntity, blockpos));
}
protected static int getSkyLightLevel(Entity pEntity, BlockPos pPos) {
return pEntity.level.getBrightness(LightLayer.SKY, pPos);
}
protected static int getBlockLightLevel(Entity pEntity, BlockPos pPos) {
return pEntity.isOnFire() ? 15 : pEntity.level.getBrightness(LightLayer.BLOCK, pPos);
}
}

View file

@ -0,0 +1,45 @@
package com.simibubi.create.content.logistics.trains.entity;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraftforge.event.entity.EntityEvent;
/**
* Removes all Carriage entities in chunks that aren't ticking
*/
public class CarriageEntityHandler {
public static void onEntityEnterSection(EntityEvent.EnteringSection event) {
if (!event.didChunkChange())
return;
Entity entity = event.getEntity();
if (!(entity instanceof CarriageContraptionEntity))
return;
SectionPos newPos = event.getNewPos();
Level level = entity.getLevel();
if (level.isClientSide)
return;
if (!isActiveChunk(level, newPos.chunk()))
entity.discard();
}
public static void validateCarriageEntity(CarriageContraptionEntity entity) {
if (!entity.isAlive())
return;
Level level = entity.getLevel();
if (level.isClientSide)
return;
if (!isActiveChunk(level, entity.chunkPosition()))
entity.discard();
}
public static boolean isActiveChunk(Level level, ChunkPos chunk) {
if (level instanceof ServerLevel serverLevel)
return serverLevel.isPositionEntityTicking(chunk);
return false;
}
}

View file

@ -0,0 +1,194 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.function.Function;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import net.minecraft.world.phys.Vec3;
public class MovingPoint {
TrackGraph graph;
TrackNode node1, node2;
TrackEdge edge;
double position;
public static enum SteerDirection {
NONE(0), LEFT(-1), RIGHT(1);
float targetDot;
private SteerDirection(float targetDot) {
this.targetDot = targetDot;
}
}
public static interface ITrackSelector
extends Function<List<Entry<TrackNode, TrackEdge>>, Entry<TrackNode, TrackEdge>> {
};
public MovingPoint(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge, double position) {
this.graph = graph;
this.node1 = node1;
this.node2 = node2;
this.edge = edge;
this.position = position;
}
public ITrackSelector random() {
return validTargets -> validTargets.get(Create.RANDOM.nextInt(validTargets.size()));
}
public ITrackSelector follow(MovingPoint other) {
return validTargets -> {
TrackNode target = other.node1;
for (Entry<TrackNode, TrackEdge> entry : validTargets)
if (entry.getKey() == target || entry.getKey() == other.node2)
return entry;
Vector<List<Entry<TrackNode, TrackEdge>>> frontiers = new Vector<>(validTargets.size());
Vector<Set<TrackEdge>> visiteds = new Vector<>(validTargets.size());
for (int j = 0; j < validTargets.size(); j++) {
ArrayList<Entry<TrackNode, TrackEdge>> e = new ArrayList<>();
Entry<TrackNode, TrackEdge> entry = validTargets.get(j);
e.add(entry);
frontiers.add(e);
HashSet<TrackEdge> e2 = new HashSet<>();
e2.add(entry.getValue());
visiteds.add(e2);
}
for (int i = 0; i < 20; i++) {
for (int j = 0; j < validTargets.size(); j++) {
Entry<TrackNode, TrackEdge> entry = validTargets.get(j);
List<Entry<TrackNode, TrackEdge>> frontier = frontiers.get(j);
if (frontier.isEmpty())
continue;
Entry<TrackNode, TrackEdge> currentEntry = frontier.remove(0);
for (Entry<TrackNode, TrackEdge> nextEntry : graph.getConnectionsFrom(currentEntry.getKey())
.entrySet()) {
TrackEdge nextEdge = nextEntry.getValue();
if (!visiteds.get(j)
.add(nextEdge))
continue;
TrackNode nextNode = nextEntry.getKey();
if (nextNode == target)
return entry;
frontier.add(nextEntry);
}
}
}
Create.LOGGER.warn("Couldn't find follow target, choosing first");
return validTargets.get(0);
};
}
public ITrackSelector steer(SteerDirection direction, Vec3 upNormal) {
return validTargets -> {
double closest = Double.MAX_VALUE;
Entry<TrackNode, TrackEdge> best = null;
for (Entry<TrackNode, TrackEdge> entry : validTargets) {
Vec3 trajectory = edge.getDirection(node1, node2, false);
Vec3 entryTrajectory = entry.getValue()
.getDirection(node2, entry.getKey(), true);
Vec3 normal = trajectory.cross(upNormal);
double dot = normal.dot(entryTrajectory);
double diff = Math.abs(direction.targetDot - dot);
if (diff > closest)
continue;
closest = diff;
best = entry;
}
if (best == null) {
Create.LOGGER.warn("Couldn't find steer target, choosing first");
return validTargets.get(0);
}
return best;
};
}
public double travel(double distance, ITrackSelector trackSelector) {
double edgeLength = edge.getLength(node1, node2);
if (distance == 0)
return 0;
double traveled = distance;
double currentT = position / edgeLength;
double incrementT = edge.incrementT(node1, node2, currentT, distance);
position = incrementT * edgeLength;
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
while (position > edgeLength) {
validTargets.clear();
for (Entry<TrackNode, TrackEdge> entry : graph.getConnectionsFrom(node2)
.entrySet()) {
TrackNode newNode = entry.getKey();
if (newNode == node1)
continue;
TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
if (currentDirection.dot(newDirection) < 0)
continue;
validTargets.add(entry);
}
if (validTargets.isEmpty()) {
traveled -= position - edgeLength;
position = edgeLength;
break;
}
Entry<TrackNode, TrackEdge> entry =
validTargets.size() == 1 ? validTargets.get(0) : trackSelector.apply(validTargets);
node1 = node2;
node2 = entry.getKey();
edge = entry.getValue();
position -= edgeLength;
edgeLength = edge.getLength(node1, node2);
}
return traveled;
}
public void reverse() {
TrackNode n = node1;
node1 = node2;
node2 = n;
position = edge.getLength(node1, node2) - position;
edge = graph.getConnectionsFrom(node1)
.get(node2);
}
public Vec3 getPosition() {
double t = position / edge.getLength(node1, node2);
return edge.getPosition(node1, node2, t)
.add(edge.getNormal(node1, node2, t)
.scale(1));
}
}

View file

@ -0,0 +1,197 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Set;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.content.logistics.trains.entity.MovingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.management.GlobalStation;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class Navigation {
TrackGraph graph;
Train train;
public GlobalStation destination;
public double distanceToDestination;
List<TrackEdge> path;
public Navigation(Train train, TrackGraph graph) {
this.train = train;
this.graph = graph;
path = new ArrayList<>();
}
public void tick(Level level) {
if (destination == null)
return;
destination.reserveFor(train);
if (distanceToDestination < 1 / 32f) {
distanceToDestination = 0;
train.speed = 0;
path.clear();
train.arriveAt(destination);
destination = null;
return;
}
if (distanceToDestination - train.speed < 1 / 32f) {
train.speed = distanceToDestination;
return;
}
if (distanceToDestination < 10) {
double target = Train.topSpeed * ((distanceToDestination) / 10);
if (target < train.speed) {
train.speed += (target - train.speed) * .5f;
return;
}
}
double brakingDistance = (train.speed * train.speed) / (2 * Train.acceleration);
train.targetSpeed = distanceToDestination > brakingDistance ? Train.topSpeed : 0;
if (Mth.equal(train.targetSpeed, train.speed))
return;
if (train.speed < train.targetSpeed)
train.speed = Math.min(train.speed + Train.acceleration, train.targetSpeed);
else if (train.speed > train.targetSpeed)
train.speed = Math.max(train.speed - Train.acceleration, train.targetSpeed);
}
public boolean isActive() {
return destination != null;
}
public ITrackSelector control(MovingPoint mp) {
return list -> {
if (!path.isEmpty()) {
TrackEdge target = path.get(0);
for (Entry<TrackNode, TrackEdge> entry : list) {
if (entry.getValue() == target) {
path.remove(0);
return entry;
}
}
}
return list.get(0);
};
}
public void cancelNavigation() {
distanceToDestination = 0;
path.clear();
if (destination == null)
return;
destination.cancelReservation(train);
}
public void setDestination(GlobalStation destination) {
findPathTo(destination);
if (path.isEmpty())
return;
train.leave();
this.destination = destination;
}
private void findPathTo(GlobalStation destination) {
path.clear();
this.distanceToDestination = 0;
Couple<TrackNodeLocation> target = destination.edgeLocation;
PriorityQueue<Pair<Double, Pair<Couple<TrackNode>, TrackEdge>>> frontier =
new PriorityQueue<>((p1, p2) -> Double.compare(p1.getFirst(), p2.getFirst()));
MovingPoint leadingPoint = train.carriages.get(0)
.getLeadingPoint();
Set<TrackEdge> visited = new HashSet<>();
Map<TrackEdge, Pair<Boolean, TrackEdge>> reachedVia = new IdentityHashMap<>();
TrackEdge initialEdge = leadingPoint.edge;
TrackNode initialNode1 = leadingPoint.node1;
TrackNode initialNode2 = leadingPoint.node2;
double distanceToNode2 = initialEdge.getLength(initialNode1, initialNode2) - leadingPoint.position;
frontier.add(Pair.of(distanceToNode2, Pair.of(Couple.create(initialNode1, initialNode2), initialEdge)));
while (!frontier.isEmpty()) {
Pair<Double, Pair<Couple<TrackNode>, TrackEdge>> poll = frontier.poll();
double distance = poll.getFirst();
Pair<Couple<TrackNode>, TrackEdge> currentEntry = poll.getSecond();
List<Entry<TrackNode, TrackEdge>> validTargets = new ArrayList<>();
TrackEdge edge = currentEntry.getSecond();
TrackNode node1 = currentEntry.getFirst()
.getFirst();
TrackNode node2 = currentEntry.getFirst()
.getSecond();
TrackNodeLocation loc1 = node1.getLocation();
TrackNodeLocation loc2 = node2.getLocation();
boolean enteringBackward = loc2.equals(target.getFirst()) && loc1.equals(target.getSecond());
boolean enteringForward = loc1.equals(target.getFirst()) && loc2.equals(target.getSecond());
if (enteringForward || train.doubleEnded && enteringBackward) {
Pair<Boolean, TrackEdge> backTrack = reachedVia.get(edge);
TrackEdge toReach = edge;
while (backTrack != null && toReach != initialEdge) {
if (backTrack.getFirst())
path.add(0, toReach);
toReach = backTrack.getSecond();
backTrack = reachedVia.get(backTrack.getSecond());
}
distanceToDestination = distance;
double position = destination.position;
if (enteringForward)
position = edge.getLength(node1, node2) - position;
else
distanceToDestination += train.getTotalLength() + 2;
distanceToDestination -= position;
return;
}
for (Entry<TrackNode, TrackEdge> entry : graph.getConnectionsFrom(node2)
.entrySet()) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
Vec3 currentDirection = edge.getDirection(node1, node2, false);
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
if (currentDirection.dot(newDirection) < 0)
continue;
if (!visited.add(entry.getValue()))
continue;
validTargets.add(entry);
}
if (validTargets.isEmpty())
continue;
for (Entry<TrackNode, TrackEdge> entry : validTargets) {
TrackNode newNode = entry.getKey();
TrackEdge newEdge = entry.getValue();
reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge));
frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance,
Pair.of(Couple.create(node2, newNode), newEdge)));
}
}
}
}

View file

@ -0,0 +1,167 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.entity.MovingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.management.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.ScheduleRuntime;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class Train {
public static final double acceleration = 0.005f;
public static final double topSpeed = 1.2f;
public double speed = 0;
public double targetSpeed = 0;
public UUID id;
public TrackGraph graph;
public Navigation navigation;
public GlobalStation currentStation;
public ScheduleRuntime runtime;
public TrainIconType icon;
public Component name;
public boolean doubleEnded;
public List<Carriage> carriages;
public List<Integer> carriageSpacing;
double[] stress;
public Train(UUID id, TrackGraph graph, List<Carriage> carriages, List<Integer> carriageSpacing) {
this.id = id;
this.graph = graph;
this.carriages = carriages;
this.carriageSpacing = carriageSpacing;
this.icon = TrainIconType.getDefault();
this.stress = new double[carriageSpacing.size()];
this.name = Lang.translate("train.unnamed");
carriages.forEach(c -> {
c.setTrain(this);
Create.RAILWAYS.carriageById.put(c.id, c);
});
doubleEnded = carriages.size() > 1 && carriages.get(carriages.size() - 1).contraption.hasControls();
navigation = new Navigation(this, graph);
runtime = new ScheduleRuntime(this);
}
public void tick(Level level) {
runtime.tick(level);
navigation.tick(level);
double distance = speed;
Carriage previousCarriage = null;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(i);
if (previousCarriage != null) {
int target = carriageSpacing.get(i - 1);
Vec3 leadingAnchor = carriage.leadingBogey().anchorPosition;
Vec3 trailingAnchor = previousCarriage.trailingBogey().anchorPosition;
double actual = leadingAnchor.distanceTo(trailingAnchor);
stress[i - 1] = target - actual;
}
previousCarriage = carriage;
}
// positive stress: carriages should move apart
// negative stress: carriages should move closer
boolean approachingStation = navigation.distanceToDestination < 5;
double leadingModifier = approachingStation ? -0.75d : -0.5d;
double trailingModifier = approachingStation ? 0d : 0.125d;
MovingPoint previous = null;
for (int i = 0; i < carriages.size(); i++) {
double leadingStress = i == 0 ? 0 : stress[i - 1] * leadingModifier;
double trailingStress = i == stress.length ? 0 : stress[i] * trailingModifier;
Carriage carriage = carriages.get(i);
MovingPoint toFollow = previous;
Function<MovingPoint, ITrackSelector> control =
previous == null ? navigation::control : mp -> mp.follow(toFollow);
double actualDistance = carriage.travel(level, distance + leadingStress + trailingStress, control);
if (i == 0)
distance = actualDistance;
previous = carriage.getTrailingPoint();
}
if (navigation.destination != null) {
navigation.distanceToDestination -= distance;
}
}
public boolean disassemble(Direction assemblyDirection, BlockPos pos) {
for (Carriage carriage : carriages) {
CarriageContraptionEntity entity = carriage.entity.get();
if (entity == null)
return false;
if (!Mth.equal(entity.pitch, 0))
return false;
if (!Mth.equal(((entity.yaw % 90) + 360) % 90, 0))
return false;
}
int offset = 1;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(i);
CarriageContraptionEntity entity = carriage.entity.get();
if (entity == null)
return false;
entity.setPos(Vec3.atLowerCornerOf(pos.relative(assemblyDirection, offset)));
entity.disassemble();
Create.RAILWAYS.carriageById.remove(carriage.id);
CreateClient.RAILWAYS.carriageById.remove(carriage.id);
offset += carriage.bogeySpacing;
if (i < carriageSpacing.size())
offset += carriageSpacing.get(i);
}
if (currentStation != null)
currentStation.cancelReservation(this);
Create.RAILWAYS.trains.remove(id);
CreateClient.RAILWAYS.trains.remove(id);
return true;
}
public int getTotalLength() {
int length = 0;
for (int i = 0; i < carriages.size(); i++) {
length += carriages.get(i).bogeySpacing;
if (i < carriageSpacing.size())
length += carriageSpacing.get(i);
}
return length;
}
public void leave() {
currentStation.trainDeparted(this);
currentStation = null;
}
public void arriveAt(GlobalStation station) {
currentStation = station;
runtime.destinationReached();
}
}

View file

@ -0,0 +1,89 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.HashMap;
import java.util.Map;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.Create;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class TrainIconType {
public static Map<ResourceLocation, TrainIconType> REGISTRY = new HashMap<>();
public static void register(ResourceLocation id, ResourceLocation sheet, int x, int y) {
REGISTRY.put(id, new TrainIconType(id, sheet, x, y));
}
static {
ResourceLocation sheet = Create.asResource("textures/gui/assemble.png");
register(Create.asResource("traditional"), sheet, 2, 205);
register(Create.asResource("electric"), sheet, 2, 216);
register(Create.asResource("modern"), sheet, 2, 227);
}
ResourceLocation sheet;
ResourceLocation id;
int x, y;
public TrainIconType(ResourceLocation id, ResourceLocation sheet, int x, int y) {
this.id = id;
this.sheet = sheet;
this.x = x;
this.y = y;
}
public static TrainIconType byId(ResourceLocation id) {
return REGISTRY.getOrDefault(id, getDefault());
}
public static TrainIconType getDefault() {
return REGISTRY.get(Create.asResource("traditional"));
}
public ResourceLocation getId() {
return id;
}
public static final int ENGINE = -1;
public static final int FLIPPED_ENGINE = -2;
@OnlyIn(Dist.CLIENT)
public int render(int lengthOrEngine, PoseStack ms, int x, int y) {
int offset = getIconOffset(lengthOrEngine);
int width = getIconWidth(lengthOrEngine);
RenderSystem.setShaderTexture(0, sheet);
GuiComponent.blit(ms, x, y, 0, this.x + offset, this.y, width, 10, 256, 256);
return width;
}
public int getIconWidth(int lengthOrEngine) {
if (lengthOrEngine == FLIPPED_ENGINE)
return 19;
if (lengthOrEngine == ENGINE)
return 19;
if (lengthOrEngine < 3)
return 7;
if (lengthOrEngine < 9)
return 13;
return 19;
}
public int getIconOffset(int lengthOrEngine) {
if (lengthOrEngine == FLIPPED_ENGINE)
return 0;
if (lengthOrEngine == ENGINE)
return 62;
if (lengthOrEngine < 3)
return 34;
if (lengthOrEngine < 9)
return 20;
return 42;
}
}

View file

@ -0,0 +1,91 @@
package com.simibubi.create.content.logistics.trains.management;
import java.lang.ref.WeakReference;
import java.util.List;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
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.element.GuiGameElement;
import com.simibubi.create.foundation.gui.widget.IconButton;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.TextComponent;
public abstract class AbstractStationScreen extends AbstractSimiScreen {
protected AllGuiTextures background;
protected StationTileEntity te;
protected GlobalStation station;
protected WeakReference<Train> displayedTrain;
private IconButton confirmButton;
public AbstractStationScreen(StationTileEntity te, GlobalStation station) {
super(new TextComponent("Station"));
this.te = te;
this.station = station;
displayedTrain = new WeakReference<>(null);
}
@Override
protected void init() {
setWindowSize(background.width, background.height);
setWindowOffset(-20, 0);
super.init();
clearWidgets();
int x = guiLeft;
int y = guiTop;
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(this::onClose);
addRenderableWidget(confirmButton);
}
public int getTrainIconWidth(Train train) {
TrainIconType icon = train.icon;
List<Carriage> carriages = train.carriages;
int w = icon.getIconWidth(TrainIconType.ENGINE);
if (carriages.size() == 1)
return w;
for (int i = 1; i < carriages.size(); i++) {
if (i == carriages.size() - 1 && train.doubleEnded) {
w += icon.getIconWidth(TrainIconType.FLIPPED_ENGINE) + 1;
break;
}
Carriage carriage = carriages.get(i);
w += icon.getIconWidth(carriage.bogeySpacing) + 1;
}
return w;
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
int y = guiTop;
background.render(ms, x, y, this);
ms.pushPose();
TransformStack.cast(ms)
.pushPose()
.translate(x + 200, y + background.height + 20, 100)
.scale(40)
.rotateX(-22)
.rotateY(63);
GuiGameElement.of(te.getBlockState()
.setValue(StationBlock.FACING, Direction.EAST))
.render(ms);
ms.popPose();
}
}

View file

@ -0,0 +1,220 @@
package com.simibubi.create.content.logistics.trains.management;
import java.lang.ref.WeakReference;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
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.widget.IconButton;
import com.simibubi.create.foundation.gui.widget.ScrollInput;
import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.client.gui.components.Widget;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
public class AssemblyScreen extends AbstractStationScreen {
private IconButton quitAssembly;
private IconButton toggleAssemblyButton;
private IconButton completeAssembly;
private List<ResourceLocation> iconTypes;
private ScrollInput iconTypeScroll;
public AssemblyScreen(StationTileEntity te, GlobalStation station) {
super(te, station);
background = AllGuiTextures.STATION_ASSEMBLING;
}
@Override
protected void init() {
super.init();
int x = guiLeft;
int y = guiTop;
int by = y + background.height - 24;
Widget widget = renderables.get(0);
if (widget instanceof IconButton ib) {
ib.setIcon(AllIcons.I_PRIORITY_VERY_LOW);
ib.setToolTip(new TextComponent("Close Window"));
}
iconTypes = TrainIconType.REGISTRY.keySet()
.stream()
.toList();
iconTypeScroll = new ScrollInput(x + 4, y + 17, 184, 14).titled(new TextComponent("Icon Type"));
iconTypeScroll.withRange(0, iconTypes.size());
iconTypeScroll.withStepFunction(ctx -> -iconTypeScroll.standardStep()
.apply(ctx));
iconTypeScroll.calling(s -> {
Train train = displayedTrain.get();
if (train != null)
train.icon = TrainIconType.byId(iconTypes.get(s));
});
iconTypeScroll.active = iconTypeScroll.visible = false;
addRenderableWidget(iconTypeScroll);
toggleAssemblyButton = new WideIconButton(x + 83, by, AllGuiTextures.I_ASSEMBLE_TRAIN);
toggleAssemblyButton.active = false;
toggleAssemblyButton.setToolTip(new TextComponent("Assemble Train"));
toggleAssemblyButton.withCallback(() -> {
AllPackets.channel.sendToServer(StationEditPacket.tryAssemble(te.getBlockPos()));
});
quitAssembly = new IconButton(x + 62, by, AllIcons.I_DISABLE);
quitAssembly.active = true;
quitAssembly.setToolTip(new TextComponent("Cancel Assembly"));
quitAssembly.withCallback(() -> {
AllPackets.channel.sendToServer(StationEditPacket.configure(te.getBlockPos(), false, station.name));
minecraft.setScreen(new StationScreen(te, station));
});
completeAssembly = new IconButton(x + 112, by, AllIcons.I_CONFIRM);
completeAssembly.active = false;
completeAssembly.setToolTip(new TextComponent("Complete Assembly"));
completeAssembly.withCallback(() -> {
AllPackets.channel.sendToServer(StationEditPacket.configure(te.getBlockPos(), false, station.name));
minecraft.setScreen(new StationScreen(te, station));
});
addRenderableWidget(toggleAssemblyButton);
addRenderableWidget(quitAssembly);
addRenderableWidget(completeAssembly);
tickTrainDisplay();
}
@Override
public void tick() {
super.tick();
tickTrainDisplay();
Train train = displayedTrain.get();
toggleAssemblyButton.active = te.bogeyCount > 0 || train != null;
}
private void tickTrainDisplay() {
Train train = displayedTrain.get();
Train imminentTrain = station.getPresentTrain();
if (train == null) {
if (imminentTrain != null) {
displayedTrain = new WeakReference<>(imminentTrain);
completeAssembly.active = true;
quitAssembly.active = false;
iconTypeScroll.active = iconTypeScroll.visible = true;
toggleAssemblyButton.setToolTip(new TextComponent("Disassemble Train"));
toggleAssemblyButton.setIcon(AllGuiTextures.I_DISASSEMBLE_TRAIN);
toggleAssemblyButton.withCallback(() -> {
AllPackets.channel.sendToServer(StationEditPacket.tryDisassemble(te.getBlockPos()));
});
}
} else {
if (imminentTrain == null) {
displayedTrain = new WeakReference<>(null);
completeAssembly.active = false;
quitAssembly.active = true;
iconTypeScroll.active = iconTypeScroll.visible = false;
toggleAssemblyButton.setToolTip(new TextComponent("Assemble Train"));
toggleAssemblyButton.setIcon(AllGuiTextures.I_ASSEMBLE_TRAIN);
toggleAssemblyButton.withCallback(() -> {
AllPackets.channel.sendToServer(StationEditPacket.tryAssemble(te.getBlockPos()));
});
}
}
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
super.renderWindow(ms, mouseX, mouseY, partialTicks);
int x = guiLeft;
int y = guiTop;
TextComponent header = new TextComponent("Train Assembly");
font.draw(ms, header, x + background.width / 2 - font.width(header) / 2, y + 4, 0x0E2233);
Train train = displayedTrain.get();
if (train != null) {
TrainIconType icon = train.icon;
int offset = 0;
int position = background.width / 2 - getTrainIconWidth(train) / 2;
List<Carriage> carriages = train.carriages;
for (int i = carriages.size() - 1; i > 0; i--) {
if (i == carriages.size() - 1 && train.doubleEnded) {
offset += icon.render(TrainIconType.FLIPPED_ENGINE, ms, x + offset + position, y + 20) + 1;
continue;
}
Carriage carriage = carriages.get(i);
offset += icon.render(carriage.bogeySpacing, ms, x + offset + position, y + 20) + 1;
}
offset += icon.render(TrainIconType.ENGINE, ms, x + offset + position, y + 20);
UIRenderHelper.drawStretched(ms, x + 21, y + 43, 150, 96, -100, AllGuiTextures.STATION_TEXTBOX_MIDDLE);
AllGuiTextures.STATION_TEXTBOX_TOP.render(ms, x + 21, y + 42);
AllGuiTextures.STATION_TEXTBOX_BOTTOM.render(ms, x + 21, y + 136);
AllGuiTextures.STATION_TEXTBOX_SPEECH.render(ms, x + offset + position - 12, y + 38);
TextComponent text = new TextComponent("Assembly Successful");
font.drawShadow(ms, text, x + 97 - font.width(text) / 2, y + 47, 0xC6C6C6);
font.drawShadow(ms,
new TextComponent("-> " + train.carriages.size() + " Carriages, " + train.getTotalLength() + "m"),
x + 30, y + 67, 0xC6C6C6);
font.drawShadow(ms, new TextComponent("-> Fuel Type: NYI"), x + 30, y + 77, 0xC6C6C6);
font.drawShadow(ms, new TextComponent("-> " + (train.doubleEnded ? "Dual Powered" : "Single Powered")),
x + 30, y + 92, 0xC6C6C6);
font.drawShadow(ms,
new TextComponent((train.doubleEnded ? "(Navigates both ways)" : "(Navigates forward only)")), x + 30,
y + 102, 0xACC4BC);
return;
}
AssemblyException lastAssemblyException = te.getLastAssemblyException();
if (lastAssemblyException != null) {
TextComponent text = new TextComponent("Assembly Failed");
font.draw(ms, text, x + 97 - font.width(text) / 2, y + 47, 0x775B5B);
int offset = 0;
if (te.failedCarriageIndex != -1) {
font.draw(ms, new TextComponent("Carriage " + te.failedCarriageIndex + ":"), x + 30, y + 67, 0x7A7A7A);
offset += 10;
}
font.drawWordWrap(lastAssemblyException.component, x + 30, y + 67 + offset, 134, 0x775B5B);
offset += font.split(lastAssemblyException.component, 134)
.size() * 9 + 5;
font.drawWordWrap(new TextComponent("Resolve this and retry"), x + 30, y + 67 + offset, 134, 0x7A7A7A);
return;
}
int bogeyCount = te.bogeyCount;
TextComponent text =
new TextComponent(bogeyCount == 0 ? "No Bogeys" : bogeyCount + (bogeyCount == 1 ? " Bogey" : " Bogeys"));
font.draw(ms, text, x + 97 - font.width(text) / 2, y + 47, 0x7A7A7A);
Component component =
new TextComponent("Right-click on highlighted Tracks to create bogeys. Use Wrench to cycle type."
+ "\n\nAttach structures to one or between two bogeys to form carriages.");
font.drawWordWrap(component, x + 30, y + 67, 134, 0x7A7A7A);
}
@Override
public void removed() {
super.removed();
Train train = displayedTrain.get();
if (train != null) {
ResourceLocation iconId = iconTypes.get(iconTypeScroll.getState());
train.icon = TrainIconType.byId(iconId);
AllPackets.channel.sendToServer(new TrainEditPacket(train.id, "", iconId));
}
}
}

View file

@ -0,0 +1,95 @@
package com.simibubi.create.content.logistics.trains.management;
import java.lang.ref.WeakReference;
import java.util.UUID;
import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.utility.Couple;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
public class GlobalStation {
public UUID id;
public Couple<TrackNodeLocation> edgeLocation;
public double position;
public String name;
public BlockPos stationPos;
public WeakReference<Train> nearestTrain;
public GlobalStation(UUID id, Couple<TrackNode> nodes, double position, BlockPos stationPos) {
this.id = id;
this.stationPos = stationPos;
this.position = position;
name = "Track Station";
edgeLocation = nodes.map(TrackNode::getLocation);
nearestTrain = new WeakReference<Train>(null);
}
public GlobalStation(CompoundTag nbt) {
id = nbt.getUUID("Id");
name = nbt.getString("Name");
position = nbt.getDouble("Position");
stationPos = NbtUtils.readBlockPos(nbt.getCompound("StationPos"));
nearestTrain = new WeakReference<Train>(null);
edgeLocation = Couple.deserializeEach(nbt.getList("Edge", Tag.TAG_COMPOUND),
tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag)));
}
public void reserveFor(Train train) {
Train nearestTrain = this.nearestTrain.get();
if (nearestTrain == null
|| nearestTrain.navigation.distanceToDestination > train.navigation.distanceToDestination)
this.nearestTrain = new WeakReference<>(train);
}
public void cancelReservation(Train train) {
if (nearestTrain.get() == train)
nearestTrain = new WeakReference<>(null);
}
public void trainDeparted(Train train) {
cancelReservation(train);
}
@Nullable
public Train getPresentTrain() {
Train nearestTrain = this.nearestTrain.get();
if (nearestTrain == null || nearestTrain.currentStation != this)
return null;
return nearestTrain;
}
@Nullable
public Train getImminentTrain() {
Train nearestTrain = this.nearestTrain.get();
if (nearestTrain == null)
return nearestTrain;
if (nearestTrain.currentStation == this)
return nearestTrain;
if (!nearestTrain.navigation.isActive())
return null;
if (nearestTrain.navigation.distanceToDestination > 30)
return null;
return nearestTrain;
}
public CompoundTag write() {
CompoundTag nbt = new CompoundTag();
nbt.putUUID("Id", id);
nbt.putString("Name", name);
nbt.put("StationPos", NbtUtils.writeBlockPos(stationPos));
nbt.putDouble("Position", position);
nbt.put("Edge", edgeLocation.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc))));
return nbt;
}
}

View file

@ -0,0 +1,136 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.font.FontSet;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FormattedCharSequence;
public class NoShadowFontWrapper extends Font {
private Font wrapped;
public NoShadowFontWrapper(Font wrapped) {
super(null);
this.wrapped = wrapped;
}
public FontSet getFontSet(ResourceLocation pFontLocation) {
return wrapped.getFontSet(pFontLocation);
}
public int drawShadow(PoseStack pPoseStack, String pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int drawShadow(PoseStack pPoseStack, String pText, float pX, float pY, int pColor, boolean pTransparent) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int draw(PoseStack pPoseStack, String pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int drawShadow(PoseStack pPoseStack, FormattedCharSequence pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int drawShadow(PoseStack pPoseStack, Component pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int draw(PoseStack pPoseStack, FormattedCharSequence pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public int draw(PoseStack pPoseStack, Component pText, float pX, float pY, int pColor) {
return wrapped.draw(pPoseStack, pText, pX, pY, pColor);
}
public String bidirectionalShaping(String pText) {
return wrapped.bidirectionalShaping(pText);
}
public int drawInBatch(String pText, float pX, float pY, int pColor, boolean pDropShadow, Matrix4f pMatrix,
MultiBufferSource pBuffer, boolean pTransparent, int pBackgroundColor, int pPackedLight) {
return wrapped.drawInBatch(pText, pX, pY, pColor, pDropShadow, pMatrix, pBuffer, pTransparent, pBackgroundColor,
pPackedLight);
}
public int drawInBatch(String pText, float pX, float pY, int pColor, boolean pDropShadow, Matrix4f pMatrix,
MultiBufferSource pBuffer, boolean pTransparent, int pBackgroundColor, int pPackedLight, boolean pBidiFlag) {
return wrapped.drawInBatch(pText, pX, pY, pColor, pDropShadow, pMatrix, pBuffer, pTransparent, pBackgroundColor,
pPackedLight, pBidiFlag);
}
public int drawInBatch(Component pText, float pX, float pY, int pColor, boolean pDropShadow, Matrix4f pMatrix,
MultiBufferSource pBuffer, boolean pTransparent, int pBackgroundColor, int pPackedLight) {
return wrapped.drawInBatch(pText, pX, pY, pColor, pDropShadow, pMatrix, pBuffer, pTransparent, pBackgroundColor,
pPackedLight);
}
public int drawInBatch(FormattedCharSequence pText, float pX, float pY, int pColor, boolean pDropShadow,
Matrix4f pMatrix, MultiBufferSource pBuffer, boolean pTransparent, int pBackgroundColor, int pPackedLight) {
return wrapped.drawInBatch(pText, pX, pY, pColor, pDropShadow, pMatrix, pBuffer, pTransparent, pBackgroundColor,
pPackedLight);
}
public void drawInBatch8xOutline(FormattedCharSequence pText, float pX, float pY, int pColor, int pBackgroundColor,
Matrix4f pMatrix, MultiBufferSource pBuffer, int pPackedLightCoords) {
wrapped.drawInBatch8xOutline(pText, pX, pY, pColor, pBackgroundColor, pMatrix, pBuffer, pPackedLightCoords);
}
public int width(String pText) {
return wrapped.width(pText);
}
public int width(FormattedText pText) {
return wrapped.width(pText);
}
public int width(FormattedCharSequence pText) {
return wrapped.width(pText);
}
public String plainSubstrByWidth(String p_92838_, int p_92839_, boolean p_92840_) {
return wrapped.plainSubstrByWidth(p_92838_, p_92839_, p_92840_);
}
public String plainSubstrByWidth(String pText, int pMaxWidth) {
return wrapped.plainSubstrByWidth(pText, pMaxWidth);
}
public FormattedText substrByWidth(FormattedText pText, int pMaxWidth) {
return wrapped.substrByWidth(pText, pMaxWidth);
}
public void drawWordWrap(FormattedText pText, int pX, int pY, int pMaxWidth, int pColor) {
wrapped.drawWordWrap(pText, pX, pY, pMaxWidth, pColor);
}
public int wordWrapHeight(String pStr, int pMaxWidth) {
return wrapped.wordWrapHeight(pStr, pMaxWidth);
}
public List<FormattedCharSequence> split(FormattedText pText, int pMaxWidth) {
return wrapped.split(pText, pMaxWidth);
}
public boolean isBidirectional() {
return wrapped.isBidirectional();
}
public StringSplitter getSplitter() {
return wrapped.getSplitter();
}
}

View file

@ -0,0 +1,10 @@
package com.simibubi.create.content.logistics.trains.management;
public class RunningScheduleScreen {
/**
* use the same rendering and mouse handling from schedule screen but add
* progress of train based on ScheduleRuntime
*/
}

View file

@ -0,0 +1,100 @@
package com.simibubi.create.content.logistics.trains.management;
import com.simibubi.create.foundation.gui.container.GhostItemContainer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.SlotItemHandler;
public class ScheduleContainer extends GhostItemContainer<ItemStack> {
public boolean slotsActive = true;
public boolean targetSlotActive = true;
public ScheduleContainer(MenuType<?> type, int id, Inventory inv, FriendlyByteBuf extraData) {
super(type, id, inv, extraData);
}
public ScheduleContainer(MenuType<?> type, int id, Inventory inv, ItemStack contentHolder) {
super(type, id, inv, contentHolder);
}
@Override
protected ItemStackHandler createGhostInventory() {
return new ItemStackHandler(1);
}
@Override
public void clicked(int slotId, int dragType, ClickType clickTypeIn, Player player) {
if (slotId != playerInventory.selected || clickTypeIn == ClickType.THROW)
super.clicked(slotId, dragType, clickTypeIn, player);
}
@Override
protected boolean allowRepeats() {
return true;
}
@Override
protected ItemStack createOnClient(FriendlyByteBuf extraData) {
return extraData.readItem();
}
@Override
protected void addSlots() {
addPlayerSlots(46, 140);
addSlot(new InactiveItemHandlerSlot(ghostInventory, 0, 54, 88));
}
@Override
protected void addPlayerSlots(int x, int y) {
for (int hotbarSlot = 0; hotbarSlot < 9; ++hotbarSlot)
this.addSlot(new InactiveSlot(playerInventory, hotbarSlot, x + hotbarSlot * 18, y + 58));
for (int row = 0; row < 3; ++row)
for (int col = 0; col < 9; ++col)
this.addSlot(new InactiveSlot(playerInventory, col + row * 9 + 9, x + col * 18, y + row * 18));
}
@Override
protected void saveData(ItemStack contentHolder) {}
@Override
public boolean stillValid(Player player) {
return playerInventory.getSelected() == contentHolder;
}
class InactiveSlot extends Slot {
public InactiveSlot(Container pContainer, int pIndex, int pX, int pY) {
super(pContainer, pIndex, pX, pY);
}
@Override
public boolean isActive() {
return slotsActive;
}
}
class InactiveItemHandlerSlot extends SlotItemHandler {
public InactiveItemHandlerSlot(IItemHandler itemHandler, int index, int xPosition, int yPosition) {
super(itemHandler, index, xPosition, yPosition);
}
@Override
public boolean isActive() {
return slotsActive && targetSlotActive;
}
}
}

View file

@ -0,0 +1,97 @@
package com.simibubi.create.content.logistics.trains.management;
import com.simibubi.create.AllContainerTypes;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraftforge.network.NetworkHooks;
public class ScheduleItem extends Item implements MenuProvider {
public ScheduleItem(Properties pProperties) {
super(pProperties);
}
@Override
public InteractionResult useOn(UseOnContext context) {
if (context.getPlayer() == null)
return InteractionResult.PASS;
return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
}
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
ItemStack heldItem = player.getItemInHand(hand);
if (!player.isShiftKeyDown() && hand == InteractionHand.MAIN_HAND) {
if (!world.isClientSide && player instanceof ServerPlayer)
NetworkHooks.openGui((ServerPlayer) player, this, buf -> {
buf.writeItem(heldItem);
});
return InteractionResultHolder.success(heldItem);
}
return InteractionResultHolder.pass(heldItem);
}
@Override
public InteractionResult interactLivingEntity(ItemStack pStack, Player pPlayer, LivingEntity pInteractionTarget,
InteractionHand pUsedHand) {
InteractionResult pass = InteractionResult.PASS;
if (!pStack.hasTag())
return pass;
if (!pStack.getTag()
.contains("Schedule"))
return pass;
Schedule schedule = Schedule.fromTag(pStack.getTagElement("Schedule"));
if (pInteractionTarget == null)
return pass;
Entity rootVehicle = pInteractionTarget.getRootVehicle();
if (!(rootVehicle instanceof CarriageContraptionEntity))
return pass;
if (pPlayer.level.isClientSide)
return InteractionResult.SUCCESS;
CarriageContraptionEntity entity = (CarriageContraptionEntity) rootVehicle;
Contraption contraption = entity.getContraption();
if (contraption instanceof CarriageContraption cc)
cc.getCarriage().train.runtime.setSchedule(schedule, false);
return InteractionResult.SUCCESS;
}
@Override
public AbstractContainerMenu createMenu(int id, Inventory inv, Player player) {
ItemStack heldItem = player.getMainHandItem();
return new ScheduleContainer(AllContainerTypes.SCHEDULE.get(), id, inv, heldItem);
}
@Override
public Component getDisplayName() {
return new TranslatableComponent(getDescriptionId());
}
@Override
public void fillItemCategory(CreativeModeTab pCategory, NonNullList<ItemStack> pItems) {}
}

View file

@ -0,0 +1,170 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.schedule.FilteredDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEntry;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleWaitCondition;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
public class ScheduleRuntime {
enum State {
PRE_TRANSIT, IN_TRANSIT, POST_TRANSIT
}
Train train;
Schedule schedule;
boolean paused;
boolean isAutoSchedule;
int currentEntry;
State state;
static final int INTERVAL = 40;
int cooldown;
List<Integer> conditionProgress;
List<CompoundTag> conditionContext;
public ScheduleRuntime(Train train) {
this.train = train;
reset();
}
public void destinationReached() {
if (state != State.IN_TRANSIT)
return;
state = State.POST_TRANSIT;
conditionProgress.clear();
if (currentEntry >= schedule.entries.size())
return;
List<List<ScheduleWaitCondition>> conditions = schedule.entries.get(currentEntry).conditions;
for (int i = 0; i < conditions.size(); i++) {
conditionProgress.add(0);
conditionContext.add(new CompoundTag());
}
}
public void tick(Level level) {
if (schedule == null)
return;
if (paused)
return;
if (train.navigation.destination != null)
return;
if (currentEntry >= schedule.entries.size()) {
currentEntry = 0;
if (!schedule.cyclic)
paused = true;
return;
}
if (cooldown-- > 0)
return;
if (state == State.IN_TRANSIT)
return;
if (state == State.POST_TRANSIT) {
tickConditions(level);
return;
}
GlobalStation nextStation = findNextStation();
if (nextStation == null) {
cooldown = INTERVAL;
return;
}
if (nextStation == train.currentStation) {
state = State.IN_TRANSIT;
destinationReached();
return;
}
train.navigation.setDestination(nextStation);
state = State.IN_TRANSIT;
}
public void tickConditions(Level level) {
List<List<ScheduleWaitCondition>> conditions = schedule.entries.get(currentEntry).conditions;
for (int i = 0; i < conditions.size(); i++) {
List<ScheduleWaitCondition> list = conditions.get(i);
int progress = conditionProgress.get(i);
if (progress >= list.size()) {
state = State.PRE_TRANSIT;
currentEntry++;
return;
}
CompoundTag tag = conditionContext.get(i);
ScheduleWaitCondition condition = list.get(progress);
if (condition.tickCompletion(level, train, tag)) {
conditionContext.set(i, new CompoundTag());
conditionProgress.set(i, progress + 1);
}
}
}
public GlobalStation findNextStation() {
ScheduleEntry entry = schedule.entries.get(currentEntry);
ScheduleDestination destination = entry.destination;
if (destination instanceof FilteredDestination filtered) {
for (GlobalStation globalStation : train.graph.getStations()) {
if (globalStation.name.equals(filtered.nameFilter))
return globalStation;
}
}
return null;
}
public void setSchedule(Schedule schedule, boolean auto) {
reset();
this.schedule = schedule;
currentEntry = 0;
paused = false;
isAutoSchedule = auto;
}
public Schedule getSchedule() {
return schedule;
}
public void discardSchedule() {
reset();
}
private void reset() {
paused = true;
isAutoSchedule = false;
currentEntry = 0;
schedule = null;
state = State.PRE_TRANSIT;
conditionProgress = new ArrayList<>();
conditionContext = new ArrayList<>();
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putInt("CurrentEntry", currentEntry);
tag.putBoolean("AutoSchedule", isAutoSchedule);
tag.putBoolean("Paused", paused);
if (schedule != null)
tag.put("Schedule", schedule.write());
return tag;
}
public void read(CompoundTag tag) {
reset();
paused = tag.getBoolean("Paused");
isAutoSchedule = tag.getBoolean("AutoSchedule");
currentEntry = tag.getInt("CurrentEntry");
if (tag.contains("Schedule"))
schedule = Schedule.fromTag(tag.getCompound("Schedule"));
}
}

View file

@ -0,0 +1,941 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import com.simibubi.create.content.logistics.trains.management.schedule.FilteredDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.IScheduleInput;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleDestination;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEditPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEntry;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleWaitCondition;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduledDelay;
import com.simibubi.create.content.logistics.trains.management.schedule.TimedWaitCondition.TimeUnit;
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.container.AbstractSimiContainerScreen;
import com.simibubi.create.foundation.gui.element.GuiGameElement;
import com.simibubi.create.foundation.gui.widget.AbstractSimiWidget;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.gui.widget.Indicator;
import com.simibubi.create.foundation.gui.widget.Indicator.State;
import com.simibubi.create.foundation.gui.widget.Label;
import com.simibubi.create.foundation.gui.widget.SelectionScrollInput;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Widget;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.client.gui.GuiUtils;
public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContainer> {
private static final int CARD_HEADER = 22;
private static final int CARD_WIDTH = 195;
private List<Rect2i> extraAreas = Collections.emptyList();
private List<LerpedFloat> horizontalScrolls = new ArrayList<>();
private LerpedFloat scroll = LerpedFloat.linear()
.startWithValue(0);
Schedule schedule;
private IconButton confirmButton;
private IconButton cyclicButton;
private Indicator cyclicIndicator;
private ScheduleDestination editingDestination;
private ScheduleWaitCondition editingCondition;
private SelectionScrollInput scrollInput;
private Label scrollInputLabel;
private IconButton editorConfirm, editorDelete;
private Consumer<Boolean> onEditorClose;
private List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets;
private List<Integer> editorDividers;
public ScheduleScreen(ScheduleContainer container, Inventory inv, Component title) {
super(container, inv, title);
schedule = new Schedule();
CompoundTag tag = container.contentHolder.getOrCreateTag()
.getCompound("Schedule");
if (!tag.isEmpty())
schedule = Schedule.fromTag(tag);
container.slotsActive = false;
editorSubWidgets = new ArrayList<>();
}
@Override
protected void init() {
AllGuiTextures bg = AllGuiTextures.SCHEDULE;
setWindowSize(bg.width, bg.height + 22);
super.init();
clearWidgets();
confirmButton = new IconButton(leftPos + bg.width - 42, topPos + bg.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(() -> minecraft.player.closeContainer());
addRenderableWidget(confirmButton);
cyclicIndicator = new Indicator(leftPos + 21, topPos + 196, TextComponent.EMPTY);
cyclicIndicator.state = schedule.cyclic ? State.ON : State.OFF;
addRenderableWidget(cyclicIndicator);
cyclicButton = new IconButton(leftPos + 21, topPos + 202, AllIcons.I_REFRESH);
cyclicButton.withCallback(() -> {
schedule.cyclic = !schedule.cyclic;
cyclicIndicator.state = schedule.cyclic ? State.ON : State.OFF;
});
List<Component> tip = cyclicButton.getToolTip();
tip.add(Lang.translate("schedule.loop"));
tip.add(Lang.translate("schedule.loop1")
.withStyle(ChatFormatting.GRAY));
tip.add(Lang.translate("schedule.loop2")
.withStyle(ChatFormatting.GRAY));
addRenderableWidget(cyclicButton);
stopEditing();
extraAreas = ImmutableList.of(new Rect2i(leftPos + 255, topPos + 182, 45, 45));
horizontalScrolls.clear();
for (int i = 0; i < schedule.entries.size(); i++)
horizontalScrolls.add(LerpedFloat.linear()
.startWithValue(0));
}
protected void startEditing(IScheduleInput field, Consumer<Boolean> onClose, boolean allowDeletion) {
onEditorClose = onClose;
confirmButton.visible = false;
cyclicButton.visible = false;
cyclicIndicator.visible = false;
scrollInput = new SelectionScrollInput(leftPos + 56, topPos + 65, 143, 16);
scrollInputLabel = new Label(leftPos + 59, topPos + 69, new TextComponent("")).withShadow();
editorConfirm = new IconButton(leftPos + 56 + 168, topPos + 65 + 22, AllIcons.I_CONFIRM);
if (allowDeletion)
editorDelete = new IconButton(leftPos + 56 - 45, topPos + 65 + 22, AllIcons.I_TRASH);
menu.slotsActive = true;
menu.targetSlotActive = field.needsSlot();
if (field instanceof ScheduleDestination dest) {
int startIndex = 0;
for (int i = 0; i < Schedule.DESTINATION_TYPES.size(); i++)
if (Schedule.DESTINATION_TYPES.get(i)
.getFirst()
.equals(dest.getId()))
startIndex = i;
editingDestination = dest;
updateEditorSubwidgets(editingDestination);
scrollInput.forOptions(Schedule.getTypeOptions(Schedule.DESTINATION_TYPES))
.titled(Lang.translate("schedule.destination_type"))
.writingTo(scrollInputLabel)
.calling(index -> {
ScheduleDestination newlyCreated = Schedule.DESTINATION_TYPES.get(index)
.getSecond()
.get();
if (editingDestination.getId()
.equals(newlyCreated.getId()))
return;
editingDestination = newlyCreated;
updateEditorSubwidgets(editingDestination);
})
.setState(startIndex);
}
if (field instanceof ScheduleWaitCondition cond) {
int startIndex = 0;
for (int i = 0; i < Schedule.CONDITION_TYPES.size(); i++)
if (Schedule.CONDITION_TYPES.get(i)
.getFirst()
.equals(cond.getId()))
startIndex = i;
editingCondition = cond;
updateEditorSubwidgets(editingCondition);
scrollInput.forOptions(Schedule.getTypeOptions(Schedule.CONDITION_TYPES))
.titled(Lang.translate("schedule.condition_type"))
.writingTo(scrollInputLabel)
.calling(index -> {
ScheduleWaitCondition newlyCreated = Schedule.CONDITION_TYPES.get(index)
.getSecond()
.get();
if (editingCondition.getId()
.equals(newlyCreated.getId()))
return;
editingCondition = newlyCreated;
updateEditorSubwidgets(editingCondition);
})
.setState(startIndex);
}
addRenderableWidget(scrollInput);
addRenderableWidget(scrollInputLabel);
addRenderableWidget(editorConfirm);
if (allowDeletion)
addRenderableWidget(editorDelete);
}
protected void stopEditing() {
confirmButton.visible = true;
cyclicButton.visible = true;
cyclicIndicator.visible = true;
if (editingCondition == null && editingDestination == null)
return;
removeWidget(scrollInput);
removeWidget(scrollInputLabel);
removeWidget(editorConfirm);
removeWidget(editorDelete);
IScheduleInput editing = editingCondition == null ? editingDestination : editingCondition;
editing.setItem(menu.getSlot(36)
.getItem());
editorSubWidgets.forEach(p -> p.getSecond()
.accept(editing, p.getFirst()));
editorSubWidgets.forEach(p -> removeWidget(p.getFirst()));
editorSubWidgets.clear();
editorDividers = null;
editingCondition = null;
editingDestination = null;
editorConfirm = null;
editorDelete = null;
menu.slotsActive = false;
init();
}
protected void updateEditorSubwidgets(IScheduleInput field) {
menu.targetSlotActive = field.needsSlot();
editorSubWidgets.forEach(p -> removeWidget(p.getFirst()));
editorSubWidgets.clear();
editorDividers = new ArrayList<>();
field.createWidgets(this, editorSubWidgets, editorDividers, leftPos - 2, topPos + 40);
if (editorSubWidgets.isEmpty())
editorDividers = null;
editorSubWidgets.forEach(pair -> {
GuiEventListener e = pair.getFirst();
if (e instanceof AbstractSimiWidget)
addRenderableWidget((AbstractSimiWidget) e);
if (e instanceof EditBox)
addRenderableWidget((EditBox) e);
});
}
@Override
protected void containerTick() {
super.containerTick();
scroll.tickChaser();
for (LerpedFloat lerpedFloat : horizontalScrolls)
lerpedFloat.tickChaser();
}
@Override
public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
partialTicks = minecraft.getFrameTime();
if (menu.slotsActive)
super.render(matrixStack, mouseX, mouseY, partialTicks);
else {
renderBackground(matrixStack);
renderBg(matrixStack, partialTicks, mouseX, mouseY);
for (Widget widget : this.renderables)
widget.render(matrixStack, mouseX, mouseY, partialTicks);
renderForeground(matrixStack, mouseX, mouseY, partialTicks);
}
}
protected void renderSchedule(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
UIRenderHelper.swapAndBlitColor(minecraft.getMainRenderTarget(), UIRenderHelper.framebuffer);
UIRenderHelper.drawStretched(matrixStack, leftPos + 33, topPos + 16, 3, 173, -100,
AllGuiTextures.SCHEDULE_STRIP_DARK);
int yOffset = 25;
List<ScheduleEntry> entries = schedule.entries;
float scrollOffset = -scroll.getValue(partialTicks);
for (int i = 0; i <= entries.size(); i++) {
startStencil(matrixStack, leftPos + 16, topPos + 16, 220, 173);
matrixStack.pushPose();
matrixStack.translate(0, scrollOffset, 0);
if (i == 0 || entries.size() == 0)
UIRenderHelper.drawStretched(matrixStack, leftPos + 33, topPos + 16, 3, 10, -100,
AllGuiTextures.SCHEDULE_STRIP_LIGHT);
if (i == entries.size()) {
if (i > 0)
yOffset += 9;
AllGuiTextures.SCHEDULE_STRIP_END.render(matrixStack, leftPos + 29, topPos + yOffset);
AllGuiTextures.SCHEDULE_CARD_NEW.render(matrixStack, leftPos + 43, topPos + yOffset);
matrixStack.popPose();
endStencil();
break;
}
ScheduleEntry scheduleEntry = entries.get(i);
int cardY = yOffset;
int cardHeight = renderScheduleEntry(matrixStack, scheduleEntry, cardY, mouseX, mouseY, partialTicks);
yOffset += cardHeight;
if (i + 1 < entries.size()) {
AllGuiTextures.SCHEDULE_STRIP_DOTTED.render(matrixStack, leftPos + 29, topPos + yOffset - 3);
yOffset += 10;
}
matrixStack.popPose();
endStencil();
float h = cardHeight - 26;
float y1 = cardY + 24 + scrollOffset;
float y2 = y1 + h;
if (y2 > 189)
h -= y2 - 189;
if (y1 < 16) {
float correction = 16 - y1;
y1 += correction;
h -= correction;
}
if (h <= 0)
continue;
startStencil(matrixStack, leftPos + 43, topPos + y1, 161, h);
matrixStack.pushPose();
matrixStack.translate(0, scrollOffset, 0);
renderScheduleConditions(matrixStack, scheduleEntry, cardY, mouseX, mouseY, partialTicks, cardHeight, i);
matrixStack.popPose();
endStencil();
if (isConditionAreaScrollable(scheduleEntry)) {
startStencil(matrixStack, leftPos + 16, topPos + 16, 220, 173);
matrixStack.pushPose();
matrixStack.translate(0, scrollOffset, 0);
int center = (cardHeight - 8 + CARD_HEADER) / 2;
float chaseTarget = horizontalScrolls.get(i)
.getChaseTarget();
if (!Mth.equal(chaseTarget, 0))
AllGuiTextures.SCHEDULE_SCROLL_LEFT.render(matrixStack, leftPos + 40, topPos + cardY + center);
if (!Mth.equal(chaseTarget, scheduleEntry.conditions.size() - 1))
AllGuiTextures.SCHEDULE_SCROLL_RIGHT.render(matrixStack, leftPos + 203, topPos + cardY + center);
matrixStack.popPose();
endStencil();
}
}
int zLevel = 200;
Matrix4f mat = matrixStack.last()
.pose();
GuiUtils.drawGradientRect(mat, zLevel, leftPos + 16, topPos + 16, leftPos + 16 + 220, topPos + 16 + 10,
0x77000000, 0x00000000);
GuiUtils.drawGradientRect(mat, zLevel, leftPos + 16, topPos + 179, leftPos + 16 + 220, topPos + 179 + 10,
0x00000000, 0x77000000);
UIRenderHelper.swapAndBlitColor(UIRenderHelper.framebuffer, minecraft.getMainRenderTarget());
}
public int renderScheduleEntry(PoseStack matrixStack, ScheduleEntry entry, int yOffset, int mouseX, int mouseY,
float partialTicks) {
int zLevel = -100;
AllGuiTextures light = AllGuiTextures.SCHEDULE_CARD_LIGHT;
AllGuiTextures medium = AllGuiTextures.SCHEDULE_CARD_MEDIUM;
AllGuiTextures dark = AllGuiTextures.SCHEDULE_CARD_DARK;
int cardWidth = CARD_WIDTH;
int cardHeader = CARD_HEADER;
int maxRows = 0;
for (List<ScheduleWaitCondition> list : entry.conditions)
maxRows = Math.max(maxRows, list.size());
int cardHeight = cardHeader + 24 + maxRows * 18;
matrixStack.pushPose();
matrixStack.translate(leftPos + 25, topPos + yOffset, 0);
UIRenderHelper.drawStretched(matrixStack, 0, 1, cardWidth, cardHeight - 2, zLevel, light);
UIRenderHelper.drawStretched(matrixStack, 1, 0, cardWidth - 2, cardHeight, zLevel, light);
UIRenderHelper.drawStretched(matrixStack, 1, 1, cardWidth - 2, cardHeight - 2, zLevel, dark);
UIRenderHelper.drawStretched(matrixStack, 2, 2, cardWidth - 4, cardHeight - 4, zLevel, medium);
UIRenderHelper.drawStretched(matrixStack, 2, 2, cardWidth - 4, cardHeader, zLevel, light);
AllGuiTextures.SCHEDULE_CARD_REMOVE.render(matrixStack, cardWidth - 14, 2);
AllGuiTextures.SCHEDULE_CARD_DUPLICATE.render(matrixStack, cardWidth - 14, cardHeight - 14);
int i = schedule.entries.indexOf(entry);
if (i > 0)
AllGuiTextures.SCHEDULE_CARD_MOVE_UP.render(matrixStack, cardWidth, cardHeader - 14);
if (i < schedule.entries.size() - 1)
AllGuiTextures.SCHEDULE_CARD_MOVE_DOWN.render(matrixStack, cardWidth, cardHeader);
UIRenderHelper.drawStretched(matrixStack, 8, 0, 3, cardHeight + 10, zLevel,
AllGuiTextures.SCHEDULE_STRIP_LIGHT);
AllGuiTextures.SCHEDULE_STRIP_TRAVEL.render(matrixStack, 4, 6);
AllGuiTextures.SCHEDULE_STRIP_WAIT.render(matrixStack, 4, 28);
Pair<ItemStack, Component> destination = entry.destination.getSummary();
renderInput(matrixStack, destination, 26, 5, false, 100);
entry.destination.renderSpecialIcon(matrixStack, 30, 5);
matrixStack.popPose();
return cardHeight;
}
public void renderScheduleConditions(PoseStack matrixStack, ScheduleEntry entry, int yOffset, int mouseX,
int mouseY, float partialTicks, int cardHeight, int entryIndex) {
int cardWidth = CARD_WIDTH;
int cardHeader = CARD_HEADER;
matrixStack.pushPose();
matrixStack.translate(leftPos + 25, topPos + yOffset, 0);
int xOffset = 26;
float scrollOffset = getConditionScroll(entry, partialTicks, entryIndex);
matrixStack.pushPose();
matrixStack.translate(-scrollOffset, 0, 0);
for (List<ScheduleWaitCondition> list : entry.conditions) {
int maxWidth = getConditionColumnWidth(list);
for (int i = 0; i < list.size(); i++) {
ScheduleWaitCondition scheduleWaitCondition = list.get(i);
Math.max(maxWidth, renderInput(matrixStack, scheduleWaitCondition.getSummary(), xOffset, 29 + i * 18,
i != 0, maxWidth));
scheduleWaitCondition.renderSpecialIcon(matrixStack, xOffset + 4, 29 + i * 18);
}
AllGuiTextures.SCHEDULE_CONDITION_APPEND.render(matrixStack, xOffset + (maxWidth - 10) / 2,
29 + list.size() * 18);
xOffset += maxWidth + 10;
}
AllGuiTextures.SCHEDULE_CONDITION_NEW.render(matrixStack, xOffset - 3, 29);
matrixStack.popPose();
if (xOffset + 16 > cardWidth - 26) {
new MatrixTransformStack(matrixStack).rotateZ(-90);
Matrix4f m = matrixStack.last()
.pose();
GuiUtils.drawGradientRect(m, 200, -cardHeight + 2, 18, -2 - cardHeader, 28, 0x44000000, 0x00000000);
GuiUtils.drawGradientRect(m, 200, -cardHeight + 2, cardWidth - 26, -2 - cardHeader, cardWidth - 16,
0x00000000, 0x44000000);
}
matrixStack.popPose();
}
private boolean isConditionAreaScrollable(ScheduleEntry entry) {
int xOffset = 26;
for (List<ScheduleWaitCondition> list : entry.conditions)
xOffset += getConditionColumnWidth(list) + 10;
return xOffset + 16 > CARD_WIDTH - 26;
}
private float getConditionScroll(ScheduleEntry entry, float partialTicks, int entryIndex) {
float scrollOffset = 0;
float scrollIndex = horizontalScrolls.get(entryIndex)
.getValue(partialTicks);
for (List<ScheduleWaitCondition> list : entry.conditions) {
int maxWidth = getConditionColumnWidth(list);
float partialOfThisColumn = Math.min(1, scrollIndex);
scrollOffset += (maxWidth + 10) * partialOfThisColumn;
scrollIndex -= partialOfThisColumn;
}
return scrollOffset;
}
private int getConditionColumnWidth(List<ScheduleWaitCondition> list) {
int maxWidth = 0;
for (ScheduleWaitCondition scheduleWaitCondition : list)
maxWidth = Math.max(maxWidth, getFieldSize(32, scheduleWaitCondition.getSummary()));
return maxWidth;
}
protected int renderInput(PoseStack matrixStack, Pair<ItemStack, Component> pair, int x, int y, boolean clean,
int minSize) {
ItemStack stack = pair.getFirst();
Component text = pair.getSecond();
boolean hasItem = !stack.isEmpty();
int fieldSize = getFieldSize(minSize, pair);
matrixStack.pushPose();
AllGuiTextures left =
clean ? AllGuiTextures.SCHEDULE_CONDITION_LEFT_CLEAN : AllGuiTextures.SCHEDULE_CONDITION_LEFT;
AllGuiTextures middle = AllGuiTextures.SCHEDULE_CONDITION_MIDDLE;
AllGuiTextures item = AllGuiTextures.SCHEDULE_CONDITION_ITEM;
AllGuiTextures right = AllGuiTextures.SCHEDULE_CONDITION_RIGHT;
matrixStack.translate(x, y, 0);
UIRenderHelper.drawStretched(matrixStack, 0, 0, fieldSize, 16, -100, middle);
left.render(matrixStack, clean ? 0 : -3, 0);
right.render(matrixStack, fieldSize - 2, 0);
if (hasItem)
item.render(matrixStack, 3, 0);
if (hasItem) {
item.render(matrixStack, 3, 0);
if (stack.getItem() != Items.STRUCTURE_VOID)
GuiGameElement.of(stack)
.at(4, 0)
.render(matrixStack);
}
if (text != null)
font.drawShadow(matrixStack, text, hasItem ? 28 : 8, 4, 0xff_f2f2ee);
matrixStack.popPose();
return fieldSize;
}
private Component clickToEdit = Lang.translate("gui.schedule.lmb_edit")
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC);
private Component rClickToDelete = Lang.translate("gui.schedule.rmb_remove")
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC);
public boolean action(PoseStack ms, double mouseX, double mouseY, int click) {
if (editingCondition != null || editingDestination != null)
return false;
Component empty = new TextComponent("");
int mx = (int) mouseX;
int my = (int) mouseY;
int x = mx - leftPos - 25;
int y = my - topPos - 25;
if (x < 0 || x >= 205)
return false;
if (y < 0 || y >= 173)
return false;
y += scroll.getValue(0);
List<ScheduleEntry> entries = schedule.entries;
for (int i = 0; i < entries.size(); i++) {
ScheduleEntry entry = entries.get(i);
int maxRows = 0;
for (List<ScheduleWaitCondition> list : entry.conditions)
maxRows = Math.max(maxRows, list.size());
int cardHeight = CARD_HEADER + 24 + maxRows * 18;
if (y >= cardHeight) {
y -= cardHeight + 10;
if (y < 0)
return false;
continue;
}
int fieldSize = getFieldSize(100, entry.destination.getSummary());
if (x > 25 && x <= 25 + fieldSize && y > 4 && y <= 20) {
List<Component> components = new ArrayList<>();
components.add(Lang.translate("schedule.destination_type")
.withStyle(ChatFormatting.GOLD));
components.addAll(entry.destination.getTitleAs("destination"));
components.add(empty);
components.add(clickToEdit);
renderTooltip(ms, components, Optional.empty(), mx, my);
if (click == 0)
startEditing(entry.destination, confirmed -> {
if (confirmed)
entry.destination = editingDestination;
}, false);
return true;
}
if (x > 180 && x <= 192) {
if (y > 0 && y <= 14) {
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.remove_entry")), Optional.empty(),
mx, my);
if (click == 0) {
entries.remove(entry);
init();
}
return true;
}
if (y > cardHeight - 14) {
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.duplicate")), Optional.empty(), mx,
my);
if (click == 0) {
entries.add(entries.indexOf(entry), entry.clone());
init();
}
return true;
}
}
if (x > 194) {
if (y > 7 && y <= 20 && i > 0) {
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.move_up")), Optional.empty(), mx,
my);
if (click == 0) {
entries.remove(entry);
entries.add(i - 1, entry);
init();
}
return true;
}
if (y > 20 && y <= 33 && i < entries.size() - 1) {
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.move_down")), Optional.empty(), mx,
my);
if (click == 0) {
entries.remove(entry);
entries.add(i + 1, entry);
init();
}
return true;
}
}
int center = (cardHeight - 8 + CARD_HEADER) / 2;
if (y > center - 1 && y <= center + 7 && isConditionAreaScrollable(entry)) {
float chaseTarget = horizontalScrolls.get(i)
.getChaseTarget();
if (x > 12 && x <= 19 && !Mth.equal(chaseTarget, 0)) {
if (click == 0)
horizontalScrolls.get(i)
.chase(chaseTarget - 1, 0.5f, Chaser.EXP);
return true;
}
if (x > 177 && x <= 184 && !Mth.equal(chaseTarget, entry.conditions.size() - 1)) {
if (click == 0)
horizontalScrolls.get(i)
.chase(chaseTarget + 1, 0.5f, Chaser.EXP);
return true;
}
}
x -= 18;
y -= 28;
if (x < 0 || y < 0 || x > 160)
return false;
x += getConditionScroll(entry, 0, i) - 8;
List<List<ScheduleWaitCondition>> columns = entry.conditions;
for (int j = 0; j < columns.size(); j++) {
List<ScheduleWaitCondition> conditions = columns.get(j);
if (x < 0)
return false;
int w = getConditionColumnWidth(conditions);
if (x >= w) {
x -= w + 10;
continue;
}
int row = y / 18;
if (row < conditions.size() && row >= 0) {
boolean canRemove = conditions.size() > 1 || columns.size() > 1;
List<Component> components = new ArrayList<>();
components.add(Lang.translate("schedule.condition_type")
.withStyle(ChatFormatting.GRAY));
ScheduleWaitCondition condition = conditions.get(row);
components.addAll(condition.getTitleAs("condition"));
components.add(empty);
components.add(clickToEdit);
if (canRemove)
components.add(rClickToDelete);
renderTooltip(ms, components, Optional.empty(), mx, my);
if (canRemove && click == 1) {
conditions.remove(row);
if (conditions.isEmpty())
columns.remove(conditions);
}
if (click == 0)
startEditing(condition, confirmed -> {
conditions.remove(row);
if (confirmed) {
conditions.add(row, editingCondition);
return;
}
if (conditions.isEmpty())
columns.remove(conditions);
}, canRemove);
return true;
}
if (y > 18 * conditions.size() && y <= 18 * conditions.size() + 10 && x >= w / 2 - 5 && x < w / 2 + 5) {
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.add_condition")), Optional.empty(),
mx, my);
if (click == 0)
startEditing(new ScheduledDelay(), confirmed -> {
if (confirmed)
conditions.add(editingCondition);
}, true);
return true;
}
return false;
}
if (x < 0 || x > 15 || y > 20)
return false;
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.alternative_condition")), Optional.empty(),
mx, my);
if (click == 0)
startEditing(new ScheduledDelay(), confirmed -> {
if (!confirmed)
return;
ArrayList<ScheduleWaitCondition> conditions = new ArrayList<>();
conditions.add(editingCondition);
columns.add(conditions);
}, true);
return true;
}
if (x < 18 || x > 33 || y > 14)
return false;
renderTooltip(ms, ImmutableList.of(Lang.translate("gui.schedule.add_entry")), Optional.empty(), mx, my);
if (click == 0)
startEditing(new FilteredDestination(), confirmed -> {
if (!confirmed)
return;
ScheduleEntry entry = new ScheduleEntry();
ScheduledDelay delay = new ScheduledDelay();
ArrayList<ScheduleWaitCondition> initialConditions = new ArrayList<>();
initialConditions.add(delay);
entry.destination = editingDestination;
delay.value = 5;
delay.timeUnit = TimeUnit.SECONDS;
entry.conditions.add(initialConditions);
schedule.entries.add(entry);
}, true);
return true;
}
private int getFieldSize(int minSize, Pair<ItemStack, Component> pair) {
ItemStack stack = pair.getFirst();
Component text = pair.getSecond();
boolean hasItem = !stack.isEmpty();
return Math.max((text == null ? 0 : font.width(text)) + (hasItem ? 20 : 0) + 16, minSize);
}
protected void startStencil(PoseStack matrixStack, float x, float y, float w, float h) {
RenderSystem.clear(GL30.GL_STENCIL_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX);
GL11.glDisable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(~0);
RenderSystem.clear(GL11.GL_STENCIL_BUFFER_BIT, Minecraft.ON_OSX);
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilOp(GL11.GL_REPLACE, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_NEVER, 1, 0xFF);
matrixStack.pushPose();
matrixStack.translate(x, y, 0);
matrixStack.scale(w, h, 1);
GuiUtils.drawGradientRect(matrixStack.last()
.pose(), -100, 0, 0, 1, 1, 0xff000000, 0xff000000);
matrixStack.popPose();
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
}
protected void endStencil() {
GL11.glDisable(GL11.GL_STENCIL_TEST);
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (editorConfirm != null && editorConfirm.isMouseOver(pMouseX, pMouseY) && onEditorClose != null) {
onEditorClose.accept(true);
stopEditing();
return true;
}
if (editorDelete != null && editorDelete.isMouseOver(pMouseX, pMouseY) && onEditorClose != null) {
onEditorClose.accept(false);
stopEditing();
return true;
}
if (action(new PoseStack(), pMouseX, pMouseY, pButton))
return true;
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
if (editingCondition == null && editingDestination == null)
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
InputConstants.Key mouseKey = InputConstants.getKey(pKeyCode, pScanCode);
boolean hitEnter = getFocused() instanceof EditBox && (pKeyCode == 257 || pKeyCode == 335);
boolean hitE = getFocused() == null && minecraft.options.keyInventory.isActiveAndMatches(mouseKey);
if (hitE || hitEnter) {
onEditorClose.accept(true);
stopEditing();
return true;
}
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
}
@Override
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
if (editingCondition != null || editingDestination != null)
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
if (hasShiftDown()) {
List<ScheduleEntry> entries = schedule.entries;
int y = (int) (pMouseY - topPos - 25 + scroll.getValue());
for (int i = 0; i < entries.size(); i++) {
ScheduleEntry entry = entries.get(i);
int maxRows = 0;
for (List<ScheduleWaitCondition> list : entry.conditions)
maxRows = Math.max(maxRows, list.size());
int cardHeight = CARD_HEADER + 24 + maxRows * 18;
if (y >= cardHeight) {
y -= cardHeight + 9;
if (y < 0)
break;
continue;
}
if (!isConditionAreaScrollable(entry))
break;
if (y < 24)
break;
if (pMouseX < leftPos + 25)
break;
if (pMouseX > leftPos + 205)
break;
float chaseTarget = horizontalScrolls.get(i)
.getChaseTarget();
if (pDelta > 0 && !Mth.equal(chaseTarget, 0)) {
horizontalScrolls.get(i)
.chase(chaseTarget - 1, 0.5f, Chaser.EXP);
return true;
}
if (pDelta < 0 && !Mth.equal(chaseTarget, entry.conditions.size() - 1)) {
horizontalScrolls.get(i)
.chase(chaseTarget + 1, 0.5f, Chaser.EXP);
return true;
}
return false;
}
}
float chaseTarget = scroll.getChaseTarget();
float max = 40 - 173;
for (ScheduleEntry scheduleEntry : schedule.entries) {
int maxRows = 0;
for (List<ScheduleWaitCondition> list : scheduleEntry.conditions)
maxRows = Math.max(maxRows, list.size());
max += CARD_HEADER + 24 + maxRows * 18 + 10;
}
if (max > 0) {
chaseTarget -= pDelta * 12;
chaseTarget = Mth.clamp(chaseTarget, 0, max);
scroll.chase((int) chaseTarget, 0.7f, Chaser.EXP);
} else
scroll.chase(0, 0.7f, Chaser.EXP);
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
}
@Override
protected void renderForeground(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
super.renderForeground(matrixStack, mouseX, mouseY, partialTicks);
GuiGameElement.of(menu.contentHolder).<GuiGameElement
.GuiRenderBuilder>at(leftPos + 251, topPos + 187, -200)
.scale(3)
.render(matrixStack);
action(matrixStack, mouseX, mouseY, -1);
if (editingCondition == null && editingDestination == null)
return;
int x = leftPos + 53;
int y = topPos + 87;
if (mouseX < x || mouseY < y || mouseX >= x + 18 || mouseY >= y + 18)
return;
IScheduleInput rendered = editingCondition == null ? editingDestination : editingCondition;
List<Component> secondLineTooltip = rendered.getSecondLineTooltip();
if (secondLineTooltip == null || (hoveredSlot != null && !hoveredSlot.getItem()
.isEmpty()))
return;
renderTooltip(matrixStack, secondLineTooltip, Optional.empty(), mouseX, mouseY);
}
@Override
protected void renderBg(PoseStack pPoseStack, float pPartialTick, int pMouseX, int pMouseY) {
AllGuiTextures.SCHEDULE.render(pPoseStack, leftPos, topPos);
FormattedCharSequence formattedcharsequence = title.getVisualOrderText();
int center = leftPos + (AllGuiTextures.SCHEDULE.width - 8) / 2;
font.draw(pPoseStack, formattedcharsequence, (float) (center - font.width(formattedcharsequence) / 2),
(float) topPos + 4, 0x505050);
renderSchedule(pPoseStack, pMouseX, pMouseY, pPartialTick);
if (editingCondition == null && editingDestination == null)
return;
this.fillGradient(pPoseStack, 0, 0, this.width, this.height, -1072689136, -804253680);
AllGuiTextures.SCHEDULE_EDITOR.render(pPoseStack, leftPos - 2, topPos + 40);
AllGuiTextures.PLAYER_INVENTORY.render(pPoseStack, leftPos + 38, topPos + 122);
font.draw(pPoseStack, playerInventoryTitle, leftPos + 46, topPos + 128, 0x505050);
formattedcharsequence = editingCondition == null ? Lang.translate("schedule.destination.editor")
.getVisualOrderText()
: Lang.translate("schedule.condition.editor")
.getVisualOrderText();
font.draw(pPoseStack, formattedcharsequence, (float) (center - font.width(formattedcharsequence) / 2),
(float) topPos + 44, 0x505050);
IScheduleInput rendered = editingCondition == null ? editingDestination : editingCondition;
if (!rendered.needsSlot() && !rendered.renderSpecialIcon(pPoseStack, leftPos + 54, topPos + 88)) {
Pair<ItemStack, Component> summary = rendered.getSummary();
ItemStack icon = summary.getFirst();
if (icon.isEmpty())
icon = rendered.getSecondLineIcon();
if (icon.isEmpty())
AllGuiTextures.SCHEDULE_EDITOR_INACTIVE_SLOT.render(pPoseStack, leftPos + 53, topPos + 87);
else
GuiGameElement.of(icon)
.at(leftPos + 54, topPos + 88)
.render(pPoseStack);
}
if (editorDividers == null)
return;
AllGuiTextures.SCHEDULE_EDITOR_SECOND_LINE.render(pPoseStack, leftPos + 74, topPos + 87);
for (Integer integer : editorDividers)
AllGuiTextures.SCHEDULE_EDITOR_DIVIDER.render(pPoseStack, leftPos + 74 + integer, topPos + 87);
}
@Override
public void removed() {
super.removed();
AllPackets.channel.sendToServer(new ScheduleEditPacket(schedule));
}
@Override
public List<Rect2i> getExtraAreas() {
return extraAreas;
}
public Font getFont() {
return font;
}
}

View file

@ -0,0 +1,99 @@
package com.simibubi.create.content.logistics.trains.management;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.gui.ScreenOpener;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
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.HorizontalDirectionalBlock;
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;
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 StationBlock extends HorizontalDirectionalBlock implements ITE<StationTileEntity> {
public static final BooleanProperty ASSEMBLING = BooleanProperty.create("assembling");
public StationBlock(Properties p_54120_) {
super(p_54120_);
registerDefaultState(defaultBlockState().setValue(ASSEMBLING, false));
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> pBuilder) {
super.createBlockStateDefinition(pBuilder.add(FACING, ASSEMBLING));
}
@Override
public void fillItemCategory(CreativeModeTab pTab, NonNullList<ItemStack> pItems) {
super.fillItemCategory(pTab, pItems);
pItems.add(AllItems.SCHEDULE.asStack());
}
@Override
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand,
BlockHitResult pHit) {
if (pPlayer == null)
return InteractionResult.PASS;
ItemStack itemInHand = pPlayer.getItemInHand(pHand);
if (AllItems.WRENCH.isIn(itemInHand))
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(StationTileEntity te, Player player) {
if (!(player instanceof LocalPlayer))
return;
GlobalStation station = te.getOrCreateGlobalStation();
if (station == null)
return;
BlockState blockState = te.getBlockState();
boolean assembling = blockState.getBlock() == this && blockState.getValue(ASSEMBLING);
ScreenOpener.open(assembling ? new AssemblyScreen(te, station) : new StationScreen(te, station));
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
return super.getStateForPlacement(pContext).setValue(FACING, pContext.getHorizontalDirection());
}
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return AllShapes.STATION.get(pState.getValue(FACING));
}
@Override
public Class<StationTileEntity> getTileEntityClass() {
return StationTileEntity.class;
}
@Override
public BlockEntityType<? extends StationTileEntity> getTileEntityType() {
return AllTileEntities.TRACK_STATION.get();
}
}

View file

@ -0,0 +1,126 @@
package com.simibubi.create.content.logistics.trains.management;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.networking.TileEntityConfigurationPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
public class StationEditPacket extends TileEntityConfigurationPacket<StationTileEntity> {
boolean assemblyMode;
Boolean tryAssemble;
String name;
public static StationEditPacket tryAssemble(BlockPos pos) {
StationEditPacket packet = new StationEditPacket(pos);
packet.tryAssemble = true;
return packet;
}
public static StationEditPacket tryDisassemble(BlockPos pos) {
StationEditPacket packet = new StationEditPacket(pos);
packet.tryAssemble = false;
return packet;
}
public static StationEditPacket configure(BlockPos pos, boolean assemble, String name) {
StationEditPacket packet = new StationEditPacket(pos);
packet.assemblyMode = assemble;
packet.tryAssemble = null;
packet.name = name;
return packet;
}
public StationEditPacket(FriendlyByteBuf buffer) {
super(buffer);
}
public StationEditPacket(BlockPos pos) {
super(pos);
}
@Override
protected void writeSettings(FriendlyByteBuf buffer) {
buffer.writeBoolean(tryAssemble != null);
if (tryAssemble != null) {
buffer.writeBoolean(tryAssemble);
return;
}
buffer.writeBoolean(assemblyMode);
buffer.writeUtf(name);
}
@Override
protected void readSettings(FriendlyByteBuf buffer) {
name = "";
if (buffer.readBoolean()) {
tryAssemble = buffer.readBoolean();
return;
}
assemblyMode = buffer.readBoolean();
name = buffer.readUtf(256);
}
@Override
protected void applySettings(StationTileEntity te) {
Level level = te.getLevel();
BlockPos blockPos = te.getBlockPos();
BlockState blockState = level.getBlockState(blockPos);
if (!name.isBlank()) {
GlobalStation station = te.getOrCreateGlobalStation();
if (station != null)
station.name = name;
Create.RAILWAYS.markTracksDirty();
}
if (!(blockState.getBlock() instanceof StationBlock))
return;
Boolean isAssemblyMode = blockState.getValue(StationBlock.ASSEMBLING);
if (tryAssemble != null) {
if (!isAssemblyMode)
return;
if (tryAssemble)
te.assemble();
else {
if (disassembleAndEnterMode(te))
te.refreshAssemblyInfo();
}
return;
}
if (isAssemblyMode == assemblyMode)
return;
BlockState newState = blockState.cycle(StationBlock.ASSEMBLING);
Boolean nowAssembling = newState.getValue(StationBlock.ASSEMBLING);
if (nowAssembling) {
if (!disassembleAndEnterMode(te))
return;
} else {
te.cancelAssembly();
}
level.setBlock(blockPos, newState, 3);
te.refreshBlockState();
if (nowAssembling)
te.refreshAssemblyInfo();
}
private boolean disassembleAndEnterMode(StationTileEntity te) {
GlobalStation station = te.getOrCreateGlobalStation();
if (station != null) {
Train train = station.getPresentTrain();
if (train != null && !train.disassemble(te.getAssemblyDirection(), te.getTarget()
.getGlobalPosition()
.above()))
return false;
}
return te.tryEnterAssemblyMode();
}
}

View file

@ -0,0 +1,105 @@
package com.simibubi.create.content.logistics.trains.management;
import com.jozufozu.flywheel.core.PartialModel;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
public class StationRenderer extends SafeTileEntityRenderer<StationTileEntity> {
public StationRenderer(BlockEntityRendererProvider.Context context) {}
@Override
protected void renderSafe(StationTileEntity te, float partialTicks, PoseStack ms, MultiBufferSource buffer,
int light, int overlay) {
BlockPos pos = te.getBlockPos();
TrackTargetingBehaviour target = te.getTarget();
BlockPos targetPosition = target.getGlobalPosition();
Level level = te.getLevel();
BlockState trackState = level.getBlockState(targetPosition);
Block block = trackState.getBlock();
if (!(block instanceof ITrackBlock))
return;
GlobalStation station = te.getOrCreateGlobalStation();
if (!te.getBlockState()
.getValue(StationBlock.ASSEMBLING) || station == null || station.getPresentTrain() != null) {
ms.pushPose();
ms.translate(-pos.getX(), -pos.getY(), -pos.getZ());
TrackTargetingBehaviour.render(level, targetPosition, target.getTargetDirection(), 0xCC993B, ms, buffer,
light, overlay);
ms.popPose();
return;
}
ITrackBlock track = (ITrackBlock) block;
Direction direction = te.assemblyDirection;
if (direction == null || te.assemblyLength == 0 || te.bogeyLocations == null)
return;
ms.pushPose();
BlockPos offset = targetPosition.subtract(pos);
ms.translate(offset.getX(), offset.getY(), offset.getZ());
MutableBlockPos currentPos = targetPosition.mutable();
PartialModel assemblyOverlay = track.prepareAssemblyOverlay(level, targetPosition, trackState, direction, ms);
int colorWhenValid = 0x7092F2;
int colorWhenCarriage = 0x70EF70;
VertexConsumer vb = buffer.getBuffer(RenderType.cutoutMipped());
currentPos.move(direction, 1);
ms.translate(0, 0, 1);
for (int i = 0; i < te.assemblyLength; i++) {
int valid = te.isValidBogeyOffset(i) ? colorWhenValid : -1;
for (int j : te.bogeyLocations)
if (i == j) {
valid = colorWhenCarriage;
break;
}
if (valid != -1) {
int lightColor = LevelRenderer.getLightColor(level, currentPos);
SuperByteBuffer sbb = CachedBufferer.partial(assemblyOverlay, trackState);
sbb.color(valid);
sbb.light(lightColor);
sbb.renderInto(ms, vb);
}
ms.translate(0, 0, 1);
currentPos.move(direction);
}
ms.popPose();
}
@Override
public boolean shouldRenderOffScreen(StationTileEntity pBlockEntity) {
return true;
}
@Override
public int getViewDistance() {
return 96;
}
}

View file

@ -0,0 +1,309 @@
package com.simibubi.create.content.logistics.trains.management;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.function.Consumer;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
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.widget.IconButton;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.Mth;
public class StationScreen extends AbstractStationScreen {
private EditBox nameBox;
private EditBox trainNameBox;
private IconButton newTrainButton;
private IconButton disassembleTrainButton;
private IconButton openScheduleButton;
private int leavingAnimation;
private LerpedFloat trainPosition;
private boolean switchingToAssemblyMode;
public StationScreen(StationTileEntity te, GlobalStation station) {
super(te, station);
background = AllGuiTextures.STATION;
leavingAnimation = 0;
trainPosition = LerpedFloat.linear()
.startWithValue(0);
switchingToAssemblyMode = false;
}
@Override
protected void init() {
super.init();
int x = guiLeft;
int y = guiTop;
Consumer<String> onTextChanged;
onTextChanged = s -> nameBox.x = nameBoxX(s, nameBox);
nameBox = new EditBox(new NoShadowFontWrapper(font), x + 23, y + 4, background.width - 20, 10,
new TextComponent(station.name));
nameBox.setBordered(false);
nameBox.setMaxLength(25);
nameBox.setTextColor(0x442000);
nameBox.setValue(station.name);
nameBox.changeFocus(false);
nameBox.mouseClicked(0, 0, 0);
nameBox.setResponder(onTextChanged);
nameBox.x = nameBoxX(nameBox.getValue(), nameBox);
addRenderableWidget(nameBox);
Runnable assemblyCallback = () -> {
switchingToAssemblyMode = true;
minecraft.setScreen(new AssemblyScreen(te, station));
};
newTrainButton = new WideIconButton(x + 84, y + 65, AllGuiTextures.I_NEW_TRAIN);
newTrainButton.setToolTip(new TextComponent("New Train"));
newTrainButton.withCallback(assemblyCallback);
addRenderableWidget(newTrainButton);
disassembleTrainButton = new WideIconButton(x + 94, y + 65, AllGuiTextures.I_DISASSEMBLE_TRAIN);
disassembleTrainButton.active = false;
disassembleTrainButton.visible = false;
disassembleTrainButton.withCallback(assemblyCallback);
addRenderableWidget(disassembleTrainButton);
openScheduleButton = new IconButton(x + 73, y + 65, AllIcons.I_VIEW_SCHEDULE);
openScheduleButton.active = false;
openScheduleButton.visible = false;
addRenderableWidget(openScheduleButton);
onTextChanged = s -> trainNameBox.x = nameBoxX(s, trainNameBox);
trainNameBox = new EditBox(font, x + 23, y + 47, background.width - 20, 10, new TextComponent(""));
trainNameBox.setBordered(false);
trainNameBox.setMaxLength(15);
trainNameBox.setTextColor(0xC6C6C6);
trainNameBox.changeFocus(false);
trainNameBox.mouseClicked(0, 0, 0);
trainNameBox.setResponder(onTextChanged);
trainNameBox.active = false;
tickTrainDisplay();
}
@Override
public void tick() {
tickTrainDisplay();
if (getFocused() != nameBox) {
nameBox.setCursorPosition(nameBox.getValue()
.length());
nameBox.setHighlightPos(nameBox.getCursorPosition());
}
if (getFocused() != trainNameBox || trainNameBox.active == false) {
trainNameBox.setCursorPosition(trainNameBox.getValue()
.length());
trainNameBox.setHighlightPos(trainNameBox.getCursorPosition());
}
super.tick();
}
private void tickTrainDisplay() {
Train train = displayedTrain.get();
if (train == null) {
if (trainNameBox.active) {
trainNameBox.active = false;
removeWidget(trainNameBox);
}
leavingAnimation = 0;
newTrainButton.active = true;
newTrainButton.visible = true;
Train imminentTrain = station.getImminentTrain();
if (imminentTrain != null) {
displayedTrain = new WeakReference<>(imminentTrain);
newTrainButton.active = false;
newTrainButton.visible = false;
disassembleTrainButton.active = false;
disassembleTrainButton.visible = true;
openScheduleButton.active = false;
openScheduleButton.visible = true;
trainNameBox.active = true;
trainNameBox.setValue(imminentTrain.name.getString());
trainNameBox.x = nameBoxX(trainNameBox.getValue(), trainNameBox);
addRenderableWidget(trainNameBox);
int trainIconWidth = getTrainIconWidth(imminentTrain);
int targetPos = background.width / 2 - trainIconWidth / 2;
float f = (float) (imminentTrain.navigation.distanceToDestination / 15f);
trainPosition.startWithValue(targetPos - (targetPos + 5) * f);
}
return;
}
int trainIconWidth = getTrainIconWidth(train);
int targetPos = background.width / 2 - trainIconWidth / 2;
if (leavingAnimation > 0) {
disassembleTrainButton.active = false;
float f = 1 - (leavingAnimation / 80f);
trainPosition.setValue(targetPos + f * f * f * (background.width - targetPos + 5));
leavingAnimation--;
if (leavingAnimation > 0)
return;
displayedTrain = new WeakReference<>(null);
disassembleTrainButton.visible = false;
openScheduleButton.active = false;
openScheduleButton.visible = false;
return;
}
if (train.navigation.destination != station && train.currentStation != station) {
leavingAnimation = 80;
return;
}
disassembleTrainButton.active = train.currentStation == station; // TODO te.canAssemble
openScheduleButton.active = train.runtime.schedule != null;
float f = (float) (train.navigation.distanceToDestination / 30f);
trainPosition.setValue(targetPos - (targetPos + trainIconWidth) * f);
}
private int nameBoxX(String s, EditBox nameBox) {
return guiLeft + background.width / 2 - (Math.min(font.width(s), nameBox.getWidth()) + 10) / 2;
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
super.renderWindow(ms, mouseX, mouseY, partialTicks);
int x = guiLeft;
int y = guiTop;
String text = nameBox.getValue();
if (!nameBox.isFocused())
AllGuiTextures.STATION_EDIT_NAME.render(ms, nameBoxX(text, nameBox) + font.width(text) + 5, y + 1);
Train train = displayedTrain.get();
if (train == null) {
TextComponent header = new TextComponent("Station is Idle");
font.draw(ms, header, x + 97 - font.width(header) / 2, y + 47, 0x7A7A7A);
return;
}
float position = trainPosition.getValue(partialTicks);
ms.pushPose();
RenderSystem.enableBlend();
ms.translate(position, 0, 0);
TrainIconType icon = train.icon;
int offset = 0;
List<Carriage> carriages = train.carriages;
for (int i = carriages.size() - 1; i > 0; i--) {
RenderSystem.setShaderColor(1, 1, 1, Math.min(1f,
Math.min((position + offset - 10) / 30f, (background.width - 40 - position - offset) / 30f)));
if (i == carriages.size() - 1 && train.doubleEnded) {
offset += icon.render(TrainIconType.FLIPPED_ENGINE, ms, x + offset, y + 20) + 1;
continue;
}
Carriage carriage = carriages.get(i);
offset += icon.render(carriage.bogeySpacing, ms, x + offset, y + 20) + 1;
}
RenderSystem.setShaderColor(1, 1, 1,
Math.min(1f, Math.min((position + offset - 10) / 30f, (background.width - 40 - position - offset) / 30f)));
offset += icon.render(TrainIconType.ENGINE, ms, x + offset, y + 20);
RenderSystem.disableBlend();
ms.popPose();
RenderSystem.setShaderColor(1, 1, 1, 1);
UIRenderHelper.drawStretched(ms, x + 21, y + 43, 150, 46, -100, AllGuiTextures.STATION_TEXTBOX_MIDDLE);
AllGuiTextures.STATION_TEXTBOX_TOP.render(ms, x + 21, y + 42);
AllGuiTextures.STATION_TEXTBOX_BOTTOM.render(ms, x + 21, y + 86);
ms.pushPose();
ms.translate(Mth.clamp(position + offset - 13, 25, 159), 0, 0);
AllGuiTextures.STATION_TEXTBOX_SPEECH.render(ms, x, y + 38);
ms.popPose();
text = trainNameBox.getValue();
if (!trainNameBox.isFocused())
AllGuiTextures.STATION_EDIT_TRAIN_NAME.render(ms, nameBoxX(text, trainNameBox) + font.width(text) + 5,
y + 44);
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (!nameBox.isFocused() && pMouseY > guiTop && pMouseY < guiTop + 14 && pMouseX > guiLeft
&& pMouseX < guiLeft + background.width) {
nameBox.setFocus(true);
nameBox.setHighlightPos(0);
setFocused(nameBox);
return true;
}
if (trainNameBox.active && !trainNameBox.isFocused() && pMouseY > guiTop + 45 && pMouseY < guiTop + 58
&& pMouseX > guiLeft + 25 && pMouseX < guiLeft + 168) {
trainNameBox.setFocus(true);
trainNameBox.setHighlightPos(0);
setFocused(trainNameBox);
return true;
}
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
boolean hitEnter = getFocused() instanceof EditBox && (pKeyCode == 257 || pKeyCode == 335);
if (hitEnter && nameBox.isFocused()) {
nameBox.setFocus(false);
syncStationName();
return true;
}
if (hitEnter && trainNameBox.isFocused()) {
trainNameBox.setFocus(false);
syncTrainName();
return true;
}
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
}
private void syncTrainName() {
Train train = displayedTrain.get();
if (train != null && !trainNameBox.getValue()
.equals(train.name.getString()))
AllPackets.channel.sendToServer(new TrainEditPacket(train.id, trainNameBox.getValue(), train.icon.getId()));
}
private void syncStationName() {
if (!nameBox.getValue()
.equals(station.name))
AllPackets.channel.sendToServer(StationEditPacket.configure(te.getBlockPos(), false, nameBox.getValue()));
}
@Override
public void removed() {
super.removed();
AllPackets.channel
.sendToServer(StationEditPacket.configure(te.getBlockPos(), switchingToAssemblyMode, nameBox.getValue()));
Train train = displayedTrain.get();
if (!switchingToAssemblyMode && train != null)
AllPackets.channel.sendToServer(new TrainEditPacket(train.id, trainNameBox.getValue(), train.icon.getId()));
}
}

View file

@ -0,0 +1,495 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
import com.simibubi.create.content.contraptions.components.structureMovement.IDisplayAssemblyExceptions;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.TrackPropagator;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.Carriage.CarriageBogey;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import com.simibubi.create.content.logistics.trains.entity.MovingPoint;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.TrackTargetingBehaviour.GraphLocation;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.WorldAttached;
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.network.chat.TextComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class StationTileEntity extends SmartTileEntity implements IDisplayAssemblyExceptions {
UUID id;
protected int failedCarriageIndex;
protected AssemblyException lastException;
public StationTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
setLazyTickRate(20);
id = UUID.randomUUID();
lastException = null;
failedCarriageIndex = -1;
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new TrackTargetingBehaviour(this));
}
public TrackTargetingBehaviour getTarget() {
return getBehaviour(TrackTargetingBehaviour.TYPE);
}
@Override
public void initialize() {
if (!level.isClientSide)
getOrCreateGlobalStation();
super.initialize();
}
public GlobalStation getOrCreateGlobalStation() {
for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) {
GlobalStation station = trackGraph.getStation(id);
if (station == null)
continue;
return station;
}
GraphLocation loc = getTarget().determineGraphLocation();
if (loc == null)
return null;
GlobalStation globalStation = new GlobalStation(id, loc.edge, loc.position, worldPosition);
loc.graph.addStation(globalStation);
return globalStation;
}
@Override
protected void read(CompoundTag tag, boolean clientPacket) {
id = tag.getUUID("Id");
lastException = AssemblyException.read(tag);
failedCarriageIndex = tag.getInt("FailedCarriageIndex");
super.read(tag, clientPacket);
}
@Override
protected void write(CompoundTag tag, boolean clientPacket) {
tag.putUUID("Id", id);
AssemblyException.write(tag, lastException);
tag.putInt("FailedCarriageIndex", failedCarriageIndex);
super.write(tag, clientPacket);
}
// Train Assembly
public static WorldAttached<Map<BlockPos, BoundingBox>> assemblyAreas = new WorldAttached<>(w -> new HashMap<>());
Direction assemblyDirection;
int assemblyLength;
int[] bogeyLocations;
IBogeyBlock[] bogeyTypes;
int bogeyCount;
@Override
public void lazyTick() {
if (isAssembling() && !level.isClientSide)
refreshAssemblyInfo();
super.lazyTick();
}
@Override
public void tick() {
if (isAssembling() && level.isClientSide)
refreshAssemblyInfo();
super.tick();
}
public void trackClicked(Player player, ITrackBlock track, BlockState state, BlockPos pos) {
refreshAssemblyInfo();
BoundingBox bb = assemblyAreas.get(level)
.get(worldPosition);
if (bb == null || !bb.isInside(pos))
return;
int bogeyOffset = pos.distManhattan(getTarget().getGlobalPosition()) - 1;
if (!isValidBogeyOffset(bogeyOffset))
return;
Vec3 upNormal = track.getUpNormal(level, pos, state);
BlockState bogeyAnchor = track.getBogeyAnchor(level, pos, state);
level.setBlock(pos.offset(new BlockPos(upNormal)), bogeyAnchor, 3);
}
public boolean isAssembling() {
BlockState state = getBlockState();
return state.hasProperty(StationBlock.ASSEMBLING) && state.getValue(StationBlock.ASSEMBLING);
}
public boolean tryEnterAssemblyMode() {
TrackTargetingBehaviour target = getTarget();
if (!target.hasValidTrack())
return false;
BlockPos targetPosition = target.getGlobalPosition();
BlockState trackState = target.getTrackBlockState();
ITrackBlock track = target.getTrack();
Vec3 trackAxis = track.getTrackAxis(level, targetPosition, trackState);
boolean axisFound = false;
for (Axis axis : Iterate.axes) {
if (trackAxis.get(axis) == 0)
continue;
if (axisFound)
return false;
axisFound = true;
}
Create.RAILWAYS.trains.clear();
Create.RAILWAYS.carriageById.clear();
return true;
}
public void refreshAssemblyInfo() {
TrackTargetingBehaviour target = getTarget();
if (!target.hasValidTrack())
return;
GlobalStation station = getOrCreateGlobalStation();
if (station == null || station.getPresentTrain() != null)
return;
int prevLength = assemblyLength;
BlockPos targetPosition = target.getGlobalPosition();
BlockState trackState = target.getTrackBlockState();
ITrackBlock track = target.getTrack();
getAssemblyDirection();
MutableBlockPos currentPos = targetPosition.mutable();
currentPos.move(assemblyDirection);
BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, targetPosition, trackState));
int MAX_LENGTH = 48;
int MAX_BOGEY_COUNT = 20;
int bogeyIndex = 0;
int maxBogeyCount = MAX_BOGEY_COUNT;
if (bogeyLocations == null)
bogeyLocations = new int[maxBogeyCount];
if (bogeyTypes == null)
bogeyTypes = new IBogeyBlock[maxBogeyCount];
Arrays.fill(bogeyLocations, -1);
Arrays.fill(bogeyTypes, null);
for (int i = 0; i < MAX_LENGTH; i++) {
if (i == MAX_LENGTH - 1 || !track.trackEquals(trackState, level.getBlockState(currentPos))) {
assemblyLength = i;
break;
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
bogeyIndex++;
}
currentPos.move(assemblyDirection);
}
bogeyCount = bogeyIndex;
if (level.isClientSide)
return;
if (prevLength == assemblyLength)
return;
Map<BlockPos, BoundingBox> map = assemblyAreas.get(level);
BlockPos startPosition = targetPosition.relative(assemblyDirection);
BlockPos trackEnd = startPosition.relative(assemblyDirection, assemblyLength - 1);
map.put(worldPosition, BoundingBox.fromCorners(startPosition, trackEnd));
}
public boolean isValidBogeyOffset(int i) {
if ((i < 4 || bogeyCount == 0) && i != 0)
return false;
for (int j : bogeyLocations) {
if (j == -1)
break;
if (i >= j - 3 && i <= j + 3)
return false;
}
return true;
}
public Direction getAssemblyDirection() {
if (assemblyDirection != null)
return assemblyDirection;
TrackTargetingBehaviour target = getTarget();
if (!target.hasValidTrack())
return null;
BlockPos targetPosition = target.getGlobalPosition();
BlockState trackState = target.getTrackBlockState();
ITrackBlock track = target.getTrack();
AxisDirection axisDirection = target.getTargetDirection();
Vec3 axis = track.getTrackAxis(level, targetPosition, trackState)
.normalize()
.scale(axisDirection.getStep());
return assemblyDirection = Direction.getNearest(axis.x, axis.y, axis.z);
}
@Override
protected void setRemovedNotDueToChunkUnload() {
assemblyAreas.get(level)
.remove(worldPosition);
for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values())
trackGraph.removeStation(id);
super.setRemovedNotDueToChunkUnload();
}
public void assemble() {
refreshAssemblyInfo();
if (bogeyLocations[0] != 0) {
exception(new AssemblyException(new TextComponent("Frontmost Bogey must be at Station Marker")), -1);
return;
}
TrackTargetingBehaviour target = getTarget();
if (!target.hasValidTrack())
return;
BlockPos trackPosition = target.getGlobalPosition();
BlockState trackState = target.getTrackBlockState();
ITrackBlock track = target.getTrack();
BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, trackPosition, trackState));
DiscoveredLocation location = null;
List<Pair<BlockPos, DiscoveredLocation>> ends =
TrackPropagator.getEnds(level, trackPosition, trackState, null, true);
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
if (trackPosition.relative(assemblyDirection)
.equals(pair.getFirst()))
location = pair.getSecond();
if (location == null)
return;
List<Double> pointOffsets = new ArrayList<>();
for (int i = 0; i < bogeyLocations.length; i++) {
int loc = bogeyLocations[i];
if (loc == -1)
break;
double bogeySize = bogeyTypes[i].getWheelPointSpacing();
pointOffsets.add(Double.valueOf(loc + .5 - bogeySize / 2));
pointOffsets.add(Double.valueOf(loc + .5 + bogeySize / 2));
}
List<MovingPoint> points = new ArrayList<>();
Vec3 directionVec = Vec3.atLowerCornerOf(assemblyDirection.getNormal());
TrackGraph graph = null;
TrackNode secondNode = null;
for (int i = 0; i < assemblyLength + 20; i++) {
if (points.size() == pointOffsets.size())
break;
DiscoveredLocation currentLocation = location;
location = new DiscoveredLocation(location.getLocation()
.add(directionVec));
if (graph == null)
graph = Create.RAILWAYS.getGraph(level, currentLocation);
if (graph == null)
continue;
TrackNode node = graph.locateNode(currentLocation);
if (node == null)
continue;
for (int pointIndex = points.size(); pointIndex < pointOffsets.size(); pointIndex++) {
double offset = pointOffsets.get(pointIndex);
if (offset > i)
break;
double positionOnEdge = i - offset;
Map<TrackNode, TrackEdge> connectionsFromNode = graph.getConnectionsFrom(node);
if (secondNode == null)
for (Entry<TrackNode, TrackEdge> entry : connectionsFromNode.entrySet()) {
TrackEdge edge = entry.getValue();
TrackNode otherNode = entry.getKey();
if (edge.isTurn())
continue;
Vec3 edgeDirection = edge.getDirection(node, otherNode, true);
if (Mth.equal(edgeDirection.normalize()
.dot(directionVec), -1d))
secondNode = otherNode;
}
if (secondNode == null) {
Create.LOGGER.warn("Cannot assemble: No valid starting node found");
return;
}
TrackEdge edge = connectionsFromNode.get(secondNode);
if (edge == null) {
Create.LOGGER.warn("Cannot assemble: Missing graph edge");
return;
}
points.add(new MovingPoint(graph, node, secondNode, edge, positionOnEdge));
}
secondNode = node;
}
if (points.size() != pointOffsets.size()) {
Create.LOGGER.warn("Cannot assemble: Not all Points created");
return;
}
if (points.size() == 0) {
exception(new AssemblyException(new TextComponent("No Bogeys Found")), -1);
return;
}
List<CarriageContraption> contraptions = new ArrayList<>();
List<Carriage> carriages = new ArrayList<>();
List<Integer> spacing = new ArrayList<>();
for (int bogeyIndex = 0; bogeyIndex < bogeyCount; bogeyIndex++) {
int pointIndex = bogeyIndex * 2;
if (bogeyIndex > 0)
spacing.add(bogeyLocations[bogeyIndex] - bogeyLocations[bogeyIndex - 1]);
CarriageContraption contraption = new CarriageContraption(assemblyDirection);
BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset);
try {
boolean success = contraption.assemble(level,
bogeyPosOffset.relative(assemblyDirection, bogeyLocations[bogeyIndex] + 1));
if (!success) {
exception(new AssemblyException(new TextComponent("Nothing attached to Bogey " + bogeyIndex)), -1);
return;
}
} catch (AssemblyException e) {
exception(e, contraptions.size() + 1);
return;
}
IBogeyBlock typeOfFirstBogey = bogeyTypes[bogeyIndex];
CarriageBogey firstBogey =
new CarriageBogey(typeOfFirstBogey, points.get(pointIndex), points.get(pointIndex + 1));
CarriageBogey secondBogey = null;
BlockPos secondBogeyPos = contraption.getSecondBogeyPos();
int bogeySpacing = 0;
if (secondBogeyPos != null) {
if (bogeyIndex == bogeyCount - 1 || !secondBogeyPos
.equals(bogeyPosOffset.relative(assemblyDirection, bogeyLocations[bogeyIndex + 1] + 1))) {
exception(new AssemblyException(new TextComponent("Bogeys are not connected in order")),
contraptions.size() + 1);
return;
}
bogeySpacing = bogeyLocations[bogeyIndex + 1] - bogeyLocations[bogeyIndex];
secondBogey = new CarriageBogey(bogeyTypes[bogeyIndex + 1], points.get(pointIndex + 2),
points.get(pointIndex + 3));
bogeyIndex++;
} else if (!typeOfFirstBogey.allowsSingleBogeyCarriage()) {
exception(
new AssemblyException(new TextComponent("This bogey type cannot support a carriage on its own")),
contraptions.size() + 1);
return;
}
contraptions.add(contraption);
Carriage carriage = new Carriage(firstBogey, secondBogey, bogeySpacing);
carriage.setContraption(contraption);
carriages.add(carriage);
}
for (CarriageContraption contraption : contraptions) {
contraption.removeBlocksFromWorld(level, BlockPos.ZERO);
contraption.expandBoundsAroundAxis(Axis.Y);
}
Create.RAILWAYS.carriageById.values()
.forEach(Carriage::discardEntity);
Create.RAILWAYS.carriageById.clear();
Train train = new Train(UUID.randomUUID(), graph, carriages, spacing);
GlobalStation station = getOrCreateGlobalStation();
train.currentStation = station;
station.reserveFor(train);
Create.RAILWAYS.trains.put(train.id, train);
clearException();
}
public void cancelAssembly() {
assemblyLength = 0;
assemblyAreas.get(level)
.remove(worldPosition);
clearException();
}
private void clearException() {
exception(null, -1);
}
private void exception(AssemblyException exception, int carriage) {
failedCarriageIndex = carriage;
lastException = exception;
sendData();
}
// Render
private AABB renderBounds = null;
@Override
public AABB getRenderBoundingBox() {
if (isAssembling())
return INFINITE_EXTENT_AABB;
if (renderBounds == null)
renderBounds = new AABB(worldPosition, getTarget().getGlobalPosition());
return renderBounds;
}
@Override
public AssemblyException getLastAssemblyException() {
return lastException;
}
}

View file

@ -0,0 +1,182 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.TrackPropagator;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
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.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class TrackTargetingBehaviour extends TileEntityBehaviour {
public static final BehaviourType<TrackTargetingBehaviour> TYPE = new BehaviourType<>();
private BlockPos targetTrack;
private AxisDirection targetDirection;
public TrackTargetingBehaviour(SmartTileEntity te) {
super(te);
targetDirection = AxisDirection.POSITIVE;
targetTrack = BlockPos.ZERO;
}
@Override
public void write(CompoundTag nbt, boolean clientPacket) {
nbt.put("TargetTrack", NbtUtils.writeBlockPos(targetTrack));
nbt.putBoolean("TargetDirection", targetDirection == AxisDirection.POSITIVE);
super.write(nbt, clientPacket);
}
@Override
public void read(CompoundTag nbt, boolean clientPacket) {
targetTrack = NbtUtils.readBlockPos(nbt.getCompound("TargetTrack"));
targetDirection = nbt.getBoolean("TargetDirection") ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE;
super.read(nbt, clientPacket);
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
public boolean hasValidTrack() {
return getTrackBlockState().getBlock() instanceof ITrackBlock;
}
public ITrackBlock getTrack() {
return (ITrackBlock) getTrackBlockState().getBlock();
}
public BlockState getTrackBlockState() {
return getWorld().getBlockState(getGlobalPosition());
}
public BlockPos getGlobalPosition() {
return targetTrack.offset(tileEntity.getBlockPos());
}
public AxisDirection getTargetDirection() {
return targetDirection;
}
static class GraphLocation {
public TrackGraph graph;
public Couple<TrackNode> edge;
public double position;
}
public GraphLocation determineGraphLocation() {
Level level = getWorld();
BlockPos pos = getGlobalPosition();
BlockState trackBlockState = getTrackBlockState();
ITrackBlock track = getTrack();
if (track == null)
return null;
Vec3 axis = track.getTrackAxis(level, pos, trackBlockState)
.normalize()
.scale(getTargetDirection().getStep());
List<Pair<BlockPos, DiscoveredLocation>> ends =
TrackPropagator.getEnds(level, pos, trackBlockState, null, true);
TrackGraph graph = null;
TrackNode frontNode = null;
TrackNode backNode = null;
double position = 0;
for (Pair<BlockPos, DiscoveredLocation> pair : ends) {
DiscoveredLocation current = pair.getSecond();
BlockPos currentPos = pair.getFirst();
Vec3 offset = Vec3.atLowerCornerOf(currentPos.subtract(pos));
boolean forward = offset.distanceToSqr(axis.scale(-1)) < 1 / 4096f;
boolean backwards = offset.distanceToSqr(axis) < 1 / 4096f;
if (!forward && !backwards)
continue;
for (int i = 0; i < 32; i++) {
DiscoveredLocation loc = current;
List<Pair<BlockPos, DiscoveredLocation>> list =
TrackPropagator.getEnds(level, currentPos, level.getBlockState(currentPos), current, true);
if (!list.isEmpty()) {
currentPos = list.get(0)
.getFirst();
current = list.get(0)
.getSecond();
}
if (graph == null)
graph = Create.RAILWAYS.getGraph(level, loc);
if (graph == null)
continue;
TrackNode node = graph.locateNode(loc);
if (node == null)
continue;
if (forward)
frontNode = node;
if (backwards) {
backNode = node;
position = i + .5;
}
break;
}
}
if (frontNode == null || backNode == null)
return null;
GraphLocation graphLocation = new GraphLocation();
graphLocation.edge = Couple.create(backNode, frontNode);
graphLocation.position = position;
graphLocation.graph = graph;
return graphLocation;
}
@OnlyIn(Dist.CLIENT)
public static void render(LevelAccessor level, BlockPos pos, AxisDirection direction, int tintColor, PoseStack ms,
MultiBufferSource buffer, int light, int overlay) {
BlockState trackState = level.getBlockState(pos);
Block block = trackState.getBlock();
if (!(block instanceof ITrackBlock))
return;
ms.pushPose();
ms.translate(pos.getX(), pos.getY(), pos.getZ());
ITrackBlock track = (ITrackBlock) block;
SuperByteBuffer sbb =
CachedBufferer.partial(track.prepareStationOverlay(level, pos, trackState, direction, ms), trackState);
sbb.color(tintColor);
sbb.light(LevelRenderer.getLightColor(level, pos));
sbb.renderInto(ms, buffer.getBuffer(RenderType.cutoutMipped()));
ms.popPose();
}
}

View file

@ -0,0 +1,102 @@
package com.simibubi.create.content.logistics.trains.management;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.foundation.render.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting;
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;
public class TrackTargetingBlockItem extends BlockItem {
public TrackTargetingBlockItem(Block pBlock, Properties pProperties) {
super(pBlock, pProperties);
}
@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("track_target.clear"), true);
stack.setTag(null);
return InteractionResult.SUCCESS;
}
if (state.getBlock() instanceof ITrackBlock track) {
if (level.isClientSide)
return InteractionResult.SUCCESS;
CompoundTag stackTag = stack.getOrCreateTag();
boolean front = player.getLookAngle()
.dot(track.getTrackAxis(level, pos, state)) < 0;
stackTag.put("SelectedPos", NbtUtils.writeBlockPos(pos));
stackTag.putBoolean("SelectedDirection", front);
player.displayClientMessage(Lang.translate("track_target.set"), true);
stack.setTag(stackTag);
return InteractionResult.SUCCESS;
}
if (!stack.hasTag()) {
player.displayClientMessage(Lang.translate("track_target.missing")
.withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
CompoundTag tag = stack.getTag();
CompoundTag teTag = new CompoundTag();
teTag.putBoolean("TargetDirection", tag.getBoolean("SelectedDirection"));
BlockPos selectedPos = NbtUtils.readBlockPos(tag.getCompound("SelectedPos"));
BlockPos placedPos = pos.relative(pContext.getClickedFace(), state.getMaterial()
.isReplaceable() ? 0 : 1);
if (!selectedPos.closerThan(placedPos, 16)) {
player.displayClientMessage(Lang.translate("track_target.too_far")
.withStyle(ChatFormatting.RED), true);
return InteractionResult.FAIL;
}
teTag.put("TargetTrack", 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("track_target.success")
.withStyle(ChatFormatting.GREEN), true);
return useOn;
}
public static void clientTick() {
}
public static void render(PoseStack ms, SuperRenderTypeBuffer buffer) {
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.content.logistics.trains.management;
import java.util.UUID;
import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkEvent.Context;
public class TrainEditPacket extends SimplePacketBase {
private String name;
private UUID id;
private ResourceLocation iconType;
public TrainEditPacket(UUID id, String name, ResourceLocation iconType) {
this.name = name;
this.id = id;
this.iconType = iconType;
}
public TrainEditPacket(FriendlyByteBuf buffer) {
id = buffer.readUUID();
name = buffer.readUtf(256);
iconType = buffer.readResourceLocation();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeUUID(id);
buffer.writeUtf(name);
buffer.writeResourceLocation(iconType);
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
Train train = Create.RAILWAYS.trains.get(id);
if (train == null)
return;
if (!name.isBlank())
train.name = new TextComponent(name);
train.icon = TrainIconType.byId(iconType);
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -0,0 +1,20 @@
package com.simibubi.create.content.logistics.trains.management;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.element.ScreenElement;
import com.simibubi.create.foundation.gui.widget.IconButton;
public class WideIconButton extends IconButton {
public WideIconButton(int x, int y, ScreenElement icon) {
super(x, y, 26, 18, icon);
}
@Override
protected void drawBg(PoseStack matrixStack, AllGuiTextures button) {
super.drawBg(matrixStack, button);
blit(matrixStack, x + 9, y, button.startX + 1, button.startY, button.width - 1, button.height);
}
}

View file

@ -0,0 +1,130 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
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.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public abstract class CargoThresholdCondition extends ScheduleWaitCondition {
public static enum Ops {
GREATER(">"), LESS("<"), EQUAL("=");
public String formatted;
private Ops(String formatted) {
this.formatted = formatted;
}
public static List<? extends Component> translatedOptions() {
return Arrays.stream(values())
.map(op -> Lang.translate("schedule.condition.threshold." + Lang.asId(op.name())))
.toList();
}
}
public CargoThresholdCondition.Ops ops = Ops.GREATER;
public int threshold;
protected abstract Component getUnit();
protected abstract ItemStack getIcon();
@Override
public Pair<ItemStack, Component> getSummary() {
return Pair.of(getIcon(), new TextComponent(ops.formatted + " " + threshold).append(getUnit()));
}
@Override
protected void write(CompoundTag tag) {
NBTHelper.writeEnum(tag, "Operator", ops);
tag.putInt("Threshold", threshold);
}
@Override
protected void read(CompoundTag tag) {
ops = NBTHelper.readEnum(tag, "Operator", CargoThresholdCondition.Ops.class);
threshold = tag.getInt("Threshold");
}
@Override
public boolean needsSlot() {
return true;
}
@Override
public List<Component> getSecondLineTooltip() {
return ImmutableList.of(Lang.translate("schedule.condition.threshold.place_item"),
Lang.translate("schedule.condition.threshold.place_item_2")
.withStyle(ChatFormatting.GRAY));
}
@Override
@OnlyIn(Dist.CLIENT)
public void createWidgets(ScheduleScreen screen,
List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets,
List<Integer> dividers, int x, int y) {
super.createWidgets(screen, editorSubWidgets, dividers, x, y);
EditBox editBox = new EditBox(screen.getFont(), x + 109, y + 52, 35, 10, new TextComponent(threshold + ""));
editBox.setBordered(false);
editBox.setValue(threshold + "");
editBox.setTextColor(0xFFFFFF);
editBox.changeFocus(false);
editBox.mouseClicked(0, 0, 0);
editBox.setFilter(s -> {
if (s.isEmpty())
return true;
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
return false;
}
});
Label label = new Label(x + 87, y + 52, new TextComponent(ops.formatted)).withShadow();
label.text = new TextComponent(ops.formatted);
ScrollInput scrollInput = new SelectionScrollInput(x + 76, y + 48, 24, 16).forOptions(Ops.translatedOptions())
.titled(Lang.translate("schedule.condition.threshold.train_holds"))
.calling(state -> {
label.text = new TextComponent(Ops.values()[state].formatted);
})
.setState(ops.ordinal());
editorSubWidgets.add(Pair.of(editBox, (dest, box) -> {
CargoThresholdCondition c = (CargoThresholdCondition) dest;
String text = ((EditBox) box).getValue();
if (text.isEmpty())
c.threshold = 0;
else
c.threshold = Integer.parseInt(text);
}));
editorSubWidgets.add(Pair.of(scrollInput, (dest, box) -> {
CargoThresholdCondition c = (CargoThresholdCondition) dest;
c.ops = Ops.values()[((ScrollInput) box).getState()];
}));
editorSubWidgets.add(Pair.of(label, (d, l) -> {
}));
dividers.add(24);
dividers.add(70);
}
}

View file

@ -0,0 +1,85 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.List;
import java.util.function.BiConsumer;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class FilteredDestination extends ScheduleDestination {
public String nameFilter = "";
@Override
public Pair<ItemStack, Component> getSummary() {
return Pair.of(AllBlocks.TRACK_STATION.asStack(), new TextComponent(nameFilter));
}
@Override
protected void read(CompoundTag tag) {
nameFilter = tag.getString("Filter");
}
@Override
protected void write(CompoundTag tag) {
tag.putString("Filter", nameFilter);
}
@Override
public ResourceLocation getId() {
return Create.asResource("filtered");
}
@Override
public ItemStack getSecondLineIcon() {
return AllBlocks.TRACK_STATION.asStack();
}
@Override
public List<Component> getTitleAs(String type) {
return ImmutableList.of(Lang.translate("schedule.destination.filtered_matching",
new TextComponent(nameFilter).withStyle(ChatFormatting.YELLOW)));
}
@Override
public List<Component> getSecondLineTooltip() {
return ImmutableList.of(Lang.translate("schedule.destination.filter"),
Lang.translate("schedule.destination.filter_2")
.withStyle(ChatFormatting.GRAY),
Lang.translate("schedule.destination.filter_3")
.withStyle(ChatFormatting.DARK_GRAY),
Lang.translate("schedule.destination.filter_4")
.withStyle(ChatFormatting.DARK_GRAY));
}
@Override
@OnlyIn(Dist.CLIENT)
public void createWidgets(ScheduleScreen screen,
List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets,
List<Integer> dividers, int x, int y) {
super.createWidgets(screen, editorSubWidgets, dividers, x, y);
EditBox editBox = new EditBox(screen.getFont(), x + 84, y + 52, 112, 10, new TextComponent(nameFilter));
editBox.setBordered(false);
editBox.setTextColor(0xFFFFFF);
editBox.setValue(nameFilter);
editBox.changeFocus(false);
editBox.mouseClicked(0, 0, 0);
editorSubWidgets
.add(Pair.of(editBox, (dest, box) -> ((FilteredDestination) dest).nameFilter = ((EditBox) box).getValue()));
}
}

View file

@ -0,0 +1,104 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.List;
import java.util.function.BiConsumer;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.processing.EmptyingByBasin;
import com.simibubi.create.content.logistics.item.filter.FilterItem;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
import com.simibubi.create.foundation.gui.widget.Label;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.FluidStack;
public class FluidThresholdCondition extends CargoThresholdCondition {
public ItemStack compareStack = ItemStack.EMPTY;
public FluidStack fluidStack = null;
@Override
protected Component getUnit() {
return new TextComponent("b");
}
@Override
protected ItemStack getIcon() {
return compareStack;
}
@Override
protected void write(CompoundTag tag) {
super.write(tag);
tag.put("Bucket", compareStack.serializeNBT());
}
@Override
protected void read(CompoundTag tag) {
super.read(tag);
compareStack = ItemStack.of(tag.getCompound("Bucket"));
}
@OnlyIn(Dist.CLIENT)
private FluidStack loadFluid() {
if (fluidStack != null)
return fluidStack;
fluidStack = FluidStack.EMPTY;
if (!EmptyingByBasin.canItemBeEmptied(Minecraft.getInstance().level, compareStack))
return fluidStack;
FluidStack fluidInFilter = EmptyingByBasin.emptyItem(Minecraft.getInstance().level, compareStack, true)
.getFirst();
if (fluidInFilter == null)
return fluidStack;
return fluidStack = fluidInFilter;
}
@Override
public List<Component> getTitleAs(String type) {
return ImmutableList.of(
Lang.translate("schedule.condition.threshold.train_holds",
Lang.translate("schedule.condition.threshold." + Lang.asId(ops.name()))),
Lang.translate("schedule.condition.threshold.x_units_of_item", threshold,
Lang.translate("schedule.condition.threshold.buckets"),
compareStack.getItem() instanceof FilterItem
? Lang.translate("schedule.condition.threshold.matching_content")
: loadFluid().getDisplayName())
.withStyle(ChatFormatting.DARK_AQUA));
}
@Override
public void setItem(ItemStack stack) {
compareStack = stack;
}
@Override
public ResourceLocation getId() {
return Create.asResource("fluid_threshold");
}
@Override
@OnlyIn(Dist.CLIENT)
public void createWidgets(ScheduleScreen screen,
List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets,
List<Integer> dividers, int x, int y) {
super.createWidgets(screen, editorSubWidgets, dividers, x, y);
TranslatableComponent buckets = Lang.translate("schedule.condition.threshold.buckets");
Label label = new Label(x + 155, y + 52, buckets).withShadow();
label.text = buckets;
editorSubWidgets.add(Pair.of(label, (d, l) -> {
}));
}
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.List;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public interface IScheduleInput {
public abstract Pair<ItemStack, Component> getSummary();
public abstract ResourceLocation getId();
public default boolean needsSlot() {
return false;
}
@OnlyIn(Dist.CLIENT)
public default void createWidgets(ScheduleScreen screen,
List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets,
List<Integer> dividers, int x, int y) {}
public default List<Component> getTitleAs(String type) {
ResourceLocation id = getId();
return ImmutableList.of(new TranslatableComponent(id.getNamespace() + ".schedule." + type + "." + id.getPath()));
}
public default ItemStack getSecondLineIcon() {
return ItemStack.EMPTY;
}
public default void setItem(ItemStack stack) {}
@Nullable
public default List<Component> getSecondLineTooltip() {
return null;
}
@OnlyIn(Dist.CLIENT)
public default boolean renderSpecialIcon(PoseStack ms, int x, int y) {
return false;
}
}

View file

@ -0,0 +1,21 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public class IdleCargoCondition extends TimedWaitCondition {
@Override
public Pair<ItemStack, Component> getSummary() {
return Pair.of(ItemStack.EMPTY, Lang.translate("schedule.condition.idle_short", formatTime(true)));
}
@Override
public ResourceLocation getId() {
return Create.asResource("idle");
}
}

View file

@ -0,0 +1,98 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.List;
import java.util.function.BiConsumer;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.item.filter.FilterItem;
import com.simibubi.create.content.logistics.trains.management.ScheduleScreen;
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.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class ItemThresholdCondition extends CargoThresholdCondition {
public ItemStack stack = ItemStack.EMPTY;
public boolean stacks;
@Override
protected Component getUnit() {
return new TextComponent(stacks ? "\u25A4" : "");
}
@Override
protected ItemStack getIcon() {
return stack;
}
@Override
protected void write(CompoundTag tag) {
super.write(tag);
tag.put("Item", stack.serializeNBT());
tag.putBoolean("Stacks", stacks);
}
@Override
protected void read(CompoundTag tag) {
super.read(tag);
stack = ItemStack.of(tag.getCompound("Item"));
stacks = tag.getBoolean("Stacks");
}
@Override
public void setItem(ItemStack stack) {
this.stack = stack;
}
@Override
public List<Component> getTitleAs(String type) {
return ImmutableList.of(
Lang.translate("schedule.condition.threshold.train_holds",
Lang.translate("schedule.condition.threshold." + Lang.asId(ops.name()))),
Lang.translate("schedule.condition.threshold.x_units_of_item", threshold,
Lang.translate("schedule.condition.threshold." + (stacks ? "stacks" : "items")),
stack.getItem() instanceof FilterItem ? Lang.translate("schedule.condition.threshold.matching_content")
: stack.getHoverName())
.withStyle(ChatFormatting.DARK_AQUA));
}
@Override
public ResourceLocation getId() {
return Create.asResource("item_threshold");
}
@Override
@OnlyIn(Dist.CLIENT)
public void createWidgets(ScheduleScreen screen,
List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets,
List<Integer> dividers, int x, int y) {
super.createWidgets(screen, editorSubWidgets, dividers, x, y);
Label label = new Label(x + 155, y + 52, new TextComponent(ops.formatted)).withShadow();
ScrollInput scrollInput = new SelectionScrollInput(x + 150, y + 48, 49, 16)
.forOptions(ImmutableList.of(Lang.translate("schedule.condition.threshold.items"),
Lang.translate("schedule.condition.threshold.stacks")))
.titled(Lang.translate("schedule.condition.threshold.item_measure"))
.writingTo(label)
.setState(stacks ? 1 : 0);
editorSubWidgets.add(Pair.of(scrollInput, (dest, box) -> {
ItemThresholdCondition c = (ItemThresholdCondition) dest;
c.stacks = ((ScrollInput) box).getState() == 1;
}));
editorSubWidgets.add(Pair.of(label, (d, l) -> {
}));
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public class NearestDestination extends ScheduleDestination {
@Override
public Pair<ItemStack, Component> getSummary() {
return Pair.of(AllBlocks.TRACK_STATION.asStack(), Lang.translate("schedule.destination.nearest"));
}
@Override
protected void write(CompoundTag tag) {}
@Override
protected void read(CompoundTag tag) {}
@Override
public ResourceLocation getId() {
return Create.asResource("nearest");
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
public class RedstoneDestination extends ScheduleDestination {
@Override
public Pair<ItemStack, Component> getSummary() {
return Pair.of(AllBlocks.TRACK_STATION.asStack(), new TextComponent("Redstone Pulse"));
}
@Override
protected void write(CompoundTag tag) {}
@Override
protected void read(CompoundTag tag) {}
@Override
public ResourceLocation getId() {
return Create.asResource("redstone");
}
}

View file

@ -0,0 +1,78 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
public class Schedule {
public static List<Pair<ResourceLocation, Supplier<? extends ScheduleDestination>>> DESTINATION_TYPES =
new ArrayList<>();
public static List<Pair<ResourceLocation, Supplier<? extends ScheduleWaitCondition>>> CONDITION_TYPES =
new ArrayList<>();
static {
registerDestination("filtered", FilteredDestination::new);
registerDestination("nearest", NearestDestination::new);
registerDestination("redstone", RedstoneDestination::new);
registerCondition("delay", ScheduledDelay::new);
registerCondition("time_of_day", TimeOfDayCondition::new);
registerCondition("fluid_threshold", FluidThresholdCondition::new);
registerCondition("item_threshold", ItemThresholdCondition::new);
registerCondition("idle", IdleCargoCondition::new);
registerCondition("unloaded", StationUnloadedCondition::new);
registerCondition("powered", StationPoweredCondition::new);
}
private static void registerDestination(String name, Supplier<? extends ScheduleDestination> factory) {
DESTINATION_TYPES.add(Pair.of(Create.asResource(name), factory));
}
private static void registerCondition(String name, Supplier<? extends ScheduleWaitCondition> factory) {
CONDITION_TYPES.add(Pair.of(Create.asResource(name), factory));
}
public static <T> List<? extends Component> getTypeOptions(List<Pair<ResourceLocation, T>> list) {
String langSection = list.equals(DESTINATION_TYPES) ? "destination." : "condition.";
return list.stream()
.map(Pair::getFirst)
.map(rl -> rl.getNamespace() + ".schedule." + langSection + rl.getPath())
.map(TranslatableComponent::new)
.toList();
}
public List<ScheduleEntry> entries;
public boolean cyclic;
public Schedule() {
entries = new ArrayList<>();
cyclic = true;
}
public CompoundTag write() {
CompoundTag tag = new CompoundTag();
ListTag list = NBTHelper.writeCompoundList(entries, ScheduleEntry::write);
tag.put("Entries", list);
tag.putBoolean("Cyclic", cyclic);
return tag;
}
public static Schedule fromTag(CompoundTag tag) {
Schedule schedule = new Schedule();
schedule.entries = NBTHelper.readCompoundList(tag.getList("Entries", Tag.TAG_COMPOUND), ScheduleEntry::fromTag);
schedule.cyclic = tag.getBoolean("Cyclic");
return schedule;
}
}

View file

@ -0,0 +1,42 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
public abstract class ScheduleDestination implements IScheduleInput {
protected abstract void write(CompoundTag tag);
protected abstract void read(CompoundTag tag);
public final CompoundTag write() {
CompoundTag tag = new CompoundTag();
tag.putString("Id", getId().toString());
write(tag);
return tag;
}
public static ScheduleDestination fromTag(CompoundTag tag) {
ResourceLocation location = new ResourceLocation(tag.getString("Id"));
Supplier<? extends ScheduleDestination> supplier = null;
for (Pair<ResourceLocation, Supplier<? extends ScheduleDestination>> pair : Schedule.DESTINATION_TYPES)
if (pair.getFirst()
.equals(location))
supplier = pair.getSecond();
if (supplier == null) {
Create.LOGGER.warn("Could not parse schedule destination type: " + location);
return null;
}
ScheduleDestination scheduleDestination = supplier.get();
scheduleDestination.read(tag);
return scheduleDestination;
}
}

View file

@ -0,0 +1,48 @@
package com.simibubi.create.content.logistics.trains.management.schedule;
import java.util.function.Supplier;
import com.simibubi.create.AllItems;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent.Context;
public class ScheduleEditPacket extends SimplePacketBase {
private Schedule schedule;
public ScheduleEditPacket(Schedule schedule) {
this.schedule = schedule;
}
public ScheduleEditPacket(FriendlyByteBuf buffer) {
schedule = Schedule.fromTag(buffer.readNbt());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeNbt(schedule.write());
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
ServerPlayer sender = context.get()
.getSender();
ItemStack mainHandItem = sender.getMainHandItem();
if (!AllItems.SCHEDULE.isIn(mainHandItem))
return;
mainHandItem.getOrCreateTag()
.put("Schedule", schedule.write());
sender.getCooldowns()
.addCooldown(mainHandItem.getItem(), 5);
});
context.get()
.setPacketHandled(true);
}
}

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