Merge branch 'mc1.20.1/dev' into mc1.20.1/mmc-5

This commit is contained in:
attackeight 2025-01-12 21:07:19 -05:00 committed by GitHub
commit 769e41d1e6
Failed to generate hash of commit
199 changed files with 4422 additions and 2060 deletions

View file

@ -119,6 +119,22 @@ repositories {
name = "squiddev"
url = "https://squiddev.cc/maven/"
}
maven {
name = "ftb"
url = "https://maven.saps.dev/releases"
}
maven {
name = "architectury"
url = "https://maven.architectury.dev/"
}
maven {
url = "https://jm.gserv.me/repository/maven-public/"
content {
includeGroup "info.journeymap"
includeGroup "mysticdrew"
}
}
maven {
url = 'https://www.cursemaven.com'
@ -155,6 +171,11 @@ dependencies {
jarJar.ranged(it, '[1.0,2.0)')
}
compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1"))
implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1")) {
jarJar.ranged(it, "[0.4.1,)")
}
implementation fg.deobf("com.tterrag.registrate:Registrate:${registrate_version}")
compileOnly fg.deobf("dev.engine_room.flywheel:flywheel-forge-api-${flywheel_minecraft_version}:${flywheel_version}")
@ -179,12 +200,20 @@ dependencies {
// implementation fg.deobf("curse.maven:ic2-classic-242942:5555152") not updated to 1.20
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903") not updated to 1.20
// implementation fg.deobf("com.railwayteam.railways:Steam_Rails-forge-1.20.1:1.6.4+forge-mc1.20.1") { transitive = false }
//
// implementation fg.deobf("com.railwayteam.railways:railways-1.19.2-1.6.4:all") { transitive = false }
// implementation fg.deobf("dev.architectury:architectury-forge:9.1.12")
// implementation fg.deobf("dev.ftb.mods:ftb-chunks-forge:2001.3.1")
// implementation fg.deobf("dev.ftb.mods:ftb-teams-forge:2001.3.0")
// implementation fg.deobf("dev.ftb.mods:ftb-library-forge:2001.2.4")
// implementation fg.deobf("curse.maven:journeymap-32274:5457831")
// implementation fg.deobf("ignored:journeymap-1.20.1-5.10.1-forge")
// runtimeOnly fg.deobf("curse.maven:framedblocks-441647:5629578")
// runtimeOnly fg.deobf("curse.maven:galosphere-631098:4983871")
runtimeOnly fg.deobf("curse.maven:elementary-ores-332609:4622524")
runtimeOnly fg.deobf("curse.maven:flib-661261:5495793")
// runtimeOnly fg.deobf("curse.maven:elementary-ores-332609:4622524")
// runtimeOnly fg.deobf("curse.maven:flib-661261:5495793")
// runtimeOnly fg.deobf("curse.maven:infernal-expansion-395078:4002091") not updated to 1.20
// runtimeOnly fg.deobf("org.violetmoon.zeta:Zeta:1.0-9.44")
// runtimeOnly fg.deobf("org.violetmoon.quark:Quark:4.0-461.3475")
@ -214,14 +243,13 @@ dependencies {
// runtimeOnly fg.deobf("curse.maven:blueprint-382216:5292242")
// runtimeOnly fg.deobf("curse.maven:windsweptmod-636321:4817132") not updated to 1.20
// runtimeOnly fg.deobf("curse.maven:good-ending-690161:5024405")
runtimeOnly fg.deobf("curse.maven:terrablender-563928:5378180")
runtimeOnly fg.deobf("curse.maven:regions-unexplored-659110:5558225")
runtimeOnly fg.deobf("curse.maven:vampirism-become-a-vampire-233029:5526512")
runtimeOnly fg.deobf("curse.maven:applied-energistics-2-223794:5641282")
runtimeOnly fg.deobf("curse.maven:silent-lib-242998:4585754")
runtimeOnly fg.deobf("curse.maven:silents-gems-220311:5193708")
runtimeOnly fg.deobf("curse.maven:project-vibrant-journeys-300297:5676296")
// runtimeOnly fg.deobf("curse.maven:terrablender-563928:5378180")
// runtimeOnly fg.deobf("curse.maven:regions-unexplored-659110:5558225")
// runtimeOnly fg.deobf("curse.maven:vampirism-become-a-vampire-233029:5526512")
// runtimeOnly fg.deobf("curse.maven:applied-energistics-2-223794:5641282")
// runtimeOnly fg.deobf("curse.maven:silent-lib-242998:4585754")
// runtimeOnly fg.deobf("curse.maven:silents-gems-220311:5193708")
// runtimeOnly fg.deobf("curse.maven:project-vibrant-journeys-300297:5676296")
// https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings

View file

@ -23,7 +23,7 @@ use_parchment = true
# dependency versions
registrate_version = MC1.20-1.3.3
flywheel_minecraft_version = 1.20.1
flywheel_version = 1.0.0-beta-113
flywheel_version = 1.0.0-beta-145
jei_minecraft_version = 1.20.1
jei_version = 15.10.0.39
curios_minecraft_version = 1.20.1

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.3560692 Create's Sequenced Assembly Recipes
// 1.20.1 2024-10-09T12:24:59.2666858 Create's Sequenced Assembly Recipes
dbaca5a5aa312f3bc7b826e51e665d32e798a5d7 data/create/recipes/sequenced_assembly/precision_mechanism.json
dacafdb106304d183b00e21fb01517ac45eca800 data/create/recipes/sequenced_assembly/sturdy_sheet.json
1274315b5c570722d6f5b2ed7f5e53fe01b6288a data/create/recipes/sequenced_assembly/track.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.2861383 Create's Advancements
// 1.20.1 2024-10-09T12:24:59.1794112 Create's Advancements
2661a689fdcf729494f46e3c719f71c62e31582e data/create/advancements/andesite_alloy.json
fa16c4afe0496edc3f157858a6e0ff177a1622ff data/create/advancements/andesite_casing.json
5a694002d0a663bc869b09d15924a10c43dc522f data/create/advancements/anvil_plough.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.2891201 Create Train Hat Information
// 1.20.1 2024-10-09T12:24:59.1824022 Create Train Hat Information
be16d47aa64e673b1107a36ce06475016e316fca assets/minecraft/train_hat_info/axolotl.json
b8ae6d9c8014439f4049622e0d6e79b9d6716260 assets/minecraft/train_hat_info/bat.json
5053a6c9fb412dfac1bf17eb0f57f9fd314198e4 assets/minecraft/train_hat_info/bee.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.3630513 Create's Mechanical Crafting Recipes
// 1.20.1 2024-10-09T12:24:59.2726698 Create's Mechanical Crafting Recipes
f076d64d9f30709bed34775136c9241097b28aa9 data/create/recipes/mechanical_crafting/crushing_wheel.json
694dca9dcff246bb7f560b3304fcc244c53217d5 data/create/recipes/mechanical_crafting/extendo_grip.json
c03bc27f537e2d6531438bf58a17d977a7e16c7b data/create/recipes/mechanical_crafting/potato_cannon.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.358065 Create's Standard Recipes
// 1.20.1 2024-10-09T12:24:59.2686824 Create's Standard Recipes
a8cc4af26f6c7c45a9eef12e92af1452fe042454 data/create/advancements/recipes/combat/crafting/appliances/netherite_backtank.json
2c2639c7b307ee7c7a4e97e5efebf496788998ad data/create/advancements/recipes/combat/crafting/appliances/netherite_backtank_from_netherite.json
81dcf0cb1aa99e39bc7d1a386e07cad4cee7d8b9 data/create/advancements/recipes/combat/crafting/appliances/netherite_diving_boots.json
@ -7,7 +7,7 @@ a8cc4af26f6c7c45a9eef12e92af1452fe042454 data/create/advancements/recipes/combat
c1f2e6d1d955fb2d6d7ccc7a6d45d051bbcab315 data/create/advancements/recipes/combat/crafting/appliances/netherite_diving_helmet_from_netherite.json
6418408e9fe53c03eae1e2b17b2229a548abc226 data/create/advancements/recipes/misc/blasting/copper_ingot_from_crushed.json
d88c5c8b6751f389d9eea30acbd566c120e77705 data/create/advancements/recipes/misc/blasting/gold_ingot_from_crushed.json
c5eabab1b28eaf8d83007b303480f12d8c319c4d data/create/advancements/recipes/misc/blasting/ingot_aluminum_compat_ic2.json
ea97407030aed9f2da5720fe7f68dd0c87f68944 data/create/advancements/recipes/misc/blasting/ingot_aluminium_compat_ic2.json
2532dd0af4124639c26525b6c4bbaf8059132903 data/create/advancements/recipes/misc/blasting/ingot_aluminum_compat_immersiveengineering.json
6b62cf9551e30b3560349e8d905cd10b446a98fd data/create/advancements/recipes/misc/blasting/ingot_lead_compat_immersiveengineering.json
4568168d851832c9eefd177c64a2de9c40e9954b data/create/advancements/recipes/misc/blasting/ingot_lead_compat_mekanism.json
@ -217,7 +217,7 @@ bb87cb8787644e20b3418d6f57726f2ca70b0aae data/create/advancements/recipes/misc/s
70342b3f5c5482caa82e0afcd559c7b200d9f247 data/create/advancements/recipes/misc/smelting/glass_pane_from_tiled_glass_pane.json
8635e2becb91b0e4e754fd79d231300492b8afce data/create/advancements/recipes/misc/smelting/glass_pane_from_vertical_framed_glass_pane.json
d76d08c3efcf9174ee2980796aa04c67fe9443eb data/create/advancements/recipes/misc/smelting/gold_ingot_from_crushed.json
6a53b4a956a5560dfa7a6ed3d3279fe502a45574 data/create/advancements/recipes/misc/smelting/ingot_aluminum_compat_ic2.json
5e222928aadd0a91f9996b0a104ec4b80646058c data/create/advancements/recipes/misc/smelting/ingot_aluminium_compat_ic2.json
1e88edf27ed1f83031f9d71cad8f3f4388f57b85 data/create/advancements/recipes/misc/smelting/ingot_aluminum_compat_immersiveengineering.json
2b6b739a2f0ad1f33db8090aa0e77c8149d67dbf data/create/advancements/recipes/misc/smelting/ingot_lead_compat_immersiveengineering.json
7392d585e5409438f70ddb43ebba58b35609265b data/create/advancements/recipes/misc/smelting/ingot_lead_compat_mekanism.json
@ -246,7 +246,7 @@ b8d5ef1eba4521441658d4c051861ecf9cc96102 data/create/advancements/recipes/misc/s
42f1375bf3004cfd891a5fbb05352f578636dd75 data/create/advancements/recipes/misc/smoking/bread.json
3c9dcf347eef42d0cca69ae5bc4a61fe90fb27c8 data/create/recipes/blasting/copper_ingot_from_crushed.json
cbd86c583643e65a0d9b7950dcf593cdf6d43d77 data/create/recipes/blasting/gold_ingot_from_crushed.json
053d94339f3b64e02b0eae56fe714b6f4a05986f data/create/recipes/blasting/ingot_aluminum_compat_ic2.json
4dccb0aaf5316c75f4dd03b497fb43bf20f7e279 data/create/recipes/blasting/ingot_aluminium_compat_ic2.json
5b1b7981636b211a956e37356423c2ba65c8042c data/create/recipes/blasting/ingot_aluminum_compat_immersiveengineering.json
f794d2eab3922ea7765866d473eb61c74a2678c5 data/create/recipes/blasting/ingot_lead_compat_immersiveengineering.json
7acb7c5bc88b238e914abc07f979c33f8d33123e data/create/recipes/blasting/ingot_lead_compat_mekanism.json
@ -451,7 +451,7 @@ d849fafedd10c68e6bc6dc1e5a85be82aae1b139 data/create/recipes/crafting/palettes/s
9a687ee9dab44c439ab669aa596117064fb13457 data/create/recipes/crafting/schematics/schematicannon.json
4a20356c9ce01ebfbcacbdc5d3c31094a5599a17 data/create/recipes/crafting/schematics/schematic_and_quill.json
4a297162a630b48407dbc8ff8ca713387dcd3206 data/create/recipes/crafting/schematics/schematic_table.json
dd2b5bfb7ebd836e8b5639894736c226f2cac8c0 data/create/recipes/crafting/tree_fertilizer.json
2cf06129b47d1a2b733619514dc9e8cf1d8967f2 data/create/recipes/crafting/tree_fertilizer.json
78526658ca5ccaa3729c967b5283069945d183b7 data/create/recipes/smelting/bread.json
04bb0c80f3b5a6fe86fc4a8ed5293fc74c2d9aba data/create/recipes/smelting/copper_ingot_from_crushed.json
d5b29fa27977691c3c50eb36c28bfe33b8462d09 data/create/recipes/smelting/glass_from_framed_glass.json
@ -463,7 +463,7 @@ ab1a181eb787f501ae7b8a8c6da2d3adb35a8f2b data/create/recipes/smelting/glass_pane
ad412d18c2084dc74fff8a079a2e7ffb20f9a0c6 data/create/recipes/smelting/glass_pane_from_tiled_glass_pane.json
67c1143c7aac88a9cc91b98dbca60770cb1422a5 data/create/recipes/smelting/glass_pane_from_vertical_framed_glass_pane.json
461e4dede50a4a318281ae9c086c8094470e21a1 data/create/recipes/smelting/gold_ingot_from_crushed.json
ae05209f9f639c7709bb25d0ff5f09f1af6cffcf data/create/recipes/smelting/ingot_aluminum_compat_ic2.json
0ceeee303ca8993c394fc597cdd7c5ef44d84ad0 data/create/recipes/smelting/ingot_aluminium_compat_ic2.json
fa0d3d6f50d344aa83ddf4ac8abf4a80deb9fb32 data/create/recipes/smelting/ingot_aluminum_compat_immersiveengineering.json
4e8cf8775719219849b1a0e95903a3605b665015 data/create/recipes/smelting/ingot_lead_compat_immersiveengineering.json
cfa90e7ba56d1ec6caa11bd019244bddd51da841 data/create/recipes/smelting/ingot_lead_compat_mekanism.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.3560692 Create's Damage Type Tags
// 1.20.1 2024-10-09T12:24:59.2656887 Create's Damage Type Tags
7884716b2f4bb1330ff215366bb4bab06e4728c2 data/minecraft/tags/damage_type/bypasses_armor.json
1fcad1f89265fba8bdb05b03a1dfcc88d7b7a550 data/minecraft/tags/damage_type/is_explosion.json
08324c61115b72bb8a6370d7f34d84d9a31afd16 data/minecraft/tags/damage_type/is_fire.json

View file

@ -842,22 +842,22 @@ e383106ff8f877b5995e20c03af5e41e3db541d9 data/create/recipes/milling/compat/biom
213e079f32baa2879702b72bdf08f146877a0bb9 data/create/recipes/milling/compat/biomesoplenty/violet.json
b3f041e005491582f055da15871891357908d998 data/create/recipes/milling/compat/biomesoplenty/wildflower.json
d08c4fcebb79e2e02ac9cb4623124332a05ed661 data/create/recipes/milling/compat/biomesoplenty/wilted_lily.json
ca03746c39143de7867aeab2fb450fe0a67b69e3 data/create/recipes/milling/compat/botania/black_petal.json
3192777eeb363a55174a0eb58197ee686b2e02c7 data/create/recipes/milling/compat/botania/blue_petal.json
ee99c9bdcc4da8d160fc762ce7b394848d6a86d1 data/create/recipes/milling/compat/botania/brown_petal.json
1d8dab24913945268f819c24e132d8bb74f792c2 data/create/recipes/milling/compat/botania/cyan_petal.json
010a111f9810f142315bc94dfb1be03ee02508c4 data/create/recipes/milling/compat/botania/gray_petal.json
dee8b4c26d6b78aceeb8e5a264693dad4e81dbcb data/create/recipes/milling/compat/botania/green_petal.json
285a24e440d6a8cf34f2925a3eed7fa1d16e102b data/create/recipes/milling/compat/botania/light_blue_petal.json
3cc232966240394aaf5a3a5d16a3ceebd41597a0 data/create/recipes/milling/compat/botania/light_gray_petal.json
1225119a7dc1f69dfaf2cec32a0267b1d47ef72d data/create/recipes/milling/compat/botania/lime_petal.json
09e114685483329a78c3cecf8b312f16d26cd981 data/create/recipes/milling/compat/botania/magenta_petal.json
ae3e7eb55dda1846b8fd849ea1c8f1cbd37b9fca data/create/recipes/milling/compat/botania/orange_petal.json
bf13e9807a96efc1ef684f0129cc21110e44cc4c data/create/recipes/milling/compat/botania/pink_petal.json
7e0a167201b9c915579ede71ee7128bccdeee9c2 data/create/recipes/milling/compat/botania/purple_petal.json
4e7c1ae95f10bb3466dd542a2e04c726c599ecd9 data/create/recipes/milling/compat/botania/red_petal.json
28cbeb278022b2ac62cae2f3deaa65cb375c6456 data/create/recipes/milling/compat/botania/white_petal.json
dd1e35234c419b1576410a2590fd33d88c8bb9bd data/create/recipes/milling/compat/botania/yellow_petal.json
4994095300eabfe98a86036e7fbba6c12cddb078 data/create/recipes/milling/compat/botania/black_petal.json
3516555e62ce7d6f0b5a57375339e69b4de41f83 data/create/recipes/milling/compat/botania/blue_petal.json
deb37dcb4b323590fbb76f21732e5b9016028f7d data/create/recipes/milling/compat/botania/brown_petal.json
d831337c28b89ce25a2be50e06719ab3be9400b6 data/create/recipes/milling/compat/botania/cyan_petal.json
1ec99d5ee65becc6c921827956e26f286398b1ba data/create/recipes/milling/compat/botania/gray_petal.json
7bcbd91fae49452fe30966b350c6830ad5bb588c data/create/recipes/milling/compat/botania/green_petal.json
5819b1ff0c54851d4bf7c60228ce2e31b8d8ffee data/create/recipes/milling/compat/botania/light_blue_petal.json
94b6cb826923907527c57e079d08fb410720b008 data/create/recipes/milling/compat/botania/light_gray_petal.json
4fe3b309902b0d59971d351d6e4a8066908df195 data/create/recipes/milling/compat/botania/lime_petal.json
b8f8ea5d52ae9cbdd59d60aabbac660859190855 data/create/recipes/milling/compat/botania/magenta_petal.json
eab4d51ba92d5a2f172de76bca72cf2746359b68 data/create/recipes/milling/compat/botania/orange_petal.json
c8cf3978e3bf0ffeccd18c1b16ea26c5e1c18634 data/create/recipes/milling/compat/botania/pink_petal.json
9ee958eb5fb176902255aa606517a362670f3d60 data/create/recipes/milling/compat/botania/purple_petal.json
3239b0a1ef91f61ab32f42ac3935ee99316089c5 data/create/recipes/milling/compat/botania/red_petal.json
15afbebf247ea66e0b023ea84aa5c5dad8ac8466 data/create/recipes/milling/compat/botania/white_petal.json
9c500a9d6cadb4f673ca63de38c1d3713dab061c data/create/recipes/milling/compat/botania/yellow_petal.json
c7d2b07396448628123b81e1f34a8b131aa99c83 data/create/recipes/milling/compat/buzzier_bees/buttercup.json
0c4a3c7da1e151868740db2037504e35a02af3d0 data/create/recipes/milling/compat/buzzier_bees/pink_clover.json
355e89a3e003ae65ff06a9277c05699220eec569 data/create/recipes/milling/compat/buzzier_bees/white_clover.json
@ -1083,7 +1083,7 @@ e3f12ec5d449caa54ebe1c453a89373492b8f48a data/create/recipes/splashing/endergeti
64535aaa3a5d4b98791337b1a8ce50ad3d39a8ac data/create/recipes/splashing/gravel.json
dd9508767f68cc8b5cc2f642690961e0c22c9985 data/create/recipes/splashing/gray_concrete_powder.json
8908b452e6bc1290ebb8cfefc2c066460de93bff data/create/recipes/splashing/green_concrete_powder.json
869a639fd7069495693fd2106165b57ce674201b data/create/recipes/splashing/ic2/crushed_raw_aluminum.json
9764857ea6ee8d31ec37e6022f89dbe662e88591 data/create/recipes/splashing/ic2/crushed_raw_aluminum.json
6fd01478f838507f9e0daf7eb0a19e8315e03cb8 data/create/recipes/splashing/ic2/crushed_raw_silver.json
b5a0a0fc79bf310965aa16e78044b3f6a8a9998f data/create/recipes/splashing/ic2/crushed_raw_tin.json
00b8d0c2577cc36da1c862234b61fb7d1cfe3e65 data/create/recipes/splashing/ic2/crushed_raw_uranium.json

View file

@ -1,2 +1,2 @@
// 1.20.1 2024-09-02T22:36:27.3041196 Create's Custom Sounds
// 1.20.1 2024-10-09T12:24:59.2018604 Create's Custom Sounds
bcfd9320f8ed54f3282b1757a41da0d1753e1754 assets/create/sounds.json

View file

@ -1,2 +1,2 @@
// 1.20.1 2024-09-02T22:36:27.3550723 Create's Recipe Serializer Tags
// 1.20.1 2024-10-09T12:24:59.2646915 Create's Recipe Serializer Tags
0d8718f7383761bc5d7bc45306ed266ebf25dc1d data/create/tags/recipe_serializer/automation_ignore.json

View file

@ -1,4 +1,4 @@
// 1.20.1 2024-09-02T22:36:27.3570669 Create's Generated Registry Entries
// 1.20.1 2024-10-09T12:24:59.2666858 Create's Generated Registry Entries
030ede1044384c4117ac1e491bf5c78bbd2842f5 data/create/damage_type/crush.json
92b0416950ffeb3ba68811e587177c2f8811c2c5 data/create/damage_type/cuckoo_surprise.json
d2a4fdb64f4ba817e13a7b20c73fd1ca34b825fc data/create/damage_type/fan_fire.json

View file

@ -2479,6 +2479,7 @@
"create.station.remove_auto_schedule": ןnpǝɥɔS-oʇnⱯ pɹɐɔsıᗡ",
"create.station.remove_schedule": ןnpǝɥɔS ǝʌǝıɹʇǝᴚ",
"create.station.retry": "ʎɹʇǝɹ puɐ sıɥʇ ǝʌןosǝᴚ",
"create.station.train_map_color": "sdɐW uo ɹoןoƆ",
"create.station.train_not_aligned": "'ǝןqɯǝssɐsıp ʇouuɐƆ",
"create.station.train_not_aligned_1": "pǝubıןɐ sǝbɐıɹɹɐɔ ןןɐ ʇou",
"create.subtitle.blaze_munch": "sǝɥɔunɯ ɹǝuɹnᗺ ǝzɐןᗺ",
@ -2649,6 +2650,21 @@
"create.train_assembly.sideways_controls": "sʎɐʍǝpıs ǝɔɐɟ ʇouuɐɔ sןoɹʇuoƆ uıɐɹ⟘",
"create.train_assembly.single_bogey_carriage": "uʍo sʇı uo ǝbɐıɹɹɐɔ ɐ ʇɹoddns ʇouuɐɔ ǝdʎʇ ʎǝboᗺ sıɥ⟘",
"create.train_assembly.too_many_bogeys": "%1$s :pǝɥɔɐʇʇɐ sʎǝboᗺ ʎuɐɯ oo⟘",
"create.train_map.cannot_traverse_section": "ǝsɹǝʌɐɹʇ ʎןןnɟ ʇouuɐƆ ",
"create.train_map.conductor_missing": "buıssıW ɹoʇɔnpuoƆ >¡< ",
"create.train_map.derailed": "pǝןıɐɹǝᗡ >¡< ",
"create.train_map.for_other_train": "%1$s ɹoɟ ",
"create.train_map.fuel_boosted": "✔ pǝʇsooq ןǝnℲ ",
"create.train_map.navigation_failed": "pǝןıɐℲ uoıʇɐbıʌɐN >¡< ",
"create.train_map.player_controlled": "ɹǝʎɐןԀ ʎq pǝןןoɹʇuoƆ >- ",
"create.train_map.redstone_powered": "pǝɹǝʍoԀ ǝuoʇspǝᴚ ",
"create.train_map.schedule_interrupted": "pǝʇdnɹɹǝʇuI ǝןnpǝɥɔS >¡< ",
"create.train_map.section_reserved": "pǝʌɹǝsǝɹ uoıʇɔǝS ",
"create.train_map.toggle": "ʎɐןɹǝʌo ʞɹoʍʇǝu uıɐɹ⟘",
"create.train_map.train_at_station": "%1$s |> ",
"create.train_map.train_moving_to_station": ")ɯ%2$s( %1$s >> ",
"create.train_map.train_owned_by": "%1$s ʎq",
"create.train_map.waiting_at_signal": "ןɐubıS ʇɐ buıʇıɐM ",
"create.tunnel.selection_mode.forced_round_robin": "uıqoᴚ punoᴚ pǝɔɹoℲ",
"create.tunnel.selection_mode.forced_split": ıןdS pǝɔɹoℲ",
"create.tunnel.selection_mode.prefer_nearest": "ʇsǝɹɐǝN ɹǝɟǝɹԀ",

View file

@ -2479,6 +2479,7 @@
"create.station.remove_auto_schedule": "Discard Auto-Schedule",
"create.station.remove_schedule": "Retrieve Schedule",
"create.station.retry": "Resolve this and retry",
"create.station.train_map_color": "Color on Maps",
"create.station.train_not_aligned": "Cannot disassemble,",
"create.station.train_not_aligned_1": "not all carriages aligned",
"create.subtitle.blaze_munch": "Blaze Burner munches",
@ -2649,6 +2650,21 @@
"create.train_assembly.sideways_controls": "Train Controls cannot face sideways",
"create.train_assembly.single_bogey_carriage": "This Bogey type cannot support a carriage on its own",
"create.train_assembly.too_many_bogeys": "Too many Bogeys attached: %1$s",
"create.train_map.cannot_traverse_section": " Cannot fully traverse",
"create.train_map.conductor_missing": " <!> Conductor Missing",
"create.train_map.derailed": " <!> Derailed",
"create.train_map.for_other_train": " for %1$s",
"create.train_map.fuel_boosted": " Fuel boosted ✔",
"create.train_map.navigation_failed": " <!> Navigation Failed",
"create.train_map.player_controlled": " -> Controlled by Player",
"create.train_map.redstone_powered": " Redstone Powered",
"create.train_map.schedule_interrupted": " <!> Schedule Interrupted",
"create.train_map.section_reserved": " Section reserved",
"create.train_map.toggle": "Train network overlay",
"create.train_map.train_at_station": " >| %1$s",
"create.train_map.train_moving_to_station": " >> %1$s (%2$sm)",
"create.train_map.train_owned_by": "by %1$s",
"create.train_map.waiting_at_signal": " Waiting at Signal",
"create.tunnel.selection_mode.forced_round_robin": "Forced Round Robin",
"create.tunnel.selection_mode.forced_split": "Forced Split",
"create.tunnel.selection_mode.prefer_nearest": "Prefer Nearest",

View file

@ -15,7 +15,7 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "create:blasting/ingot_aluminum_compat_ic2"
"recipe": "create:blasting/ingot_aluminium_compat_ic2"
},
"trigger": "minecraft:recipe_unlocked"
}
@ -28,7 +28,7 @@
],
"rewards": {
"recipes": [
"create:blasting/ingot_aluminum_compat_ic2"
"create:blasting/ingot_aluminium_compat_ic2"
]
},
"sends_telemetry_event": false

View file

@ -15,7 +15,7 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "create:smelting/ingot_aluminum_compat_ic2"
"recipe": "create:smelting/ingot_aluminium_compat_ic2"
},
"trigger": "minecraft:recipe_unlocked"
}
@ -28,7 +28,7 @@
],
"rewards": {
"recipes": [
"create:smelting/ingot_aluminum_compat_ic2"
"create:smelting/ingot_aluminium_compat_ic2"
]
},
"sends_telemetry_event": false

View file

@ -12,5 +12,5 @@
"ingredient": {
"item": "create:crushed_raw_aluminum"
},
"result": "ic2:ingot_aluminum"
"result": "ic2:ingot_aluminium"
}

View file

@ -1,6 +1,12 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "upgrade_aquatic"
}
],
"ingredients": [
{
"tag": "minecraft:small_flowers"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/black"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/blue"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/brown"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/cyan"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/gray"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/green"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/light_blue"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/light_gray"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/lime"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/magenta"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/orange"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/pink"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/purple"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/red"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/white"

View file

@ -1,5 +1,11 @@
{
"type": "create:milling",
"conditions": [
{
"type": "forge:mod_loaded",
"modid": "botania"
}
],
"ingredients": [
{
"tag": "botania:petals/yellow"

View file

@ -12,5 +12,5 @@
"ingredient": {
"item": "create:crushed_raw_aluminum"
},
"result": "ic2:ingot_aluminum"
"result": "ic2:ingot_aluminium"
}

View file

@ -14,7 +14,7 @@
"results": [
{
"count": 9,
"item": "ic2:nugget_aluminum"
"item": "ic2:nugget_aluminium"
}
]
}

View file

@ -1,5 +1,21 @@
{
"values": [
"create:copper_shingle_slab",
"create:exposed_copper_shingle_slab",
"create:weathered_copper_shingle_slab",
"create:oxidized_copper_shingle_slab",
"create:waxed_copper_shingle_slab",
"create:waxed_exposed_copper_shingle_slab",
"create:waxed_weathered_copper_shingle_slab",
"create:waxed_oxidized_copper_shingle_slab",
"create:copper_tile_slab",
"create:exposed_copper_tile_slab",
"create:weathered_copper_tile_slab",
"create:oxidized_copper_tile_slab",
"create:waxed_copper_tile_slab",
"create:waxed_exposed_copper_tile_slab",
"create:waxed_weathered_copper_tile_slab",
"create:waxed_oxidized_copper_tile_slab",
"create:cut_granite_slab",
"create:polished_cut_granite_slab",
"create:cut_granite_brick_slab",

View file

@ -1,5 +1,21 @@
{
"values": [
"create:copper_shingle_stairs",
"create:exposed_copper_shingle_stairs",
"create:weathered_copper_shingle_stairs",
"create:oxidized_copper_shingle_stairs",
"create:waxed_copper_shingle_stairs",
"create:waxed_exposed_copper_shingle_stairs",
"create:waxed_weathered_copper_shingle_stairs",
"create:waxed_oxidized_copper_shingle_stairs",
"create:copper_tile_stairs",
"create:exposed_copper_tile_stairs",
"create:weathered_copper_tile_stairs",
"create:oxidized_copper_tile_stairs",
"create:waxed_copper_tile_stairs",
"create:waxed_exposed_copper_tile_stairs",
"create:waxed_weathered_copper_tile_stairs",
"create:waxed_oxidized_copper_tile_stairs",
"create:cut_granite_stairs",
"create:polished_cut_granite_stairs",
"create:cut_granite_brick_stairs",

View file

@ -199,6 +199,7 @@ import com.simibubi.create.content.schematics.cannon.SchematicannonRenderer;
import com.simibubi.create.content.schematics.cannon.SchematicannonVisual;
import com.simibubi.create.content.schematics.table.SchematicTableBlockEntity;
import com.simibubi.create.content.trains.bogey.BogeyBlockEntityRenderer;
import com.simibubi.create.content.trains.bogey.BogeyBlockEntityVisual;
import com.simibubi.create.content.trains.bogey.StandardBogeyBlockEntity;
import com.simibubi.create.content.trains.display.FlapDisplayBlockEntity;
import com.simibubi.create.content.trains.display.FlapDisplayRenderer;
@ -844,6 +845,7 @@ public class AllBlockEntityTypes {
public static final BlockEntityEntry<StandardBogeyBlockEntity> BOGEY = REGISTRATE
.blockEntity("bogey", StandardBogeyBlockEntity::new)
.visual(() -> BogeyBlockEntityVisual::new, false)
.renderer(() -> BogeyBlockEntityRenderer::new)
.validBlocks(AllBlocks.SMALL_BOGEY, AllBlocks.LARGE_BOGEY)
.register();

View file

@ -1,132 +1,42 @@
package com.simibubi.create;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableMap;
import com.simibubi.create.content.trains.bogey.AbstractBogeyBlock;
import com.simibubi.create.content.trains.bogey.BogeyRenderer;
import com.simibubi.create.content.trains.bogey.BogeyRenderer.CommonRenderer;
import org.jetbrains.annotations.ApiStatus;
import com.simibubi.create.content.trains.bogey.BogeySizes;
import com.simibubi.create.content.trains.bogey.BogeyStyle;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.CommonStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.LargeStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer.SmallStandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.BogeyStyle.SizeRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyRenderer;
import com.simibubi.create.content.trains.bogey.StandardBogeyVisual;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import com.tterrag.registrate.util.entry.BlockEntry;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
public class AllBogeyStyles {
public static final Map<ResourceLocation, BogeyStyle> BOGEY_STYLES = new HashMap<>();
public static final Map<ResourceLocation, Map<ResourceLocation, BogeyStyle>> CYCLE_GROUPS = new HashMap<>();
private static final Map<ResourceLocation, BogeyStyle> EMPTY_GROUP = ImmutableMap.of();
private static final Map<ResourceLocation, BogeyStyle> EMPTY_GROUP = Collections.emptyMap();
public static final ResourceLocation STANDARD_CYCLE_GROUP = Create.asResource("standard");
public static final BogeyStyle STANDARD =
builder("standard", STANDARD_CYCLE_GROUP).displayName(Components.translatable("create.bogey.style.standard"))
.size(BogeySizes.SMALL, AllBlocks.SMALL_BOGEY, () -> new SizeRenderer(new StandardBogeyRenderer.Small(), StandardBogeyVisual.Small::new))
.size(BogeySizes.LARGE, AllBlocks.LARGE_BOGEY, () -> new SizeRenderer(new StandardBogeyRenderer.Large(), StandardBogeyVisual.Large::new))
.build();
public static Map<ResourceLocation, BogeyStyle> getCycleGroup(ResourceLocation cycleGroup) {
return CYCLE_GROUPS.getOrDefault(cycleGroup, EMPTY_GROUP);
}
public static final String STANDARD_CYCLE_GROUP = "standard";
public static final BogeyStyle STANDARD =
create("standard", STANDARD_CYCLE_GROUP).commonRenderer(() -> CommonStandardBogeyRenderer::new)
.displayName(Components.translatable("create.bogey.style.standard"))
.size(BogeySizes.SMALL, () -> SmallStandardBogeyRenderer::new, AllBlocks.SMALL_BOGEY)
.size(BogeySizes.LARGE, () -> LargeStandardBogeyRenderer::new, AllBlocks.LARGE_BOGEY)
.build();
private static BogeyStyleBuilder create(String name, String cycleGroup) {
return create(Create.asResource(name), Create.asResource(cycleGroup));
private static BogeyStyle.Builder builder(String name, ResourceLocation cycleGroup) {
return new BogeyStyle.Builder(Create.asResource(name), cycleGroup);
}
public static BogeyStyleBuilder create(ResourceLocation name, ResourceLocation cycleGroup) {
return new BogeyStyleBuilder(name, cycleGroup);
}
public static void register() {}
public static class BogeyStyleBuilder {
protected final Map<BogeySizes.BogeySize, Supplier<BogeyStyle.SizeRenderData>> sizeRenderers = new HashMap<>();
protected final Map<BogeySizes.BogeySize, ResourceLocation> sizes = new HashMap<>();
protected final ResourceLocation name;
protected final ResourceLocation cycleGroup;
protected Component displayName = Lang.translateDirect("bogey.style.invalid");
protected ResourceLocation soundType = AllSoundEvents.TRAIN2.getId();
protected CompoundTag defaultData = new CompoundTag();
protected ParticleOptions contactParticle = ParticleTypes.CRIT;
protected ParticleOptions smokeParticle = ParticleTypes.POOF;
protected Optional<Supplier<? extends CommonRenderer>> commonRenderer = Optional.empty();
public BogeyStyleBuilder(ResourceLocation name, ResourceLocation cycleGroup) {
this.name = name;
this.cycleGroup = cycleGroup;
}
public BogeyStyleBuilder displayName(Component displayName) {
this.displayName = displayName;
return this;
}
public BogeyStyleBuilder soundType(ResourceLocation soundType) {
this.soundType = soundType;
return this;
}
public BogeyStyleBuilder defaultData(CompoundTag defaultData) {
this.defaultData = defaultData;
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<Supplier<? extends BogeyRenderer>> renderer,
BlockEntry<? extends AbstractBogeyBlock<?>> blockEntry) {
this.size(size, renderer, blockEntry.getId());
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<Supplier<? extends BogeyRenderer>> renderer,
ResourceLocation location) {
this.sizes.put(size, location);
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.sizeRenderers.put(size, () -> new BogeyStyle.SizeRenderData(renderer.get(), renderer.get()
.get()));
});
return this;
}
public BogeyStyleBuilder contactParticle(ParticleOptions contactParticle) {
this.contactParticle = contactParticle;
return this;
}
public BogeyStyleBuilder smokeParticle(ParticleOptions smokeParticle) {
this.smokeParticle = smokeParticle;
return this;
}
public BogeyStyleBuilder commonRenderer(Supplier<Supplier<? extends CommonRenderer>> commonRenderer) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.commonRenderer = Optional.of(commonRenderer.get());
});
return this;
}
public BogeyStyle build() {
BogeyStyle entry = new BogeyStyle(name, cycleGroup, displayName, soundType, contactParticle, smokeParticle,
defaultData, sizes, sizeRenderers, commonRenderer);
BOGEY_STYLES.put(name, entry);
CYCLE_GROUPS.computeIfAbsent(cycleGroup, l -> new HashMap<>())
.put(name, entry);
return entry;
}
@ApiStatus.Internal
public static void init() {
}
}

View file

@ -8,6 +8,8 @@ import java.util.function.Function;
import java.util.function.Supplier;
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
import com.simibubi.create.compat.trainmap.TrainMapSyncPacket;
import com.simibubi.create.compat.trainmap.TrainMapSyncRequestPacket;
import com.simibubi.create.content.contraptions.ContraptionBlockChangedPacket;
import com.simibubi.create.content.contraptions.ContraptionColliderLockPacket;
import com.simibubi.create.content.contraptions.ContraptionColliderLockPacket.ContraptionColliderLockPacketRequest;
@ -164,6 +166,7 @@ public enum AllPackets {
CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER),
CONTRAPTION_COLLIDER_LOCK_REQUEST(ContraptionColliderLockPacketRequest.class,
ContraptionColliderLockPacketRequest::new, PLAY_TO_SERVER),
TRAIN_MAP_REQUEST(TrainMapSyncRequestPacket.class, TrainMapSyncRequestPacket::new, PLAY_TO_SERVER),
// Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),
@ -208,7 +211,8 @@ public enum AllPackets {
CONTRAPTION_ACTOR_TOGGLE(ContraptionDisableActorPacket.class, ContraptionDisableActorPacket::new, PLAY_TO_CLIENT),
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
ATTACHED_COMPUTER(AttachedComputerPacket.class, AttachedComputerPacket::new, PLAY_TO_CLIENT),
SERVER_DEBUG_INFO(ServerDebugInfoPacket.class, ServerDebugInfoPacket::new, PLAY_TO_CLIENT)
SERVER_DEBUG_INFO(ServerDebugInfoPacket.class, ServerDebugInfoPacket::new, PLAY_TO_CLIENT),
TRAIN_MAP_SYNC(TrainMapSyncPacket.class, TrainMapSyncPacket::new, PLAY_TO_CLIENT)
;
public static final ResourceLocation CHANNEL_NAME = Create.asResource("main");

View file

@ -136,7 +136,7 @@ public class Create {
AllFanProcessingTypes.register();
BlockSpoutingBehaviour.registerDefaults();
BogeySizes.init();
AllBogeyStyles.register();
AllBogeyStyles.init();
// ----
ComputerCraftProxy.register();

View file

@ -31,7 +31,10 @@ public enum Mods {
TCONSTRUCT,
FRAMEDBLOCKS,
XLPACKETS,
MODERNUI;
MODERNUI,
FTBCHUNKS,
JOURNEYMAP,
BETTEREND;
private final String id;
@ -53,11 +56,11 @@ public enum Mods {
public Block getBlock(String id) {
return ForgeRegistries.BLOCKS.getValue(rl(id));
}
public Item getItem(String id) {
return ForgeRegistries.ITEMS.getValue(rl(id));
}
public boolean contains(ItemLike entry) {
if (!isLoaded())
return false;

View file

@ -0,0 +1,66 @@
package com.simibubi.create.compat.betterend;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import com.simibubi.create.Create;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.portal.PortalInfo;
public class BetterEndPortalCompat {
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static MethodHandle constructorHandle;
private static VarHandle portalEntrancePosHandle;
private static MethodHandle findDimensionEntryPointHandle;
private static boolean hasErrored = false;
static {
try {
Class<?> travelerStateClass = Class.forName("org.betterx.betterend.portal.TravelerState");
MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(travelerStateClass, lookup);
MethodType travelerStateConstructorTypes = MethodType.methodType(void.class, Entity.class);
constructorHandle = lookup.findConstructor(travelerStateClass, travelerStateConstructorTypes);
portalEntrancePosHandle = privateLookup.findVarHandle(travelerStateClass, "portalEntrancePos", BlockPos.class);
MethodType findDimensionEntryPointTypes = MethodType.methodType(PortalInfo.class, ServerLevel.class);
findDimensionEntryPointHandle = privateLookup.findVirtual(travelerStateClass, "findDimensionEntryPoint", findDimensionEntryPointTypes);
} catch (Exception e) {
Create.LOGGER.error("Create's Better End Portal compat failed to initialize: ", e);
hasErrored = true;
}
}
/**
* Retrieves the adjusted {@link PortalInfo} for the Better End portal using reflection.
*
* @param targetLevel The target {@link ServerLevel} (dimension).
* @param entity The probe {@link Entity} used for portal traversal calculations.
* @return The adjusted {@link PortalInfo} for the target dimension, or {@code null} if an error occurs.
*/
public static PortalInfo getBetterEndPortalInfo(ServerLevel targetLevel, Entity entity) {
if (!hasErrored) {
try {
Object travelerState = constructorHandle.invoke(entity);
// Set the private portalEntrancePos field to the entity's block position
// as assumed in TravelerState#findDimensionEntryPoint
portalEntrancePosHandle.set(travelerState, entity.blockPosition().immutable());
return (PortalInfo) findDimensionEntryPointHandle.invoke(travelerState, targetLevel);
} catch (Throwable e) {
Create.LOGGER.error("Create's Better End Portal compat failed to initialize: ", e);
}
}
return null;
}
}

View file

@ -129,7 +129,7 @@ public class StationPeripheral extends SyncedPeripheral<StationBlockEntity> {
public final void setTrainName(String name) throws LuaException {
Train train = getTrainOrThrow();
train.name = Components.literal(name);
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId()));
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId(), train.mapColorIndex));
}
@LuaFunction

View file

@ -10,8 +10,10 @@ import com.simibubi.create.content.fluids.potion.PotionFluidHandler;
import com.simibubi.create.content.fluids.transfer.EmptyingRecipe;
import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.forge.ForgeTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
@ -22,6 +24,7 @@ import mezz.jei.api.runtime.IIngredientManager;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ItemStackLinkedSet;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.PotionItem;
import net.minecraft.world.item.crafting.Ingredient;
@ -41,6 +44,7 @@ public class ItemDrainCategory extends CreateRecipeCategory<EmptyingRecipe> {
}
public static void consumeRecipes(Consumer<EmptyingRecipe> consumer, IIngredientManager ingredientManager) {
ObjectOpenCustomHashSet<ItemStack> emptiedItems = new ObjectOpenCustomHashSet<>(ItemStackLinkedSet.TYPE_AND_TAG);
for (ItemStack stack : ingredientManager.getAllIngredients(VanillaTypes.ITEM_STACK)) {
if (PotionFluidHandler.isPotionItem(stack)) {
FluidStack fluidFromPotionItem = PotionFluidHandler.getFluidFromPotionItem(stack);
@ -68,6 +72,11 @@ public class ItemDrainCategory extends CreateRecipeCategory<EmptyingRecipe> {
if (result.isEmpty())
continue;
// There can be a lot of duplicate empty tanks (e.g. from emptying tanks with different fluids). Merge
// them to reduce memory usage. If the item is exactly the same as the input, just use the input stack
// instead of the copy.
result = ItemHelper.sameItem(stack, result) ? stack : emptiedItems.addOrGet(result);
Ingredient ingredient = Ingredient.of(stack);
ResourceLocation itemName = RegisteredObjects.getKeyOrThrow(stack.getItem());
ResourceLocation fluidName = RegisteredObjects.getKeyOrThrow(extracted.getFluid());

View file

@ -63,7 +63,16 @@ public class SpoutCategory extends CreateRecipeCategory<FillingRecipe> {
if (!capability.isPresent())
continue;
var existingFluidHandler = capability.orElse(null);
int numTanks = existingFluidHandler.getTanks();
FluidStack existingFluid = numTanks == 1 ? existingFluidHandler.getFluidInTank(0) : FluidStack.EMPTY;
for (FluidStack fluidStack : fluidStacks) {
// Hoist the fluid equality check to avoid the work of copying the stack + populating capabilities
// when most fluids will not match
if (numTanks == 1 && (!existingFluid.isEmpty() && !existingFluid.isFluidEqual(fluidStack))) {
continue;
}
ItemStack copy = stack.copy();
copy.getCapability(ForgeCapabilities.FLUID_HANDLER_ITEM)
.ifPresent(fhi -> {

View file

@ -0,0 +1,158 @@
package com.simibubi.create.compat.trainmap;
import java.util.List;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.gui.RemovedGuiUtils;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.infrastructure.config.AllConfigs;
import dev.ftb.mods.ftbchunks.client.gui.LargeMapScreen;
import dev.ftb.mods.ftbchunks.client.gui.RegionMapPanel;
import dev.ftb.mods.ftblibrary.ui.ScreenWrapper;
import dev.ftb.mods.ftblibrary.ui.Widget;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.util.Mth;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.client.event.RenderTooltipEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
public class FTBChunksTrainMap {
private static int cancelTooltips = 0;
private static boolean renderingTooltip = false;
private static boolean requesting;
public static void tick() {
if (cancelTooltips > 0)
cancelTooltips--;
if (!AllConfigs.client().showTrainMapOverlay.get()
|| getAsLargeMapScreen(Minecraft.getInstance().screen) == null) {
if (requesting)
TrainMapSyncClient.stopRequesting();
requesting = false;
return;
}
TrainMapManager.tick();
requesting = true;
TrainMapSyncClient.requestData();
}
public static void cancelTooltips(RenderTooltipEvent.Pre event) {
if (getAsLargeMapScreen(Minecraft.getInstance().screen) == null)
return;
if (renderingTooltip || cancelTooltips == 0)
return;
event.setCanceled(true);
}
public static void mouseClick(InputEvent.MouseButton.Pre event) {
LargeMapScreen screen = getAsLargeMapScreen(Minecraft.getInstance().screen);
if (screen == null)
return;
if (TrainMapManager.handleToggleWidgetClick(screen.getMouseX(), screen.getMouseY(), 20, 2))
event.setCanceled(true);
}
public static void renderGui(ScreenEvent.Render.Post event) {
LargeMapScreen largeMapScreen = getAsLargeMapScreen(event.getScreen());
if (largeMapScreen == null)
return;
Object panel = ObfuscationReflectionHelper.getPrivateValue(LargeMapScreen.class, largeMapScreen, "regionPanel");
if (!(panel instanceof RegionMapPanel regionMapPanel))
return;
GuiGraphics graphics = event.getGuiGraphics();
if (!AllConfigs.client().showTrainMapOverlay.get()) {
renderToggleWidgetAndTooltip(event, largeMapScreen, graphics);
return;
}
int blocksPerRegion = 16 * 32;
int minX = Mth.floor(regionMapPanel.getScrollX());
int minY = Mth.floor(regionMapPanel.getScrollY());
float regionTileSize = largeMapScreen.getRegionTileSize() / (float) blocksPerRegion;
int regionMinX =
ObfuscationReflectionHelper.getPrivateValue(RegionMapPanel.class, regionMapPanel, "regionMinX");
int regionMinZ =
ObfuscationReflectionHelper.getPrivateValue(RegionMapPanel.class, regionMapPanel, "regionMinZ");
float mouseX = event.getMouseX();
float mouseY = event.getMouseY();
boolean linearFiltering = largeMapScreen.getRegionTileSize() * Minecraft.getInstance()
.getWindow()
.getGuiScale() < 512D;
PoseStack pose = graphics.pose();
pose.pushPose();
pose.translate(-minX, -minY, 0);
pose.scale(regionTileSize, regionTileSize, 1);
pose.translate(-regionMinX * blocksPerRegion, -regionMinZ * blocksPerRegion, 0);
mouseX += minX;
mouseY += minY;
mouseX /= regionTileSize;
mouseY /= regionTileSize;
mouseX += regionMinX * blocksPerRegion;
mouseY += regionMinZ * blocksPerRegion;
Rect2i bounds = new Rect2i(Mth.floor(minX / regionTileSize + regionMinX * blocksPerRegion),
Mth.floor(minY / regionTileSize + regionMinZ * blocksPerRegion),
Mth.floor(largeMapScreen.width / regionTileSize), Mth.floor(largeMapScreen.height / regionTileSize));
List<FormattedText> tooltip = TrainMapManager.renderAndPick(graphics, Mth.floor(mouseX), Mth.floor(mouseY),
event.getPartialTick(), linearFiltering, bounds);
pose.popPose();
if (!renderToggleWidgetAndTooltip(event, largeMapScreen, graphics) && tooltip != null) {
renderingTooltip = true;
RemovedGuiUtils.drawHoveringText(graphics, tooltip, event.getMouseX(), event.getMouseY(),
largeMapScreen.width, largeMapScreen.height, 256, Minecraft.getInstance().font);
renderingTooltip = false;
cancelTooltips = 5;
}
pose.pushPose();
pose.translate(0, 0, 300);
for (Widget widget : largeMapScreen.getWidgets()) {
if (!widget.isEnabled())
continue;
if (widget == panel)
continue;
widget.draw(graphics, largeMapScreen.getTheme(), widget.getPosX(), widget.getPosY(), widget.getWidth(),
widget.getHeight());
}
pose.popPose();
}
private static boolean renderToggleWidgetAndTooltip(ScreenEvent.Render.Post event, LargeMapScreen largeMapScreen,
GuiGraphics graphics) {
TrainMapManager.renderToggleWidget(graphics, 20, 2);
if (!TrainMapManager.isToggleWidgetHovered(event.getMouseX(), event.getMouseY(), 20, 2))
return false;
renderingTooltip = true;
RemovedGuiUtils.drawHoveringText(graphics, List.of(Lang.translate("train_map.toggle")
.component()), event.getMouseX(), event.getMouseY() + 20, largeMapScreen.width, largeMapScreen.height, 256,
Minecraft.getInstance().font);
renderingTooltip = false;
cancelTooltips = 5;
return true;
}
private static LargeMapScreen getAsLargeMapScreen(Screen screen) {
if (!(screen instanceof ScreenWrapper screenWrapper))
return null;
Object wrapped = ObfuscationReflectionHelper.getPrivateValue(ScreenWrapper.class, screenWrapper, "wrappedGui");
if (!(wrapped instanceof LargeMapScreen largeMapScreen))
return null;
return largeMapScreen;
}
}

View file

@ -0,0 +1,108 @@
package com.simibubi.create.compat.trainmap;
import java.util.List;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.gui.RemovedGuiUtils;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.infrastructure.config.AllConfigs;
import journeymap.client.api.display.Context.UI;
import journeymap.client.api.util.UIState;
import journeymap.client.ui.fullscreen.Fullscreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.util.Mth;
import net.minecraftforge.client.event.InputEvent;
public class JourneyTrainMap {
private static boolean requesting;
public static void tick() {
if (!AllConfigs.client().showTrainMapOverlay.get() || !(Minecraft.getInstance().screen instanceof Fullscreen)) {
if (requesting)
TrainMapSyncClient.stopRequesting();
requesting = false;
return;
}
TrainMapManager.tick();
requesting = true;
TrainMapSyncClient.requestData();
}
public static void mouseClick(InputEvent.MouseButton.Pre event) {
Minecraft mc = Minecraft.getInstance();
if (!(mc.screen instanceof Fullscreen screen))
return;
Window window = mc.getWindow();
double mX = mc.mouseHandler.xpos() * window.getGuiScaledWidth() / window.getScreenWidth();
double mY = mc.mouseHandler.ypos() * window.getGuiScaledHeight() / window.getScreenHeight();
if (TrainMapManager.handleToggleWidgetClick(Mth.floor(mX), Mth.floor(mY), 3, 30))
event.setCanceled(true);
}
// Called by JourneyFullscreenMapMixin
public static void onRender(GuiGraphics graphics, Fullscreen screen, double x, double z, int mX, int mY, float pt) {
UIState state = screen.getUiState();
if (state == null)
return;
if (state.ui != UI.Fullscreen)
return;
if (!state.active)
return;
if (!AllConfigs.client().showTrainMapOverlay.get()) {
renderToggleWidgetAndTooltip(graphics, screen, mX, mY);
return;
}
Minecraft mc = Minecraft.getInstance();
Window window = mc.getWindow();
double guiScale = (double) window.getScreenWidth() / window.getGuiScaledWidth();
double scale = state.blockSize / guiScale;
PoseStack pose = graphics.pose();
pose.pushPose();
pose.translate(screen.width / 2.0f, screen.height / 2.0f, 0);
pose.scale((float) scale, (float) scale, 1);
pose.translate(-x, -z, 0);
float mouseX = mX - screen.width / 2.0f;
float mouseY = mY - screen.height / 2.0f;
mouseX /= scale;
mouseY /= scale;
mouseX += x;
mouseY += z;
Rect2i bounds =
new Rect2i(Mth.floor(-screen.width / 2.0f / scale + x), Mth.floor(-screen.height / 2.0f / scale + z),
Mth.floor(screen.width / scale), Mth.floor(screen.height / scale));
List<FormattedText> tooltip =
TrainMapManager.renderAndPick(graphics, Mth.floor(mouseX), Mth.floor(mouseY), pt, false, bounds);
pose.popPose();
if (!renderToggleWidgetAndTooltip(graphics, screen, mX, mY) && tooltip != null)
RemovedGuiUtils.drawHoveringText(graphics, tooltip, mX, mY, screen.width, screen.height, 256, mc.font);
}
private static boolean renderToggleWidgetAndTooltip(GuiGraphics graphics, Fullscreen screen, int mouseX,
int mouseY) {
TrainMapManager.renderToggleWidget(graphics, 3, 30);
if (!TrainMapManager.isToggleWidgetHovered(mouseX, mouseY, 3, 30))
return false;
RemovedGuiUtils.drawHoveringText(graphics, List.of(Lang.translate("train_map.toggle")
.component()), mouseX, mouseY + 20, screen.width, screen.height, 256, Minecraft.getInstance().font);
return true;
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.compat.trainmap;
import com.mojang.blaze3d.platform.InputConstants;
import com.simibubi.create.compat.Mods;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.client.event.RenderTooltipEvent;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.event.TickEvent.Phase;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber(value = Dist.CLIENT)
public class TrainMapEvents {
@SubscribeEvent
public static void tick(ClientTickEvent event) {
if (event.phase == Phase.START)
return;
Minecraft mc = Minecraft.getInstance();
if (mc.level == null)
return;
if (Mods.FTBCHUNKS.isLoaded())
FTBChunksTrainMap.tick();
if (Mods.JOURNEYMAP.isLoaded())
JourneyTrainMap.tick();
}
@SubscribeEvent
public static void mouseClick(InputEvent.MouseButton.Pre event) {
if (event.getAction() != InputConstants.PRESS)
return;
if (Mods.FTBCHUNKS.isLoaded())
FTBChunksTrainMap.mouseClick(event);
if (Mods.JOURNEYMAP.isLoaded())
JourneyTrainMap.mouseClick(event);
}
@SubscribeEvent
public static void cancelTooltips(RenderTooltipEvent.Pre event) {
if (Mods.FTBCHUNKS.isLoaded())
FTBChunksTrainMap.cancelTooltips(event);
}
@SubscribeEvent
public static void renderGui(ScreenEvent.Render.Post event) {
if (Mods.FTBCHUNKS.isLoaded())
FTBChunksTrainMap.renderGui(event);
}
}

View file

@ -0,0 +1,730 @@
package com.simibubi.create.compat.trainmap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.simibubi.create.CreateClient;
import com.simibubi.create.compat.trainmap.TrainMapSync.SignalState;
import com.simibubi.create.compat.trainmap.TrainMapSync.TrainMapSyncEntry;
import com.simibubi.create.compat.trainmap.TrainMapSync.TrainState;
import com.simibubi.create.content.trains.entity.Carriage;
import com.simibubi.create.content.trains.entity.Train;
import com.simibubi.create.content.trains.graph.EdgePointType;
import com.simibubi.create.content.trains.graph.TrackEdge;
import com.simibubi.create.content.trains.graph.TrackGraph;
import com.simibubi.create.content.trains.graph.TrackNode;
import com.simibubi.create.content.trains.graph.TrackNodeLocation;
import com.simibubi.create.content.trains.station.GlobalStation;
import com.simibubi.create.content.trains.track.BezierConnection;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.Components;
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.Pair;
import com.simibubi.create.infrastructure.config.AllConfigs;
import com.simibubi.create.infrastructure.config.CClient;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.FastColor;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
public class TrainMapManager {
public static void tick() {
TrainMapRenderer map = TrainMapRenderer.INSTANCE;
if (map.trackingVersion != CreateClient.RAILWAYS.version
|| map.trackingDim != Minecraft.getInstance().level.dimension()
|| map.trackingTheme != AllConfigs.client().trainMapColorTheme.get()) {
redrawAll();
}
}
public static List<FormattedText> renderAndPick(GuiGraphics graphics, int mouseX, int mouseY, float pt,
boolean linearFiltering, Rect2i bounds) {
Object hoveredElement = null;
int offScreenMargin = 32;
bounds.setX(bounds.getX() - offScreenMargin);
bounds.setY(bounds.getY() - offScreenMargin);
bounds.setWidth(bounds.getWidth() + 2 * offScreenMargin);
bounds.setHeight(bounds.getHeight() + 2 * offScreenMargin);
TrainMapRenderer.INSTANCE.render(graphics, mouseX, mouseY, pt, linearFiltering, bounds);
hoveredElement = drawTrains(graphics, mouseX, mouseY, pt, hoveredElement, bounds);
hoveredElement = drawPoints(graphics, mouseX, mouseY, pt, hoveredElement, bounds);
graphics.bufferSource()
.endBatch();
if (hoveredElement instanceof GlobalStation station)
return List.of(Components.literal(station.name));
if (hoveredElement instanceof Train train)
return listTrainDetails(train);
return null;
}
public static void renderToggleWidget(GuiGraphics graphics, int x, int y) {
boolean enabled = AllConfigs.client().showTrainMapOverlay.get();
if (CreateClient.RAILWAYS.trackNetworks.isEmpty())
return;
RenderSystem.enableBlend();
PoseStack pose = graphics.pose();
pose.pushPose();
pose.translate(0, 0, 300);
AllGuiTextures.TRAINMAP_TOGGLE_PANEL.render(graphics, x, y);
(enabled ? AllGuiTextures.TRAINMAP_TOGGLE_ON : AllGuiTextures.TRAINMAP_TOGGLE_OFF).render(graphics, x + 18,
y + 3);
pose.popPose();
}
public static boolean handleToggleWidgetClick(int mouseX, int mouseY, int x, int y) {
if (!isToggleWidgetHovered(mouseX, mouseY, x, y))
return false;
CClient config = AllConfigs.client();
config.showTrainMapOverlay.set(!config.showTrainMapOverlay.get());
return true;
}
public static boolean isToggleWidgetHovered(int mouseX, int mouseY, int x, int y) {
if (CreateClient.RAILWAYS.trackNetworks.isEmpty())
return false;
if (mouseX < x || mouseX >= x + AllGuiTextures.TRAINMAP_TOGGLE_PANEL.width)
return false;
if (mouseY < y || mouseY >= y + AllGuiTextures.TRAINMAP_TOGGLE_PANEL.height)
return false;
return true;
}
private static List<FormattedText> listTrainDetails(Train train) {
List<FormattedText> output = new ArrayList<>();
int blue = 0xD3DEDC;
int darkBlue = 0x92A9BD;
int bright = 0xFFEFEF;
int orange = 0xFFAD60;
TrainMapSyncEntry trainEntry = TrainMapSyncClient.currentData.get(train.id);
if (trainEntry == null)
return Collections.emptyList();
TrainState state = trainEntry.state;
SignalState signalState = trainEntry.signalState;
Lang.text(train.name.getString())
.color(bright)
.addTo(output);
if (!trainEntry.ownerName.isBlank())
Lang.translate("train_map.train_owned_by", trainEntry.ownerName)
.color(blue)
.addTo(output);
switch (state) {
case CONDUCTOR_MISSING:
Lang.translate("train_map.conductor_missing")
.color(orange)
.addTo(output);
return output;
case DERAILED:
Lang.translate("train_map.derailed")
.color(orange)
.addTo(output);
return output;
case NAVIGATION_FAILED:
Lang.translate("train_map.navigation_failed")
.color(orange)
.addTo(output);
return output;
case SCHEDULE_INTERRUPTED:
Lang.translate("train_map.schedule_interrupted")
.color(orange)
.addTo(output);
return output;
case RUNNING_MANUALLY:
Lang.translate("train_map.player_controlled")
.color(blue)
.addTo(output);
break;
case RUNNING:
default:
break;
}
String currentStation = trainEntry.targetStationName;
int targetStationDistance = trainEntry.targetStationDistance;
if (!currentStation.isBlank()) {
if (targetStationDistance == 0)
Lang.translate("train_map.train_at_station", currentStation)
.color(darkBlue)
.addTo(output);
else
Lang.translate("train_map.train_moving_to_station", currentStation, targetStationDistance)
.color(darkBlue)
.addTo(output);
}
if (signalState != SignalState.NOT_WAITING) {
boolean chainSignal = signalState == SignalState.CHAIN_SIGNAL;
Lang.translate("train_map.waiting_at_signal")
.color(orange)
.addTo(output);
if (signalState == SignalState.WAITING_FOR_REDSTONE)
Lang.translate("train_map.redstone_powered")
.color(blue)
.addTo(output);
else {
UUID waitingFor = trainEntry.waitingForTrain;
boolean trainFound = false;
if (waitingFor != null) {
Train trainWaitingFor = CreateClient.RAILWAYS.trains.get(waitingFor);
if (trainWaitingFor != null) {
Lang.translate("train_map.for_other_train", trainWaitingFor.name.getString())
.color(blue)
.addTo(output);
trainFound = true;
}
}
if (!trainFound) {
if (chainSignal)
Lang.translate("train_map.cannot_traverse_section")
.color(blue)
.addTo(output);
else
Lang.translate("train_map.section_reserved")
.color(blue)
.addTo(output);
}
}
}
if (trainEntry.fueled)
Lang.translate("train_map.fuel_boosted")
.color(darkBlue)
.addTo(output);
return output;
}
private static Object drawPoints(GuiGraphics graphics, int mouseX, int mouseY, float pt, Object hoveredElement,
Rect2i bounds) {
PoseStack pose = graphics.pose();
RenderSystem.enableDepthTest();
for (TrackGraph graph : CreateClient.RAILWAYS.trackNetworks.values()) {
for (GlobalStation station : graph.getPoints(EdgePointType.STATION)) {
Couple<TrackNodeLocation> edgeLocation = station.edgeLocation;
TrackNode node = graph.locateNode(edgeLocation.getFirst());
TrackNode other = graph.locateNode(edgeLocation.getSecond());
if (node == null || other == null)
continue;
if (node.getLocation().dimension != TrainMapRenderer.INSTANCE.trackingDim)
continue;
TrackEdge edge = graph.getConnection(Couple.create(node, other));
if (edge == null)
continue;
double tLength = station.getLocationOn(edge);
double t = tLength / edge.getLength();
Vec3 position = edge.getPosition(graph, t);
int x = Mth.floor(position.x());
int y = Mth.floor(position.z());
if (!bounds.contains(x, y))
continue;
Vec3 diff = edge.getDirectionAt(tLength)
.normalize();
int rotation = Mth.positiveModulo(Mth.floor(0.5
+ (Math.atan2(diff.z, diff.x) * Mth.RAD_TO_DEG + 90 + (station.isPrimary(node) ? 180 : 0)) / 45),
8);
AllGuiTextures sprite = AllGuiTextures.TRAINMAP_STATION_ORTHO;
AllGuiTextures highlightSprite = AllGuiTextures.TRAINMAP_STATION_ORTHO_HIGHLIGHT;
if (rotation % 2 != 0) {
sprite = AllGuiTextures.TRAINMAP_STATION_DIAGO;
highlightSprite = AllGuiTextures.TRAINMAP_STATION_DIAGO_HIGHLIGHT;
}
boolean highlight = hoveredElement == null && Math.max(Math.abs(mouseX - x), Math.abs(mouseY - y)) < 3;
pose.pushPose();
pose.translate(x - 2, y - 2, 5);
pose.translate(sprite.width / 2.0, sprite.height / 2.0, 0);
pose.mulPose(Axis.ZP.rotationDegrees(90 * (rotation / 2)));
pose.translate(-sprite.width / 2.0, -sprite.height / 2.0, 0);
sprite.render(graphics, 0, 0);
sprite.render(graphics, 0, 0);
if (highlight) {
pose.translate(0, 0, 5);
highlightSprite.render(graphics, -1, -1);
hoveredElement = station;
}
pose.popPose();
}
}
return hoveredElement;
}
private static Object drawTrains(GuiGraphics graphics, int mouseX, int mouseY, float pt, Object hoveredElement,
Rect2i bounds) {
PoseStack pose = graphics.pose();
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
int spriteYOffset = -3;
double time = AnimationTickHolder.getTicks();
time += AnimationTickHolder.getPartialTicks();
time -= TrainMapSyncClient.lastPacket;
time /= TrainMapSync.lightPacketInterval;
time = Mth.clamp(time, 0, 1);
int[] sliceXShiftByRotationIndex = new int[] { 0, 1, 2, 2, 3, -2, -2, -1 };
int[] sliceYShiftByRotationIndex = new int[] { 3, 2, 2, 1, 0, 1, 2, 2 };
for (Train train : CreateClient.RAILWAYS.trains.values()) {
TrainMapSyncEntry trainEntry = TrainMapSyncClient.currentData.get(train.id);
if (trainEntry == null)
continue;
Vec3 frontPos = Vec3.ZERO;
List<Carriage> carriages = train.carriages;
boolean otherDim = true;
double avgY = 0;
for (int i = 0; i < carriages.size(); i++) {
for (boolean firstBogey : Iterate.trueAndFalse)
avgY += trainEntry.getPosition(i, firstBogey, time)
.y();
}
avgY /= carriages.size() * 2;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(i);
Vec3 pos1 = trainEntry.getPosition(i, true, time);
Vec3 pos2 = trainEntry.getPosition(i, false, time);
ResourceKey<Level> dim = trainEntry.dimensions.get(i);
if (dim == null || dim != TrainMapRenderer.INSTANCE.trackingDim)
continue;
if (!bounds.contains(Mth.floor(pos1.x()), Mth.floor(pos1.z()))
&& !bounds.contains(Mth.floor(pos2.x()), Mth.floor(pos2.z())))
continue;
otherDim = false;
if (!trainEntry.backwards && i == 0)
frontPos = pos1;
if (trainEntry.backwards && i == train.carriages.size() - 1)
frontPos = pos2;
Vec3 diff = pos2.subtract(pos1);
int size = carriage.bogeySpacing + 1;
Vec3 center = pos1.add(pos2)
.scale(0.5);
double pX = center.x;
double pY = center.z;
int rotation =
Mth.positiveModulo(Mth.floor(0.5 + (Math.atan2(diff.x, diff.z) * Mth.RAD_TO_DEG) / 22.5), 8);
if (trainEntry.state == TrainState.DERAILED)
rotation =
Mth.positiveModulo((AnimationTickHolder.getTicks() / 8 + i * 3) * (i % 2 == 0 ? 1 : -1), 8);
AllGuiTextures sprite = AllGuiTextures.TRAINMAP_SPRITES;
int slices = 2;
if (rotation == 0 || rotation == 4) {
// Orthogonal, slices add 3 pixels
slices += Mth.floor((size - 2) / (3.0) + 0.5);
}
else if (rotation == 2 || rotation == 6) {
// Diagonal, slices add 2*sqrt(2) pixels
slices += Mth.floor((size - (5 - 2 * Mth.SQRT_OF_TWO)) / (2 * Mth.SQRT_OF_TWO) + 0.5);
}
else {
// Slanty, slices add sqrt(5) pixels
slices += Mth.floor((size - (5 - Mth.sqrt(5))) / (Mth.sqrt(5)) + 0.5);
}
slices = Math.max(2, slices);
sprite.bind();
pose.pushPose();
float pivotX = 7.5f + (slices - 3) * sliceXShiftByRotationIndex[rotation] / 2.0f;
float pivotY = 6.5f + (slices - 3) * sliceYShiftByRotationIndex[rotation] / 2.0f;
// Ysort at home
pose.translate(pX - pivotX, pY - pivotY, 10 + (avgY / 512.0) + (1024.0 + center.z() % 8192.0) / 1024.0);
int trainColorIndex = train.mapColorIndex;
int colorRow = trainColorIndex / 4;
int colorCol = trainColorIndex % 4;
for (int slice = 0; slice < slices; slice++) {
int row = slice == 0 ? 1 : slice == slices - 1 ? 2 : 3;
int sliceShifts = slice == 0 ? 0 : slice == slices - 1 ? slice - 2 : slice - 1;
int col = rotation;
int positionX = sliceShifts * sliceXShiftByRotationIndex[rotation];
int positionY = sliceShifts * sliceYShiftByRotationIndex[rotation] + spriteYOffset;
int sheetX = col * 16 + colorCol * 128;
int sheetY = row * 16 + colorRow * 64;
graphics.blit(sprite.location, positionX, positionY, sheetX, sheetY, 16, 16, sprite.width,
sprite.height);
}
pose.popPose();
int margin = 1;
int sizeX = 8 + (slices - 3) * sliceXShiftByRotationIndex[rotation];
int sizeY = 12 + (slices - 3) * sliceYShiftByRotationIndex[rotation];
double pXm = pX - sizeX / 2;
double pYm = pY - sizeY / 2 + spriteYOffset;
if (hoveredElement == null && mouseX < pXm + margin + sizeX && mouseX > pXm - margin
&& mouseY < pYm + margin + sizeY && mouseY > pYm - margin)
hoveredElement = train;
}
if (otherDim)
continue;
if (trainEntry.signalState != SignalState.NOT_WAITING) {
pose.pushPose();
pose.translate(frontPos.x - 0.5, frontPos.z - 0.5, 20 + (1024.0 + frontPos.z() % 8192.0) / 1024.0);
AllGuiTextures.TRAINMAP_SIGNAL.render(graphics, 0, -3);
pose.popPose();
}
}
return hoveredElement;
}
// Background first so we can mindlessly paint over it
static final int PHASE_BACKGROUND = 0;
// Straights before curves so that curves anti-alias properly at the transition
static final int PHASE_STRAIGHTS = 1;
static final int PHASE_CURVES = 2;
public static void redrawAll() {
TrainMapRenderer map = TrainMapRenderer.INSTANCE;
map.trackingVersion = CreateClient.RAILWAYS.version;
map.trackingDim = Minecraft.getInstance().level.dimension();
map.trackingTheme = AllConfigs.client().trainMapColorTheme.get();
map.startDrawing();
int mainColor = 0xFF_7C57D4;
int darkerColor = 0xFF_70437D;
int darkerColorShadow = 0xFF_4A2754;
switch (map.trackingTheme) {
case GREY:
mainColor = 0xFF_A8B5B5;
darkerColor = 0xFF_776E6C;
darkerColorShadow = 0xFF_56504E;
break;
case WHITE:
mainColor = 0xFF_E8F9F9;
darkerColor = 0xFF_889595;
darkerColorShadow = 0xFF_56504E;
break;
default:
break;
}
List<Couple<Integer>> collisions = new ObjectArrayList<>();
for (int phase = 0; phase <= 2; phase++)
renderPhase(map, collisions, mainColor, darkerColor, phase);
highlightYDifferences(map, collisions, mainColor, darkerColor, darkerColor, darkerColorShadow);
map.finishDrawing();
}
private static void renderPhase(TrainMapRenderer map, List<Couple<Integer>> collisions, int mainColor,
int darkerColor, int phase) {
int outlineColor = 0xFF_000000;
int portalFrameColor = 0xFF_4C2D5B;
int portalColor = 0xFF_FF7FD6;
for (TrackGraph graph : CreateClient.RAILWAYS.trackNetworks.values()) {
for (TrackNodeLocation nodeLocation : graph.getNodes()) {
if (nodeLocation.dimension != map.trackingDim)
continue;
TrackNode node = graph.locateNode(nodeLocation);
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node);
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
TrackNode other = entry.getKey();
TrackNodeLocation otherLocation = other.getLocation();
TrackEdge edge = entry.getValue();
BezierConnection turn = edge.getTurn();
// Portal track
if (edge.isInterDimensional()) {
Vec3 vec = node.getLocation()
.getLocation();
int x = Mth.floor(vec.x);
int z = Mth.floor(vec.z);
if (phase == PHASE_CURVES)
continue;
if (phase == PHASE_BACKGROUND) {
map.setPixels(x - 3, z - 2, x + 3, z + 2, outlineColor);
map.setPixels(x - 2, z - 3, x + 2, z + 3, outlineColor);
continue;
}
int a = mapYtoAlpha(Mth.floor(vec.y()));
for (int xi = x - 2; xi <= x + 2; xi++) {
for (int zi = z - 2; zi <= z + 2; zi++) {
int alphaAt = map.alphaAt(xi, zi);
if (alphaAt > 0 && alphaAt != a)
collisions.add(Couple.create(xi, zi));
int c = (xi - x) * (xi - x) + (zi - z) * (zi - z) > 2 ? portalFrameColor : portalColor;
if (alphaAt <= a) {
map.setPixel(xi, zi, markY(c, vec.y()));
}
}
}
continue;
}
if (other.hashCode() > hashCode)
continue;
if (turn == null) {
if (phase == PHASE_CURVES)
continue;
float x1 = nodeLocation.getX();
float z1 = nodeLocation.getZ();
float x2 = otherLocation.getX();
float z2 = otherLocation.getZ();
double y1 = nodeLocation.getLocation()
.y();
double y2 = otherLocation.getLocation()
.y();
float xDiffSign = Math.signum(x2 - x1);
float zDiffSign = Math.signum(z2 - z1);
boolean diagonal = xDiffSign != 0 && zDiffSign != 0;
if (xDiffSign != 0) {
x2 -= xDiffSign * .25;
x1 += xDiffSign * .25;
}
if (zDiffSign != 0) {
z2 -= zDiffSign * .25;
z1 += zDiffSign * .25;
}
x1 /= 2;
x2 /= 2;
z1 /= 2;
z2 /= 2;
int y = Mth.floor(y1);
int a = mapYtoAlpha(y);
// Diagonal
if (diagonal) {
int z = Mth.floor(z1);
int x = Mth.floor(x1);
for (int s = 0; s <= Math.abs(x1 - x2); s++) {
if (phase == PHASE_BACKGROUND) {
map.setPixels(x - 1, z, x + 1, z + 1, outlineColor);
map.setPixels(x, z - 1, x, z + 2, outlineColor);
x += xDiffSign;
z += zDiffSign;
continue;
}
int alphaAt = map.alphaAt(x, z);
if (alphaAt > 0 && alphaAt != a)
collisions.add(Couple.create(x, z));
if (alphaAt <= a) {
map.setPixel(x, z, markY(mainColor, y));
}
if (map.alphaAt(x, z + 1) < a) {
map.setPixel(x, z + 1, markY(darkerColor, y));
}
x += xDiffSign;
z += zDiffSign;
}
continue;
}
// Straight
if (phase == PHASE_BACKGROUND) {
int x1i = Mth.floor(Math.min(x1, x2));
int z1i = Mth.floor(Math.min(z1, z2));
int x2i = Mth.floor(Math.max(x1, x2));
int z2i = Mth.floor(Math.max(z1, z2));
map.setPixels(x1i - 1, z1i, x2i + 1, z2i, outlineColor);
map.setPixels(x1i, z1i - 1, x2i, z2i + 1, outlineColor);
continue;
}
int z = Mth.floor(z1);
int x = Mth.floor(x1);
float diff = Math.max(Math.abs(x1 - x2), Math.abs(z1 - z2));
double yStep = (y2 - y1) / diff;
for (int s = 0; s <= diff; s++) {
int alphaAt = map.alphaAt(x, z);
if (alphaAt > 0 && alphaAt != a)
collisions.add(Couple.create(x, z));
if (alphaAt <= a) {
map.setPixel(x, z, markY(mainColor, y));
}
x += xDiffSign;
y += yStep;
z += zDiffSign;
}
continue;
}
if (phase == PHASE_STRAIGHTS)
continue;
BlockPos origin = turn.bePositions.getFirst();
Map<Pair<Integer, Integer>, Double> rasterise = turn.rasterise();
for (boolean antialias : Iterate.falseAndTrue) {
for (Entry<Pair<Integer, Integer>, Double> offset : rasterise.entrySet()) {
Pair<Integer, Integer> xz = offset.getKey();
int x = origin.getX() + xz.getFirst();
int y = Mth.floor(origin.getY() + offset.getValue() + 0.5);
int z = origin.getZ() + xz.getSecond();
if (phase == PHASE_BACKGROUND) {
map.setPixels(x - 1, z, x + 1, z, outlineColor);
map.setPixels(x, z - 1, x, z + 1, outlineColor);
continue;
}
int a = mapYtoAlpha(y);
if (!antialias) {
int alphaAt = map.alphaAt(x, z);
if (alphaAt > 0 && alphaAt != a)
collisions.add(Couple.create(x, z));
if (alphaAt > a)
continue;
map.setPixel(x, z, markY(mainColor, y));
continue;
}
boolean mainColorBelowLeft =
map.is(x + 1, z + 1, mainColor) && Math.abs(map.alphaAt(x + 1, z + 1) - a) <= 1;
boolean mainColorBelowRight =
map.is(x - 1, z + 1, mainColor) && Math.abs(map.alphaAt(x - 1, z + 1) - a) <= 1;
if (mainColorBelowLeft || mainColorBelowRight) {
int alphaAt = map.alphaAt(x, z + 1);
if (alphaAt > 0 && alphaAt != a)
collisions.add(Couple.create(x, z));
if (alphaAt >= a)
continue;
map.setPixel(x, z + 1, markY(darkerColor, y));
// Adjust background
if (map.isEmpty(x + 1, z + 1))
map.setPixel(x + 1, z + 1, outlineColor);
if (map.isEmpty(x - 1, z + 1))
map.setPixel(x - 1, z + 1, outlineColor);
if (map.isEmpty(x, z + 2))
map.setPixel(x, z + 2, outlineColor);
}
}
if (phase == PHASE_BACKGROUND)
break;
}
}
}
}
}
private static void highlightYDifferences(TrainMapRenderer map, List<Couple<Integer>> collisions, int mainColor,
int darkerColor, int mainColorShadow, int darkerColorShadow) {
for (Couple<Integer> couple : collisions) {
int x = couple.getFirst();
int z = couple.getSecond();
int a = map.alphaAt(x, z);
if (a == 0)
continue;
for (int xi = x - 2; xi <= x + 2; xi++) {
for (int zi = z - 2; zi <= z + 2; zi++) {
if (map.alphaAt(xi, zi) >= a)
continue;
if (map.is(xi, zi, mainColor))
map.setPixel(xi, zi, FastColor.ABGR32.color(a, mainColorShadow));
else if (map.is(xi, zi, darkerColor))
map.setPixel(xi, zi, FastColor.ABGR32.color(a, darkerColorShadow));
}
}
}
}
private static int mapYtoAlpha(double y) {
int minY = Minecraft.getInstance().level.getMinBuildHeight();
return Mth.clamp(32 + Mth.floor((y - minY) / 4.0), 0, 255);
}
private static int markY(int color, double y) {
return FastColor.ABGR32.color(mapYtoAlpha(y), color);
}
}

View file

@ -0,0 +1,259 @@
package com.simibubi.create.compat.trainmap;
import java.util.HashSet;
import java.util.Set;
import org.joml.Matrix4f;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.foundation.render.RenderTypes;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.infrastructure.config.CClient;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FastColor;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
public class TrainMapRenderer implements AutoCloseable {
public static final TrainMapRenderer INSTANCE = new TrainMapRenderer();
public static final int WIDTH = 128, HEIGHT = 128;
private Object2ObjectMap<Couple<Integer>, TrainMapInstance> maps = new Object2ObjectOpenHashMap<>();
public int trackingVersion;
public ResourceKey<Level> trackingDim;
public CClient.TrainMapTheme trackingTheme;
//
private TrainMapInstance previouslyAccessed;
public void startDrawing() {
previouslyAccessed = null;
maps.values()
.forEach(tmi -> {
tmi.getImage()
.fillRect(0, 0, WIDTH, HEIGHT, 0);
tmi.untouched = true;
});
}
public Object2ObjectMap<Couple<Integer>, TrainMapInstance> getMaps() {
return maps;
}
public void setPixel(int xCoord, int zCoord, int color) {
TrainMapInstance instance = getOrCreateAt(xCoord, zCoord);
xCoord = Mth.positiveModulo(xCoord, WIDTH);
zCoord = Mth.positiveModulo(zCoord, HEIGHT);
instance.getImage()
.setPixelRGBA(xCoord, zCoord, color);
}
public int getPixel(int xCoord, int zCoord) {
Couple<Integer> sectionKey = toSectionKey(xCoord, zCoord);
if (!maps.containsKey(sectionKey))
return 0;
TrainMapInstance instance = getOrCreateAt(xCoord, zCoord);
xCoord = Mth.positiveModulo(xCoord, WIDTH);
zCoord = Mth.positiveModulo(zCoord, HEIGHT);
return instance.getImage()
.getPixelRGBA(xCoord, zCoord);
}
public void setPixels(int xCoordFrom, int zCoordFrom, int xCoordTo, int zCoordTo, int color) {
for (int x = Math.min(xCoordFrom, xCoordTo); x <= Math.max(xCoordFrom, xCoordTo); x++)
for (int z = Math.min(zCoordFrom, zCoordTo); z <= Math.max(zCoordFrom, zCoordTo); z++)
setPixel(x, z, color);
}
public void blendPixel(int xCoord, int zCoord, int color, int alpha) {
TrainMapInstance instance = getOrCreateAt(xCoord, zCoord);
xCoord = Mth.positiveModulo(xCoord, WIDTH);
zCoord = Mth.positiveModulo(zCoord, HEIGHT);
instance.getImage()
.blendPixel(xCoord, zCoord, FastColor.ABGR32.color(alpha, color));
}
public void blendPixels(int xCoordFrom, int zCoordFrom, int xCoordTo, int zCoordTo, int color, int alpha) {
for (int x = Math.min(xCoordFrom, xCoordTo); x <= Math.max(xCoordFrom, xCoordTo); x++)
for (int z = Math.min(zCoordFrom, zCoordTo); z <= Math.max(zCoordFrom, zCoordTo); z++)
blendPixel(x, z, color, alpha);
}
public void finishDrawing() {
previouslyAccessed = null;
Set<Couple<Integer>> stale = new HashSet<>();
maps.forEach((key, tmi) -> {
if (!tmi.untouched)
return;
tmi.close();
stale.add(key);
});
stale.forEach(key -> {
TrainMapInstance tmi = maps.remove(key);
if (tmi != null)
tmi.close();
});
}
public boolean is(int x, int z, int color) {
return (getPixel(x, z) & 0xFFFFFF) == (color & 0xFFFFFF);
}
public boolean isEmpty(int x, int z) {
return getPixel(x, z) == 0;
}
public int alphaAt(int x, int z) {
int pixel = getPixel(x, z);
return ((pixel & 0xFFFFFF) != 0) ? ((pixel >>> 24) & 0xFF) : 0;
}
//
public void render(GuiGraphics graphics, int mouseX, int mouseY, float pt, boolean linearFiltering, Rect2i bounds) {
BufferSource bufferSource = graphics.bufferSource();
PoseStack pose = graphics.pose();
maps.forEach((key, tmi) -> {
if (tmi.canBeSkipped(bounds))
return;
int x = key.getFirst();
int y = key.getSecond();
pose.pushPose();
pose.translate(x * WIDTH, y * HEIGHT, 0);
tmi.draw(pose, bufferSource, linearFiltering);
pose.popPose();
});
}
public TrainMapInstance getOrCreateAt(int xCoord, int zCoord) {
Couple<Integer> sectionKey = toSectionKey(xCoord, zCoord);
if (previouslyAccessed != null && previouslyAccessed.sectionKey.equals(sectionKey))
return previouslyAccessed;
return maps.compute(sectionKey, (key, instance) -> instance == null ? new TrainMapInstance(key) : instance);
}
public Couple<Integer> toSectionKey(int xCoord, int zCoord) {
return Couple.create(Mth.floor(xCoord / (float) WIDTH), Mth.floor(zCoord / (float) HEIGHT));
}
public void resetData() {
for (TrainMapInstance instance : maps.values())
instance.close();
maps.clear();
}
public void close() {
this.resetData();
}
public class TrainMapInstance implements AutoCloseable {
private DynamicTexture texture;
private RenderType renderType;
private boolean requiresUpload;
private boolean linearFiltering;
private Rect2i bounds;
private boolean untouched;
private Couple<Integer> sectionKey;
public ResourceLocation location;
public TrainMapInstance(Couple<Integer> sectionKey) {
TextureManager textureManager = Minecraft.getInstance()
.getTextureManager();
this.sectionKey = sectionKey;
untouched = false;
requiresUpload = true;
texture = new DynamicTexture(128, 128, true);
linearFiltering = false;
location = textureManager
.register("create_trainmap/" + sectionKey.getFirst() + "_" + sectionKey.getSecond(), texture);
renderType = RenderTypes.TRAIN_MAP.apply(location, linearFiltering);
bounds = new Rect2i(sectionKey.getFirst() * WIDTH, sectionKey.getSecond() * HEIGHT, WIDTH, HEIGHT);
}
public boolean canBeSkipped(Rect2i bounds) {
return bounds.getX() + bounds.getWidth() < this.bounds.getX()
|| this.bounds.getX() + this.bounds.getWidth() < bounds.getX()
|| bounds.getY() + bounds.getHeight() < this.bounds.getY()
|| this.bounds.getY() + this.bounds.getHeight() < bounds.getY();
}
public NativeImage getImage() {
untouched = false;
requiresUpload = true;
return texture.getPixels();
}
public void draw(PoseStack pPoseStack, MultiBufferSource pBufferSource, boolean linearFiltering) {
if (texture.getPixels() == null)
return;
if (requiresUpload) {
texture.upload();
requiresUpload = false;
}
if (pPoseStack == null)
return;
if (linearFiltering != this.linearFiltering) {
this.linearFiltering = linearFiltering;
renderType = RenderTypes.TRAIN_MAP.apply(location, linearFiltering);
}
int pPackedLight = LightTexture.FULL_BRIGHT;
Matrix4f matrix4f = pPoseStack.last()
.pose();
VertexConsumer vertexconsumer = pBufferSource.getBuffer(renderType);
vertexconsumer.vertex(matrix4f, 0.0F, HEIGHT, 0)
.color(255, 255, 255, 255)
.uv(0.0F, 1.0F)
.uv2(pPackedLight)
.endVertex();
vertexconsumer.vertex(matrix4f, WIDTH, HEIGHT, 0)
.color(255, 255, 255, 255)
.uv(1.0F, 1.0F)
.uv2(pPackedLight)
.endVertex();
vertexconsumer.vertex(matrix4f, WIDTH, 0.0F, 0)
.color(255, 255, 255, 255)
.uv(1.0F, 0.0F)
.uv2(pPackedLight)
.endVertex();
vertexconsumer.vertex(matrix4f, 0.0F, 0.0F, 0)
.color(255, 255, 255, 255)
.uv(0.0F, 0.0F)
.uv2(pPackedLight)
.endVertex();
}
public void close() {
texture.close();
}
}
}

View file

@ -0,0 +1,353 @@
package com.simibubi.create.compat.trainmap;
import java.lang.ref.WeakReference;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.simibubi.create.AllPackets;
import com.simibubi.create.Create;
import com.simibubi.create.content.trains.entity.Carriage;
import com.simibubi.create.content.trains.entity.Carriage.DimensionalCarriageEntity;
import com.simibubi.create.content.trains.entity.Train;
import com.simibubi.create.content.trains.entity.TravellingPoint;
import com.simibubi.create.content.trains.graph.DimensionPalette;
import com.simibubi.create.content.trains.graph.EdgePointType;
import com.simibubi.create.content.trains.schedule.ScheduleRuntime;
import com.simibubi.create.content.trains.signal.SignalBlock.SignalType;
import com.simibubi.create.content.trains.signal.SignalBoundary;
import com.simibubi.create.content.trains.signal.SignalEdgeGroup;
import com.simibubi.create.content.trains.station.GlobalStation;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.event.TickEvent.ServerTickEvent;
import net.minecraftforge.network.PacketDistributor;
public class TrainMapSync {
public static final int lightPacketInterval = 5;
public static final int fullPacketInterval = 10;
public static int ticks;
public enum TrainState {
RUNNING, RUNNING_MANUALLY, DERAILED, SCHEDULE_INTERRUPTED, CONDUCTOR_MISSING, NAVIGATION_FAILED
}
public enum SignalState {
NOT_WAITING, WAITING_FOR_REDSTONE, BLOCK_SIGNAL, CHAIN_SIGNAL
}
public static class TrainMapSyncEntry {
// Clientside
public float[] prevPositions;
public List<ResourceKey<Level>> prevDims;
// Updated every 5 ticks
public float[] positions;
public List<ResourceKey<Level>> dimensions;
public TrainState state = TrainState.RUNNING;
public SignalState signalState = SignalState.NOT_WAITING;
public boolean fueled = false;
public boolean backwards = false;
public int targetStationDistance = 0;
// Updated every 10 ticks
public String ownerName = "";
public String targetStationName = "";
public UUID waitingForTrain = null;
public void gatherDimensions(DimensionPalette dimensionPalette) {
for (ResourceKey<Level> resourceKey : dimensions)
if (resourceKey != null)
dimensionPalette.encode(resourceKey);
}
public void send(FriendlyByteBuf buffer, DimensionPalette dimensionPalette, boolean light) {
buffer.writeVarInt(positions.length);
for (float f : positions)
buffer.writeFloat(f);
buffer.writeVarInt(dimensions.size());
for (ResourceKey<Level> resourceKey : dimensions)
buffer.writeVarInt(resourceKey == null ? -1 : dimensionPalette.encode(resourceKey));
buffer.writeVarInt(state.ordinal());
buffer.writeVarInt(signalState.ordinal());
buffer.writeBoolean(fueled);
buffer.writeBoolean(backwards);
buffer.writeVarInt(targetStationDistance);
if (light)
return;
buffer.writeUtf(ownerName);
buffer.writeUtf(targetStationName);
buffer.writeBoolean(waitingForTrain != null);
if (waitingForTrain != null)
buffer.writeUUID(waitingForTrain);
}
public void receive(FriendlyByteBuf buffer, DimensionPalette dimensionPalette, boolean light) {
positions = new float[buffer.readVarInt()];
for (int i = 0; i < positions.length; i++)
positions[i] = buffer.readFloat();
dimensions = new ArrayList<>();
int dimensionsSize = buffer.readVarInt();
for (int i = 0; i < dimensionsSize; i++) {
int index = buffer.readVarInt();
dimensions.add(index == -1 ? null : dimensionPalette.decode(index));
}
state = TrainState.values()[buffer.readVarInt()];
signalState = SignalState.values()[buffer.readVarInt()];
fueled = buffer.readBoolean();
backwards = buffer.readBoolean();
targetStationDistance = buffer.readVarInt();
if (light)
return;
ownerName = buffer.readUtf();
targetStationName = buffer.readUtf();
waitingForTrain = null;
if (buffer.readBoolean())
waitingForTrain = buffer.readUUID();
}
public void updateFrom(TrainMapSyncEntry other, boolean light) {
prevPositions = positions;
prevDims = dimensions;
positions = other.positions;
dimensions = other.dimensions;
state = other.state;
signalState = other.signalState;
fueled = other.fueled;
backwards = other.backwards;
targetStationDistance = other.targetStationDistance;
if (prevDims != null)
for (int i = 0; i < Math.min(prevDims.size(), dimensions.size()); i++)
if (prevDims.get(i) != dimensions.get(i))
for (int j = 0; j < 6; j++)
prevPositions[i * 6 + j] = positions[i * 6 + j];
if (light)
return;
ownerName = other.ownerName;
targetStationName = other.targetStationName;
waitingForTrain = other.waitingForTrain;
}
public Vec3 getPosition(int carriageIndex, boolean firstBogey, double time) {
int startIndex = carriageIndex * 6 + (firstBogey ? 0 : 3);
if (positions == null || positions.length <= startIndex + 2)
return Vec3.ZERO;
Vec3 position = new Vec3(positions[startIndex], positions[startIndex + 1], positions[startIndex + 2]);
if (prevPositions == null || prevPositions.length <= startIndex + 2)
return position;
Vec3 prevPosition =
new Vec3(prevPositions[startIndex], prevPositions[startIndex + 1], prevPositions[startIndex + 2]);
return prevPosition.lerp(position, time);
}
}
public static Cache<UUID, WeakReference<ServerPlayer>> requestingPlayers = CacheBuilder.newBuilder()
.expireAfterWrite(Duration.ofSeconds(1))
.build();
public static void requestReceived(ServerPlayer sender) {
boolean sendImmediately = requestingPlayers.getIfPresent(sender.getUUID()) == null;
requestingPlayers.put(sender.getUUID(), new WeakReference<>(sender));
if (sendImmediately)
send(sender.server, false);
}
public static void serverTick(ServerTickEvent event) {
ticks++;
if (ticks % fullPacketInterval == 0)
send(event.getServer(), false);
else if (ticks % lightPacketInterval == 0)
send(event.getServer(), true);
}
public static void send(MinecraftServer minecraftServer, boolean light) {
if (requestingPlayers.size() == 0)
return;
TrainMapSyncPacket packet = new TrainMapSyncPacket(light);
for (Train train : Create.RAILWAYS.trains.values())
packet.add(train.id, createEntry(minecraftServer, train));
for (WeakReference<ServerPlayer> weakReference : requestingPlayers.asMap()
.values()) {
ServerPlayer player = weakReference.get();
if (player == null)
continue;
AllPackets.getChannel()
.send(PacketDistributor.PLAYER.with(() -> player), packet);
}
}
private static TrainMapSyncEntry createEntry(MinecraftServer minecraftServer, Train train) {
TrainMapSyncEntry entry = new TrainMapSyncEntry();
boolean stopped = Math.abs(train.speed) < 0.05;
entry.positions = new float[train.carriages.size() * 6];
entry.dimensions = new ArrayList<>();
List<Carriage> carriages = train.carriages;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(i);
Vec3 leadingPos;
Vec3 trailingPos;
if (train.graph == null) {
// Train is derailed
Pair<ResourceKey<Level>, DimensionalCarriageEntity> dimCarriage =
carriage.anyAvailableDimensionalCarriage();
if (dimCarriage == null || carriage.presentInMultipleDimensions()) {
entry.dimensions.add(null);
continue;
}
leadingPos = dimCarriage.getSecond().rotationAnchors.getFirst();
trailingPos = dimCarriage.getSecond().rotationAnchors.getSecond();
if (leadingPos == null || trailingPos == null) {
entry.dimensions.add(null);
continue;
}
entry.dimensions.add(dimCarriage.getFirst());
} else {
// Train is on Track
TravellingPoint leading = carriage.getLeadingPoint();
TravellingPoint trailing = carriage.getTrailingPoint();
if (leading == null || trailing == null || leading.edge == null || trailing.edge == null) {
entry.dimensions.add(null);
continue;
}
ResourceKey<Level> leadingDim =
(leading.node1 == null || leading.edge == null || leading.edge.isInterDimensional()) ? null
: leading.node1.getLocation()
.getDimension();
ResourceKey<Level> trailingDim =
(trailing.node1 == null || trailing.edge == null || trailing.edge.isInterDimensional()) ? null
: trailing.node1.getLocation()
.getDimension();
ResourceKey<Level> carriageDim = (leadingDim == null || leadingDim != trailingDim) ? null : leadingDim;
entry.dimensions.add(carriageDim);
leadingPos = leading.getPosition(train.graph);
trailingPos = trailing.getPosition(train.graph);
}
entry.positions[i * 6] = (float) leadingPos.x();
entry.positions[i * 6 + 1] = (float) leadingPos.y();
entry.positions[i * 6 + 2] = (float) leadingPos.z();
entry.positions[i * 6 + 3] = (float) trailingPos.x();
entry.positions[i * 6 + 4] = (float) trailingPos.y();
entry.positions[i * 6 + 5] = (float) trailingPos.z();
}
entry.backwards = train.currentlyBackwards;
if (train.owner != null) {
ServerPlayer owner = minecraftServer.getPlayerList()
.getPlayer(train.owner);
if (owner != null)
entry.ownerName = owner.getName()
.getString();
}
if (train.derailed) {
entry.state = TrainState.DERAILED;
return entry;
}
ScheduleRuntime runtime = train.runtime;
if (runtime.getSchedule() != null && stopped) {
if (runtime.paused) {
entry.state = TrainState.SCHEDULE_INTERRUPTED;
return entry;
}
if (train.status.conductor) {
entry.state = TrainState.CONDUCTOR_MISSING;
return entry;
}
if (train.status.navigation) {
entry.state = TrainState.NAVIGATION_FAILED;
return entry;
}
}
if ((runtime.getSchedule() == null || runtime.paused) && train.speed != 0)
entry.state = TrainState.RUNNING_MANUALLY;
GlobalStation currentStation = train.getCurrentStation();
if (currentStation != null) {
entry.targetStationName = currentStation.name;
entry.targetStationDistance = 0;
} else if (train.navigation.destination != null && !runtime.paused) {
entry.targetStationName = train.navigation.destination.name;
entry.targetStationDistance = Math.max(0, Mth.floor(train.navigation.distanceToDestination));
}
if (stopped && train.navigation.waitingForSignal != null) {
UUID signalId = train.navigation.waitingForSignal.getFirst();
boolean side = train.navigation.waitingForSignal.getSecond();
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, signalId);
if (signal != null) {
boolean chainSignal = signal.types.get(side) == SignalType.CROSS_SIGNAL;
entry.signalState = chainSignal ? SignalState.CHAIN_SIGNAL : SignalState.BLOCK_SIGNAL;
if (signal.isForcedRed(side))
entry.signalState = SignalState.WAITING_FOR_REDSTONE;
else {
SignalEdgeGroup group = Create.RAILWAYS.signalEdgeGroups.get(signal.groups.get(side));
if (group != null) {
for (Train other : group.trains) {
if (other == train)
continue;
entry.waitingForTrain = other.id;
break;
}
}
}
}
}
if (train.fuelTicks > 0 && !stopped)
entry.fueled = true;
return entry;
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.compat.trainmap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.simibubi.create.AllPackets;
import com.simibubi.create.compat.trainmap.TrainMapSync.TrainMapSyncEntry;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.Pair;
public class TrainMapSyncClient {
public static Map<UUID, TrainMapSyncEntry> currentData = new HashMap<>();
public static double lastPacket;
private static int ticks;
public static void requestData() {
ticks++;
if (ticks % 5 == 0)
AllPackets.getChannel()
.sendToServer(new TrainMapSyncRequestPacket());
}
public static void stopRequesting() {
ticks = 0;
currentData.clear();
}
public static void receive(TrainMapSyncPacket packet) {
if (ticks == 0)
return;
lastPacket = AnimationTickHolder.getTicks();
lastPacket += AnimationTickHolder.getPartialTicks();
Set<UUID> staleEntries = new HashSet<>();
staleEntries.addAll(currentData.keySet());
for (Pair<UUID, TrainMapSyncEntry> pair : packet.entries) {
UUID id = pair.getFirst();
TrainMapSyncEntry entry = pair.getSecond();
staleEntries.remove(id);
currentData.computeIfAbsent(id, $ -> entry)
.updateFrom(entry, packet.light);
}
for (UUID uuid : staleEntries)
currentData.remove(uuid);
}
}

View file

@ -0,0 +1,65 @@
package com.simibubi.create.compat.trainmap;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.simibubi.create.compat.trainmap.TrainMapSync.TrainMapSyncEntry;
import com.simibubi.create.content.trains.graph.DimensionPalette;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent.Context;
public class TrainMapSyncPacket extends SimplePacketBase {
public List<Pair<UUID, TrainMapSyncEntry>> entries = new ArrayList<>();
public boolean light;
public TrainMapSyncPacket(boolean light) {
this.light = light;
}
public void add(UUID trainId, TrainMapSyncEntry data) {
entries.add(Pair.of(trainId, data));
}
public TrainMapSyncPacket(FriendlyByteBuf buffer) {
DimensionPalette dimensionPalette = DimensionPalette.receive(buffer);
light = buffer.readBoolean();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++) {
UUID id = buffer.readUUID();
TrainMapSyncEntry entry = new TrainMapSyncEntry();
entry.receive(buffer, dimensionPalette, light);
entries.add(Pair.of(id, entry));
}
}
@Override
public void write(FriendlyByteBuf buffer) {
DimensionPalette dimensionPalette = new DimensionPalette();
for (Pair<UUID, TrainMapSyncEntry> pair : entries)
pair.getSecond()
.gatherDimensions(dimensionPalette);
dimensionPalette.send(buffer);
buffer.writeBoolean(light);
buffer.writeVarInt(entries.size());
for (Pair<UUID, TrainMapSyncEntry> pair : entries) {
buffer.writeUUID(pair.getFirst());
pair.getSecond()
.send(buffer, dimensionPalette, light);
}
}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> TrainMapSyncClient.receive(this));
return true;
}
}

View file

@ -0,0 +1,23 @@
package com.simibubi.create.compat.trainmap;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent.Context;
public class TrainMapSyncRequestPacket extends SimplePacketBase {
public TrainMapSyncRequestPacket() {}
public TrainMapSyncRequestPacket(FriendlyByteBuf buffer) {}
@Override
public void write(FriendlyByteBuf buffer) {}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> TrainMapSync.requestReceived(context.getSender()));
return true;
}
}

View file

@ -545,9 +545,12 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
relativeMotion = reverseRotation(relativeMotion, 1);
context.relativeMotion = relativeMotion;
return !BlockPos.containing(previousPosition).equals(gridPosition)
|| (context.relativeMotion.length() > 0 || context.contraption instanceof CarriageContraption)
&& context.firstMovement;
boolean ignoreMotionForFirstMovement =
context.contraption instanceof CarriageContraption || actor instanceof PortableStorageInterfaceMovement;
return !BlockPos.containing(previousPosition)
.equals(gridPosition)
|| (context.relativeMotion.length() > 0 || ignoreMotionForFirstMovement) && context.firstMovement;
}
public void move(double x, double y, double z) {

View file

@ -1143,6 +1143,7 @@ public abstract class Contraption {
if (blockEntity instanceof IMultiBlockEntityContainer) {
if (tag.contains("LastKnownPos") || capturedMultiblocks.isEmpty()) {
tag.put("LastKnownPos", NbtUtils.writeBlockPos(BlockPos.ZERO.below(Integer.MAX_VALUE - 1)));
tag.remove("Controller");
}
}
@ -1199,6 +1200,9 @@ public abstract class Contraption {
// swap nbt data to the new controller position
StructureBlockInfo prevControllerInfo = blocks.get(controllerPos);
StructureBlockInfo newControllerInfo = blocks.get(otherPos);
if (prevControllerInfo == null || newControllerInfo == null)
return;
blocks.put(otherPos, new StructureBlockInfo(newControllerInfo.pos(), newControllerInfo.state(), prevControllerInfo.nbt()));
blocks.put(controllerPos, new StructureBlockInfo(prevControllerInfo.pos(), prevControllerInfo.state(), newControllerInfo.nbt()));
});
@ -1384,6 +1388,9 @@ public abstract class Contraption {
private void gatherBBsOffThread() {
getContraptionWorld();
if (simplifiedEntityColliderProvider != null) {
simplifiedEntityColliderProvider.cancel(false);
}
simplifiedEntityColliderProvider = CompletableFuture.supplyAsync(() -> {
VoxelShape combinedShape = Shapes.empty();
for (Entry<BlockPos, StructureBlockInfo> entry : blocks.entrySet()) {
@ -1400,7 +1407,6 @@ public abstract class Contraption {
})
.thenAccept(r -> {
simplifiedEntityColliders = Optional.of(r);
simplifiedEntityColliderProvider = null;
});
}

View file

@ -14,7 +14,6 @@ import org.apache.commons.lang3.mutable.MutableFloat;
import org.apache.commons.lang3.mutable.MutableObject;
import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.base.Predicates;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.AllPackets;
@ -161,8 +160,7 @@ public class ContraptionCollider {
List<AABB> bbs = new ArrayList<>();
List<VoxelShape> potentialHits =
getPotentiallyCollidedShapes(world, contraption, localBB.expandTowards(motionCopy));
potentialHits.forEach(shape -> shape.toAabbs()
.forEach(bbs::add));
potentialHits.forEach(shape -> bbs.addAll(shape.toAabbs()));
return bbs;
});
@ -669,19 +667,23 @@ public class ContraptionCollider {
BlockPos min = BlockPos.containing(blockScanBB.minX, blockScanBB.minY, blockScanBB.minZ);
BlockPos max = BlockPos.containing(blockScanBB.maxX, blockScanBB.maxY, blockScanBB.maxZ);
List<VoxelShape> potentialHits = BlockPos.betweenClosedStream(min, max)
.filter(contraption.getBlocks()::containsKey)
.filter(Predicates.not(contraption::isHiddenInPortal))
.map(p -> {
BlockState blockState = contraption.getBlocks()
.get(p).state();
BlockPos pos = contraption.getBlocks()
.get(p).pos();
VoxelShape collisionShape = blockState.getCollisionShape(world, p);
return collisionShape.move(pos.getX(), pos.getY(), pos.getZ());
})
.filter(Predicates.not(VoxelShape::isEmpty))
.toList();
List<VoxelShape> potentialHits = new ArrayList<>();
for (BlockPos p : BlockPos.betweenClosed(min, max)) {
if (contraption.blocks.containsKey(p) && !contraption.isHiddenInPortal(p)) {
StructureBlockInfo info = contraption.getBlocks().get(p);
BlockState blockState = info.state();
BlockPos pos = info.pos();
VoxelShape collisionShape = blockState.getCollisionShape(world, p)
.move(pos.getX(), pos.getY(), pos.getZ());
if (!collisionShape.isEmpty()) {
potentialHits.add(collisionShape);
}
}
}
return potentialHits;
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.contraptions;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.content.equipment.toolbox.ToolboxInventory;
import com.simibubi.create.content.kinetics.crafter.MechanicalCrafterBlockEntity;
import com.simibubi.create.content.logistics.crate.BottomlessItemHandler;
import com.simibubi.create.content.logistics.vault.ItemVaultBlockEntity;
@ -177,6 +178,8 @@ public class MountedStorage {
CompoundTag tag = handler.serializeNBT();
if (noFuel)
NBTHelper.putMarker(tag, "NoFuel");
if (handler instanceof ToolboxInventory)
NBTHelper.putMarker(tag, "Toolbox");
if (!(handler instanceof BottomlessItemHandler))
return tag;
@ -191,6 +194,9 @@ public class MountedStorage {
storage.handler = new ItemStackHandler();
if (nbt == null)
return storage;
if (nbt.contains("Toolbox"))
storage.handler = new ToolboxInventory(null);
storage.valid = true;
storage.noFuel = nbt.contains("NoFuel");

View file

@ -107,7 +107,7 @@ public class ContraptionControlsBlockEntity extends SmartBlockEntity {
public static void sendStatus(Player player, ItemStack filter, boolean enabled) {
MutableComponent state = Lang.translate("contraption.controls.actor_toggle." + (enabled ? "on" : "off"))
.color(DyeHelper.DYE_TABLE.get(enabled ? DyeColor.LIME : DyeColor.ORANGE)
.color(DyeHelper.getDyeColors(enabled ? DyeColor.LIME : DyeColor.ORANGE)
.getFirst())
.component();

View file

@ -83,7 +83,7 @@ public class ContraptionControlsRenderer extends SmartBlockEntityRenderer<Contra
: ctx.position.distanceToSqr(cameraEntity.getEyePosition()));
float flicker = r.nextFloat();
Couple<Integer> couple = DyeHelper.DYE_TABLE.get(efs.targetYEqualsSelection ? DyeColor.WHITE : DyeColor.ORANGE);
Couple<Integer> couple = DyeHelper.getDyeColors(efs.targetYEqualsSelection ? DyeColor.WHITE : DyeColor.ORANGE);
int brightColor = couple.getFirst();
int darkColor = couple.getSecond();
int flickeringBrightColor = Color.mixColors(brightColor, darkColor, flicker / 4);

View file

@ -38,7 +38,7 @@ public class BearingVisual<B extends KineticBlockEntity & IBearingBlockEntity> e
PartialModel top =
blockEntity.isWoodenTop() ? AllPartialModels.BEARING_TOP_WOODEN : AllPartialModels.BEARING_TOP;
topInstance = instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(top))
topInstance = instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(top))
.createInstance();
topInstance.position(getVisualPosition())

View file

@ -29,7 +29,7 @@ public class StickerVisual extends AbstractBlockEntityVisual<StickerBlockEntity>
public StickerVisual(VisualizationContext context, StickerBlockEntity blockEntity, float partialTick) {
super(context, blockEntity, partialTick);
head = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.STICKER_HEAD)).createInstance();
head = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.STICKER_HEAD)).createInstance();
fakeWorld = blockEntity.getLevel() != Minecraft.getInstance().level;
facing = blockState.getValue(StickerBlock.FACING);

View file

@ -34,7 +34,7 @@ public class GantryCarriageVisual extends ShaftVisual<GantryCarriageBlockEntity>
public GantryCarriageVisual(VisualizationContext context, GantryCarriageBlockEntity blockEntity, float partialTick) {
super(context, blockEntity, partialTick);
gantryCogs = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GANTRY_COGS))
gantryCogs = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GANTRY_COGS))
.createInstance();
facing = blockState.getValue(GantryCarriageBlock.FACING);

View file

@ -5,17 +5,16 @@ import java.util.function.Consumer;
import com.mojang.math.Axis;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
import com.simibubi.create.content.kinetics.base.ShaftVisual;
import com.simibubi.create.foundation.render.ConditionalInstance;
import com.simibubi.create.foundation.render.GroupInstance;
import com.simibubi.create.foundation.render.SelectInstance;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visual.DynamicVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.math.MoreMath;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.util.SmartRecycler;
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.bytes.ByteList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@ -29,9 +28,8 @@ import net.minecraft.world.level.LightLayer;
public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends ShaftVisual<T> implements SimpleDynamicVisual {
private final OrientedInstance coil;
private final SelectInstance<OrientedInstance> magnet;
private final GroupInstance<OrientedInstance> rope;
private final ConditionalInstance<OrientedInstance> halfRope;
private final TransformedInstance magnet;
private final SmartRecycler<Boolean, TransformedInstance> rope;
protected final Direction rotatingAbout;
protected final Axis rotationAxis;
@ -50,14 +48,12 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
.position(getVisualPosition());
coil.setChanged();
magnet = new SelectInstance<>(this::getMagnetModelIndex);
magnet.addModel(getMagnetModel())
.addModel(getHalfMagnetModel());
magnet = magnetInstancer().createInstance();
rope = new GroupInstance<>(getRopeModel());
halfRope = new ConditionalInstance<>(getHalfRopeModel()).withCondition(this::shouldRenderHalfRope);
rope = new SmartRecycler<>(b -> b ? getHalfRopeModel().createInstance() : getRopeModel().createInstance());
updateOffset(partialTick);
updateLight(partialTick);
}
@Override
@ -66,67 +62,68 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
lightCache.updateSections();
}
protected abstract Instancer<OrientedInstance> getRopeModel();
protected abstract Instancer<TransformedInstance> getRopeModel();
protected abstract Instancer<OrientedInstance> getMagnetModel();
protected abstract Instancer<TransformedInstance> getMagnetModel();
protected abstract Instancer<OrientedInstance> getHalfMagnetModel();
protected abstract Instancer<TransformedInstance> getHalfMagnetModel();
protected abstract Instancer<OrientedInstance> getCoilModel();
protected abstract Instancer<OrientedInstance> getHalfRopeModel();
protected abstract Instancer<TransformedInstance> getHalfRopeModel();
protected abstract float getOffset(float pt);
protected abstract boolean isRunning();
private Instancer<TransformedInstance> magnetInstancer() {
return offset > .25f ? getMagnetModel() : getHalfMagnetModel();
}
@Override
public void beginFrame(DynamicVisual.Context ctx) {
updateOffset(ctx.partialTick());
coil.rotation(rotationAxis.rotationDegrees(offset * 180))
.setChanged();
int neededRopeCount = getNeededRopeCount();
rope.resize(neededRopeCount);
magnet.setVisible(isRunning() || offset == 0);
magnet.update()
.get()
.ifPresent(data -> {
int i = Math.max(0, Mth.floor(offset));
int light = lightCache.getPackedLight(i);
data.position(getVisualPosition())
.translatePosition(0, -offset, 0)
.light(light)
.setChanged();
});
magnetInstancer().stealInstance(magnet);
halfRope.update()
.get()
.ifPresent(rope1 -> {
float f = offset % 1;
float halfRopeNudge = f > .75f ? f - 1 : f;
magnet.setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -offset, 0)
.light(lightCache.getPackedLight(Math.max(0, Mth.floor(offset))))
.setChanged();
int light = lightCache.getPackedLight(0);
rope1.position(getVisualPosition())
.translatePosition(0, -halfRopeNudge, 0)
.light(light)
.setChanged();
});
rope.resetCount();
if (shouldRenderHalfRope()) {
float f = offset % 1;
float halfRopeNudge = f > .75f ? f - 1 : f;
rope.get(true).setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -halfRopeNudge, 0)
.light(lightCache.getPackedLight(0))
.setChanged();
}
if (isRunning()) {
int size = rope.size();
for (int i = 0; i < size; i++) {
int light = lightCache.getPackedLight(size - 1 - i);
int neededRopeCount = getNeededRopeCount();
rope.get(i)
.position(getVisualPosition())
.translatePosition(0, -offset + i + 1, 0)
.light(light)
for (int i = 0; i < neededRopeCount; i++) {
rope.get(false)
.setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -offset + i + 1, 0)
.light(lightCache.getPackedLight(neededRopeCount - 1 - i))
.setChanged();
}
} else {
rope.clear();
}
rope.discardExtra();
}
@Override
@ -151,21 +148,11 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
return offset > .75f && (f < .25f || f > .75f);
}
private int getMagnetModelIndex() {
if (isRunning() || offset == 0) {
return offset > .25f ? 0 : 1;
} else {
return -1;
}
}
@Override
public void collectCrumblingInstances(Consumer<Instance> consumer) {
super.collectCrumblingInstances(consumer);
consumer.accept(coil);
magnet.forEach(consumer);
rope.forEach(consumer);
halfRope.forEach(consumer);
consumer.accept(magnet);
}
@Override
@ -173,8 +160,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
super._delete();
coil.delete();
magnet.delete();
rope.clear();
halfRope.delete();
rope.delete();
}
private class LightCache {
@ -186,6 +172,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
public void setSize(int size) {
if (size != data.size()) {
data.size(size);
update();
int sectionCount = MoreMath.ceilingDiv(size + 15 - pos.getY() + pos.getY() / 4 * 4, SectionPos.SECTION_SIZE);
if (sectionCount != this.sectionCount) {
@ -215,7 +202,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
for (int i = 0; i < data.size(); i++) {
int blockLight = level.getBrightness(LightLayer.BLOCK, mutablePos);
int skyLight = level.getBrightness(LightLayer.SKY, mutablePos);
int light = ((skyLight << 4) & 0xF) | (blockLight & 0xF);
int light = ((skyLight & 0xF) << 4) | (blockLight & 0xF);
data.set(i, (byte) light);
mutablePos.move(Direction.DOWN);
}

View file

@ -7,6 +7,7 @@ import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models;
public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity> {
@ -15,28 +16,28 @@ public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity
}
@Override
protected Instancer<OrientedInstance> getRopeModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE));
protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE));
}
@Override
protected Instancer<OrientedInstance> getMagnetModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_MAGNET));
protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_MAGNET));
}
@Override
protected Instancer<OrientedInstance> getHalfMagnetModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET));
protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET));
}
@Override
protected Instancer<OrientedInstance> getCoilModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_COIL, rotatingAbout));
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_COIL, rotatingAbout));
}
@Override
protected Instancer<OrientedInstance> getHalfRopeModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF));
protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF));
}
@Override

View file

@ -9,6 +9,7 @@ import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models;
public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
@ -17,28 +18,28 @@ public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
}
@Override
protected Instancer<OrientedInstance> getRopeModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState()));
protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState()));
}
@Override
protected Instancer<OrientedInstance> getMagnetModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState()));
protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState()));
}
@Override
protected Instancer<OrientedInstance> getHalfMagnetModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET));
protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET));
}
@Override
protected Instancer<OrientedInstance> getCoilModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_COIL, rotatingAbout));
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_COIL, rotatingAbout));
}
@Override
protected Instancer<OrientedInstance> getHalfRopeModel() {
return instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF));
protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF));
}
@Override

View file

@ -93,6 +93,8 @@ public class ContraptionVisual<E extends AbstractContraptionEntity> extends Abst
.instancer(InstanceTypes.TRANSFORMED, model)
.createInstance();
structure.setChanged();
for (BlockEntity be : contraption.getRenderedBEs()) {
setupVisualizer(be, partialTick);
}

View file

@ -164,6 +164,11 @@ public class CopycatPanelBlock extends WaterloggedCopycatBlock {
return false;
}
@Override
public boolean skipRendering(BlockState state, BlockState adjacentState, Direction direction) {
return state.equals(adjacentState) && direction.getAxis().isHorizontal();
}
@Override
public boolean supportsExternalFaceHiding(BlockState state) {
return true;

View file

@ -184,8 +184,10 @@ public class BacktankBlock extends HorizontalKineticBlock implements IBE<Backtan
Optional<BacktankBlockEntity> blockEntityOptional = getBlockEntityOptional(blockGetter, pos);
CompoundTag forgeCapsTag = blockEntityOptional.map(BacktankBlockEntity::getForgeCapsTag)
.map(CompoundTag::copy)
.orElse(null);
CompoundTag vanillaTag = blockEntityOptional.map(BacktankBlockEntity::getVanillaTag)
.map(CompoundTag::copy)
.orElse(new CompoundTag());
int air = blockEntityOptional.map(BacktankBlockEntity::getAirLevel)
.orElse(0);

View file

@ -120,10 +120,10 @@ public class BacktankBlockEntity extends KineticBlockEntity implements Nameable
compound.putInt("Air", airLevel);
compound.putInt("Timer", airLevelTimer);
compound.putInt("CapacityEnchantment", capacityEnchantLevel);
if (this.customName != null)
compound.putString("CustomName", Component.Serializer.toJson(this.customName));
compound.put("VanillaTag", vanillaTag);
if (forgeCapsTag != null)
compound.put("ForgeCapsTag", forgeCapsTag);
@ -136,10 +136,10 @@ public class BacktankBlockEntity extends KineticBlockEntity implements Nameable
airLevel = compound.getInt("Air");
airLevelTimer = compound.getInt("Timer");
capacityEnchantLevel = compound.getInt("CapacityEnchantment");
if (compound.contains("CustomName", 8))
this.customName = Component.Serializer.fromJson(compound.getString("CustomName"));
vanillaTag = compound.getCompound("VanillaTag");
forgeCapsTag = compound.contains("ForgeCapsTag") ? compound.getCompound("ForgeCapsTag") : null;
@ -181,16 +181,18 @@ public class BacktankBlockEntity extends KineticBlockEntity implements Nameable
public void setCapacityEnchantLevel(int capacityEnchantLevel) {
this.capacityEnchantLevel = capacityEnchantLevel;
}
public void setTags(CompoundTag vanillaTag, @Nullable CompoundTag forgeCapsTag) {
this.vanillaTag = vanillaTag;
this.forgeCapsTag = forgeCapsTag;
this.vanillaTag = vanillaTag.copy();
this.forgeCapsTag = forgeCapsTag == null ? null : forgeCapsTag.copy();
// Prevent nesting of the ctrl+pick block added tag
vanillaTag.remove("BlockEntityTag");
}
public CompoundTag getVanillaTag() {
return vanillaTag;
}
public CompoundTag getForgeCapsTag() {
return forgeCapsTag;
}

View file

@ -68,6 +68,8 @@ public class ClipboardBlockEntity extends SmartBlockEntity {
protected void read(CompoundTag tag, boolean clientPacket) {
super.read(tag, clientPacket);
dataContainer = ItemStack.of(tag.getCompound("Item"));
if (!AllBlocks.CLIPBOARD.isIn(dataContainer))
dataContainer = AllBlocks.CLIPBOARD.asStack();
if (clientPacket)
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> readClientSide(tag));

View file

@ -52,19 +52,13 @@ public class SymmetryHandler {
if (event.getLevel()
.isClientSide())
return;
if (!(event.getEntity() instanceof Player))
if (!(event.getEntity() instanceof Player player))
return;
Player player = (Player) event.getEntity();
Inventory inv = player.getInventory();
for (int i = 0; i < Inventory.getSelectionSize(); i++) {
if (!inv.getItem(i)
.isEmpty()
&& inv.getItem(i)
.getItem() == AllItems.WAND_OF_SYMMETRY.get()) {
for (int i = 0; i < Inventory.getSelectionSize(); i++)
if (AllItems.WAND_OF_SYMMETRY.isIn(inv.getItem(i)))
SymmetryWandItem.apply(player.level(), inv.getItem(i), player, event.getPos(), event.getPlacedBlock());
}
}
}
@SubscribeEvent(priority = EventPriority.LOWEST)
@ -75,12 +69,9 @@ public class SymmetryHandler {
Player player = event.getPlayer();
Inventory inv = player.getInventory();
for (int i = 0; i < Inventory.getSelectionSize(); i++) {
if (!inv.getItem(i)
.isEmpty() && AllItems.WAND_OF_SYMMETRY.isIn(inv.getItem(i))) {
for (int i = 0; i < Inventory.getSelectionSize(); i++)
if (AllItems.WAND_OF_SYMMETRY.isIn(inv.getItem(i)))
SymmetryWandItem.remove(player.level(), inv.getItem(i), player, event.getPos());
}
}
}
@OnlyIn(Dist.CLIENT)

View file

@ -9,6 +9,7 @@ import java.util.function.Consumer;
import javax.annotation.Nonnull;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllPackets;
import com.simibubi.create.content.contraptions.mounted.CartAssemblerBlock;
import com.simibubi.create.content.equipment.symmetryWand.mirror.CrossPlaneMirror;
@ -27,6 +28,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
@ -328,5 +330,13 @@ public class SymmetryWandItem extends Item {
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(SimpleCustomRenderer.create(this, new SymmetryWandItemRenderer()));
}
public static boolean presentInHotbar(Player player) {
Inventory inv = player.getInventory();
for (int i = 0; i < Inventory.getSelectionSize(); i++)
if (AllItems.WAND_OF_SYMMETRY.isIn(inv.getItem(i)))
return true;
return false;
}
}

View file

@ -28,10 +28,10 @@ public class ToolBoxVisual extends AbstractBlockEntityVisual<ToolboxBlockEntity>
facing = blockState.getValue(ToolboxBlock.FACING)
.getOpposite();
Instancer<TransformedInstance> drawerModel = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.TOOLBOX_DRAWER));
Instancer<TransformedInstance> drawerModel = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.TOOLBOX_DRAWER));
drawers = new TransformedInstance[]{drawerModel.createInstance(), drawerModel.createInstance()};
lid = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.TOOLBOX_LIDS.get(blockEntity.getColor())))
lid = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.TOOLBOX_LIDS.get(blockEntity.getColor())))
.createInstance();
}

View file

@ -8,6 +8,7 @@ import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.BlockHelper;
import net.minecraft.core.BlockPos;
@ -22,7 +23,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
@ -38,6 +38,7 @@ import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.network.NetworkHooks;
@ -171,7 +172,7 @@ public class ToolboxBlock extends HorizontalDirectionalBlock implements SimpleWa
public Class<ToolboxBlockEntity> getBlockEntityClass() {
return ToolboxBlockEntity.class;
}
@Override
public BlockEntityType<? extends ToolboxBlockEntity> getBlockEntityType() {
return AllBlockEntityTypes.TOOLBOX.get();
@ -181,9 +182,14 @@ public class ToolboxBlock extends HorizontalDirectionalBlock implements SimpleWa
return color;
}
public static Ingredient getMainBox() {
return Ingredient.of(AllBlocks.TOOLBOXES.get(DyeColor.BROWN)
.get());
@Override
public boolean hasAnalogOutputSignal(BlockState pState) {
return true;
}
@Override
public int getAnalogOutputSignal(BlockState pState, Level pLevel, BlockPos pPos) {
return ItemHelper.calcRedstoneFromBlockEntity(this, pLevel, pPos);
}
}

View file

@ -83,7 +83,7 @@ public class ToolboxInventory extends ItemStackHandler {
}
}
settling = false;
blockEntity.sendData();
notifyUpdate();
}
@Override
@ -109,7 +109,7 @@ public class ToolboxInventory extends ItemStackHandler {
if (!stack.isEmpty() && filters.get(compartment)
.isEmpty()) {
filters.set(compartment, ItemHandlerHelper.copyStackWithSize(stack, 1));
blockEntity.sendData();
notifyUpdate();
}
}
@ -121,7 +121,7 @@ public class ToolboxInventory extends ItemStackHandler {
if (!stack.isEmpty() && filters.get(compartment)
.isEmpty()) {
filters.set(compartment, ItemHandlerHelper.copyStackWithSize(stack, 1));
blockEntity.sendData();
notifyUpdate();
}
}
return insertItem;
@ -136,10 +136,9 @@ public class ToolboxInventory extends ItemStackHandler {
@Override
protected void onContentsChanged(int slot) {
if (!settling && !blockEntity.getLevel().isClientSide)
if (!settling && (blockEntity == null || !blockEntity.getLevel().isClientSide))
settle(slot / STACKS_PER_COMPARTMENT);
blockEntity.sendData();
blockEntity.setChanged();
notifyUpdate();
super.onContentsChanged(slot);
}
@ -208,4 +207,9 @@ public class ToolboxInventory extends ItemStackHandler {
return ItemHandlerHelper.canItemStacksStack(stack1, stack2);
}
private void notifyUpdate() {
if (blockEntity != null)
blockEntity.notifyUpdate();
}
}

View file

@ -192,7 +192,7 @@ public class OpenEndedPipe extends FlowSource {
if (!FluidHelper.hasBlockState(fluid.getFluid()))
return true;
if (!fluidState.isEmpty() && fluidState.getType() != fluid.getFluid()) {
if (!fluidState.isEmpty() && FluidHelper.convertToStill(fluidState.getType()) != fluid.getFluid()) {
FluidReactions.handlePipeSpillCollision(world, outputPos, fluid.getFluid(), fluidState);
return false;
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.fluids.drain;
import com.simibubi.create.content.fluids.transfer.GenericItemEmptying;
import com.simibubi.create.content.kinetics.belt.transport.TransportedItemStack;
import com.simibubi.create.foundation.item.ItemHelper;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@ -38,7 +39,8 @@ public class ItemDrainItemHandler implements IItemHandler {
if (stack.getCount() > 1 && GenericItemEmptying.canItemBeEmptied(blockEntity.getLevel(), stack)) {
returned = ItemHandlerHelper.copyStackWithSize(stack, stack.getCount() - 1);
stack = ItemHandlerHelper.copyStackWithSize(stack, 1);
}
} else
returned = ItemHelper.limitCountToMaxStackSize(stack, simulate);
if (!simulate) {
TransportedItemStack heldItem = new TransportedItemStack(stack);

View file

@ -41,7 +41,7 @@ public class FluidValveVisual extends ShaftVisual<FluidValveBlockEntity> impleme
pointerRotationOffset = twist ? 90 : 0;
settled = false;
pointer = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.FLUID_VALVE_POINTER)).createInstance();
pointer = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.FLUID_VALVE_POINTER)).createInstance();
transformPointer(partialTick);
}

View file

@ -106,7 +106,7 @@ public class FluidTankBlock extends Block implements IWrenchable, IBE<FluidTankB
@Override
public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) {
FluidTankBlockEntity tankAt = ConnectivityHandler.partAt(getBlockEntityType(), world, pos);
if (tankAt == null)
if (tankAt == null || !tankAt.hasLevel())
return 0;
FluidTankBlockEntity controllerBE = tankAt.getControllerBE();
if (controllerBE == null || !controllerBE.window)

View file

@ -197,7 +197,7 @@ public class FluidTankBlockEntity extends SmartBlockEntity implements IHaveGoggl
@SuppressWarnings("unchecked")
@Override
public FluidTankBlockEntity getControllerBE() {
if (isController())
if (isController() || !hasLevel())
return this;
BlockEntity blockEntity = level.getBlockEntity(controller);
if (blockEntity instanceof FluidTankBlockEntity)

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.fluids.tank;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.api.connectivity.ConnectivityHandler;
import com.simibubi.create.content.equipment.symmetryWand.SymmetryWandItem;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -75,6 +76,8 @@ public class FluidTankItem extends BlockItem {
if (!FluidTankBlock.isTank(placedOnState))
return;
if (SymmetryWandItem.presentInHotbar(player))
return;
boolean creative = getBlock().equals(AllBlocks.CREATIVE_FLUID_TANK.get());
FluidTankBlockEntity tankAt = ConnectivityHandler.partAt(
creative ? AllBlockEntityTypes.CREATIVE_FLUID_TANK.get() : AllBlockEntityTypes.FLUID_TANK.get(), world, placedOnPos

View file

@ -110,7 +110,7 @@ public abstract class BlockBreakingKineticBlockEntity extends KineticBlockEntity
float breakSpeed = getBreakSpeed();
destroyProgress += Mth.clamp((int) (breakSpeed / blockHardness), 1, 10 - destroyProgress);
level.playSound(null, worldPosition, stateToBreak.getSoundType()
.getHitSound(), SoundSource.NEUTRAL, .25f, 1);
.getHitSound(), SoundSource.BLOCKS, .25f, 1);
if (destroyProgress >= 10) {
onBlockBroken(stateToBreak);
@ -142,7 +142,7 @@ public abstract class BlockBreakingKineticBlockEntity extends KineticBlockEntity
return;
if (level.restoringBlockSnapshots)
return;
ItemEntity itementity = new ItemEntity(level, vec.x, vec.y, vec.z, stack);
itementity.setDefaultPickUpDelay();
itementity.setDeltaMovement(Vec3.ZERO);

View file

@ -73,9 +73,8 @@ public abstract class GeneratingKineticBlockEntity extends KineticBlockEntity {
float speed = getTheoreticalSpeed();
if (speed != getGeneratedSpeed() && speed != 0)
stressBase *= getGeneratedSpeed() / speed;
speed = Math.abs(speed);
float stressTotal = stressBase * speed;
float stressTotal = Math.abs(stressBase * speed);
Lang.number(stressTotal)
.translate("generic.unit.stress")

View file

@ -15,7 +15,7 @@ public class SingleRotatingVisual<T extends KineticBlockEntity> extends KineticB
public SingleRotatingVisual(VisualizationContext context, T blockEntity, float partialTick) {
super(context, blockEntity, partialTick);
rotatingModel = instancerProvider.instancer(AllInstanceTypes.ROTATING, model())
rotatingModel = instancerProvider().instancer(AllInstanceTypes.ROTATING, model())
.createInstance();
setup(rotatingModel);
}

View file

@ -218,6 +218,8 @@ public class BeltBlock extends HorizontalKineticBlock
.copy(), false);
if (remainder.isEmpty())
itemEntity.discard();
else if (remainder.getCount() != itemEntity.getItem().getCount())
itemEntity.setItem(remainder);
});
return;
}

View file

@ -239,7 +239,7 @@ public class BeltRenderer extends SafeBlockEntityRenderer<BeltBlockEntity> {
be.getBlockPos().getZ())
.add(offsetVec);
if (this.shouldCullItem(itemPos)) {
if (this.shouldCullItem(itemPos, be.getLevel())) {
continue;
}

View file

@ -65,7 +65,7 @@ public class BeltVisual extends KineticBlockEntityVisual<BeltBlockEntity> {
PartialModel beltPartial = BeltRenderer.getBeltPartial(diagonal, start, end, bottom);
SpriteShiftEntry spriteShift = BeltRenderer.getSpriteShiftEntry(color, diagonal, bottom);
Instancer<BeltInstance> beltModel = instancerProvider.instancer(AllInstanceTypes.BELT, Models.partial(beltPartial));
Instancer<BeltInstance> beltModel = instancerProvider().instancer(AllInstanceTypes.BELT, Models.partial(beltPartial));
keys.add(setup(beltModel.createInstance(), bottom, spriteShift));
@ -138,7 +138,7 @@ public class BeltVisual extends KineticBlockEntityVisual<BeltBlockEntity> {
msr.uncenter();
});
return instancerProvider.instancer(AllInstanceTypes.ROTATING, model);
return instancerProvider().instancer(AllInstanceTypes.ROTATING, model);
}
private Direction getOrientation() {

View file

@ -1,5 +1,7 @@
package com.simibubi.create.content.kinetics.belt.transport;
import com.simibubi.create.foundation.item.ItemHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
@ -29,6 +31,7 @@ public class ItemHandlerBeltSegment implements IItemHandler {
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
if (this.beltInventory.canInsertAt(offset)) {
ItemStack remainder = ItemHelper.limitCountToMaxStackSize(stack, simulate);
if (!simulate) {
TransportedItemStack newStack = new TransportedItemStack(stack);
newStack.insertedAt = offset;
@ -38,7 +41,7 @@ public class ItemHandlerBeltSegment implements IItemHandler {
this.beltInventory.belt.setChanged();
this.beltInventory.belt.sendData();
}
return ItemStack.EMPTY;
return remainder;
}
return stack;
}

View file

@ -27,13 +27,13 @@ public class HandCrankVisual extends KineticBlockEntityVisual<HandCrankBlockEnti
facing = blockState.getValue(BlockStateProperties.FACING);
Model model = blockEntity.getRenderedHandleInstance();
crank = instancerProvider.instancer(InstanceTypes.TRANSFORMED, model)
crank = instancerProvider().instancer(InstanceTypes.TRANSFORMED, model)
.createInstance();
rotateCrank(partialTick);
if (blockEntity.shouldRenderShaft()) {
rotatingModel = instancerProvider.instancer(AllInstanceTypes.ROTATING, VirtualRenderHelper.blockModel(blockState))
rotatingModel = instancerProvider().instancer(AllInstanceTypes.ROTATING, VirtualRenderHelper.blockModel(blockState))
.createInstance();
setup(rotatingModel);
}

View file

@ -171,6 +171,8 @@ public class DeployerBlockEntity extends KineticBlockEntity {
}
if (level.isClientSide)
return;
if (player == null)
return;
ItemStack stack = player.getMainHandItem();
if (state == State.WAITING) {
@ -549,7 +551,10 @@ public class DeployerBlockEntity extends KineticBlockEntity {
ItemStack heldItemMainhand = player.getMainHandItem();
if (heldItemMainhand.getItem() instanceof SandPaperItem) {
sandpaperInv.setItem(0, stack);
return checkRecipe(AllRecipeTypes.SANDPAPER_POLISHING, sandpaperInv, level).orElse(null);
Optional<? extends Recipe<? extends Container>> polishingRecipe = checkRecipe(AllRecipeTypes.SANDPAPER_POLISHING, sandpaperInv, level);
if (polishingRecipe.isPresent()){
return polishingRecipe.get();
}
}
recipeInv.setItem(0, stack);

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.kinetics.deployer;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.item.ItemHelper;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
@ -52,9 +53,10 @@ public class DeployerItemHandler implements IItemHandlerModifiable {
ItemStack held = getHeld();
if (held.isEmpty()) {
ItemStack remainder = ItemHelper.limitCountToMaxStackSize(stack, simulate);
if (!simulate)
set(stack);
return ItemStack.EMPTY;
return remainder;
}
if (!ItemHandlerHelper.canItemStacksStack(held, stack))

View file

@ -52,11 +52,11 @@ public class DeployerVisual extends ShaftVisual<DeployerBlockEntity> implements
xRot = facing == Direction.UP ? 270 : facing == Direction.DOWN ? 90 : 0;
zRot = rotatePole ? 90 : 0;
pole = instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.DEPLOYER_POLE)).createInstance();
pole = instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.DEPLOYER_POLE)).createInstance();
currentHand = this.blockEntity.getHandPose();
hand = instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(currentHand)).createInstance();
hand = instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(currentHand)).createInstance();
progress = getProgress(partialTick);
updateRotation(pole, hand, yRot, xRot, zRot);
@ -69,7 +69,7 @@ public class DeployerVisual extends ShaftVisual<DeployerBlockEntity> implements
if (currentHand != handPose) {
currentHand = handPose;
instancerProvider.instancer(InstanceTypes.ORIENTED, Models.partial(currentHand))
instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(currentHand))
.stealInstance(hand);
}
}

View file

@ -29,8 +29,8 @@ public class FanVisual extends KineticBlockEntityVisual<EncasedFanBlockEntity> {
direction = blockState.getValue(FACING);
opposite = direction.getOpposite();
shaft = instancerProvider.instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.SHAFT_HALF, opposite)).createInstance();
fan = instancerProvider.instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.ENCASED_FAN_INNER, opposite))
shaft = instancerProvider().instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.SHAFT_HALF, opposite)).createInstance();
fan = instancerProvider().instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.ENCASED_FAN_INNER, opposite))
.createInstance();
setup(shaft);

View file

@ -27,9 +27,9 @@ public class FlywheelVisual extends KineticBlockEntityVisual<FlywheelBlockEntity
public FlywheelVisual(VisualizationContext context, FlywheelBlockEntity blockEntity, float partialTick) {
super(context, blockEntity, partialTick);
shaft = setup(instancerProvider.instancer(AllInstanceTypes.ROTATING, VirtualRenderHelper.blockModel(shaft()))
shaft = setup(instancerProvider().instancer(AllInstanceTypes.ROTATING, VirtualRenderHelper.blockModel(shaft()))
.createInstance());
wheel = instancerProvider.instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(blockState))
wheel = instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(blockState))
.createInstance();
animate(blockEntity.angle);

View file

@ -34,7 +34,7 @@ public abstract class GaugeVisual extends ShaftVisual<GaugeBlockEntity> implemen
GaugeBlock gaugeBlock = (GaugeBlock) blockState.getBlock();
Instancer<TransformedInstance> dialModel = instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_DIAL));
Instancer<TransformedInstance> dialModel = instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_DIAL));
Instancer<TransformedInstance> headModel = getHeadModel();
var msr = TransformStack.of(ms);
@ -157,7 +157,7 @@ public abstract class GaugeVisual extends ShaftVisual<GaugeBlockEntity> implemen
@Override
protected Instancer<TransformedInstance> getHeadModel() {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_HEAD_SPEED));
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_HEAD_SPEED));
}
}
@ -168,7 +168,7 @@ public abstract class GaugeVisual extends ShaftVisual<GaugeBlockEntity> implemen
@Override
protected Instancer<TransformedInstance> getHeadModel() {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_HEAD_STRESS));
return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.GAUGE_HEAD_STRESS));
}
}
}

View file

@ -41,7 +41,7 @@ public class GearboxVisual extends KineticBlockEntityVisual<GearboxBlockEntity>
if (boxAxis == axis)
continue;
RotatingInstance key = instancerProvider.instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.SHAFT_HALF, direction))
RotatingInstance key = instancerProvider().instancer(AllInstanceTypes.ROTATING, Models.partial(AllPartialModels.SHAFT_HALF, direction))
.createInstance();
key.setRotationAxis(axis)

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