diff --git a/README.md b/README.md
index 8e864c398..3d8590c60 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
Create
-
+
@@ -19,7 +19,7 @@ Check out the wiki and in-game Tool-tips for further info on how to use these fe
[](https://www.patreon.com/simibubi "Support Us")
- Support for Minecraft 1.12: Not planned
-- Support for Minecraft 1.16: Porting efforts will begin soon.
+- Support for Minecraft 1.16: Porting efforts are making good progress.
- Support for Fabric: Not planned
Find out more about Create on our Project Page
diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache
index f79cecdda..1081deef7 100644
--- a/src/generated/resources/.cache/cache
+++ b/src/generated/resources/.cache/cache
@@ -368,6 +368,7 @@ a3a11524cd3515fc01d905767b4b7ea782adaf03 assets/create/blockstates/yellow_seat.j
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
c113d0a180880243538e9b1c3019c863df3fbdc1 assets/create/lang/en_ud.json
+<<<<<<< HEAD
a4cd12907a1ddfd60883077b2d11c5459d436016 assets/create/lang/en_us.json
063195daed96a4420588e6d6d13f4a9b1f099ff4 assets/create/lang/unfinished/de_de.json
30da89bafac8a5ea4d82903928ba4d2a63385117 assets/create/lang/unfinished/fr_fr.json
@@ -384,6 +385,24 @@ b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_windo
1358ff546e99004444fccca5355853d1738ef191 assets/create/models/block/acacia_window_pane_post.json
bd33b944ec6ad89850a1d275b3d2843fe6f831cc assets/create/models/block/acacia_window_pane_side.json
b272035cd746364a0bd3bf13061f51190e9b46d6 assets/create/models/block/acacia_window_pane_side_alt.json
+=======
+ee39bea21ca57e7b61f1af0d609d63cbbf8c8425 assets/create/lang/en_us.json
+233e7fae9df99bcb79d3e08f57e6d5e02bdf49ad assets/create/lang/unfinished/de_de.json
+d56a40fa6097af1badd3ab24bbcfa191ea5074ca assets/create/lang/unfinished/fr_fr.json
+1945bf0aebfc97e6f2ee1ac7e4dd754e7fd19fa5 assets/create/lang/unfinished/it_it.json
+3f76057ebb1c0e9a5c285fb79c1ef72ddec83a32 assets/create/lang/unfinished/ja_jp.json
+504d30d63a19e117c51be32f16c778bc1a7114c7 assets/create/lang/unfinished/ko_kr.json
+654efcd49befcac96bd632e65f0a36247bd0fc2e assets/create/lang/unfinished/nl_nl.json
+2c0f9c853b85a47b91877cd7b89faddc918f0170 assets/create/lang/unfinished/pt_br.json
+7193bf2573473b3fa8ffd7c8a6dd58ce069201a7 assets/create/lang/unfinished/ru_ru.json
+28b75a0dd8c021a7e385b3479b91b1edd47f13f4 assets/create/lang/unfinished/zh_cn.json
+846200eb548d3bfa2e77b41039de159b4b6cfb45 assets/create/models/block/acacia_window.json
+1930fa3a3c98d53dd19e4ee7f55bc27fd47aa281 assets/create/models/block/acacia_window_pane_noside.json
+1763ea2c9b981d187f5031ba608f3d5d3be3986a assets/create/models/block/acacia_window_pane_noside_alt.json
+7d23c8e4543108f5f53b78ccb4908f7a5bb76c54 assets/create/models/block/acacia_window_pane_post.json
+3bdcc6bd616a179ffc22e66307aab538e9bcb75f assets/create/models/block/acacia_window_pane_side.json
+7b5b6809e3ef685a497ba15b549d3918aeb6c135 assets/create/models/block/acacia_window_pane_side_alt.json
+>>>>>>> mc1.15/dev
7c81658c8839fdb23429e54f847c2a0aee0e866e assets/create/models/block/adjustable_pulley_end_horizontal.json
20cfb751fc395a0e07c66ce87ab035207f9d1e0e assets/create/models/block/adjustable_pulley_end_horizontal_powered.json
bb02f94b260be8fe1944b801cc4050b4d213f1df assets/create/models/block/adjustable_pulley_end_vertical.json
diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json
index 225ade75f..a08be7777 100644
--- a/src/generated/resources/assets/create/lang/en_us.json
+++ b/src/generated/resources/assets/create/lang/en_us.json
@@ -646,6 +646,12 @@
"create.blockzapper.leftClickToSet": "Left-Click a Block to set Material",
"create.blockzapper.empty": "Out of Blocks!",
+ "create.minecart_coupling.two_couplings_max": "Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "Minecarts are too far apart",
+
"create.contraptions.movement_mode": "Movement Mode",
"create.contraptions.movement_mode.move_place": "Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "Place only in Starting Position",
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 aec16a020..6ae217fe2 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: 853",
+ "_": "Missing Localizations: 858",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "Linksklick auf einen Block zum Auswählen",
"create.blockzapper.empty": "Keine Blöcke übrig!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "UNLOCALIZED: Movement Mode",
"create.contraptions.movement_mode.move_place": "UNLOCALIZED: Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "UNLOCALIZED: Place only in Starting Position",
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 cac22b50d..5a0f91088 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: 477",
+ "_": "Missing Localizations: 482",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "Clic gauche sur un bloc pour en définir le matériau",
"create.blockzapper.empty": "Plus de blocs!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "Mode de mouvement",
"create.contraptions.movement_mode.move_place": "Toujours placer à l'arrêt",
"create.contraptions.movement_mode.move_place_returned": "Placer uniquement en position de départ",
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 8bd21d4f1..31af5bfe2 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: 461",
+ "_": "Missing Localizations: 466",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "Clic-Sinistro su un blocco per impostare il materiale",
"create.blockzapper.empty": "Fuori dai Blocchi!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "Modalità Movimento",
"create.contraptions.movement_mode.move_place": "Posizionare Sempre quando è Fermo",
"create.contraptions.movement_mode.move_place_returned": "Posiziona solo nella Posizione Iniziale",
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 2344475a9..88744ce60 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: 456",
+ "_": "Missing Localizations: 461",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "ブロックをシフト-左クリックでマテリアルを選択",
"create.blockzapper.empty": "ブロック不足!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "移動モード",
"create.contraptions.movement_mode.move_place": "停止時に常に配置",
"create.contraptions.movement_mode.move_place_returned": "開始位置のみに配置",
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 cd6cdeed1..930ed34dc 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: 461",
+ "_": "Missing Localizations: 466",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "좌클릭으로 블럭 설정하기",
"create.blockzapper.empty": "블럭이 없습니다!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "이동 설정",
"create.contraptions.movement_mode.move_place": "멈췄을때 항상 블럭 설치하기",
"create.contraptions.movement_mode.move_place_returned": "멈췄을떄 최초 위치에서만 블럭 설치하기",
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 4f50be3b3..7542687a3 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: 791",
+ "_": "Missing Localizations: 796",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "Klik met links op een Blok om een Materiaal te kiezen",
"create.blockzapper.empty": "De Blokken zijn op!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "UNLOCALIZED: Movement Mode",
"create.contraptions.movement_mode.move_place": "UNLOCALIZED: Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "UNLOCALIZED: Place only in Starting Position",
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 8b67ae7a3..406db061d 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: 860",
+ "_": "Missing Localizations: 865",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "Botão-Esquerdo em um Bloco para selecionar Material",
"create.blockzapper.empty": "Sem Blocos!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "UNLOCALIZED: Movement Mode",
"create.contraptions.movement_mode.move_place": "UNLOCALIZED: Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "UNLOCALIZED: Place only in Starting Position",
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 cc0e86c5a..cf68d0a0d 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: 854",
+ "_": "Missing Localizations: 859",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "ЛКМ на блок, чтобы выбрать материал",
"create.blockzapper.empty": "Закончились блоки!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "UNLOCALIZED: Movement Mode",
"create.contraptions.movement_mode.move_place": "UNLOCALIZED: Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "UNLOCALIZED: Place only in Starting Position",
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 ac6a23398..95ac26aa2 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: 141",
+ "_": "Missing Localizations: 146",
"_": "->------------------------] Game Elements [------------------------<-",
@@ -647,6 +647,12 @@
"create.blockzapper.leftClickToSet": "左键点击方块以设定方块",
"create.blockzapper.empty": "方块不足!",
+ "create.minecart_coupling.two_couplings_max": "UNLOCALIZED: Minecarts cannot have more than two couplings each",
+ "create.minecart_coupling.unloaded": "UNLOCALIZED: Parts of your train seem to be in unloaded chunks",
+ "create.minecart_coupling.no_loops": "UNLOCALIZED: Couplings cannot form a loop",
+ "create.minecart_coupling.removed": "UNLOCALIZED: Removed all couplings from minecart",
+ "create.minecart_coupling.too_far": "UNLOCALIZED: Minecarts are too far apart",
+
"create.contraptions.movement_mode": "运动模式",
"create.contraptions.movement_mode.move_place": "停止时总是实体化方块",
"create.contraptions.movement_mode.move_place_returned": "停止时只在初始位置实体化方块",
diff --git a/src/main/java/com/simibubi/create/AllShapes.java b/src/main/java/com/simibubi/create/AllShapes.java
index 232a0e9d6..dbf65f2b4 100644
--- a/src/main/java/com/simibubi/create/AllShapes.java
+++ b/src/main/java/com/simibubi/create/AllShapes.java
@@ -49,6 +49,7 @@ public class AllShapes {
.forDirectional(),
CART_ASSEMBLER = shape(0, 12, 0, 16, 16, 16).add(-2, 0, 1, 18, 14, 15)
.forHorizontalAxis(),
+ CART_ASSEMBLER_PLAYER_COLLISION = shape(0, 0, 1, 16, 16, 15).forHorizontalAxis(),
STOCKPILE_SWITCH = shape(0, 0, 0, 16, 2, 16).add(1, 0, 1, 15, 16, 15)
.add(0, 14, 0, 16, 16, 16)
.add(3, 3, -2, 13, 13, 2)
diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java
index 59f1484e3..d00106565 100644
--- a/src/main/java/com/simibubi/create/Create.java
+++ b/src/main/java/com/simibubi/create/Create.java
@@ -9,6 +9,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.simibubi.create.content.CreateItemGroup;
import com.simibubi.create.content.contraptions.TorquePropagator;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController;
import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler;
import com.simibubi.create.content.palettes.AllPaletteBlocks;
import com.simibubi.create.content.palettes.PalettesItemGroup;
@@ -98,6 +99,7 @@ public class Create {
}
public static void init(final FMLCommonSetupEvent event) {
+ CapabilityMinecartController.register();
schematicReceiver = new ServerSchematicLoader();
redstoneLinkNetworkHandler = new RedstoneLinkNetworkHandler();
torquePropagator = new TorquePropagator();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java b/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java
index dd1a9350a..5eba3d335 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/RotationPropagator.java
@@ -200,7 +200,7 @@ public class RotationPropagator {
* @param pos
*/
public static void handleAdded(World worldIn, BlockPos pos, KineticTileEntity addedTE) {
- if (worldIn.isRemote || isFrozen())
+ if (worldIn.isRemote)
return;
if (!worldIn.isBlockPresent(pos))
return;
@@ -297,7 +297,7 @@ public class RotationPropagator {
* @param removedTE
*/
public static void handleRemoved(World worldIn, BlockPos pos, KineticTileEntity removedTE) {
- if (worldIn.isRemote || isFrozen())
+ if (worldIn.isRemote)
return;
if (removedTE == null)
return;
@@ -454,8 +454,4 @@ public class RotationPropagator {
return neighbours;
}
- public static boolean isFrozen() {
- return AllConfigs.SERVER.control.freezeRotationPropagator.get();
- }
-
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java
index 000280891..e9de2dcf7 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/BellMovementBehaviour.java
@@ -18,8 +18,7 @@ public class BellMovementBehaviour extends MovementBehaviour {
public void onSpeedChanged(MovementContext context, Vector3d oldMotion, Vector3d motion) {
double dotProduct = oldMotion.dotProduct(motion);
- if (dotProduct <= 0 && (context.relativeMotion.length() != 0 || context.rotation.length() == 0)
- || context.firstMovement)
+ if (dotProduct <= 0 && (context.relativeMotion.length() != 0) || context.firstMovement)
context.world.playSound(null, new BlockPos(context.position), SoundEvents.BLOCK_BELL_USE,
SoundCategory.BLOCKS, 2.0F, 1.0F);
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java
index e5677918c..ec5f06fa9 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PortableStorageInterfaceMovement.java
@@ -138,7 +138,7 @@ public class PortableStorageInterfaceMovement extends MovementBehaviour {
private Optional getCurrentFacingIfValid(MovementContext context) {
Vector3d directionVec = Vector3d.of(context.state.get(PortableStorageInterfaceBlock.FACING)
.getDirectionVec());
- directionVec = VecHelper.rotate(directionVec, context.rotation.x, context.rotation.y, context.rotation.z);
+ directionVec = context.rotation.apply(directionVec);
Direction facingFromVector = Direction.getFacingFromVector(directionVec.x, directionVec.y, directionVec.z);
if (directionVec.distanceTo(Vector3d.of(facingFromVector.getDirectionVec())) > 1 / 8f)
return Optional.empty();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java
index 17c1887ed..61f76ad0d 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/DispenserMovementBehaviour.java
@@ -5,7 +5,6 @@ import java.util.HashMap;
import javax.annotation.ParametersAreNonnullByDefault;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
-import com.simibubi.create.foundation.utility.VecHelper;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block;
@@ -63,7 +62,7 @@ public class DispenserMovementBehaviour extends DropperMovementBehaviour {
}
Vector3d facingVec = Vector3d.of(context.state.get(DispenserBlock.FACING).getDirectionVec());
- facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z);
+ facingVec = context.rotation.apply(facingVec);
facingVec.normalize();
Direction clostestFacing = Direction.getFacingFromVector(facingVec.x, facingVec.y, facingVec.z);
ContraptionBlockSource blockSource = new ContraptionBlockSource(context, pos, clostestFacing);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/IMovedDispenseItemBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/IMovedDispenseItemBehaviour.java
index e00a451f5..b87b9d20b 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/IMovedDispenseItemBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/IMovedDispenseItemBehaviour.java
@@ -35,6 +35,7 @@ import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
+
public interface IMovedDispenseItemBehaviour {
static void init() {
MovedProjectileDispenserBehaviour movedPotionDispenseItemBehaviour = new MovedProjectileDispenserBehaviour() {
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java
index 8cafefeb4..21b36b8b8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/dispenser/MovedDefaultDispenseItemBehaviour.java
@@ -37,7 +37,7 @@ public class MovedDefaultDispenseItemBehaviour implements IMovedDispenseItemBeha
@Override
public ItemStack dispense(ItemStack itemStack, MovementContext context, BlockPos pos) {
Vector3d facingVec = Vector3d.of(context.state.get(DispenserBlock.FACING).getDirectionVec());
- facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z);
+ facingVec = context.rotation.apply(facingVec);
facingVec.normalize();
Direction closestToFacing = getClosestFacingDirection(facingVec);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crank/ValveHandleBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/crank/ValveHandleBlock.java
index 2bc864ccc..619587dc8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/crank/ValveHandleBlock.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/crank/ValveHandleBlock.java
@@ -1,5 +1,7 @@
package com.simibubi.create.content.contraptions.components.crank;
+import javax.annotation.ParametersAreNonnullByDefault;
+
import com.simibubi.create.AllBlockPartials;
import net.minecraft.item.ItemGroup;
@@ -8,8 +10,6 @@ import net.minecraft.util.NonNullList;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
-import javax.annotation.ParametersAreNonnullByDefault;
-
@ParametersAreNonnullByDefault
public class ValveHandleBlock extends HandCrankBlock {
private final boolean inCreativeTab;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
index e50c9bd55..3ea8e26e8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerBlock.java
@@ -69,7 +69,7 @@ public class CrushingWheelControllerBlock extends Block
}
public void onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn) {
- if (!state.get(VALID) || CrushingWheelControllerTileEntity.isFrozen())
+ if (!state.get(VALID))
return;
withTileEntityDo(worldIn, pos, te -> {
if (te.processingEntity == entityIn)
@@ -80,9 +80,6 @@ public class CrushingWheelControllerBlock extends Block
@Override
public void onLanded(IBlockReader worldIn, Entity entityIn) {
super.onLanded(worldIn, entityIn);
- if (CrushingWheelControllerTileEntity.isFrozen())
- return;
-
try {
CrushingWheelControllerTileEntity te = getTileEntity(worldIn, entityIn.getBlockPos().down());
if (te.crushingspeed == 0)
@@ -122,7 +119,7 @@ public class CrushingWheelControllerBlock extends Block
public void updateSpeed(BlockState state, IWorld world, BlockPos pos) {
withTileEntityDo(world, pos, te -> {
- if (!state.get(VALID) || CrushingWheelControllerTileEntity.isFrozen()) {
+ if (!state.get(VALID)) {
if (te.crushingspeed != 0) {
te.crushingspeed = 0;
te.sendData();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java
index cdbfb51a2..4745894d0 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/crusher/CrushingWheelControllerTileEntity.java
@@ -70,8 +70,6 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity {
@Override
public void tick() {
super.tick();
- if (isFrozen())
- return;
if (searchForEntity) {
searchForEntity = false;
List search = world.getEntitiesInAABBexcluding(null, new AxisAlignedBB(getPos()),
@@ -225,7 +223,7 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity {
@Override
protected void fromTag(BlockState state, CompoundNBT compound, boolean clientPacket) {
super.fromTag(state, compound, clientPacket);
- if (compound.contains("Entity") && !isFrozen() && !isOccupied()) {
+ if (compound.contains("Entity") && !isOccupied()) {
entityUUID = NBTUtil.readUniqueId(compound.getCompound("Entity"));
this.searchForEntity = true;
}
@@ -265,8 +263,4 @@ public class CrushingWheelControllerTileEntity extends SmartTileEntity {
return processingEntity != null;
}
- public static boolean isFrozen() {
- return AllConfigs.SERVER.control.freezeCrushing.get();
- }
-
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java
index 0ad3f873f..ed413d8cf 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerMovementBehaviour.java
@@ -13,7 +13,6 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Mov
import com.simibubi.create.content.logistics.item.filter.FilterItem;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.NBTHelper;
-import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.entity.player.PlayerInventory;
@@ -52,7 +51,7 @@ public class DeployerMovementBehaviour extends MovementBehaviour {
public void activate(MovementContext context, BlockPos pos, DeployerFakePlayer player, Mode mode) {
Vector3d facingVec = Vector3d.of(context.state.get(DeployerBlock.FACING)
.getDirectionVec());
- facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z);
+ facingVec = context.rotation.apply(facingVec);
Vector3d vec = context.position.subtract(facingVec.scale(2));
player.rotationYaw = ContraptionEntity.yawFromVector(facingVec);
player.rotationPitch = ContraptionEntity.pitchFromVector(facingVec) - 90;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/fan/AirCurrent.java b/src/main/java/com/simibubi/create/content/contraptions/components/fan/AirCurrent.java
index 9ec75f3f5..d2e8de8c6 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/fan/AirCurrent.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/fan/AirCurrent.java
@@ -105,9 +105,6 @@ public class AirCurrent {
if (entity instanceof ServerPlayerEntity)
((ServerPlayerEntity) entity).connection.floatingTickCount = 0;
- if (InWorldProcessing.isFrozen())
- return;
-
entityDistance -= .5f;
InWorldProcessing.Type processingType = getSegmentAt((float) entityDistance);
if (processingType != null) {
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java
index 265dc264e..e23399bb5 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java
@@ -142,10 +142,6 @@ public abstract class Contraption {
return contraption;
}
- public static boolean isFrozen() {
- return AllConfigs.SERVER.control.freezeContraptions.get();
- }
-
protected static MovementBehaviour getMovement(BlockState state) {
Block block = state.getBlock();
if (!AllMovementBehaviours.hasMovementBehaviour(block))
@@ -337,9 +333,14 @@ public abstract class Contraption {
frontier.add(otherPartPos);
}
+ // Cart assemblers attach themselves
+ BlockState stateBelow = world.getBlockState(pos.down());
+ if (!visited.contains(pos.down()) && AllBlocks.CART_ASSEMBLER.has(stateBelow))
+ frontier.add(pos.down());
+
Map superglue = SuperGlueHandler.gatherGlue(world, pos);
- // Slime blocks drag adjacent blocks if possible
+ // Slime blocks and super glue drag adjacent blocks if possible
boolean isSlimeBlock = state.getBlock() instanceof SlimeBlock;
for (Direction offset : Direction.values()) {
BlockPos offsetPos = pos.offset(offset);
@@ -360,7 +361,6 @@ public abstract class Contraption {
if (!wasVisited && ((isSlimeBlock && !brittle) || blockAttachedTowardsFace || faceHasGlue))
frontier.add(offsetPos);
-
if (faceHasGlue)
addGlue(superglue.get(offset));
}
@@ -764,7 +764,7 @@ public abstract class Contraption {
ctx.position = null;
ctx.motion = Vector3d.ZERO;
ctx.relativeMotion = Vector3d.ZERO;
- ctx.rotation = Vector3d.ZERO;
+ ctx.rotation = v -> v;
});
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java
index e7b2744bf..1e7c01916 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java
@@ -3,9 +3,7 @@ package com.simibubi.create.content.contraptions.components.structureMovement;
import static net.minecraft.entity.Entity.collideBoundingBoxHeuristically;
import static net.minecraft.entity.Entity.horizontalMag;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
@@ -33,6 +31,7 @@ import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.Direction;
+import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.ReuseableStream;
import net.minecraft.util.math.AxisAlignedBB;
@@ -51,28 +50,11 @@ import net.minecraftforge.fml.DistExecutor;
public class ContraptionCollider {
- public static void runCollisions(World world) {
- List> list = ContraptionHandler.activeContraptions.getIfPresent(world);
- if (list == null)
- return;
- for (Iterator> iterator = list.iterator(); iterator.hasNext();) {
- WeakReference weakReference = iterator.next();
- ContraptionEntity contraptionEntity = weakReference.get();
- if (contraptionEntity == null) {
- iterator.remove();
- continue;
- }
- if (!contraptionEntity.isAlive())
- continue;
- collideEntities(contraptionEntity);
- }
- }
-
enum PlayerType {
NONE, CLIENT, REMOTE, SERVER
}
- private static void collideEntities(ContraptionEntity contraptionEntity) {
+ static void collideEntities(ContraptionEntity contraptionEntity) {
World world = contraptionEntity.getEntityWorld();
Contraption contraption = contraptionEntity.getContraption();
AxisAlignedBB bounds = contraptionEntity.getBoundingBox();
@@ -89,8 +71,18 @@ public class ContraptionCollider {
Vector3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
double conRotX = contraptionRotation.x;
- double conRotY = contraptionRotation.y;
+ double conRotY = contraptionRotation.y + contraptionEntity.getInitialYaw();
double conRotZ = contraptionRotation.z;
+
+ double reverseYaw = 0;
+
+ // Collision algorithm does not support rotation around two axes -> rotate
+ // entities manually
+ if (conRotZ != 0 && contraptionRotation.y != 0) {
+ reverseYaw = contraptionRotation.y;
+ conRotY = contraptionEntity.getInitialYaw();
+ }
+
Vector3d contraptionCentreOffset = contraptionEntity.stationary ? centerOfBlock : Vector3d.ZERO.add(0, 0.5, 0);
boolean axisAlignedCollision = contraptionRotation.equals(Vector3d.ZERO);
Matrix3d rotation = null;
@@ -118,13 +110,15 @@ public class ContraptionCollider {
Vector3d centerY = new Vector3d(0, entityBounds.getYSize() / 2, 0);
Vector3d motion = entity.getMotion();
- Vector3d position = entityPosition.subtract(contraptionCentreOffset)
- .add(centerY);
+ Vector3d position = entityPosition;
+ position = position.subtract(contraptionCentreOffset);
+ position = position.add(centerY);
position = position.subtract(contraptionPosition);
+ position = VecHelper.rotate(position, -reverseYaw, Axis.Y);
position = rotation.transform(position);
- position = position.add(centerOfBlock)
- .subtract(centerY)
- .subtract(entityPosition);
+ position = position.add(centerOfBlock);
+ position = position.subtract(centerY);
+ position = position.subtract(entityPosition);
// Find all potential block shapes to collide with
AxisAlignedBB localBB = entityBounds.offset(position)
@@ -209,6 +203,7 @@ public class ContraptionCollider {
motionResponse = rotation.transform(motionResponse)
.add(contraptionMotion);
totalResponse = rotation.transform(totalResponse);
+ totalResponse = VecHelper.rotate(totalResponse, reverseYaw, Axis.Y);
rotation.transpose();
if (futureCollision.isTrue() && playerType != PlayerType.SERVER) {
@@ -224,7 +219,7 @@ public class ContraptionCollider {
entity.fallDistance = 0;
entity.setOnGround(true);
contraptionEntity.collidingEntities.add(entity);
- if (playerType != PlayerType.SERVER)
+ if (playerType != PlayerType.SERVER)
contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition);
}
@@ -253,6 +248,7 @@ public class ContraptionCollider {
continue;
}
+
totalResponse = totalResponse.add(contactPointMotion);
Vector3d allowedMovement = getAllowedMovement(totalResponse, entity);
contraptionEntity.collidingEntities.add(entity);
@@ -263,7 +259,7 @@ public class ContraptionCollider {
if (playerType != PlayerType.CLIENT)
continue;
-
+
double d0 = entity.getX() - entity.prevPosX - contactPointMotion.x;
double d1 = entity.getZ() - entity.prevPosZ - contactPointMotion.z;
float limbSwing = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F;
@@ -357,8 +353,6 @@ public class ContraptionCollider {
}
public static boolean collideBlocks(ContraptionEntity contraptionEntity) {
- if (Contraption.isFrozen())
- return true;
if (!contraptionEntity.collisionEnabled())
return false;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java
index 3e9baf232..2f2d0ea4c 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java
@@ -20,11 +20,13 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket;
-import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCoupling;
-import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.AngleHelper;
+import com.simibubi.create.foundation.utility.Couple;
+import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
@@ -49,10 +51,12 @@ import net.minecraft.network.PacketBuffer;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
+import net.minecraft.network.datasync.IDataSerializer;
import net.minecraft.tags.BlockTags;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
+import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
@@ -62,32 +66,55 @@ import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.fml.network.PacketDistributor;
public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnData {
+ public static final IDataSerializer> OPTIONAL_DIRECTION =
+ new IDataSerializer>() {
+
+ public void write(PacketBuffer buffer, Optional opt) {
+ buffer.writeVarInt(opt.map(Direction::ordinal)
+ .orElse(-1) + 1);
+ }
+
+ public Optional read(PacketBuffer buffer) {
+ int i = buffer.readVarInt();
+ return i == 0 ? Optional.empty() : Optional.of(Direction.values()[i - 1]);
+ }
+
+ public Optional copyValue(Optional opt) {
+ return Optional.ofNullable(opt.orElse(null));
+ }
+ };
+
+ static {
+ DataSerializers.registerSerializer(OPTIONAL_DIRECTION);
+ }
+
+ final List collidingEntities = new ArrayList<>();
+
protected Contraption contraption;
- protected float initialAngle;
- protected float forcedAngle;
protected BlockPos controllerPos;
protected Vector3d motionBeforeStall;
+ protected boolean forceAngle;
protected boolean stationary;
protected boolean initialized;
- final List collidingEntities = new ArrayList<>();
private boolean isSerializingFurnaceCart;
private boolean attachedExtraInventories;
private boolean prevPosInvalid;
private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL);
+
private static final DataParameter STALLED =
EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.BOOLEAN);
-
private static final DataParameter> COUPLING =
EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.OPTIONAL_UNIQUE_ID);
- private static final DataParameter> COUPLED_CART =
- EntityDataManager.createKey(ContraptionEntity.class, DataSerializers.OPTIONAL_UNIQUE_ID);
+ private static final DataParameter> INITIAL_ORIENTATION =
+ EntityDataManager.createKey(ContraptionEntity.class, ContraptionEntity.OPTIONAL_DIRECTION);
public float prevYaw;
public float prevPitch;
@@ -107,23 +134,15 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
stationary = entityTypeIn == AllEntityTypes.STATIONARY_CONTRAPTION.get();
isSerializingFurnaceCart = false;
attachedExtraInventories = false;
- forcedAngle = -1;
prevPosInvalid = true;
}
- public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) {
+ public static ContraptionEntity createMounted(World world, Contraption contraption,
+ Optional initialOrientation) {
ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.CONTRAPTION.get(), world);
entity.contraptionCreated(contraption);
- entity.initialAngle = initialAngle;
- entity.forceYaw(initialAngle);
- return entity;
- }
-
- public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle,
- Direction facing) {
- ContraptionEntity entity = createMounted(world, contraption, initialAngle);
- entity.forcedAngle = facing.getHorizontalAngle();
- entity.forceYaw(entity.forcedAngle);
+ initialOrientation.ifPresent(entity::setInitialOrientation);
+ entity.startAtInitialYaw();
return entity;
}
@@ -133,6 +152,10 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return entity;
}
+ public void reOrientate(Direction newInitialAngle) {
+ setInitialOrientation(newInitialAngle);
+ }
+
protected void contraptionCreated(Contraption contraption) {
this.contraption = contraption;
if (contraption == null)
@@ -219,7 +242,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
BlockPos seat = contraption.getSeat(passenger.getUniqueID());
if (seat == null)
return null;
- Vector3d transformedVector = toGlobalVector(Vector3d.of(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5))
+ Vector3d transformedVector = toGlobalVector(Vector3d.of(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5), 1)
.add(VecHelper.getCenterOf(BlockPos.ZERO))
.subtract(0.5, ySize, 0.5);
return transformedVector;
@@ -267,20 +290,21 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return true;
}
- public Vector3d toGlobalVector(Vector3d localVec) {
+ public Vector3d toGlobalVector(Vector3d localVec, float partialTicks) {
Vector3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
localVec = localVec.subtract(rotationOffset);
- localVec = VecHelper.rotate(localVec, getRotationVec());
+ localVec = applyRotation(localVec, partialTicks);
localVec = localVec.add(rotationOffset)
.add(getAnchorVec());
return localVec;
}
- public Vector3d toLocalVector(Vector3d globalVec) {
+
+ public Vector3d toLocalVector(Vector3d globalVec, float partialTicks) {
Vector3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
globalVec = globalVec.subtract(getAnchorVec())
.subtract(rotationOffset);
- globalVec = VecHelper.rotate(globalVec, getRotationVec().scale(-1));
+ globalVec = reverseRotation(globalVec, partialTicks);
globalVec = globalVec.add(rotationOffset);
return globalVec;
}
@@ -291,7 +315,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
remove();
return;
}
-
+
prevPosX = getX();
prevPosY = getY();
prevPosZ = getZ();
@@ -315,29 +339,41 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (ContraptionCollider.collideBlocks(this))
getController().collided();
- Vector3d movement = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ);
- tickActors(movement);
+ tickActors();
prevYaw = yaw;
prevPitch = pitch;
prevRoll = roll;
super.tick();
+
}
public void tickAsPassenger(Entity e) {
boolean rotationLock = false;
boolean pauseWhileRotating = false;
boolean rotating = false;
+ boolean wasStalled = isStalled();
+ if (contraption instanceof MountedContraption) {
+ MountedContraption mountedContraption = (MountedContraption) contraption;
+ rotationLock = mountedContraption.rotationMode == CartMovementMode.ROTATION_LOCKED;
+ pauseWhileRotating = mountedContraption.rotationMode == CartMovementMode.ROTATE_PAUSED;
+ }
Entity riding = e;
while (riding.getRidingEntity() != null)
riding = riding.getRidingEntity();
+
+ boolean isOnCoupling = false;
+ UUID couplingId = getCouplingId();
+ isOnCoupling = couplingId != null && riding instanceof AbstractMinecartEntity;
+
if (!attachedExtraInventories) {
- contraption.addExtraInventories(riding);
+ attachInventoriesFromRidingCarts(riding, isOnCoupling, couplingId);
attachedExtraInventories = true;
}
-
+/*
+<<<<<<< HEAD
boolean isOnCoupling = false;
if (contraption instanceof MountedContraption) {
MountedContraption mountedContraption = (MountedContraption) contraption;
@@ -362,19 +398,49 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (notOnMainCart) {
yaw += 180;
}
+=======*/
+ if (isOnCoupling) {
+ Couple coupledCarts = getCoupledCartsIfPresent();
+ if (coupledCarts != null) {
+
+ Vector3d positionVec = coupledCarts.getFirst()
+ .cart()
+ .getPositionVec();
+ Vector3d coupledVec = coupledCarts.getSecond()
+ .cart()
+ .getPositionVec();
+
+ double diffX = positionVec.x - coupledVec.x;
+ double diffY = positionVec.y - coupledVec.y;
+ double diffZ = positionVec.z - coupledVec.z;
+
+ prevYaw = yaw;
+ prevPitch = pitch;
+ yaw = (float) (MathHelper.atan2(diffZ, diffX) * 180 / Math.PI);
+ pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI);
+
+ if (couplingId.equals(riding.getUniqueID())) {
+ pitch *= -1;
+ yaw += 180;
}
+
}
-
- rotationLock = mountedContraption.rotationMode == CartMovementMode.ROTATION_LOCKED;
- pauseWhileRotating = mountedContraption.rotationMode == CartMovementMode.ROTATE_PAUSED;
- }
-
- Vector3d movementVector = riding.getMotion();
- if (!isOnCoupling) {
- if (riding instanceof BoatEntity)
+ } else if (!wasStalled) {
+ Vector3d movementVector = riding.getMotion();
+ if (!(riding instanceof AbstractMinecartEntity))
movementVector = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ);
Vector3d motion = movementVector.normalize();
+ if (!dataManager.get(INITIAL_ORIENTATION)
+ .isPresent() && !world.isRemote) {
+ if (motion.length() > 0) {
+ Direction facingFromVector = Direction.getFacingFromVector(motion.x, motion.y, motion.z);
+ if (facingFromVector.getAxis()
+ .isHorizontal())
+ setInitialOrientation(facingFromVector);
+ }
+ }
+
if (!rotationLock) {
if (motion.length() > 0) {
targetYaw = yawFromVector(motion);
@@ -393,66 +459,131 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
}
- boolean wasStalled = isStalled();
if (!rotating || !pauseWhileRotating)
- tickActors(movementVector);
- if (isStalled()) {
- if (!wasStalled)
- motionBeforeStall = riding.getMotion();
- riding.setMotion(0, 0, 0);
+ tickActors();
+ boolean isStalled = isStalled();
+
+ LazyOptional capability =
+ riding.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY);
+ if (capability.isPresent()) {
+ if (!world.isRemote())
+ capability.orElse(null)
+ .setStalledExternally(isStalled);
+ } else {
+ if (isStalled) {
+ if (!wasStalled)
+ motionBeforeStall = riding.getMotion();
+ riding.setMotion(0, 0, 0);
+ }
+ if (wasStalled && !isStalled) {
+ riding.setMotion(motionBeforeStall);
+ motionBeforeStall = Vector3d.ZERO;
+ }
}
- if (wasStalled && !isStalled()) {
- riding.setMotion(motionBeforeStall);
- motionBeforeStall = Vector3d.ZERO;
- }
+ if (world.isRemote)
+ return;
- if (!isStalled() && (riding instanceof FurnaceMinecartEntity)) {
- FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) riding;
-
- // Notify to not trigger serialization side-effects
- isSerializingFurnaceCart = true;
- CompoundNBT nbt = furnaceCart.serializeNBT();
- isSerializingFurnaceCart = false;
-
- int fuel = nbt.getInt("Fuel");
- int fuelBefore = fuel;
- double pushX = nbt.getDouble("PushX");
- double pushZ = nbt.getDouble("PushZ");
-
- int i = MathHelper.floor(furnaceCart.getX());
- int j = MathHelper.floor(furnaceCart.getY());
- int k = MathHelper.floor(furnaceCart.getZ());
- if (furnaceCart.world.getBlockState(new BlockPos(i, j - 1, k))
- .isIn(BlockTags.RAILS))
- --j;
-
- BlockPos blockpos = new BlockPos(i, j, k);
- BlockState blockstate = this.world.getBlockState(blockpos);
- if (furnaceCart.canUseRail() && blockstate.isIn(BlockTags.RAILS))
- if (fuel > 1)
- riding.setMotion(riding.getMotion()
- .normalize()
- .scale(1));
- if (fuel < 5 && contraption != null) {
- ItemStack coal = ItemHelper.extract(contraption.inventory, FUEL_ITEMS, 1, false);
- if (!coal.isEmpty())
- fuel += 3600;
- }
-
- if (fuel != fuelBefore || pushX != 0 || pushZ != 0) {
- nbt.putInt("Fuel", fuel);
- nbt.putDouble("PushX", 0);
- nbt.putDouble("PushZ", 0);
- furnaceCart.deserializeNBT(nbt);
+ if (!isStalled()) {
+ if (isOnCoupling) {
+ Couple coupledCarts = getCoupledCartsIfPresent();
+ if (coupledCarts == null)
+ return;
+ coupledCarts.map(MinecartController::cart)
+ .forEach(this::powerFurnaceCartWithFuelFromStorage);
+ return;
}
+ powerFurnaceCartWithFuelFromStorage(riding);
}
}
- public void tickActors(Vector3d movementVector) {
- Vector3d rotationVec = getRotationVec();
- Vector3d reversedRotationVec = rotationVec.scale(-1);
- Vector3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
+ protected void powerFurnaceCartWithFuelFromStorage(Entity riding) {
+ if (!(riding instanceof FurnaceMinecartEntity))
+ return;
+ FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) riding;
+
+ // Notify to not trigger serialization side-effects
+ isSerializingFurnaceCart = true;
+ CompoundNBT nbt = furnaceCart.serializeNBT();
+ isSerializingFurnaceCart = false;
+
+ int fuel = nbt.getInt("Fuel");
+ int fuelBefore = fuel;
+ double pushX = nbt.getDouble("PushX");
+ double pushZ = nbt.getDouble("PushZ");
+
+ int i = MathHelper.floor(furnaceCart.getX());
+ int j = MathHelper.floor(furnaceCart.getY());
+ int k = MathHelper.floor(furnaceCart.getZ());
+ if (furnaceCart.world.getBlockState(new BlockPos(i, j - 1, k))
+ .isIn(BlockTags.RAILS))
+ --j;
+
+ BlockPos blockpos = new BlockPos(i, j, k);
+ BlockState blockstate = this.world.getBlockState(blockpos);
+ if (furnaceCart.canUseRail() && blockstate.isIn(BlockTags.RAILS))
+ if (fuel > 1)
+ riding.setMotion(riding.getMotion()
+ .normalize()
+ .scale(1));
+ if (fuel < 5 && contraption != null) {
+ ItemStack coal = ItemHelper.extract(contraption.inventory, FUEL_ITEMS, 1, false);
+ if (!coal.isEmpty())
+ fuel += 3600;
+ }
+
+ if (fuel != fuelBefore || pushX != 0 || pushZ != 0) {
+ nbt.putInt("Fuel", fuel);
+ nbt.putDouble("PushX", 0);
+ nbt.putDouble("PushZ", 0);
+ furnaceCart.deserializeNBT(nbt);
+ }
+ }
+
+ @Nullable
+ public Couple getCoupledCartsIfPresent() {
+ UUID couplingId = getCouplingId();
+ if (couplingId == null)
+ return null;
+ MinecartController controller = CapabilityMinecartController.getIfPresent(world, couplingId);
+ if (controller == null || !controller.isPresent())
+ return null;
+ UUID coupledCart = controller.getCoupledCart(true);
+ MinecartController coupledController = CapabilityMinecartController.getIfPresent(world, coupledCart);
+ if (coupledController == null || !coupledController.isPresent())
+ return null;
+ return Couple.create(controller, coupledController);
+ }
+
+ protected void attachInventoriesFromRidingCarts(Entity riding, boolean isOnCoupling, UUID couplingId) {
+ if (isOnCoupling) {
+ Couple coupledCarts = getCoupledCartsIfPresent();
+ if (coupledCarts == null)
+ return;
+ coupledCarts.map(MinecartController::cart)
+ .forEach(contraption::addExtraInventories);
+ return;
+ }
+ contraption.addExtraInventories(riding);
+ }
+
+ public Vector3d applyRotation(Vector3d localPos, float partialTicks) {
+ localPos = VecHelper.rotate(localPos, getRoll(partialTicks), Axis.X);
+ localPos = VecHelper.rotate(localPos, getInitialYaw(), Axis.Y);
+ localPos = VecHelper.rotate(localPos, getPitch(partialTicks), Axis.Z);
+ localPos = VecHelper.rotate(localPos, getYaw(partialTicks), Axis.Y);
+ return localPos;
+ }
+
+ public Vector3d reverseRotation(Vector3d localPos, float partialTicks) {
+ localPos = VecHelper.rotate(localPos, -getYaw(partialTicks), Axis.Y);
+ localPos = VecHelper.rotate(localPos, -getPitch(partialTicks), Axis.Z);
+ localPos = VecHelper.rotate(localPos, -getInitialYaw(), Axis.Y);
+ localPos = VecHelper.rotate(localPos, -getRoll(partialTicks), Axis.X);
+ return localPos;
+ }
+
+ public void tickActors() {
boolean stalledPreviously = contraption.stalled;
if (!world.isRemote)
@@ -462,13 +593,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
MovementContext context = pair.right;
BlockInfo blockInfo = pair.left;
MovementBehaviour actor = Contraption.getMovement(blockInfo.state);
-
- Vector3d actorPosition = Vector3d.of(blockInfo.pos);
- actorPosition = actorPosition.add(actor.getActiveAreaOffset(context));
- actorPosition = VecHelper.rotate(actorPosition, rotationVec);
- actorPosition = actorPosition.add(rotationOffset)
- .add(getAnchorVec());
-
+ Vector3d actorPosition = toGlobalVector(VecHelper.getCenterOf(blockInfo.pos)
+ .add(actor.getActiveAreaOffset(context)), 1);
boolean newPosVisited = false;
BlockPos gridPosition = new BlockPos(actorPosition);
Vector3d oldMotion = context.motion;
@@ -478,7 +604,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (previousPosition != null) {
context.motion = actorPosition.subtract(previousPosition);
Vector3d relativeMotion = context.motion;
- relativeMotion = VecHelper.rotate(relativeMotion, reversedRotationVec);
+ relativeMotion = reverseRotation(relativeMotion, 1);
context.relativeMotion = relativeMotion;
newPosVisited = !new BlockPos(previousPosition).equals(gridPosition)
|| context.relativeMotion.length() > 0 && context.firstMovement;
@@ -506,8 +632,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
}
- context.rotation = rotationVec;
+ context.rotation = v -> applyRotation(v, 1);
context.position = actorPosition;
+
if (actor.isActive(context)) {
if (newPosVisited && !context.stall) {
actor.visitNewPosition(context, gridPosition);
@@ -552,6 +679,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
@Override
public void notifyDataManagerChange(DataParameter> key) {
super.notifyDataManagerChange(key);
+ if (key == INITIAL_ORIENTATION)
+ startAtInitialYaw();
}
public void rotate(double roll, double yaw, double pitch) {
@@ -590,7 +719,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
public float getYaw(float partialTicks) {
return (getRidingEntity() == null ? 1 : -1)
- * (partialTicks == 1.0F ? yaw : angleLerp(partialTicks, prevYaw, yaw)) + initialAngle;
+ * (partialTicks == 1.0F ? yaw : angleLerp(partialTicks, prevYaw, yaw));
}
public float getPitch(float partialTicks) {
@@ -611,16 +740,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
protected void registerData() {
this.dataManager.register(STALLED, false);
this.dataManager.register(COUPLING, Optional.empty());
- this.dataManager.register(COUPLED_CART, Optional.empty());
+ this.dataManager.register(INITIAL_ORIENTATION, Optional.empty());
}
@Override
protected void readAdditional(CompoundNBT compound) {
initialized = compound.getBoolean("Initialized");
contraption = Contraption.fromNBT(world, compound.getCompound("Contraption"));
- initialAngle = compound.getFloat("InitialAngle");
- forceYaw(compound.contains("ForcedYaw") ? compound.getFloat("ForcedYaw") : initialAngle);
dataManager.set(STALLED, compound.getBoolean("Stalled"));
+
+ if (compound.contains("InitialOrientation"))
+ setInitialOrientation(NBTHelper.readEnum(compound, "InitialOrientation", Direction.class));
+ if (compound.contains("ForceYaw"))
+ startAtYaw(compound.getFloat("ForceYaw"));
+
ListNBT vecNBT = compound.getList("CachedMotion", 6);
if (!vecNBT.isEmpty()) {
motionBeforeStall = new Vector3d(vecNBT.getDouble(0), vecNBT.getDouble(1), vecNBT.getDouble(2));
@@ -628,20 +761,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
targetYaw = prevYaw = yaw += yawFromVector(motionBeforeStall);
setMotion(Vector3d.ZERO);
}
+
if (compound.contains("Controller"))
controllerPos = NBTUtil.readBlockPos(compound.getCompound("Controller"));
-
- if (compound.contains("OnCoupling")) {
- setCouplingId(NBTUtil.readUniqueId(compound.getCompound("OnCoupling")));
- setCoupledCart(NBTUtil.readUniqueId(compound.getCompound("CoupledCart")));
- } else {
- setCouplingId(null);
- setCoupledCart(null);
- }
+ setCouplingId(
+ compound.contains("OnCoupling") ? NBTUtil.readUniqueId(compound.getCompound("OnCoupling")) : null);
}
- public void forceYaw(float forcedYaw) {
- targetYaw = yaw = prevYaw = forcedYaw;
+ public void startAtInitialYaw() {
+ startAtYaw(getInitialYaw());
+ }
+
+ public void startAtYaw(float yaw) {
+ targetYaw = this.yaw = prevYaw = yaw;
+ forceAngle = true;
}
public void checkController() {
@@ -670,17 +803,20 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
newDoubleNBTList(motionBeforeStall.x, motionBeforeStall.y, motionBeforeStall.z));
if (controllerPos != null)
compound.put("Controller", NBTUtil.writeBlockPos(controllerPos));
- if (forcedAngle != -1)
- compound.putFloat("ForcedYaw", forcedAngle);
- compound.putFloat("InitialAngle", initialAngle);
+ Optional optional = dataManager.get(INITIAL_ORIENTATION);
+ if (optional.isPresent())
+ NBTHelper.writeEnum(compound, "InitialOrientation", optional.get());
+ if (forceAngle) {
+ compound.putFloat("ForceYaw", yaw);
+ forceAngle = false;
+ }
+
compound.putBoolean("Stalled", isStalled());
compound.putBoolean("Initialized", initialized);
- if (getCouplingId() != null) {
+ if (getCouplingId() != null)
compound.put("OnCoupling", NBTUtil.fromUuid(getCouplingId()));
- compound.put("CoupledCart", NBTUtil.fromUuid(getCoupledCart()));
- }
}
@Override
@@ -706,7 +842,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (getContraption() != null) {
remove();
BlockPos offset = new BlockPos(getAnchorVec().add(.5, .5, .5));
- Vector3d rotation = getRotationVec();
+ Vector3d rotation = getRotationVec().add(0, getInitialYaw(), 0);
StructureTransform transform = new StructureTransform(offset, rotation);
contraption.addBlocksToWorld(world, transform);
contraption.addPassengersToWorld(world, transform, getPassengers());
@@ -716,7 +852,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
Vector3d positionVec = getPositionVec();
Vector3d localVec = entity.getPositionVec()
.subtract(positionVec);
- localVec = VecHelper.rotate(localVec, getRotationVec().scale(-1));
+ localVec = VecHelper.rotate(localVec, rotation.scale(-1));
Vector3d transformed = transform.apply(localVec);
entity.setPositionAndUpdate(transformed.x, transformed.y, transformed.z);
}
@@ -835,8 +971,18 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return false;
}
- public float getInitialAngle() {
- return initialAngle;
+ public void setInitialOrientation(Direction direction) {
+ dataManager.set(INITIAL_ORIENTATION, Optional.of(direction));
+ }
+
+ public Optional getInitialOrientation() {
+ return dataManager.get(INITIAL_ORIENTATION);
+ }
+
+ public float getInitialYaw() {
+ return dataManager.get(INITIAL_ORIENTATION)
+ .orElse(Direction.SOUTH)
+ .getHorizontalAngle();
}
public Vector3d getRotationVec() {
@@ -854,18 +1000,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
public Vector3d getContactPointMotion(Vector3d globalContactPoint) {
if (prevPosInvalid)
return Vector3d.ZERO;
-
- Vector3d positionVec = getPositionVec();
- Vector3d conMotion = positionVec.subtract(getPrevPositionVec());
- Vector3d conAngularMotion = getRotationVec().subtract(getPrevRotationVec());
- Vector3d contraptionCentreOffset = stationary ? VecHelper.getCenterOf(BlockPos.ZERO) : Vector3d.ZERO.add(0, 0.5, 0);
- Vector3d contactPoint = globalContactPoint.subtract(contraptionCentreOffset)
- .subtract(positionVec);
- contactPoint = VecHelper.rotate(contactPoint, conAngularMotion.x, conAngularMotion.y, conAngularMotion.z);
- contactPoint = contactPoint.add(positionVec)
- .add(contraptionCentreOffset)
- .add(conMotion);
- return contactPoint.subtract(globalContactPoint);
+ Vector3d contactPoint = toGlobalVector(toLocalVector(globalContactPoint, 0), 1);
+ return contactPoint.subtract(globalContactPoint)
+ .add(getPositionVec().subtract(getPrevPositionVec()));
}
public boolean canCollideWith(Entity e) {
@@ -906,16 +1043,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
dataManager.set(COUPLING, Optional.ofNullable(id));
}
- @Nullable
- public UUID getCoupledCart() {
- Optional uuid = dataManager.get(COUPLED_CART);
- return uuid.isPresent() ? uuid.get() : null;
- }
-
- public void setCoupledCart(UUID id) {
- dataManager.set(COUPLED_CART, Optional.ofNullable(id));
- }
-
@Override
public boolean isOnePlayerRiding() {
return false;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
index c33f6014c..01109988f 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntityRenderer.java
@@ -39,13 +39,10 @@ public class ContraptionEntityRenderer extends EntityRenderer
MatrixStack msLocal = getLocalTransform(entity);
MatrixStack[] matrixStacks = new MatrixStack[] { ms, msLocal };
- float degYaw = entity.getYaw(partialTicks);
- float degPitch = entity.getPitch(partialTicks);
- float degRoll = entity.getRoll(partialTicks);
-
- float angleYaw = (float) (degYaw / 180 * Math.PI);
- float anglePitch = (float) (degPitch / 180 * Math.PI);
- float angleRoll = (float) (degRoll / 180 * Math.PI);
+ float angleInitialYaw = entity.getInitialYaw();
+ float angleYaw = entity.getYaw(partialTicks);
+ float anglePitch = entity.getPitch(partialTicks);
+ float angleRoll = entity.getRoll(partialTicks);
ms.push();
Entity ridingEntity = entity.getRidingEntity();
@@ -80,7 +77,10 @@ public class ContraptionEntityRenderer extends EntityRenderer
MatrixStacker.of(stack)
.nudge(entity.getEntityId())
.centre()
- .rotateRadians(angleRoll, angleYaw, anglePitch)
+ .rotateY(angleYaw)
+ .rotateZ(anglePitch)
+ .rotateY(angleInitialYaw)
+ .rotateX(angleRoll)
.unCentre();
ContraptionRenderer.render(entity.world, entity.getContraption(), ms, msLocal, buffers);
ms.pop();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java
index ec45acedc..7dbc13de8 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java
@@ -1,17 +1,15 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ExecutionException;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
import com.simibubi.create.foundation.utility.VecHelper;
+import com.simibubi.create.foundation.utility.WorldAttached;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.CompoundNBT;
@@ -21,21 +19,39 @@ import net.minecraftforge.common.util.Constants.NBT;
public class ContraptionHandler {
- public static Cache>> activeContraptions = CacheBuilder.newBuilder()
- .expireAfterAccess(400, SECONDS)
- .build();
+ /* Global map of loaded contraptions */
+
+ public static WorldAttached>> loadedContraptions;
+ static WorldAttached> queuedAdditions;
+
+ static {
+ loadedContraptions = new WorldAttached<>(ArrayList::new);
+ queuedAdditions = new WorldAttached<>(() -> ObjectLists.synchronize(new ObjectArrayList<>()));
+ }
+
+ public static void tick(World world) {
+ List> list = loadedContraptions.get(world);
+ List queued = queuedAdditions.get(world);
+
+ for (ContraptionEntity contraptionEntity : queued)
+ list.add(new WeakReference<>(contraptionEntity));
+ queued.clear();
+
+ for (Iterator> iterator = list.iterator(); iterator.hasNext();) {
+ WeakReference weakReference = iterator.next();
+ ContraptionEntity contraptionEntity = weakReference.get();
+ if (contraptionEntity == null || !contraptionEntity.isAlive()) {
+ iterator.remove();
+ continue;
+ }
+ ContraptionCollider.collideEntities(contraptionEntity);
+ }
+ }
public static void addSpawnedContraptionsToCollisionList(Entity entity, World world) {
- if (!(entity instanceof ContraptionEntity))
- return;
- try {
- List> list =
- activeContraptions.get(world, () -> Collections.synchronizedList(new ArrayList<>()));
- ContraptionEntity contraption = (ContraptionEntity) entity;
- list.add(new WeakReference<>(contraption));
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
+ if (entity instanceof ContraptionEntity)
+ queuedAdditions.get(world)
+ .add((ContraptionEntity) entity);
}
public static void entitiesWhoJustDismountedGetSentToTheRightLocation(LivingEntity entityLiving, World world) {
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java
index bb5d8a183..41d588191 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java
@@ -78,8 +78,8 @@ public class ContraptionHandlerClient {
for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class,
new AxisAlignedBB(origin, target))) {
- Vector3d localOrigin = contraptionEntity.toLocalVector(origin);
- Vector3d localTarget = contraptionEntity.toLocalVector(target);
+ Vector3d localOrigin = contraptionEntity.toLocalVector(origin, 1);
+ Vector3d localTarget = contraptionEntity.toLocalVector(target, 1);
Contraption contraption = contraptionEntity.getContraption();
MutableObject mutableResult = new MutableObject<>();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java
index a869c5ead..e7a54d976 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/MovementContext.java
@@ -1,5 +1,7 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
+import java.util.function.UnaryOperator;
+
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
@@ -15,7 +17,8 @@ public class MovementContext {
public Vector3d position;
public Vector3d motion;
public Vector3d relativeMotion;
- public Vector3d rotation;
+ public UnaryOperator rotation;
+
public World world;
public BlockState state;
public BlockPos localPos;
@@ -36,7 +39,7 @@ public class MovementContext {
firstMovement = true;
motion = Vector3d.ZERO;
relativeMotion = Vector3d.ZERO;
- rotation = Vector3d.ZERO;
+ rotation = v -> v;
position = null;
data = new CompoundNBT();
stall = false;
@@ -56,7 +59,6 @@ public class MovementContext {
MovementContext context = new MovementContext(world, info);
context.motion = VecHelper.readNBT(nbt.getList("Motion", NBT.TAG_DOUBLE));
context.relativeMotion = VecHelper.readNBT(nbt.getList("RelativeMotion", NBT.TAG_DOUBLE));
- context.rotation = VecHelper.readNBT(nbt.getList("Rotation", NBT.TAG_DOUBLE));
if (nbt.contains("Position"))
context.position = VecHelper.readNBT(nbt.getList("Position", NBT.TAG_DOUBLE));
context.stall = nbt.getBoolean("Stall");
@@ -68,7 +70,6 @@ public class MovementContext {
public CompoundNBT writeToNBT(CompoundNBT nbt) {
nbt.put("Motion", VecHelper.writeNBT(motion));
nbt.put("RelativeMotion", VecHelper.writeNBT(relativeMotion));
- nbt.put("Rotation", VecHelper.writeNBT(rotation));
if (position != null)
nbt.put("Position", VecHelper.writeNBT(position));
nbt.putBoolean("Stall", stall);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java
index 4806c00e0..a29da4fa1 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/BearingContraption.java
@@ -24,8 +24,6 @@ public class BearingContraption extends Contraption {
}
public static BearingContraption assembleBearingAt(World world, BlockPos pos, Direction direction) {
- if (isFrozen())
- return null;
BearingContraption construct = new BearingContraption();
construct.facing = direction;
BlockPos offset = pos.offset(direction);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkBearingTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkBearingTileEntity.java
index 57e6dc613..e01131bd4 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkBearingTileEntity.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkBearingTileEntity.java
@@ -3,7 +3,6 @@ package com.simibubi.create.content.contraptions.components.structureMovement.be
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
-import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.ClockworkContraption.HandType;
import com.simibubi.create.foundation.utility.AngleHelper;
@@ -46,9 +45,6 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
clientHourAngleDiff /= 2;
}
- if (running && Contraption.isFrozen())
- disassemble();
-
if (!world.isRemote && assembleNextTick) {
assembleNextTick = false;
if (running) {
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkContraption.java
index 97abf1372..7cc15d761 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/ClockworkContraption.java
@@ -39,9 +39,6 @@ public class ClockworkContraption extends Contraption {
public static Pair assembleClockworkAt(World world, BlockPos pos,
Direction direction) {
- if (isFrozen())
- return null;
-
int hourArmBlocks = 0;
ClockworkContraption hourArm = new ClockworkContraption();
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingTileEntity.java
index ea9a205b5..bc713decf 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingTileEntity.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/bearing/MechanicalBearingTileEntity.java
@@ -5,7 +5,6 @@ import static net.minecraft.state.properties.BlockStateProperties.FACING;
import java.util.List;
import com.simibubi.create.content.contraptions.base.GeneratingKineticTileEntity;
-import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.DirectionalExtenderScrollOptionSlot;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@@ -207,8 +206,6 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
if (world.isRemote)
clientAngleDiff /= 2;
- if (running && Contraption.isFrozen())
- disassemble();
if (!world.isRemote && assembleNextTick) {
assembleNextTick = false;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java
index e3b36e10c..b97358268 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java
@@ -2,6 +2,8 @@ package com.simibubi.create.content.contraptions.components.structureMovement.mo
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -11,12 +13,16 @@ import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode;
-import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandler;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
import com.simibubi.create.foundation.block.ITE;
+import com.simibubi.create.foundation.utility.Couple;
+import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.AbstractRailBlock;
@@ -58,6 +64,7 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
+import net.minecraftforge.common.util.LazyOptional;
public class CartAssemblerBlock extends AbstractRailBlock
implements ITE, IWrenchable, ISpecialBlockItemRequirement {
@@ -116,8 +123,13 @@ public class CartAssemblerBlock extends AbstractRailBlock
AbstractMinecartEntity cart) {
if (!canAssembleTo(cart))
return;
+ if (world.isRemote)
+ return;
withTileEntityDo(world, pos, te -> {
+ /*
+ }
+<<<<<<< HEAD
if (te.isMinecartUpdateValid()) {
switch (state.get(RAIL_TYPE)) {
case POWERED_RAIL:
@@ -162,10 +174,61 @@ public class CartAssemblerBlock extends AbstractRailBlock
break;
}
te.resetTicksSinceMinecartUpdate();
+=======*/
+ if (!te.isMinecartUpdateValid())
+ return;
+
+ CartAssemblerAction action = getActionForCart(state, cart);
+ if (action.shouldAssemble())
+ assemble(world, pos, cart);
+ if (action.shouldDisassemble())
+ disassemble(world, pos, cart);
+ if (action == CartAssemblerAction.ASSEMBLE_ACCELERATE) {
+ Direction facing = cart.getAdjustedHorizontalFacing();
+ float speed = getRailMaxSpeed(state, world, pos, cart);
+ cart.setMotion(facing.getXOffset() * speed, facing.getYOffset() * speed, facing.getZOffset() * speed);
}
+ if (action == CartAssemblerAction.DISASSEMBLE_BRAKE) {
+ Vector3d diff = VecHelper.getCenterOf(pos)
+ .subtract(cart.getPositionVec());
+ cart.setMotion(diff.x / 16f, 0, diff.z / 16f);
+ }
+
});
}
+ public enum CartAssemblerAction {
+ ASSEMBLE, DISASSEMBLE, ASSEMBLE_ACCELERATE, DISASSEMBLE_BRAKE, PASS;
+
+ public boolean shouldAssemble() {
+ return this == ASSEMBLE || this == ASSEMBLE_ACCELERATE;
+ }
+
+ public boolean shouldDisassemble() {
+ return this == DISASSEMBLE || this == DISASSEMBLE_BRAKE;
+ }
+ }
+
+ public static CartAssemblerAction getActionForCart(BlockState state, AbstractMinecartEntity cart) {
+ CartAssembleRailType type = state.get(RAIL_TYPE);
+ boolean powered = state.get(POWERED);
+
+ if (type == CartAssembleRailType.REGULAR)
+ return powered ? CartAssemblerAction.ASSEMBLE : CartAssemblerAction.DISASSEMBLE;
+
+ if (type == CartAssembleRailType.ACTIVATOR_RAIL)
+ return powered ? CartAssemblerAction.DISASSEMBLE : CartAssemblerAction.PASS;
+
+ if (type == CartAssembleRailType.POWERED_RAIL)
+ return powered ? CartAssemblerAction.ASSEMBLE_ACCELERATE : CartAssemblerAction.DISASSEMBLE_BRAKE;
+
+ if (type == CartAssembleRailType.DETECTOR_RAIL)
+ return cart.getPassengers()
+ .isEmpty() ? CartAssemblerAction.ASSEMBLE_ACCELERATE : CartAssemblerAction.DISASSEMBLE;
+
+ return CartAssemblerAction.PASS;
+ }
+
public static boolean canAssembleTo(AbstractMinecartEntity cart) {
return cart.canBeRidden() || cart instanceof FurnaceMinecartEntity || cart instanceof ChestMinecartEntity;
}
@@ -204,34 +267,44 @@ public class CartAssemblerBlock extends AbstractRailBlock
.isEmpty())
return;
+ LazyOptional optional =
+ cart.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY);
+ if (optional.isPresent() && optional.orElse(null)
+ .isCoupledThroughContraption())
+ return;
+
MountedContraption contraption = MountedContraption.assembleMinecart(world, pos);
if (contraption == null)
return;
if (contraption.blocks.size() == 1)
return;
- Direction facing = cart.getAdjustedHorizontalFacing();
- float initialAngle = facing.getHorizontalAngle();
-
withTileEntityDo(world, pos, te -> contraption.rotationMode = CartMovementMode.values()[te.movementMode.value]);
boolean couplingFound = contraption.connectedCart != null;
+ Optional initialOrientation = cart.getMotion()
+ .length() < 1 / 512f ? Optional.empty() : Optional.of(cart.getAdjustedHorizontalFacing());
+
+ if (couplingFound) {
+ cart.setPosition(pos.getX() + .5f, pos.getY(), pos.getZ() + .5f);
+ if (!CouplingHandler.tryToCoupleCarts(null, world, cart.getEntityId(),
+ contraption.connectedCart.getEntityId()))
+ return;
+ }
+
+ contraption.removeBlocksFromWorld(world, BlockPos.ZERO);
+ contraption.initActors(world);
+ contraption.expandBoundsAroundAxis(Axis.Y);
+
if (couplingFound) {
- MinecartCouplingHandler.connectCarts(null, world, cart.getEntityId(),
- contraption.connectedCart.getEntityId());
Vector3d diff = contraption.connectedCart.getPositionVec()
.subtract(cart.getPositionVec());
- initialAngle = Direction.fromAngle(MathHelper.atan2(diff.z, diff.x) * 180 / Math.PI)
- .getHorizontalAngle();
+ initialOrientation = Optional.of(Direction.fromAngle(MathHelper.atan2(diff.z, diff.x) * 180 / Math.PI));
}
- ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialAngle, facing);
-
- if (couplingFound) {
+ ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialOrientation);
+ if (couplingFound)
entity.setCouplingId(cart.getUniqueID());
- entity.setCoupledCart(contraption.connectedCart.getUniqueID());
- }
-
entity.setPosition(pos.getX(), pos.getY(), pos.getZ());
world.addEntity(entity);
entity.startRiding(cart);
@@ -248,11 +321,44 @@ public class CartAssemblerBlock extends AbstractRailBlock
if (cart.getPassengers()
.isEmpty())
return;
- if (!(cart.getPassengers()
- .get(0) instanceof ContraptionEntity))
+ Entity entity = cart.getPassengers()
+ .get(0);
+ if (!(entity instanceof ContraptionEntity))
return;
- cart.removePassengers();
+ ContraptionEntity contraption = (ContraptionEntity) entity;
+ UUID couplingId = contraption.getCouplingId();
+ if (couplingId == null) {
+ disassembleCart(cart);
+ return;
+ }
+
+ Couple coupledCarts = contraption.getCoupledCartsIfPresent();
+ if (coupledCarts == null)
+ return;
+
+ // Make sure connected cart is present and being disassembled
+ for (boolean current : Iterate.trueAndFalse) {
+ MinecartController minecartController = coupledCarts.get(current);
+ if (minecartController.cart() == cart)
+ continue;
+ BlockPos otherPos = minecartController.cart()
+ .getBlockPos();
+ BlockState blockState = world.getBlockState(otherPos);
+ if (!AllBlocks.CART_ASSEMBLER.has(blockState))
+ return;
+ if (!getActionForCart(blockState, minecartController.cart()).shouldDisassemble())
+ return;
+ }
+
+ for (boolean current : Iterate.trueAndFalse)
+ coupledCarts.get(current)
+ .removeConnection(current);
+ disassembleCart(cart);
+ }
+
+ protected void disassembleCart(AbstractMinecartEntity cart) {
+ cart.removePassengers();
if (cart instanceof FurnaceMinecartEntity) {
CompoundNBT nbt = cart.serializeNBT();
nbt.putDouble("PushZ", cart.getMotion().x);
@@ -285,16 +391,22 @@ public class CartAssemblerBlock extends AbstractRailBlock
@Nonnull
public VoxelShape getShape(BlockState state, @Nonnull IBlockReader worldIn, @Nonnull BlockPos pos,
@Nonnull ISelectionContext context) {
- return AllShapes.CART_ASSEMBLER
- .get(state.get(RAIL_SHAPE) == RailShape.NORTH_SOUTH ? Direction.Axis.Z : Direction.Axis.X);
+ return AllShapes.CART_ASSEMBLER.get(getRailAxis(state));
+ }
+
+ protected Axis getRailAxis(BlockState state) {
+ return state.get(RAIL_SHAPE) == RailShape.NORTH_SOUTH ? Direction.Axis.Z : Direction.Axis.X;
}
@Override
@Nonnull
public VoxelShape getCollisionShape(@Nonnull BlockState state, @Nonnull IBlockReader worldIn, @Nonnull BlockPos pos,
ISelectionContext context) {
- if (context.getEntity() instanceof AbstractMinecartEntity)
+ Entity entity = context.getEntity();
+ if (entity instanceof AbstractMinecartEntity)
return VoxelShapes.empty();
+ if (entity instanceof PlayerEntity)
+ return AllShapes.CART_ASSEMBLER_PLAYER_COLLISION.get(getRailAxis(state));
return VoxelShapes.fullCube();
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java
index a0d3c23c2..6ed2b83c3 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java
@@ -1,12 +1,14 @@
package com.simibubi.create.content.contraptions.components.structureMovement.mounted;
import java.util.List;
+import java.util.Optional;
import javax.annotation.Nullable;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
+import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.block.AbstractRailBlock;
import net.minecraft.block.BlockState;
@@ -27,6 +29,7 @@ import net.minecraft.state.properties.RailShape;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
+import net.minecraft.util.Direction.Axis;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@@ -154,18 +157,21 @@ public class MinecartContraptionItem extends Item {
CompoundNBT tag = itemstack.getOrCreateTag();
if (tag.contains("Contraption")) {
CompoundNBT contraptionTag = tag.getCompound("Contraption");
- float initialAngle = contraptionTag.getFloat("InitialAngle");
+
+ Direction initialOrientation = Direction.SOUTH;
+ if (contraptionTag.contains("InitialOrientation"))
+ initialOrientation = NBTHelper.readEnum(contraptionTag, "InitialOrientation", Direction.class);
+
Contraption mountedContraption = Contraption.fromNBT(world, contraptionTag);
- ContraptionEntity contraption;
+ ContraptionEntity contraptionEntity =
+ ContraptionEntity.createMounted(world, mountedContraption, Optional.of(initialOrientation));
if (newFacing != null)
- contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle, newFacing);
- else
- contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle);
+ contraptionEntity.reOrientate(newFacing.getAxis() == Axis.X ? newFacing : newFacing.getOpposite());
- contraption.startRiding(cart);
- contraption.setPosition(cart.getX(), cart.getY(), cart.getZ());
- world.addEntity(contraption);
+ contraptionEntity.startRiding(cart);
+ contraptionEntity.setPosition(cart.getX(), cart.getY(), cart.getZ());
+ world.addEntity(contraptionEntity);
}
}
@@ -218,7 +224,11 @@ public class MinecartContraptionItem extends Item {
tag.remove("UUID");
tag.remove("Pos");
tag.remove("Motion");
- tag.putFloat("InitialAngle", entity.getInitialAngle());
+
+ Optional initialOrientation = entity.getInitialOrientation();
+ if (initialOrientation.isPresent())
+ NBTHelper.writeEnum(tag, "InitialOrientation", initialOrientation.orElse(null));
+
stack.getOrCreateTag()
.put("Contraption", tag);
return stack;
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java
index 574e1a505..9fb5d2538 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java
@@ -30,7 +30,9 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
+import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
+import net.minecraftforge.items.wrapper.InvWrapper;
public class MountedContraption extends Contraption {
@@ -47,9 +49,6 @@ public class MountedContraption extends Contraption {
}
public static MountedContraption assembleMinecart(World world, BlockPos pos) {
- if (isFrozen())
- return null;
-
BlockState state = world.getBlockState(pos);
if (!BlockHelper.hasBlockStateProperty(state, RAIL_SHAPE))
return null;
@@ -61,10 +60,6 @@ public class MountedContraption extends Contraption {
Axis axis = state.get(RAIL_SHAPE) == RailShape.EAST_WEST ? Axis.X : Axis.Z;
contraption.add(pos, Pair.of(new BlockInfo(pos, AllBlocks.MINECART_ANCHOR.getDefaultState()
.with(BlockStateProperties.HORIZONTAL_AXIS, axis), null), null));
- contraption.removeBlocksFromWorld(world, BlockPos.ZERO);
- contraption.initActors(world);
- contraption.expandBoundsAroundAxis(Axis.Y);
-
return contraption;
}
@@ -79,35 +74,51 @@ public class MountedContraption extends Contraption {
protected Pair capture(World world, BlockPos pos) {
Pair pair = super.capture(world, pos);
BlockInfo capture = pair.getKey();
- if (AllBlocks.CART_ASSEMBLER.has(capture.state)) {
- if (!pos.equals(anchor)) {
- for (Axis axis : Iterate.axes) {
- if (axis.isVertical())
- continue;
- if (VecHelper.onSameAxis(anchor, pos, axis) && connectedCart == null) {
- for (AbstractMinecartEntity abstractMinecartEntity : world
- .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) {
- if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity))
- break;
- connectedCart = abstractMinecartEntity;
- addExtraInventories(abstractMinecartEntity);
- }
- }
- }
+ if (!AllBlocks.CART_ASSEMBLER.has(capture.state))
+ return pair;
+
+ Pair anchorSwap =
+ Pair.of(new BlockInfo(pos, CartAssemblerBlock.createAnchor(capture.state), null), pair.getValue());
+ if (pos.equals(anchor) || connectedCart != null)
+ return anchorSwap;
+
+ for (Axis axis : Iterate.axes) {
+ if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis))
+ continue;
+ for (AbstractMinecartEntity abstractMinecartEntity : world
+ .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) {
+ if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity))
+ break;
+ connectedCart = abstractMinecartEntity;
+ connectedCart.setPosition(pos.getX() + .5, pos.getY(), pos.getZ() + .5f);
}
- return Pair.of(new BlockInfo(pos, CartAssemblerBlock.createAnchor(capture.state), null), pair.getValue());
}
- return pair;
+
+ return anchorSwap;
}
@Override
protected boolean movementAllowed(World world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if (!pos.equals(anchor) && AllBlocks.CART_ASSEMBLER.has(blockState))
- return true;
+ return testSecondaryCartAssembler(world, blockState, pos);
return super.movementAllowed(world, pos);
}
+ protected boolean testSecondaryCartAssembler(World world, BlockState state, BlockPos pos) {
+ for (Axis axis : Iterate.axes) {
+ if (axis.isVertical() || !VecHelper.onSameAxis(anchor, pos, axis))
+ continue;
+ for (AbstractMinecartEntity abstractMinecartEntity : world
+ .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) {
+ if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity))
+ break;
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public CompoundNBT writeNBT() {
CompoundNBT writeNBT = super.writeNBT();
@@ -133,7 +144,9 @@ public class MountedContraption extends Contraption {
@Override
public void addExtraInventories(Entity cart) {
- if (cart instanceof IInventory)
- inventory = new CombinedInvWrapper(new ItemHandlerModifiableFromIInventory((IInventory) cart), inventory);
+ if (!(cart instanceof IInventory))
+ return;
+ IItemHandlerModifiable handlerFromInv = new InvWrapper((IInventory) cart);
+ inventory = new CombinedInvWrapper(handlerFromInv, inventory);
}
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/piston/PistonContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/piston/PistonContraption.java
index 7af815d55..79b69b36f 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/piston/PistonContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/piston/PistonContraption.java
@@ -49,8 +49,6 @@ public class PistonContraption extends Contraption {
}
public static PistonContraption movePistonAt(World world, BlockPos pos, Direction direction, boolean retract) {
- if (isFrozen())
- return null;
PistonContraption construct = new PistonContraption();
construct.orientation = direction;
if (!construct.collectExtensions(world, pos, direction))
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/pulley/PulleyContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/pulley/PulleyContraption.java
index 68a21a1f0..32b8b3a3f 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/pulley/PulleyContraption.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/pulley/PulleyContraption.java
@@ -17,8 +17,6 @@ public class PulleyContraption extends Contraption {
}
public static PulleyContraption assemblePulleyAt(World world, BlockPos pos, int initialOffset) {
- if (isFrozen())
- return null;
PulleyContraption construct = new PulleyContraption();
construct.initialOffset = initialOffset;
if (!construct.searchMovedStructure(world, pos, null))
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingCreationPacket.java
similarity index 74%
rename from src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java
rename to src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingCreationPacket.java
index a382acc55..795a3e89e 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingCreationPacket.java
@@ -9,16 +9,16 @@ import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent.Context;
-public class MinecartCouplingCreationPacket extends SimplePacketBase {
+public class CouplingCreationPacket extends SimplePacketBase {
int id1, id2;
- public MinecartCouplingCreationPacket(AbstractMinecartEntity cart1, AbstractMinecartEntity cart2) {
+ public CouplingCreationPacket(AbstractMinecartEntity cart1, AbstractMinecartEntity cart2) {
id1 = cart1.getEntityId();
id2 = cart2.getEntityId();
}
- public MinecartCouplingCreationPacket(PacketBuffer buffer) {
+ public CouplingCreationPacket(PacketBuffer buffer) {
id1 = buffer.readInt();
id2 = buffer.readInt();
}
@@ -36,7 +36,7 @@ public class MinecartCouplingCreationPacket extends SimplePacketBase {
ServerPlayerEntity sender = context.get()
.getSender();
if (sender != null)
- MinecartCouplingHandler.connectCarts(sender, sender.world, id1, id2);
+ CouplingHandler.tryToCoupleCarts(sender, sender.world, id1, id2);
});
context.get()
.setPacketHandled(true);
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java
new file mode 100644
index 000000000..2cdbb52d6
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandler.java
@@ -0,0 +1,184 @@
+package com.simibubi.create.content.contraptions.components.structureMovement.train;
+
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+import javax.annotation.Nullable;
+
+import com.simibubi.create.AllItems;
+import com.simibubi.create.Create;
+import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.CapabilityMinecartController;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
+import com.simibubi.create.foundation.config.AllConfigs;
+import com.simibubi.create.foundation.utility.Couple;
+import com.simibubi.create.foundation.utility.Iterate;
+import com.simibubi.create.foundation.utility.Lang;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Hand;
+import net.minecraft.util.text.StringTextComponent;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.LazyOptional;
+import net.minecraftforge.event.entity.EntityMountEvent;
+import net.minecraftforge.eventbus.api.Event.Result;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
+
+@EventBusSubscriber
+public class CouplingHandler {
+
+ @SubscribeEvent
+ public static void preventEntitiesFromMoutingOccupiedCart(EntityMountEvent event) {
+ Entity e = event.getEntityBeingMounted();
+ LazyOptional optional = e.getCapability(CapabilityMinecartController.MINECART_CONTROLLER_CAPABILITY);
+ if (!optional.isPresent())
+ return;
+ if (event.getEntityMounting() instanceof ContraptionEntity)
+ return;
+ MinecartController controller = optional.orElse(null);
+ if (controller.isCoupledThroughContraption()) {
+ event.setCanceled(true);
+ event.setResult(Result.DENY);
+ }
+ }
+
+ public static void forEachLoadedCoupling(World world, Consumer> consumer) {
+ if (world == null)
+ return;
+ Set cartsWithCoupling = CapabilityMinecartController.loadedMinecartsWithCoupling.get(world);
+ if (cartsWithCoupling == null)
+ return;
+ cartsWithCoupling.forEach(id -> {
+ MinecartController controller = CapabilityMinecartController.getIfPresent(world, id);
+ if (controller == null)
+ return;
+ if (!controller.isLeadingCoupling())
+ return;
+ UUID coupledCart = controller.getCoupledCart(true);
+ MinecartController coupledController = CapabilityMinecartController.getIfPresent(world, coupledCart);
+ if (coupledController == null)
+ return;
+ consumer.accept(Couple.create(controller, coupledController));
+ });
+ }
+
+ public static boolean tryToCoupleCarts(@Nullable PlayerEntity player, World world, int cartId1, int cartId2) {
+ Entity entity1 = world.getEntityByID(cartId1);
+ Entity entity2 = world.getEntityByID(cartId2);
+
+ if (!(entity1 instanceof AbstractMinecartEntity))
+ return false;
+ if (!(entity2 instanceof AbstractMinecartEntity))
+ return false;
+
+ String tooMany = "two_couplings_max";
+ String unloaded = "unloaded";
+ String noLoops = "no_loops";
+ String tooFar = "too_far";
+
+ int distanceTo = (int) entity1.getPositionVec()
+ .distanceTo(entity2.getPositionVec());
+ boolean contraptionCoupling = player == null;
+
+ if (distanceTo < 2) {
+ if (contraptionCoupling)
+ return false; // dont allow train contraptions with <2 distance
+ distanceTo = 2;
+ }
+
+ if (distanceTo > AllConfigs.SERVER.kinetics.maxCartCouplingLength.get()) {
+ status(player, tooFar);
+ return false;
+ }
+
+ AbstractMinecartEntity cart1 = (AbstractMinecartEntity) entity1;
+ AbstractMinecartEntity cart2 = (AbstractMinecartEntity) entity2;
+ UUID mainID = cart1.getUniqueID();
+ UUID connectedID = cart2.getUniqueID();
+ MinecartController mainController = CapabilityMinecartController.getIfPresent(world, mainID);
+ MinecartController connectedController = CapabilityMinecartController.getIfPresent(world, connectedID);
+
+ if (mainController == null || connectedController == null) {
+ status(player, unloaded);
+ return false;
+ }
+ if (mainController.isFullyCoupled() || connectedController.isFullyCoupled()) {
+ status(player, tooMany);
+ return false;
+ }
+
+ if (mainController.isLeadingCoupling() && mainController.getCoupledCart(true)
+ .equals(connectedID) || connectedController.isLeadingCoupling()
+ && connectedController.getCoupledCart(true)
+ .equals(mainID))
+ return false;
+
+ for (boolean main : Iterate.trueAndFalse) {
+ MinecartController current = main ? mainController : connectedController;
+ boolean forward = current.isLeadingCoupling();
+ int safetyCount = 1000;
+
+ while (true) {
+ if (safetyCount-- <= 0) {
+ Create.logger.warn("Infinite loop in coupling iteration");
+ return false;
+ }
+
+ current = getNextInCouplingChain(world, current, forward);
+ if (current == null) {
+ status(player, unloaded);
+ return false;
+ }
+ if (current == connectedController) {
+ status(player, noLoops);
+ return false;
+ }
+ if (current == MinecartController.EMPTY)
+ break;
+ }
+ }
+
+ if (!contraptionCoupling) {
+ for (Hand hand : Hand.values()) {
+ if (player.isCreative())
+ break;
+ ItemStack heldItem = player.getHeldItem(hand);
+ if (!AllItems.MINECART_COUPLING.isIn(heldItem))
+ continue;
+ heldItem.shrink(1);
+ break;
+ }
+ }
+
+ mainController.prepareForCoupling(true);
+ connectedController.prepareForCoupling(false);
+
+ mainController.coupleWith(true, connectedID, distanceTo, contraptionCoupling);
+ connectedController.coupleWith(false, mainID, distanceTo, contraptionCoupling);
+ return true;
+ }
+
+ @Nullable
+ /**
+ * MinecartController.EMPTY if none connected, null if not yet loaded
+ */
+ public static MinecartController getNextInCouplingChain(World world, MinecartController controller,
+ boolean forward) {
+ UUID coupledCart = controller.getCoupledCart(forward);
+ if (coupledCart == null)
+ return MinecartController.empty();
+ return CapabilityMinecartController.getIfPresent(world, coupledCart);
+ }
+
+ public static void status(PlayerEntity player, String key) {
+ if (player == null)
+ return;
+ player.sendStatusMessage(new StringTextComponent(Lang.translate("minecart_coupling." + key)), true);
+ }
+
+}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandlerClient.java
similarity index 94%
rename from src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java
rename to src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandlerClient.java
index e88859cdf..00fda4789 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingHandlerClient.java
@@ -18,7 +18,7 @@ import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.vector.Vector3d;
-public class ClientMinecartCouplingHandler {
+public class CouplingHandlerClient {
static AbstractMinecartEntity selectedCart;
static Random r = new Random();
@@ -44,7 +44,7 @@ public class ClientMinecartCouplingHandler {
return;
}
spawnSelectionParticles(entity.getBoundingBox(), true);
- AllPackets.channel.sendToServer(new MinecartCouplingCreationPacket(selectedCart, entity));
+ AllPackets.channel.sendToServer(new CouplingCreationPacket(selectedCart, entity));
selectedCart = null;
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingPhysics.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingPhysics.java
new file mode 100644
index 000000000..c8d27344e
--- /dev/null
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingPhysics.java
@@ -0,0 +1,142 @@
+package com.simibubi.create.content.contraptions.components.structureMovement.train;
+
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
+import com.simibubi.create.foundation.utility.Couple;
+import com.simibubi.create.foundation.utility.Iterate;
+import com.simibubi.create.foundation.utility.VecHelper;
+
+import net.minecraft.block.AbstractRailBlock;
+import net.minecraft.block.BlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.MoverType;
+import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
+import net.minecraft.state.properties.RailShape;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vector3d;
+import net.minecraft.world.World;
+
+public class CouplingPhysics {
+
+ public static void tick(World world) {
+ CouplingHandler.forEachLoadedCoupling(world, c -> tickCoupling(world, c));
+ }
+
+ public static void tickCoupling(World world, Couple c) {
+ Couple carts = c.map(MinecartController::cart);
+ float couplingLength = c.getFirst().getCouplingLength(true);
+ softCollisionStep(world, carts, couplingLength);
+ hardCollisionStep(world, carts, couplingLength);
+ }
+
+ public static void hardCollisionStep(World world, Couple carts, double couplingLength) {
+ Couple corrections = Couple.create(null, null);
+ Couple maxSpeed = carts.map(AbstractMinecartEntity::getMaxCartSpeedOnRail);
+ boolean firstLoop = true;
+ for (boolean current : new boolean[] { true, false, true }) {
+ AbstractMinecartEntity cart = carts.get(current);
+ AbstractMinecartEntity otherCart = carts.get(!current);
+
+ float stress = (float) (couplingLength - cart.getPositionVec()
+ .distanceTo(otherCart.getPositionVec()));
+
+ RailShape shape = null;
+ BlockPos railPosition = cart.getCurrentRailPosition();
+ BlockState railState = world.getBlockState(railPosition.up());
+
+ if (railState.getBlock() instanceof AbstractRailBlock) {
+ AbstractRailBlock block = (AbstractRailBlock) railState.getBlock();
+ shape = block.getRailDirection(railState, world, railPosition, cart);
+ }
+
+ Vector3d correction = Vector3d.ZERO;
+ Vector3d pos = cart.getPositionVec();
+ Vector3d link = otherCart.getPositionVec()
+ .subtract(pos);
+ float correctionMagnitude = firstLoop ? -stress / 2f : -stress;
+ correction = shape != null ? followLinkOnRail(link, pos, correctionMagnitude, shape).subtract(pos)
+ : link.normalize()
+ .scale(correctionMagnitude);
+
+ float maxResolveSpeed = 1.75f;
+ correction = VecHelper.clamp(correction, Math.min(maxResolveSpeed, maxSpeed.get(current)));
+
+ if (corrections.get(current) == null)
+ corrections.set(current, correction);
+
+ if (shape != null)
+ MinecartSim2020.moveCartAlongTrack(cart, correction, railPosition, railState);
+ else {
+ cart.move(MoverType.SELF, correction);
+ cart.setMotion(cart.getMotion()
+ .scale(0.5f));
+ }
+ firstLoop = false;
+ }
+ }
+
+ public static void softCollisionStep(World world, Couple carts, double couplingLength) {
+
+ Couple positions = carts.map(Entity::getPositionVector);
+ Couple maxSpeed = carts.map(AbstractMinecartEntity::getMaxCartSpeedOnRail);
+ Couple canAddmotion = carts.map(MinecartSim2020::canAddMotion);
+
+ Couple shapes = carts.map(current -> {
+ BlockPos railPosition = current.getCurrentRailPosition();
+ BlockState railState = world.getBlockState(railPosition.up());
+ if (!(railState.getBlock() instanceof AbstractRailBlock))
+ return null;
+ AbstractRailBlock block = (AbstractRailBlock) railState.getBlock();
+ return block.getRailDirection(railState, world, railPosition, current);
+ });
+
+ Couple motions = carts.map(MinecartSim2020::predictMotionOf);
+ Couple nextPositions = positions.copy();
+ nextPositions.replaceWithParams(Vector3d::add, motions);
+
+ float futureStress = (float) (couplingLength - nextPositions.getFirst()
+ .distanceTo(nextPositions.getSecond()));
+ if (Math.abs(futureStress) < 1 / 128f)
+ return;
+
+ for (boolean current : Iterate.trueAndFalse) {
+ Vector3d correction = Vector3d.ZERO;
+ Vector3d pos = nextPositions.get(current);
+ Vector3d link = nextPositions.get(!current)
+ .subtract(pos);
+ float correctionMagnitude = -futureStress / 2f;
+
+ if (canAddmotion.get(current) != canAddmotion.get(!current))
+ correctionMagnitude = !canAddmotion.get(current) ? 0 : correctionMagnitude * 2;
+
+ RailShape shape = shapes.get(current);
+ correction = shape != null ? followLinkOnRail(link, pos, correctionMagnitude, shape).subtract(pos)
+ : link.normalize()
+ .scale(correctionMagnitude);
+ correction = VecHelper.clamp(correction, maxSpeed.get(current));
+ motions.set(current, motions.get(current)
+ .add(correction));
+ }
+
+ motions.replaceWithParams(VecHelper::clamp, maxSpeed);
+ carts.forEachWithParams(Entity::setMotion, motions);
+ }
+
+ public static Vector3d followLinkOnRail(Vector3d link, Vector3d cart, float diffToReduce, RailShape shape) {
+ Vector3d railAxis = MinecartSim2020.getRailVec(shape);
+ double dotProduct = railAxis.dotProduct(link);
+ if (Double.isNaN(dotProduct) || dotProduct == 0 || diffToReduce == 0)
+ return cart;
+
+ Vector3d axis = railAxis.scale(-Math.signum(dotProduct));
+ Vector3d center = cart.add(link);
+ double radius = link.length() - diffToReduce;
+ Vector3d intersectSphere = VecHelper.intersectSphere(cart, axis, center, radius);
+
+ // Cannot satisfy on current rail vector
+ if (intersectSphere == null)
+ return cart.add(VecHelper.project(link, axis));
+
+ return intersectSphere;
+ }
+
+}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java
similarity index 79%
rename from src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java
rename to src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java
index 1f4238252..19c3cc17b 100644
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java
+++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/CouplingRenderer.java
@@ -5,6 +5,10 @@ import static net.minecraft.util.math.MathHelper.lerp;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import com.simibubi.create.AllBlockPartials;
+import com.simibubi.create.CreateClient;
+import com.simibubi.create.content.contraptions.KineticDebugger;
+import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartController;
+import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.MatrixStacker;
import com.simibubi.create.foundation.utility.SuperByteBuffer;
@@ -24,13 +28,24 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
-public class MinecartCouplingRenderer {
+public class CouplingRenderer {
- public static void renderCoupling(MatrixStack ms, IRenderTypeBuffer buffer, MinecartCoupling coupling) {
- if (!coupling.areBothEndsPresent())
- return;
+ public static void renderAll(MatrixStack ms, IRenderTypeBuffer buffer) {
+ CouplingHandler.forEachLoadedCoupling(Minecraft.getInstance().world,
+ c -> {
+ if (c.getFirst().hasContraptionCoupling(true))
+ return;
+ CouplingRenderer.renderCoupling(ms, buffer, c.map(MinecartController::cart));
+ });
+ }
+
+ public static void tickDebugModeRenders() {
+ if (KineticDebugger.isActive())
+ CouplingHandler.forEachLoadedCoupling(Minecraft.getInstance().world, CouplingRenderer::doDebugRender);
+ }
+
+ public static void renderCoupling(MatrixStack ms, IRenderTypeBuffer buffer, Couple carts) {
ClientWorld world = Minecraft.getInstance().world;
- Couple carts = coupling.asCouple();
Couple lightValues =
carts.map(c -> WorldRenderer.getLightmapCoordinates(world, new BlockPos(c.getBoundingBox()
.getCenter())));
@@ -194,4 +209,29 @@ public class MinecartCouplingRenderer {
}
+ public static void doDebugRender(Couple c) {
+ int yOffset = 1;
+ MinecartController first = c.getFirst();
+ AbstractMinecartEntity mainCart = first.cart();
+ Vector3d mainCenter = mainCart.getPositionVec()
+ .add(0, yOffset, 0);
+ Vector3d connectedCenter = c.getSecond()
+ .cart()
+ .getPositionVec()
+ .add(0, yOffset, 0);
+
+ int color = ColorHelper.mixColors(0xabf0e9, 0xee8572, (float) MathHelper
+ .clamp(Math.abs(first.getCouplingLength(true) - connectedCenter.distanceTo(mainCenter)) * 8, 0, 1));
+
+ CreateClient.outliner.showLine(mainCart.getEntityId() + "", mainCenter, connectedCenter)
+ .colored(color)
+ .lineWidth(1 / 8f);
+
+ Vector3d point = mainCart.getPositionVec()
+ .add(0, yOffset, 0);
+ CreateClient.outliner.showLine(mainCart.getEntityId() + "_dot", point, point.add(0, 1 / 128f, 0))
+ .colored(0xffffff)
+ .lineWidth(1 / 4f);
+ }
+
}
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java
deleted file mode 100644
index 2f1241ad1..000000000
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.simibubi.create.content.contraptions.components.structureMovement.train;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import javax.annotation.Nullable;
-
-import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSerializer.CouplingData;
-import com.simibubi.create.foundation.utility.Couple;
-
-import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
-import net.minecraft.world.World;
-
-public class MinecartCoupling {
-
- WeakReference mainCart;
- WeakReference connectedCart;
- double length;
-
- private MinecartCoupling(AbstractMinecartEntity mainCart, AbstractMinecartEntity connectedCart, double length) {
- this.mainCart = new WeakReference<>(mainCart);
- this.connectedCart = new WeakReference<>(connectedCart);
- this.length = length;
- }
-
- public static MinecartCoupling create(AbstractMinecartEntity mainCart, AbstractMinecartEntity connectedCart) {
- return new MinecartCoupling(mainCart, connectedCart,
- Math.max(2, getDistanceBetweenCarts(mainCart, connectedCart)));
- }
-
- @Nullable
- public static List loadAllAttached(World world, AbstractMinecartEntity minecart) {
- List loaded = new ArrayList<>(2);
- List otherCartsInRange =
- world.getEntitiesWithinAABB(AbstractMinecartEntity.class, minecart.getBoundingBox()
- .grow(MinecartCouplingHandler.maxDistance()), c -> c != minecart);
-
- List couplingData = MinecartCouplingSerializer.getCouplingData(minecart);
- Connections: for (CouplingData connection : couplingData) {
- boolean cartIsMain = connection.main;
- UUID cartCouplingUUID = connection.id;
-
- for (AbstractMinecartEntity otherCart : otherCartsInRange) {
- List otherCouplingData = MinecartCouplingSerializer.getCouplingData(otherCart);
- for (CouplingData otherConnection : otherCouplingData) {
- boolean otherIsMain = otherConnection.main;
- UUID otherCouplingUUID = otherConnection.id;
-
- if (cartIsMain == otherIsMain)
- continue;
- if (!otherCouplingUUID.equals(cartCouplingUUID))
- continue;
-
- AbstractMinecartEntity mainCart = cartIsMain ? minecart : otherCart;
- AbstractMinecartEntity connectedCart = cartIsMain ? otherCart : minecart;
- loaded.add(new MinecartCoupling(mainCart, connectedCart, connection.length));
- continue Connections;
- }
- }
- }
-
- return loaded;
- }
-
- public void writeToCarts() {
- MinecartCouplingSerializer.removeCouplingFromCart(mainCart.get(), this);
- MinecartCouplingSerializer.removeCouplingFromCart(connectedCart.get(), this);
-
- MinecartCouplingSerializer.addCouplingToCart(mainCart.get(), this);
- MinecartCouplingSerializer.addCouplingToCart(connectedCart.get(), this);
- }
-
- /**
- * Swap main and connected cart for aliging couplings of a train.
- * Changes are written to the carts' nbt data!
- * Id of this coupling will change!
- */
- public void flip() {
- MinecartCouplingSerializer.removeCouplingFromCart(mainCart.get(), this);
- MinecartCouplingSerializer.removeCouplingFromCart(connectedCart.get(), this);
-
- WeakReference oldMain = mainCart;
- mainCart = connectedCart;
- connectedCart = oldMain;
-
- MinecartCouplingSerializer.addCouplingToCart(mainCart.get(), this);
- MinecartCouplingSerializer.addCouplingToCart(connectedCart.get(), this);
- }
-
- public static double getDistanceBetweenCarts(AbstractMinecartEntity mainCart,
- AbstractMinecartEntity connectedCart) {
- return mainCart.getBoundingBox()
- .getCenter()
- .subtract(connectedCart.getBoundingBox()
- .getCenter())
- .length();
- }
-
- public boolean areBothEndsPresent() {
- return (mainCart.get() != null && mainCart.get()
- .isAlive()) && (connectedCart.get() != null
- && connectedCart.get()
- .isAlive());
- }
-
- public UUID getId() {
- return mainCart.get()
- .getUniqueID();
- }
-
- public Couple asCouple() {
- return Couple.create(mainCart.get(), connectedCart.get());
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java
deleted file mode 100644
index a55fea532..000000000
--- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java
+++ /dev/null
@@ -1,404 +0,0 @@
-package com.simibubi.create.content.contraptions.components.structureMovement.train;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.UUID;
-
-import javax.annotation.Nullable;
-
-import org.apache.commons.lang3.tuple.Pair;
-
-import com.mojang.blaze3d.matrix.MatrixStack;
-import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
-import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSerializer.CouplingData;
-import com.simibubi.create.foundation.config.AllConfigs;
-import com.simibubi.create.foundation.networking.AllPackets;
-import com.simibubi.create.foundation.utility.Couple;
-import com.simibubi.create.foundation.utility.WorldAttached;
-
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
-import it.unimi.dsi.fastutil.objects.ObjectLists;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.renderer.IRenderTypeBuffer;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.nbt.CompoundNBT;
-import net.minecraft.world.World;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.api.distmarker.OnlyIn;
-import net.minecraftforge.fml.network.PacketDistributor;
-
-/*
- *
- * Couplings are a directional connection of two Minecart entities
- * - ID and Key is the UUID of the main cart
- * - They are immediately written to both minecarts' nbt tags upon creation.
- * {Main: true, Id: {L: ", M: "}, Length: 5}
- *
- * Trains are an ordered list of Couplings
- * - ID and Key is the UUID of the main coupling
- * - Every coupling is part of exactly one train, lonely couplings are still treated as such
- * - When two trains are merged, the couplings have to be re-oriented to always point towards the main coupling
- *
- * Loaded carts are queued to be dealt with on world tick,
- * so that the world functions are not accessed during the chunk deserialization
- *
- * Challenges:
- * - Minecarts can only be corrected by changing their motion or their position
- * - A Minecarts' motion vector does not represent its actual movement next tick
- * - There is no accessible simulation step (can be copied / at'd)
- * - It is not always known which cart is ahead/behind
- * - If both ends keep a contant momentum, the link stress is not necessarily satisfied
- * - Carts cannot be "dragged" directly towards resolving link stress;
- * It is not entirely predictable how motions outside of the rail vector get projected
- *
- *
- *
- * Day III, couplings still too unstable. Why is that? What causes the instability, is it the general approach or a specific issue
- * Explored strategies:
- *
- * Acellerate against violation diff -> perpetual motion, Jittering, bouncyness
- * Brake and correct towards violation diff -> quick loss of momentum
- * Move against diff -> de-rails carts on tricky paths
- *
- * Not yet explored: running an actual simulation step for the minecarts' movement.
- *
- * - satisfied link
- * -- stretched link
- * . shortened link
- * ? not visible in ctx
- * = cart
- * => moving cart
- *
- * Create algorithm to find a tick order which maximizes resolved stress
- *
- * => ? <= ? = - => (@t)
- * ^ tick here first
- *
- * cart[], motion[], position[]
- * Predict through simulation + motion, that without any intervention, this happens:
- *
- * => ? <= ? = -- => (@t+1)
- *
- * Decision: Accelerate trailing? (free motion)
- * Brake leading? (loss of momentum)
- * -> Both?
- *
- * Soft collisions can always be resolved. Just have to adjust motions accordingly.
- * Hard collisions should never be resolved by the soft/motion resolver, as it would generate or void momentum!
- *
- * Approach: Hard pass then soft pass. two iterations of the coupling list
- *
- * find starting point of hard resolve: the center of balance
- * i from left, j from right
- * compare and approach the exact center of the resolved chain.
- *
- * -3 -2-10v 0
- * 0-----0-0-0-0
- * 0--0--0--0--0
- * 2 1
- * 0---0-0---0---0--0--0-0-0-0-0
- * 0--0--0--0--0--0--0--0--0--0--0
- *
- * v
- * 0-0-0
- * 0--0--0
- *
- * v
- * 0---0---0---0
- * 0-0-0-0
- *
- * -1 0 -1 0
- * 0-0---0--0---0-0
- * 0--0--0--0--0--0
- *
- *
- *
- * iterate both ways from the center and resolve hard collisions.
- *
- *
- * if coupling is NOT ok @t {
- * Something unpredictable happened.
- * Try to reach soft state asap. every lost cycle in hard will act strangely and render inconsistently
- * Using motion to hard resolve is probably a bad idea
- *
- * if cart on rail -> force move along rail away from collision
- * else straight-up push it
- * use simulation to test if movements are possible
- *
- * hard resolves are usually quite tiny. If they go beyond 1m then something really messed up
- * }
- *
- * if coupling could not be fixed {
- * clear all motion of the two carts (?)
- * A failed hard collision implies that the Minecarts cannot be forced together/apart, might aswell not make things worse
- * }
- *
- *
- * Soft collisions only mess with motion values. It is still good to find a good order of iteration-
- * that way predictions of earlier couplings in the loop are still accurate
- *
- * =>>> - = - <= - =>>
- *
- * left to right
- * =>> - = - => - =>
- * right to left
- * =>> - => - = - =>
- *
- *
- *
- *
- * if now coupling is ok @t {
- *
- * Run Prediction I
- * if (coupling is ok @t+1)
- * my job here is done; return
- *
- * get required force to resolve (front or back)
- * distribute equally over both carts
- *
- * Run Prediction II
- * if (coupling is ok @t+1*)
- * looks good; return
- *
- * re-distribute force to other cart
- * all collisions should be fixed at this point. return;
- * (in case of sudden changes/ bad predictions, the next cycle can handle the hard resolve)
- *
- * }
- *
- *
- *
- * NEXT STEPS
- * 1. normalize diagonal rail vectors and debug all possible rail motion transfers. The required tools have to work properly
- * 2. implement a prediction step
- * 3. find a suitable hard collision resolver
- * 4. find a suitable soft collision order
- *
- */
-
-public class MinecartCouplingHandler {
-
- static WorldAttached