Merge remote-tracking branch 'origin/mc1.18/0.5.1' into mc1.19/0.5.1

This commit is contained in:
simibubi 2023-05-12 22:59:18 +02:00
commit 500019465a
219 changed files with 6797 additions and 1078 deletions

23
.github/workflows/gametest.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: gametest
on: [ pull_request, push, workflow_dispatch ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@v3
- name: setup Java
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
cache: gradle
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: run gametests
run: ./gradlew prepareRunGameTestServer runGameTestServer --no-daemon

1
.gitignore vendored
View file

@ -43,3 +43,4 @@ local.properties
.buildpath .buildpath
.DS_Store .DS_Store
/libs/

View file

@ -90,6 +90,18 @@ minecraft {
} }
} }
} }
gameTestServer {
workingDirectory project.file('run/gametest')
arg '-mixin.config=create.mixins.json'
property 'forge.logging.console.level', 'info'
mods {
create {
source sourceSets.main
}
}
setForceExit false
}
} }
} }
@ -135,6 +147,17 @@ repositories {
includeGroup "maven.modrinth" includeGroup "maven.modrinth"
} }
} }
flatDir {
dirs 'libs'
}
maven {
// Location of maven for CC: Tweaked
name = "squiddev"
url = "https://squiddev.cc/maven/"
content {
includeGroup "org.squiddev"
}
}
} }
dependencies { dependencies {
@ -162,6 +185,11 @@ dependencies {
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}:api") compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}:api")
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}") runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}")
if (cc_tweaked_enable.toBoolean()) {
compileOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}:api")
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}")
}
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903") // implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
// implementation fg.deobf("com.ferreusveritas.dynamictrees:DynamicTrees-1.16.5:0.10.0-Beta25") // implementation fg.deobf("com.ferreusveritas.dynamictrees:DynamicTrees-1.16.5:0.10.0-Beta25")
// runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69") // runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69")
@ -169,6 +197,7 @@ dependencies {
// runtimeOnly fg.deobf("slimeknights.mantle:Mantle:1.16.5-1.6.115") // runtimeOnly fg.deobf("slimeknights.mantle:Mantle:1.16.5-1.6.115")
// runtimeOnly fg.deobf("slimeknights.tconstruct:TConstruct:1.16.5-3.1.1.252") // runtimeOnly fg.deobf("slimeknights.tconstruct:TConstruct:1.16.5-3.1.1.252")
// runtimeOnly fg.deobf("maven.modrinth:rubidium:0.5.3") // runtimeOnly fg.deobf("maven.modrinth:rubidium:0.5.3")
// implementation fg.deobf("com.railwayteam.railways:railways-1.18.2-1.1.1:all") { transitive = false }
// https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497 // https://discord.com/channels/313125603924639766/725850371834118214/910619168821354497
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings // Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings
@ -178,6 +207,12 @@ dependencies {
} }
} }
sourceSets.main.java {
if (!cc_tweaked_enable.toBoolean()) {
exclude 'com/simibubi/create/compat/computercraft/implementation/**'
}
}
sourceSets.main.resources { sourceSets.main.resources {
srcDir 'src/generated/resources' srcDir 'src/generated/resources'
exclude '.cache/' exclude '.cache/'

View file

@ -27,6 +27,10 @@ jei_version = 11.2.0.254
curios_minecraft_version = 1.19.2 curios_minecraft_version = 1.19.2
curios_version = 5.1.1.0 curios_version = 5.1.1.0
cc_tweaked_enable = true
cc_tweaked_minecraft_version = 1.18.2
cc_tweaked_version = 1.100.10
# curseforge information # curseforge information
projectId = 328085 projectId = 328085
curse_type = beta curse_type = beta

View file

@ -1192,6 +1192,8 @@
"create.schematicAndQuill.convert": "Save and Upload Immediately", "create.schematicAndQuill.convert": "Save and Upload Immediately",
"create.schematicAndQuill.fallbackName": "My Schematic", "create.schematicAndQuill.fallbackName": "My Schematic",
"create.schematicAndQuill.saved": "Saved as %1$s", "create.schematicAndQuill.saved": "Saved as %1$s",
"create.schematicAndQuill.failed": "Failed to save schematic, check logs for details",
"create.schematicAndQuill.instant_failed": "Schematic instant-upload failed, check logs for details",
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead", "create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
"create.schematic.error": "Schematic failed to Load - Check Game Logs", "create.schematic.error": "Schematic failed to Load - Check Game Logs",
@ -1811,6 +1813,7 @@
"create.display_source.redstone_power.progress_bar": "Progress Bar", "create.display_source.redstone_power.progress_bar": "Progress Bar",
"create.display_source.boiler.not_enough_space": "Not enough space ", "create.display_source.boiler.not_enough_space": "Not enough space ",
"create.display_source.boiler.for_boiler_status": "for Boiler Status", "create.display_source.boiler.for_boiler_status": "for Boiler Status",
"create.display_source.computer_display_source": "From Computer",
"create.display_target.line": "Line %1$s", "create.display_target.line": "Line %1$s",
"create.display_target.page": "Page %1$s", "create.display_target.page": "Page %1$s",
@ -1833,6 +1836,8 @@
"create.super_glue.not_enough": "Not enough glue in inventory", "create.super_glue.not_enough": "Not enough glue in inventory",
"create.super_glue.success": "Applying Glue...", "create.super_glue.success": "Applying Glue...",
"create.gui.attached_computer.controlled": "This device is being controlled by a computer",
"create.gui.attached_computer.hint": "To use device manually, disconnect all computers and modems",
"create.gui.config.overlay1": "Hi :)", "create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay", "create.gui.config.overlay2": "This is a sample overlay",
"create.gui.config.overlay3": "Click or drag with your mouse", "create.gui.config.overlay3": "Click or drag with your mouse",
@ -1856,6 +1861,12 @@
"enchantment.create.capacity.desc": "Increases Backtank air capacity.", "enchantment.create.capacity.desc": "Increases Backtank air capacity.",
"enchantment.create.potato_recovery.desc": "Potato Cannon projectiles have a chance to be reused.", "enchantment.create.potato_recovery.desc": "Potato Cannon projectiles have a chance to be reused.",
"create.bogey.style.updated_style": "Updated style",
"create.bogey.style.updated_style_and_size": "Updated style and size",
"create.bogey.style.no_other_sizes": "No other sizes",
"create.bogey.style.invalid": "Unnamed style",
"create.bogey.style.standard": "Standard",
"_": "->------------------------] Subtitles [------------------------<-", "_": "->------------------------] Subtitles [------------------------<-",
@ -2814,7 +2825,7 @@
"create.ponder.mechanical_roller_fill.text_5": "As opposed to 'clear & pave', neither of these modes will cause the rollers to break existing blocks", "create.ponder.mechanical_roller_fill.text_5": "As opposed to 'clear & pave', neither of these modes will cause the rollers to break existing blocks",
"create.ponder.mechanical_roller_pave.header": "Clearing and Paving with the Roller", "create.ponder.mechanical_roller_pave.header": "Clearing and Paving with the Roller",
"create.ponder.mechanical_roller_pave.text_1": "Mechanical rollers help to clean up long tracks or paths conveniently", "create.ponder.mechanical_roller_pave.text_1": "Mechanical rollers help to clean up terrain around tracks or paths",
"create.ponder.mechanical_roller_pave.text_2": "In its default mode, without a material set, it will simply clear blocks like a Drill", "create.ponder.mechanical_roller_pave.text_2": "In its default mode, without a material set, it will simply clear blocks like a Drill",
"create.ponder.mechanical_roller_pave.text_3": "While disassembled, a suitable paving material can be specified", "create.ponder.mechanical_roller_pave.text_3": "While disassembled, a suitable paving material can be specified",
"create.ponder.mechanical_roller_pave.text_4": "Materials can be supplied via chests or barrels attached to the structure", "create.ponder.mechanical_roller_pave.text_4": "Materials can be supplied via chests or barrels attached to the structure",

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"create:track"
]
}

View file

@ -18,10 +18,8 @@
"create:andesite_belt_funnel", "create:andesite_belt_funnel",
"create:brass_funnel", "create:brass_funnel",
"create:brass_belt_funnel", "create:brass_belt_funnel",
"create:creative_crate",
"create:redstone_link", "create:redstone_link",
"create:analog_lever", "create:analog_lever",
"create:placard",
"create:pulse_repeater", "create:pulse_repeater",
"create:pulse_extender", "create:pulse_extender",
"create:clipboard", "create:clipboard",

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"create:track"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_diving_boots",
"create:netherite_diving_boots"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_backtank",
"create:netherite_backtank"
]
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"create:copper_diving_helmet",
"create:netherite_diving_helmet"
]
}

View file

@ -195,6 +195,7 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlockEntity;
import com.simibubi.create.content.logistics.item.LecternControllerBlockEntity; import com.simibubi.create.content.logistics.item.LecternControllerBlockEntity;
import com.simibubi.create.content.logistics.item.LecternControllerRenderer; import com.simibubi.create.content.logistics.item.LecternControllerRenderer;
import com.simibubi.create.content.logistics.trains.BogeyBlockEntityRenderer; import com.simibubi.create.content.logistics.trains.BogeyBlockEntityRenderer;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlockEntity; import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlockEntity;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayRenderer; import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayRenderer;
import com.simibubi.create.content.logistics.trains.management.edgePoint.observer.TrackObserverBlockEntity; import com.simibubi.create.content.logistics.trains.management.edgePoint.observer.TrackObserverBlockEntity;
@ -831,8 +832,8 @@ public class AllBlockEntityTypes {
public static final BlockEntityEntry<TrackBlockEntity> TRACK = REGISTRATE public static final BlockEntityEntry<TrackBlockEntity> TRACK = REGISTRATE
.blockEntity("track", TrackBlockEntity::new) .blockEntity("track", TrackBlockEntity::new)
.instance(() -> TrackInstance::new) .instance(() -> TrackInstance::new)
.validBlocksDeferred(TrackMaterial::allBlocks)
.renderer(() -> TrackRenderer::new) .renderer(() -> TrackRenderer::new)
.validBlocks(AllBlocks.TRACK)
.register(); .register();
public static final BlockEntityEntry<FakeTrackBlockEntity> FAKE_TRACK = REGISTRATE public static final BlockEntityEntry<FakeTrackBlockEntity> FAKE_TRACK = REGISTRATE
@ -859,8 +860,8 @@ public class AllBlockEntityTypes {
AllBlocks.BRASS_DOOR, AllBlocks.COPPER_DOOR) AllBlocks.BRASS_DOOR, AllBlocks.COPPER_DOOR)
.register(); .register();
public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT = REGISTRATE public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT =
.blockEntity("copycat", CopycatBlockEntity::new) REGISTRATE.blockEntity("copycat", CopycatBlockEntity::new)
.validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP) .validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP)
.register(); .register();

View file

@ -230,6 +230,8 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultCTBehaviour; import com.simibubi.create.content.logistics.block.vault.ItemVaultCTBehaviour;
import com.simibubi.create.content.logistics.block.vault.ItemVaultItem; import com.simibubi.create.content.logistics.block.vault.ItemVaultItem;
import com.simibubi.create.content.logistics.item.LecternControllerBlock; import com.simibubi.create.content.logistics.item.LecternControllerBlock;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlock; import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
@ -718,7 +720,7 @@ public class AllBlocks {
.onRegister(movementBehaviour(new BlazeBurnerMovementBehaviour())) .onRegister(movementBehaviour(new BlazeBurnerMovementBehaviour()))
.onRegister(interactionBehaviour(new BlazeBurnerInteractionBehaviour())) .onRegister(interactionBehaviour(new BlazeBurnerInteractionBehaviour()))
.item(BlazeBurnerBlockItem::withBlaze) .item(BlazeBurnerBlockItem::withBlaze)
.model(AssetLookup.<BlazeBurnerBlockItem>customBlockItemModel("blaze_burner", "block_with_blaze")) .model(AssetLookup.customBlockItemModel("blaze_burner", "block_with_blaze"))
.build() .build()
.register(); .register();
@ -938,7 +940,7 @@ public class AllBlocks {
.onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status")) .onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status"))
.addLayer(() -> RenderType::cutoutMipped) .addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new) .item(FluidTankItem::new)
.model(AssetLookup.<FluidTankItem>customBlockItemModel("_", "block_single_window")) .model(AssetLookup.customBlockItemModel("_", "block_single_window"))
.build() .build()
.register(); .register();
@ -1541,7 +1543,7 @@ public class AllBlocks {
.transform(customItemModel()) .transform(customItemModel())
.register(); .register();
public static final BlockEntry<TrackBlock> TRACK = REGISTRATE.block("track", TrackBlock::new) public static final BlockEntry<TrackBlock> TRACK = REGISTRATE.block("track", TrackMaterial.ANDESITE::createBlock)
.initialProperties(Material.STONE) .initialProperties(Material.STONE)
.properties(p -> p.color(MaterialColor.METAL) .properties(p -> p.color(MaterialColor.METAL)
.strength(0.8F) .strength(0.8F)
@ -1552,6 +1554,8 @@ public class AllBlocks {
.onRegister(CreateRegistrate.blockModel(() -> TrackModel::new)) .onRegister(CreateRegistrate.blockModel(() -> TrackModel::new))
.blockstate(new TrackBlockStateGenerator()::generate) .blockstate(new TrackBlockStateGenerator()::generate)
.tag(AllBlockTags.RELOCATION_NOT_SUPPORTED.tag) .tag(AllBlockTags.RELOCATION_NOT_SUPPORTED.tag)
.tag(AllBlockTags.TRACKS.tag)
.tag(AllBlockTags.GIRDABLE_TRACKS.tag)
.lang("Train Track") .lang("Train Track")
.item(TrackBlockItem::new) .item(TrackBlockItem::new)
.model((c, p) -> p.generated(c, Create.asResource("item/" + c.getName()))) .model((c, p) -> p.generated(c, Create.asResource("item/" + c.getName())))
@ -1620,13 +1624,13 @@ public class AllBlocks {
.register(); .register();
public static final BlockEntry<StandardBogeyBlock> SMALL_BOGEY = public static final BlockEntry<StandardBogeyBlock> SMALL_BOGEY =
REGISTRATE.block("small_bogey", p -> new StandardBogeyBlock(p, false)) REGISTRATE.block("small_bogey", p -> new StandardBogeyBlock(p, BogeySizes.SMALL))
.properties(p -> p.color(MaterialColor.PODZOL)) .properties(p -> p.color(MaterialColor.PODZOL))
.transform(BuilderTransformers.bogey()) .transform(BuilderTransformers.bogey())
.register(); .register();
public static final BlockEntry<StandardBogeyBlock> LARGE_BOGEY = public static final BlockEntry<StandardBogeyBlock> LARGE_BOGEY =
REGISTRATE.block("large_bogey", p -> new StandardBogeyBlock(p, true)) REGISTRATE.block("large_bogey", p -> new StandardBogeyBlock(p, BogeySizes.LARGE))
.properties(p -> p.color(MaterialColor.PODZOL)) .properties(p -> p.color(MaterialColor.PODZOL))
.transform(BuilderTransformers.bogey()) .transform(BuilderTransformers.bogey())
.register(); .register();
@ -1765,7 +1769,6 @@ public class AllBlocks {
REGISTRATE.block("creative_crate", CreativeCrateBlock::new) REGISTRATE.block("creative_crate", CreativeCrateBlock::new)
.transform(BuilderTransformers.crate("creative")) .transform(BuilderTransformers.crate("creative"))
.properties(p -> p.color(MaterialColor.COLOR_PURPLE)) .properties(p -> p.color(MaterialColor.COLOR_PURPLE))
.tag(AllBlockTags.SAFE_NBT.tag)
.register(); .register();
public static final BlockEntry<DisplayLinkBlock> DISPLAY_LINK = public static final BlockEntry<DisplayLinkBlock> DISPLAY_LINK =
@ -1861,7 +1864,6 @@ public class AllBlocks {
public static final BlockEntry<PlacardBlock> PLACARD = REGISTRATE.block("placard", PlacardBlock::new) public static final BlockEntry<PlacardBlock> PLACARD = REGISTRATE.block("placard", PlacardBlock::new)
.initialProperties(SharedProperties::copperMetal) .initialProperties(SharedProperties::copperMetal)
.transform(pickaxeOnly()) .transform(pickaxeOnly())
.tag(AllBlockTags.SAFE_NBT.tag)
.blockstate((c, p) -> p.horizontalFaceBlock(c.get(), AssetLookup.standardModel(c, p))) .blockstate((c, p) -> p.horizontalFaceBlock(c.get(), AssetLookup.standardModel(c, p)))
.simpleItem() .simpleItem()
.register(); .register();

View file

@ -0,0 +1,122 @@
package com.simibubi.create;
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.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer.CommonRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.CommonStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.LargeStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.StandardBogeyRenderer.SmallStandardBogeyRenderer;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
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;
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();
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));
}
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, BogeyStyle.SizeData> 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<? extends BogeyRenderer> renderer,
BlockEntry<? extends AbstractBogeyBlock<?>> blockEntry) {
this.size(size, renderer, blockEntry.getId());
return this;
}
public BogeyStyleBuilder size(BogeySizes.BogeySize size, Supplier<? extends BogeyRenderer> renderer,
ResourceLocation location) {
this.sizes.put(size, new BogeyStyle.SizeData(location, renderer, renderer.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<? extends CommonRenderer> commonRenderer) {
this.commonRenderer = Optional.of(commonRenderer);
return this;
}
public BogeyStyle build() {
BogeyStyle entry =
new BogeyStyle(name, cycleGroup, displayName, soundType, contactParticle, smokeParticle, defaultData, sizes, commonRenderer);
BOGEY_STYLES.put(name, entry);
CYCLE_GROUPS.computeIfAbsent(cycleGroup, l -> new HashMap<>()).put(name, entry);
return entry;
}
}
}

View file

@ -228,14 +228,16 @@ public class AllItems {
// wrapped by COPPER_BACKTANK for block placement uses. // wrapped by COPPER_BACKTANK for block placement uses.
// must be registered as of 1.18.2 // must be registered as of 1.18.2
public static final ItemEntry<BacktankBlockItem> COPPER_BACKTANK_PLACEABLE = REGISTRATE public static final ItemEntry<BacktankBlockItem> COPPER_BACKTANK_PLACEABLE = REGISTRATE
.item("copper_backtank_placeable", p -> new BacktankBlockItem(AllBlocks.COPPER_BACKTANK.get(), AllItems.COPPER_BACKTANK::get, p)) .item("copper_backtank_placeable",
p -> new BacktankBlockItem(AllBlocks.COPPER_BACKTANK.get(), AllItems.COPPER_BACKTANK::get, p))
.model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier"))) .model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier")))
.register(); .register();
// wrapped by NETHERITE_BACKTANK for block placement uses. // wrapped by NETHERITE_BACKTANK for block placement uses.
// must be registered as of 1.18.2 // must be registered as of 1.18.2
public static final ItemEntry<BacktankBlockItem> NETHERITE_BACKTANK_PLACEABLE = REGISTRATE public static final ItemEntry<BacktankBlockItem> NETHERITE_BACKTANK_PLACEABLE = REGISTRATE
.item("netherite_backtank_placeable", p -> new BacktankBlockItem(AllBlocks.NETHERITE_BACKTANK.get(), AllItems.NETHERITE_BACKTANK::get, p)) .item("netherite_backtank_placeable",
p -> new BacktankBlockItem(AllBlocks.NETHERITE_BACKTANK.get(), AllItems.NETHERITE_BACKTANK::get, p))
.model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier"))) .model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier")))
.register(); .register();
@ -248,6 +250,7 @@ public class AllItems {
COPPER_BACKTANK_PLACEABLE)) COPPER_BACKTANK_PLACEABLE))
.model(AssetLookup.customGenericItemModel("_", "item")) .model(AssetLookup.customGenericItemModel("_", "item"))
.tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag) .tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag)
.tag(forgeItemTag("armors/chestplates"))
.register(), .register(),
NETHERITE_BACKTANK = REGISTRATE NETHERITE_BACKTANK = REGISTRATE
@ -257,6 +260,7 @@ public class AllItems {
.model(AssetLookup.customGenericItemModel("_", "item")) .model(AssetLookup.customGenericItemModel("_", "item"))
.properties(p -> p.fireResistant()) .properties(p -> p.fireResistant())
.tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag) .tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag)
.tag(forgeItemTag("armors/chestplates"))
.register(); .register();
public static final ItemEntry<? extends DivingHelmetItem> public static final ItemEntry<? extends DivingHelmetItem>
@ -265,12 +269,14 @@ public class AllItems {
REGISTRATE REGISTRATE
.item("copper_diving_helmet", .item("copper_diving_helmet",
p -> new DivingHelmetItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving"))) p -> new DivingHelmetItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving")))
.tag(forgeItemTag("armors/helmets"))
.register(), .register(),
NETHERITE_DIVING_HELMET = REGISTRATE NETHERITE_DIVING_HELMET = REGISTRATE
.item("netherite_diving_helmet", .item("netherite_diving_helmet",
p -> new DivingHelmetItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) p -> new DivingHelmetItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving")))
.properties(p -> p.fireResistant()) .properties(p -> p.fireResistant())
.tag(forgeItemTag("armors/helmets"))
.register(); .register();
public static final ItemEntry<? extends DivingBootsItem> public static final ItemEntry<? extends DivingBootsItem>
@ -279,12 +285,14 @@ public class AllItems {
REGISTRATE REGISTRATE
.item("copper_diving_boots", .item("copper_diving_boots",
p -> new DivingBootsItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving"))) p -> new DivingBootsItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving")))
.tag(forgeItemTag("armors/boots"))
.register(), .register(),
NETHERITE_DIVING_BOOTS = REGISTRATE NETHERITE_DIVING_BOOTS = REGISTRATE
.item("netherite_diving_boots", .item("netherite_diving_boots",
p -> new DivingBootsItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) p -> new DivingBootsItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving")))
.properties(p -> p.fireResistant()) .properties(p -> p.fireResistant())
.tag(forgeItemTag("armors/boots"))
.register(); .register();
public static final ItemEntry<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new) public static final ItemEntry<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new)

View file

@ -14,6 +14,7 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ItemTags; import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@ -80,6 +81,8 @@ public class AllTags {
SAFE_NBT, SAFE_NBT,
SEATS, SEATS,
TOOLBOXES, TOOLBOXES,
TRACKS,
GIRDABLE_TRACKS,
TREE_ATTACHMENTS, TREE_ATTACHMENTS,
VALVE_HANDLES, VALVE_HANDLES,
WINDMILL_SAILS, WINDMILL_SAILS,
@ -131,6 +134,10 @@ public class AllTags {
.is(tag); .is(tag);
} }
public boolean matches(ItemStack stack) {
return stack != null && stack.getItem() instanceof BlockItem blockItem && matches(blockItem.getBlock());
}
public boolean matches(BlockState state) { public boolean matches(BlockState state) {
return state.is(tag); return state.is(tag);
} }

View file

@ -2,6 +2,8 @@ package com.simibubi.create;
import java.util.Random; import java.util.Random;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -9,6 +11,7 @@ import com.google.gson.GsonBuilder;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour; import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
import com.simibubi.create.compat.Mods; import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.compat.curios.Curios; import com.simibubi.create.compat.curios.Curios;
import com.simibubi.create.content.contraptions.TorquePropagator; import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.fluids.tank.BoilerHeaters; import com.simibubi.create.content.contraptions.fluids.tank.BoilerHeaters;
@ -125,6 +128,8 @@ public class Create {
AllFeatures.register(modEventBus); AllFeatures.register(modEventBus);
AllPlacementModifiers.register(modEventBus); AllPlacementModifiers.register(modEventBus);
BuiltinRegistration.register(modEventBus); BuiltinRegistration.register(modEventBus);
BogeySizes.init();
AllBogeyStyles.register();
AllConfigs.register(modLoadingContext); AllConfigs.register(modLoadingContext);
@ -134,6 +139,7 @@ public class Create {
ContraptionMovementSetting.registerDefaults(); ContraptionMovementSetting.registerDefaults();
AllArmInteractionPointTypes.register(); AllArmInteractionPointTypes.register();
BlockSpoutingBehaviour.registerDefaults(); BlockSpoutingBehaviour.registerDefaults();
ComputerCraftProxy.register();
ForgeMod.enableMilkFluid(); ForgeMod.enableMilkFluid();
CopperRegistries.inject(); CopperRegistries.inject();

View file

@ -0,0 +1,22 @@
package com.simibubi.create.api.event;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import net.minecraftforge.eventbus.api.Event;
public class TrackGraphMergeEvent extends Event{
private TrackGraph mergedInto, mergedFrom;
public TrackGraphMergeEvent(TrackGraph from, TrackGraph into) {
mergedInto = into;
mergedFrom = from;
}
public TrackGraph getGraphMergedInto() {
return mergedInto;
}
public TrackGraph getGraphMergedFrom() {
return mergedFrom;
}
}

View file

@ -5,7 +5,10 @@ import java.util.function.Supplier;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModList;
import net.minecraftforge.registries.ForgeRegistries;
/** /**
* For compatibility with and without another mod present, we have to define load conditions of the specific code * For compatibility with and without another mod present, we have to define load conditions of the specific code
@ -14,6 +17,8 @@ public enum Mods {
DYNAMICTREES, DYNAMICTREES,
TCONSTRUCT, TCONSTRUCT,
CURIOS, CURIOS,
COMPUTERCRAFT,
STORAGEDRAWERS, STORAGEDRAWERS,
XLPACKETS; XLPACKETS;
@ -51,4 +56,8 @@ public enum Mods {
toExecute.get().run(); toExecute.get().run();
} }
} }
public Block getBlock(String id) {
return ForgeRegistries.BLOCKS.getValue(new ResourceLocation(asId(), id));
}
} }

View file

@ -0,0 +1,57 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BehaviourType;
import net.minecraft.nbt.CompoundTag;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class AbstractComputerBehaviour extends BlockEntityBehaviour {
public static final BehaviourType<AbstractComputerBehaviour> TYPE = new BehaviourType<>();
boolean hasAttachedComputer;
public AbstractComputerBehaviour(SmartBlockEntity te) {
super(te);
this.hasAttachedComputer = false;
}
@Override
public void read(CompoundTag nbt, boolean clientPacket) {
hasAttachedComputer = nbt.getBoolean("HasAttachedComputer");
super.read(nbt, clientPacket);
}
@Override
public void write(CompoundTag nbt, boolean clientPacket) {
nbt.putBoolean("HasAttachedComputer", hasAttachedComputer);
super.write(nbt, clientPacket);
}
public <T> boolean isPeripheralCap(Capability<T> cap) {
return false;
}
public <T> LazyOptional<T> getPeripheralCapability() {
return LazyOptional.empty();
}
public void removePeripheral() {}
public void setHasAttachedComputer(boolean hasAttachedComputer) {
this.hasAttachedComputer = hasAttachedComputer;
}
public boolean hasAttachedComputer() {
return hasAttachedComputer;
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
}

View file

@ -0,0 +1,37 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.SyncedBlockEntity;
import com.simibubi.create.foundation.networking.BlockEntityDataPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
public class AttachedComputerPacket extends BlockEntityDataPacket<SyncedBlockEntity> {
private final boolean hasAttachedComputer;
public AttachedComputerPacket(BlockPos tilePos, boolean hasAttachedComputer) {
super(tilePos);
this.hasAttachedComputer = hasAttachedComputer;
}
public AttachedComputerPacket(FriendlyByteBuf buffer) {
super(buffer);
this.hasAttachedComputer = buffer.readBoolean();
}
@Override
protected void writeData(FriendlyByteBuf buffer) {
buffer.writeBoolean(hasAttachedComputer);
}
@Override
protected void handlePacket(SyncedBlockEntity tile) {
if (tile instanceof SmartBlockEntity smartTile) {
smartTile.getBehaviour(AbstractComputerBehaviour.TYPE)
.setHasAttachedComputer(hasAttachedComputer);
}
}
}

View file

@ -0,0 +1,30 @@
package com.simibubi.create.compat.computercraft;
import java.util.function.Function;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
public class ComputerCraftProxy {
public static void register() {
fallbackFactory = FallbackComputerBehaviour::new;
Mods.COMPUTERCRAFT.executeIfInstalled(() -> ComputerCraftProxy::registerWithDependency);
}
private static void registerWithDependency() {
/* Comment if computercraft.implementation is not in the source set */
computerFactory = ComputerBehaviour::new;
}
private static Function<SmartBlockEntity, ? extends AbstractComputerBehaviour> fallbackFactory;
private static Function<SmartBlockEntity, ? extends AbstractComputerBehaviour> computerFactory;
public static AbstractComputerBehaviour behaviour(SmartBlockEntity sbe) {
if (computerFactory == null)
return fallbackFactory.apply(sbe);
return computerFactory.apply(sbe);
}
}

View file

@ -0,0 +1,96 @@
package com.simibubi.create.compat.computercraft;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.element.GuiGameElement;
import com.simibubi.create.foundation.gui.widget.AbstractSimiWidget;
import com.simibubi.create.foundation.gui.widget.ElementWidget;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
public class ComputerScreen extends AbstractSimiScreen {
private final AllGuiTextures background = AllGuiTextures.COMPUTER;
private final Supplier<Component> displayTitle;
private final RenderWindowFunction additional;
private final Screen previousScreen;
private final Supplier<Boolean> hasAttachedComputer;
private AbstractSimiWidget computerWidget;
private IconButton confirmButton;
public ComputerScreen(Component title, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
this(title, () -> title, additional, previousScreen, hasAttachedComputer);
}
public ComputerScreen(Component title, Supplier<Component> displayTitle, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
super(title);
this.displayTitle = displayTitle;
this.additional = additional;
this.previousScreen = previousScreen;
this.hasAttachedComputer = hasAttachedComputer;
}
@Override
public void tick() {
if (!hasAttachedComputer.get())
minecraft.setScreen(previousScreen);
super.tick();
}
@Override
protected void init() {
setWindowSize(background.width, background.height);
super.init();
int x = guiLeft;
int y = guiTop;
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
computerWidget = new ElementWidget(x + 33, y + 38)
.showingElement(GuiGameElement.of(Mods.COMPUTERCRAFT.getBlock("computer_advanced")));
computerWidget.getToolTip().add(Lang.translate("gui.attached_computer.hint").component());
addRenderableWidget(computerWidget);
});
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(this::onClose);
addRenderableWidget(confirmButton);
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
int y = guiTop;
background.render(ms, x, y, this);
font.draw(ms, displayTitle.get(), x + background.width / 2.0F - font.width(displayTitle.get()) / 2.0F, y + 4, 0x442000);
font.drawWordWrap(Lang.translate("gui.attached_computer.controlled").component(), x + 55, y + 32, 111, 0x7A7A7A);
if (additional != null)
additional.render(ms, mouseX, mouseY, partialTicks, x, y, background);
}
@FunctionalInterface
public interface RenderWindowFunction {
void render(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop, AllGuiTextures background);
}
}

View file

@ -0,0 +1,16 @@
package com.simibubi.create.compat.computercraft;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
public class FallbackComputerBehaviour extends AbstractComputerBehaviour {
public FallbackComputerBehaviour(SmartBlockEntity te) {
super(te);
}
@Override
public boolean hasAttachedComputer() {
return false;
}
}

View file

@ -0,0 +1,74 @@
package com.simibubi.create.compat.computercraft.implementation;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.implementation.peripherals.DisplayLinkPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SequencedGearshiftPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedControllerPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedGaugePeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.StationPeripheral;
import com.simibubi.create.compat.computercraft.implementation.peripherals.StressGaugePeripheral;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlockEntity;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftBlockEntity;
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeBlockEntity;
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeBlockEntity;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlockEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.CapabilityToken;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullSupplier;
public class ComputerBehaviour extends AbstractComputerBehaviour {
protected static final Capability<IPeripheral> PERIPHERAL_CAPABILITY =
CapabilityManager.get(new CapabilityToken<>() {
});
LazyOptional<IPeripheral> peripheral;
NonNullSupplier<IPeripheral> peripheralSupplier;
public ComputerBehaviour(SmartBlockEntity te) {
super(te);
this.peripheralSupplier = getPeripheralFor(te);
}
public static NonNullSupplier<IPeripheral> getPeripheralFor(SmartBlockEntity te) {
if (te instanceof SpeedControllerBlockEntity scte)
return () -> new SpeedControllerPeripheral(scte, scte.targetSpeed);
if (te instanceof DisplayLinkBlockEntity dlte)
return () -> new DisplayLinkPeripheral(dlte);
if (te instanceof SequencedGearshiftBlockEntity sgte)
return () -> new SequencedGearshiftPeripheral(sgte);
if (te instanceof SpeedGaugeBlockEntity sgte)
return () -> new SpeedGaugePeripheral(sgte);
if (te instanceof StressGaugeBlockEntity sgte)
return () -> new StressGaugePeripheral(sgte);
if (te instanceof StationBlockEntity ste)
return () -> new StationPeripheral(ste);
throw new IllegalArgumentException("No peripheral available for " + te.getType()
.getRegistryName());
}
@Override
public <T> boolean isPeripheralCap(Capability<T> cap) {
return cap == PERIPHERAL_CAPABILITY;
}
@Override
public <T> LazyOptional<T> getPeripheralCapability() {
if (peripheral == null || !peripheral.isPresent())
peripheral = LazyOptional.of(peripheralSupplier);
return peripheral.cast();
}
@Override
public void removePeripheral() {
if (peripheral != null)
peripheral.invalidate();
}
}

View file

@ -0,0 +1,172 @@
package com.simibubi.create.compat.computercraft.implementation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaTable;
import dan200.computercraft.api.lua.LuaValues;
public class CreateLuaTable implements LuaTable<Object, Object> {
private final Map<Object, Object> map;
public CreateLuaTable() {
this.map = new HashMap<>();
}
public CreateLuaTable(Map<?, ?> map) {
this.map = new HashMap<>(map);
}
public boolean getBoolean(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return (Boolean) value;
}
public String getString(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof String))
throw LuaValues.badField(key, "string", LuaValues.getType(value));
return (String) value;
}
public CreateLuaTable getTable(String key) throws LuaException {
Object value = get(key);
if (!(value instanceof Map<?, ?>))
throw LuaValues.badField(key, "table", LuaValues.getType(value));
return new CreateLuaTable((Map<?, ?>) value);
}
public Optional<Boolean> getOptBoolean(String key) throws LuaException {
Object value = get(key);
if (value == null)
return Optional.empty();
if (!(value instanceof Boolean))
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
return Optional.of((Boolean) value);
}
public Set<String> stringKeySet() throws LuaException {
Set<String> stringSet = new HashSet<>();
for (Object key : keySet()) {
if (!(key instanceof String))
throw new LuaException("key " + key + " is not string (got " + LuaValues.getType(key) + ")");
stringSet.add((String) key);
}
return Collections.unmodifiableSet(stringSet);
}
public Collection<CreateLuaTable> tableValues() throws LuaException {
List<CreateLuaTable> tables = new ArrayList<>();
for (int i = 1; i <= size(); i++) {
Object value = get((double) i);
if (!(value instanceof Map<?, ?>))
throw new LuaException("value " + value + " is not table (got " + LuaValues.getType(value) + ")");
tables.add(new CreateLuaTable((Map<?, ?>) value));
}
return Collections.unmodifiableList(tables);
}
public Map<Object, Object> getMap() {
return map;
}
@Nullable
@Override
public Object put(Object key, Object value) {
return map.put(key, value);
}
public void putBoolean(String key, boolean value) {
map.put(key, value);
}
public void putDouble(String key, double value) {
map.put(key, value);
}
public void putString(String key, String value) {
map.put(key, value);
}
public void putTable(String key, CreateLuaTable value) {
map.put(key, value);
}
public void putTable(int i, CreateLuaTable value) {
map.put(i, value);
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return map.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return map.containsValue(o);
}
@Override
public Object get(Object o) {
return map.get(o);
}
@NotNull
@Override
public Set<Object> keySet() {
return map.keySet();
}
@NotNull
@Override
public Collection<Object> values() {
return map.values();
}
@NotNull
@Override
public Set<Entry<Object, Object>> entrySet() {
return map.entrySet();
}
}

View file

@ -0,0 +1,108 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlockEntity;
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
public class DisplayLinkPeripheral extends SyncedPeripheral<DisplayLinkBlockEntity> {
public static final String TAG_KEY = "ComputerSourceList";
private final AtomicInteger cursorX = new AtomicInteger();
private final AtomicInteger cursorY = new AtomicInteger();
public DisplayLinkPeripheral(DisplayLinkBlockEntity tile) {
super(tile);
}
@LuaFunction
public final void setCursorPos(int x, int y) {
cursorX.set(x - 1);
cursorY.set(y - 1);
}
@LuaFunction
public final Object[] getCursorPos() {
return new Object[] {cursorX.get() + 1, cursorY.get() + 1};
}
@LuaFunction(mainThread = true)
public final Object[] getSize() {
DisplayTargetStats stats = tile.activeTarget.provideStats(new DisplayLinkContext(tile.getLevel(), tile));
return new Object[]{stats.maxRows(), stats.maxColumns()};
}
@LuaFunction
public final boolean isColor() {
return false;
}
@LuaFunction
public final boolean isColour() {
return false;
}
@LuaFunction
public final void write(String text) {
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
int x = cursorX.get();
int y = cursorY.get();
for (int i = tag.size(); i <= y; i++) {
tag.add(StringTag.valueOf(""));
}
StringBuilder builder = new StringBuilder(tag.getString(y));
builder.append(" ".repeat(Math.max(0, x - builder.length())));
builder.replace(x, x + text.length(), text);
tag.set(y, StringTag.valueOf(builder.toString()));
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, tag);
}
cursorX.set(x + text.length());
}
@LuaFunction
public final void clearLine() {
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
if (tag.size() > cursorY.get())
tag.set(cursorY.get(), StringTag.valueOf(""));
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, tag);
}
}
@LuaFunction
public final void clear() {
synchronized (tile) {
tile.getSourceConfig().put(TAG_KEY, new ListTag());
}
}
@LuaFunction(mainThread = true)
public final void update() {
tile.tickSource();
}
@NotNull
@Override
public String getType() {
return "Create_DisplayLink";
}
}

View file

@ -0,0 +1,54 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Instruction;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.InstructionSpeedModifiers;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftBlockEntity;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencerInstructions;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
public class SequencedGearshiftPeripheral extends SyncedPeripheral<SequencedGearshiftBlockEntity> {
public SequencedGearshiftPeripheral(SequencedGearshiftBlockEntity tile) {
super(tile);
}
@LuaFunction(mainThread = true)
public final void rotate(IArguments arguments) throws LuaException {
runInstruction(arguments, SequencerInstructions.TURN_ANGLE);
}
@LuaFunction(mainThread = true)
public final void move(IArguments arguments) throws LuaException {
runInstruction(arguments, SequencerInstructions.TURN_DISTANCE);
}
@LuaFunction
public final boolean isRunning() {
return !this.tile.isIdle();
}
private void runInstruction(IArguments arguments, SequencerInstructions instructionType) throws LuaException {
int speedModifier = arguments.count() > 1 ? arguments.getInt(1) : 1;
this.tile.getInstructions().clear();
this.tile.getInstructions().add(new Instruction(
instructionType,
InstructionSpeedModifiers.getByModifier(speedModifier),
Math.abs(arguments.getInt(0))));
this.tile.getInstructions().add(new Instruction(SequencerInstructions.END));
this.tile.run(0);
}
@NotNull
@Override
public String getType() {
return "Create_SequencedGearshift";
}
}

View file

@ -0,0 +1,35 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollValueBehaviour;
import dan200.computercraft.api.lua.LuaFunction;
public class SpeedControllerPeripheral extends SyncedPeripheral<SpeedControllerBlockEntity> {
private final ScrollValueBehaviour targetSpeed;
public SpeedControllerPeripheral(SpeedControllerBlockEntity tile, ScrollValueBehaviour targetSpeed) {
super(tile);
this.targetSpeed = targetSpeed;
}
@LuaFunction(mainThread = true)
public final void setTargetSpeed(int speed) {
this.targetSpeed.setValue(speed);
}
@LuaFunction
public final float getTargetSpeed() {
return this.targetSpeed.getValue();
}
@NotNull
@Override
public String getType() {
return "Create_RotationSpeedController";
}
}

View file

@ -0,0 +1,26 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeBlockEntity;
import dan200.computercraft.api.lua.LuaFunction;
public class SpeedGaugePeripheral extends SyncedPeripheral<SpeedGaugeBlockEntity> {
public SpeedGaugePeripheral(SpeedGaugeBlockEntity tile) {
super(tile);
}
@LuaFunction
public final float getSpeed() {
return this.tile.getSpeed();
}
@NotNull
@Override
public String getType() {
return "Create_Speedometer";
}
}

View file

@ -0,0 +1,269 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.Map;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.compat.computercraft.implementation.CreateLuaTable;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.StringHelper;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.CollectionTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraftforge.network.PacketDistributor;
public class StationPeripheral extends SyncedPeripheral<StationBlockEntity> {
public StationPeripheral(StationBlockEntity tile) {
super(tile);
}
@LuaFunction(mainThread = true)
public final void assemble() throws LuaException {
if (!tile.isAssembling())
throw new LuaException("station must be in assembly mode");
tile.assemble(null);
if (tile.getStation() == null || tile.getStation().getPresentTrain() == null)
throw new LuaException("failed to assemble train");
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
@LuaFunction(mainThread = true)
public final void disassemble() throws LuaException {
if (tile.isAssembling())
throw new LuaException("station must not be in assembly mode");
getTrainOrThrow();
if (!tile.enterAssemblyMode(null))
throw new LuaException("could not disassemble train");
}
@LuaFunction(mainThread = true)
public final void setAssemblyMode(boolean assemblyMode) throws LuaException {
if (assemblyMode) {
if (!tile.enterAssemblyMode(null))
throw new LuaException("failed to enter assembly mode");
} else {
if (!tile.exitAssemblyMode())
throw new LuaException("failed to exit assembly mode");
}
}
@LuaFunction
public final boolean isInAssemblyMode() {
return tile.isAssembling();
}
@LuaFunction
public final String getStationName() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.name;
}
@LuaFunction(mainThread = true)
public final void setStationName(String name) throws LuaException {
if (!tile.updateName(name))
throw new LuaException("could not set station name");
}
@LuaFunction
public final boolean isTrainPresent() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getPresentTrain() != null;
}
@LuaFunction
public final boolean isTrainImminent() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getImminentTrain() != null;
}
@LuaFunction
public final boolean isTrainEnroute() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
return station.getNearestTrain() != null;
}
@LuaFunction
public final String getTrainName() throws LuaException {
Train train = getTrainOrThrow();
return train.name.getString();
}
@LuaFunction(mainThread = true)
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()));
}
@LuaFunction
public final boolean hasSchedule() throws LuaException {
Train train = getTrainOrThrow();
return train.runtime.getSchedule() != null;
}
@LuaFunction
public final CreateLuaTable getSchedule() throws LuaException {
Train train = getTrainOrThrow();
Schedule schedule = train.runtime.getSchedule();
if (schedule == null)
throw new LuaException("train doesn't have a schedule");
return fromCompoundTag(schedule.write());
}
@LuaFunction(mainThread = true)
public final void setSchedule(IArguments arguments) throws LuaException {
Train train = getTrainOrThrow();
Schedule schedule = Schedule.fromTag(toCompoundTag(new CreateLuaTable(arguments.getTable(0))));
boolean autoSchedule = train.runtime.getSchedule() == null || train.runtime.isAutoSchedule;
train.runtime.setSchedule(schedule, autoSchedule);
}
private @NotNull Train getTrainOrThrow() throws LuaException {
GlobalStation station = tile.getStation();
if (station == null)
throw new LuaException("station is not connected to a track");
Train train = station.getPresentTrain();
if (train == null)
throw new LuaException("there is no train present");
return train;
}
private static @NotNull CreateLuaTable fromCompoundTag(CompoundTag tag) throws LuaException {
return (CreateLuaTable) fromNBTTag(null, tag);
}
private static @NotNull Object fromNBTTag(@Nullable String key, Tag tag) throws LuaException {
byte type = tag.getId();
if (type == Tag.TAG_BYTE && key != null && key.equals("Count"))
return ((NumericTag) tag).getAsByte();
else if (type == Tag.TAG_BYTE)
return ((NumericTag) tag).getAsByte() != 0;
else if (type == Tag.TAG_SHORT || type == Tag.TAG_INT || type == Tag.TAG_LONG)
return ((NumericTag) tag).getAsLong();
else if (type == Tag.TAG_FLOAT || type == Tag.TAG_DOUBLE)
return ((NumericTag) tag).getAsDouble();
else if (type == Tag.TAG_STRING)
return tag.getAsString();
else if (type == Tag.TAG_LIST || type == Tag.TAG_BYTE_ARRAY || type == Tag.TAG_INT_ARRAY || type == Tag.TAG_LONG_ARRAY) {
CreateLuaTable list = new CreateLuaTable();
CollectionTag<?> listTag = (CollectionTag<?>) tag;
for (int i = 0; i < listTag.size(); i++) {
list.put(i + 1, fromNBTTag(null, listTag.get(i)));
}
return list;
} else if (type == Tag.TAG_COMPOUND) {
CreateLuaTable table = new CreateLuaTable();
CompoundTag compoundTag = (CompoundTag) tag;
for (String compoundKey : compoundTag.getAllKeys()) {
table.put(
StringHelper.camelCaseToSnakeCase(compoundKey),
fromNBTTag(compoundKey, compoundTag.get(compoundKey))
);
}
return table;
}
throw new LuaException("unknown tag type " + tag.getType().getName());
}
private static @NotNull CompoundTag toCompoundTag(CreateLuaTable table) throws LuaException {
return (CompoundTag) toNBTTag(null, table.getMap());
}
private static @NotNull Tag toNBTTag(@Nullable String key, Object value) throws LuaException {
if (value instanceof Boolean v)
return ByteTag.valueOf(v);
else if (value instanceof Byte || (key != null && key.equals("count")))
return ByteTag.valueOf(((Number) value).byteValue());
else if (value instanceof Number v) {
// If number is numerical integer
if (v.intValue() == v.doubleValue())
return IntTag.valueOf(v.intValue());
else
return DoubleTag.valueOf(v.doubleValue());
} else if (value instanceof String v)
return StringTag.valueOf(v);
else if (value instanceof Map<?, ?> v && v.containsKey(1.0)) { // List
ListTag list = new ListTag();
for (Object o : v.values()) {
list.add(toNBTTag(null, o));
}
return list;
} else if (value instanceof Map<?, ?> v) { // Table/Map
CompoundTag compound = new CompoundTag();
for (Object objectKey : v.keySet()) {
if (!(objectKey instanceof String compoundKey))
throw new LuaException("table key is not of type string");
compound.put(
// Items serialize their resource location as "id" and not as "Id".
// This check is needed to see if the 'i' should be left lowercase or not.
// Items store "count" in the same compound tag, so we can check for its presence to see if this is a serialized item
compoundKey.equals("id") && v.containsKey("count") ? "id" : StringHelper.snakeCaseToCamelCase(compoundKey),
toNBTTag(compoundKey, v.get(compoundKey))
);
}
return compound;
}
throw new LuaException("unknown object type " + value.getClass().getName());
}
@NotNull
@Override
public String getType() {
return "Create_Station";
}
}

View file

@ -0,0 +1,31 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeBlockEntity;
import dan200.computercraft.api.lua.LuaFunction;
public class StressGaugePeripheral extends SyncedPeripheral<StressGaugeBlockEntity> {
public StressGaugePeripheral(StressGaugeBlockEntity tile) {
super(tile);
}
@LuaFunction
public final float getStress() {
return this.tile.getNetworkStress();
}
@LuaFunction
public final float getStressCapacity() {
return this.tile.getNetworkCapacity();
}
@NotNull
@Override
public String getType() {
return "Create_Stressometer";
}
}

View file

@ -0,0 +1,50 @@
package com.simibubi.create.compat.computercraft.implementation.peripherals;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.networking.AllPackets;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraftforge.network.PacketDistributor;
public abstract class SyncedPeripheral<T extends SmartBlockEntity> implements IPeripheral {
protected final T tile;
private final AtomicInteger computers = new AtomicInteger();
public SyncedPeripheral(T tile) {
this.tile = tile;
}
@Override
public void attach(@NotNull IComputerAccess computer) {
computers.incrementAndGet();
updateTile();
}
@Override
public void detach(@NotNull IComputerAccess computer) {
computers.decrementAndGet();
updateTile();
}
private void updateTile() {
boolean hasAttachedComputer = computers.get() > 0;
tile.getBehaviour(ComputerBehaviour.TYPE).setHasAttachedComputer(hasAttachedComputer);
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new AttachedComputerPacket(tile.getBlockPos(), hasAttachedComputer));
}
@Override
public boolean equals(@Nullable IPeripheral other) {
return this == other;
}
}

View file

@ -4,7 +4,7 @@ import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld; import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ActorInstance; import com.simibubi.create.content.contraptions.components.structureMovement.render.ActorInstance;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
@ -62,7 +62,7 @@ public class DrillMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override @Override
public boolean canBreak(Level world, BlockPos breakingPos, BlockState state) { public boolean canBreak(Level world, BlockPos breakingPos, BlockState state) {
return super.canBreak(world, breakingPos, state) && !state.getCollisionShape(world, breakingPos) return super.canBreak(world, breakingPos, state) && !state.getCollisionShape(world, breakingPos)
.isEmpty() && !AllBlocks.TRACK.has(state); .isEmpty() && !AllTags.AllBlockTags.TRACKS.matches(state);
} }
} }

View file

@ -3,8 +3,8 @@ package com.simibubi.create.content.contraptions.components.press;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipeTypes; import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCraftingRecipe; import com.simibubi.create.content.contraptions.components.crafter.MechanicalCraftingRecipe;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode; import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.Mode;
import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.PressingBehaviourSpecifics; import com.simibubi.create.content.contraptions.components.press.PressingBehaviour.PressingBehaviourSpecifics;
@ -68,7 +68,7 @@ public class MechanicalPressBlockEntity extends BasinOperatingBlockEntity implem
public void onItemPressed(ItemStack result) { public void onItemPressed(ItemStack result) {
award(AllAdvancements.PRESS); award(AllAdvancements.PRESS);
if (AllBlocks.TRACK.isIn(result)) if (AllTags.AllBlockTags.TRACKS.matches(result))
tracksCreated += result.getCount(); tracksCreated += result.getCount();
if (tracksCreated >= 1000) { if (tracksCreated >= 1000) {
award(AllAdvancements.TRACK_CRAFTING); award(AllAdvancements.TRACK_CRAFTING);

View file

@ -29,7 +29,7 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankBlock;
import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock; import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock;
import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkBlock; import com.simibubi.create.content.logistics.block.redstone.RedstoneLinkBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock; import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlock; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlock;
import com.simibubi.create.foundation.config.ContraptionMovementSetting; import com.simibubi.create.foundation.config.ContraptionMovementSetting;
@ -338,7 +338,7 @@ public class BlockMovementChecks {
return direction == state.getValue(StickerBlock.FACING) return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite()); && !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
} }
if (block instanceof IBogeyBlock bogey) if (block instanceof AbstractBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state) return bogey.getStickySurfaces(world, pos, state)
.contains(direction); .contains(direction);
if (block instanceof WhistleBlock) if (block instanceof WhistleBlock)

View file

@ -64,7 +64,7 @@ import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateBlockEntity; import com.simibubi.create.content.logistics.block.inventories.CreativeCrateBlockEntity;
import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock; import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultBlockEntity; import com.simibubi.create.content.logistics.block.vault.ItemVaultBlockEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.blockEntity.IMultiBlockEntityContainer; import com.simibubi.create.foundation.blockEntity.IMultiBlockEntityContainer;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
@ -347,7 +347,7 @@ public abstract class Contraption {
} }
// Bogeys tend to have sticky sides // Bogeys tend to have sticky sides
if (state.getBlock()instanceof IBogeyBlock bogey) if (state.getBlock()instanceof AbstractBogeyBlock<?> bogey)
for (Direction d : bogey.getStickySurfaces(world, pos, state)) for (Direction d : bogey.getStickySurfaces(world, pos, state))
if (!visited.contains(pos.relative(d))) if (!visited.contains(pos.relative(d)))
frontier.add(pos.relative(d)); frontier.add(pos.relative(d));

View file

@ -202,6 +202,7 @@ public class GantryContraptionEntity extends AbstractContraptionEntity {
} }
@Override @Override
@OnlyIn(Dist.CLIENT)
public void applyLocalTransforms(PoseStack matrixStack, float partialTicks) {} public void applyLocalTransforms(PoseStack matrixStack, float partialTicks) {}
public void updateClientMotion() { public void updateClientMotion() {

View file

@ -42,7 +42,6 @@ public class WaterWheelInstance<T extends WaterWheelBlockEntity> extends CutoutR
return getRotatingMaterial().model(key, () -> { return getRotatingMaterial().model(key, () -> {
BakedModel model = WaterWheelRenderer.generateModel(key); BakedModel model = WaterWheelRenderer.generateModel(key);
BlockState state = key.state(); BlockState state = key.state();
// TODO waterwheels
Direction dir; Direction dir;
if (key.large()) { if (key.large()) {
dir = Direction.fromAxisAndDirection(state.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE); dir = Direction.fromAxisAndDirection(state.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE);

View file

@ -3,9 +3,7 @@ package com.simibubi.create.content.contraptions.components.waterwheel;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Random;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.StitchedSprite; import com.jozufozu.flywheel.core.StitchedSprite;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllPartialModels; import com.simibubi.create.AllPartialModels;
@ -32,6 +30,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.ModelData;
import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistries;
public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends KineticBlockEntityRenderer<T> { public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends KineticBlockEntityRenderer<T> {
@ -41,6 +40,8 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
public static final StitchedSprite OAK_LOG_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log")); public static final StitchedSprite OAK_LOG_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log"));
public static final StitchedSprite OAK_LOG_TOP_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log_top")); public static final StitchedSprite OAK_LOG_TOP_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log_top"));
private static final String[] LOG_SUFFIXES = new String[] { "_log", "_stem" };
protected final boolean large; protected final boolean large;
public WaterWheelRenderer(Context context, boolean large) { public WaterWheelRenderer(Context context, boolean large) {
@ -60,9 +61,8 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
protected SuperByteBuffer getRotatedModel(T be, BlockState state) { protected SuperByteBuffer getRotatedModel(T be, BlockState state) {
WaterWheelModelKey key = new WaterWheelModelKey(large, state, be.material); WaterWheelModelKey key = new WaterWheelModelKey(large, state, be.material);
return CreateClient.BUFFER_CACHE.get(WATER_WHEEL, key, () -> { return CreateClient.BUFFER_CACHE.get(WATER_WHEEL, key, () -> {
BakedModel model = WaterWheelRenderer.generateModel(key); BakedModel model = generateModel(key);
BlockState state1 = key.state(); BlockState state1 = key.state();
// TODO waterwheels
Direction dir; Direction dir;
if (key.large()) { if (key.large()) {
dir = Direction.fromAxisAndDirection(state1.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE); dir = Direction.fromAxisAndDirection(state1.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE);
@ -74,25 +74,24 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
}); });
} }
public static PartialModel getTemplateModel(boolean large, boolean extension) {
if (large) {
if (extension) {
return AllPartialModels.LARGE_WATER_WHEEL_EXTENSION;
} else {
return AllPartialModels.LARGE_WATER_WHEEL;
}
} else {
return AllPartialModels.WATER_WHEEL;
}
}
public static BakedModel generateModel(WaterWheelModelKey key) { public static BakedModel generateModel(WaterWheelModelKey key) {
BakedModel template;
if (key.large()) {
boolean extension = key.state() boolean extension = key.state()
.getOptionalValue(LargeWaterWheelBlock.EXTENSION) .getValue(LargeWaterWheelBlock.EXTENSION);
.orElse(false); if (extension) {
BakedModel template = getTemplateModel(key.large(), extension).get(); template = AllPartialModels.LARGE_WATER_WHEEL_EXTENSION.get();
} else {
template = AllPartialModels.LARGE_WATER_WHEEL.get();
}
} else {
template = AllPartialModels.WATER_WHEEL.get();
}
BlockState planksBlockState = key.material(); return generateModel(template, key.material());
}
public static BakedModel generateModel(BakedModel template, BlockState planksBlockState) {
Block planksBlock = planksBlockState.getBlock(); Block planksBlock = planksBlockState.getBlock();
ResourceLocation id = RegisteredObjects.getKeyOrThrow(planksBlock); ResourceLocation id = RegisteredObjects.getKeyOrThrow(planksBlock);
String path = id.getPath(); String path = id.getPath();
@ -104,7 +103,7 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
Map<TextureAtlasSprite, TextureAtlasSprite> map = new Reference2ReferenceOpenHashMap<>(); Map<TextureAtlasSprite, TextureAtlasSprite> map = new Reference2ReferenceOpenHashMap<>();
map.put(OAK_PLANKS_TEMPLATE.get(), getSpriteOnSide(planksBlockState, Direction.UP)); map.put(OAK_PLANKS_TEMPLATE.get(), getSpriteOnSide(planksBlockState, Direction.UP));
map.put(OAK_LOG_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.NORTH)); map.put(OAK_LOG_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.SOUTH));
map.put(OAK_LOG_TOP_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.UP)); map.put(OAK_LOG_TOP_TEMPLATE.get(), getSpriteOnSide(logBlockState, Direction.UP));
return BakedModelHelper.generateModel(template, map::get); return BakedModelHelper.generateModel(template, map::get);
@ -114,7 +113,7 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
} }
private static BlockState getLogBlockState(String namespace, String wood) { private static BlockState getLogBlockState(String namespace, String wood) {
for (String suffix : new String[] { "_log", "_stem" }) { for (String suffix : LOG_SUFFIXES) {
Optional<BlockState> state = Optional<BlockState> state =
ForgeRegistries.BLOCKS.getHolder(new ResourceLocation(namespace, wood + suffix)) ForgeRegistries.BLOCKS.getHolder(new ResourceLocation(namespace, wood + suffix))
.map(Holder::value) .map(Holder::value)
@ -125,18 +124,29 @@ public class WaterWheelRenderer<T extends WaterWheelBlockEntity> extends Kinetic
return Blocks.OAK_LOG.defaultBlockState(); return Blocks.OAK_LOG.defaultBlockState();
} }
private static TextureAtlasSprite getSpriteOnSide(BlockState blockstate, Direction side) { private static TextureAtlasSprite getSpriteOnSide(BlockState state, Direction side) {
BakedModel blockModel = Minecraft.getInstance() BakedModel model = Minecraft.getInstance()
.getBlockRenderer() .getBlockRenderer()
.getBlockModel(blockstate); .getBlockModel(state);
if (blockModel == null) if (model == null)
return null;
@SuppressWarnings("deprecation")
List<BakedQuad> quads = blockModel.getQuads(blockstate, side, RandomSource.create());
if (quads.isEmpty())
return null; return null;
RandomSource random = RandomSource.create();
random.setSeed(42L);
List<BakedQuad> quads = model.getQuads(state, side, random, ModelData.EMPTY, null);
if (!quads.isEmpty()) {
return quads.get(0) return quads.get(0)
.getSprite(); .getSprite();
} }
random.setSeed(42L);
quads = model.getQuads(state, null, random, ModelData.EMPTY, null);
if (!quads.isEmpty()) {
for (BakedQuad quad : quads) {
if (quad.getDirection() == side) {
return quad.getSprite();
}
}
}
return model.getParticleIcon(ModelData.EMPTY);
}
} }

View file

@ -25,6 +25,7 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -39,6 +40,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.ForgeConfig;
import net.minecraftforge.common.Tags; import net.minecraftforge.common.Tags;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
@ -54,6 +56,7 @@ public class OpenEndedPipe extends FlowSource {
registerEffectHandler(new MilkEffectHandler()); registerEffectHandler(new MilkEffectHandler());
registerEffectHandler(new WaterEffectHandler()); registerEffectHandler(new WaterEffectHandler());
registerEffectHandler(new LavaEffectHandler()); registerEffectHandler(new LavaEffectHandler());
registerEffectHandler(new TeaEffectHandler());
} }
private Level world; private Level world;
@ -443,4 +446,23 @@ public class OpenEndedPipe extends FlowSource {
} }
} }
public static class TeaEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return fluid.getFluid().isSame(AllFluids.TEA.get());
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities = world
.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
entity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 21, 0, false, false, false));
}
}
}
} }

View file

@ -125,4 +125,8 @@ public class HosePulleyFluidHandler implements IFluidHandler {
return internalTank.isFluidValid(tank, stack); return internalTank.isFluidValid(tank, stack);
} }
public SmartFluidTank getInternalTank() {
return internalTank;
}
} }

View file

@ -38,14 +38,24 @@ public class SpoutRenderer extends SafeBlockEntityRenderer<SpoutBlockEntity> {
.getValue(partialTicks); .getValue(partialTicks);
if (!fluidStack.isEmpty() && level != 0) { if (!fluidStack.isEmpty() && level != 0) {
boolean top = fluidStack.getFluid()
.getAttributes()
.isLighterThanAir();
level = Math.max(level, 0.175f); level = Math.max(level, 0.175f);
float min = 2.5f / 16f; float min = 2.5f / 16f;
float max = min + (11 / 16f); float max = min + (11 / 16f);
float yOffset = (11 / 16f) * level; float yOffset = (11 / 16f) * level;
ms.pushPose(); ms.pushPose();
ms.translate(0, yOffset, 0); if (!top) ms.translate(0, yOffset, 0);
FluidRenderer.renderFluidBox(fluidStack, min, min - yOffset, min, max, min, max, buffer, ms, light, else ms.translate(0, max - min, 0);
false);
FluidRenderer.renderFluidBox(fluidStack,
min, min - yOffset, min,
max, min, max,
buffer, ms, light, false);
ms.popPose(); ms.popPose();
} }

View file

@ -12,6 +12,8 @@ import java.util.function.Supplier;
import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.EncasedBlock; import com.simibubi.create.content.contraptions.relays.elementary.EncasedBlock;
@ -36,7 +38,9 @@ import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock; import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -46,7 +50,8 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.ticks.TickPriority; import net.minecraft.world.ticks.TickPriority;
public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock { public class EncasedPipeBlock extends Block
implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock, ITransformableBlock {
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION; public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
private final Supplier<Block> casing; private final Supplier<Block> casing;
@ -173,4 +178,20 @@ public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBloc
EncasedPipeBlock.transferSixWayProperties(state, defaultBlockState())); EncasedPipeBlock.transferSixWayProperties(state, defaultBlockState()));
FluidTransportBehaviour.loadFlows(level, pos); FluidTransportBehaviour.loadFlows(level, pos);
} }
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return FluidPipeBlockRotation.rotate(pState, pRotation);
}
@Override
public BlockState mirror(BlockState pState, Mirror pMirror) {
return FluidPipeBlockRotation.mirror(pState, pMirror);
}
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
return FluidPipeBlockRotation.transform(state, transform);
}
} }

View file

@ -7,6 +7,8 @@ import javax.annotation.Nullable;
import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
@ -36,7 +38,9 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock; import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.SimpleWaterloggedBlock; import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@ -50,8 +54,8 @@ import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.TickPriority; import net.minecraft.world.ticks.TickPriority;
public class FluidPipeBlock extends PipeBlock public class FluidPipeBlock extends PipeBlock implements SimpleWaterloggedBlock, IWrenchableWithBracket,
implements SimpleWaterloggedBlock, IWrenchableWithBracket, IBE<FluidPipeBlockEntity>, EncasableBlock { IBE<FluidPipeBlockEntity>, EncasableBlock, ITransformableBlock {
private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12); private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12);
@ -337,4 +341,20 @@ public class FluidPipeBlock extends PipeBlock
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) { public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
return OCCLUSION_BOX; return OCCLUSION_BOX;
} }
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return FluidPipeBlockRotation.rotate(pState, pRotation);
}
@Override
public BlockState mirror(BlockState pState, Mirror pMirror) {
return FluidPipeBlockRotation.mirror(pState, pMirror);
}
@Override
public BlockState transform(BlockState state, StructureTransform transform) {
return FluidPipeBlockRotation.transform(state, transform);
}
} }

View file

@ -0,0 +1,49 @@
package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.Map;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
public class FluidPipeBlockRotation {
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
public static BlockState rotate(BlockState state, Rotation rotation) {
BlockState rotated = state;
for (Direction direction : Iterate.horizontalDirections)
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(rotation.rotate(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return rotated;
}
public static BlockState mirror(BlockState state, Mirror mirror) {
BlockState mirrored = state;
for (Direction direction : Iterate.horizontalDirections)
mirrored = mirrored.setValue(FACING_TO_PROPERTY_MAP.get(mirror.mirror(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return mirrored;
}
public static BlockState transform(BlockState state, StructureTransform transform) {
if (transform.mirror != null)
state = mirror(state, transform.mirror);
if (transform.rotationAxis == Direction.Axis.Y)
return rotate(state, transform.rotation);
BlockState rotated = state;
for (Direction direction : Iterate.directions)
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(transform.rotateFacing(direction)),
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
return rotated;
}
}

View file

@ -92,8 +92,7 @@ public class SmartFluidPipeBlock extends FaceAttachedHorizontalDirectionalBlock
boolean blockTypeChanged = state.getBlock() != newState.getBlock(); boolean blockTypeChanged = state.getBlock() != newState.getBlock();
if (blockTypeChanged && !world.isClientSide) if (blockTypeChanged && !world.isClientSide)
FluidPropagator.propagateChangedPipe(world, pos, state); FluidPropagator.propagateChangedPipe(world, pos, state);
if (state.hasBlockEntity() && (blockTypeChanged || !newState.hasBlockEntity())) IBE.onRemove(state, world, pos, newState);
world.removeBlockEntity(pos);
} }
@Override @Override

View file

@ -44,7 +44,8 @@ public class SequencedAssemblyRecipe implements Recipe<RecipeWrapper> {
protected List<SequencedRecipe<?>> sequence; protected List<SequencedRecipe<?>> sequence;
protected int loops; protected int loops;
protected ProcessingOutput transitionalItem; protected ProcessingOutput transitionalItem;
protected List<ProcessingOutput> resultPool;
public final List<ProcessingOutput> resultPool;
public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) { public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) {
this.id = recipeId; this.id = recipeId;

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.advanced;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.RotationPropagator; import com.simibubi.create.content.contraptions.RotationPropagator;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity; import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.components.motor.KineticScrollValueBehaviour; import com.simibubi.create.content.contraptions.components.motor.KineticScrollValueBehaviour;
@ -20,11 +25,14 @@ import net.minecraft.core.Direction;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SpeedControllerBlockEntity extends KineticBlockEntity { public class SpeedControllerBlockEntity extends KineticBlockEntity {
public static final int DEFAULT_SPEED = 16; public static final int DEFAULT_SPEED = 16;
protected ScrollValueBehaviour targetSpeed; public ScrollValueBehaviour targetSpeed;
public AbstractComputerBehaviour computerBehaviour;
boolean hasBracket; boolean hasBracket;
@ -50,6 +58,7 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
targetSpeed.value = DEFAULT_SPEED; targetSpeed.value = DEFAULT_SPEED;
targetSpeed.withCallback(i -> this.updateTargetRotation()); targetSpeed.withCallback(i -> this.updateTargetRotation());
behaviours.add(targetSpeed); behaviours.add(targetSpeed);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.SPEED_CONTROLLER); registerAwardables(behaviours, AllAdvancements.SPEED_CONTROLLER);
} }
@ -125,6 +134,20 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
.isHorizontal(); .isHorizontal();
} }
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
private class ControllerValueBoxTransform extends ValueBoxTransform.Sided { private class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
@Override @Override

View file

@ -35,6 +35,9 @@ public class ConfigureSequencedGearshiftPacket extends BlockEntityConfigurationP
@Override @Override
protected void applySettings(SequencedGearshiftBlockEntity be) { protected void applySettings(SequencedGearshiftBlockEntity be) {
if (be.computerBehaviour.hasAttachedComputer())
return;
be.run(-1); be.run(-1);
be.instructions = Instruction.deserializeAll(instructions); be.instructions = Instruction.deserializeAll(instructions);
be.sendData(); be.sendData();

View file

@ -19,8 +19,12 @@ public class Instruction {
} }
public Instruction(SequencerInstructions instruction, int value) { public Instruction(SequencerInstructions instruction, int value) {
this(instruction, InstructionSpeedModifiers.FORWARD, value);
}
public Instruction(SequencerInstructions instruction, InstructionSpeedModifiers speedModifier, int value) {
this.instruction = instruction; this.instruction = instruction;
speedModifier = InstructionSpeedModifiers.FORWARD; this.speedModifier = speedModifier;
this.value = value; this.value = value;
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.relays.advanced.sequencer; package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Components;
@ -36,4 +37,11 @@ public enum InstructionSpeedModifiers {
return options; return options;
} }
public static InstructionSpeedModifiers getByModifier(int modifier) {
return Arrays.stream(InstructionSpeedModifiers.values())
.filter(speedModifier -> speedModifier.value == modifier)
.findAny()
.orElse(InstructionSpeedModifiers.FORWARD);
}
} }

View file

@ -1,9 +1,17 @@
package com.simibubi.create.content.contraptions.relays.advanced.sequencer; package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
import java.util.List;
import java.util.Vector; import java.util.Vector;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity; import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.relays.encased.SplitShaftBlockEntity; import com.simibubi.create.content.contraptions.relays.encased.SplitShaftBlockEntity;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -12,6 +20,8 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity { public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
@ -22,6 +32,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
int timer; int timer;
boolean poweredPreviously; boolean poweredPreviously;
public AbstractComputerBehaviour computerBehaviour;
public record SequenceContext(SequencerInstructions instruction, double relativeValue) { public record SequenceContext(SequencerInstructions instruction, double relativeValue) {
public static SequenceContext fromGearshift(SequencerInstructions instruction, double kineticSpeed, public static SequenceContext fromGearshift(SequencerInstructions instruction, double kineticSpeed,
@ -61,6 +73,12 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
poweredPreviously = false; poweredPreviously = false;
} }
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
}
@Override @Override
public void tick() { public void tick() {
super.tick(); super.tick();
@ -103,6 +121,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
} }
public void onRedstoneUpdate(boolean isPowered, boolean isRunning) { public void onRedstoneUpdate(boolean isPowered, boolean isRunning) {
if (computerBehaviour.hasAttachedComputer())
return;
if (!poweredPreviously && isPowered) if (!poweredPreviously && isPowered)
risingFlank(); risingFlank();
poweredPreviously = isPowered; poweredPreviously = isPowered;
@ -136,7 +156,7 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
} }
} }
protected void run(int instructionIndex) { public void run(int instructionIndex) {
Instruction instruction = getInstruction(instructionIndex); Instruction instruction = getInstruction(instructionIndex);
if (instruction == null || instruction.instruction == SequencerInstructions.END) { if (instruction == null || instruction.instruction == SequencerInstructions.END) {
if (getModifier() != 0) if (getModifier() != 0)
@ -193,6 +213,20 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
super.read(compound, clientPacket); super.read(compound, clientPacket);
} }
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
@Override @Override
public float getRotationSpeedModifier(Direction face) { public float getRotationSpeedModifier(Direction face) {
if (isVirtual()) if (isVirtual())
@ -208,4 +242,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
.getSpeedModifier(); .getSpeedModifier();
} }
public Vector<Instruction> getInstructions() {
return this.instructions;
}
} }

View file

@ -4,6 +4,7 @@ import java.util.Vector;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.compat.computercraft.ComputerScreen;
import com.simibubi.create.foundation.gui.AbstractSimiScreen; import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures; import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons; import com.simibubi.create.foundation.gui.AllIcons;
@ -15,7 +16,6 @@ import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -25,22 +25,26 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
private final ItemStack renderedItem = AllBlocks.SEQUENCED_GEARSHIFT.asStack(); private final ItemStack renderedItem = AllBlocks.SEQUENCED_GEARSHIFT.asStack();
private final AllGuiTextures background = AllGuiTextures.SEQUENCER; private final AllGuiTextures background = AllGuiTextures.SEQUENCER;
private IconButton confirmButton; private IconButton confirmButton;
private SequencedGearshiftBlockEntity be;
private ListTag compareTag; private ListTag compareTag;
private Vector<Instruction> instructions; private Vector<Instruction> instructions;
private BlockPos pos;
private Vector<Vector<ScrollInput>> inputs; private Vector<Vector<ScrollInput>> inputs;
public SequencedGearshiftScreen(SequencedGearshiftBlockEntity be) { public SequencedGearshiftScreen(SequencedGearshiftBlockEntity be) {
super(Lang.translateDirect("gui.sequenced_gearshift.title")); super(Lang.translateDirect("gui.sequenced_gearshift.title"));
this.instructions = be.instructions; this.instructions = be.instructions;
this.pos = be.getBlockPos(); this.be = be;
compareTag = Instruction.serializeAll(instructions); compareTag = Instruction.serializeAll(instructions);
} }
@Override @Override
protected void init() { protected void init() {
if (be.computerBehaviour.hasAttachedComputer())
minecraft.setScreen(
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
setWindowSize(background.width, background.height); setWindowSize(background.width, background.height);
setWindowOffset(-20, 0); setWindowOffset(-20, 0);
super.init(); super.init();
@ -55,8 +59,7 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
for (int row = 0; row < instructions.size(); row++) for (int row = 0; row < instructions.size(); row++)
initInputsOfRow(row, x, y); initInputsOfRow(row, x, y);
confirmButton = confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
confirmButton.withCallback(() -> { confirmButton.withCallback(() -> {
onClose(); onClose();
}); });
@ -127,6 +130,15 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
modifier.setState(instruction.speedModifier.ordinal()); modifier.setState(instruction.speedModifier.ordinal());
} }
@Override
public void tick() {
super.tick();
if (be.computerBehaviour.hasAttachedComputer())
minecraft.setScreen(
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
}
@Override @Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) { protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft; int x = guiLeft;
@ -134,6 +146,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
background.render(ms, x, y, this); background.render(ms, x, y, this);
for (int row = 0; row < instructions.capacity(); row++) {
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
int yOffset = toDraw.height * row;
toDraw.render(ms, x, y + 14 + yOffset, this);
}
for (int row = 0; row < instructions.capacity(); row++) { for (int row = 0; row < instructions.capacity(); row++) {
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY; AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
int yOffset = toDraw.height * row; int yOffset = toDraw.height * row;
@ -157,9 +176,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
} }
font.draw(ms, title, x + (background.width - 8) / 2 - font.width(title) / 2, y + 4, 0x592424); font.draw(ms, title, x + (background.width - 8) / 2 - font.width(title) / 2, y + 4, 0x592424);
renderAdditional(ms, mouseX, mouseY, partialTicks, x, y, background);
}
GuiGameElement.of(renderedItem) private void renderAdditional(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop,
.<GuiGameElement.GuiRenderBuilder>at(x + background.width + 6, y + background.height - 56, -200) AllGuiTextures background) {
GuiGameElement.of(renderedItem).<GuiGameElement
.GuiRenderBuilder>at(guiLeft + background.width + 6, guiTop + background.height - 56, 100)
.scale(5) .scale(5)
.render(ms); .render(ms);
} }
@ -172,7 +195,8 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
ListTag serialized = Instruction.serializeAll(instructions); ListTag serialized = Instruction.serializeAll(instructions);
if (serialized.equals(compareTag)) if (serialized.equals(compareTag))
return; return;
AllPackets.getChannel().sendToServer(new ConfigureSequencedGearshiftPacket(pos, serialized)); AllPackets.getChannel()
.sendToServer(new ConfigureSequencedGearshiftPacket(be.getBlockPos(), serialized));
} }
@Override @Override

View file

@ -12,7 +12,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation { public abstract class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation {
public float dialTarget; public float dialTarget;
public float dialState; public float dialState;
@ -52,4 +52,5 @@ public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleI
return true; return true;
} }
} }

View file

@ -2,24 +2,41 @@ package com.simibubi.create.content.contraptions.relays.gauge;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Color; import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class SpeedGaugeBlockEntity extends GaugeBlockEntity { public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
public AbstractComputerBehaviour computerBehaviour;
public SpeedGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public SpeedGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
} }
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
}
@Override @Override
public void onSpeedChanged(float prevSpeed) { public void onSpeedChanged(float prevSpeed) {
super.onSpeedChanged(prevSpeed); super.onSpeedChanged(prevSpeed);
@ -62,4 +79,19 @@ public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
.forGoggles(tooltip); .forGoggles(tooltip);
return true; return true;
} }
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
} }

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.gauge;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact; import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour; import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
@ -13,14 +18,19 @@ import com.simibubi.create.foundation.utility.LangBuilder;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class StressGaugeBlockEntity extends GaugeBlockEntity { public class StressGaugeBlockEntity extends GaugeBlockEntity {
public AbstractComputerBehaviour computerBehaviour;
static BlockPos lastSent; static BlockPos lastSent;
public StressGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public StressGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
@ -30,6 +40,7 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
@Override @Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) { public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
super.addBehaviours(behaviours); super.addBehaviours(behaviours);
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.STRESSOMETER, AllAdvancements.STRESSOMETER_MAXED); registerAwardables(behaviours, AllAdvancements.STRESSOMETER, AllAdvancements.STRESSOMETER_MAXED);
} }
@ -141,4 +152,18 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
award(AllAdvancements.STRESSOMETER_MAXED); award(AllAdvancements.STRESSOMETER_MAXED);
} }
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
} }

View file

@ -440,7 +440,7 @@ public class ClipboardScreen extends AbstractSimiScreen {
if (currentEntries.get(editingIndex).text.getString() if (currentEntries.get(editingIndex).text.getString()
.isEmpty() && currentEntries.size() > 1) { .isEmpty() && currentEntries.size() > 1) {
currentEntries.remove(editingIndex); currentEntries.remove(editingIndex);
editingIndex -= 1; editingIndex = Math.max(0, editingIndex - 1);
editContext.setCursorToEnd(); editContext.setCursorToEnd();
return true; return true;
} else if (hasControlDown()) { } else if (hasControlDown()) {

View file

@ -6,6 +6,7 @@ import static net.minecraft.world.level.block.state.properties.BlockStatePropert
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity; import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.fluids.pipes.BracketBlock; import com.simibubi.create.content.contraptions.fluids.pipes.BracketBlock;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
@ -223,7 +224,7 @@ public class GirderBlock extends Block implements SimpleWaterloggedBlock, IWrenc
for (Direction d2 : Iterate.directionsInAxis(axis == Axis.X ? Axis.Z : Axis.X)) { for (Direction d2 : Iterate.directionsInAxis(axis == Axis.X ? Axis.Z : Axis.X)) {
BlockState above = level.getBlockState(pos.above() BlockState above = level.getBlockState(pos.above()
.relative(d2)); .relative(d2));
if (AllBlocks.TRACK.has(above)) { if (AllTags.AllBlockTags.GIRDABLE_TRACKS.matches(above)) {
TrackShape shape = above.getValue(TrackBlock.SHAPE); TrackShape shape = above.getValue(TrackBlock.SHAPE);
if (shape == (axis == Axis.X ? TrackShape.XO : TrackShape.ZO)) if (shape == (axis == Axis.X ? TrackShape.XO : TrackShape.ZO))
state = state.setValue(updateProperty, true); state = state.setValue(updateProperty, true);

View file

@ -9,7 +9,6 @@ import java.util.Optional;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintCraftingInventory; import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintCraftingInventory;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintSection; import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintSection;
@ -106,7 +105,7 @@ public class BlueprintOverlayRenderer {
int tracks = info.requiredTracks; int tracks = info.requiredTracks;
while (tracks > 0) { while (tracks > 0) {
ingredients.add(Pair.of(AllBlocks.TRACK.asStack(Math.min(64, tracks)), info.hasRequiredTracks)); ingredients.add(Pair.of(new ItemStack(info.trackMaterial.getBlock(), Math.min(64, tracks)), info.hasRequiredTracks));
tracks -= 64; tracks -= 64;
} }

View file

@ -26,6 +26,7 @@ import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringB
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.SidedFilteringBehaviour; import com.simibubi.create.foundation.blockEntity.behaviour.filtering.SidedFilteringBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.INamedIconOptions; import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.INamedIconOptions;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollOptionBehaviour; import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollOptionBehaviour;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.gui.AllIcons; import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Components;
@ -174,7 +175,7 @@ public class BrassTunnelBlockEntity extends BeltTunnelBlockEntity implements IHa
return; return;
if (selectionMode.get() != SelectionMode.SYNCHRONIZE || syncedOutputActive) { if (selectionMode.get() != SelectionMode.SYNCHRONIZE || syncedOutputActive) {
distributionProgress = 10; distributionProgress = AllConfigs.server().logistics.brassTunnelTimer.get();
sendData(); sendData();
} }
return; return;

View file

@ -7,6 +7,7 @@ import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
@ -33,4 +34,8 @@ public class DepotBlockEntity extends SmartBlockEntity {
return depotBehaviour.getItemCapability(cap, side); return depotBehaviour.getItemCapability(cap, side);
return super.getCapability(cap, side); return super.getCapability(cap, side);
} }
public ItemStack getHeldItem() {
return depotBehaviour.getHeldItemStack();
}
} }

View file

@ -9,6 +9,8 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.compat.Mods;
import com.simibubi.create.content.logistics.block.display.source.ComputerDisplaySource;
import com.simibubi.create.content.logistics.block.display.source.DeathCounterDisplaySource; import com.simibubi.create.content.logistics.block.display.source.DeathCounterDisplaySource;
import com.simibubi.create.content.logistics.block.display.source.DisplaySource; import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
import com.simibubi.create.content.logistics.block.display.source.EnchantPowerDisplaySource; import com.simibubi.create.content.logistics.block.display.source.EnchantPowerDisplaySource;
@ -241,5 +243,14 @@ public class AllDisplayBehaviours {
assignBlockEntity(register(Create.asResource("scoreboard_display_source"), new ScoreboardDisplaySource()), BlockEntityType.COMMAND_BLOCK); assignBlockEntity(register(Create.asResource("scoreboard_display_source"), new ScoreboardDisplaySource()), BlockEntityType.COMMAND_BLOCK);
assignBlockEntity(register(Create.asResource("enchant_power_display_source"), new EnchantPowerDisplaySource()), BlockEntityType.ENCHANTING_TABLE); assignBlockEntity(register(Create.asResource("enchant_power_display_source"), new EnchantPowerDisplaySource()), BlockEntityType.ENCHANTING_TABLE);
assignBlock(register(Create.asResource("redstone_power_display_source"), new RedstonePowerDisplaySource()), Blocks.TARGET); assignBlock(register(Create.asResource("redstone_power_display_source"), new RedstonePowerDisplaySource()), Blocks.TARGET);
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
DisplayBehaviour computerDisplaySource = register(Create.asResource("computer_display_source"), new ComputerDisplaySource());
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "wired_modem_full"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_normal"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced"));
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_command"));
});
} }
} }

View file

@ -2,6 +2,11 @@ package com.simibubi.create.content.logistics.block.display;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.content.logistics.block.display.source.DisplaySource; import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
import com.simibubi.create.content.logistics.block.display.target.DisplayTarget; import com.simibubi.create.content.logistics.block.display.target.DisplayTarget;
import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.advancement.AllAdvancements;
@ -18,6 +23,8 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
public class DisplayLinkBlockEntity extends SmartBlockEntity { public class DisplayLinkBlockEntity extends SmartBlockEntity {
@ -33,6 +40,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
private boolean sendPulse; private boolean sendPulse;
public int refreshTicks; public int refreshTicks;
public AbstractComputerBehaviour computerBehaviour;
public DisplayLinkBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public DisplayLinkBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
@ -44,6 +52,12 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
glow.chase(0, 0.5f, Chaser.EXP); glow.chase(0, 0.5f, Chaser.EXP);
} }
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
}
@Override @Override
public void tick() { public void tick() {
super.tick(); super.tick();
@ -61,7 +75,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
} }
refreshTicks++; refreshTicks++;
if (refreshTicks < activeSource.getPassiveRefreshTicks()) if (refreshTicks < activeSource.getPassiveRefreshTicks() || !activeSource.shouldPassiveReset())
return; return;
tickSource(); tickSource();
} }
@ -118,11 +132,6 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
award(AllAdvancements.DISPLAY_LINK); award(AllAdvancements.DISPLAY_LINK);
} }
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
}
@Override @Override
public void writeSafe(CompoundTag tag) { public void writeSafe(CompoundTag tag) {
super.writeSafe(tag); super.writeSafe(tag);
@ -173,6 +182,21 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
sourceConfig = data.copy(); sourceConfig = data.copy();
} }
@NotNull
@Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (computerBehaviour.isPeripheralCap(cap))
return computerBehaviour.getPeripheralCapability();
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
computerBehaviour.removePeripheral();
}
public void target(BlockPos targetPosition) { public void target(BlockPos targetPosition) {
this.targetOffset = targetPosition.subtract(worldPosition); this.targetOffset = targetPosition.subtract(worldPosition);
} }

View file

@ -0,0 +1,33 @@
package com.simibubi.create.content.logistics.block.display.source;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
import com.simibubi.create.foundation.utility.Components;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.MutableComponent;
public class ComputerDisplaySource extends DisplaySource {
@Override
public List<MutableComponent> provideText(DisplayLinkContext context, DisplayTargetStats stats) {
List<MutableComponent> components = new ArrayList<>();
ListTag tag = context.sourceConfig().getList("ComputerSourceList", Tag.TAG_STRING);
for (int i = 0; i < tag.size(); i++) {
components.add(Components.literal(tag.getString(i)));
}
return components;
}
@Override
public boolean shouldPassiveReset() {
return false;
}
}

View file

@ -49,6 +49,10 @@ public abstract class DisplaySource extends DisplayBehaviour {
return 100; return 100;
}; };
public boolean shouldPassiveReset() {
return true;
}
protected String getTranslationKey() { protected String getTranslationKey() {
return id.getPath(); return id.getPath();
} }

View file

@ -123,6 +123,10 @@ public class NixieTubeBlockEntity extends SmartBlockEntity {
customText = Optional.empty(); customText = Optional.empty();
} }
public int getRedstoneStrength() {
return redstoneStrength;
}
// //
@Override @Override

View file

@ -127,6 +127,8 @@ public class FilterItem extends Item implements MenuProvider {
for (Tag inbt : attributes) { for (Tag inbt : attributes) {
CompoundTag compound = (CompoundTag) inbt; CompoundTag compound = (CompoundTag) inbt;
ItemAttribute attribute = ItemAttribute.fromNBT(compound); ItemAttribute attribute = ItemAttribute.fromNBT(compound);
if (attribute == null)
continue;
boolean inverted = compound.getBoolean("Inverted"); boolean inverted = compound.getBoolean("Inverted");
if (count > 3) { if (count > 3) {
list.add(Components.literal("- ...") list.add(Components.literal("- ...")
@ -194,15 +196,19 @@ public class FilterItem extends Item implements MenuProvider {
return test(world, stack, filter, true); return test(world, stack, filter, true);
} }
private static boolean test(Level world, ItemStack stack, ItemStack filter, boolean matchNBT) { public static boolean test(Level world, ItemStack stack, ItemStack filter, boolean matchNBT) {
if (filter.isEmpty()) if (filter.isEmpty())
return true; return true;
if (!(filter.getItem() instanceof FilterItem)) if (!(filter.getItem() instanceof FilterItem))
return (matchNBT ? ItemHandlerHelper.canItemStacksStack(filter, stack) : ItemStack.isSame(filter, stack)); return testDirect(filter, stack, matchNBT);
boolean defaults = !filter.hasTag(); boolean defaults = !filter.hasTag();
if (defaults) {
return testDirect(filter, stack, matchNBT);
}
if (AllItems.FILTER.get() == filter.getItem()) { if (AllItems.FILTER.get() == filter.getItem()) {
ItemStackHandler filterItems = getFilterItems(filter); ItemStackHandler filterItems = getFilterItems(filter);
boolean respectNBT = defaults ? false boolean respectNBT = defaults ? false
@ -211,24 +217,32 @@ public class FilterItem extends Item implements MenuProvider {
boolean blacklist = defaults ? false boolean blacklist = defaults ? false
: filter.getTag() : filter.getTag()
.getBoolean("Blacklist"); .getBoolean("Blacklist");
boolean isEmpty = true;
for (int slot = 0; slot < filterItems.getSlots(); slot++) { for (int slot = 0; slot < filterItems.getSlots(); slot++) {
ItemStack stackInSlot = filterItems.getStackInSlot(slot); ItemStack stackInSlot = filterItems.getStackInSlot(slot);
if (stackInSlot.isEmpty()) if (stackInSlot.isEmpty())
continue; continue;
isEmpty = false;
boolean matches = test(world, stack, stackInSlot, respectNBT); boolean matches = test(world, stack, stackInSlot, respectNBT);
if (matches) if (matches)
return !blacklist; return !blacklist;
} }
if (isEmpty) {
return testDirect(filter, stack, matchNBT);
}
return blacklist; return blacklist;
} }
if (AllItems.ATTRIBUTE_FILTER.get() == filter.getItem()) { if (AllItems.ATTRIBUTE_FILTER.get() == filter.getItem()) {
WhitelistMode whitelistMode = WhitelistMode.values()[defaults ? 0
: filter.getTag()
.getInt("WhitelistMode")];
ListTag attributes = defaults ? new ListTag() ListTag attributes = defaults ? new ListTag()
: filter.getTag() : filter.getTag()
.getList("MatchedAttributes", Tag.TAG_COMPOUND); .getList("MatchedAttributes", Tag.TAG_COMPOUND);
if (attributes.isEmpty()) {
return testDirect(filter, stack, matchNBT);
}
WhitelistMode whitelistMode = WhitelistMode.values()[defaults ? 0
: filter.getTag()
.getInt("WhitelistMode")];
for (Tag inbt : attributes) { for (Tag inbt : attributes) {
CompoundTag compound = (CompoundTag) inbt; CompoundTag compound = (CompoundTag) inbt;
ItemAttribute attribute = ItemAttribute.fromNBT(compound); ItemAttribute attribute = ItemAttribute.fromNBT(compound);
@ -270,7 +284,7 @@ public class FilterItem extends Item implements MenuProvider {
return false; return false;
} }
private static boolean test(Level world, FluidStack stack, ItemStack filter, boolean matchNBT) { public static boolean test(Level world, FluidStack stack, ItemStack filter, boolean matchNBT) {
if (filter.isEmpty()) if (filter.isEmpty())
return true; return true;
if (stack.isEmpty()) if (stack.isEmpty())
@ -313,4 +327,12 @@ public class FilterItem extends Item implements MenuProvider {
return false; return false;
} }
private static boolean testDirect(ItemStack filter, ItemStack stack, boolean matchNBT) {
if (matchNBT) {
return ItemHandlerHelper.canItemStacksStack(filter, stack);
} else {
return ItemStack.isSame(filter, stack);
}
}
} }

View file

@ -10,6 +10,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllRecipeTypes; import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.content.contraptions.processing.InWorldProcessing; import com.simibubi.create.content.contraptions.processing.InWorldProcessing;
@ -78,6 +79,7 @@ public interface ItemAttribute {
return attributeType; return attributeType;
} }
@Nullable
static ItemAttribute fromNBT(CompoundTag nbt) { static ItemAttribute fromNBT(CompoundTag nbt) {
for (ItemAttribute itemAttribute : types) for (ItemAttribute itemAttribute : types)
if (itemAttribute.canRead(nbt)) if (itemAttribute.canRead(nbt))

View file

@ -0,0 +1,361 @@
package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public abstract class AbstractBogeyBlock<T extends AbstractBogeyBlockEntity> extends Block implements IBE<T>, ProperWaterloggedBlock, ISpecialBlockItemRequirement, IWrenchable {
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public BogeySizes.BogeySize size;
public AbstractBogeyBlock(Properties pProperties, BogeySizes.BogeySize size) {
super(pProperties);
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false));
this.size = size;
}
public boolean isOnIncompatibleTrack(Carriage carriage, boolean leading) {
TravellingPoint point = leading ? carriage.getLeadingPoint() : carriage.getTrailingPoint();
CarriageBogey bogey = leading ? carriage.leadingBogey() : carriage.trailingBogey();
return point.edge.getTrackMaterial().trackType != getTrackType(bogey.getStyle());
}
public Set<TrackMaterial.TrackType> getValidPathfindingTypes(BogeyStyle style) {
return ImmutableSet.of(getTrackType(style));
}
public abstract TrackMaterial.TrackType getTrackType(BogeyStyle style);
/**
* Only for internal Create use. If you have your own style set, do not call this method
*/
@Deprecated
public static void registerStandardBogey(ResourceLocation block) {
BOGEYS.add(block);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(AXIS, WATERLOGGED);
super.createBlockStateDefinition(builder);
}
@Override
public BlockState updateShape(BlockState pState, Direction pDirection, BlockState pNeighborState,
LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pNeighborPos) {
updateWater(pLevel, pState, pCurrentPos);
return pState;
}
@Override
public FluidState getFluidState(BlockState pState) {
return fluidState(pState);
}
static final EnumSet<Direction> STICKY_X = EnumSet.of(Direction.EAST, Direction.WEST);
static final EnumSet<Direction> STICKY_Z = EnumSet.of(Direction.SOUTH, Direction.NORTH);
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state) {
return state.getValue(BlockStateProperties.HORIZONTAL_AXIS) == Direction.Axis.X ? STICKY_X : STICKY_Z;
}
public abstract double getWheelPointSpacing();
public abstract double getWheelRadius();
public Vec3 getConnectorAnchorOffset(boolean upsideDown) {
return getConnectorAnchorOffset();
}
/**
* This should be implemented, but not called directly
*/
protected abstract Vec3 getConnectorAnchorOffset();
public boolean allowsSingleBogeyCarriage() {
return true;
}
public abstract BogeyStyle getDefaultStyle();
/**
* Legacy system doesn't capture bogey tile entities when constructing a train
*/
public boolean captureTileEntityForTrain() {
return false;
}
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay, BogeyStyle style, CompoundTag bogeyData) {
if (style == null)
style = getDefaultStyle();
final Optional<BogeyRenderer.CommonRenderer> commonRenderer
= style.getInWorldCommonRenderInstance();
final BogeyRenderer renderer = style.getInWorldRenderInstance(this.getSize());
if (state != null) {
ms.translate(.5f, .5f, .5f);
if (state.getValue(AXIS) == Direction.Axis.X)
ms.mulPose(Vector3f.YP.rotationDegrees(90));
}
ms.translate(0, -1.5 - 1 / 128f, 0);
VertexConsumer vb = buffers.getBuffer(RenderType.cutoutMipped());
if (bogeyData == null)
bogeyData = new CompoundTag();
renderer.render(bogeyData, wheelAngle, ms, light, vb, state == null);
CompoundTag finalBogeyData = bogeyData;
commonRenderer.ifPresent(common ->
common.render(finalBogeyData, wheelAngle, ms, light, vb, state == null));
}
public BogeySizes.BogeySize getSize() {
return this.size;
}
public Direction getBogeyUpDirection() {
return Direction.UP;
}
public boolean isTrackAxisAlongFirstCoordinate(BlockState state) {
return state.getValue(AXIS) == Direction.Axis.X;
}
@Nullable
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst) {
if (upDirection != Direction.UP)
return null;
return defaultBlockState().setValue(AXIS, axisAlongFirst ? Direction.Axis.X : Direction.Axis.Z);
}
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult hit) {
if (level.isClientSide)
return InteractionResult.PASS;
ItemStack stack = player.getItemInHand(hand);
if (!player.isShiftKeyDown() && stack.is(AllItems.WRENCH.get()) && !player.getCooldowns().isOnCooldown(stack.getItem())
&& AllBogeyStyles.BOGEY_STYLES.size() > 1) {
BlockEntity be = level.getBlockEntity(pos);
if (!(be instanceof AbstractBogeyBlockEntity sbte))
return InteractionResult.FAIL;
player.getCooldowns().addCooldown(stack.getItem(), 20);
BogeyStyle currentStyle = sbte.getStyle();
BogeySizes.BogeySize size = getSize();
BogeyStyle style = this.getNextStyle(currentStyle);
if (style == currentStyle)
return InteractionResult.PASS;
Set<BogeySizes.BogeySize> validSizes = style.validSizes();
for (int i = 0; i < BogeySizes.count(); i++) {
if (validSizes.contains(size)) break;
size = size.increment();
}
sbte.setBogeyStyle(style);
CompoundTag defaultData = style.defaultData;
sbte.setBogeyData(sbte.getBogeyData().merge(defaultData));
if (size == getSize()) {
player.displayClientMessage(Lang.translateDirect("bogey.style.updated_style")
.append(": ").append(style.displayName), true);
} else {
CompoundTag oldData = sbte.getBogeyData();
level.setBlock(pos, this.getStateOfSize(sbte, size), 3);
BlockEntity newBlockEntity = level.getBlockEntity(pos);
if (!(newBlockEntity instanceof AbstractBogeyBlockEntity newTileEntity))
return InteractionResult.FAIL;
newTileEntity.setBogeyData(oldData);
player.displayClientMessage(Lang.translateDirect("bogey.style.updated_style_and_size")
.append(": ").append(style.displayName), true);
}
return InteractionResult.CONSUME;
}
return InteractionResult.PASS;
}
/**
* If, instead of using the style-based cycling system you prefer to use separate blocks, return them from this method
*/
protected List<ResourceLocation> getBogeyBlockCycle() {
return BOGEYS;
}
@Override
public BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
Block block = state.getBlock();
List<ResourceLocation> bogeyCycle = getBogeyBlockCycle();
int indexOf = bogeyCycle.indexOf(RegisteredObjects.getKeyOrThrow(block));
if (indexOf == -1)
return state;
int index = (indexOf + 1) % bogeyCycle.size();
Direction bogeyUpDirection = getBogeyUpDirection();
boolean trackAxisAlongFirstCoordinate = isTrackAxisAlongFirstCoordinate(state);
while (index != indexOf) {
ResourceLocation id = bogeyCycle.get(index);
Block newBlock = ForgeRegistries.BLOCKS.getValue(id);
if (newBlock instanceof AbstractBogeyBlock<?> bogey) {
BlockState matchingBogey = bogey.getMatchingBogey(bogeyUpDirection, trackAxisAlongFirstCoordinate);
if (matchingBogey != null)
return copyProperties(state, matchingBogey);
}
index = (index + 1) % bogeyCycle.size();
}
return state;
}
public BlockState getNextSize(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof AbstractBogeyBlockEntity sbte)
return this.getNextSize(sbte);
return level.getBlockState(pos);
}
/**
* List of BlockState Properties to copy between sizes
*/
public List<Property<?>> propertiesToCopy() {
return ImmutableList.of(WATERLOGGED, AXIS);
}
// generic method needed to satisfy Property and BlockState's generic requirements
private <V extends Comparable<V>> BlockState copyProperty(BlockState source, BlockState target, Property<V> property) {
if (source.hasProperty(property) && target.hasProperty(property)) {
return target.setValue(property, source.getValue(property));
}
return target;
}
private BlockState copyProperties(BlockState source, BlockState target) {
for (Property<?> property : propertiesToCopy())
target = copyProperty(source, target, property);
return target;
}
public BlockState getNextSize(AbstractBogeyBlockEntity sbte) {
BogeySizes.BogeySize size = this.getSize();
BogeyStyle style = sbte.getStyle();
BlockState nextBlock = style.getNextBlock(size).defaultBlockState();
nextBlock = copyProperties(sbte.getBlockState(), nextBlock);
return nextBlock;
}
public BlockState getStateOfSize(AbstractBogeyBlockEntity sbte, BogeySizes.BogeySize size) {
BogeyStyle style = sbte.getStyle();
BlockState state = style.getBlockOfSize(size).defaultBlockState();
return copyProperties(sbte.getBlockState(), state);
}
public BogeyStyle getNextStyle(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof AbstractBogeyBlockEntity sbte)
return this.getNextStyle(sbte.getStyle());
return getDefaultStyle();
}
public BogeyStyle getNextStyle(BogeyStyle style) {
Collection<BogeyStyle> allStyles = style.getCycleGroup().values();
if (allStyles.size() <= 1)
return style;
List<BogeyStyle> list = new ArrayList<>(allStyles);
return Iterate.cycleValue(list, style);
}
@Override
public @NotNull BlockState rotate(@NotNull BlockState pState, Rotation pRotation) {
return switch (pRotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> pState.cycle(AXIS);
default -> pState;
};
}
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) {
return new ItemRequirement(ItemRequirement.ItemUseType.CONSUME, AllBlocks.RAILWAY_CASING.asStack());
}
public boolean canBeUpsideDown() {
return false;
}
public boolean isUpsideDown(BlockState state) {
return false;
}
public BlockState getVersion(BlockState base, boolean upsideDown) {
return base;
}
}

View file

@ -26,6 +26,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
@ -42,6 +43,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public Couple<Integer> smoothing; public Couple<Integer> smoothing;
public boolean primary; public boolean primary;
public boolean hasGirder; public boolean hasGirder;
protected TrackMaterial trackMaterial;
// runtime // runtime
@ -58,19 +60,20 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
private AABB bounds; private AABB bounds;
public BezierConnection(Couple<BlockPos> positions, Couple<Vec3> starts, Couple<Vec3> axes, Couple<Vec3> normals, public BezierConnection(Couple<BlockPos> positions, Couple<Vec3> starts, Couple<Vec3> axes, Couple<Vec3> normals,
boolean primary, boolean girder) { boolean primary, boolean girder, TrackMaterial material) {
tePositions = positions; tePositions = positions;
this.starts = starts; this.starts = starts;
this.axes = axes; this.axes = axes;
this.normals = normals; this.normals = normals;
this.primary = primary; this.primary = primary;
this.hasGirder = girder; this.hasGirder = girder;
this.trackMaterial = material;
resolved = false; resolved = false;
} }
public BezierConnection secondary() { public BezierConnection secondary() {
BezierConnection bezierConnection = BezierConnection bezierConnection = new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(),
new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), !primary, hasGirder); normals.swap(), !primary, hasGirder, trackMaterial);
if (smoothing != null) if (smoothing != null)
bezierConnection.smoothing = smoothing.swap(); bezierConnection.smoothing = smoothing.swap();
return bezierConnection; return bezierConnection;
@ -80,6 +83,26 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
return secondary().secondary(); return secondary().secondary();
} }
private static boolean coupleEquals(Couple<?> a, Couple<?> b) {
return (a.getFirst()
.equals(b.getFirst())
&& a.getSecond()
.equals(b.getSecond()))
|| (a.getFirst() instanceof Vec3 aFirst && a.getSecond() instanceof Vec3 aSecond
&& b.getFirst() instanceof Vec3 bFirst && b.getSecond() instanceof Vec3 bSecond
&& aFirst.closerThan(bFirst, 1e-6) && aSecond.closerThan(bSecond, 1e-6));
}
public boolean equalsSansMaterial(BezierConnection other) {
return equalsSansMaterialInner(other) || equalsSansMaterialInner(other.secondary());
}
private boolean equalsSansMaterialInner(BezierConnection other) {
return this == other || (other != null && coupleEquals(this.tePositions, other.tePositions)
&& coupleEquals(this.starts, other.starts) && coupleEquals(this.axes, other.axes)
&& coupleEquals(this.normals, other.normals) && this.hasGirder == other.hasGirder);
}
public BezierConnection(CompoundTag compound, BlockPos localTo) { public BezierConnection(CompoundTag compound, BlockPos localTo) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos) this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(b -> b.offset(localTo)), .map(b -> b.offset(localTo)),
@ -87,7 +110,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.map(v -> v.add(Vec3.atLowerCornerOf(localTo))), .map(v -> v.add(Vec3.atLowerCornerOf(localTo))),
Couple.deserializeEach(compound.getList("Axes", Tag.TAG_COMPOUND), VecHelper::readNBTCompound), Couple.deserializeEach(compound.getList("Axes", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
Couple.deserializeEach(compound.getList("Normals", Tag.TAG_COMPOUND), VecHelper::readNBTCompound), Couple.deserializeEach(compound.getList("Normals", Tag.TAG_COMPOUND), VecHelper::readNBTCompound),
compound.getBoolean("Primary"), compound.getBoolean("Girder")); compound.getBoolean("Primary"), compound.getBoolean("Girder"), TrackMaterial.deserialize(compound.getString("Material")));
if (compound.contains("Smoothing")) if (compound.contains("Smoothing"))
smoothing = smoothing =
@ -105,6 +128,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound)); compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound));
compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound)); compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound));
compound.put("Normals", normals.serializeEach(VecHelper::writeNBTCompound)); compound.put("Normals", normals.serializeEach(VecHelper::writeNBTCompound));
compound.putString("Material", getMaterial().id.toString());
if (smoothing != null) if (smoothing != null)
compound.put("Smoothing", smoothing.serializeEach(NBTHelper::intToCompound)); compound.put("Smoothing", smoothing.serializeEach(NBTHelper::intToCompound));
@ -115,7 +139,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public BezierConnection(FriendlyByteBuf buffer) { public BezierConnection(FriendlyByteBuf buffer) {
this(Couple.create(buffer::readBlockPos), Couple.create(() -> VecHelper.read(buffer)), this(Couple.create(buffer::readBlockPos), Couple.create(() -> VecHelper.read(buffer)),
Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)), Couple.create(() -> VecHelper.read(buffer)),
buffer.readBoolean(), buffer.readBoolean()); buffer.readBoolean(), buffer.readBoolean(), TrackMaterial.deserialize(buffer.readUtf()));
if (buffer.readBoolean()) if (buffer.readBoolean())
smoothing = Couple.create(buffer::readVarInt); smoothing = Couple.create(buffer::readVarInt);
} }
@ -127,6 +151,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
normals.forEach(v -> VecHelper.write(v, buffer)); normals.forEach(v -> VecHelper.write(v, buffer));
buffer.writeBoolean(primary); buffer.writeBoolean(primary);
buffer.writeBoolean(hasGirder); buffer.writeBoolean(hasGirder);
buffer.writeUtf(getMaterial().id.toString());
buffer.writeBoolean(smoothing != null); buffer.writeBoolean(smoothing != null);
if (smoothing != null) if (smoothing != null)
smoothing.forEach(buffer::writeVarInt); smoothing.forEach(buffer::writeVarInt);
@ -333,7 +358,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
Inventory inv = player.getInventory(); Inventory inv = player.getInventory();
int tracks = getTrackItemCost(); int tracks = getTrackItemCost();
while (tracks > 0) { while (tracks > 0) {
inv.placeItemBackInInventory(AllBlocks.TRACK.asStack(Math.min(64, tracks))); inv.placeItemBackInInventory(new ItemStack(getMaterial().getBlock(), Math.min(64, tracks)));
tracks -= 64; tracks -= 64;
} }
int girders = getGirderItemCost(); int girders = getGirderItemCost();
@ -361,7 +386,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
continue; continue;
Vec3 v = VecHelper.offsetRandomly(segment.position, level.random, .125f) Vec3 v = VecHelper.offsetRandomly(segment.position, level.random, .125f)
.add(origin); .add(origin);
ItemEntity entity = new ItemEntity(level, v.x, v.y, v.z, AllBlocks.TRACK.asStack()); ItemEntity entity = new ItemEntity(level, v.x, v.y, v.z, getMaterial().asStack());
entity.setDefaultPickUpDelay(); entity.setDefaultPickUpDelay();
level.addFreshEntity(entity); level.addFreshEntity(entity);
if (!hasGirder) if (!hasGirder)
@ -375,7 +400,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
} }
public void spawnDestroyParticles(Level level) { public void spawnDestroyParticles(Level level) {
BlockParticleOption data = new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.TRACK.getDefaultState()); BlockParticleOption data = new BlockParticleOption(ParticleTypes.BLOCK, getMaterial().getBlock().defaultBlockState());
BlockParticleOption girderData = BlockParticleOption girderData =
new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.METAL_GIRDER.getDefaultState()); new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.METAL_GIRDER.getDefaultState());
if (!(level instanceof ServerLevel slevel)) if (!(level instanceof ServerLevel slevel))
@ -393,6 +418,14 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
} }
} }
public TrackMaterial getMaterial() {
return trackMaterial;
}
public void setMaterial(TrackMaterial material) {
trackMaterial = material;
}
public static class Segment { public static class Segment {
public int index; public int index;

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains; package com.simibubi.create.content.logistics.trains;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlockEntity; import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer; import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
@ -17,11 +17,11 @@ public class BogeyBlockEntityRenderer<T extends BlockEntity> extends SafeBlockEn
protected void renderSafe(T be, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light, protected void renderSafe(T be, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) { int overlay) {
BlockState blockState = be.getBlockState(); BlockState blockState = be.getBlockState();
float angle = 0; if (be instanceof AbstractBogeyBlockEntity sbte) {
if (be instanceof StandardBogeyBlockEntity sbte) float angle = sbte.getVirtualAngle(partialTicks);
angle = sbte.getVirtualAngle(partialTicks); if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey)
if (blockState.getBlock()instanceof IBogeyBlock bogey) bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay, sbte.getStyle(), sbte.getBogeyData());
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay); }
} }
} }

View file

@ -0,0 +1,309 @@
package com.simibubi.create.content.logistics.trains;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public abstract class BogeyRenderer {
Map<String, ModelData[]> contraptionModelData = new HashMap<>();
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* partial model
*
* @param model The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inInstancedContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?>[] getTransformsFromPartial(PartialModel model, PoseStack ms, boolean inInstancedContraption, int size) {
return (inInstancedContraption) ? transformContraptionModelData(keyFromModel(model), ms) : createModelData(model, size);
}
/**
* A common interface for getting transform data for both in-world and in-contraption model data safely from a
* blockstate
*
* @param state The key for the model data to instantiate or retrieve
* @param ms The posestack used for contraption model data
* @param inContraption The type of model needed
* @param size The amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?>[] getTransformsFromBlockState(BlockState state, PoseStack ms, boolean inContraption, int size) {
return inContraption ? transformContraptionModelData(keyFromModel(state), ms) : createModelData(state, size);
}
/**
* Used for calling both in-world and in-contraption rendering
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
* @param light (Optional) Light used for in-world rendering
* @param vb (Optional) Vertex Consumer used for in-world rendering
*/
@OnlyIn(Dist.CLIENT)
public abstract void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption);
/**
* Used for calling in-contraption rendering ensuring that falsey data is handled correctly
*
* @param bogeyData Custom data stored on the bogey able to be used for rendering
* @param wheelAngle The angle of the wheel
* @param ms The posestack to render to
*/
@OnlyIn(Dist.CLIENT)
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms) {
this.render(bogeyData, wheelAngle, ms, 0, null, true);
}
public abstract BogeySizes.BogeySize getSize();
/**
* Used to collect Contraption Model Data for in-contraption rendering, should not be utilised directly when
* rendering to prevent render type mismatch
*
* @param key The key used to access the model
* @param ms Posestack of the contraption to bind the model data to
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] transformContraptionModelData(String key, PoseStack ms) {
ModelData[] modelData = contraptionModelData.get(key);
Arrays.stream(modelData).forEach(modelDataElement -> modelDataElement.setTransform(ms));
return modelData;
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param model The partial model of the model data ot be made
* @param size The Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] createModelData(PartialModel model, int size) {
BlockState air = Blocks.AIR.defaultBlockState();
SuperByteBuffer[] data = { CachedBufferer.partial(model, air) };
return expandArrayToLength(data, size);
}
/**
* Used for in world rendering, creates a set count of model data to be rendered, allowing for a generic response
* when rendering multiple models both in-world and in-contraption for example, with wheels
*
* @param state The state of the model data to be made
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] createModelData(BlockState state, int size) {
SuperByteBuffer[] data = { CachedBufferer.block(state) };
return expandArrayToLength(data, size);
}
/**
* Utility function to clone in-world models to a set size to allow for common handling of rendering with multiple
* instances of the same model for example with wheels
*
* @param data An in-world model to be replicated
* @param size Amount of models needed
* @return A generic transform which can be used for both in-world and in-contraption models
*/
private Transform<?>[] expandArrayToLength(SuperByteBuffer[] data, int size) {
return Arrays.stream(Collections.nCopies(size, data).toArray())
.flatMap(inner -> Arrays.stream((SuperByteBuffer[]) inner))
.toArray(SuperByteBuffer[]::new);
}
/**
* Helper function to collect or create a single model from a partial model used for both in-world and
* in-contraption rendering
*
* @param model The key of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inInstancedContraption Type of rendering required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?> getTransformFromPartial(PartialModel model, PoseStack ms, boolean inInstancedContraption) {
BlockState air = Blocks.AIR.defaultBlockState();
return inInstancedContraption ? contraptionModelData.get(keyFromModel(model))[0].setTransform(ms)
: CachedBufferer.partial(model, air);
}
/**
* A common interface for getting transform data for blockstates, for a single model
*
* @param state The state of the model to be collected or instantiated
* @param ms Posestack to bind the model to if it is within a contraption
* @param inContraption Type of model required
* @return A generic transform which can be used for both in-world and in-contraption models
*/
public Transform<?> getTransformFromBlockState(BlockState state, PoseStack ms, boolean inContraption) {
return (inContraption) ? contraptionModelData.get(keyFromModel(state))[0].setTransform(ms)
: CachedBufferer.block(state);
}
/**
* Provides render implementations a point in setup to instantiate all model data to be needed
*
* @param materialManager The material manager
*/
@OnlyIn(Dist.CLIENT)
public abstract void initialiseContraptionModelData(MaterialManager materialManager);
/**
* Creates instances of models for in-world rendering to a set length from a provided partial model
*
* @param materialManager The material manager
* @param model Partial model to be instanced
* @param count Amount of models neeeded
*/
public void createModelInstances(MaterialManager materialManager, PartialModel model, int count) {
ModelData[] modelData = new ModelData[count];
materialManager.defaultSolid().material(Materials.TRANSFORMED)
.getModel(model).createInstances(modelData);
contraptionModelData.put(keyFromModel(model), modelData);
}
/**
* Creates instances of models for in-contraption rendering to a set length from a provided blockstate
*
* @param materialManager The material manager
* @param state Blockstate of the model to be created
* @param count Amount of models needed
*/
public void createModelInstances(MaterialManager materialManager, BlockState state, int count) {
ModelData[] modelData = new ModelData[count];
materialManager.defaultSolid().material(Materials.TRANSFORMED)
.getModel(state).createInstances(modelData);
contraptionModelData.put(keyFromModel(state), modelData);
}
/**
* Creates a single instance of models for in-contraption rendering from a provided blockstate
*
* @param materialManager The material manager
* @param state Blockstate of the model to be created
*/
public void createModelInstance(MaterialManager materialManager, BlockState state) {
this.createModelInstances(materialManager, state, 1);
}
/**
* Helper function to create a single model instance for in-contraption rendering
*
* @param materialManager The material manager
* @param models The type of model to create instances of
*/
public void createModelInstances(MaterialManager materialManager, PartialModel... models) {
for (PartialModel model : models)
createModelInstances(materialManager, model, 1);
}
/**
* Handles scale for all model data and renders non contraption model data
*
* @param b The model data itself
* @param ms Pose stack to render to
* @param light light level of the scene
* @param vb Vertex Consumber to render to
* @param <B> Generic alias for both contraption and in-world model data
*/
public static <B extends Transform<?>> void finalize(B b, PoseStack ms, int light, @Nullable VertexConsumer vb) {
b.scale(1 - 1/512f);
if (b instanceof SuperByteBuffer byteBuf && vb != null)
byteBuf.light(light).renderInto(ms, vb);
}
/**
* Automatic handling for setting empty transforms for all model data
*
*/
public void emptyTransforms() {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.setEmptyTransform();
}
/**
* Automatic handling for updating all model data's light
*
* @param blockLight the blocklight to be applied
* @param skyLight the skylight to be applied
*/
public void updateLight(int blockLight, int skyLight) {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.setBlockLight(blockLight).setSkyLight(skyLight);
}
/**
* Automatic handling for clearing all model data of a contraption
*
*/
public void remove() {
for (ModelData[] data : contraptionModelData.values())
for (ModelData model : data)
model.delete();
contraptionModelData.clear();
}
/**
* Create a model key from a partial model, so it can be easily accessed
*
* @param partialModel the model we want a unique key for
* @return Key of the model
*/
private String keyFromModel(PartialModel partialModel) {
return partialModel.getLocation().toString();
}
/**
* Create a model key from a blockstate, so it can be easily accessed
*
* @param state Blockstate of the model
* @return Key of the model
*/
private String keyFromModel(BlockState state) {
return state.toString();
}
public static abstract class CommonRenderer extends BogeyRenderer {
@Override
public BogeySizes.BogeySize getSize() {
return null;
}
}
}

View file

@ -0,0 +1,70 @@
package com.simibubi.create.content.logistics.trains;
import com.simibubi.create.Create;
import net.minecraft.resources.ResourceLocation;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public class BogeySizes {
private static final Collection<BogeySize> BOGEY_SIZES = new HashSet<>();
public static final BogeySize SMALL = new BogeySize(Create.ID, "small", 6.5f / 16f);
public static final BogeySize LARGE = new BogeySize(Create.ID, "large", 12.5f / 16f);
static {
BOGEY_SIZES.add(SMALL);
BOGEY_SIZES.add(LARGE);
}
public static BogeySize addSize(String modId, String name, float size) {
ResourceLocation location = new ResourceLocation(modId, name);
return addSize(location, size);
}
public static BogeySize addSize(ResourceLocation location, float size) {
BogeySize customSize = new BogeySize(location, size);
BOGEY_SIZES.add(customSize);
return customSize;
}
public static List<BogeySize> getAllSizesSmallToLarge() {
return BOGEY_SIZES.stream()
.sorted(Comparator.comparing(BogeySize::wheelRadius))
.collect(Collectors.toList());
}
public static List<BogeySize> getAllSizesLargeToSmall() {
List<BogeySize> sizes = getAllSizesSmallToLarge();
Collections.reverse(sizes);
return sizes;
}
public static int count() {
return BOGEY_SIZES.size();
}
public record BogeySize(ResourceLocation location, float wheelRadius) {
public BogeySize(String modId, String name, float wheelRadius) {
this(new ResourceLocation(modId, name), wheelRadius);
}
public BogeySize increment() {
List<BogeySize> values = getAllSizesSmallToLarge();
int ordinal = values.indexOf(this);
return values.get((ordinal + 1) % values.size());
}
public boolean is(BogeySize size) {
return size.location == this.location;
}
}
public static void init() {
}
}

View file

@ -265,13 +265,17 @@ public class GlobalRailwayManager {
public void clientTick() { public void clientTick() {
if (isTrackGraphDebugActive()) if (isTrackGraphDebugActive())
for (TrackGraph trackGraph : trackNetworks.values()) for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.debugViewGraph(trackGraph); TrackGraphVisualizer.debugViewGraph(trackGraph, isTrackGraphDebugExtended());
} }
private static boolean isTrackGraphDebugActive() { private static boolean isTrackGraphDebugActive() {
return KineticDebugger.isF3DebugModeActive() && AllConfigs.client().showTrackGraphOnF3.get(); return KineticDebugger.isF3DebugModeActive() && AllConfigs.client().showTrackGraphOnF3.get();
} }
private static boolean isTrackGraphDebugExtended() {
return AllConfigs.client().showExtendedTrackGraphOnF3.get();
}
public GlobalRailwayManager sided(LevelAccessor level) { public GlobalRailwayManager sided(LevelAccessor level) {
if (level != null && !level.isClientSide()) if (level != null && !level.isClientSide())
return this; return this;

View file

@ -1,91 +0,0 @@
package com.simibubi.create.content.logistics.trains;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.logistics.trains.entity.BogeyInstance;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
public interface IBogeyBlock extends IWrenchable {
static final List<ResourceLocation> BOGEYS = new ArrayList<>();
public static void register(ResourceLocation block) {
BOGEYS.add(block);
}
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state);
public double getWheelPointSpacing();
public double getWheelRadius();
public boolean allowsSingleBogeyCarriage();
public Vec3 getConnectorAnchorOffset();
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay);
@OnlyIn(Dist.CLIENT)
public BogeyInstance createInstance(MaterialManager materialManager, CarriageBogey bogey);
public default Direction getBogeyUpDirection() {
return Direction.UP;
}
public boolean isTrackAxisAlongFirstCoordinate(BlockState state);
@Nullable
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst);
@Override
default BlockState getRotatedBlockState(BlockState state, Direction targetedFace) {
Block block = state.getBlock();
int indexOf = BOGEYS.indexOf(RegisteredObjects.getKeyOrThrow(block));
if (indexOf == -1)
return state;
int index = (indexOf + 1) % BOGEYS.size();
Direction bogeyUpDirection = getBogeyUpDirection();
boolean trackAxisAlongFirstCoordinate = isTrackAxisAlongFirstCoordinate(state);
while (index != indexOf) {
ResourceLocation id = BOGEYS.get(index);
Block newBlock = ForgeRegistries.BLOCKS.getValue(id);
if (newBlock instanceof IBogeyBlock bogey) {
BlockState matchingBogey = bogey.getMatchingBogey(bogeyUpDirection, trackAxisAlongFirstCoordinate);
if (matchingBogey != null)
return matchingBogey.hasProperty(WATERLOGGED)
? matchingBogey.setValue(WATERLOGGED, state.getValue(WATERLOGGED))
: matchingBogey;
}
index = (index + 1) % BOGEYS.size();
}
return state;
}
}

View file

@ -25,6 +25,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
@ -85,20 +86,38 @@ public interface ITrackBlock {
Function<Vec3, Integer> yOffsetFactory = v -> getYOffsetAt(world, pos, state, v); Function<Vec3, Integer> yOffsetFactory = v -> getYOffsetAt(world, pos, state, v);
addToListIfConnected(connectedTo, list, offsetFactory, b -> shape.getNormal(), dimensionFactory, addToListIfConnected(connectedTo, list, offsetFactory, b -> shape.getNormal(), dimensionFactory,
yOffsetFactory, axis, null); yOffsetFactory, axis, null, (b, v) -> getMaterialSimple(world, v));
}); });
return list; return list;
} }
public static TrackMaterial getMaterialSimple(BlockGetter world, Vec3 pos) {
return getMaterialSimple(world, pos, TrackMaterial.ANDESITE);
}
public static TrackMaterial getMaterialSimple(BlockGetter world, Vec3 pos, TrackMaterial defaultMaterial) {
if (defaultMaterial == null)
defaultMaterial = TrackMaterial.ANDESITE;
if (world != null) {
Block block = world.getBlockState(new BlockPos(pos)).getBlock();
if (block instanceof ITrackBlock track) {
return track.getMaterial();
}
}
return defaultMaterial;
}
public static void addToListIfConnected(@Nullable TrackNodeLocation fromEnd, Collection<DiscoveredLocation> list, public static void addToListIfConnected(@Nullable TrackNodeLocation fromEnd, Collection<DiscoveredLocation> list,
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory, BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
Function<Boolean, ResourceKey<Level>> dimensionFactory, Function<Vec3, Integer> yOffsetFactory, Vec3 axis, Function<Boolean, ResourceKey<Level>> dimensionFactory, Function<Vec3, Integer> yOffsetFactory, Vec3 axis,
BezierConnection viaTurn) { BezierConnection viaTurn, BiFunction<Boolean, Vec3, TrackMaterial> materialFactory) {
Vec3 firstOffset = offsetFactory.apply(0.5d, true); Vec3 firstOffset = offsetFactory.apply(0.5d, true);
DiscoveredLocation firstLocation = DiscoveredLocation firstLocation =
new DiscoveredLocation(dimensionFactory.apply(true), firstOffset).viaTurn(viaTurn) new DiscoveredLocation(dimensionFactory.apply(true), firstOffset).viaTurn(viaTurn)
.materialA(materialFactory.apply(true, offsetFactory.apply(0.0d, true)))
.materialB(materialFactory.apply(true, offsetFactory.apply(1.0d, true)))
.withNormal(normalFactory.apply(true)) .withNormal(normalFactory.apply(true))
.withDirection(axis) .withDirection(axis)
.withYOffset(yOffsetFactory.apply(firstOffset)); .withYOffset(yOffsetFactory.apply(firstOffset));
@ -106,6 +125,8 @@ public interface ITrackBlock {
Vec3 secondOffset = offsetFactory.apply(0.5d, false); Vec3 secondOffset = offsetFactory.apply(0.5d, false);
DiscoveredLocation secondLocation = DiscoveredLocation secondLocation =
new DiscoveredLocation(dimensionFactory.apply(false), secondOffset).viaTurn(viaTurn) new DiscoveredLocation(dimensionFactory.apply(false), secondOffset).viaTurn(viaTurn)
.materialA(materialFactory.apply(false, offsetFactory.apply(0.0d, false)))
.materialB(materialFactory.apply(false, offsetFactory.apply(1.0d, false)))
.withNormal(normalFactory.apply(false)) .withNormal(normalFactory.apply(false))
.withDirection(axis) .withDirection(axis)
.withYOffset(yOffsetFactory.apply(secondOffset)); .withYOffset(yOffsetFactory.apply(secondOffset));
@ -169,4 +190,6 @@ public interface ITrackBlock {
.normalize()) < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE); .normalize()) < 0 ? AxisDirection.POSITIVE : AxisDirection.NEGATIVE);
} }
TrackMaterial getMaterial();
} }

View file

@ -0,0 +1,134 @@
package com.simibubi.create.content.logistics.trains;
import static com.simibubi.create.AllPartialModels.BOGEY_DRIVE;
import static com.simibubi.create.AllPartialModels.BOGEY_FRAME;
import static com.simibubi.create.AllPartialModels.BOGEY_PIN;
import static com.simibubi.create.AllPartialModels.BOGEY_PISTON;
import static com.simibubi.create.AllPartialModels.LARGE_BOGEY_WHEELS;
import static com.simibubi.create.AllPartialModels.SMALL_BOGEY_WHEELS;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
public class StandardBogeyRenderer {
public static class CommonStandardBogeyRenderer extends BogeyRenderer.CommonRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), 2);
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?>[] shafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), ms, inInstancedContraption, 2);
for (int i : Iterate.zeroAndOne) {
shafts[i].translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre();
finalize(shafts[i], ms, light, vb);
}
}
}
public static class SmallStandardBogeyRenderer extends BogeyRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, SMALL_BOGEY_WHEELS, 2);
createModelInstances(materialManager, BOGEY_FRAME);
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.SMALL;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?> transform = getTransformFromPartial(BOGEY_FRAME, ms, inInstancedContraption);
finalize(transform, ms, light, vb);
Transform<?>[] wheels = getTransformsFromPartial(SMALL_BOGEY_WHEELS, ms, inInstancedContraption, 2);
for (int side : Iterate.positiveAndNegative) {
if (!inInstancedContraption)
ms.pushPose();
Transform<?> wheel = wheels[(side + 1)/2];
wheel.translate(0, 12 / 16f, side)
.rotateX(wheelAngle);
finalize(wheel, ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
}
}
}
public static class LargeStandardBogeyRenderer extends BogeyRenderer {
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
createModelInstances(materialManager, LARGE_BOGEY_WHEELS, BOGEY_DRIVE, BOGEY_PISTON, BOGEY_PIN);
createModelInstances(materialManager, AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), 2);
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.LARGE;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
boolean inInstancedContraption = vb == null;
Transform<?>[] secondaryShafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), ms, inInstancedContraption, 2);
for (int i : Iterate.zeroAndOne) {
Transform<?> secondShaft = secondaryShafts[i];
secondShaft.translate(-.5f, .25f, .5f + i * -2)
.centre()
.rotateX(wheelAngle)
.unCentre();
finalize(secondShaft, ms, light, vb);
}
Transform<?> bogeyDrive = getTransformFromPartial(BOGEY_DRIVE, ms, inInstancedContraption);
finalize(bogeyDrive, ms, light, vb);
Transform<?> bogeyPiston = getTransformFromPartial(BOGEY_PISTON, ms, inInstancedContraption)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)));
finalize(bogeyPiston, ms, light, vb);
if (!inInstancedContraption)
ms.pushPose();
Transform<?> bogeyWheels = getTransformFromPartial(LARGE_BOGEY_WHEELS, ms, inInstancedContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle);
finalize(bogeyWheels, ms, light, vb);
Transform<?> bogeyPin = getTransformFromPartial(BOGEY_PIN, ms, inInstancedContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle);
finalize(bogeyPin, ms, light, vb);
if (!inInstancedContraption)
ms.popPose();
}
}
}

View file

@ -24,13 +24,19 @@ public class TrackEdge {
BezierConnection turn; BezierConnection turn;
EdgeData edgeData; EdgeData edgeData;
boolean interDimensional; boolean interDimensional;
TrackMaterial trackMaterial;
public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn) { public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn, TrackMaterial trackMaterial) {
this.interDimensional = !node1.location.dimension.equals(node2.location.dimension); this.interDimensional = !node1.location.dimension.equals(node2.location.dimension);
this.edgeData = new EdgeData(this); this.edgeData = new EdgeData(this);
this.node1 = node1; this.node1 = node1;
this.node2 = node2; this.node2 = node2;
this.turn = turn; this.turn = turn;
this.trackMaterial = trackMaterial;
}
public TrackMaterial getTrackMaterial() {
return trackMaterial;
} }
public boolean isTurn() { public boolean isTurn() {
@ -230,13 +236,15 @@ public class TrackEdge {
public CompoundTag write(DimensionPalette dimensions) { public CompoundTag write(DimensionPalette dimensions) {
CompoundTag baseCompound = isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag(); CompoundTag baseCompound = isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag();
baseCompound.put("Signals", edgeData.write(dimensions)); baseCompound.put("Signals", edgeData.write(dimensions));
baseCompound.putString("Material", getTrackMaterial().id.toString());
return baseCompound; return baseCompound;
} }
public static TrackEdge read(TrackNode node1, TrackNode node2, CompoundTag tag, TrackGraph graph, public static TrackEdge read(TrackNode node1, TrackNode node2, CompoundTag tag, TrackGraph graph,
DimensionPalette dimensions) { DimensionPalette dimensions) {
TrackEdge trackEdge = TrackEdge trackEdge =
new TrackEdge(node1, node2, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null); new TrackEdge(node1, node2, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null,
TrackMaterial.deserialize(tag.getString("Material")));
trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph, dimensions); trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph, dimensions);
return trackEdge; return trackEdge;
} }

View file

@ -393,14 +393,15 @@ public class TrackGraph {
return connectionsFrom.get(nodes.getSecond()); return connectionsFrom.get(nodes.getSecond());
} }
public void connectNodes(LevelAccessor reader, TrackNodeLocation location, TrackNodeLocation location2, public void connectNodes(LevelAccessor reader, DiscoveredLocation location, DiscoveredLocation location2,
@Nullable BezierConnection turn) { @Nullable BezierConnection turn) {
TrackNode node1 = nodes.get(location); TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2); TrackNode node2 = nodes.get(location2);
boolean bezier = turn != null; boolean bezier = turn != null;
TrackEdge edge = new TrackEdge(node1, node2, turn); TrackMaterial material = bezier ? turn.getMaterial() : location2.materialA;
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null); TrackEdge edge = new TrackEdge(node1, node2, turn, material);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null, material);
for (TrackGraph graph : Create.RAILWAYS.trackNetworks.values()) { for (TrackGraph graph : Create.RAILWAYS.trackNetworks.values()) {
for (TrackNode otherNode1 : graph.nodes.values()) { for (TrackNode otherNode1 : graph.nodes.values()) {

View file

@ -60,7 +60,7 @@ public class TrackGraphSync {
public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) { public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) {
flushGraphPacket(graph); flushGraphPacket(graph);
currentGraphSyncPacket.addedEdges currentGraphSyncPacket.addedEdges
.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge.getTurn())); .add(Pair.of(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge.getTrackMaterial()), edge.getTurn()));
currentPayload++; currentPayload++;
} }
@ -82,7 +82,7 @@ public class TrackGraphSync {
if (currentGraphSyncPacket.addedNodes.remove(nodeId) == null) if (currentGraphSyncPacket.addedNodes.remove(nodeId) == null)
currentGraphSyncPacket.removedNodes.add(nodeId); currentGraphSyncPacket.removedNodes.add(nodeId);
currentGraphSyncPacket.addedEdges.removeIf(pair -> { currentGraphSyncPacket.addedEdges.removeIf(pair -> {
Couple<Integer> ids = pair.getFirst(); Couple<Integer> ids = pair.getFirst().getFirst();
return ids.getFirst() return ids.getFirst()
.intValue() == nodeId .intValue() == nodeId
|| ids.getSecond() || ids.getSecond()
@ -156,7 +156,7 @@ public class TrackGraphSync {
graph.connectionsByNode.get(node) graph.connectionsByNode.get(node)
.forEach((node2, edge) -> { .forEach((node2, edge) -> {
Couple<Integer> key = Couple.create(node.getNetId(), node2.getNetId()); Couple<Integer> key = Couple.create(node.getNetId(), node2.getNetId());
currentPacket.addedEdges.add(Pair.of(key, edge.getTurn())); currentPacket.addedEdges.add(Pair.of(Pair.of(key, edge.getTrackMaterial()), edge.getTurn()));
currentPacket.syncEdgeData(node, node2, edge); currentPacket.syncEdgeData(node, node2, edge);
}); });

View file

@ -22,7 +22,7 @@ import net.minecraft.world.phys.Vec3;
public class TrackGraphSyncPacket extends TrackGraphPacket { public class TrackGraphSyncPacket extends TrackGraphPacket {
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes; Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, BezierConnection>> addedEdges; List<Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection>> addedEdges;
List<Integer> removedNodes; List<Integer> removedNodes;
List<TrackEdgePoint> addedEdgePoints; List<TrackEdgePoint> addedEdgePoints;
List<UUID> removedEdgePoints; List<UUID> removedEdgePoints;
@ -79,7 +79,7 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
size = buffer.readVarInt(); size = buffer.readVarInt();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
addedEdges.add( addedEdges.add(
Pair.of(Couple.create(buffer::readVarInt), buffer.readBoolean() ? new BezierConnection(buffer) : null)); Pair.of(Pair.of(Couple.create(buffer::readVarInt), TrackMaterial.deserialize(buffer.readUtf())), buffer.readBoolean() ? new BezierConnection(buffer) : null));
size = buffer.readVarInt(); size = buffer.readVarInt();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
@ -134,8 +134,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
buffer.writeVarInt(addedEdges.size()); buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> { addedEdges.forEach(pair -> {
pair.getFirst() pair.getFirst().getFirst()
.forEach(buffer::writeVarInt); .forEach(buffer::writeVarInt);
buffer.writeUtf(pair.getFirst().getSecond().id.toString());
BezierConnection turn = pair.getSecond(); BezierConnection turn = pair.getSecond();
buffer.writeBoolean(turn != null); buffer.writeBoolean(turn != null);
if (turn != null) if (turn != null)
@ -192,13 +193,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond()); graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond());
} }
for (Pair<Couple<Integer>, BezierConnection> pair : addedEdges) { for (Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst() Couple<TrackNode> nodes = pair.getFirst().getFirst()
.map(graph::getNode); .map(graph::getNode);
TrackNode node1 = nodes.getFirst(); TrackNode node1 = nodes.getFirst();
TrackNode node2 = nodes.getSecond(); TrackNode node2 = nodes.getSecond();
if (node1 != null && node2 != null) if (node1 != null && node2 != null)
graph.putConnection(node1, node2, new TrackEdge(node1, node2, pair.getSecond())); graph.putConnection(node1, node2, new TrackEdge(node1, node2, pair.getSecond(), pair.getFirst().getSecond()));
} }
for (TrackEdgePoint edgePoint : addedEdgePoints) for (TrackEdgePoint edgePoint : addedEdgePoints)

View file

@ -37,7 +37,6 @@ public class TrackGraphVisualizer {
Vec3 camera = cameraEntity.getEyePosition(); Vec3 camera = cameraEntity.getEyePosition();
Outliner outliner = CreateClient.OUTLINER; Outliner outliner = CreateClient.OUTLINER;
boolean ctrl = false; // AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups; Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.sided(null).signalEdgeGroups;
float width = 1 / 8f; float width = 1 / 8f;
@ -213,7 +212,7 @@ public class TrackGraphVisualizer {
} }
} }
public static void debugViewGraph(TrackGraph graph) { public static void debugViewGraph(TrackGraph graph, boolean extended) {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
Entity cameraEntity = mc.cameraEntity; Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null) if (cameraEntity == null)
@ -266,6 +265,17 @@ public class TrackGraphVisualizer {
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0); yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
if (!edge.isTurn()) { if (!edge.isTurn()) {
if (extended) {
Vec3 materialPos = edge.getPosition(graph, 0.5)
.add(0, 1, 0);
CreateClient.OUTLINER.showItem(Pair.of(edge, edge.edgeData), materialPos,
edge.getTrackMaterial()
.asStack());
CreateClient.OUTLINER.showAABB(edge.edgeData, AABB.ofSize(materialPos, .25, 0, .25)
.move(0, -0.5, 0))
.lineWidth(1 / 16f)
.colored(graph.color);
}
CreateClient.OUTLINER.showLine(edge, edge.getPosition(graph, 0) CreateClient.OUTLINER.showLine(edge, edge.getPosition(graph, 0)
.add(yOffset), .add(yOffset),
edge.getPosition(graph, 1) edge.getPosition(graph, 1)
@ -277,6 +287,16 @@ public class TrackGraphVisualizer {
Vec3 previous = null; Vec3 previous = null;
BezierConnection turn = edge.getTurn(); BezierConnection turn = edge.getTurn();
if (extended) {
Vec3 materialPos = edge.getPosition(graph, 0.5)
.add(0, 1, 0);
CreateClient.OUTLINER.showItem(Pair.of(edge, edge.edgeData), materialPos, edge.getTrackMaterial()
.asStack());
CreateClient.OUTLINER.showAABB(edge.edgeData, AABB.ofSize(materialPos, .25, 0, .25)
.move(0, -0.5, 0))
.lineWidth(1 / 16f)
.colored(graph.color);
}
for (int i = 0; i <= turn.getSegmentCount(); i++) { for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(graph, i * 1f / turn.getSegmentCount()); Vec3 current = edge.getPosition(graph, i * 1f / turn.getSegmentCount());
if (previous != null) if (previous != null)

View file

@ -0,0 +1,171 @@
package com.simibubi.create.content.logistics.trains;
import static com.simibubi.create.content.logistics.trains.TrackMaterialFactory.make;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.PartialModel;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class TrackMaterial {
public static final Map<ResourceLocation, TrackMaterial> ALL = new HashMap<>();
public static final TrackMaterial ANDESITE = make(Create.asResource("andesite"))
.lang("Andesite")
.block(NonNullSupplier.lazy(() -> AllBlocks.TRACK))
.particle(Create.asResource("block/palettes/stone_types/polished/andesite_cut_polished"))
.defaultModels()
.build();
public final ResourceLocation id;
public final String langName;
public final NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock;
public final Ingredient sleeperIngredient;
public final Ingredient railsIngredient;
public final ResourceLocation particle;
public final TrackType trackType;
@Nullable
private final TrackMaterial.TrackType.TrackBlockFactory customFactory;
@OnlyIn(Dist.CLIENT)
protected TrackModelHolder modelHolder;
@OnlyIn(Dist.CLIENT)
public TrackModelHolder getModelHolder() {
return modelHolder;
}
public TrackMaterial(ResourceLocation id, String langName, NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock,
ResourceLocation particle, Ingredient sleeperIngredient, Ingredient railsIngredient,
TrackType trackType, Supplier<Supplier<TrackModelHolder>> modelHolder) {
this(id, langName, trackBlock, particle, sleeperIngredient, railsIngredient, trackType, modelHolder, null);
}
public TrackMaterial(ResourceLocation id, String langName, NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock,
ResourceLocation particle, Ingredient sleeperIngredient, Ingredient railsIngredient,
TrackType trackType, Supplier<Supplier<TrackModelHolder>> modelHolder,
@Nullable TrackType.TrackBlockFactory customFactory) {
this.id = id;
this.langName = langName;
this.trackBlock = trackBlock;
this.sleeperIngredient = sleeperIngredient;
this.railsIngredient = railsIngredient;
this.particle = particle;
this.trackType = trackType;
this.customFactory = customFactory;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.modelHolder = modelHolder.get().get());
ALL.put(this.id, this);
}
public NonNullSupplier<? extends TrackBlock> getBlockSupplier() {
return this.trackBlock.get();
}
public TrackBlock getBlock() {
return getBlockSupplier().get();
}
public ItemStack asStack() {
return asStack(1);
}
public ItemStack asStack(int count) {
return new ItemStack(getBlock(), count);
}
public TrackBlock createBlock(BlockBehaviour.Properties properties) {
return (this.customFactory != null ? this.customFactory : this.trackType.factory)
.create(properties, this);
}
public boolean isFromMod(String modId) {
return this.id.getNamespace().equals(modId);
}
public static List<TrackMaterial> allFromMod(String modid) {
return ALL.values().stream().filter(tm -> tm.isFromMod(modid)).toList();
}
public static List<NonNullSupplier<? extends Block>> allBlocksFromMod(String modid) {
List<NonNullSupplier<? extends Block>> list = new ArrayList<>();
for (TrackMaterial material : allFromMod(modid)) {
list.add(material.getBlockSupplier());
}
return list;
}
public static List<NonNullSupplier<? extends Block>> allBlocks() {
List<NonNullSupplier<? extends Block>> list = new ArrayList<>();
for (TrackMaterial material : ALL.values()) {
list.add(material.getBlockSupplier());
}
return list;
}
public String resourceName() {
return this.id.getPath();
}
public static TrackMaterial deserialize(String serializedName) {
if (serializedName.isBlank()) // Data migrating from 0.5
return ANDESITE;
ResourceLocation id = ResourceLocation.tryParse(serializedName);
if (ALL.containsKey(id))
return ALL.get(id);
Create.LOGGER.error("Failed to locate serialized track material: " + serializedName);
return ANDESITE;
}
public static class TrackType {
@FunctionalInterface
public interface TrackBlockFactory {
TrackBlock create(BlockBehaviour.Properties properties, TrackMaterial material);
}
public static final TrackType STANDARD = new TrackType(Create.asResource("standard"), TrackBlock::new);
public final ResourceLocation id;
protected final TrackBlockFactory factory;
public TrackType(ResourceLocation id, TrackBlockFactory factory) {
this.id = id;
this.factory = factory;
}
}
public static TrackMaterial fromItem(Item item) {
if (item instanceof BlockItem blockItem && blockItem.getBlock() instanceof ITrackBlock trackBlock)
return trackBlock.getMaterial();
return TrackMaterial.ANDESITE;
}
@OnlyIn(Dist.CLIENT)
public record TrackModelHolder(PartialModel tie, PartialModel segment_left, PartialModel segment_right) {
static final TrackModelHolder DEFAULT = new TrackModelHolder(AllPartialModels.TRACK_TIE,
AllPartialModels.TRACK_SEGMENT_LEFT, AllPartialModels.TRACK_SEGMENT_RIGHT);
}
}

View file

@ -0,0 +1,142 @@
package com.simibubi.create.content.logistics.trains;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.PartialModel;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class TrackMaterialFactory {
private final ResourceLocation id;
private String langName;
private NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock;
private Ingredient sleeperIngredient = Ingredient.EMPTY;
private Ingredient railsIngredient = Ingredient.fromValues(Stream.of(new Ingredient.TagValue(AllTags.forgeItemTag("nuggets/iron")), new Ingredient.TagValue(AllTags.forgeItemTag("nuggets/zinc"))));
private ResourceLocation particle;
private TrackMaterial.TrackType trackType = TrackMaterial.TrackType.STANDARD;
@Nullable
private TrackMaterial.TrackType.TrackBlockFactory customFactory = null;
@OnlyIn(Dist.CLIENT)
private TrackMaterial.TrackModelHolder modelHolder;
@OnlyIn(Dist.CLIENT)
private PartialModel tieModel;
@OnlyIn(Dist.CLIENT)
private PartialModel leftSegmentModel;
@OnlyIn(Dist.CLIENT)
private PartialModel rightSegmentModel;
public TrackMaterialFactory(ResourceLocation id) {
this.id = id;
}
public static TrackMaterialFactory make(ResourceLocation id) { // Convenience function for static import
return new TrackMaterialFactory(id);
}
public TrackMaterialFactory lang(String langName) {
this.langName = langName;
return this;
}
public TrackMaterialFactory block(NonNullSupplier<NonNullSupplier<? extends TrackBlock>> trackBlock) {
this.trackBlock = trackBlock;
return this;
}
public TrackMaterialFactory defaultModels() { // was setBuiltin
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.modelHolder = TrackMaterial.TrackModelHolder.DEFAULT);
return this;
}
public TrackMaterialFactory sleeper(Ingredient sleeperIngredient) {
this.sleeperIngredient = sleeperIngredient;
return this;
}
public TrackMaterialFactory sleeper(ItemLike... items) {
this.sleeperIngredient = Ingredient.of(items);
return this;
}
public TrackMaterialFactory rails(Ingredient railsIngredient) {
this.railsIngredient = railsIngredient;
return this;
}
public TrackMaterialFactory rails(ItemLike... items) {
this.railsIngredient = Ingredient.of(items);
return this;
}
public TrackMaterialFactory noRecipeGen() {
this.railsIngredient = Ingredient.EMPTY;
this.sleeperIngredient = Ingredient.EMPTY;
return this;
}
public TrackMaterialFactory particle(ResourceLocation particle) {
this.particle = particle;
return this;
}
public TrackMaterialFactory trackType(TrackMaterial.TrackType trackType) {
this.trackType = trackType;
return this;
}
public TrackMaterialFactory standardModels() { // was defaultModels
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
String namespace = id.getNamespace();
String prefix = "block/track/" + id.getPath() + "/";
tieModel = new PartialModel(new ResourceLocation(namespace, prefix + "tie"));
leftSegmentModel = new PartialModel(new ResourceLocation(namespace, prefix + "segment_left"));
rightSegmentModel = new PartialModel(new ResourceLocation(namespace, prefix + "segment_right"));
});
return this;
}
public TrackMaterialFactory customModels(Supplier<Supplier<PartialModel>> tieModel, Supplier<Supplier<PartialModel>> leftSegmentModel, Supplier<Supplier<PartialModel>> rightSegmentModel) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
this.tieModel = tieModel.get().get();
this.leftSegmentModel = leftSegmentModel.get().get();
this.rightSegmentModel = rightSegmentModel.get().get();
});
return this;
}
public TrackMaterialFactory customBlockFactory(TrackMaterial.TrackType.TrackBlockFactory factory) {
this.customFactory = factory;
return this;
}
public TrackMaterial build() {
assert trackBlock != null;
assert langName != null;
assert particle != null;
assert trackType != null;
assert sleeperIngredient != null;
assert railsIngredient != null;
assert id != null;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
assert modelHolder != null;
if (tieModel != null || leftSegmentModel != null || rightSegmentModel != null) {
assert tieModel != null && leftSegmentModel != null && rightSegmentModel != null;
modelHolder = new TrackMaterial.TrackModelHolder(tieModel, leftSegmentModel, rightSegmentModel);
}
});
return new TrackMaterial(id, langName, trackBlock, particle, sleeperIngredient, railsIngredient, trackType, () -> () -> modelHolder, customFactory);
}
}

View file

@ -25,8 +25,8 @@ public class TrackNodeLocation extends Vec3i {
this(vec.x, vec.y, vec.z); this(vec.x, vec.y, vec.z);
} }
public TrackNodeLocation(double p_121865_, double p_121866_, double p_121867_) { public TrackNodeLocation(double x, double y, double z) {
super(Math.round(p_121865_ * 2), Math.floor(p_121866_) * 2, Math.round(p_121867_ * 2)); super(Math.round(x * 2), Math.floor(y) * 2, Math.round(z * 2));
} }
public TrackNodeLocation in(Level level) { public TrackNodeLocation in(Level level) {
@ -122,9 +122,11 @@ public class TrackNodeLocation extends Vec3i {
boolean forceNode = false; boolean forceNode = false;
Vec3 direction; Vec3 direction;
Vec3 normal; Vec3 normal;
TrackMaterial materialA;
TrackMaterial materialB;
public DiscoveredLocation(Level level, double p_121865_, double p_121866_, double p_121867_) { public DiscoveredLocation(Level level, double x, double y, double z) {
super(p_121865_, p_121866_, p_121867_); super(x, y, z);
in(level); in(level);
} }
@ -137,6 +139,22 @@ public class TrackNodeLocation extends Vec3i {
this(level.dimension(), vec); this(level.dimension(), vec);
} }
public DiscoveredLocation materialA(TrackMaterial material) {
this.materialA = material;
return this;
}
public DiscoveredLocation materialB(TrackMaterial material) {
this.materialB = material;
return this;
}
public DiscoveredLocation materials(TrackMaterial materialA, TrackMaterial materialB) {
this.materialA = materialA;
this.materialB = materialB;
return this;
}
public DiscoveredLocation viaTurn(BezierConnection turn) { public DiscoveredLocation viaTurn(BezierConnection turn) {
this.turn = turn; this.turn = turn;
if (turn != null) if (turn != null)
@ -176,6 +194,10 @@ public class TrackNodeLocation extends Vec3i {
return forceNode; return forceNode;
} }
public boolean differentMaterials() {
return materialA != materialB;
}
public boolean notInLineWith(Vec3 direction) { public boolean notInLineWith(Vec3 direction) {
return this.direction != null return this.direction != null
&& Math.max(direction.dot(this.direction), direction.dot(this.direction.scale(-1))) < 7 / 8f; && Math.max(direction.dot(this.direction), direction.dot(this.direction.scale(-1))) < 7 / 8f;

View file

@ -8,6 +8,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.api.event.TrackGraphMergeEvent;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalPropagator; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalPropagator;
@ -16,6 +17,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class TrackPropagator { public class TrackPropagator {
@ -135,6 +137,7 @@ public class TrackPropagator {
if (graph == null) if (graph == null)
graph = other; graph = other;
else { else {
MinecraftForge.EVENT_BUS.post(new TrackGraphMergeEvent(other, graph));
other.transferAll(graph); other.transferAll(graph);
manager.removeGraphAndGroup(other); manager.removeGraphAndGroup(other);
sync.graphRemoved(other); sync.graphRemoved(other);
@ -234,6 +237,8 @@ public class TrackPropagator {
return true; return true;
if (location.shouldForceNode()) if (location.shouldForceNode())
return true; return true;
if (location.differentMaterials())
return true;
if (next.stream() if (next.stream()
.anyMatch(DiscoveredLocation::shouldForceNode)) .anyMatch(DiscoveredLocation::shouldForceNode))
return true; return true;

View file

@ -0,0 +1,22 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import net.minecraft.nbt.CompoundTag;
public class BackupBogeyRenderer extends BogeyRenderer.CommonRenderer {
public static BackupBogeyRenderer INSTANCE = new BackupBogeyRenderer();
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb, boolean inContraption) {
}
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
}
}

View file

@ -1,235 +1,69 @@
package com.simibubi.create.content.logistics.trains.entity; package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.Material; import java.util.Optional;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks; import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.AllPartialModels; import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public sealed class BogeyInstance { public final class BogeyInstance {
private final BogeySizes.BogeySize size;
private final BogeyStyle style;
public final CarriageBogey bogey; public final CarriageBogey bogey;
private final ModelData[] shafts; public final BogeyRenderer renderer;
public final Optional<BogeyRenderer.CommonRenderer> commonRenderer;
protected BogeyInstance(CarriageBogey bogey, MaterialManager materialManager) { public BogeyInstance(CarriageBogey bogey, BogeyStyle style, BogeySizes.BogeySize size,
MaterialManager materialManager) {
this.bogey = bogey; this.bogey = bogey;
this.size = size;
this.style = style;
shafts = new ModelData[2]; this.renderer = this.style.createRendererInstance(this.size);
this.commonRenderer = this.style.getNewCommonRenderInstance();
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z))
.createInstances(shafts);
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.initialiseContraptionModelData(materialManager));
renderer.initialiseContraptionModelData(materialManager);
} }
public void remove() { void hiddenFrame() {
for (ModelData shaft : shafts)
shaft.delete();
}
public void hiddenFrame() {
beginFrame(0, null); beginFrame(0, null);
} }
public void beginFrame(float wheelAngle, PoseStack ms) { public void beginFrame(float wheelAngle, PoseStack ms) {
if (ms == null) { if (ms == null) {
for (int i : Iterate.zeroAndOne) renderer.emptyTransforms();
shafts[i].setEmptyTransform();
return; return;
} }
for (int i : Iterate.zeroAndOne) commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.render(bogey.bogeyData, wheelAngle, ms));
shafts[i].setTransform(ms) renderer.render(bogey.bogeyData, wheelAngle, ms);
.translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre();
} }
public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) { public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) {
var lightPos = new BlockPos(getLightPos(entity)); var lightPos = new BlockPos(getLightPos(entity));
commonRenderer
updateLight(world.getBrightness(LightLayer.BLOCK, lightPos), world.getBrightness(LightLayer.SKY, lightPos)); .ifPresent(bogeyRenderer -> bogeyRenderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos)));
renderer.updateLight(world.getBrightness(LightLayer.BLOCK, lightPos),
world.getBrightness(LightLayer.SKY, lightPos));
} }
private Vec3 getLightPos(CarriageContraptionEntity entity) { private Vec3 getLightPos(CarriageContraptionEntity entity) {
if (bogey.getAnchorPosition() != null) { return bogey.getAnchorPosition() != null ? bogey.getAnchorPosition()
return bogey.getAnchorPosition(); : entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
} else {
return entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
} }
public void updateLight(int blockLight, int skyLight) { @FunctionalInterface
for (ModelData shaft : shafts) { interface BogeyInstanceFactory {
shaft.setBlockLight(blockLight) BogeyInstance create(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager);
.setSkyLight(skyLight);
}
}
public static final class Frame extends BogeyInstance {
private final ModelData frame;
private final ModelData[] wheels;
public Frame(CarriageBogey bogey, MaterialManager materialManager) {
super(bogey, materialManager);
frame = materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllPartialModels.BOGEY_FRAME)
.createInstance();
wheels = new ModelData[2];
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllPartialModels.SMALL_BOGEY_WHEELS)
.createInstances(wheels);
}
@Override
public void beginFrame(float wheelAngle, PoseStack ms) {
super.beginFrame(wheelAngle, ms);
if (ms == null) {
frame.setEmptyTransform();
for (int side : Iterate.positiveAndNegative)
wheels[(side + 1) / 2].setEmptyTransform();
return;
}
frame.setTransform(ms);
for (int side : Iterate.positiveAndNegative) {
wheels[(side + 1) / 2].setTransform(ms)
.translate(0, 12 / 16f, side)
.rotateX(wheelAngle);
}
}
@Override
public void updateLight(int blockLight, int skyLight) {
super.updateLight(blockLight, skyLight);
frame.setBlockLight(blockLight)
.setSkyLight(skyLight);
for (ModelData wheel : wheels)
wheel.setBlockLight(blockLight)
.setSkyLight(skyLight);
}
@Override
public void remove() {
super.remove();
frame.delete();
for (ModelData wheel : wheels)
wheel.delete();
}
}
public static final class Drive extends BogeyInstance {
private final ModelData[] secondShaft;
private final ModelData drive;
private final ModelData piston;
private final ModelData wheels;
private final ModelData pin;
public Drive(CarriageBogey bogey, MaterialManager materialManager) {
super(bogey, materialManager);
Material<ModelData> mat = materialManager.defaultSolid()
.material(Materials.TRANSFORMED);
secondShaft = new ModelData[2];
mat.getModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X))
.createInstances(secondShaft);
drive = mat.getModel(AllPartialModels.BOGEY_DRIVE)
.createInstance();
piston = mat.getModel(AllPartialModels.BOGEY_PISTON)
.createInstance();
wheels = mat.getModel(AllPartialModels.LARGE_BOGEY_WHEELS)
.createInstance();
pin = mat.getModel(AllPartialModels.BOGEY_PIN)
.createInstance();
}
@Override
public void beginFrame(float wheelAngle, PoseStack ms) {
super.beginFrame(wheelAngle, ms);
if (ms == null) {
for (int i : Iterate.zeroAndOne)
secondShaft[i].setEmptyTransform();
drive.setEmptyTransform();
piston.setEmptyTransform();
wheels.setEmptyTransform();
pin.setEmptyTransform();
return;
}
for (int i : Iterate.zeroAndOne)
secondShaft[i].setTransform(ms)
.translate(-.5f, .25f, .5f + i * -2)
.centre()
.rotateX(wheelAngle)
.unCentre();
drive.setTransform(ms);
piston.setTransform(ms)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)));
wheels.setTransform(ms)
.translate(0, 1, 0)
.rotateX(wheelAngle);
pin.setTransform(ms)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle);
}
@Override
public void updateLight(int blockLight, int skyLight) {
super.updateLight(blockLight, skyLight);
for (ModelData shaft : secondShaft)
shaft.setBlockLight(blockLight)
.setSkyLight(skyLight);
drive.setBlockLight(blockLight)
.setSkyLight(skyLight);
piston.setBlockLight(blockLight)
.setSkyLight(skyLight);
wheels.setBlockLight(blockLight)
.setSkyLight(skyLight);
pin.setBlockLight(blockLight)
.setSkyLight(skyLight);
}
@Override
public void remove() {
super.remove();
for (ModelData shaft : secondShaft)
shaft.delete();
drive.delete();
piston.delete();
wheels.delete();
pin.delete();
}
} }
} }

View file

@ -0,0 +1,114 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeyRenderer.CommonRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class BogeyStyle {
private final Optional<Supplier<? extends CommonRenderer>> commonRendererFactory;
public final ResourceLocation name;
public final ResourceLocation cycleGroup;
private final Optional<CommonRenderer> commonRenderer;
private final Map<BogeySizes.BogeySize, SizeData> sizes;
public final Component displayName;
public final ResourceLocation soundType;
public final ParticleOptions contactParticle;
public final ParticleOptions smokeParticle;
public final CompoundTag defaultData;
public BogeyStyle(ResourceLocation name, ResourceLocation cycleGroup, Component displayName, ResourceLocation soundType, ParticleOptions contactParticle, ParticleOptions smokeParticle,
CompoundTag defaultData, Map<BogeySizes.BogeySize, SizeData> sizes, Optional<Supplier<? extends CommonRenderer>> commonRenderer) {
this.name = name;
this.cycleGroup = cycleGroup;
this.displayName = displayName;
this.soundType = soundType;
this.contactParticle = contactParticle;
this.smokeParticle = smokeParticle;
this.defaultData = defaultData;
this.sizes = sizes;
this.commonRendererFactory = commonRenderer;
this.commonRenderer = commonRenderer.map(Supplier::get);
}
public Map<ResourceLocation, BogeyStyle> getCycleGroup() {
return AllBogeyStyles.getCycleGroup(cycleGroup);
}
public Block getNextBlock(BogeySizes.BogeySize currentSize) {
return Stream.iterate(currentSize.increment(), BogeySizes.BogeySize::increment)
.filter(sizes::containsKey)
.findFirst()
.map(size -> ForgeRegistries.BLOCKS.getValue(sizes.get(size).block()))
.orElse(ForgeRegistries.BLOCKS.getValue(sizes.get(currentSize).block()));
}
public Block getBlockOfSize(BogeySizes.BogeySize size) {
return ForgeRegistries.BLOCKS.getValue(sizes.get(size).block());
}
public Set<BogeySizes.BogeySize> validSizes() {
return sizes.keySet();
}
@NotNull
public SoundEvent getSoundType() {
AllSoundEvents.SoundEntry entry = AllSoundEvents.ALL.get(this.soundType);
if (entry == null || entry.getMainEvent() == null) entry = AllSoundEvents.TRAIN2;
return entry.getMainEvent();
}
public BogeyRenderer createRendererInstance(BogeySizes.BogeySize size) {
return this.sizes.get(size).createRenderInstance();
}
public BogeyRenderer getInWorldRenderInstance(BogeySizes.BogeySize size) {
SizeData sizeData = this.sizes.get(size);
return sizeData != null ? sizeData.getInWorldInstance() : BackupBogeyRenderer.INSTANCE;
}
public Optional<CommonRenderer> getInWorldCommonRenderInstance() {
return this.commonRenderer;
}
public Optional<CommonRenderer> getNewCommonRenderInstance() {
return this.commonRendererFactory.map(Supplier::get);
}
public BogeyInstance createInstance(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager) {
return new BogeyInstance(bogey, this, size, materialManager);
}
public record SizeData(ResourceLocation block, Supplier<? extends BogeyRenderer> rendererFactory, BogeyRenderer instance) {
public BogeyRenderer createRenderInstance() {
return rendererFactory.get();
}
public BogeyRenderer getInWorldInstance() {
return instance;
}
}
}

View file

@ -85,6 +85,11 @@ public class Carriage {
bogey2.carriage = this; bogey2.carriage = this;
} }
public boolean isOnIncompatibleTrack() {
return leadingBogey().type.isOnIncompatibleTrack(this, true)
|| trailingBogey().type.isOnIncompatibleTrack(this, false);
}
public void setTrain(Train train) { public void setTrain(Train train) {
this.train = train; this.train = train;
} }
@ -103,7 +108,7 @@ public class Carriage {
DimensionalCarriageEntity dimensional = getDimensional(level); DimensionalCarriageEntity dimensional = getDimensional(level);
dimensional.alignEntity(entity); dimensional.alignEntity(entity);
dimensional.removeAndSaveEntity(entity, false); dimensional.removeAndSaveEntity(entity, true);
} }
public DimensionalCarriageEntity getDimensional(Level level) { public DimensionalCarriageEntity getDimensional(Level level) {
@ -306,6 +311,9 @@ public class Carriage {
double leadingWheelSpacing = leadingBogey.type.getWheelPointSpacing(); double leadingWheelSpacing = leadingBogey.type.getWheelPointSpacing();
double trailingWheelSpacing = trailingBogey.type.getWheelPointSpacing(); double trailingWheelSpacing = trailingBogey.type.getWheelPointSpacing();
boolean leadingUpsideDown = leadingBogey.isUpsideDown();
boolean trailingUpsideDown = trailingBogey.isUpsideDown();
for (boolean leading : Iterate.trueAndFalse) { for (boolean leading : Iterate.trueAndFalse) {
TravellingPoint point = leading ? getLeadingPoint() : getTrailingPoint(); TravellingPoint point = leading ? getLeadingPoint() : getTrailingPoint();
TravellingPoint otherPoint = !leading ? getLeadingPoint() : getTrailingPoint(); TravellingPoint otherPoint = !leading ? getLeadingPoint() : getTrailingPoint();
@ -321,26 +329,31 @@ public class Carriage {
dce.positionAnchor = dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition() dce.positionAnchor = dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2); leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown);
boolean backAnchorFlip = trailingBogey.isUpsideDown() ^ leadingBogey.isUpsideDown();
if (isOnTwoBogeys()) { if (isOnTwoBogeys()) {
dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition() dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2)); leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2,
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition() leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition(backAnchorFlip)
: pivoted(dce, dimension, point, : pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2)); leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown));
} else { } else {
if (dimension.equals(otherDimension)) { if (dimension.equals(otherDimension)) {
dce.rotationAnchors = leadingBogey.points.map(tp -> tp.getPosition(train.graph)); dce.rotationAnchors = leadingBogey.points.map(tp -> tp.getPosition(train.graph));
} else { } else {
dce.rotationAnchors dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point
.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition(train.graph) ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing)); : pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point
.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition(train.graph) ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing)); : pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
} }
} }
@ -358,15 +371,16 @@ public class Carriage {
} }
private Vec3 pivoted(DimensionalCarriageEntity dce, ResourceKey<Level> dimension, TravellingPoint start, private Vec3 pivoted(DimensionalCarriageEntity dce, ResourceKey<Level> dimension, TravellingPoint start,
double offset) { double offset, boolean leadingUpsideDown, boolean trailingUpsideDown) {
if (train.graph == null) if (train.graph == null)
return dce.pivot == null ? null : dce.pivot.getLocation(); return dce.pivot == null ? null : dce.pivot.getLocation();
TrackNodeLocation pivot = dce.findPivot(dimension, start == getLeadingPoint()); TrackNodeLocation pivot = dce.findPivot(dimension, start == getLeadingPoint());
if (pivot == null) if (pivot == null)
return null; return null;
Vec3 startVec = start.getPosition(train.graph); boolean flipped = start != getLeadingPoint() && (leadingUpsideDown != trailingUpsideDown);
Vec3 startVec = start.getPosition(train.graph, flipped);
Vec3 portalVec = pivot.getLocation() Vec3 portalVec = pivot.getLocation()
.add(0, 1, 0); .add(0, leadingUpsideDown ? -1.0 : 1.0, 0);
return VecHelper.lerp((float) (offset / startVec.distanceTo(portalVec)), startVec, portalVec); return VecHelper.lerp((float) (offset / startVec.distanceTo(portalVec)), startVec, portalVec);
} }
@ -725,8 +739,8 @@ public class Carriage {
} }
private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean portal) { private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean capture) {
if (!portal) { if (!capture) {
sp.stopRiding(); sp.stopRiding();
return; return;
} }

View file

@ -1,15 +1,21 @@
package com.simibubi.create.content.logistics.trains.entity; package com.simibubi.create.content.logistics.trains.entity;
import static com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity.BOGEY_DATA_KEY;
import static com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity.BOGEY_STYLE_KEY;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.RegisteredObjects; import com.simibubi.create.foundation.utility.RegisteredObjects;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat; import com.simibubi.create.foundation.utility.animation.LerpedFloat;
@ -27,11 +33,15 @@ import net.minecraftforge.registries.ForgeRegistries;
public class CarriageBogey { public class CarriageBogey {
public Carriage carriage; public static final String UPSIDE_DOWN_KEY = "UpsideDown";
public Carriage carriage;
boolean isLeading; boolean isLeading;
IBogeyBlock type; public CompoundTag bogeyData;
AbstractBogeyBlock<?> type;
boolean upsideDown;
Couple<TravellingPoint> points; Couple<TravellingPoint> points;
LerpedFloat wheelAngle; LerpedFloat wheelAngle;
@ -42,8 +52,15 @@ public class CarriageBogey {
int derailAngle; int derailAngle;
public CarriageBogey(IBogeyBlock type, TravellingPoint point, TravellingPoint point2) { public CarriageBogey(AbstractBogeyBlock<?> type, boolean upsideDown, CompoundTag bogeyData, TravellingPoint point, TravellingPoint point2) {
this.type = type; this.type = type;
this.upsideDown = type.canBeUpsideDown() && upsideDown;
point.upsideDown = this.upsideDown;
point2.upsideDown = this.upsideDown;
if (bogeyData == null || bogeyData.isEmpty())
bogeyData = this.createBogeyData(); // Prevent Crash When Updating
bogeyData.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
this.bogeyData = bogeyData;
points = Couple.create(point, point2); points = Couple.create(point, point2);
wheelAngle = LerpedFloat.angular(); wheelAngle = LerpedFloat.angular();
yaw = LerpedFloat.angular(); yaw = LerpedFloat.angular();
@ -100,11 +117,15 @@ public class CarriageBogey {
} }
public TravellingPoint leading() { public TravellingPoint leading() {
return points.getFirst(); TravellingPoint point = points.getFirst();
point.upsideDown = isUpsideDown();
return point;
} }
public TravellingPoint trailing() { public TravellingPoint trailing() {
return points.getSecond(); TravellingPoint point = points.getSecond();
point.upsideDown = isUpsideDown();
return point;
} }
public double getStress() { public double getStress() {
@ -118,18 +139,25 @@ public class CarriageBogey {
@Nullable @Nullable
public Vec3 getAnchorPosition() { public Vec3 getAnchorPosition() {
return getAnchorPosition(false);
}
@Nullable
public Vec3 getAnchorPosition(boolean flipUpsideDown) {
if (leading().edge == null) if (leading().edge == null)
return null; return null;
return points.getFirst() return points.getFirst()
.getPosition(carriage.train.graph) .getPosition(carriage.train.graph, flipUpsideDown)
.add(points.getSecond() .add(points.getSecond()
.getPosition(carriage.train.graph)) .getPosition(carriage.train.graph, flipUpsideDown))
.scale(.5); .scale(.5);
} }
public void updateCouplingAnchor(Vec3 entityPos, float entityXRot, float entityYRot, int bogeySpacing, public void updateCouplingAnchor(Vec3 entityPos, float entityXRot, float entityYRot, int bogeySpacing,
float partialTicks, boolean leading) { float partialTicks, boolean leading) {
Vec3 thisOffset = type.getConnectorAnchorOffset(); boolean selfUpsideDown = isUpsideDown();
boolean leadingUpsideDown = carriage.leadingBogey().isUpsideDown();
Vec3 thisOffset = type.getConnectorAnchorOffset(selfUpsideDown);
thisOffset = thisOffset.multiply(1, 1, leading ? -1 : 1); thisOffset = thisOffset.multiply(1, 1, leading ? -1 : 1);
thisOffset = VecHelper.rotate(thisOffset, pitch.getValue(partialTicks), Axis.X); thisOffset = VecHelper.rotate(thisOffset, pitch.getValue(partialTicks), Axis.X);
@ -141,6 +169,8 @@ public class CarriageBogey {
thisOffset = VecHelper.rotate(thisOffset, 180, Axis.Y); thisOffset = VecHelper.rotate(thisOffset, 180, Axis.Y);
thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X); thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X);
thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y); thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y);
if (selfUpsideDown != leadingUpsideDown)
thisOffset = thisOffset.add(0, selfUpsideDown ? -2 : 2, 0);
couplingAnchors.set(leading, entityPos.add(thisOffset)); couplingAnchors.set(leading, entityPos.add(thisOffset));
} }
@ -150,23 +180,46 @@ public class CarriageBogey {
tag.putString("Type", RegisteredObjects.getKeyOrThrow((Block) type) tag.putString("Type", RegisteredObjects.getKeyOrThrow((Block) type)
.toString()); .toString());
tag.put("Points", points.serializeEach(tp -> tp.write(dimensions))); tag.put("Points", points.serializeEach(tp -> tp.write(dimensions)));
tag.putBoolean("UpsideDown", upsideDown);
bogeyData.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
NBTHelper.writeResourceLocation(bogeyData, BOGEY_STYLE_KEY, getStyle().name);
tag.put(BOGEY_DATA_KEY, bogeyData);
return tag; return tag;
} }
public static CarriageBogey read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { public static CarriageBogey read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) {
ResourceLocation location = new ResourceLocation(tag.getString("Type")); ResourceLocation location = new ResourceLocation(tag.getString("Type"));
IBogeyBlock type = (IBogeyBlock) ForgeRegistries.BLOCKS.getValue(location); AbstractBogeyBlock<?> type = (AbstractBogeyBlock<?>) ForgeRegistries.BLOCKS.getValue(location);
boolean upsideDown = tag.getBoolean("UpsideDown");
Couple<TravellingPoint> points = Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND), Couple<TravellingPoint> points = Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND),
c -> TravellingPoint.read(c, graph, dimensions)); c -> TravellingPoint.read(c, graph, dimensions));
CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond()); CompoundTag data = tag.getCompound(AbstractBogeyBlockEntity.BOGEY_DATA_KEY);
return carriageBogey; return new CarriageBogey(type, upsideDown, data, points.getFirst(), points.getSecond());
} }
public BogeyInstance createInstance(MaterialManager materialManager) { public BogeyInstance createInstance(MaterialManager materialManager) {
return type.createInstance(materialManager, this); return this.getStyle().createInstance(this, type.getSize(), materialManager);
}
public BogeyStyle getStyle() {
ResourceLocation location = NBTHelper.readResourceLocation(this.bogeyData, BOGEY_STYLE_KEY);
BogeyStyle style = AllBogeyStyles.BOGEY_STYLES.get(location);
return style != null ? style : AllBogeyStyles.STANDARD; // just for safety
}
private CompoundTag createBogeyData() {
BogeyStyle style = type != null ? type.getDefaultStyle() : AllBogeyStyles.STANDARD;
CompoundTag nbt = style.defaultData != null ? style.defaultData : new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, style.name);
nbt.putBoolean(UPSIDE_DOWN_KEY, isUpsideDown());
return nbt;
} }
void setLeading() { void setLeading() {
isLeading = true; isLeading = true;
} }
public boolean isUpsideDown() {
return type.canBeUpsideDown() && upsideDown;
}
} }

View file

@ -22,7 +22,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.ren
import com.simibubi.create.content.contraptions.components.structureMovement.train.TrainCargoManager; import com.simibubi.create.content.contraptions.components.structureMovement.train.TrainCargoManager;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
@ -162,11 +162,13 @@ public class CarriageContraption extends Contraption {
.getStep(), toLocalPos(pos)); .getStep(), toLocalPos(pos));
} }
if (blockState.getBlock() instanceof IBogeyBlock) { if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey) {
boolean captureTE = bogey.captureTileEntityForTrain();
bogeys++; bogeys++;
if (bogeys == 2) if (bogeys == 2)
secondBogeyPos = pos; secondBogeyPos = pos;
return Pair.of(new StructureBlockInfo(pos, blockState, null), null); return Pair.of(new StructureBlockInfo(pos, blockState, captureTE ? getBlockEntityNBT(world, pos) : null),
captureTE ? world.getBlockEntity(pos) : null);
} }
if (AllBlocks.BLAZE_BURNER.has(blockState) if (AllBlocks.BLAZE_BURNER.has(blockState)

View file

@ -13,6 +13,7 @@ import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer<CarriageContraptionEntity> { public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer<CarriageContraptionEntity> {
@ -65,8 +66,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks); translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks);
int light = getBogeyLightCoords(entity, bogey, partialTicks); int light = getBogeyLightCoords(entity, bogey, partialTicks);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light, bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light,
overlay); overlay, bogey.getStyle(), bogey.bogeyData);
ms.popPose(); ms.popPose();
} }
@ -80,6 +82,8 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot, public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot,
float viewXRot, float partialTicks) { float viewXRot, float partialTicks) {
boolean selfUpsideDown = bogey.isUpsideDown();
boolean leadingUpsideDown = bogey.carriage.leadingBogey().isUpsideDown();
TransformStack.cast(ms) TransformStack.cast(ms)
.rotateY(viewYRot + 90) .rotateY(viewYRot + 90)
.rotateX(-viewXRot) .rotateX(-viewXRot)
@ -90,7 +94,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
.rotateY(-viewYRot - 90) .rotateY(-viewYRot - 90)
.rotateY(bogey.yaw.getValue(partialTicks)) .rotateY(bogey.yaw.getValue(partialTicks))
.rotateX(bogey.pitch.getValue(partialTicks)) .rotateX(bogey.pitch.getValue(partialTicks))
.translate(0, .5f, 0); .translate(0, .5f, 0)
.rotateZ(selfUpsideDown ? 180 : 0)
.translateY(selfUpsideDown != leadingUpsideDown ? 2 : 0);
} }
public static int getBogeyLightCoords(CarriageContraptionEntity entity, CarriageBogey bogey, float partialTicks) { public static int getBogeyLightCoords(CarriageContraptionEntity entity, CarriageBogey bogey, float partialTicks) {

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -31,7 +32,8 @@ public class CarriageContraptionInstance extends EntityInstance<CarriageContrapt
if (carriage == null) if (carriage == null)
return; return;
bogeys = carriage.bogeys.mapNotNullWithParam(CarriageBogey::createInstance, materialManager); bogeys = carriage.bogeys.mapNotNullWithParam((bogey, manager) ->
bogey.getStyle().createInstance(bogey, bogey.type.getSize(), manager), materialManager);
updateLight(); updateLight();
} }
@ -98,8 +100,10 @@ public class CarriageContraptionInstance extends EntityInstance<CarriageContrapt
return; return;
bogeys.forEach(instance -> { bogeys.forEach(instance -> {
if (instance != null) if (instance != null) {
instance.remove(); instance.commonRenderer.ifPresent(BogeyRenderer::remove);
instance.renderer.remove();
}
}); });
} }

View file

@ -79,7 +79,7 @@ public class CarriageCouplingRenderer {
float margin = 3 / 16f; float margin = 3 / 16f;
double couplingDistance = train.carriageSpacing.get(i) - 2 * margin double couplingDistance = train.carriageSpacing.get(i) - 2 * margin
- bogey1.type.getConnectorAnchorOffset().z - bogey2.type.getConnectorAnchorOffset().z; - bogey1.type.getConnectorAnchorOffset(bogey1.isUpsideDown()).z - bogey2.type.getConnectorAnchorOffset(bogey2.isUpsideDown()).z;
int couplingSegments = (int) Math.round(couplingDistance * 4); int couplingSegments = (int) Math.round(couplingDistance * 4);
double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments; double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments;
for (int j = 0; j < couplingSegments; j++) { for (int j = 0; j < couplingSegments; j++) {

View file

@ -8,7 +8,6 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.Axis;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -109,7 +108,7 @@ public class CarriageParticles {
m = m.add(contraptionMotion.scale(.75f)); m = m.add(contraptionMotion.scale(.75f));
level.addParticle(spark ? ParticleTypes.CRIT : ParticleTypes.POOF, v.x, v.y, v.z, m.x, m.y, m.z); level.addParticle(spark ? bogey.getStyle().contactParticle : bogey.getStyle().smokeParticle, v.x, v.y, v.z, m.x, m.y, m.z);
} }
} }
} }

View file

@ -3,6 +3,7 @@ package com.simibubi.create.content.logistics.trains.entity;
import com.simibubi.create.AllSoundEvents; import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllSoundEvents.SoundEntry; import com.simibubi.create.AllSoundEvents.SoundEntry;
import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity; import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.animation.LerpedFloat; import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser; import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
@ -30,6 +31,9 @@ public class CarriageSounds {
LoopingSound sharedWheelSoundSeated; LoopingSound sharedWheelSoundSeated;
LoopingSound sharedHonkSound; LoopingSound sharedHonkSound;
Couple<SoundEvent> bogeySounds;
SoundEvent closestBogeySound;
boolean arrived; boolean arrived;
int tick; int tick;
@ -37,6 +41,10 @@ public class CarriageSounds {
public CarriageSounds(CarriageContraptionEntity entity) { public CarriageSounds(CarriageContraptionEntity entity) {
this.entity = entity; this.entity = entity;
bogeySounds = entity.getCarriage().bogeys.map(bogey ->
bogey != null && bogey.getStyle() != null ? bogey.getStyle().getSoundType()
: AllSoundEvents.TRAIN2.getMainEvent());
closestBogeySound = bogeySounds.getFirst();
distanceFactor = LerpedFloat.linear(); distanceFactor = LerpedFloat.linear();
speedFactor = LerpedFloat.linear(); speedFactor = LerpedFloat.linear();
approachFactor = LerpedFloat.linear(); approachFactor = LerpedFloat.linear();
@ -80,6 +88,15 @@ public class CarriageSounds {
double distance1 = toBogey1.length(); double distance1 = toBogey1.length();
double distance2 = toBogey2.length(); double distance2 = toBogey2.length();
Couple<CarriageBogey> bogeys = entity.getCarriage().bogeys;
CarriageBogey relevantBogey = bogeys.get(distance1 > distance2);
if (relevantBogey == null) {
relevantBogey = bogeys.getFirst();
}
if (relevantBogey != null) {
closestBogeySound = relevantBogey.getStyle().getSoundType();
}
Vec3 toCarriage = distance1 > distance2 ? toBogey2 : toBogey1; Vec3 toCarriage = distance1 > distance2 ? toBogey2 : toBogey1;
double distance = Math.min(distance1, distance2); double distance = Math.min(distance1, distance2);
Vec3 soundLocation = cam.add(toCarriage); Vec3 soundLocation = cam.add(toCarriage);
@ -98,7 +115,7 @@ public class CarriageSounds {
seatCrossfade.tickChaser(); seatCrossfade.tickChaser();
minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent()); minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, AllSoundEvents.TRAIN2.getMainEvent()); sharedWheelSound = playIfMissing(mc, sharedWheelSound, closestBogeySound);
sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent()); sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent());
float volume = Math.min(Math.min(speedFactor.getValue(), distanceFactor.getValue() / 100), float volume = Math.min(Math.min(speedFactor.getValue(), distanceFactor.getValue() / 100),
@ -206,7 +223,7 @@ public class CarriageSounds {
public void submitSharedSoundVolume(Vec3 location, float volume) { public void submitSharedSoundVolume(Vec3 location, float volume) {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent()); minecartEsqueSound = playIfMissing(mc, minecartEsqueSound, AllSoundEvents.TRAIN.getMainEvent());
sharedWheelSound = playIfMissing(mc, sharedWheelSound, AllSoundEvents.TRAIN2.getMainEvent()); sharedWheelSound = playIfMissing(mc, sharedWheelSound, closestBogeySound);
sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent()); sharedWheelSoundSeated = playIfMissing(mc, sharedWheelSoundSeated, AllSoundEvents.TRAIN3.getMainEvent());
boolean approach = true; boolean approach = true;

View file

@ -14,6 +14,8 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
@ -555,6 +557,21 @@ public class Navigation {
if (graph == null) if (graph == null)
return; return;
// Cache the list of track types that the train can travel on
Set<TrackMaterial.TrackType> validTypes = new HashSet<>();
for (int i = 0; i < train.carriages.size(); i++) {
Carriage carriage = train.carriages.get(i);
if (i == 0) {
validTypes.addAll(carriage.leadingBogey().type.getValidPathfindingTypes(carriage.leadingBogey().getStyle()));
} else {
validTypes.retainAll(carriage.leadingBogey().type.getValidPathfindingTypes(carriage.leadingBogey().getStyle()));
}
if (carriage.isOnTwoBogeys())
validTypes.retainAll(carriage.trailingBogey().type.getValidPathfindingTypes(carriage.trailingBogey().getStyle()));
}
if (validTypes.isEmpty()) // if there are no valid track types, a route can't be found
return;
Map<TrackEdge, Integer> penalties = new IdentityHashMap<>(); Map<TrackEdge, Integer> penalties = new IdentityHashMap<>();
boolean costRelevant = maxCost >= 0; boolean costRelevant = maxCost >= 0;
if (costRelevant) { if (costRelevant) {
@ -674,6 +691,8 @@ public class Navigation {
continue; continue;
for (Entry<TrackNode, TrackEdge> target : validTargets) { for (Entry<TrackNode, TrackEdge> target : validTargets) {
if (!validTypes.contains(target.getValue().getTrackMaterial().trackType))
continue;
TrackNode newNode = target.getKey(); TrackNode newNode = target.getKey();
TrackEdge newEdge = target.getValue(); TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength() + distance; double newDistance = newEdge.getLength() + distance;

View file

@ -42,6 +42,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -66,6 +67,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion.BlockInteraction; import net.minecraft.world.level.Explosion.BlockInteraction;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
@ -86,6 +88,7 @@ public class Train {
public boolean honk = false; public boolean honk = false;
public UUID id; public UUID id;
@Nullable
public UUID owner; public UUID owner;
public TrackGraph graph; public TrackGraph graph;
public Navigation navigation; public Navigation navigation;
@ -313,7 +316,13 @@ public class Train {
if (leadingAnchor == null || trailingAnchor == null) if (leadingAnchor == null || trailingAnchor == null)
continue; continue;
total += leadingAnchor.distanceTo(trailingAnchor); double distanceTo = leadingAnchor.distanceToSqr(trailingAnchor);
if (carriage.leadingBogey().isUpsideDown() != previousCarriage.trailingBogey().isUpsideDown()) {
distanceTo = Math.sqrt(distanceTo - 4);
} else {
distanceTo = Math.sqrt(distanceTo);
}
total += distanceTo;
entries++; entries++;
} }
} }
@ -375,7 +384,7 @@ public class Train {
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE; int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
double actualDistance = double actualDistance =
carriage.travel(level, graph, distance + totalStress, toFollowForward, toFollowBackward, carriageType); carriage.travel(level, graph, distance + totalStress, toFollowForward, toFollowBackward, carriageType);
blocked |= carriage.blocked; blocked |= carriage.blocked || carriage.isOnIncompatibleTrack();
boolean onTwoBogeys = carriage.isOnTwoBogeys(); boolean onTwoBogeys = carriage.isOnTwoBogeys();
maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0); maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0);
@ -722,9 +731,20 @@ public class Train {
if (entity.getContraption()instanceof CarriageContraption cc) if (entity.getContraption()instanceof CarriageContraption cc)
cc.returnStorageForDisassembly(carriage.storage); cc.returnStorageForDisassembly(carriage.storage);
entity.setPos(Vec3 entity.setPos(Vec3
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset))); .atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset).below(carriage.leadingBogey().isUpsideDown() ? 2 : 0)));
entity.disassemble(); entity.disassemble();
for (CarriageBogey bogey : carriage.bogeys) {
if (bogey == null)
continue;
Vec3 bogeyPosition = bogey.getAnchorPosition();
if (bogeyPosition == null) continue;
BlockEntity be = level.getBlockEntity(new BlockPos(bogeyPosition));
if (!(be instanceof AbstractBogeyBlockEntity sbte))
continue;
sbte.setBogeyData(bogey.bogeyData);
}
offset += carriage.bogeySpacing; offset += carriage.bogeySpacing;
if (i < carriageSpacing.size()) if (i < carriageSpacing.size())
@ -944,7 +964,7 @@ public class Train {
occupiedObservers.clear(); occupiedObservers.clear();
cachedObserverFiltering.clear(); cachedObserverFiltering.clear();
TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position); TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position, false);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups; Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups;
MutableObject<UUID> prevGroup = new MutableObject<>(null); MutableObject<UUID> prevGroup = new MutableObject<>(null);
@ -1087,6 +1107,7 @@ public class Train {
public CompoundTag write(DimensionPalette dimensions) { public CompoundTag write(DimensionPalette dimensions) {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.putUUID("Id", id); tag.putUUID("Id", id);
if (owner != null)
tag.putUUID("Owner", owner); tag.putUUID("Owner", owner);
if (graph != null) if (graph != null)
tag.putUUID("Graph", graph.id); tag.putUUID("Graph", graph.id);
@ -1133,7 +1154,7 @@ public class Train {
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) { public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) {
UUID id = tag.getUUID("Id"); UUID id = tag.getUUID("Id");
UUID owner = tag.getUUID("Owner"); UUID owner = tag.contains("Owner") ? tag.getUUID("Owner") : null;
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null; UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId); TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
List<Carriage> carriages = new ArrayList<>(); List<Carriage> carriages = new ArrayList<>();

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