From b970c0029fafc6c71f422038733574f838b7bb13 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Thu, 14 Apr 2022 02:23:55 +0200 Subject: [PATCH] Turn to target - Bezier turn segments between tracks can now be hovered as if they were blocks - Bezier turns can now be destroyed by breaking any of their segments - Signals and Stations can now be pointed to anywhere on a track, not only to physical blocks - Added a basic collision shape for straight tracks --- src/generated/resources/.cache/cache | 32 +-- .../resources/assets/create/lang/en_us.json | 2 + .../assets/create/lang/unfinished/de_de.json | 4 +- .../assets/create/lang/unfinished/es_cl.json | 4 +- .../assets/create/lang/unfinished/es_es.json | 4 +- .../assets/create/lang/unfinished/fr_fr.json | 4 +- .../assets/create/lang/unfinished/it_it.json | 4 +- .../assets/create/lang/unfinished/ja_jp.json | 4 +- .../assets/create/lang/unfinished/ko_kr.json | 4 +- .../assets/create/lang/unfinished/nl_nl.json | 4 +- .../assets/create/lang/unfinished/pl_pl.json | 4 +- .../assets/create/lang/unfinished/pt_br.json | 4 +- .../assets/create/lang/unfinished/pt_pt.json | 4 +- .../assets/create/lang/unfinished/ro_ro.json | 4 +- .../assets/create/lang/unfinished/ru_ru.json | 4 +- .../assets/create/lang/unfinished/zh_cn.json | 4 +- .../assets/create/lang/unfinished/zh_tw.json | 4 +- .../java/com/simibubi/create/AllBlocks.java | 6 +- .../java/com/simibubi/create/AllShapes.java | 2 + .../logistics/trains/BezierConnection.java | 13 ++ .../content/logistics/trains/ITrackBlock.java | 3 +- .../edgePoint/CurvedTrackSelectionPacket.java | 88 ++++++++ .../edgePoint/TrackTargetingBehaviour.java | 78 ++++++- .../edgePoint/TrackTargetingBlockItem.java | 30 ++- .../edgePoint/signal/SignalRenderer.java | 4 +- .../edgePoint/station/StationRenderer.java | 4 +- .../management/schedule/ScheduleScreen.java | 2 +- .../track/BezierTrackPointLocation.java | 6 + .../track/CurvedTrackDestroyPacket.java | 69 ++++++ .../trains/track/CurvedTrackInteraction.java | 132 +++++++++++ .../logistics/trains/track/TrackBlock.java | 93 ++++++-- .../trains/track/TrackBlockItem.java | 11 +- .../trains/track/TrackBlockOutline.java | 210 +++++++++++++++--- .../trains/track/TrackPlacement.java | 5 +- .../trains/track/TrackTileEntity.java | 48 +++- .../simibubi/create/events/ClientEvents.java | 4 + .../simibubi/create/events/InputEvents.java | 6 + .../foundation/mixin/GameRendererMixin.java | 2 + .../foundation/networking/AllPackets.java | 4 + .../assets/create/lang/default/interface.json | 2 + 40 files changed, 828 insertions(+), 88 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/CurvedTrackSelectionPacket.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/BezierTrackPointLocation.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/CurvedTrackDestroyPacket.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/CurvedTrackInteraction.java diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index afda9a349..7c1c5908b 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -543,22 +543,22 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json 7fbb25c577025ff61388c54c43401d8bb80723dd assets/create/lang/en_ud.json -0a3b09db8a3bd71570afc1452d62a1fa6bd22baa assets/create/lang/en_us.json -7ac6b939d4d1b574806ac3fa3ec495498c9c00a6 assets/create/lang/unfinished/de_de.json -58a1b86058fcebf323e2208ba26df584d42f9f97 assets/create/lang/unfinished/es_cl.json -1419151466490bfa42b84a99e0e296b54e1aeec5 assets/create/lang/unfinished/es_es.json -a366bd2c9b80079b9c42572623165497b6d49531 assets/create/lang/unfinished/fr_fr.json -b21c2d0a7cdf713492ab1eb85061df8c73a9d9b9 assets/create/lang/unfinished/it_it.json -9874ddfacd2effec2eb852ea9cc009923e372ae5 assets/create/lang/unfinished/ja_jp.json -0334b52e3e84870e84fce1e485c6153456dc84e9 assets/create/lang/unfinished/ko_kr.json -7f0bd618c72b9743832a7133db0359f546b48f5e assets/create/lang/unfinished/nl_nl.json -f6826d25ea7aca1db76e806a44434f64fb11e6b2 assets/create/lang/unfinished/pl_pl.json -5ff6c067e106418b32dbb97b3bce56a6990eaf67 assets/create/lang/unfinished/pt_br.json -8e639a6e5337e7723c454b3d01dd70e9715318fe assets/create/lang/unfinished/pt_pt.json -763413532441a056bd88bbcea394fcf9e50e8c29 assets/create/lang/unfinished/ro_ro.json -24430fe32f666e6df5fa9185dabedbe9746155d2 assets/create/lang/unfinished/ru_ru.json -9955890700831881c15b3df9d720448c09ae341e assets/create/lang/unfinished/zh_cn.json -7294335fc11c054fb761a251493b17866f01f0c2 assets/create/lang/unfinished/zh_tw.json +7cd7e879350cbb56393165c24062fda5676466b9 assets/create/lang/en_us.json +10e938a41280ab88265f59b293cf1a19ea012f18 assets/create/lang/unfinished/de_de.json +8b17b8042e844f53bf2ae605c0a7d5b6f9e470ee assets/create/lang/unfinished/es_cl.json +4bc7d56deb76a73fa7af16e21b7a95d00d1c1b3c assets/create/lang/unfinished/es_es.json +200deeabcf50a3d91387f90528154f3d0ebea8a4 assets/create/lang/unfinished/fr_fr.json +d806ae2e13f9415b79ce7c7928c8e93ec93dd9bb assets/create/lang/unfinished/it_it.json +5a0afa8b1ccc8b9dcbd0b0353aed8a3d14398059 assets/create/lang/unfinished/ja_jp.json +b851295ec91158c2f6e4b8433ff13057367c5694 assets/create/lang/unfinished/ko_kr.json +fe7e927998605dfa186b0bed7c179b0a92e5da5b assets/create/lang/unfinished/nl_nl.json +352ef01578bc2fedd31ad95c8f48069ee2714240 assets/create/lang/unfinished/pl_pl.json +bc5bddb12f2b01c034e7bf8619ebc8ea3136a526 assets/create/lang/unfinished/pt_br.json +67a9b9769fe289213f95c9f9680b4fa2922930de assets/create/lang/unfinished/pt_pt.json +38ec533de9f914ca5c8209e31cba9b3e06c734b7 assets/create/lang/unfinished/ro_ro.json +556d68b3d26152f1a3a25a568cd1b98b844c66a6 assets/create/lang/unfinished/ru_ru.json +4239813d57b74da7eb654a66342769e111679729 assets/create/lang/unfinished/zh_cn.json +78ef5f771b4d1bb62dc2d4eca06124d28696e8b2 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 diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 47f819ec7..123eebf53 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -1412,6 +1412,8 @@ "create.track.leave_slope_ascending": "Cannot leave this slope while ascending", "create.track.leave_slope_descending": "Cannot leave this slope while descending", "create.track.turn_90": "Can only turn up to 90 Degrees", + "create.track.junction_start": "Cannot start connection from a Junction", + "create.track.turn_start": "Cannot start connection from a Turn", "create.station.create_train": "Create new Train", "create.station.disassemble_train": "Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/de_de.json b/src/generated/resources/assets/create/lang/unfinished/de_de.json index db10b77e0..debb5d7fd 100644 --- a/src/generated/resources/assets/create/lang/unfinished/de_de.json +++ b/src/generated/resources/assets/create/lang/unfinished/de_de.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 1518", + "_": "Missing Localizations: 1520", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_cl.json b/src/generated/resources/assets/create/lang/unfinished/es_cl.json index 870271cc1..9e1714656 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_cl.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_cl.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 529", + "_": "Missing Localizations: 531", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_es.json b/src/generated/resources/assets/create/lang/unfinished/es_es.json index f94d8ccc4..4d8ce2cce 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_es.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_es.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 200", + "_": "Missing Localizations: 202", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json index b369c8a0c..5439e96dc 100644 --- a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json +++ b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 1780", + "_": "Missing Localizations: 1782", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/it_it.json b/src/generated/resources/assets/create/lang/unfinished/it_it.json index d68626acd..2979d5f2c 100644 --- a/src/generated/resources/assets/create/lang/unfinished/it_it.json +++ b/src/generated/resources/assets/create/lang/unfinished/it_it.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 1469", + "_": "Missing Localizations: 1471", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json index bf9206515..e1f304caf 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json +++ b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 195", + "_": "Missing Localizations: 197", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json index 9e15aa7e7..af9a03591 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json +++ b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 195", + "_": "Missing Localizations: 197", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json index 6eeb25223..16d6de968 100644 --- a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json +++ b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 2133", + "_": "Missing Localizations: 2135", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json index fc519ef79..de061929f 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json +++ b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 568", + "_": "Missing Localizations: 570", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_br.json b/src/generated/resources/assets/create/lang/unfinished/pt_br.json index d3c6d1d77..3e5e54cf4 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_br.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_br.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 1380", + "_": "Missing Localizations: 1382", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json index 42cac3f8d..2d6ba9725 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 1752", + "_": "Missing Localizations: 1754", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/ro_ro.json b/src/generated/resources/assets/create/lang/unfinished/ro_ro.json index f8a0fb3f4..f910838be 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ro_ro.json +++ b/src/generated/resources/assets/create/lang/unfinished/ro_ro.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 196", + "_": "Missing Localizations: 198", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json index 42f0900ec..3186ee41b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json +++ b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 573", + "_": "Missing Localizations: 575", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json index 1d7b3a8ab..6f3a1c6b7 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 195", + "_": "Missing Localizations: 197", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json index 7d8c3e2f7..53e3f0aea 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 587", + "_": "Missing Localizations: 589", "_": "->------------------------] Game Elements [------------------------<-", @@ -1413,6 +1413,8 @@ "create.track.leave_slope_ascending": "UNLOCALIZED: Cannot leave this slope while ascending", "create.track.leave_slope_descending": "UNLOCALIZED: Cannot leave this slope while descending", "create.track.turn_90": "UNLOCALIZED: Can only turn up to 90 Degrees", + "create.track.junction_start": "UNLOCALIZED: Cannot start connection from a Junction", + "create.track.turn_start": "UNLOCALIZED: Cannot start connection from a Turn", "create.station.create_train": "UNLOCALIZED: Create new Train", "create.station.disassemble_train": "UNLOCALIZED: Disassemble Train", diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index 1f18e70a1..a1e6f43a4 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -234,6 +234,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.properties.PistonType; +import net.minecraft.world.level.material.Material; import net.minecraft.world.level.material.MaterialColor; import net.minecraft.world.level.storage.loot.LootPool; import net.minecraft.world.level.storage.loot.LootTable; @@ -1336,7 +1337,10 @@ public class AllBlocks { .register(); public static final BlockEntry TRACK = REGISTRATE.block("track", TrackBlock::new) - .initialProperties(() -> Blocks.RAIL) + .initialProperties(Material.DECORATION) + .properties(p -> p.strength(0.8F) + .sound(SoundType.METAL) + .noOcclusion()) .addLayer(() -> RenderType::cutoutMipped) .transform(pickaxeOnly()) .blockstate(new TrackBlockStateGenerator()::generate) diff --git a/src/main/java/com/simibubi/create/AllShapes.java b/src/main/java/com/simibubi/create/AllShapes.java index ee27cd7be..d832e4109 100644 --- a/src/main/java/com/simibubi/create/AllShapes.java +++ b/src/main/java/com/simibubi/create/AllShapes.java @@ -178,6 +178,8 @@ public class AllShapes { TRACK_CROSS_DIAG = shape(TRACK_DIAG.get(SOUTH)).add(TRACK_DIAG.get(EAST)) .build(), + TRACK_COLLISION = shape(0, 0, 0, 16, 2, 16).build(), + TRACK_FALLBACK = shape(0, 0, 0, 16, 4, 16).build(), BASIN_BLOCK_SHAPE = shape(0, 2, 0, 16, 16, 16).erase(2, 2, 2, 14, 16, 14) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java index 2e64212b7..f053ca2a9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/BezierConnection.java @@ -17,6 +17,7 @@ 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.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -42,6 +43,8 @@ public class BezierConnection implements Iterable { private double radius; private double handleLength; + private AABB bounds; + public BezierConnection(Couple positions, Couple starts, Couple axes, Couple normals, boolean primary, boolean girder) { tePositions = positions; @@ -148,6 +151,11 @@ public class BezierConnection implements Iterable { return currentT + distance / dx; } + + public AABB getBounds() { + resolve(); + return bounds; + } public Vec3 getNormal(double t) { resolve(); @@ -202,12 +210,15 @@ public class BezierConnection implements Iterable { stepLUT[0] = 1; float combinedDistance = 0; + bounds = new AABB(end1, end2); + // 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); + bounds = bounds.minmax(new AABB(result, result)); if (i > 0) { combinedDistance += result.distanceTo(previous) / length; stepLUT[i] = (float) (t / combinedDistance); @@ -215,6 +226,8 @@ public class BezierConnection implements Iterable { previous = result; } } + + bounds = bounds.inflate(1.375f); } private void determineHandles(Vec3 end1, Vec3 end2, Vec3 axis1, Vec3 axis2) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java index 10da02eb4..eeb570bf8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java @@ -12,6 +12,7 @@ import com.jozufozu.flywheel.core.PartialModel; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour.RenderedTrackOverlayType; +import com.simibubi.create.content.logistics.trains.track.BezierTrackPointLocation; import com.simibubi.create.content.logistics.trains.track.TrackBlock; import com.simibubi.create.content.logistics.trains.track.TrackShape; import com.simibubi.create.foundation.utility.Iterate; @@ -105,7 +106,7 @@ public interface ITrackBlock { @OnlyIn(Dist.CLIENT) public PartialModel prepareTrackOverlay(BlockGetter world, BlockPos pos, BlockState state, - AxisDirection direction, PoseStack transform, RenderedTrackOverlayType type); + BezierTrackPointLocation bezierPoint, AxisDirection direction, PoseStack transform, RenderedTrackOverlayType type); @OnlyIn(Dist.CLIENT) public PartialModel prepareAssemblyOverlay(BlockGetter world, BlockPos pos, BlockState state, Direction direction, diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/CurvedTrackSelectionPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/CurvedTrackSelectionPacket.java new file mode 100644 index 000000000..b89d831bb --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/CurvedTrackSelectionPacket.java @@ -0,0 +1,88 @@ +package com.simibubi.create.content.logistics.trains.management.edgePoint; + +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.track.TrackTileEntity; +import com.simibubi.create.foundation.networking.TileEntityConfigurationPacket; +import com.simibubi.create.foundation.utility.Lang; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +public class CurvedTrackSelectionPacket extends TileEntityConfigurationPacket { + + private BlockPos targetPos; + private boolean front; + private int segment; + private int slot; + + public CurvedTrackSelectionPacket(BlockPos pos, BlockPos targetPos, int segment, boolean front, int slot) { + super(pos); + this.targetPos = targetPos; + this.segment = segment; + this.front = front; + this.slot = slot; + } + + public CurvedTrackSelectionPacket(FriendlyByteBuf buffer) { + super(buffer); + } + + @Override + protected void writeSettings(FriendlyByteBuf buffer) { + buffer.writeBlockPos(targetPos); + buffer.writeVarInt(segment); + buffer.writeBoolean(front); + buffer.writeVarInt(slot); + } + + @Override + protected void readSettings(FriendlyByteBuf buffer) { + targetPos = buffer.readBlockPos(); + segment = buffer.readVarInt(); + front = buffer.readBoolean(); + slot = buffer.readVarInt(); + } + + @Override + protected void applySettings(ServerPlayer player, TrackTileEntity te) { + if (!te.getBlockPos() + .closerThan(player.blockPosition(), 48)) { + Create.LOGGER.warn(player.getScoreboardName() + " too far away from targeted track"); + return; + } + + if (player.getInventory().selected != slot) + return; + ItemStack stack = player.getInventory() + .getItem(slot); + if (!(stack.getItem() instanceof TrackTargetingBlockItem)) + return; + + if (player.isSteppingCarefully() && stack.hasTag()) { + player.displayClientMessage(Lang.translate("track_target.clear"), true); + stack.setTag(null); + return; + } + + CompoundTag stackTag = stack.getOrCreateTag(); + stackTag.put("SelectedPos", NbtUtils.writeBlockPos(pos)); + stackTag.putBoolean("SelectedDirection", front); + + CompoundTag bezierNbt = new CompoundTag(); + bezierNbt.putInt("Segment", segment); + bezierNbt.put("Key", NbtUtils.writeBlockPos(targetPos)); + bezierNbt.putBoolean("FromStack", true); + stackTag.put("Bezier", bezierNbt); + + player.displayClientMessage(Lang.translate("track_target.set"), true); + stack.setTag(stackTag); + } + + @Override + protected void applySettings(TrackTileEntity te) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java index cbb3d6f74..5fbeb85f7 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java @@ -6,19 +6,25 @@ import javax.annotation.Nullable; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.GraphLocation; 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.TrackGraphHelper; import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.TrackNodeLocation; +import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SingleTileEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; +import com.simibubi.create.content.logistics.trains.track.BezierTrackPointLocation; +import com.simibubi.create.content.logistics.trains.track.TrackTileEntity; 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 net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.MultiBufferSource; @@ -39,6 +45,7 @@ public class TrackTargetingBehaviour extends TileEntit public static final BehaviourType> TYPE = new BehaviourType<>(); private BlockPos targetTrack; + private BezierTrackPointLocation targetBezier; private AxisDirection targetDirection; private UUID id; @@ -62,6 +69,13 @@ public class TrackTargetingBehaviour extends TileEntit nbt.putBoolean("TargetDirection", targetDirection == AxisDirection.POSITIVE); if (migrationData != null && !clientPacket) nbt.put("Migrate", migrationData); + if (targetBezier != null) { + CompoundTag bezierNbt = new CompoundTag(); + bezierNbt.putInt("Segment", targetBezier.segment()); + bezierNbt.put("Key", NbtUtils.writeBlockPos(targetBezier.curveTarget() + .subtract(getPos()))); + nbt.put("Bezier", bezierNbt); + } super.write(nbt, clientPacket); } @@ -74,6 +88,12 @@ public class TrackTargetingBehaviour extends TileEntit migrationData = nbt.getCompound("Migrate"); if (clientPacket) edgePoint = null; + if (nbt.contains("Bezier")) { + CompoundTag bezierNbt = nbt.getCompound("Bezier"); + BlockPos key = NbtUtils.readBlockPos(bezierNbt.getCompound("Key")); + targetBezier = new BezierTrackPointLocation(bezierNbt.contains("FromStack") ? key : key.offset(getPos()), + bezierNbt.getInt("Segment")); + } super.read(nbt, clientPacket); } @@ -195,7 +215,13 @@ public class TrackTargetingBehaviour extends TileEntit return targetDirection; } + public BezierTrackPointLocation getTargetBezier() { + return targetBezier; + } + public GraphLocation determineGraphLocation() { + if (targetBezier != null) + return determineBezierGraphLocation(); Level level = getWorld(); BlockPos pos = getGlobalPosition(); BlockState state = getTrackBlockState(); @@ -204,13 +230,57 @@ public class TrackTargetingBehaviour extends TileEntit .get(0)); } + public GraphLocation determineBezierGraphLocation() { + Level level = getWorld(); + BlockPos pos = getGlobalPosition(); + BlockState state = getTrackBlockState(); + if (!(state.getBlock() instanceof ITrackBlock track)) + return null; + if (!(level.getBlockEntity(pos) instanceof TrackTileEntity trackTE)) + return null; + BezierConnection bc = trackTE.getConnections() + .get(targetBezier.curveTarget()); + if (bc == null || !bc.isPrimary()) + return null; + + TrackNodeLocation targetLoc = new TrackNodeLocation(bc.starts.getSecond()); + for (DiscoveredLocation location : track.getConnected(level, pos, state, true, null)) { + TrackGraph graph = Create.RAILWAYS.sided(level) + .getGraph(level, location); + if (graph == null) + continue; + TrackNode targetNode = graph.locateNode(targetLoc); + if (targetNode == null) + continue; + TrackNode node = graph.locateNode(location); + TrackEdge edge = graph.getConnectionsFrom(node) + .get(targetNode); + if (edge == null) + continue; + + GraphLocation graphLocation = new GraphLocation(); + graphLocation.graph = graph; + graphLocation.edge = Couple.create(location, targetLoc); + graphLocation.position = (targetBezier.segment() + 1) / 2f; + if (targetDirection == AxisDirection.POSITIVE) { + graphLocation.edge = graphLocation.edge.swap(); + graphLocation.position = edge.getLength(node, targetNode) - graphLocation.position; + } + + return graphLocation; + } + + return null; + } + public static enum RenderedTrackOverlayType { STATION, SIGNAL, DUAL_SIGNAL; } @OnlyIn(Dist.CLIENT) - public static void render(LevelAccessor level, BlockPos pos, AxisDirection direction, int tintColor, PoseStack ms, - MultiBufferSource buffer, int light, int overlay, RenderedTrackOverlayType type) { + public static void render(LevelAccessor level, BlockPos pos, AxisDirection direction, + BezierTrackPointLocation bezier, int tintColor, PoseStack ms, MultiBufferSource buffer, int light, int overlay, + RenderedTrackOverlayType type) { BlockState trackState = level.getBlockState(pos); Block block = trackState.getBlock(); if (!(block instanceof ITrackBlock)) @@ -220,8 +290,8 @@ public class TrackTargetingBehaviour extends TileEntit ms.translate(pos.getX(), pos.getY(), pos.getZ()); ITrackBlock track = (ITrackBlock) block; - SuperByteBuffer sbb = - CachedBufferer.partial(track.prepareTrackOverlay(level, pos, trackState, direction, ms, type), trackState); + SuperByteBuffer sbb = CachedBufferer + .partial(track.prepareTrackOverlay(level, pos, trackState, bezier, direction, ms, type), trackState); sbb.light(LevelRenderer.getLightColor(level, pos)); sbb.renderInto(ms, buffer.getBuffer(RenderType.cutoutMipped())); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java index 63c8957db..c3422b5b8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java @@ -2,10 +2,16 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.content.logistics.trains.ITrackBlock; +import com.simibubi.create.content.logistics.trains.track.BezierTrackPointLocation; +import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline.BezierPointSelection; +import com.simibubi.create.content.logistics.trains.track.TrackTileEntity; +import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.render.SuperRenderTypeBuffer; import com.simibubi.create.foundation.utility.Lang; import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction.AxisDirection; import net.minecraft.nbt.CompoundTag; @@ -19,6 +25,8 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; public class TrackTargetingBlockItem extends BlockItem { @@ -45,8 +53,9 @@ public class TrackTargetingBlockItem extends BlockItem { return InteractionResult.SUCCESS; } - if (state.getBlock()instanceof ITrackBlock track) { - if (track.getTrackAxes(level, pos, state).size() > 1) { + if (state.getBlock() instanceof ITrackBlock track) { + if (track.getTrackAxes(level, pos, state) + .size() > 1) { player.displayClientMessage(Lang.translate("track_target.no_junctions") .withStyle(ChatFormatting.RED), true); return InteractionResult.FAIL; @@ -84,6 +93,9 @@ public class TrackTargetingBlockItem extends BlockItem { return InteractionResult.FAIL; } + if (tag.contains("Bezier")) + teTag.put("Bezier", tag.getCompound("Bezier")); + teTag.put("TargetTrack", NbtUtils.writeBlockPos(selectedPos.subtract(placedPos))); tag.put("BlockEntityTag", teTag); @@ -99,6 +111,20 @@ public class TrackTargetingBlockItem extends BlockItem { return useOn; } + @OnlyIn(Dist.CLIENT) + public boolean useOnCurve(BezierPointSelection selection, ItemStack stack) { + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + TrackTileEntity te = selection.te(); + BezierTrackPointLocation loc = selection.loc(); + boolean front = player.getLookAngle() + .dot(selection.direction()) < 0; + + AllPackets.channel.sendToServer(new CurvedTrackSelectionPacket(te.getBlockPos(), loc.curveTarget(), + loc.segment(), front, player.getInventory().selected)); + return true; + } + public static void clientTick() { } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalRenderer.java index 0a3972e85..4137b8ec0 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalRenderer.java @@ -53,8 +53,8 @@ public class SignalRenderer extends SafeTileEntityRenderer { ms.pushPose(); ms.translate(-pos.getX(), -pos.getY(), -pos.getZ()); - TrackTargetingBehaviour.render(level, targetPosition, target.getTargetDirection(), 0xd0cccc, ms, buffer, light, - overlay, + TrackTargetingBehaviour.render(level, targetPosition, target.getTargetDirection(), target.getTargetBezier(), + 0xd0cccc, ms, buffer, light, overlay, overlayState == OverlayState.DUAL ? RenderedTrackOverlayType.DUAL_SIGNAL : RenderedTrackOverlayType.SIGNAL); ms.popPose(); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationRenderer.java index 55239454b..c6539ba8d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationRenderer.java @@ -63,8 +63,8 @@ public class StationRenderer extends SafeTileEntityRenderer { partialTicks, ms, buffer, light, overlay); ms.pushPose(); ms.translate(-pos.getX(), -pos.getY(), -pos.getZ()); - TrackTargetingBehaviour.render(level, targetPosition, target.getTargetDirection(), 0xCC993B, ms, buffer, - light, overlay, RenderedTrackOverlayType.STATION); + TrackTargetingBehaviour.render(level, targetPosition, target.getTargetDirection(), target.getTargetBezier(), + 0xCC993B, ms, buffer, light, overlay, RenderedTrackOverlayType.STATION); ms.popPose(); return; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java index 19be8f685..abeb14452 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java @@ -319,7 +319,7 @@ public class ScheduleScreen extends AbstractSimiContainerScreen { + + private BlockPos targetPos; + private BlockPos soundSource; + + public CurvedTrackDestroyPacket(BlockPos pos, BlockPos targetPos, BlockPos soundSource) { + super(pos); + this.targetPos = targetPos; + this.soundSource = soundSource; + } + + public CurvedTrackDestroyPacket(FriendlyByteBuf buffer) { + super(buffer); + } + + @Override + protected void writeSettings(FriendlyByteBuf buffer) { + buffer.writeBlockPos(targetPos); + buffer.writeBlockPos(soundSource); + } + + @Override + protected void readSettings(FriendlyByteBuf buffer) { + targetPos = buffer.readBlockPos(); + soundSource = buffer.readBlockPos(); + } + + @Override + protected void applySettings(ServerPlayer player, TrackTileEntity te) { + if (!te.getBlockPos() + .closerThan(player.blockPosition(), 48)) { + Create.LOGGER.warn(player.getScoreboardName() + " too far away from destroyed Curve track"); + return; + } + + Level level = te.getLevel(); + te.removeConnection(targetPos); + if (level.getBlockEntity(targetPos) instanceof TrackTileEntity other) + other.removeConnection(pos); + + BlockState blockState = te.getBlockState(); + TrackPropagator.onRailRemoved(level, pos, blockState); + + SoundType soundtype = blockState.getSoundType(level, pos, player); + if (soundtype == null) + return; + + level.playSound(null, soundSource, soundtype.getBreakSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); + } + + @Override + protected void applySettings(TrackTileEntity te) {} + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/CurvedTrackInteraction.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/CurvedTrackInteraction.java new file mode 100644 index 000000000..305d0ec1f --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/CurvedTrackInteraction.java @@ -0,0 +1,132 @@ +package com.simibubi.create.content.logistics.trains.track; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem; +import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline.BezierPointSelection; +import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.BlockParticleOption; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.client.event.InputEvent.ClickInputEvent; + +public class CurvedTrackInteraction { + + static final int breakerId = new Object().hashCode(); + + static int breakTicks; + static int breakTimeout; + static float breakProgress; + static BlockPos breakPos; + + public static void clientTick() { + BezierPointSelection result = TrackBlockOutline.result; + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + ClientLevel level = mc.level; + + if (mc.options.keyAttack.isDown() && result != null) { + breakPos = result.te() + .getBlockPos(); + BlockState blockState = level.getBlockState(breakPos); + if (blockState.isAir()) { + resetBreakProgress(); + return; + } + + if (breakTicks % 4.0F == 0.0F) { + SoundType soundtype = blockState.getSoundType(level, breakPos, player); + mc.getSoundManager() + .play(new SimpleSoundInstance(soundtype.getHitSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 8.0F, soundtype.getPitch() * 0.5F, + new BlockPos(result.vec()))); + } + + boolean creative = player.getAbilities().instabuild; + + breakTicks++; + breakTimeout = 2; + breakProgress += creative ? 0.25f : blockState.getDestroyProgress(player, level, breakPos); + + Vec3 vec = VecHelper.offsetRandomly(result.vec(), level.random, 0.25f); + level.addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockState), vec.x, vec.y, vec.z, 0, 0, 0); + + int progress = (int) (breakProgress * 10.0F) - 1; + level.destroyBlockProgress(player.getId(), breakPos, progress); + player.swing(InteractionHand.MAIN_HAND); + + if (breakProgress >= 1) { + AllPackets.channel.sendToServer(new CurvedTrackDestroyPacket(breakPos, result.loc() + .curveTarget(), new BlockPos(result.vec()))); + resetBreakProgress(); + } + + return; + } + + if (breakTimeout == 0) + return; + if (--breakTimeout > 0) + return; + + resetBreakProgress(); + } + + private static void resetBreakProgress() { + Minecraft mc = Minecraft.getInstance(); + ClientLevel level = mc.level; + + if (breakPos != null && level != null) + level.destroyBlockProgress(mc.player.getId(), breakPos, -1); + + breakProgress = 0; + breakTicks = 0; + breakPos = null; + } + + public static boolean onClickInput(ClickInputEvent event) { + BezierPointSelection result = TrackBlockOutline.result; + if (result == null) + return false; + + Minecraft mc = Minecraft.getInstance(); + LocalPlayer player = mc.player; + ClientLevel level = mc.level; + + if (player == null || level == null) + return false; + + if (event.isUseItem()) { + ItemStack heldItem = player.getMainHandItem(); + if (AllBlocks.TRACK.isIn(heldItem)) { + player.displayClientMessage(Lang.translate("track.turn_start") + .withStyle(ChatFormatting.RED), true); + player.swing(InteractionHand.MAIN_HAND); + return true; + } + if (heldItem.getItem() instanceof TrackTargetingBlockItem ttbi && ttbi.useOnCurve(result, heldItem)) { + player.swing(InteractionHand.MAIN_HAND); + return true; + } + } + + if (event.isAttack()) + return true; + + return false; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java index ad776cd5b..9177332e8 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java @@ -32,11 +32,14 @@ import com.simibubi.create.content.logistics.trains.TrackNodeLocation.Discovered import com.simibubi.create.content.logistics.trains.TrackPropagator; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour.RenderedTrackOverlayType; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity; +import com.simibubi.create.foundation.block.render.DestroyProgressRenderingHandler; import com.simibubi.create.foundation.block.render.ReducedDestroyEffects; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.VecHelper; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; @@ -68,6 +71,7 @@ import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.ticks.LevelTickAccess; import net.minecraftforge.api.distmarker.Dist; @@ -92,7 +96,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @OnlyIn(Dist.CLIENT) public void initializeClient(Consumer consumer) { - consumer.accept(new ReducedDestroyEffects()); + consumer.accept(new RenderProperties()); } @Override @@ -318,6 +322,17 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac return AllShapes.TRACK_FALLBACK; } + @Override + public VoxelShape getCollisionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, + CollisionContext pContext) { + switch (pState.getValue(SHAPE)) { + case AE, AW, AN, AS: + return Shapes.empty(); + default: + return AllShapes.TRACK_COLLISION; + } + } + @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState state) { if (!state.getValue(HAS_TURN)) @@ -419,24 +434,60 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @Override @OnlyIn(Dist.CLIENT) - public PartialModel prepareTrackOverlay(BlockGetter world, BlockPos pos, BlockState state, AxisDirection direction, - PoseStack ms, RenderedTrackOverlayType type) { - Vec3 axis = state.getValue(SHAPE) - .getAxes() - .get(0); - Vec3 directionVec = axis.scale(direction.getStep()) - .normalize(); - Vec3 normal = getUpNormal(world, pos, state); - Vec3 angles = TrackRenderer.getModelAngles(normal, directionVec); + public PartialModel prepareTrackOverlay(BlockGetter world, BlockPos pos, BlockState state, + BezierTrackPointLocation bezierPoint, AxisDirection direction, PoseStack ms, RenderedTrackOverlayType type) { + TransformStack msr = TransformStack.cast(ms); - TransformStack.cast(ms) - .centre() + Vec3 axis = null; + Vec3 diff = null; + Vec3 normal = null; + Vec3 offset = null; + + if (bezierPoint != null && world.getBlockEntity(pos) instanceof TrackTileEntity trackTE) { + BezierConnection bc = trackTE.connections.get(bezierPoint.curveTarget()); + if (bc != null) { + double length = Mth.floor(bc.getLength() * 2); + int seg = bezierPoint.segment() + 1; + double t = seg / length; + double tpre = (seg - 1) / length; + double tpost = (seg + 1) / length; + + offset = bc.getPosition(t); + normal = bc.getNormal(t); + diff = bc.getPosition(tpost) + .subtract(bc.getPosition(tpre)) + .normalize(); + + msr.translate(offset.subtract(Vec3.atBottomCenterOf(pos))); + msr.translate(0, -4 / 16f, 0); + } + } + + if (normal == null) { + axis = state.getValue(SHAPE) + .getAxes() + .get(0); + diff = axis.scale(direction.getStep()) + .normalize(); + normal = getUpNormal(world, pos, state); + } + + Vec3 angles = TrackRenderer.getModelAngles(normal, diff); + + msr.centre() .rotateYRadians(angles.y) .rotateXRadians(angles.x) - .unCentre() - .translate(0, axis.y != 0 ? 7 / 16f : 0, axis.y != 0 ? direction.getStep() * 2.5f / 16f : 0) - .scale(type == RenderedTrackOverlayType.STATION ? 1 + 1 / 512f : 1); + .unCentre(); + if (axis != null) + msr.translate(0, axis.y != 0 ? 7 / 16f : 0, axis.y != 0 ? direction.getStep() * 2.5f / 16f : 0); + else { + msr.translate(0, 4 / 16f, 0); + if (direction == AxisDirection.NEGATIVE) + msr.rotateCentered(Direction.UP, Mth.PI); + } + + msr.scale(type == RenderedTrackOverlayType.STATION ? 1 + 1 / 512f : 1); return type == RenderedTrackOverlayType.STATION ? AllBlockPartials.TRACK_STATION_OVERLAY : type == RenderedTrackOverlayType.SIGNAL ? AllBlockPartials.TRACK_SIGNAL_OVERLAY : AllBlockPartials.TRACK_SIGNAL_DUAL_OVERLAY; @@ -448,4 +499,16 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac && state1.setValue(HAS_TURN, false) == state2.setValue(HAS_TURN, false); } + public static class RenderProperties extends ReducedDestroyEffects implements DestroyProgressRenderingHandler { + @Override + public boolean renderDestroyProgress(ClientLevel level, LevelRenderer renderer, int breakerId, BlockPos pos, + int progress, BlockState blockState) { + BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof TrackTileEntity track) + for (BlockPos trackPos : track.connections.keySet()) + renderer.destroyBlockProgress(trackPos.hashCode(), trackPos, progress); + return false; + } + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockItem.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockItem.java index e256caa45..84bb35603 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockItem.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockItem.java @@ -8,6 +8,7 @@ import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.VecHelper; +import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction.AxisDirection; import net.minecraft.nbt.CompoundTag; @@ -45,6 +46,14 @@ public class TrackBlockItem extends BlockItem { Vec3 lookAngle = player.getLookAngle(); if (!isFoil(stack)) { + if (state.getBlock() instanceof TrackBlock track && track.getTrackAxes(level, pos, state) + .size() > 1) { + if (!level.isClientSide) + player.displayClientMessage(Lang.translate("track.junction_start") + .withStyle(ChatFormatting.RED), true); + return InteractionResult.SUCCESS; + } + if (select(level, pos, lookAngle, stack)) return InteractionResult.SUCCESS; return super.useOn(pContext); @@ -81,7 +90,7 @@ public class TrackBlockItem extends BlockItem { if (level.isClientSide) return InteractionResult.SUCCESS; - if (offhandItem.getItem()instanceof BlockItem blockItem) { + if (offhandItem.getItem() instanceof BlockItem blockItem) { Block block = blockItem.getBlock(); if (block == null) return InteractionResult.SUCCESS; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java index 31fc38a82..9c3552b1c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java @@ -1,31 +1,165 @@ package com.simibubi.create.content.logistics.trains.track; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import com.jozufozu.flywheel.util.transform.TransformStack; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; +import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.foundation.utility.AngleHelper; +import com.simibubi.create.foundation.utility.AnimationTickHolder; +import com.simibubi.create.foundation.utility.RaycastHelper; +import com.simibubi.create.foundation.utility.VecHelper; +import com.simibubi.create.foundation.utility.WorldAttached; import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; import net.minecraft.util.Mth; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.DrawSelectionEvent; +import net.minecraftforge.common.ForgeMod; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; @EventBusSubscriber(Dist.CLIENT) public class TrackBlockOutline { + public static WorldAttached> TRACKS_WITH_TURNS = + new WorldAttached<>(w -> new HashMap<>()); + + static BezierPointSelection result; + + public static void pickCurves() { + Minecraft mc = Minecraft.getInstance(); + if (!(mc.cameraEntity instanceof LocalPlayer player)) + return; + if (mc.level == null) + return; + + Vec3 origin = player.getEyePosition(AnimationTickHolder.getPartialTicks(mc.level)); + + double maxRange = mc.hitResult == null ? Double.MAX_VALUE + : mc.hitResult.getLocation() + .distanceToSqr(origin); + + result = null; + + AttributeInstance range = player.getAttribute(ForgeMod.REACH_DISTANCE.get()); + Vec3 target = RaycastHelper.getTraceTarget(player, Math.min(maxRange, range.getValue()) + 1, origin); + Map turns = TRACKS_WITH_TURNS.get(mc.level); + + for (TrackTileEntity te : turns.values()) { + for (BezierConnection bc : te.connections.values()) { + if (!bc.isPrimary()) + continue; + + AABB bounds = bc.getBounds(); + if (!bounds.contains(origin) && bounds.clip(origin, target) + .isEmpty()) + continue; + + float[] stepLUT = bc.getStepLUT(); + int segments = (int) (bc.getLength() * 2); + AABB segmentBounds = AllShapes.TRACK_ORTHO.get(Direction.SOUTH) + .bounds(); + segmentBounds = segmentBounds.move(-.5, segmentBounds.getYsize() / -2, -.5); + + int bestSegment = -1; + double bestDistance = Double.MAX_VALUE; + double newMaxRange = maxRange; + + for (int i = 0; i < stepLUT.length - 2; i++) { + float t = stepLUT[i] * i / segments; + float t1 = stepLUT[i + 1] * (i + 1) / segments; + float t2 = stepLUT[i + 2] * (i + 2) / segments; + + Vec3 v1 = bc.getPosition(t); + Vec3 v2 = bc.getPosition(t2); + Vec3 diff = v2.subtract(v1); + Vec3 angles = TrackRenderer.getModelAngles(bc.getNormal(t1), diff); + + Vec3 anchor = v1.add(diff.scale(.5)); + Vec3 localOrigin = origin.subtract(anchor); + Vec3 localDirection = target.subtract(origin); + localOrigin = VecHelper.rotate(localOrigin, AngleHelper.deg(-angles.x), Axis.X); + localOrigin = VecHelper.rotate(localOrigin, AngleHelper.deg(-angles.y), Axis.Y); + localDirection = VecHelper.rotate(localDirection, AngleHelper.deg(-angles.x), Axis.X); + localDirection = VecHelper.rotate(localDirection, AngleHelper.deg(-angles.y), Axis.Y); + + Optional clip = segmentBounds.clip(localOrigin, localOrigin.add(localDirection)); + if (clip.isEmpty()) + continue; + + if (bestSegment != -1 && bestDistance < clip.get() + .distanceToSqr(0, 0.25f, 0)) + continue; + + double distanceToSqr = clip.get() + .distanceToSqr(localOrigin); + if (distanceToSqr > maxRange) + continue; + + bestSegment = i; + newMaxRange = distanceToSqr; + bestDistance = clip.get() + .distanceToSqr(0, 0.25f, 0); + + BezierTrackPointLocation location = new BezierTrackPointLocation(bc.getKey(), i); + result = new BezierPointSelection(te, location, anchor, angles, diff.normalize()); + } + + if (bestSegment != -1) + maxRange = newMaxRange; + } + } + + if (result == null) + return; + + if (mc.hitResult != null && mc.hitResult.getType() != Type.MISS) { + Vec3 priorLoc = mc.hitResult.getLocation(); + mc.hitResult = BlockHitResult.miss(priorLoc, Direction.UP, new BlockPos(priorLoc)); + } + } + + public static void drawCurveSelection(PoseStack ms, MultiBufferSource buffer) { + BezierPointSelection result = TrackBlockOutline.result; + if (result == null) + return; + + VertexConsumer vb = buffer.getBuffer(RenderType.lines()); + Vec3 vec = result.vec(); + Vec3 angles = result.angles(); + TransformStack.cast(ms) + .pushPose() + .translate(vec.x, vec.y + .125f, vec.z) + .rotateYRadians(angles.y) + .rotateXRadians(angles.x) + .translate(-.5, -.125f, -.5); + + boolean holdingTrack = AllBlocks.TRACK.isIn(Minecraft.getInstance().player.getMainHandItem()); + renderShape(AllShapes.TRACK_ORTHO.get(Direction.SOUTH), ms, vb, holdingTrack ? false : null); + ms.popPose(); + } + @SubscribeEvent public static void drawCustomBlockSelection(DrawSelectionEvent.HighlightBlock event) { Minecraft mc = Minecraft.getInstance(); @@ -41,41 +175,63 @@ public class TrackBlockOutline { VertexConsumer vb = event.getMultiBufferSource() .getBuffer(RenderType.lines()); - PoseStack ms = event.getPoseStack(); - - ms.pushPose(); Vec3 camPos = event.getCamera() .getPosition(); + PoseStack ms = event.getPoseStack(); + + ms.pushPose(); ms.translate(pos.getX() - camPos.x, pos.getY() - camPos.y, pos.getZ() - camPos.z); - walkShapes(blockstate.getValue(TrackBlock.SHAPE), TransformStack.cast(ms), s -> { + boolean holdingTrack = AllBlocks.TRACK.isIn(Minecraft.getInstance().player.getMainHandItem()); + TrackShape shape = blockstate.getValue(TrackBlock.SHAPE); + boolean isJunction = shape.isJunction(); + walkShapes(shape, TransformStack.cast(ms), s -> { + renderShape(s, ms, vb, holdingTrack ? !isJunction : null); event.setCanceled(true); - PoseStack.Pose transform = ms.last(); - s.forAllEdges((x1, y1, z1, x2, y2, z2) -> { - float xDiff = (float) (x2 - x1); - float yDiff = (float) (y2 - y1); - float zDiff = (float) (z2 - z1); - float length = Mth.sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); - - xDiff /= length; - yDiff /= length; - zDiff /= length; - - vb.vertex(transform.pose(), (float) x1, (float) y1, (float) z1) - .color(0f, 0f, 0f, .4f) - .normal(transform.normal(), xDiff, yDiff, zDiff) - .endVertex(); - vb.vertex(transform.pose(), (float) x2, (float) y2, (float) z2) - .color(0f, 0f, 0f, .4f) - .normal(transform.normal(), xDiff, yDiff, zDiff) - .endVertex(); - - }); }); ms.popPose(); + } + private static void renderShape(VoxelShape s, PoseStack ms, VertexConsumer vb, Boolean valid) { + PoseStack.Pose transform = ms.last(); + s.forAllEdges((x1, y1, z1, x2, y2, z2) -> { + float xDiff = (float) (x2 - x1); + float yDiff = (float) (y2 - y1); + float zDiff = (float) (z2 - z1); + float length = Mth.sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); + + xDiff /= length; + yDiff /= length; + zDiff /= length; + + float r = 0f; + float g = 0f; + float b = 0f; + + if (valid != null && valid) { + g = 1f; + b = 1f; + r = 1f; + } + + if (valid != null && !valid) { + r = 1f; + b = 0.125f; + g = 0.25f; + } + + vb.vertex(transform.pose(), (float) x1, (float) y1, (float) z1) + .color(r, g, b, .4f) + .normal(transform.normal(), xDiff, yDiff, zDiff) + .endVertex(); + vb.vertex(transform.pose(), (float) x2, (float) y2, (float) z2) + .color(r, g, b, .4f) + .normal(transform.normal(), xDiff, yDiff, zDiff) + .endVertex(); + + }); } private static final VoxelShape LONG_CROSS = @@ -115,4 +271,8 @@ public class TrackBlockOutline { renderer.accept(LONG_ORTHO); } + public static record BezierPointSelection(TrackTileEntity te, BezierTrackPointLocation loc, Vec3 vec, Vec3 angles, + Vec3 direction) { + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java index f7182e0f1..2d0873d0b 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java @@ -334,7 +334,7 @@ public class TrackPlacement { boolean canPlace = stateAtPos.getMaterial() .isReplaceable(); - if (stateAtPos.getBlock()instanceof ITrackBlock trackAtPos) { + if (stateAtPos.getBlock() instanceof ITrackBlock trackAtPos) { toPlace = trackAtPos.overlay(level, offsetPos, stateAtPos, toPlace); canPlace = true; } @@ -413,7 +413,8 @@ public class TrackPlacement { .withStyle(ChatFormatting.GREEN), true); else if (info.message != null) player.displayClientMessage(Lang.translate(info.message) - .withStyle(ChatFormatting.RED), true); + .withStyle(info.message.equals("track.second_point") ? ChatFormatting.WHITE : ChatFormatting.RED), + true); animation.chase(info.valid ? 1 : 0, 0.25, Chaser.EXP); animation.tickChaser(); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java index 0fbf46901..016e0667c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java @@ -49,6 +49,13 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE return connections; } + @Override + public void initialize() { + super.initialize(); + if (!level.isClientSide && hasInteractableConnections()) + registerToCurveInteraction(); + } + private void validateConnections() { Set invalid = new HashSet<>(); for (Entry entry : connections.entrySet()) { @@ -63,7 +70,7 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE } connectionsValidated = true; - for (BlockPos blockPos : invalid) + for (BlockPos blockPos : invalid) removeConnection(blockPos); } @@ -117,6 +124,11 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE } DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> InstancedRenderDispatcher.enqueueUpdate(this)); + + if (hasInteractableConnections()) + registerToCurveInteraction(); + else + removeFromCurveInteraction(); } @Override @@ -136,6 +148,13 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE level.scheduleTick(worldPosition, getBlockState().getBlock(), 1); } + public boolean hasInteractableConnections() { + for (BezierConnection connection : connections.values()) + if (connection.isPrimary()) + return true; + return false; + } + @Override public void transform(StructureTransform transform) { if (transform.rotationAxis != Axis.Y) @@ -173,4 +192,31 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE connections = transformedConnections; } + @Override + public void setRemoved() { + super.setRemoved(); + if (level.isClientSide) + removeFromCurveInteraction(); + } + + private void registerToCurveInteraction() { + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::registerToCurveInteractionUnsafe); + } + + private void removeFromCurveInteraction() { + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::removeFromCurveInteractionUnsafe); + } + + @OnlyIn(Dist.CLIENT) + private void registerToCurveInteractionUnsafe() { + TrackBlockOutline.TRACKS_WITH_TURNS.get(level) + .put(worldPosition, this); + } + + @OnlyIn(Dist.CLIENT) + private void removeFromCurveInteractionUnsafe() { + TrackBlockOutline.TRACKS_WITH_TURNS.get(level) + .remove(worldPosition); + } + } diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 275797b7a..12cced589 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -38,6 +38,8 @@ import com.simibubi.create.content.logistics.trains.entity.CarriageCouplingRende import com.simibubi.create.content.logistics.trains.entity.TrainRelocator; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem; import com.simibubi.create.content.logistics.trains.management.schedule.TrainHatArmorLayer; +import com.simibubi.create.content.logistics.trains.track.CurvedTrackInteraction; +import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline; import com.simibubi.create.content.logistics.trains.track.TrackPlacement; import com.simibubi.create.content.logistics.trains.track.TrackRemoval; import com.simibubi.create.foundation.config.AllConfigs; @@ -159,6 +161,7 @@ public class ClientEvents { TrackRemoval.clientTick(); TrainRelocator.clientTick(); DataGathererBlockItem.clientTick(); + CurvedTrackInteraction.clientTick(); } @SubscribeEvent @@ -205,6 +208,7 @@ public class ClientEvents { ms.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z()); SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance(); + TrackBlockOutline.drawCurveSelection(ms, buffer); TrackTargetingBlockItem.render(ms, buffer); CouplingRenderer.renderAll(ms, buffer); CarriageCouplingRenderer.renderAll(ms, buffer); diff --git a/src/main/java/com/simibubi/create/events/InputEvents.java b/src/main/java/com/simibubi/create/events/InputEvents.java index 98b70788b..2b99dacc2 100644 --- a/src/main/java/com/simibubi/create/events/InputEvents.java +++ b/src/main/java/com/simibubi/create/events/InputEvents.java @@ -4,6 +4,7 @@ import com.simibubi.create.CreateClient; import com.simibubi.create.content.curiosities.toolbox.ToolboxHandlerClient; import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler; import com.simibubi.create.content.logistics.trains.entity.TrainRelocator; +import com.simibubi.create.content.logistics.trains.track.CurvedTrackInteraction; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringHandler; import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueHandler; @@ -60,6 +61,11 @@ public class InputEvents { public static void onClickInput(ClickInputEvent event) { if (Minecraft.getInstance().screen != null) return; + + if (CurvedTrackInteraction.onClickInput(event)) { + event.setCanceled(true); + return; + } if (event.getKeyMapping() == Minecraft.getInstance().options.keyPickItem) { if (ToolboxHandlerClient.onPickItem()) diff --git a/src/main/java/com/simibubi/create/foundation/mixin/GameRendererMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/GameRendererMixin.java index 34500a6c0..bf42cb7c3 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/GameRendererMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/GameRendererMixin.java @@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline; import com.simibubi.create.foundation.block.BigOutlines; import net.minecraft.client.renderer.GameRenderer; @@ -18,6 +19,7 @@ public class GameRendererMixin { @Inject(at = @At("TAIL"), method = "pick") private void bigShapePick(CallbackInfo ci) { BigOutlines.pick(); + TrackBlockOutline.pickCurves(); } } diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index 6af6c64f6..d412c64b7 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -49,11 +49,13 @@ import com.simibubi.create.content.logistics.packet.TunnelFlapPacket; import com.simibubi.create.content.logistics.trains.TrackGraphSyncPacket; import com.simibubi.create.content.logistics.trains.entity.TrainPacket; import com.simibubi.create.content.logistics.trains.entity.TrainRelocationPacket; +import com.simibubi.create.content.logistics.trains.management.edgePoint.CurvedTrackSelectionPacket; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroupPacket; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationEditPacket; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket.TrainEditReturnPacket; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEditPacket; +import com.simibubi.create.content.logistics.trains.track.CurvedTrackDestroyPacket; import com.simibubi.create.content.logistics.trains.track.TrackRemovalPacket; import com.simibubi.create.content.schematics.packet.ConfigureSchematicannonPacket; import com.simibubi.create.content.schematics.packet.InstantSchematicPacket; @@ -122,6 +124,8 @@ public enum AllPackets { CONTROLS_INPUT(ControlsInputPacket.class, ControlsInputPacket::new, PLAY_TO_SERVER), REMOVE_TRACKS(TrackRemovalPacket.class, TrackRemovalPacket::new, PLAY_TO_SERVER), CONFIGURE_DATA_GATHERER(DataGathererConfigurationPacket.class, DataGathererConfigurationPacket::new, PLAY_TO_SERVER), + DESTROY_CURVED_TRACK(CurvedTrackDestroyPacket.class, CurvedTrackDestroyPacket::new, PLAY_TO_SERVER), + SELECT_CURVED_TRACK(CurvedTrackSelectionPacket.class, CurvedTrackSelectionPacket::new, PLAY_TO_SERVER), // Server to Client SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index 1a5fa9861..f85bdc0c4 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -636,6 +636,8 @@ "create.track.leave_slope_ascending": "Cannot leave this slope while ascending", "create.track.leave_slope_descending": "Cannot leave this slope while descending", "create.track.turn_90": "Can only turn up to 90 Degrees", + "create.track.junction_start": "Cannot start connection from a Junction", + "create.track.turn_start": "Cannot start connection from a Turn", "create.station.create_train": "Create new Train", "create.station.disassemble_train": "Disassemble Train",