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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 223", "_": "Missing Localizations: 226",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1592,7 +1592,10 @@
"create.subtitle.potato_hit": "Impact de legumă", "create.subtitle.potato_hit": "Impact de legumă",
"create.subtitle.saw_activate_wood": "Ferăstrău Mecanic se activează", "create.subtitle.saw_activate_wood": "Ferăstrău Mecanic se activează",
"create.subtitle.whistle_high": "UNLOCALIZED: High whistling", "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.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.deny": "Boop de refuz",
"create.subtitle.controller_click": "Controlor clickuiește", "create.subtitle.controller_click": "Controlor clickuiește",
"create.subtitle.whistle_low": "UNLOCALIZED: Low whistling", "create.subtitle.whistle_low": "UNLOCALIZED: Low whistling",

View file

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

View file

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

View file

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

View file

@ -419,6 +419,33 @@
], ],
"subtitle": "create.subtitle.whistle_low" "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": { "worldshaper_place": {
"sounds": [ "sounds": [
{ {

View file

@ -228,7 +228,7 @@ public class AllSoundEvents {
.attenuationDistance(64) .attenuationDistance(64)
.build(), .build(),
WHISTLE = create("whistle").subtitle("Whistling") WHISTLE_MEDIUM = create("whistle").subtitle("Whistling")
.category(SoundSource.RECORDS) .category(SoundSource.RECORDS)
.attenuationDistance(64) .attenuationDistance(64)
.build(), .build(),
@ -238,6 +238,18 @@ public class AllSoundEvents {
.attenuationDistance(64) .attenuationDistance(64)
.build(), .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() WHISTLE_CHIFF = create("chiff").noSubtitle()
.category(SoundSource.RECORDS) .category(SoundSource.RECORDS)
.build(), .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.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.curiosities.bell.AbstractBellBlock; 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.core.BlockPos;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
@ -18,6 +19,11 @@ public class BellMovementBehaviour implements MovementBehaviour {
return true; return true;
} }
@Override
public boolean isActive(MovementContext context) {
return !(context.contraption instanceof CarriageContraption);
}
@Override @Override
public void onSpeedChanged(MovementContext context, Vec3 oldMotion, Vec3 motion) { public void onSpeedChanged(MovementContext context, Vec3 oldMotion, Vec3 motion) {
double dotProduct = oldMotion.dot(motion); double dotProduct = oldMotion.dot(motion);
@ -28,7 +34,7 @@ public class BellMovementBehaviour implements MovementBehaviour {
@Override @Override
public void stopMoving(MovementContext context) { public void stopMoving(MovementContext context) {
if (context.position != null) if (context.position != null && isActive(context))
playSound(context); playSound(context);
} }

View file

@ -83,8 +83,13 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
.getNormal()); .getNormal());
facingVec = context.rotation.apply(facingVec); facingVec = context.rotation.apply(facingVec);
Vec3 vec = context.position.subtract(facingVec.scale(2)); 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.setYRot(AbstractContraptionEntity.yawFromVector(facingVec));
player.setXRot(AbstractContraptionEntity.pitchFromVector(facingVec) - 90); player.setXRot(xRot);
DeployerHandler.activate(player, vec, pos, facingVec, mode); DeployerHandler.activate(player, vec, pos, facingVec, mode);
} }
@ -128,8 +133,8 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
return; return;
} }
for (ItemRequirement.StackRequirement required : requiredItems) for (ItemRequirement.StackRequirement required : requiredItems)
ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s), ExtractionCountMode.UPTO, ItemHelper.extract(iItemHandler, s -> ItemRequirement.validate(required.item, s),
required.item.getCount(), false); ExtractionCountMode.UPTO, required.item.getCount(), false);
} }
CompoundTag data = null; CompoundTag data = null;
@ -183,7 +188,8 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (player == null) if (player == null)
return; return;
context.tileData.put("Inventory", player.getInventory().save(new ListTag())); context.tileData.put("Inventory", player.getInventory()
.save(new ListTag()));
player.discard(); player.discard();
} }
@ -215,8 +221,7 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
if (itemstack.isEmpty()) if (itemstack.isEmpty())
continue; continue;
if (list == inv.items && i == inv.selected if (list == inv.items && i == inv.selected && FilterItem.test(context.world, itemstack, filter))
&& FilterItem.test(context.world, itemstack, filter))
continue; continue;
dropItem(context, itemstack); dropItem(context, itemstack);
@ -237,9 +242,11 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
private DeployerFakePlayer getPlayer(MovementContext context) { private DeployerFakePlayer getPlayer(MovementContext context) {
if (!(context.temporaryData instanceof DeployerFakePlayer) && context.world instanceof ServerLevel) { if (!(context.temporaryData instanceof DeployerFakePlayer) && context.world instanceof ServerLevel) {
DeployerFakePlayer deployerFakePlayer = new DeployerFakePlayer((ServerLevel) context.world); 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")) 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.tileData.remove("Inventory");
context.temporaryData = deployerFakePlayer; context.temporaryData = deployerFakePlayer;
} }
@ -268,7 +275,8 @@ public class DeployerMovementBehaviour implements MovementBehaviour {
@Nullable @Nullable
@Override @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); return new DeployerActorInstance(materialManager, simulationWorld, context);
} }
} }

View file

@ -1,6 +1,6 @@
package com.simibubi.create.content.contraptions.components.steam.whistle; 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_HIGH;
import static com.simibubi.create.AllSoundEvents.WHISTLE_LOW; import static com.simibubi.create.AllSoundEvents.WHISTLE_LOW;
@ -18,7 +18,7 @@ public class WhistleSoundInstance extends AbstractTickableSoundInstance {
private WhistleSize size; private WhistleSize size;
public WhistleSoundInstance(WhistleSize size, BlockPos worldPosition) { 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); .getMainEvent(), SoundSource.RECORDS);
this.size = size; this.size = size;
looping = true; 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.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.content.contraptions.components.crank.HandCrankBlock; 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.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.ClockworkBearingBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.ClockworkBearingTileEntity; import com.simibubi.create.content.contraptions.components.structureMovement.bearing.ClockworkBearingTileEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.MechanicalBearingBlock; 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 * Attached blocks will move if blocks they are attached to are moved
*/ */
public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, public static boolean isBlockAttachedTowards(BlockState state, Level world, BlockPos pos, Direction direction) {
Direction direction) {
for (AttachedCheck check : ATTACHED_CHECKS) { for (AttachedCheck check : ATTACHED_CHECKS) {
CheckResult result = check.isBlockAttachedTowards(state, world, pos, direction); CheckResult result = check.isBlockAttachedTowards(state, world, pos, direction);
if (result != CheckResult.PASS) { if (result != CheckResult.PASS) {
@ -258,6 +259,10 @@ public class BlockMovementChecks {
return true; return true;
if (block instanceof WoolCarpetBlock) if (block instanceof WoolCarpetBlock)
return true; return true;
if (block instanceof WhistleBlock)
return true;
if (block instanceof WhistleExtenderBlock)
return true;
return AllBlockTags.BRITTLE.matches(state); return AllBlockTags.BRITTLE.matches(state);
} }
@ -343,6 +348,11 @@ public class BlockMovementChecks {
if (block instanceof IBogeyBlock bogey) if (block instanceof IBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state) return bogey.getStickySurfaces(world, pos, state)
.contains(direction); .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; return false;
} }
@ -415,13 +425,12 @@ public class BlockMovementChecks {
public CheckResult isNotSupportive(BlockState state, Direction direction); 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 { public static enum CheckResult {
SUCCESS, SUCCESS, FAIL, PASS;
FAIL,
PASS;
public Boolean toBoolean() { public Boolean toBoolean() {
return this == PASS ? null : (this == SUCCESS ? true : false); 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 Train train;
public int id; public int id;
public boolean blocked; public boolean blocked;
public boolean stalled;
public Couple<Boolean> presentConductors; public Couple<Boolean> presentConductors;
public int bogeySpacing; public int bogeySpacing;
@ -50,6 +51,7 @@ public class Carriage {
// client // client
public boolean pointsInitialised; public boolean pointsInitialised;
static final int FIRST = 0, MIDDLE = 1, LAST = 2, BOTH = 3; static final int FIRST = 0, MIDDLE = 1, LAST = 2, BOTH = 3;
public Carriage(CarriageBogey bogey1, @Nullable CarriageBogey bogey2, int bogeySpacing) { public Carriage(CarriageBogey bogey1, @Nullable CarriageBogey bogey2, int bogeySpacing) {
@ -284,6 +286,7 @@ public class Carriage {
tag.putInt("Spacing", bogeySpacing); tag.putInt("Spacing", bogeySpacing);
tag.putBoolean("FrontConductor", presentConductors.getFirst()); tag.putBoolean("FrontConductor", presentConductors.getFirst());
tag.putBoolean("BackConductor", presentConductors.getSecond()); tag.putBoolean("BackConductor", presentConductors.getSecond());
tag.putBoolean("Stalled", stalled);
CarriageContraptionEntity entity = this.entity.get(); CarriageContraptionEntity entity = this.entity.get();
if (entity != null) if (entity != null)
@ -303,6 +306,7 @@ public class Carriage {
Carriage carriage = new Carriage(bogey1, bogey2, tag.getInt("Spacing")); 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.presentConductors = Couple.create(tag.getBoolean("FrontConductor"), tag.getBoolean("BackConductor"));
carriage.serialisedEntity = tag.getCompound("Entity") carriage.serialisedEntity = tag.getCompound("Entity")
.copy(); .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.Iterate;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -42,6 +43,7 @@ public class CarriageContraption extends Contraption {
private boolean backwardControls; private boolean backwardControls;
public Couple<Boolean> blazeBurnerConductors; public Couple<Boolean> blazeBurnerConductors;
public Map<BlockPos, Couple<Boolean>> conductorSeats; public Map<BlockPos, Couple<Boolean>> conductorSeats;
public ArrivalSoundQueue soundQueue;
// during assembly only // during assembly only
private int bogeys; private int bogeys;
@ -53,6 +55,11 @@ public class CarriageContraption extends Contraption {
conductorSeats = new HashMap<>(); conductorSeats = new HashMap<>();
assembledBlazeBurners = new ArrayList<>(); assembledBlazeBurners = new ArrayList<>();
blazeBurnerConductors = Couple.create(false, false); blazeBurnerConductors = Couple.create(false, false);
soundQueue = new ArrivalSoundQueue();
}
public void setSoundQueueOffset(int offset) {
soundQueue.offset = offset;
} }
public CarriageContraption(Direction assemblyDirection) { public CarriageContraption(Direction assemblyDirection) {
@ -106,6 +113,13 @@ public class CarriageContraption extends Contraption {
protected Pair<StructureBlockInfo, BlockEntity> capture(Level world, BlockPos pos) { protected Pair<StructureBlockInfo, BlockEntity> capture(Level world, BlockPos pos) {
BlockState blockState = world.getBlockState(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) { if (blockState.getBlock() instanceof IBogeyBlock) {
bogeys++; bogeys++;
if (bogeys == 2) if (bogeys == 2)
@ -151,6 +165,7 @@ public class CarriageContraption extends Contraption {
return compoundTag; return compoundTag;
}); });
tag.put("ConductorSeats", list); tag.put("ConductorSeats", list);
soundQueue.serialize(tag);
return tag; return tag;
} }
@ -165,6 +180,7 @@ public class CarriageContraption extends Contraption {
NBTHelper.iterateCompoundList(nbt.getList("ConductorSeats", Tag.TAG_COMPOUND), NBTHelper.iterateCompoundList(nbt.getList("ConductorSeats", Tag.TAG_COMPOUND),
c -> conductorSeats.put(NbtUtils.readBlockPos(c.getCompound("Pos")), c -> conductorSeats.put(NbtUtils.readBlockPos(c.getCompound("Pos")),
Couple.create(c.getBoolean("Forward"), c.getBoolean("Backward")))); Couple.create(c.getBoolean("Forward"), c.getBoolean("Backward"))));
soundQueue.deserialize(nbt);
super.readNBT(world, nbt, spawnData); 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.lang.ref.WeakReference;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.AllEntityDataSerializers; import com.simibubi.create.AllEntityDataSerializers;
import com.simibubi.create.AllEntityTypes; import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.CreateClient; 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.OrientedContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -65,10 +64,15 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
public boolean leftTickingChunks; public boolean leftTickingChunks;
public boolean firstPositionUpdate; public boolean firstPositionUpdate;
private boolean arrivalSoundPlaying;
private boolean arrivalSoundReversed;
private int arrivalSoundTicks;
public CarriageContraptionEntity(EntityType<?> type, Level world) { public CarriageContraptionEntity(EntityType<?> type, Level world) {
super(type, world); super(type, world);
validForRender = false; validForRender = false;
firstPositionUpdate = true; firstPositionUpdate = true;
arrivalSoundTicks = Integer.MIN_VALUE;
} }
@Override @Override
@ -167,15 +171,13 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
} }
tickActors(); tickActors();
contraption.stalled = false; boolean isStalled = isStalled();
for (MutablePair<StructureBlockInfo, MovementContext> pair : contraption.getActors()) { carriage.stalled = isStalled;
MovementContext context = pair.right;
context.stall = false;
}
CarriageSyncData carriageData = getCarriageData(); CarriageSyncData carriageData = getCarriageData();
if (!level.isClientSide) { if (!level.isClientSide) {
entityData.set(SCHEDULED, carriage.train.runtime.getSchedule() != null); entityData.set(SCHEDULED, carriage.train.runtime.getSchedule() != null);
boolean shouldCarriageSyncThisTick = boolean shouldCarriageSyncThisTick =
carriage.train.shouldCarriageSyncThisTick(level.getGameTime(), getType().updateInterval()); carriage.train.shouldCarriageSyncThisTick(level.getGameTime(), getType().updateInterval());
@ -184,6 +186,18 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
entityData.set(CARRIAGE_DATA, carriageData); entityData.set(CARRIAGE_DATA, carriageData);
carriageData.setDirty(false); 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; return;
} }
@ -223,6 +237,59 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
validForRender = true; 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) Vec3 derailParticleOffset = VecHelper.offsetRandomly(Vec3.ZERO, Create.RANDOM, 1.5f)
.multiply(1, .25f, 1); .multiply(1, .25f, 1);

View file

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

View file

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

View file

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

View file

@ -153,7 +153,10 @@ public class AssemblyScreen extends AbstractStationScreen {
TrainIconType icon = train.icon; TrainIconType icon = train.icon;
int offset = 0; 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 frontConductor = false;
boolean backConductor = false; boolean backConductor = false;

View file

@ -154,6 +154,8 @@ public class StationScreen extends AbstractStationScreen {
int trainIconWidth = getTrainIconWidth(imminentTrain); int trainIconWidth = getTrainIconWidth(imminentTrain);
int targetPos = background.width / 2 - trainIconWidth / 2; int targetPos = background.width / 2 - trainIconWidth / 2;
if (trainIconWidth > 130)
targetPos -= trainIconWidth - 130;
float f = (float) (imminentTrain.navigation.distanceToDestination / 15f); float f = (float) (imminentTrain.navigation.distanceToDestination / 15f);
if (trainPresent()) if (trainPresent())
f = 0; f = 0;
@ -164,6 +166,8 @@ public class StationScreen extends AbstractStationScreen {
int trainIconWidth = getTrainIconWidth(train); int trainIconWidth = getTrainIconWidth(train);
int targetPos = background.width / 2 - trainIconWidth / 2; int targetPos = background.width / 2 - trainIconWidth / 2;
if (trainIconWidth > 130)
targetPos -= trainIconWidth - 130;
if (leavingAnimation > 0) { if (leavingAnimation > 0) {
disassembleTrainButton.active = false; 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.edgePoint.TrackTargetingBehaviour;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule; import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleItem; 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.networking.AllPackets;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; 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)); BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, targetPosition, trackState));
int MAX_LENGTH = 48; int MAX_LENGTH = AllConfigs.SERVER.trains.maxAssemblyLength.get();
int MAX_BOGEY_COUNT = 20; int MAX_BOGEY_COUNT = AllConfigs.SERVER.trains.maxBogeyCount.get();
int bogeyIndex = 0; int bogeyIndex = 0;
int maxBogeyCount = MAX_BOGEY_COUNT; int maxBogeyCount = MAX_BOGEY_COUNT;
@ -324,10 +325,14 @@ public class StationTileEntity extends SmartTileEntity {
Arrays.fill(bogeyTypes, null); Arrays.fill(bogeyTypes, null);
for (int i = 0; i < MAX_LENGTH; i++) { 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; assemblyLength = i;
break; break;
} }
if (!track.trackEquals(trackState, level.getBlockState(currentPos))) {
assemblyLength = Math.max(0, i - 1);
break;
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos)); BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) { if (potentialBogeyState.getBlock()instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
@ -510,9 +515,10 @@ public class StationTileEntity extends SmartTileEntity {
BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset); BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset);
try { try {
boolean success = contraption.assemble(level, int offset = bogeyLocations[bogeyIndex] + 1;
bogeyPosOffset.relative(assemblyDirection, bogeyLocations[bogeyIndex] + 1)); boolean success = contraption.assemble(level, bogeyPosOffset.relative(assemblyDirection, offset));
atLeastOneForwardControls |= contraption.hasForwardControls(); atLeastOneForwardControls |= contraption.hasForwardControls();
contraption.setSoundQueueOffset(offset);
if (!success) { if (!success) {
exception(new AssemblyException(Lang.translate("train_assembly.nothing_attached", bogeyIndex + 1)), exception(new AssemblyException(Lang.translate("train_assembly.nothing_attached", bogeyIndex + 1)),
-1); -1);

View file

@ -7,6 +7,8 @@ public class CTrains extends ConfigBase {
public final ConfigFloat trainTurningTopSpeed = public final ConfigFloat trainTurningTopSpeed =
f(18, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed); f(18, 0, "trainTurningTopSpeed", Comments.mps, Comments.trainTurningTopSpeed);
public final ConfigFloat trainAcceleration = f(4, 0, "trainAcceleration", Comments.acc, Comments.trainAcceleration); 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 @Override
public String getName() { 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 trainTurningTopSpeed = "The top speed of Trains during a turn.";
static String trainAcceleration = "The acceleration of any assembled Train."; static String trainAcceleration = "The acceleration of any assembled Train.";
static String trainsCauseDamage = "Whether moving Trains can hurt colliding mobs and players."; 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.";
} }
} }