Choo Choo, Part IV

- Trains now play a sound sequence based on attached blocks when arriving at a station after travelling for some time
- Trains can now be stalled by movement behaviours of mounted blocks
- Fixed bells constantly activating when mounted to a train
- Max train length is now configurable and substantially longer
This commit is contained in:
simibubi 2022-05-10 00:16:12 +02:00
parent 520983d5d0
commit e281512787
38 changed files with 443 additions and 86 deletions

View file

@ -544,22 +544,22 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
875f9aff979888b9d63d6a425cbf544431f1af5a assets/create/lang/en_ud.json
c8e4df6292133a1873e4dccb34c00b1e52afcd92 assets/create/lang/en_us.json
3ca43c518fc5c0120b5cf2aaf37cc78f51625fd4 assets/create/lang/unfinished/de_de.json
128d3e35cf2d41dacdb54abd8562091f15323a8c assets/create/lang/unfinished/es_cl.json
f02d6b15275a22ac665303e6d7181998f408e1a7 assets/create/lang/unfinished/es_es.json
02e43d3f9d2527c2cd72f1a5bfe76397b6cfa32f assets/create/lang/unfinished/fr_fr.json
9f0982f15705f8c3f675f9dfac5b68a753ddf97a assets/create/lang/unfinished/it_it.json
84d0f74256115e505a239ed826b28002fbc67f86 assets/create/lang/unfinished/ja_jp.json
96dfb6f44098b2a44c4ba6c53ed6f42a4d7811f5 assets/create/lang/unfinished/ko_kr.json
5ea64ddc9613b72376220353819a477bf4c6d64d assets/create/lang/unfinished/nl_nl.json
dd81cd81f426833b627831530c77ab3f3655bb39 assets/create/lang/unfinished/pl_pl.json
7cdff911043ccc6232932f85825090305ce92426 assets/create/lang/unfinished/pt_br.json
ba139e8ea012ed05598ea5bb57ee6ac95693efdd assets/create/lang/unfinished/pt_pt.json
86ac8dc000591768856bbaa873f9d101faddc1cd assets/create/lang/unfinished/ro_ro.json
14988b8d3a0b97a7c6b49f392d5088fd59a3aaaa assets/create/lang/unfinished/ru_ru.json
99ffa28fe67913dda825628947e5763f0a21ee8a assets/create/lang/unfinished/zh_cn.json
01da3a96eb7aeea75afed38dd1a83321959fcf0a assets/create/lang/unfinished/zh_tw.json
0a0155981901a175ef3d80376bd12bf6ed87fc40 assets/create/lang/en_us.json
2441d72bd7c3f9bd175a3b7ad6dfd08650244333 assets/create/lang/unfinished/de_de.json
c78d38ed11410f3749b0531faddb47bc864c602e assets/create/lang/unfinished/es_cl.json
3f324c1793d15fc4efee2496ded04ef40a149eef assets/create/lang/unfinished/es_es.json
a75c200cad527244a00e9c8d8044b99ebcb4f434 assets/create/lang/unfinished/fr_fr.json
f8498450f21370fc25144fb02d7f3de644302eae assets/create/lang/unfinished/it_it.json
fb99ffda9c4f753478d69d4a7e672d2934e4eefe assets/create/lang/unfinished/ja_jp.json
c5b3a59940d9d1f31bd333acb05e77a07833f8f7 assets/create/lang/unfinished/ko_kr.json
1a8e15b473cc565d4a0ce6a077ebb21b04436caa assets/create/lang/unfinished/nl_nl.json
a4c4763232172a82d131bdcf9418e8944faa4767 assets/create/lang/unfinished/pl_pl.json
4293a80519e12fe25df446daf66a2c93abde0c29 assets/create/lang/unfinished/pt_br.json
40b6e4ba6fbdc90902288bbf0be9fd65a08d0bce assets/create/lang/unfinished/pt_pt.json
e4a748bfbb60cb7b597921115ce69b45bbfb3d79 assets/create/lang/unfinished/ro_ro.json
5539e89820b997b42e5605202e5c8ba508e80b77 assets/create/lang/unfinished/ru_ru.json
b3f274b8dc40d041733ba10cab527c5130f68bcf assets/create/lang/unfinished/zh_cn.json
5309ae6bd90efd2cf5e8adb730c2d2f4818edf7d assets/create/lang/unfinished/zh_tw.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
@ -2155,7 +2155,7 @@ d080b1b25e5bc8baf5aee68691b08c7f12ece3b0 assets/create/models/item/windmill_bear
866fbb0ce2878a73e0440d1caf6534c8bd7c384f assets/create/models/item/zinc_ingot.json
a80fb25a0b655e76be986b5b49fcb0f03461a1ab assets/create/models/item/zinc_nugget.json
b1689617190c05ef34bd18456b0c7ae09bb3210f assets/create/models/item/zinc_ore.json
92c34840e05861f7f98aa2f942f9a90370f82cd6 assets/create/sounds.json
56f5b100aa98b37efb44b85856ff4bfeaa7a89ec assets/create/sounds.json
0f1b4b980afba9bf2caf583b88e261bba8b10313 data/create/advancements/aesthetics.json
613e64b44bed959da899fdd54c1cacb227fb33f2 data/create/advancements/andesite_alloy.json
81885c6bfb85792c88aaa7c9b70f58832945d31f data/create/advancements/andesite_casing.json

View file

@ -1591,7 +1591,10 @@
"create.subtitle.potato_hit": "Vegetable impacts",
"create.subtitle.saw_activate_wood": "Mechanical Saw activates",
"create.subtitle.whistle_high": "High whistling",
"create.subtitle.whistle_train": "Whistling",
"create.subtitle.haunted_bell_convert": "Haunted Bell awakens",
"create.subtitle.whistle_train_high": "High whistling",
"create.subtitle.whistle_train_low": "Low whistling",
"create.subtitle.deny": "Declining boop",
"create.subtitle.controller_click": "Controller clicks",
"create.subtitle.whistle_low": "Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1534",
"_": "Missing Localizations: 1537",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 556",
"_": "Missing Localizations: 559",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Impactos vegetales",
"create.subtitle.saw_activate_wood": "Sierra Mecánica se activa",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "Campana Embrujada despierta",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "Boop denegante",
"create.subtitle.controller_click": "Controlador cliquea",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 227",
"_": "Missing Localizations: 230",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Impacto de vegetal",
"create.subtitle.saw_activate_wood": "Sierra mecánica activada",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "Campana maldita se despierta",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "Pitido denegante",
"create.subtitle.controller_click": "",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1796",
"_": "Missing Localizations: 1799",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1485",
"_": "Missing Localizations: 1488",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 222",
"_": "Missing Localizations: 225",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "野菜の衝撃",
"create.subtitle.saw_activate_wood": "メカニカルソーが動作する",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "憑りつかれた鐘が目覚める",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "失敗音",
"create.subtitle.controller_click": "コントローラーのカチカチ音",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 222",
"_": "Missing Localizations: 225",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "채소가 부딫힘",
"create.subtitle.saw_activate_wood": "톱이 작동함",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "종에 귀신이 들림",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "취소음",
"create.subtitle.controller_click": "조작기를 누름",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 2148",
"_": "Missing Localizations: 2151",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 595",
"_": "Missing Localizations: 598",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Warzywo ląduje",
"create.subtitle.saw_activate_wood": "Mechaniczna piła aktywuje się",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "Nawiedzony dzwon budzi się",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "Dźwięk odmowy",
"create.subtitle.controller_click": "Sterownik klika",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1396",
"_": "Missing Localizations: 1399",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 1768",
"_": "Missing Localizations: 1771",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "UNLOCALIZED: Vegetable impacts",
"create.subtitle.saw_activate_wood": "UNLOCALIZED: Mechanical Saw activates",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "UNLOCALIZED: Haunted Bell awakens",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "UNLOCALIZED: Controller clicks",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 223",
"_": "Missing Localizations: 226",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Impact de legumă",
"create.subtitle.saw_activate_wood": "Ferăstrău Mecanic se activează",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "Clopot Bântuit se trezețte",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "Boop de refuz",
"create.subtitle.controller_click": "Controlor clickuiește",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 600",
"_": "Missing Localizations: 603",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Овощ врезается",
"create.subtitle.saw_activate_wood": "Активируется механическая пила",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "Призрачный колокол пробуждается",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "Отрицательный «Буп»",
"create.subtitle.controller_click": "Клики контроллера",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 222",
"_": "Missing Localizations: 225",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "土豆:击中",
"create.subtitle.saw_activate_wood": "动力锯:切割",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "奇异钟:转化",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "提示声:出错",
"create.subtitle.controller_click": "遥控器:按下按钮",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -1,5 +1,5 @@
{
"_": "Missing Localizations: 614",
"_": "Missing Localizations: 617",
"_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "食物撞擊聲",
"create.subtitle.saw_activate_wood": "機械鋸子運作聲",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train": "UNLOCALIZED: Whistling",
"create.subtitle.haunted_bell_convert": "靈魂鐘轉化聲",
"create.subtitle.whistle_train_high": "UNLOCALIZED: High whistling",
"create.subtitle.whistle_train_low": "UNLOCALIZED: Low whistling",
"create.subtitle.deny": "UNLOCALIZED: Declining boop",
"create.subtitle.controller_click": "遙控器按鍵聲",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

@ -419,6 +419,33 @@
],
"subtitle": "create.subtitle.whistle_low"
},
"whistle_train": {
"sounds": [
{
"name": "create:whistle_train",
"type": "file"
}
],
"subtitle": "create.subtitle.whistle_train"
},
"whistle_train_high": {
"sounds": [
{
"name": "create:whistle_train_high",
"type": "file"
}
],
"subtitle": "create.subtitle.whistle_train_high"
},
"whistle_train_low": {
"sounds": [
{
"name": "create:whistle_train_low",
"type": "file"
}
],
"subtitle": "create.subtitle.whistle_train_low"
},
"worldshaper_place": {
"sounds": [
{

View file

@ -228,7 +228,7 @@ public class AllSoundEvents {
.attenuationDistance(64)
.build(),
WHISTLE = create("whistle").subtitle("Whistling")
WHISTLE_MEDIUM = create("whistle").subtitle("Whistling")
.category(SoundSource.RECORDS)
.attenuationDistance(64)
.build(),
@ -238,6 +238,18 @@ public class AllSoundEvents {
.attenuationDistance(64)
.build(),
WHISTLE_TRAIN_HIGH = create("whistle_train_high").subtitle("High whistling")
.category(SoundSource.RECORDS)
.build(),
WHISTLE_TRAIN_MEDIUM = create("whistle_train").subtitle("Whistling")
.category(SoundSource.RECORDS)
.build(),
WHISTLE_TRAIN_LOW = create("whistle_train_low").subtitle("Low whistling")
.category(SoundSource.RECORDS)
.build(),
WHISTLE_CHIFF = create("chiff").noSubtitle()
.category(SoundSource.RECORDS)
.build(),

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.curiosities.bell.AbstractBellBlock;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
import net.minecraft.core.BlockPos;
import net.minecraft.sounds.SoundEvents;
@ -17,6 +18,11 @@ public class BellMovementBehaviour implements MovementBehaviour {
public boolean renderAsNormalTileEntity() {
return true;
}
@Override
public boolean isActive(MovementContext context) {
return !(context.contraption instanceof CarriageContraption);
}
@Override
public void onSpeedChanged(MovementContext context, Vec3 oldMotion, Vec3 motion) {
@ -28,7 +34,7 @@ public class BellMovementBehaviour implements MovementBehaviour {
@Override
public void stopMoving(MovementContext context) {
if (context.position != null)
if (context.position != null && isActive(context))
playSound(context);
}

View file

@ -83,8 +83,13 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
.getNormal());
facingVec = context.rotation.apply(facingVec);
Vec3 vec = context.position.subtract(facingVec.scale(2));
float xRot = AbstractContraptionEntity.pitchFromVector(facingVec) - 90;
if (Math.abs(xRot) > 89)
facingVec = context.rotation.apply(new Vec3(0, 0, 1));
player.setYRot(AbstractContraptionEntity.yawFromVector(facingVec));
player.setXRot(AbstractContraptionEntity.pitchFromVector(facingVec) - 90);
player.setXRot(xRot);
DeployerHandler.activate(player, vec, pos, facingVec, mode);
}
@ -105,7 +110,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (schematicWorld == null)
return;
if (!schematicWorld.getBounds()
.isInside(pos.subtract(schematicWorld.anchor)))
.isInside(pos.subtract(schematicWorld.anchor)))
return;
BlockState blockState = schematicWorld.getBlockState(pos);
ItemRequirement requirement = ItemRequirement.of(blockState, schematicWorld.getBlockEntity(pos));
@ -121,15 +126,15 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
IItemHandler iItemHandler = context.contraption.inventory;
for (ItemRequirement.StackRequirement required : requiredItems) {
int amountFound = ItemHelper
.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO,
required.item.getCount(), true)
.getCount();
.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO,
required.item.getCount(), true)
.getCount();
if (amountFound < required.item.getCount())
return;
}
for (ItemRequirement.StackRequirement required : requiredItems)
ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO,
required.item.getCount(), false);
ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s),
ExtractionCountMode.UPTO, required.item.getCount(), false);
}
CompoundTag data = null;
@ -183,7 +188,8 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (player == null)
return;
context.tileData.put("Inventory", player.getInventory().save(new ListTag()));
context.tileData.put("Inventory", player.getInventory()
.save(new ListTag()));
player.discard();
}
@ -215,8 +221,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (itemstack.isEmpty())
continue;
if (list == inv.items && i == inv.selected
&& FilterItem.test(context.world, itemstack, filter))
if (list == inv.items && i == inv.selected && FilterItem.test(context.world, itemstack, filter))
continue;
dropItem(context, itemstack);
@ -237,9 +242,11 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
private DeployerFakePlayer getPlayer(MovementContext context) {
if (!(context.temporaryData instanceof DeployerFakePlayer) && context.world instanceof ServerLevel) {
DeployerFakePlayer deployerFakePlayer = new DeployerFakePlayer((ServerLevel) context.world);
deployerFakePlayer.getInventory().load(context.tileData.getList("Inventory", Tag.TAG_COMPOUND));
deployerFakePlayer.getInventory()
.load(context.tileData.getList("Inventory", Tag.TAG_COMPOUND));
if (context.data.contains("HeldItem"))
deployerFakePlayer.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.of(context.data.getCompound("HeldItem")));
deployerFakePlayer.setItemInHand(InteractionHand.MAIN_HAND,
ItemStack.of(context.data.getCompound("HeldItem")));
context.tileData.remove("Inventory");
context.temporaryData = deployerFakePlayer;
}
@ -257,7 +264,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
@Override
public void renderInContraption(MovementContext context, VirtualRenderWorld renderWorld,
ContraptionMatrices matrices, MultiBufferSource buffers) {
if (!Backend.isOn())
if (!Backend.isOn())
DeployerRenderer.renderInContraption(context, renderWorld, matrices, buffers);
}
@ -268,7 +275,8 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
@Nullable
@Override
public ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld, MovementContext context) {
public ActorInstance createInstance(MaterialManager materialManager, VirtualRenderWorld simulationWorld,
MovementContext context) {
return new DeployerActorInstance(materialManager, simulationWorld, context);
}
}

View file

@ -1,6 +1,6 @@
package com.simibubi.create.content.contraptions.components.steam.whistle;
import static com.simibubi.create.AllSoundEvents.WHISTLE;
import static com.simibubi.create.AllSoundEvents.WHISTLE_MEDIUM;
import static com.simibubi.create.AllSoundEvents.WHISTLE_HIGH;
import static com.simibubi.create.AllSoundEvents.WHISTLE_LOW;
@ -18,7 +18,7 @@ public class WhistleSoundInstance extends AbstractTickableSoundInstance {
private WhistleSize size;
public WhistleSoundInstance(WhistleSize size, BlockPos worldPosition) {
super((size == WhistleSize.SMALL ? WHISTLE_HIGH : size == WhistleSize.MEDIUM ? WHISTLE : WHISTLE_LOW)
super((size == WhistleSize.SMALL ? WHISTLE_HIGH : size == WhistleSize.MEDIUM ? WHISTLE_MEDIUM : WHISTLE_LOW)
.getMainEvent(), SoundSource.RECORDS);
this.size = size;
looping = true;

View file

@ -11,6 +11,8 @@ import com.simibubi.create.content.contraptions.components.actors.PloughBlock;
import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.content.contraptions.components.crank.HandCrankBlock;
import com.simibubi.create.content.contraptions.components.fan.NozzleBlock;
import com.simibubi.create.content.contraptions.components.steam.whistle.WhistleBlock;
import com.simibubi.create.content.contraptions.components.steam.whistle.WhistleExtenderBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.ClockworkBearingBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.ClockworkBearingTileEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.MechanicalBearingBlock;
@ -146,8 +148,7 @@ public class BlockMovementChecks {
/**
* Attached blocks will move if blocks they are attached to are moved
*/
public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos,
Direction direction) {
public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction) {
for (AttachedCheck check : ATTACHED_CHECKS) {
CheckResult result = check.isBlockAttachedTowards(state, world, pos, direction);
if (result != CheckResult.PASS) {
@ -179,10 +180,10 @@ public class BlockMovementChecks {
if (state.getBlock() instanceof FenceGateBlock)
return true;
if (state.getMaterial()
.isReplaceable())
.isReplaceable())
return false;
if (state.getCollisionShape(world, pos)
.isEmpty())
.isEmpty())
return false;
return true;
}
@ -258,11 +259,15 @@ public class BlockMovementChecks {
return true;
if (block instanceof WoolCarpetBlock)
return true;
if (block instanceof WhistleBlock)
return true;
if (block instanceof WhistleExtenderBlock)
return true;
return AllBlockTags.BRITTLE.matches(state);
}
private static boolean isBlockAttachedTowardsFallback(BlockState state, Level world, BlockPos pos,
Direction direction) {
Direction direction) {
Block block = state.getBlock();
if (block instanceof LadderBlock)
return state.getValue(LadderBlock.FACING) == direction.getOpposite();
@ -331,18 +336,23 @@ public class BlockMovementChecks {
}
if (state.getBlock() instanceof SailBlock)
return direction.getAxis() != state.getValue(SailBlock.FACING)
.getAxis();
.getAxis();
if (state.getBlock() instanceof FluidTankBlock)
return FluidTankConnectivityHandler.isConnected(world, pos, pos.relative(direction));
if (state.getBlock() instanceof ItemVaultBlock)
return ItemVaultConnectivityHandler.isConnected(world, pos, pos.relative(direction));
if (AllBlocks.STICKER.has(state) && state.getValue(StickerBlock.EXTENDED)) {
return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
}
if (block instanceof IBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state)
.contains(direction);
if (block instanceof WhistleBlock)
return direction == (state.getValue(WhistleBlock.WALL) ? state.getValue(WhistleBlock.FACING)
: Direction.DOWN);
if (block instanceof WhistleExtenderBlock)
return direction == Direction.DOWN;
return false;
}
@ -355,7 +365,7 @@ public class BlockMovementChecks {
return state.getValue(HarvesterBlock.FACING) == facing;
if (AllBlocks.MECHANICAL_PLOUGH.has(state))
return state.getValue(PloughBlock.FACING) == facing;
if (AllBlocks.CART_ASSEMBLER.has(state))
return Direction.DOWN == facing;
if (AllBlocks.MECHANICAL_SAW.has(state))
@ -373,10 +383,10 @@ public class BlockMovementChecks {
.getAxis();
if (AllBlocks.PISTON_EXTENSION_POLE.has(state))
return facing.getAxis() != state.getValue(BlockStateProperties.FACING)
.getAxis();
.getAxis();
if (AllBlocks.MECHANICAL_PISTON_HEAD.has(state))
return facing.getAxis() != state.getValue(BlockStateProperties.FACING)
.getAxis();
.getAxis();
if (AllBlocks.STICKER.has(state) && !state.getValue(StickerBlock.EXTENDED))
return facing == state.getValue(StickerBlock.FACING);
return isBrittle(state);
@ -415,13 +425,12 @@ public class BlockMovementChecks {
public CheckResult isNotSupportive(BlockState state, Direction direction);
}
public static interface AllChecks extends MovementNecessaryCheck, MovementAllowedCheck, BrittleCheck, AttachedCheck, NotSupportiveCheck {
public static interface AllChecks
extends MovementNecessaryCheck, MovementAllowedCheck, BrittleCheck, AttachedCheck, NotSupportiveCheck {
}
public static enum CheckResult {
SUCCESS,
FAIL,
PASS;
SUCCESS, FAIL, PASS;
public Boolean toBoolean() {
return this == PASS ? null : (this == SUCCESS ? true : false);

View file

@ -0,0 +1,133 @@
package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList;
import java.util.HashMap;
import javax.annotation.Nullable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.contraptions.components.steam.whistle.WhistleBlock;
import com.simibubi.create.content.contraptions.components.steam.whistle.WhistleBlock.WhistleSize;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.BellBlock;
import net.minecraft.world.level.block.NoteBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
public class ArrivalSoundQueue {
public int offset;
int min, max;
Multimap<Integer, BlockPos> sources;
public ArrivalSoundQueue() {
sources = Multimaps.newMultimap(new HashMap<>(), ArrayList::new);
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
}
@Nullable
public Integer firstTick() {
return sources.isEmpty() ? null : min + offset;
}
@Nullable
public Integer lastTick() {
return sources.isEmpty() ? null : max + offset;
}
public boolean tick(CarriageContraptionEntity entity, int tick, boolean backwards) {
tick = tick - offset;
if (!sources.containsKey(tick))
return backwards ? tick > min : tick < max;
Contraption contraption = entity.getContraption();
for (BlockPos blockPos : sources.get(tick))
play(entity, contraption.getBlocks()
.get(blockPos));
return backwards ? tick > min : tick < max;
}
public void serialize(CompoundTag tagIn) {
CompoundTag tag = new CompoundTag();
tag.putInt("Offset", offset);
tag.put("Sources", NBTHelper.writeCompoundList(sources.entries(), e -> {
CompoundTag c = new CompoundTag();
c.putInt("Tick", e.getKey());
c.put("Pos", NbtUtils.writeBlockPos(e.getValue()));
return c;
}));
tagIn.put("SoundQueue", tag);
}
public void deserialize(CompoundTag tagIn) {
CompoundTag tag = tagIn.getCompound("SoundQueue");
offset = tag.getInt("Offset");
NBTHelper.iterateCompoundList(tag.getList("Sources", Tag.TAG_COMPOUND),
c -> add(c.getInt("Tick"), NbtUtils.readBlockPos(c.getCompound("Pos"))));
}
public void add(int offset, BlockPos localPos) {
sources.put(offset, localPos);
min = Math.min(offset, min);
max = Math.max(offset, max);
}
public static boolean isPlayable(BlockState state) {
if (state.getBlock() instanceof BellBlock)
return true;
if (state.getBlock() instanceof NoteBlock)
return true;
if (state.getBlock() instanceof WhistleBlock)
return true;
return false;
}
public static void play(CarriageContraptionEntity entity, StructureBlockInfo info) {
if (info == null)
return;
BlockState state = info.state;
if (state.getBlock() instanceof BellBlock) {
if (AllBlocks.HAUNTED_BELL.has(state))
playSimple(entity, AllSoundEvents.HAUNTED_BELL_USE.getMainEvent(), 1, 1);
else
playSimple(entity, SoundEvents.BELL_BLOCK, 1, 1);
}
if (state.getBlock()instanceof NoteBlock nb) {
float f = (float) Math.pow(2, (state.getValue(NoteBlock.NOTE) - 12) / 12.0);
playSimple(entity, state.getValue(NoteBlock.INSTRUMENT)
.getSoundEvent(), 1, f);
}
if (state.getBlock() instanceof WhistleBlock && info.nbt != null) {
int pitch = info.nbt.getInt("Pitch");
float f = (float) Math.pow(2, (pitch - 12) / 12.0);
WhistleSize size = state.getValue(WhistleBlock.SIZE);
playSimple(entity, (size == WhistleSize.LARGE ? AllSoundEvents.WHISTLE_TRAIN_LOW
: size == WhistleSize.MEDIUM ? AllSoundEvents.WHISTLE_TRAIN_MEDIUM : AllSoundEvents.WHISTLE_TRAIN_HIGH)
.getMainEvent(),
1, f);
playSimple(entity, AllSoundEvents.WHISTLE_CHIFF.getMainEvent(), .75f,
size == WhistleSize.SMALL ? f + .75f : f);
}
}
private static void playSimple(CarriageContraptionEntity entity, SoundEvent event, float volume, float pitch) {
entity.level.playSound(null, entity, event, SoundSource.NEUTRAL, 5 * volume, pitch);
}
}

View file

@ -36,6 +36,7 @@ public class Carriage {
public Train train;
public int id;
public boolean blocked;
public boolean stalled;
public Couple<Boolean> presentConductors;
public int bogeySpacing;
@ -50,6 +51,7 @@ public class Carriage {
// client
public boolean pointsInitialised;
static final int FIRST = 0, MIDDLE = 1, LAST = 2, BOTH = 3;
public Carriage(CarriageBogey bogey1, @Nullable CarriageBogey bogey2, int bogeySpacing) {
@ -284,6 +286,7 @@ public class Carriage {
tag.putInt("Spacing", bogeySpacing);
tag.putBoolean("FrontConductor", presentConductors.getFirst());
tag.putBoolean("BackConductor", presentConductors.getSecond());
tag.putBoolean("Stalled", stalled);
CarriageContraptionEntity entity = this.entity.get();
if (entity != null)
@ -303,6 +306,7 @@ public class Carriage {
Carriage carriage = new Carriage(bogey1, bogey2, tag.getInt("Spacing"));
carriage.stalled = tag.getBoolean("Stalled");
carriage.presentConductors = Couple.create(tag.getBoolean("FrontConductor"), tag.getBoolean("BackConductor"));
carriage.serialisedEntity = tag.getCompound("Entity")
.copy();

View file

@ -21,6 +21,7 @@ import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -42,6 +43,7 @@ public class CarriageContraption extends Contraption {
private boolean backwardControls;
public Couple<Boolean> blazeBurnerConductors;
public Map<BlockPos, Couple<Boolean>> conductorSeats;
public ArrivalSoundQueue soundQueue;
// during assembly only
private int bogeys;
@ -53,6 +55,11 @@ public class CarriageContraption extends Contraption {
conductorSeats = new HashMap<>();
assembledBlazeBurners = new ArrayList<>();
blazeBurnerConductors = Couple.create(false, false);
soundQueue = new ArrivalSoundQueue();
}
public void setSoundQueueOffset(int offset) {
soundQueue.offset = offset;
}
public CarriageContraption(Direction assemblyDirection) {
@ -106,6 +113,13 @@ public class CarriageContraption extends Contraption {
protected Pair<StructureBlockInfo, BlockEntity> capture(Level world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if (ArrivalSoundQueue.isPlayable(blockState)) {
int anchorCoord = VecHelper.getCoordinate(anchor, assemblyDirection.getAxis());
int posCoord = VecHelper.getCoordinate(pos, assemblyDirection.getAxis());
soundQueue.add((posCoord - anchorCoord) * assemblyDirection.getAxisDirection()
.getStep(), toLocalPos(pos));
}
if (blockState.getBlock() instanceof IBogeyBlock) {
bogeys++;
if (bogeys == 2)
@ -151,6 +165,7 @@ public class CarriageContraption extends Contraption {
return compoundTag;
});
tag.put("ConductorSeats", list);
soundQueue.serialize(tag);
return tag;
}
@ -165,6 +180,7 @@ public class CarriageContraption extends Contraption {
NBTHelper.iterateCompoundList(nbt.getList("ConductorSeats", Tag.TAG_COMPOUND),
c -> conductorSeats.put(NbtUtils.readBlockPos(c.getCompound("Pos")),
Couple.create(c.getBoolean("Forward"), c.getBoolean("Backward"))));
soundQueue.deserialize(nbt);
super.readNBT(world, nbt, spawnData);
}

View file

@ -2,19 +2,18 @@ package com.simibubi.create.content.logistics.trains.entity;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.base.Strings;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.AllEntityDataSerializers;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -65,10 +64,15 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
public boolean leftTickingChunks;
public boolean firstPositionUpdate;
private boolean arrivalSoundPlaying;
private boolean arrivalSoundReversed;
private int arrivalSoundTicks;
public CarriageContraptionEntity(EntityType<?> type, Level world) {
super(type, world);
validForRender = false;
firstPositionUpdate = true;
arrivalSoundTicks = Integer.MIN_VALUE;
}
@Override
@ -167,15 +171,13 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
}
tickActors();
contraption.stalled = false;
for (MutablePair<StructureBlockInfo, MovementContext> pair : contraption.getActors()) {
MovementContext context = pair.right;
context.stall = false;
}
boolean isStalled = isStalled();
carriage.stalled = isStalled;
CarriageSyncData carriageData = getCarriageData();
if (!level.isClientSide) {
entityData.set(SCHEDULED, carriage.train.runtime.getSchedule() != null);
boolean shouldCarriageSyncThisTick =
carriage.train.shouldCarriageSyncThisTick(level.getGameTime(), getType().updateInterval());
@ -184,6 +186,18 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
entityData.set(CARRIAGE_DATA, carriageData);
carriageData.setDirty(false);
}
Navigation navigation = carriage.train.navigation;
if (navigation.announceArrival && Math.abs(navigation.distanceToDestination) < 60
&& carriageIndex == (carriage.train.speed < 0 ? carriage.train.carriages.size() - 1 : 0)) {
navigation.announceArrival = false;
arrivalSoundPlaying = true;
arrivalSoundReversed = carriage.train.speed < 0;
arrivalSoundTicks = Integer.MIN_VALUE;
}
if (arrivalSoundPlaying)
tickArrivalSound(cc);
return;
}
@ -223,6 +237,59 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
validForRender = true;
}
private void tickArrivalSound(CarriageContraption cc) {
List<Carriage> carriages = carriage.train.carriages;
if (arrivalSoundTicks == Integer.MIN_VALUE) {
int carriageCount = carriages.size();
Integer tick = null;
for (int index = 0; index < carriageCount; index++) {
int i = arrivalSoundReversed ? carriageCount - 1 - index : index;
Carriage carriage = carriages.get(i);
CarriageContraptionEntity entity = carriage.entity.get();
if (entity == null || !(entity.contraption instanceof CarriageContraption otherCC))
break;
tick = arrivalSoundReversed ? otherCC.soundQueue.lastTick() : otherCC.soundQueue.firstTick();
if (tick != null)
break;
}
if (tick == null) {
arrivalSoundPlaying = false;
return;
}
arrivalSoundTicks = tick;
}
if (tickCount % 2 == 0)
return;
boolean keepTicking = false;
for (Carriage c : carriages) {
CarriageContraptionEntity entity = c.entity.get();
if (entity == null || !(entity.contraption instanceof CarriageContraption otherCC))
continue;
keepTicking |= otherCC.soundQueue.tick(entity, arrivalSoundTicks, arrivalSoundReversed);
}
if (!keepTicking) {
arrivalSoundPlaying = false;
return;
}
arrivalSoundTicks += arrivalSoundReversed ? -1 : 1;
}
@Override
public void tickActors() {
super.tickActors();
}
@Override
protected void handleStallInformation(float x, float y, float z, float angle) {}
Vec3 derailParticleOffset = VecHelper.offsetRandomly(Vec3.ZERO, Create.RANDOM, 1.5f)
.multiply(1, .25f, 1);
@ -308,7 +375,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
train.navigation.waitingForSignal = null;
return true;
}
@Override
public Component getDisplayName() {
if (carriage == null)

View file

@ -17,6 +17,7 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -52,6 +53,7 @@ public class Navigation {
public double distanceToDestination;
public double distanceStartedAt;
public boolean destinationBehindTrain;
public boolean announceArrival;
List<Couple<TrackNode>> currentPath;
private TravellingPoint signalScout;
@ -366,6 +368,9 @@ public class Navigation {
cancelNavigation();
return -1;
}
if (Math.abs(distanceToDestination) > 100)
announceArrival = true;
currentPath = pathTo.path;
destinationBehindTrain = pathTo.distance < 0;
@ -676,6 +681,7 @@ public class Navigation {
tag.putDouble("DistanceToDestination", distanceToDestination);
tag.putDouble("DistanceStartedAt", distanceStartedAt);
tag.putBoolean("BehindTrain", destinationBehindTrain);
tag.putBoolean("AnnounceArrival", announceArrival);
tag.put("Path", NBTHelper.writeCompoundList(currentPath, c -> {
CompoundTag nbt = new CompoundTag();
nbt.put("Nodes", c.map(TrackNode::getLocation)
@ -703,6 +709,7 @@ public class Navigation {
distanceToDestination = tag.getDouble("DistanceToDestination");
distanceStartedAt = tag.getDouble("DistanceStartedAt");
destinationBehindTrain = tag.getBoolean("BehindTrain");
announceArrival = tag.getBoolean("AnnounceArrival");
currentPath.clear();
NBTHelper.iterateCompoundList(tag.getList("Path", Tag.TAG_COMPOUND),
c -> currentPath.add(Couple

View file

@ -181,6 +181,10 @@ public class Train {
stress[i - 1] = target - actual;
}
previousCarriage = carriage;
if (carriage.stalled) {
distance = 0;
speed = 0;
}
}
// positive stress: carriages should move apart
@ -532,7 +536,6 @@ public class Train {
navigation.cancelNavigation();
forEachTravellingPoint(tp -> migratingPoints.add(new TrainMigration(tp)));
graph = null;
syncTrackGraphChanges();
}
public void forEachTravellingPoint(Consumer<TravellingPoint> callback) {
@ -613,14 +616,13 @@ public class Train {
if (derailed)
status.successfulMigration();
derailed = false;
if (runtime.getSchedule() != null) {
if (runtime.state == State.IN_TRANSIT)
runtime.state = State.PRE_TRANSIT;
}
if (runtime.getSchedule() != null && runtime.state == State.IN_TRANSIT)
runtime.state = State.PRE_TRANSIT;
GlobalStation currentStation = getCurrentStation();
if (currentStation != null)
currentStation.reserveFor(this);
updateSignalBlocks = true;
migrationCooldown = 0;
syncTrackGraphChanges();
return;
}
@ -722,7 +724,7 @@ public class Train {
while (current != null) {
prev = current;
d = current.getLocationOn(edge);
current = signalData.next(EdgePointType.SIGNAL, d);
current = signalData.next(EdgePointType.SIGNAL, d);
}
if (prev != null) {
UUID group = prev.getGroup(node2);

View file

@ -65,6 +65,7 @@ public abstract class AbstractStationScreen extends AbstractSimiScreen {
Carriage carriage = carriages.get(i);
w += icon.getIconWidth(carriage.bogeySpacing) + 1;
}
return w;
}

View file

@ -153,7 +153,10 @@ public class AssemblyScreen extends AbstractStationScreen {
TrainIconType icon = train.icon;
int offset = 0;
int position = background.width / 2 - getTrainIconWidth(train) / 2;
int trainIconWidth = getTrainIconWidth(train);
int position = background.width / 2 - trainIconWidth / 2;
if (trainIconWidth > 130)
position -= trainIconWidth - 130;
boolean frontConductor = false;
boolean backConductor = false;

View file

@ -116,7 +116,7 @@ public class StationScreen extends AbstractStationScreen {
.length());
trainNameBox.setHighlightPos(trainNameBox.getCursorPosition());
}
super.tick();
updateAssemblyTooltip(te.edgePoint.isOnCurve() ? "no_assembly_curve"
@ -154,6 +154,8 @@ public class StationScreen extends AbstractStationScreen {
int trainIconWidth = getTrainIconWidth(imminentTrain);
int targetPos = background.width / 2 - trainIconWidth / 2;
if (trainIconWidth > 130)
targetPos -= trainIconWidth - 130;
float f = (float) (imminentTrain.navigation.distanceToDestination / 15f);
if (trainPresent())
f = 0;
@ -164,6 +166,8 @@ public class StationScreen extends AbstractStationScreen {
int trainIconWidth = getTrainIconWidth(train);
int targetPos = background.width / 2 - trainIconWidth / 2;
if (trainIconWidth > 130)
targetPos -= trainIconWidth - 130;
if (leavingAnimation > 0) {
disassembleTrainButton.active = false;

View file

@ -33,6 +33,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePoi
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleItem;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -311,8 +312,8 @@ public class StationTileEntity extends SmartTileEntity {
BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, targetPosition, trackState));
int MAX_LENGTH = 48;
int MAX_BOGEY_COUNT = 20;
int MAX_LENGTH = AllConfigs.SERVER.trains.maxAssemblyLength.get();
int MAX_BOGEY_COUNT = AllConfigs.SERVER.trains.maxBogeyCount.get();
int bogeyIndex = 0;
int maxBogeyCount = MAX_BOGEY_COUNT;
@ -324,10 +325,14 @@ public class StationTileEntity extends SmartTileEntity {
Arrays.fill(bogeyTypes, null);
for (int i = 0; i < MAX_LENGTH; i++) {
if (i == MAX_LENGTH - 1 || !track.trackEquals(trackState, level.getBlockState(currentPos))) {
if (i == MAX_LENGTH - 1) {
assemblyLength = i;
break;
}
if (!track.trackEquals(trackState, level.getBlockState(currentPos))) {
assemblyLength = Math.max(0, i - 1);
break;
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
@ -510,9 +515,10 @@ public class StationTileEntity extends SmartTileEntity {
BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset);
try {
boolean success = contraption.assemble(level,
bogeyPosOffset.relative(assemblyDirection, bogeyLocations[bogeyIndex] + 1));
int offset = bogeyLocations[bogeyIndex] + 1;
boolean success = contraption.assemble(level, bogeyPosOffset.relative(assemblyDirection, offset));
atLeastOneForwardControls |= contraption.hasForwardControls();
contraption.setSoundQueueOffset(offset);
if (!success) {
exception(new AssemblyException(Lang.translate("train_assembly.nothing_attached", bogeyIndex + 1)),
-1);

View file

@ -7,6 +7,8 @@ public class CTrains extends ConfigBase {
public final ConfigFloat trainTurningTopSpeed =
f(18, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed);
public final ConfigFloat trainAcceleration = f(4, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration);
public final ConfigInt maxAssemblyLength = i(128, 5, "maxAssemblyLength", Comments.maxAssemblyLength);
public final ConfigInt maxBogeyCount = i(20, 1, "maxBogeyCount", Comments.maxBogeyCount);
@Override
public String getName() {
@ -32,6 +34,8 @@ public class CTrains extends ConfigBase {
static String trainTurningTopSpeed = "The top speed of Trains during a turn.";
static String trainAcceleration = "The acceleration of any assembled Train.";
static String trainsCauseDamage = "Whether moving Trains can hurt colliding mobs and players.";
static String maxAssemblyLength = "Maximum length of a Train Stations' assembly track.";
static String maxBogeyCount = "Maximum amount of bogeys assembled as a single Train.";
}
}