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

This commit is contained in:
simibubi 2023-05-09 19:37:24 +02:00
commit ce6ecad0f2
76 changed files with 2344 additions and 708 deletions

3
.gitignore vendored
View file

@ -42,4 +42,5 @@ local.properties
# PDT-specific # PDT-specific
.buildpath .buildpath
.DS_Store .DS_Store
/libs/

View file

@ -135,6 +135,9 @@ repositories {
includeGroup "maven.modrinth" includeGroup "maven.modrinth"
} }
} }
flatDir {
dirs 'libs'
}
} }
dependencies { dependencies {
@ -169,6 +172,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

View file

@ -582,7 +582,7 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
fcaad84ac4ebdb1e6d9301b77245ce855dbde503 assets/create/lang/en_ud.json fcaad84ac4ebdb1e6d9301b77245ce855dbde503 assets/create/lang/en_ud.json
fc2c57b8a2e8f364a2e74e8f64cffcb8c74b35f1 assets/create/lang/en_us.json 8c536f441c4515d44faf340cefb2c0247b49033a assets/create/lang/en_us.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
@ -5316,6 +5316,7 @@ d99d5c67bdffff60789a19bd51a5c5267c75e0a4 data/create/tags/blocks/casing.json
bc203f09dd7f48965d146d0bd035fb904cb75e7d data/create/tags/blocks/copycat_allow.json bc203f09dd7f48965d146d0bd035fb904cb75e7d data/create/tags/blocks/copycat_allow.json
d4a3b66f4b763b9a2dcdea74b7273f0ae85cb335 data/create/tags/blocks/copycat_deny.json d4a3b66f4b763b9a2dcdea74b7273f0ae85cb335 data/create/tags/blocks/copycat_deny.json
73c2c85233075d2854d209b71ff2160308a7919c data/create/tags/blocks/fan_transparent.json 73c2c85233075d2854d209b71ff2160308a7919c data/create/tags/blocks/fan_transparent.json
ad8fa04f7bbbafd70d0ce158af78a35e899301e2 data/create/tags/blocks/girdable_tracks.json
5445d23a146003f0aa8de86643c4315d4afd4ef6 data/create/tags/blocks/movable_empty_collider.json 5445d23a146003f0aa8de86643c4315d4afd4ef6 data/create/tags/blocks/movable_empty_collider.json
6e5d3b2123fbb00e7f439c091623619502551bca data/create/tags/blocks/non_movable.json 6e5d3b2123fbb00e7f439c091623619502551bca data/create/tags/blocks/non_movable.json
10781e8cfcbb3486327aace3aa00e437fb44b331 data/create/tags/blocks/ore_override_stone.json 10781e8cfcbb3486327aace3aa00e437fb44b331 data/create/tags/blocks/ore_override_stone.json
@ -5323,6 +5324,7 @@ d4a3b66f4b763b9a2dcdea74b7273f0ae85cb335 data/create/tags/blocks/copycat_deny.js
af314e7ec90377e69387523a4c9af19e0056734e data/create/tags/blocks/safe_nbt.json af314e7ec90377e69387523a4c9af19e0056734e data/create/tags/blocks/safe_nbt.json
6cdeeac1689f7b5bfd9bc40b462143d8eaf3ad0b data/create/tags/blocks/seats.json 6cdeeac1689f7b5bfd9bc40b462143d8eaf3ad0b data/create/tags/blocks/seats.json
d063e12c9ef75f39518c6d129ea35d833464d547 data/create/tags/blocks/toolboxes.json d063e12c9ef75f39518c6d129ea35d833464d547 data/create/tags/blocks/toolboxes.json
ad8fa04f7bbbafd70d0ce158af78a35e899301e2 data/create/tags/blocks/tracks.json
9460e92c8e483446318b849abe7e6f52dcd4a269 data/create/tags/blocks/tree_attachments.json 9460e92c8e483446318b849abe7e6f52dcd4a269 data/create/tags/blocks/tree_attachments.json
50936b211d94167a35ec78c89954082a336b6269 data/create/tags/blocks/valve_handles.json 50936b211d94167a35ec78c89954082a336b6269 data/create/tags/blocks/valve_handles.json
eac71740fb12bdb38b5dfaa2268613d7ba82b809 data/create/tags/blocks/windmill_sails.json eac71740fb12bdb38b5dfaa2268613d7ba82b809 data/create/tags/blocks/windmill_sails.json

View file

@ -1852,6 +1852,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 [------------------------<-",

View file

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

View file

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

View file

@ -851,7 +851,7 @@ public class AllBlockEntityTypes {
.renderer(() -> StationRenderer::new) .renderer(() -> StationRenderer::new)
.validBlocks(AllBlocks.TRACK_STATION) .validBlocks(AllBlocks.TRACK_STATION)
.register(); .register();
public static final BlockEntityEntry<SlidingDoorBlockEntity> SLIDING_DOOR = public static final BlockEntityEntry<SlidingDoorBlockEntity> SLIDING_DOOR =
REGISTRATE.blockEntity("sliding_door", SlidingDoorBlockEntity::new) REGISTRATE.blockEntity("sliding_door", SlidingDoorBlockEntity::new)
.renderer(() -> SlidingDoorRenderer::new) .renderer(() -> SlidingDoorRenderer::new)
@ -859,10 +859,10 @@ 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();
public static final BlockEntityEntry<FlapDisplayBlockEntity> FLAP_DISPLAY = REGISTRATE public static final BlockEntityEntry<FlapDisplayBlockEntity> FLAP_DISPLAY = REGISTRATE
.blockEntity("flap_display", FlapDisplayBlockEntity::new) .blockEntity("flap_display", FlapDisplayBlockEntity::new)

View file

@ -230,6 +230,9 @@ 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.BogeyRenderer;
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 +721,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 +941,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 +1544,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 +1555,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 +1625,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();

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

@ -81,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,
@ -132,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;
@ -127,6 +129,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);

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.Create; import com.simibubi.create.Create;
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;
@ -69,7 +69,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

@ -8,6 +8,7 @@ import java.util.Random;
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;
@ -224,7 +225,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.getTrackBlock().get(), Math.min(64, tracks)), info.hasRequiredTracks));
tracks -= 64; tracks -= 64;
} }

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 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.AbstractBogeyTileEntity;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.foundation.block.ITE;
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;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractBogeyBlock<T extends AbstractBogeyTileEntity> extends Block implements ITE<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 AbstractBogeyTileEntity 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 AbstractBogeyTileEntity 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 AbstractBogeyTileEntity 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(AbstractBogeyTileEntity 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(AbstractBogeyTileEntity 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 AbstractBogeyTileEntity 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().getTrackBlock().get(), 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, new ItemStack(getMaterial().getTrackBlock().get()));
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().getTrackBlock().get().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,137 @@
package com.simibubi.create.content.logistics.trains;
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;
import org.jetbrains.annotations.Nullable;
import static com.simibubi.create.AllBlockPartials.LARGE_BOGEY_WHEELS;
import static com.simibubi.create.AllBlockPartials.BOGEY_PIN;
import static com.simibubi.create.AllBlockPartials.BOGEY_DRIVE;
import static com.simibubi.create.AllBlockPartials.BOGEY_PISTON;
import static com.simibubi.create.AllBlockPartials.SMALL_BOGEY_WHEELS;
import static com.simibubi.create.AllBlockPartials.BOGEY_FRAME;
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)
@ -268,4 +269,4 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
updatedEdgeData.put(key, Pair.of(groupType, list)); updatedEdgeData.put(key, Pair.of(groupType, list));
} }
} }

View file

@ -20,6 +20,7 @@ import com.simibubi.create.foundation.utility.outliner.Outliner;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -213,7 +214,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 +267,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,
new ItemStack(edge.getTrackMaterial().trackBlock.get()
.get()));
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 +289,17 @@ 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,
new ItemStack(edge.getTrackMaterial().trackBlock.get()
.get()));
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,144 @@
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 com.jozufozu.flywheel.core.PartialModel;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.AllBlocks;
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.crafting.Ingredient;
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;
@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 = id;
this.langName = langName;
this.trackBlock = trackBlock;
this.sleeperIngredient = sleeperIngredient;
this.railsIngredient = railsIngredient;
this.particle = particle;
this.trackType = trackType;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.modelHolder = modelHolder.get().get());
ALL.put(this.id, this);
}
public NonNullSupplier<? extends TrackBlock> getTrackBlock() {
return this.trackBlock.get();
}
public TrackBlock createBlock(BlockBehaviour.Properties properties) {
return this.trackType.factory.create(properties, this);
}
public boolean isCustom(String modId) {
return this.id.getNamespace().equals(modId);
}
public static TrackMaterial[] allCustom(String modid) {
return ALL.values().stream().filter(tm -> tm.isCustom(modid)).toArray(TrackMaterial[]::new);
}
public static List<NonNullSupplier<? extends TrackBlock>> allCustomBlocks(String modid) {
List<NonNullSupplier<? extends TrackBlock>> list = new ArrayList<>();
for (TrackMaterial material : allCustom(modid)) {
list.add(material.getTrackBlock());
}
return list;
}
public static List<NonNullSupplier<? extends TrackBlock>> allBlocks() {
List<NonNullSupplier<? extends TrackBlock>> list = new ArrayList<>();
for (TrackMaterial material : ALL.values()) {
list.add(material.getTrackBlock());
}
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 (id != null)
for (TrackMaterial material : ALL.values())
if (material.id.equals(id))
return material;
Create.LOGGER.error("Failed to locate serialized track material: " + serializedName);
return ANDESITE;
}
public static class TrackType {
@FunctionalInterface
protected 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(AllBlockPartials.TRACK_TIE, AllBlockPartials.TRACK_SEGMENT_LEFT, AllBlockPartials.TRACK_SEGMENT_RIGHT);
}
}

View file

@ -0,0 +1,133 @@
package com.simibubi.create.content.logistics.trains;
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;
import java.util.function.Supplier;
import java.util.stream.Stream;
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;
@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 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);
}
}

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

@ -234,10 +234,12 @@ 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;
Vec3 direction = location.direction; Vec3 direction = location.direction;
if (direction != null && next.stream() if (direction != null && next.stream()
.anyMatch(dl -> dl.notInLineWith(direction))) .anyMatch(dl -> dl.notInLineWith(direction)))

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;
} }
@ -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);
} }

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;
@ -71,7 +71,7 @@ public class CarriageContraption extends Contraption {
// render // render
public int portalCutoffMin; public int portalCutoffMin;
public int portalCutoffMax; public int portalCutoffMax;
static final IItemHandlerModifiable fallbackItems = new ItemStackHandler(); static final IItemHandlerModifiable fallbackItems = new ItemStackHandler();
static final IFluidHandler fallbackFluids = new FluidTank(0); static final IFluidHandler fallbackFluids = new FluidTank(0);
@ -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 ? getTileEntityNBT(world, pos) : null),
captureTE ? world.getBlockEntity(pos) : null);
} }
if (AllBlocks.BLAZE_BURNER.has(blockState) if (AllBlocks.BLAZE_BURNER.has(blockState)
@ -235,7 +237,7 @@ public class CarriageContraption extends Contraption {
protected MountedStorageManager getStorageForSpawnPacket() { protected MountedStorageManager getStorageForSpawnPacket() {
return storageProxy; return storageProxy;
} }
@Override @Override
protected ContraptionType getType() { protected ContraptionType getType() {
return ContraptionType.CARRIAGE; return ContraptionType.CARRIAGE;

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> {
@ -37,7 +38,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
MultiBufferSource buffers, int overlay) { MultiBufferSource buffers, int overlay) {
if (!entity.validForRender || entity.firstPositionUpdate) if (!entity.validForRender || entity.firstPositionUpdate)
return; return;
super.render(entity, yaw, partialTicks, ms, buffers, overlay); super.render(entity, yaw, partialTicks, ms, buffers, overlay);
Carriage carriage = entity.getCarriage(); Carriage carriage = entity.getCarriage();
@ -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

@ -10,7 +10,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.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -110,7 +109,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;
@ -29,6 +30,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;
@ -36,6 +40,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();
@ -79,6 +87,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);
@ -97,7 +114,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),
@ -205,7 +222,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

@ -17,6 +17,10 @@ import java.util.function.Consumer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyTileEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
@ -124,7 +128,7 @@ public class Train {
public int honkPitch; public int honkPitch;
public float accumulatedSteamRelease; public float accumulatedSteamRelease;
int tickOffset; int tickOffset;
double[] stress; double[] stress;
@ -277,7 +281,7 @@ public class Train {
int carriageCount = carriages.size(); int carriageCount = carriages.size();
boolean stalled = false; boolean stalled = false;
double maxStress = 0; double maxStress = 0;
if (carriageWaitingForChunks != -1) if (carriageWaitingForChunks != -1)
distance = 0; distance = 0;
@ -313,11 +317,17 @@ 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++;
} }
} }
if (entries > 0) if (entries > 0)
actual = total / entries; actual = total / entries;
@ -369,13 +379,13 @@ public class Train {
.getLeadingPoint(); .getLeadingPoint();
double totalStress = derailed ? 0 : leadingStress + trailingStress; double totalStress = derailed ? 0 : leadingStress + trailingStress;
boolean first = i == 0; boolean first = i == 0;
boolean last = i == carriageCount - 1; boolean last = i == carriageCount - 1;
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 +732,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 AbstractBogeyTileEntity sbte))
continue;
sbte.setBogeyData(bogey.bogeyData);
}
offset += carriage.bogeySpacing; offset += carriage.bogeySpacing;
if (i < carriageSpacing.size()) if (i < carriageSpacing.size())
@ -944,7 +965,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);

View file

@ -6,12 +6,13 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.networking.SimplePacketBase; import com.simibubi.create.foundation.networking.SimplePacketBase;
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.RegisteredObjects; import com.simibubi.create.foundation.utility.RegisteredObjects;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@ -43,11 +44,13 @@ public class TrainPacket extends SimplePacketBase {
int size = buffer.readVarInt(); int size = buffer.readVarInt();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Couple<CarriageBogey> bogies = Couple.create(null, null); Couple<CarriageBogey> bogies = Couple.create(null, null);
for (boolean first : Iterate.trueAndFalse) { for (boolean isFirst : Iterate.trueAndFalse) {
if (!first && !buffer.readBoolean()) if (!isFirst && !buffer.readBoolean())
continue; continue;
IBogeyBlock type = (IBogeyBlock) ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation()); AbstractBogeyBlock<?> type = (AbstractBogeyBlock<?>) ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation());
bogies.set(first, new CarriageBogey(type, new TravellingPoint(), new TravellingPoint())); boolean upsideDown = buffer.readBoolean();
CompoundTag data = buffer.readNbt();
bogies.set(isFirst, new CarriageBogey(type, upsideDown, data, new TravellingPoint(), new TravellingPoint()));
} }
int spacing = buffer.readVarInt(); int spacing = buffer.readVarInt();
carriages.add(new Carriage(bogies.getFirst(), bogies.getSecond(), spacing)); carriages.add(new Carriage(bogies.getFirst(), bogies.getSecond(), spacing));
@ -85,6 +88,8 @@ public class TrainPacket extends SimplePacketBase {
} }
CarriageBogey bogey = carriage.bogeys.get(first); CarriageBogey bogey = carriage.bogeys.get(first);
buffer.writeResourceLocation(RegisteredObjects.getKeyOrThrow((Block) bogey.type)); buffer.writeResourceLocation(RegisteredObjects.getKeyOrThrow((Block) bogey.type));
buffer.writeBoolean(bogey.upsideDown);
buffer.writeNbt(bogey.bogeyData);
} }
buffer.writeVarInt(carriage.bogeySpacing); buffer.writeVarInt(carriage.bogeySpacing);
} }

View file

@ -115,10 +115,13 @@ public class TrainRelocator {
BlockPos blockPos = blockhit.getBlockPos(); BlockPos blockPos = blockhit.getBlockPos();
BezierTrackPointLocation hoveredBezier = null; BezierTrackPointLocation hoveredBezier = null;
boolean upsideDown = relocating.carriages.get(0).leadingBogey().isUpsideDown();
Vec3 offset = upsideDown ? new Vec3(0, -0.5, 0) : Vec3.ZERO;
if (simulate && toVisualise != null && lastHoveredResult != null) { if (simulate && toVisualise != null && lastHoveredResult != null) {
for (int i = 0; i < toVisualise.size() - 1; i++) { for (int i = 0; i < toVisualise.size() - 1; i++) {
Vec3 vec1 = toVisualise.get(i); Vec3 vec1 = toVisualise.get(i).add(offset);
Vec3 vec2 = toVisualise.get(i + 1); Vec3 vec2 = toVisualise.get(i + 1).add(offset);
CreateClient.OUTLINER.showLine(Pair.of(relocating, i), vec1.add(0, -.925f, 0), vec2.add(0, -.925f, 0)) CreateClient.OUTLINER.showLine(Pair.of(relocating, i), vec1.add(0, -.925f, 0), vec2.add(0, -.925f, 0))
.colored(lastHoveredResult || i != toVisualise.size() - 2 ? 0x95CD41 : 0xEA5C2B) .colored(lastHoveredResult || i != toVisualise.size() - 2 ? 0x95CD41 : 0xEA5C2B)
.disableLineNormals() .disableLineNormals()
@ -150,7 +153,7 @@ public class TrainRelocator {
boolean direction = bezierSelection != null && lookAngle.dot(bezierSelection.direction()) < 0; boolean direction = bezierSelection != null && lookAngle.dot(bezierSelection.direction()) < 0;
boolean result = relocate(relocating, mc.level, blockPos, hoveredBezier, direction, lookAngle, true); boolean result = relocate(relocating, mc.level, blockPos, hoveredBezier, direction, lookAngle, true);
if (!simulate && result) { if (!simulate && result) {
relocating.carriages.forEach(c -> c.forEachPresentEntity(e -> e.nonDamageTicks = 10)); relocating.carriages.forEach(c -> c.forEachPresentEntity(e -> e.nonDamageTicks = 10));
AllPackets.getChannel().sendToServer(new TrainRelocationPacket(relocatingTrain, blockPos, hoveredBezier, AllPackets.getChannel().sendToServer(new TrainRelocationPacket(relocatingTrain, blockPos, hoveredBezier,
direction, lookAngle, relocatingEntityId)); direction, lookAngle, relocatingEntityId));
} }
@ -182,7 +185,7 @@ public class TrainRelocator {
if (edge == null) if (edge == null)
return false; return false;
TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position); TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position, false);
IEdgePointListener ignoreSignals = probe.ignoreEdgePoints(); IEdgePointListener ignoreSignals = probe.ignoreEdgePoints();
ITurnListener ignoreTurns = probe.ignoreTurns(); ITurnListener ignoreTurns = probe.ignoreTurns();
List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>(); List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>();

View file

@ -38,6 +38,7 @@ public class TravellingPoint {
public TrackEdge edge; public TrackEdge edge;
public double position; public double position;
public boolean blocked; public boolean blocked;
public boolean upsideDown;
public static enum SteerDirection { public static enum SteerDirection {
NONE(0), LEFT(-1), RIGHT(1); NONE(0), LEFT(-1), RIGHT(1);
@ -64,11 +65,12 @@ public class TravellingPoint {
public TravellingPoint() {} public TravellingPoint() {}
public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position) { public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position, boolean upsideDown) {
this.node1 = node1; this.node1 = node1;
this.node2 = node2; this.node2 = node2;
this.edge = edge; this.edge = edge;
this.position = position; this.position = position;
this.upsideDown = upsideDown;
} }
public IEdgePointListener ignoreEdgePoints() { public IEdgePointListener ignoreEdgePoints() {
@ -395,13 +397,18 @@ public class TravellingPoint {
} }
public Vec3 getPosition(@Nullable TrackGraph trackGraph) { public Vec3 getPosition(@Nullable TrackGraph trackGraph) {
return getPositionWithOffset(trackGraph, 0); return getPosition(trackGraph, false);
} }
public Vec3 getPositionWithOffset(@Nullable TrackGraph trackGraph, double offset) { public Vec3 getPosition(@Nullable TrackGraph trackGraph, boolean flipUpsideDown) {
return getPositionWithOffset(trackGraph, 0, flipUpsideDown);
}
public Vec3 getPositionWithOffset(@Nullable TrackGraph trackGraph, double offset, boolean flipUpsideDown) {
double t = (position + offset) / edge.getLength(); double t = (position + offset) / edge.getLength();
return edge.getPosition(trackGraph, t) return edge.getPosition(trackGraph, t)
.add(edge.getNormal(trackGraph, t)); .add(edge.getNormal(trackGraph, t)
.scale(upsideDown ^ flipUpsideDown ? -1 : 1));
} }
public void migrateTo(List<GraphLocation> locations) { public void migrateTo(List<GraphLocation> locations) {
@ -422,12 +429,13 @@ public class TravellingPoint {
tag.put("Nodes", nodes.map(TrackNode::getLocation) tag.put("Nodes", nodes.map(TrackNode::getLocation)
.serializeEach(loc -> loc.write(dimensions))); .serializeEach(loc -> loc.write(dimensions)));
tag.putDouble("Position", position); tag.putDouble("Position", position);
tag.putBoolean("UpsideDown", upsideDown);
return tag; return tag;
} }
public static TravellingPoint read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { public static TravellingPoint read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) {
if (graph == null) if (graph == null)
return new TravellingPoint(null, null, null, 0); return new TravellingPoint(null, null, null, 0, false);
Couple<TrackNode> locs = tag.contains("Nodes") Couple<TrackNode> locs = tag.contains("Nodes")
? Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), c -> TrackNodeLocation.read(c, dimensions)) ? Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), c -> TrackNodeLocation.read(c, dimensions))
@ -435,11 +443,11 @@ public class TravellingPoint {
: Couple.create(null, null); : Couple.create(null, null);
if (locs.either(Objects::isNull)) if (locs.either(Objects::isNull))
return new TravellingPoint(null, null, null, 0); return new TravellingPoint(null, null, null, 0, false);
double position = tag.getDouble("Position"); double position = tag.getDouble("Position");
return new TravellingPoint(locs.getFirst(), locs.getSecond(), graph.getConnectionsFrom(locs.getFirst()) return new TravellingPoint(locs.getFirst(), locs.getSecond(), graph.getConnectionsFrom(locs.getFirst())
.get(locs.getSecond()), position); .get(locs.getSecond()), position, tag.getBoolean("UpsideDown"));
} }
} }

View file

@ -22,7 +22,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.ITr
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform; import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.logistics.block.depot.DepotBehaviour; import com.simibubi.create.content.logistics.block.depot.DepotBehaviour;
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlock; import com.simibubi.create.content.logistics.block.display.DisplayLinkBlock;
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.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
@ -39,6 +39,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePoi
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour;
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule; import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleItem; import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleItem;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyTileEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock; import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour; import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
@ -52,6 +53,7 @@ import com.simibubi.create.foundation.utility.WorldAttached;
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;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -67,6 +69,7 @@ import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
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;
import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.BoundingBox;
@ -192,7 +195,8 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
Direction assemblyDirection; Direction assemblyDirection;
int assemblyLength; int assemblyLength;
int[] bogeyLocations; int[] bogeyLocations;
IBogeyBlock[] bogeyTypes; AbstractBogeyBlock<?>[] bogeyTypes;
boolean[] upsideDownBogeys;
int bogeyCount; int bogeyCount;
@Override @Override
@ -269,14 +273,30 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return false; return false;
BlockPos up = new BlockPos(track.getUpNormal(level, pos, state)); BlockPos up = new BlockPos(track.getUpNormal(level, pos, state));
BlockPos down = new BlockPos(track.getUpNormal(level, pos, state).scale(-1));
int bogeyOffset = pos.distManhattan(edgePoint.getGlobalPosition()) - 1; int bogeyOffset = pos.distManhattan(edgePoint.getGlobalPosition()) - 1;
if (!isValidBogeyOffset(bogeyOffset)) { if (!isValidBogeyOffset(bogeyOffset)) {
for (int i = -1; i <= 1; i++) { for (boolean upsideDown : Iterate.falseAndTrue) {
BlockPos bogeyPos = pos.relative(assemblyDirection, i) for (int i = -1; i <= 1; i++) {
.offset(up); BlockPos bogeyPos = pos.relative(assemblyDirection, i)
BlockState blockState = level.getBlockState(bogeyPos); .offset(upsideDown ? down : up);
if (blockState.getBlock() instanceof IBogeyBlock bogey) { BlockState blockState = level.getBlockState(bogeyPos);
level.setBlock(bogeyPos, bogey.getRotatedBlockState(blockState, Direction.DOWN), 3); if (!(blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey))
continue;
BlockEntity be = level.getBlockEntity(bogeyPos);
if (!(be instanceof AbstractBogeyTileEntity oldTE))
continue;
CompoundTag oldData = oldTE.getBogeyData();
BlockState newBlock = bogey.getNextSize(oldTE);
if (newBlock.getBlock() == bogey)
player.displayClientMessage(Lang.translateDirect("bogey.style.no_other_sizes")
.withStyle(ChatFormatting.RED), true);
level.setBlock(bogeyPos, newBlock, 3);
BlockEntity newEntity = level.getBlockEntity(bogeyPos);
if (!(newEntity instanceof AbstractBogeyTileEntity newTE))
continue;
newTE.setBogeyData(oldData);
bogey.playRotateSound(level, bogeyPos); bogey.playRotateSound(level, bogeyPos);
return true; return true;
} }
@ -291,7 +311,9 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return false; return false;
} }
BlockPos targetPos = pos.offset(up); boolean upsideDown = (player.getViewXRot(1.0F) < 0 && (track.getBogeyAnchor(level, pos, state)).getBlock() instanceof AbstractBogeyBlock<?> bogey && bogey.canBeUpsideDown());
BlockPos targetPos = upsideDown ? pos.offset(down) : pos.offset(up);
if (level.getBlockState(targetPos) if (level.getBlockState(targetPos)
.getDestroySpeed(level, targetPos) == -1) { .getDestroySpeed(level, targetPos) == -1) {
return false; return false;
@ -299,7 +321,11 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
level.destroyBlock(targetPos, true); level.destroyBlock(targetPos, true);
BlockState bogeyAnchor = ProperWaterloggedBlock.withWater(level, track.getBogeyAnchor(level, pos, state), pos); BlockState bogeyAnchor = track.getBogeyAnchor(level, pos, state);
if (bogeyAnchor.getBlock() instanceof AbstractBogeyBlock<?> bogey) {
bogeyAnchor = bogey.getVersion(bogeyAnchor, upsideDown);
}
bogeyAnchor = ProperWaterloggedBlock.withWater(level, bogeyAnchor, pos);
level.setBlock(targetPos, bogeyAnchor, 3); level.setBlock(targetPos, bogeyAnchor, 3);
player.displayClientMessage(Lang.translateDirect("train_assembly.bogey_created"), true); player.displayClientMessage(Lang.translateDirect("train_assembly.bogey_created"), true);
SoundType soundtype = bogeyAnchor.getBlock() SoundType soundtype = bogeyAnchor.getBlock()
@ -373,9 +399,12 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
if (bogeyLocations == null) if (bogeyLocations == null)
bogeyLocations = new int[maxBogeyCount]; bogeyLocations = new int[maxBogeyCount];
if (bogeyTypes == null) if (bogeyTypes == null)
bogeyTypes = new IBogeyBlock[maxBogeyCount]; bogeyTypes = new AbstractBogeyBlock[maxBogeyCount];
if (upsideDownBogeys == null)
upsideDownBogeys = new boolean[maxBogeyCount];
Arrays.fill(bogeyLocations, -1); Arrays.fill(bogeyLocations, -1);
Arrays.fill(bogeyTypes, null); Arrays.fill(bogeyTypes, null);
Arrays.fill(upsideDownBogeys, false);
for (int i = 0; i < MAX_LENGTH; i++) { for (int i = 0; i < MAX_LENGTH; i++) {
if (i == MAX_LENGTH - 1) { if (i == MAX_LENGTH - 1) {
@ -388,10 +417,19 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
} }
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos)); BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock() instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) { BlockPos upsideDownBogeyOffset = new BlockPos(bogeyOffset.getX(), bogeyOffset.getY()*-1, bogeyOffset.getZ());
bogeyTypes[bogeyIndex] = bogey; if (bogeyIndex < bogeyLocations.length) {
bogeyLocations[bogeyIndex] = i; if (potentialBogeyState.getBlock() instanceof AbstractBogeyBlock<?> bogey && !bogey.isUpsideDown(potentialBogeyState)) {
bogeyIndex++; bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
upsideDownBogeys[bogeyIndex] = false;
bogeyIndex++;
} else if ((potentialBogeyState = level.getBlockState(upsideDownBogeyOffset.offset(currentPos))).getBlock() instanceof AbstractBogeyBlock<?> bogey && bogey.isUpsideDown(potentialBogeyState)) {
bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
upsideDownBogeys[bogeyIndex] = true;
bogeyIndex++;
}
} }
currentPos.move(assemblyDirection); currentPos.move(assemblyDirection);
@ -551,7 +589,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return; return;
} }
points.add(new TravellingPoint(node, secondNode, edge, positionOnEdge)); points.add(new TravellingPoint(node, secondNode, edge, positionOnEdge, false));
} }
secondNode = node; secondNode = node;
@ -578,10 +616,11 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
spacing.add(bogeyLocations[bogeyIndex] - bogeyLocations[bogeyIndex - 1]); spacing.add(bogeyLocations[bogeyIndex] - bogeyLocations[bogeyIndex - 1]);
CarriageContraption contraption = new CarriageContraption(assemblyDirection); CarriageContraption contraption = new CarriageContraption(assemblyDirection);
BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset); BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset);
BlockPos upsideDownBogeyPosOffset = trackPosition.offset(new BlockPos(bogeyOffset.getX(), bogeyOffset.getY() * -1, bogeyOffset.getZ()));
try { try {
int offset = bogeyLocations[bogeyIndex] + 1; int offset = bogeyLocations[bogeyIndex] + 1;
boolean success = contraption.assemble(level, bogeyPosOffset.relative(assemblyDirection, offset)); boolean success = contraption.assemble(level, upsideDownBogeys[bogeyIndex] ? upsideDownBogeyPosOffset.relative(assemblyDirection, offset) : bogeyPosOffset.relative(assemblyDirection, offset));
atLeastOneForwardControls |= contraption.hasForwardControls(); atLeastOneForwardControls |= contraption.hasForwardControls();
contraption.setSoundQueueOffset(offset); contraption.setSoundQueueOffset(offset);
if (!success) { if (!success) {
@ -594,24 +633,28 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return; return;
} }
IBogeyBlock typeOfFirstBogey = bogeyTypes[bogeyIndex]; AbstractBogeyBlock<?> typeOfFirstBogey = bogeyTypes[bogeyIndex];
boolean firstBogeyIsUpsideDown = upsideDownBogeys[bogeyIndex];
BlockPos firstBogeyPos = contraption.anchor;
AbstractBogeyTileEntity firstBogeyTileEntity = (AbstractBogeyTileEntity) level.getBlockEntity(firstBogeyPos);
CarriageBogey firstBogey = CarriageBogey firstBogey =
new CarriageBogey(typeOfFirstBogey, points.get(pointIndex), points.get(pointIndex + 1)); new CarriageBogey(typeOfFirstBogey, firstBogeyIsUpsideDown, firstBogeyTileEntity.getBogeyData(), points.get(pointIndex), points.get(pointIndex + 1));
CarriageBogey secondBogey = null; CarriageBogey secondBogey = null;
BlockPos secondBogeyPos = contraption.getSecondBogeyPos(); BlockPos secondBogeyPos = contraption.getSecondBogeyPos();
int bogeySpacing = 0; int bogeySpacing = 0;
if (secondBogeyPos != null) { if (secondBogeyPos != null) {
if (bogeyIndex == bogeyCount - 1 || !secondBogeyPos if (bogeyIndex == bogeyCount - 1 || !secondBogeyPos
.equals(bogeyPosOffset.relative(assemblyDirection, bogeyLocations[bogeyIndex + 1] + 1))) { .equals((upsideDownBogeys[bogeyIndex + 1] ? upsideDownBogeyPosOffset : bogeyPosOffset).relative(assemblyDirection, bogeyLocations[bogeyIndex + 1] + 1))) {
exception(new AssemblyException(Lang.translateDirect("train_assembly.not_connected_in_order")), exception(new AssemblyException(Lang.translateDirect("train_assembly.not_connected_in_order")),
contraptions.size() + 1); contraptions.size() + 1);
return; return;
} }
AbstractBogeyTileEntity secondBogeyTileEntity =
(AbstractBogeyTileEntity) level.getBlockEntity(secondBogeyPos);
bogeySpacing = bogeyLocations[bogeyIndex + 1] - bogeyLocations[bogeyIndex]; bogeySpacing = bogeyLocations[bogeyIndex + 1] - bogeyLocations[bogeyIndex];
secondBogey = new CarriageBogey(bogeyTypes[bogeyIndex + 1], points.get(pointIndex + 2), secondBogey = new CarriageBogey(bogeyTypes[bogeyIndex + 1], upsideDownBogeys[bogeyIndex + 1], secondBogeyTileEntity.getBogeyData(),
points.get(pointIndex + 3)); points.get(pointIndex + 2), points.get(pointIndex + 3));
bogeyIndex++; bogeyIndex++;
} else if (!typeOfFirstBogey.allowsSingleBogeyCarriage()) { } else if (!typeOfFirstBogey.allowsSingleBogeyCarriage()) {

View file

@ -0,0 +1,119 @@
package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.foundation.tileEntity.CachedRenderBBTileEntity;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import static com.simibubi.create.content.logistics.trains.entity.CarriageBogey.UPSIDE_DOWN_KEY;
public abstract class AbstractBogeyTileEntity extends CachedRenderBBTileEntity {
public static final String BOGEY_STYLE_KEY = "BogeyStyle";
public static final String BOGEY_DATA_KEY = "BogeyData";
private CompoundTag bogeyData;
public AbstractBogeyTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
public abstract BogeyStyle getDefaultStyle();
public CompoundTag getBogeyData() {
if (this.bogeyData == null || !this.bogeyData.contains(BOGEY_STYLE_KEY))
this.bogeyData = this.createBogeyData();
return this.bogeyData;
}
public void setBogeyData(@NotNull CompoundTag newData) {
if (!newData.contains(BOGEY_STYLE_KEY)) {
ResourceLocation style = getDefaultStyle().name;
NBTHelper.writeResourceLocation(newData, BOGEY_STYLE_KEY, style);
}
this.bogeyData = newData;
}
public void setBogeyStyle(@NotNull BogeyStyle style) {
ResourceLocation location = style.name;
CompoundTag data = this.getBogeyData();
NBTHelper.writeResourceLocation(data, BOGEY_STYLE_KEY, location);
markUpdated();
}
@NotNull
public BogeyStyle getStyle() {
CompoundTag data = this.getBogeyData();
ResourceLocation currentStyle = NBTHelper.readResourceLocation(data, BOGEY_STYLE_KEY);
BogeyStyle style = AllBogeyStyles.BOGEY_STYLES.get(currentStyle);
if (style == null) {
setBogeyStyle(getDefaultStyle());
return getStyle();
}
return style;
}
@Override
protected void saveAdditional(@NotNull CompoundTag pTag) {
CompoundTag data = this.getBogeyData();
if (data != null) pTag.put(BOGEY_DATA_KEY, data); // Now contains style
super.saveAdditional(pTag);
}
@Override
public void load(CompoundTag pTag) {
if (pTag.contains(BOGEY_DATA_KEY))
this.bogeyData = pTag.getCompound(BOGEY_DATA_KEY);
else
this.bogeyData = this.createBogeyData();
super.load(pTag);
}
private CompoundTag createBogeyData() {
CompoundTag nbt = new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, getDefaultStyle().name);
boolean upsideDown = false;
if (getBlockState().getBlock() instanceof AbstractBogeyBlock<?> bogeyBlock)
upsideDown = bogeyBlock.isUpsideDown(getBlockState());
nbt.putBoolean(UPSIDE_DOWN_KEY, upsideDown);
return nbt;
}
@Override
protected AABB createRenderBoundingBox() {
return super.createRenderBoundingBox().inflate(2);
}
// Ponder
LerpedFloat virtualAnimation = LerpedFloat.angular();
public float getVirtualAngle(float partialTicks) {
return virtualAnimation.getValue(partialTicks);
}
public void animate(float distanceMoved) {
BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof AbstractBogeyBlock<?> type))
return;
double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius());
double newWheelAngle = (virtualAnimation.getValue() - angleDiff) % 360;
virtualAnimation.setValue(newWheelAngle);
}
private void markUpdated() {
setChanged();
Level level = getLevel();
if (level != null)
getLevel().sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains.track; package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline.BezierPointSelection; import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline.BezierPointSelection;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -117,7 +117,7 @@ public class CurvedTrackInteraction {
if (event.isUseItem()) { if (event.isUseItem()) {
ItemStack heldItem = player.getMainHandItem(); ItemStack heldItem = player.getMainHandItem();
Item item = heldItem.getItem(); Item item = heldItem.getItem();
if (AllBlocks.TRACK.isIn(heldItem)) { if (AllTags.AllBlockTags.TRACKS.matches(heldItem)) {
player.displayClientMessage(Lang.translateDirect("track.turn_start") player.displayClientMessage(Lang.translateDirect("track.turn_start")
.withStyle(ChatFormatting.RED), true); .withStyle(ChatFormatting.RED), true);
player.swing(InteractionHand.MAIN_HAND); player.swing(InteractionHand.MAIN_HAND);

View file

@ -1,6 +1,6 @@
package com.simibubi.create.content.logistics.trains.track; package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTags;
import com.simibubi.create.foundation.networking.SimplePacketBase; import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@ -14,7 +14,7 @@ public class PlaceExtendedCurvePacket extends SimplePacketBase {
boolean mainHand; boolean mainHand;
boolean ctrlDown; boolean ctrlDown;
public PlaceExtendedCurvePacket(boolean mainHand, boolean ctrlDown) { public PlaceExtendedCurvePacket(boolean mainHand, boolean ctrlDown) {
this.mainHand = mainHand; this.mainHand = mainHand;
this.ctrlDown = ctrlDown; this.ctrlDown = ctrlDown;
@ -36,7 +36,7 @@ public class PlaceExtendedCurvePacket extends SimplePacketBase {
context.enqueueWork(() -> { context.enqueueWork(() -> {
ServerPlayer sender = context.getSender(); ServerPlayer sender = context.getSender();
ItemStack stack = sender.getItemInHand(mainHand ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND); ItemStack stack = sender.getItemInHand(mainHand ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND);
if (!AllBlocks.TRACK.isIn(stack) || !stack.hasTag()) if (!AllTags.AllBlockTags.TRACKS.matches(stack) || !stack.hasTag())
return; return;
CompoundTag tag = stack.getTag(); CompoundTag tag = stack.getTag();
tag.putBoolean("ExtendCurve", true); tag.putBoolean("ExtendCurve", true);

View file

@ -1,87 +1,36 @@
package com.simibubi.create.content.logistics.trains.track; package com.simibubi.create.content.logistics.trains.track;
import java.util.EnumSet;
import com.jozufozu.flywheel.api.MaterialManager;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels; import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.entity.BogeyInstance; import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey; import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement; import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
import com.simibubi.create.foundation.block.IBE; import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock; import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Rotation;
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;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class StandardBogeyBlock extends Block public class StandardBogeyBlock extends AbstractBogeyBlock<StandardBogeyBlockEntity>
implements IBogeyBlock, IBE<StandardBogeyBlockEntity>, ProperWaterloggedBlock, ISpecialBlockItemRequirement { implements IBE<StandardBogeyBlockEntity>, ProperWaterloggedBlock, ISpecialBlockItemRequirement {
public static final EnumProperty<Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS; public StandardBogeyBlock(Properties props, BogeySizes.BogeySize size) {
private final boolean large; super(props, size);
public StandardBogeyBlock(Properties p_i48440_1_, boolean large) {
super(p_i48440_1_);
this.large = large;
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false)); registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false));
} }
@Override @Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) { public TrackMaterial.TrackType getTrackType(BogeyStyle style) {
builder.add(AXIS, WATERLOGGED); return TrackMaterial.TrackType.STANDARD;
super.createBlockStateDefinition(builder);
}
static final EnumSet<Direction> STICKY_X = EnumSet.of(Direction.EAST, Direction.WEST);
static final EnumSet<Direction> STICKY_Z = EnumSet.of(Direction.SOUTH, Direction.NORTH);
@Override
public EnumSet<Direction> getStickySurfaces(BlockGetter world, BlockPos pos, BlockState state) {
return state.getValue(BlockStateProperties.HORIZONTAL_AXIS) == Axis.X ? STICKY_X : STICKY_Z;
}
@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);
} }
@Override @Override
@ -91,7 +40,7 @@ public class StandardBogeyBlock extends Block
@Override @Override
public double getWheelRadius() { public double getWheelRadius() {
return (large ? 12.5 : 6.5) / 16d; return (size == BogeySizes.LARGE ? 12.5 : 6.5) / 16d;
} }
@Override @Override
@ -100,122 +49,8 @@ public class StandardBogeyBlock extends Block
} }
@Override @Override
public boolean allowsSingleBogeyCarriage() { public BogeyStyle getDefaultStyle() {
return true; return AllBogeyStyles.STANDARD;
}
@Override
public BlockState getMatchingBogey(Direction upDirection, boolean axisAlongFirst) {
if (upDirection != Direction.UP)
return null;
return defaultBlockState().setValue(AXIS, axisAlongFirst ? Axis.X : Axis.Z);
}
@Override
public boolean isTrackAxisAlongFirstCoordinate(BlockState state) {
return state.getValue(AXIS) == Axis.X;
}
@Override
@OnlyIn(Dist.CLIENT)
public void render(BlockState state, float wheelAngle, PoseStack ms, float partialTicks, MultiBufferSource buffers,
int light, int overlay) {
if (state != null) {
ms.translate(.5f, .5f, .5f);
if (state.getValue(AXIS) == Axis.X)
ms.mulPose(Vector3f.YP.rotationDegrees(90));
}
ms.translate(0, -1.5 - 1 / 128f, 0);
VertexConsumer vb = buffers.getBuffer(RenderType.cutoutMipped());
BlockState air = Blocks.AIR.defaultBlockState();
for (int i : Iterate.zeroAndOne)
CachedBufferer.block(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Axis.Z))
.translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre()
.light(light)
.renderInto(ms, vb);
if (large) {
renderLargeBogey(wheelAngle, ms, light, vb, air);
} else {
renderBogey(wheelAngle, ms, light, vb, air);
}
}
private void renderBogey(float wheelAngle, PoseStack ms, int light, VertexConsumer vb, BlockState air) {
CachedBufferer.partial(AllPartialModels.BOGEY_FRAME, air)
.scale(1 - 1 / 512f)
.light(light)
.renderInto(ms, vb);
for (int side : Iterate.positiveAndNegative) {
ms.pushPose();
CachedBufferer.partial(AllPartialModels.SMALL_BOGEY_WHEELS, air)
.translate(0, 12 / 16f, side)
.rotateX(wheelAngle)
.light(light)
.renderInto(ms, vb);
ms.popPose();
}
}
private void renderLargeBogey(float wheelAngle, PoseStack ms, int light, VertexConsumer vb, BlockState air) {
for (int i : Iterate.zeroAndOne)
CachedBufferer.block(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Axis.X))
.translate(-.5f, .25f, .5f + i * -2)
.centre()
.rotateX(wheelAngle)
.unCentre()
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllPartialModels.BOGEY_DRIVE, air)
.scale(1 - 1 / 512f)
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllPartialModels.BOGEY_PISTON, air)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.light(light)
.renderInto(ms, vb);
ms.pushPose();
CachedBufferer.partial(AllPartialModels.LARGE_BOGEY_WHEELS, air)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllPartialModels.BOGEY_PIN, air)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle)
.light(light)
.renderInto(ms, vb);
ms.popPose();
}
@Override
public BogeyInstance createInstance(MaterialManager materialManager, CarriageBogey bogey) {
if (large) {
return new BogeyInstance.Drive(bogey, materialManager);
} else {
return new BogeyInstance.Frame(bogey, materialManager);
}
}
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return switch (pRotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> pState.cycle(AXIS);
default -> pState;
};
} }
@Override @Override
@ -234,9 +69,4 @@ public class StandardBogeyBlock extends Block
return AllBlockEntityTypes.BOGEY.get(); return AllBlockEntityTypes.BOGEY.get();
} }
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity be) {
return new ItemRequirement(ItemUseType.CONSUME, AllBlocks.RAILWAY_CASING.asStack());
}
} }

View file

@ -1,40 +1,20 @@
package com.simibubi.create.content.logistics.trains.track; package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.foundation.blockEntity.CachedRenderBBBlockEntity; import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
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.AABB;
public class StandardBogeyBlockEntity extends CachedRenderBBBlockEntity { public class StandardBogeyBlockEntity extends AbstractBogeyBlockEntity {
public StandardBogeyBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public StandardBogeyBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
} }
@Override @Override
protected AABB createRenderBoundingBox() { public BogeyStyle getDefaultStyle() {
return super.createRenderBoundingBox().inflate(2); return AllBogeyStyles.STANDARD;
} }
// Ponder
LerpedFloat virtualAnimation = LerpedFloat.angular();
public float getVirtualAngle(float partialTicks) {
return virtualAnimation.getValue(partialTicks);
}
public void animate(float distanceMoved) {
BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof IBogeyBlock type))
return;
double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius());
double newWheelAngle = (virtualAnimation.getValue() - angleDiff) % 360;
virtualAnimation.setValue(newWheelAngle);
}
} }

View file

@ -29,12 +29,14 @@ import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels; import com.simibubi.create.AllPartialModels;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.particle.CubeParticleData; import com.simibubi.create.content.contraptions.particle.CubeParticleData;
import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.curiosities.girder.GirderBlock; import com.simibubi.create.content.curiosities.girder.GirderBlock;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
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.TrackPropagator; import com.simibubi.create.content.logistics.trains.TrackPropagator;
@ -55,6 +57,8 @@ import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -113,11 +117,14 @@ public class TrackBlock extends Block
public static final EnumProperty<TrackShape> SHAPE = EnumProperty.create("shape", TrackShape.class); public static final EnumProperty<TrackShape> SHAPE = EnumProperty.create("shape", TrackShape.class);
public static final BooleanProperty HAS_BE = BooleanProperty.create("turn"); public static final BooleanProperty HAS_BE = BooleanProperty.create("turn");
public TrackBlock(Properties p_49795_) { protected final TrackMaterial material;
public TrackBlock(Properties p_49795_, TrackMaterial material) {
super(p_49795_); super(p_49795_);
registerDefaultState(defaultBlockState().setValue(SHAPE, TrackShape.ZO) registerDefaultState(defaultBlockState().setValue(SHAPE, TrackShape.ZO)
.setValue(HAS_BE, false) .setValue(HAS_BE, false)
.setValue(WATERLOGGED, false)); .setValue(WATERLOGGED, false));
this.material = material;
} }
@Override @Override
@ -388,7 +395,7 @@ public class TrackBlock extends Block
(d, b) -> axis.scale(b ? 0 : fromCenter ? -d : d) (d, b) -> axis.scale(b ? 0 : fromCenter ? -d : d)
.add(center), .add(center),
b -> shape.getNormal(), b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, v -> 0, b -> shape.getNormal(), b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, v -> 0,
axis, null); axis, null, (b, v) -> ITrackBlock.getMaterialSimple(world, v));
} else } else
list = ITrackBlock.super.getConnected(world, pos, state, linear, connectedTo); list = ITrackBlock.super.getConnected(world, pos, state, linear, connectedTo);
@ -404,7 +411,8 @@ public class TrackBlock extends Block
Map<BlockPos, BezierConnection> connections = trackTE.getConnections(); Map<BlockPos, BezierConnection> connections = trackTE.getConnections();
connections.forEach((connectedPos, bc) -> ITrackBlock.addToListIfConnected(connectedTo, list, connections.forEach((connectedPos, bc) -> ITrackBlock.addToListIfConnected(connectedTo, list,
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get, (d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get,
b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, bc::yOffsetAt, null, bc)); b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, bc::yOffsetAt, null, bc,
(b, v) -> ITrackBlock.getMaterialSimple(world, v, bc.getMaterial())));
if (trackTE.boundLocation == null || !(world instanceof ServerLevel level)) if (trackTE.boundLocation == null || !(world instanceof ServerLevel level))
return list; return list;
@ -416,7 +424,7 @@ public class TrackBlock extends Block
return list; return list;
BlockPos boundPos = trackTE.boundLocation.getSecond(); BlockPos boundPos = trackTE.boundLocation.getSecond();
BlockState boundState = otherLevel.getBlockState(boundPos); BlockState boundState = otherLevel.getBlockState(boundPos);
if (!AllBlocks.TRACK.has(boundState)) if (!AllTags.AllBlockTags.TRACKS.matches(boundState))
return list; return list;
Vec3 center = Vec3.atBottomCenterOf(pos) Vec3 center = Vec3.atBottomCenterOf(pos)
@ -430,7 +438,8 @@ public class TrackBlock extends Block
getTrackAxes(world, pos, state).forEach(axis -> { getTrackAxes(world, pos, state).forEach(axis -> {
ITrackBlock.addToListIfConnected(connectedTo, list, (d, b) -> (b ? axis : boundAxis).scale(d) ITrackBlock.addToListIfConnected(connectedTo, list, (d, b) -> (b ? axis : boundAxis).scale(d)
.add(b ? center : boundCenter), b -> (b ? shape : boundShape).getNormal(), .add(b ? center : boundCenter), b -> (b ? shape : boundShape).getNormal(),
b -> b ? level.dimension() : otherLevel.dimension(), v -> 0, axis, null); b -> b ? level.dimension() : otherLevel.dimension(), v -> 0, axis, null,
(b, v) -> ITrackBlock.getMaterialSimple(b ? level : otherLevel, v));
}); });
return list; return list;
@ -479,7 +488,7 @@ public class TrackBlock extends Block
if (!entry.getValue() if (!entry.getValue()
.isInside(pos)) .isInside(pos))
continue; continue;
if (world.getBlockEntity(entry.getKey())instanceof StationBlockEntity station) if (world.getBlockEntity(entry.getKey()) instanceof StationBlockEntity station)
if (station.trackClicked(player, hand, this, state, pos)) if (station.trackClicked(player, hand, this, state, pos))
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
} }
@ -495,7 +504,7 @@ public class TrackBlock extends Block
BlockPos girderPos = pPos.below() BlockPos girderPos = pPos.below()
.offset(vec3.z * side, 0, vec3.x * side); .offset(vec3.z * side, 0, vec3.x * side);
BlockState girderState = pLevel.getBlockState(girderPos); BlockState girderState = pLevel.getBlockState(girderPos);
if (girderState.getBlock()instanceof GirderBlock girderBlock if (girderState.getBlock() instanceof GirderBlock girderBlock
&& !blockTicks.hasScheduledTick(girderPos, girderBlock)) && !blockTicks.hasScheduledTick(girderPos, girderBlock))
pLevel.scheduleTick(girderPos, girderBlock, 1); pLevel.scheduleTick(girderPos, girderBlock, 1);
} }
@ -700,7 +709,7 @@ public class TrackBlock extends Block
Vec3 normal = null; Vec3 normal = null;
Vec3 offset = null; Vec3 offset = null;
if (bezierPoint != null && world.getBlockEntity(pos)instanceof TrackBlockEntity trackTE) { if (bezierPoint != null && world.getBlockEntity(pos) instanceof TrackBlockEntity trackTE) {
BezierConnection bc = trackTE.connections.get(bezierPoint.curveTarget()); BezierConnection bc = trackTE.connections.get(bezierPoint.curveTarget());
if (bc != null) { if (bc != null) {
double length = Mth.floor(bc.getLength() * 2); double length = Mth.floor(bc.getLength() * 2);
@ -745,7 +754,8 @@ public class TrackBlock extends Block
msr.rotateCentered(Direction.UP, Mth.PI); msr.rotateCentered(Direction.UP, Mth.PI);
} }
if (bezierPoint == null && world.getBlockEntity(pos)instanceof TrackBlockEntity trackTE && trackTE.isTilted()) { if (bezierPoint == null && world.getBlockEntity(pos) instanceof TrackBlockEntity trackTE
&& trackTE.isTilted()) {
double yOffset = 0; double yOffset = 0;
for (BezierConnection bc : trackTE.connections.values()) for (BezierConnection bc : trackTE.connections.values())
yOffset += bc.starts.getFirst().y - pos.getY(); yOffset += bc.starts.getFirst().y - pos.getY();
@ -771,7 +781,8 @@ public class TrackBlock extends Block
@Override @Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity be) { public ItemRequirement getRequiredItems(BlockState state, BlockEntity be) {
int trackAmount = 1; int sameTypeTrackAmount = 1;
Object2IntMap<TrackMaterial> otherTrackAmounts = new Object2IntArrayMap<>();
int girderAmount = 0; int girderAmount = 0;
if (be instanceof TrackBlockEntity track) { if (be instanceof TrackBlockEntity track) {
@ -779,15 +790,28 @@ public class TrackBlock extends Block
.values()) { .values()) {
if (!bezierConnection.isPrimary()) if (!bezierConnection.isPrimary())
continue; continue;
trackAmount += bezierConnection.getTrackItemCost(); TrackMaterial material = bezierConnection.getMaterial();
if (material == getMaterial()) {
sameTypeTrackAmount += bezierConnection.getTrackItemCost();
} else {
otherTrackAmounts.put(material, otherTrackAmounts.getOrDefault(material, 0) + 1);
}
girderAmount += bezierConnection.getGirderItemCost(); girderAmount += bezierConnection.getGirderItemCost();
} }
} }
List<ItemStack> stacks = new ArrayList<>(); List<ItemStack> stacks = new ArrayList<>();
while (trackAmount > 0) { while (sameTypeTrackAmount > 0) {
stacks.add(AllBlocks.TRACK.asStack(Math.min(trackAmount, 64))); stacks.add(new ItemStack(state.getBlock(), Math.min(sameTypeTrackAmount, 64)));
trackAmount -= 64; sameTypeTrackAmount -= 64;
}
for (TrackMaterial material : otherTrackAmounts.keySet()) {
int amt = otherTrackAmounts.getOrDefault(material, 0);
while (amt > 0) {
stacks.add(new ItemStack(material.getTrackBlock()
.get(), Math.min(amt, 64)));
amt -= 64;
}
} }
while (girderAmount > 0) { while (girderAmount > 0) {
stacks.add(AllBlocks.METAL_GIRDER.asStack(Math.min(girderAmount, 64))); stacks.add(AllBlocks.METAL_GIRDER.asStack(Math.min(girderAmount, 64)));
@ -797,6 +821,11 @@ public class TrackBlock extends Block
return new ItemRequirement(ItemUseType.CONSUME, stacks); return new ItemRequirement(ItemUseType.CONSUME, stacks);
} }
@Override
public TrackMaterial getMaterial() {
return material;
}
public static class RenderProperties extends ReducedDestroyEffects implements MultiPosDestructionHandler { public static class RenderProperties extends ReducedDestroyEffects implements MultiPosDestructionHandler {
@Override @Override
@Nullable @Nullable

View file

@ -10,6 +10,7 @@ import java.util.Set;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlockEntity; import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlockEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform; import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
@ -125,6 +126,9 @@ public class TrackBlockEntity extends SmartBlockEntity implements ITransformable
} }
public void addConnection(BezierConnection connection) { public void addConnection(BezierConnection connection) {
// don't replace existing connections with different materials
if (connections.containsKey(connection.getKey()) && connection.equalsSansMaterial(connections.get(connection.getKey())))
return;
connections.put(connection.getKey(), connection); connections.put(connection.getKey(), connection);
level.scheduleTick(worldPosition, getBlockState().getBlock(), 1); level.scheduleTick(worldPosition, getBlockState().getBlock(), 1);
notifyUpdate(); notifyUpdate();
@ -326,7 +330,7 @@ public class TrackBlockEntity extends SmartBlockEntity implements ITransformable
.getLevel(boundLocation.getFirst()); .getLevel(boundLocation.getFirst());
if (otherLevel == null) if (otherLevel == null)
return; return;
if (AllBlocks.TRACK.has(otherLevel.getBlockState(boundLocation.getSecond()))) if (AllTags.AllBlockTags.TRACKS.matches(otherLevel.getBlockState(boundLocation.getSecond())))
otherLevel.destroyBlock(boundLocation.getSecond(), false); otherLevel.destroyBlock(boundLocation.getSecond(), false);
} }
} }

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllSoundEvents; import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackPlacement.PlacementInfo; import com.simibubi.create.content.logistics.trains.track.TrackPlacement.PlacementInfo;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
@ -118,7 +119,7 @@ public class TrackBlockItem extends BlockItem {
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
stack = player.getMainHandItem(); stack = player.getMainHandItem();
if (AllBlocks.TRACK.isIn(stack)) { if (AllTags.AllBlockTags.TRACKS.matches(stack)) {
stack.setTag(null); stack.setTag(null);
player.setItemInHand(pContext.getHand(), stack); player.setItemInHand(pContext.getHand(), stack);
} }
@ -161,7 +162,7 @@ public class TrackBlockItem extends BlockItem {
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public static void sendExtenderPacket(PlayerInteractEvent.RightClickBlock event) { public static void sendExtenderPacket(PlayerInteractEvent.RightClickBlock event) {
ItemStack stack = event.getItemStack(); ItemStack stack = event.getItemStack();
if (!AllBlocks.TRACK.isIn(stack) || !stack.hasTag()) if (!AllTags.AllBlockTags.TRACKS.matches(stack) || !stack.hasTag())
return; return;
if (Minecraft.getInstance().options.keySprint.isDown()) if (Minecraft.getInstance().options.keySprint.isDown())
AllPackets.getChannel() AllPackets.getChannel()

View file

@ -8,8 +8,8 @@ import java.util.function.Consumer;
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.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.AnimationTickHolder;
@ -162,7 +162,7 @@ public class TrackBlockOutline {
.rotateXRadians(angles.x) .rotateXRadians(angles.x)
.translate(-.5, -.125f, -.5); .translate(-.5, -.125f, -.5);
boolean holdingTrack = AllBlocks.TRACK.isIn(Minecraft.getInstance().player.getMainHandItem()); boolean holdingTrack = AllTags.AllBlockTags.TRACKS.matches(Minecraft.getInstance().player.getMainHandItem());
renderShape(AllShapes.TRACK_ORTHO.get(Direction.SOUTH), ms, vb, holdingTrack ? false : null); renderShape(AllShapes.TRACK_ORTHO.get(Direction.SOUTH), ms, vb, holdingTrack ? false : null);
ms.popPose(); ms.popPose();
} }
@ -190,7 +190,7 @@ public class TrackBlockOutline {
ms.pushPose(); ms.pushPose();
ms.translate(pos.getX() - camPos.x, pos.getY() - camPos.y, pos.getZ() - camPos.z); ms.translate(pos.getX() - camPos.x, pos.getY() - camPos.y, pos.getZ() - camPos.z);
boolean holdingTrack = AllBlocks.TRACK.isIn(Minecraft.getInstance().player.getMainHandItem()); boolean holdingTrack = AllTags.AllBlockTags.TRACKS.matches(Minecraft.getInstance().player.getMainHandItem());
TrackShape shape = blockstate.getValue(TrackBlock.SHAPE); TrackShape shape = blockstate.getValue(TrackBlock.SHAPE);
boolean canConnectFrom = !shape.isJunction() boolean canConnectFrom = !shape.isJunction()
&& !(mc.level.getBlockEntity(pos)instanceof TrackBlockEntity tbe && tbe.isTilted()); && !(mc.level.getBlockEntity(pos)instanceof TrackBlockEntity tbe && tbe.isTilted());

View file

@ -20,6 +20,7 @@ import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles; import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles;
import com.simibubi.create.content.logistics.trains.BezierConnection.SegmentAngles; import com.simibubi.create.content.logistics.trains.BezierConnection.SegmentAngles;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
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;
@ -38,7 +39,7 @@ public class TrackInstance extends BlockEntityInstance<TrackBlockEntity> {
@Override @Override
public void update() { public void update() {
if (blockEntity.connections.isEmpty()) if (blockEntity.connections.isEmpty())
return; return;
remove(); remove();
@ -110,11 +111,13 @@ public class TrackInstance extends BlockEntityInstance<TrackBlockEntity> {
leftLightPos = new BlockPos[segCount]; leftLightPos = new BlockPos[segCount];
rightLightPos = new BlockPos[segCount]; rightLightPos = new BlockPos[segCount];
mat.getModel(AllPartialModels.TRACK_TIE) TrackMaterial.TrackModelHolder modelHolder = bc.getMaterial().getModelHolder();
mat.getModel(modelHolder.tie())
.createInstances(ties); .createInstances(ties);
mat.getModel(AllPartialModels.TRACK_SEGMENT_LEFT) mat.getModel(modelHolder.segment_left())
.createInstances(left); .createInstances(left);
mat.getModel(AllPartialModels.TRACK_SEGMENT_RIGHT) mat.getModel(modelHolder.segment_right())
.createInstances(right); .createInstances(right);
SegmentAngles[] segments = bc.getBakedSegments(); SegmentAngles[] segments = bc.getBakedSegments();

View file

@ -6,16 +6,17 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.simibubi.create.AllBlocks; import com.jozufozu.flywheel.util.Color;
import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.AllTags;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer; import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock; import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Color;
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;
@ -46,6 +47,7 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.HitResult.Type;
@ -57,6 +59,11 @@ import net.minecraftforge.items.ItemHandlerHelper;
public class TrackPlacement { public class TrackPlacement {
public static class PlacementInfo { public static class PlacementInfo {
public PlacementInfo(TrackMaterial material) {
this.trackMaterial = material;
}
BezierConnection curve = null; BezierConnection curve = null;
boolean valid = false; boolean valid = false;
int end1Extent = 0; int end1Extent = 0;
@ -68,6 +75,7 @@ public class TrackPlacement {
public int requiredPavement = 0; public int requiredPavement = 0;
public boolean hasRequiredPavement = false; public boolean hasRequiredPavement = false;
public final TrackMaterial trackMaterial;
// for visualisation // for visualisation
Vec3 end1; Vec3 end1;
@ -109,7 +117,7 @@ public class TrackPlacement {
&& hoveringMaxed == maximiseTurn && lookAngle == hoveringAngle) && hoveringMaxed == maximiseTurn && lookAngle == hoveringAngle)
return cached; return cached;
PlacementInfo info = new PlacementInfo(); PlacementInfo info = new PlacementInfo(TrackMaterial.fromItem(stack.getItem()));
hoveringMaxed = maximiseTurn; hoveringMaxed = maximiseTurn;
hoveringAngle = lookAngle; hoveringAngle = lookAngle;
hoveringPos = pos2; hoveringPos = pos2;
@ -195,7 +203,7 @@ public class TrackPlacement {
BlockPos targetPos2 = pos2.offset(offset2.x, offset2.y, offset2.z); BlockPos targetPos2 = pos2.offset(offset2.x, offset2.y, offset2.z);
info.curve = new BezierConnection(Couple.create(targetPos1, targetPos2), info.curve = new BezierConnection(Couple.create(targetPos1, targetPos2),
Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2), Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2),
Couple.create(normal1, normal2), true, girder); Couple.create(normal1, normal2), true, girder, TrackMaterial.fromItem(stack.getItem()));
} }
// S curve or Straight // S curve or Straight
@ -355,7 +363,7 @@ public class TrackPlacement {
info.curve = skipCurve ? null info.curve = skipCurve ? null
: new BezierConnection(Couple.create(targetPos1, targetPos2), : new BezierConnection(Couple.create(targetPos1, targetPos2),
Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2), Couple.create(end1.add(offset1), end2.add(offset2)), Couple.create(normedAxis1, normedAxis2),
Couple.create(normal1, normal2), true, girder); Couple.create(normal1, normal2), true, girder, TrackMaterial.fromItem(stack.getItem()));
info.valid = true; info.valid = true;
@ -400,7 +408,7 @@ public class TrackPlacement {
continue; continue;
ItemStack stackInSlot = (offhand ? inv.offhand : inv.items).get(i); ItemStack stackInSlot = (offhand ? inv.offhand : inv.items).get(i);
boolean isTrack = AllBlocks.TRACK.isIn(stackInSlot); boolean isTrack = AllTags.AllBlockTags.TRACKS.matches(stackInSlot) && stackInSlot.is(stack.getItem());
if (!isTrack && (!shouldPave || offhandItem.getItem() != stackInSlot.getItem())) if (!isTrack && (!shouldPave || offhandItem.getItem() != stackInSlot.getItem()))
continue; continue;
if (isTrack ? foundTracks >= tracks : foundPavement >= pavement) if (isTrack ? foundTracks >= tracks : foundPavement >= pavement)
@ -473,6 +481,18 @@ public class TrackPlacement {
info.requiredPavement += TrackPaver.paveCurve(level, info.curve, block, simulate, visited); info.requiredPavement += TrackPaver.paveCurve(level, info.curve, block, simulate, visited);
} }
private static BlockState copyProperties(BlockState from, BlockState onto) {
for (Property property : onto.getProperties()) {
if (from.hasProperty(property))
onto = onto.setValue(property, from.getValue(property));
}
return onto;
}
private static BlockState copyProperties(BlockState from, BlockState onto, boolean keepFrom) {
return keepFrom ? from : copyProperties(from, onto);
}
private static PlacementInfo placeTracks(Level level, PlacementInfo info, BlockState state1, BlockState state2, private static PlacementInfo placeTracks(Level level, PlacementInfo info, BlockState state1, BlockState state2,
BlockPos targetPos1, BlockPos targetPos2, boolean simulate) { BlockPos targetPos1, BlockPos targetPos2, boolean simulate) {
info.requiredTracks = 0; info.requiredTracks = 0;
@ -500,7 +520,8 @@ public class TrackPlacement {
Vec3 offset = axis.scale(i); Vec3 offset = axis.scale(i);
BlockPos offsetPos = pos.offset(offset.x, offset.y, offset.z); BlockPos offsetPos = pos.offset(offset.x, offset.y, offset.z);
BlockState stateAtPos = level.getBlockState(offsetPos); BlockState stateAtPos = level.getBlockState(offsetPos);
BlockState toPlace = state; // copy over all shared properties from the shaped state to the correct track material block
BlockState toPlace = copyProperties(state, info.trackMaterial.getTrackBlock().get().defaultBlockState());
boolean canPlace = stateAtPos.getMaterial() boolean canPlace = stateAtPos.getMaterial()
.isReplaceable(); .isReplaceable();
@ -523,15 +544,16 @@ public class TrackPlacement {
return info; return info;
if (!simulate) { if (!simulate) {
BlockState onto = info.trackMaterial.getTrackBlock().get().defaultBlockState();
BlockState stateAtPos = level.getBlockState(targetPos1); BlockState stateAtPos = level.getBlockState(targetPos1);
level.setBlock(targetPos1, ProperWaterloggedBlock.withWater(level, level.setBlock(targetPos1, ProperWaterloggedBlock.withWater(level,
(stateAtPos.getBlock() == state1.getBlock() ? stateAtPos : state1).setValue(TrackBlock.HAS_BE, true), (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state1, onto))
targetPos1), 3); .setValue(TrackBlock.HAS_BE, true), targetPos1), 3);
stateAtPos = level.getBlockState(targetPos2); stateAtPos = level.getBlockState(targetPos2);
level.setBlock(targetPos2, ProperWaterloggedBlock.withWater(level, level.setBlock(targetPos2, ProperWaterloggedBlock.withWater(level,
(stateAtPos.getBlock() == state2.getBlock() ? stateAtPos : state2).setValue(TrackBlock.HAS_BE, true), (AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state2, onto))
targetPos2), 3); .setValue(TrackBlock.HAS_BE, true), targetPos2), 3);
} }
BlockEntity te1 = level.getBlockEntity(targetPos1); BlockEntity te1 = level.getBlockEntity(targetPos1);
@ -582,10 +604,10 @@ public class TrackPlacement {
return; return;
InteractionHand hand = InteractionHand.MAIN_HAND; InteractionHand hand = InteractionHand.MAIN_HAND;
if (!AllBlocks.TRACK.isIn(stack)) { if (!AllTags.AllBlockTags.TRACKS.matches(stack)) {
stack = player.getOffhandItem(); stack = player.getOffhandItem();
hand = InteractionHand.OFF_HAND; hand = InteractionHand.OFF_HAND;
if (!AllBlocks.TRACK.isIn(stack)) if (!AllTags.AllBlockTags.TRACKS.matches(stack))
return; return;
} }

View file

@ -3,9 +3,6 @@ package com.simibubi.create.content.logistics.trains.track;
import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_BOTTOM; import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_BOTTOM;
import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_MIDDLE; import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_MIDDLE;
import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_TOP; import static com.simibubi.create.AllPartialModels.GIRDER_SEGMENT_TOP;
import static com.simibubi.create.AllPartialModels.TRACK_SEGMENT_LEFT;
import static com.simibubi.create.AllPartialModels.TRACK_SEGMENT_RIGHT;
import static com.simibubi.create.AllPartialModels.TRACK_TIE;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -14,6 +11,7 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles; import com.simibubi.create.content.logistics.trains.BezierConnection.GirderAngles;
import com.simibubi.create.content.logistics.trains.BezierConnection.SegmentAngles; import com.simibubi.create.content.logistics.trains.BezierConnection.SegmentAngles;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer; import com.simibubi.create.foundation.blockEntity.renderer.SafeBlockEntityRenderer;
import com.simibubi.create.foundation.render.CachedBufferer; import com.simibubi.create.foundation.render.CachedBufferer;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
@ -62,7 +60,9 @@ public class TrackRenderer extends SafeBlockEntityRenderer<TrackBlockEntity> {
SegmentAngles segment = segments[i]; SegmentAngles segment = segments[i];
int light = LevelRenderer.getLightColor(level, segment.lightPosition.offset(tePosition)); int light = LevelRenderer.getLightColor(level, segment.lightPosition.offset(tePosition));
CachedBufferer.partial(TRACK_TIE, air) TrackMaterial.TrackModelHolder modelHolder = bc.getMaterial().getModelHolder();
CachedBufferer.partial(modelHolder.tie(), air)
.mulPose(segment.tieTransform.pose()) .mulPose(segment.tieTransform.pose())
.mulNormal(segment.tieTransform.normal()) .mulNormal(segment.tieTransform.normal())
.light(light) .light(light)
@ -70,7 +70,7 @@ public class TrackRenderer extends SafeBlockEntityRenderer<TrackBlockEntity> {
for (boolean first : Iterate.trueAndFalse) { for (boolean first : Iterate.trueAndFalse) {
Pose transform = segment.railTransforms.get(first); Pose transform = segment.railTransforms.get(first);
CachedBufferer.partial(first ? TRACK_SEGMENT_LEFT : TRACK_SEGMENT_RIGHT, air) CachedBufferer.partial(first ? modelHolder.segment_left() : modelHolder.segment_right(), air)
.mulPose(transform.pose()) .mulPose(transform.pose())
.mulNormal(transform.normal()) .mulNormal(transform.normal())
.light(light) .light(light)

View file

@ -62,9 +62,11 @@ import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.forgespi.language.IModFileInfo; import net.minecraftforge.forgespi.language.IModFileInfo;
import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.forgespi.locating.IModFile;
import net.minecraftforge.registries.NewRegistryEvent;
@EventBusSubscriber @EventBusSubscriber
public class CommonEvents { public class CommonEvents {
@ -90,7 +92,7 @@ public class CommonEvents {
ToolboxHandler.playerLogin(player); ToolboxHandler.playerLogin(player);
Create.RAILWAYS.playerLogin(player); Create.RAILWAYS.playerLogin(player);
} }
@SubscribeEvent @SubscribeEvent
public static void playerLoggedOut(PlayerLoggedOutEvent event) { public static void playerLoggedOut(PlayerLoggedOutEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
@ -166,7 +168,7 @@ public class CommonEvents {
public static void onEntityEnterSection(EntityEvent.EnteringSection event) { public static void onEntityEnterSection(EntityEvent.EnteringSection event) {
CarriageEntityHandler.onEntityEnterSection(event); CarriageEntityHandler.onEntityEnterSection(event);
} }
@SubscribeEvent @SubscribeEvent
public static void addReloadListeners(AddReloadListenerEvent event) { public static void addReloadListeners(AddReloadListenerEvent event) {
event.addListener(RecipeFinder.LISTENER); event.addListener(RecipeFinder.LISTENER);
@ -248,7 +250,5 @@ public class CommonEvents {
}); });
} }
} }
} }
} }

View file

@ -60,7 +60,8 @@ public class AllCommands {
.then(CameraDistanceCommand.register()) .then(CameraDistanceCommand.register())
.then(CameraAngleCommand.register()) .then(CameraAngleCommand.register())
.then(FlySpeedCommand.register()) .then(FlySpeedCommand.register())
.then(KillTPSCommand.register()) //.then(DebugValueCommand.register())
//.then(KillTPSCommand.register())
.build(); .build();
} }

View file

@ -0,0 +1,41 @@
package com.simibubi.create.foundation.command;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.simibubi.create.Create;
import net.minecraft.SharedConstants;
import org.apache.commons.lang3.mutable.MutableInt;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.simibubi.create.foundation.utility.Components;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.BaseCommandBlock;
import net.minecraft.world.level.block.CommandBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.CommandBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
public class DebugValueCommand {
public static float value = 0;
public static ArgumentBuilder<CommandSourceStack, ?> register() {
return Commands.literal("debugValue")
.requires(cs -> cs.hasPermission(4))
.then(Commands.argument("value", FloatArgumentType.floatArg())
.executes((ctx) -> {
value = FloatArgumentType.getFloat(ctx, "value");
ctx.getSource().sendSuccess(Components.literal("Set value to: "+value), true);
return 1;
}));
}
}

View file

@ -77,6 +77,7 @@ public class CClient extends ConfigBase {
public final ConfigGroup trains = group(1, "trains", Comments.trains); public final ConfigGroup trains = group(1, "trains", Comments.trains);
public final ConfigFloat mountedZoomMultiplier = f(3, 0, "mountedZoomMultiplier", Comments.mountedZoomMultiplier); public final ConfigFloat mountedZoomMultiplier = f(3, 0, "mountedZoomMultiplier", Comments.mountedZoomMultiplier);
public final ConfigBool showTrackGraphOnF3 = b(false, "showTrackGraphOnF3", Comments.showTrackGraphOnF3); public final ConfigBool showTrackGraphOnF3 = b(false, "showTrackGraphOnF3", Comments.showTrackGraphOnF3);
public final ConfigBool showExtendedTrackGraphOnF3 = b(false, "showExtendedTrackGraphOnF3", Comments.showExtendedTrackGraphOnF3);
@Override @Override
public String getName() { public String getName() {
@ -147,6 +148,7 @@ public class CClient extends ConfigBase {
static String trains = "Railway related settings"; static String trains = "Railway related settings";
static String mountedZoomMultiplier = "How far away the Camera should zoom when seated on a train"; static String mountedZoomMultiplier = "How far away the Camera should zoom when seated on a train";
static String showTrackGraphOnF3 = "Display nodes and edges of a Railway Network while f3 debug mode is active"; static String showTrackGraphOnF3 = "Display nodes and edges of a Railway Network while f3 debug mode is active";
static String showExtendedTrackGraphOnF3 = "Additionally display materials of a Rail Network while f3 debug mode is active";
} }
} }

View file

@ -37,7 +37,7 @@ import com.simibubi.create.content.curiosities.frames.CopycatBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock; import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock.Shape; import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock.Shape;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelItem; import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelItem;
import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlock; import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlock;
import com.simibubi.create.foundation.block.BlockStressDefaults; import com.simibubi.create.foundation.block.BlockStressDefaults;
import com.simibubi.create.foundation.block.ItemUseOverrides; import com.simibubi.create.foundation.block.ItemUseOverrides;
@ -95,6 +95,7 @@ public class BuilderTransformers {
.build(); .build();
} }
@SuppressWarnings("deprecation")
public static <B extends StandardBogeyBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> bogey() { public static <B extends StandardBogeyBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> bogey() {
return b -> b.initialProperties(SharedProperties::softMetal) return b -> b.initialProperties(SharedProperties::softMetal)
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK)) .properties(p -> p.sound(SoundType.NETHERITE_BLOCK))
@ -103,7 +104,7 @@ public class BuilderTransformers {
.blockstate((c, p) -> BlockStateGen.horizontalAxisBlock(c, p, s -> p.models() .blockstate((c, p) -> BlockStateGen.horizontalAxisBlock(c, p, s -> p.models()
.getExistingFile(p.modLoc("block/track/bogey/top")))) .getExistingFile(p.modLoc("block/track/bogey/top"))))
.loot((p, l) -> p.dropOther(l, AllBlocks.RAILWAY_CASING.get())) .loot((p, l) -> p.dropOther(l, AllBlocks.RAILWAY_CASING.get()))
.onRegister(block -> IBogeyBlock.register(RegisteredObjects.getKeyOrThrow(block))); .onRegister(block -> AbstractBogeyBlock.registerStandardBogey(RegisteredObjects.getKeyOrThrow(block)));
} }
public static <B extends CopycatBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> copycat() { public static <B extends CopycatBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> copycat() {

View file

@ -3,6 +3,8 @@ package com.simibubi.create.foundation.ponder.content;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.ponder.PonderRegistrationHelper; import com.simibubi.create.foundation.ponder.PonderRegistrationHelper;
import com.simibubi.create.foundation.ponder.PonderRegistry; import com.simibubi.create.foundation.ponder.PonderRegistry;
@ -20,8 +22,15 @@ import com.simibubi.create.foundation.ponder.content.trains.TrainScenes;
import com.simibubi.create.foundation.ponder.content.trains.TrainSignalScenes; import com.simibubi.create.foundation.ponder.content.trains.TrainSignalScenes;
import com.simibubi.create.foundation.ponder.content.trains.TrainStationScenes; import com.simibubi.create.foundation.ponder.content.trains.TrainStationScenes;
import com.tterrag.registrate.util.entry.BlockEntry;
import com.tterrag.registrate.util.entry.ItemProviderEntry;
import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import java.util.stream.Collectors;
public class PonderIndex { public class PonderIndex {
@ -320,7 +329,12 @@ public class PonderIndex {
.addStoryBoard("threshold_switch", DetectorScenes::thresholdSwitch); .addStoryBoard("threshold_switch", DetectorScenes::thresholdSwitch);
// Trains // Trains
HELPER.forComponents(AllBlocks.TRACK) HELPER.forComponents(TrackMaterial.allBlocks().stream()
.map((trackSupplier) -> new BlockEntry<TrackBlock>(
// note: these blocks probably WON'T be in the Create Registrate, but a simple code trace reveals the Entry's registrate isn't used
Create.REGISTRATE,
RegistryObject.create(trackSupplier.get().getRegistryName(), ForgeRegistries.BLOCKS)))
.toList())
.addStoryBoard("train_track/placement", TrackScenes::placement) .addStoryBoard("train_track/placement", TrackScenes::placement)
.addStoryBoard("train_track/portal", TrackScenes::portal) .addStoryBoard("train_track/portal", TrackScenes::portal)
.addStoryBoard("train_track/chunks", TrackScenes::chunks); .addStoryBoard("train_track/chunks", TrackScenes::chunks);

View file

@ -7,7 +7,7 @@ import java.util.function.Function;
import com.simibubi.create.content.contraptions.components.deployer.DeployerBlockEntity; import com.simibubi.create.content.contraptions.components.deployer.DeployerBlockEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.IBearingBlockEntity; import com.simibubi.create.content.contraptions.components.structureMovement.bearing.IBearingBlockEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyBlockEntity; import com.simibubi.create.content.contraptions.components.structureMovement.pulley.PulleyBlockEntity;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyBlockEntity; import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
import com.simibubi.create.foundation.ponder.PonderScene; import com.simibubi.create.foundation.ponder.PonderScene;
import com.simibubi.create.foundation.ponder.PonderWorld; import com.simibubi.create.foundation.ponder.PonderWorld;
@ -34,7 +34,7 @@ public class AnimateBlockEntityInstruction extends TickingInstruction {
public static AnimateBlockEntityInstruction bogey(BlockPos location, float totalDelta, int ticks) { public static AnimateBlockEntityInstruction bogey(BlockPos location, float totalDelta, int ticks) {
float movedPerTick = totalDelta / ticks; float movedPerTick = totalDelta / ticks;
return new AnimateBlockEntityInstruction(location, totalDelta, ticks, return new AnimateBlockEntityInstruction(location, totalDelta, ticks,
(w, f) -> castIfPresent(w, location, StandardBogeyBlockEntity.class) (w, f) -> castIfPresent(w, location, AbstractBogeyBlockEntity.class)
.ifPresent(bte -> bte.animate(f.equals(totalDelta) ? 0 : movedPerTick)), .ifPresent(bte -> bte.animate(f.equals(totalDelta) ? 0 : movedPerTick)),
(w) -> 0f); (w) -> 0f);
} }

View file

@ -45,4 +45,13 @@ public class Iterate {
public static List<BlockPos> hereBelowAndAbove(BlockPos pos) { public static List<BlockPos> hereBelowAndAbove(BlockPos pos) {
return Arrays.asList(pos, pos.below(), pos.above()); return Arrays.asList(pos, pos.below(), pos.above());
} }
public static <T> T cycleValue(List<T> list, T current) {
int currentIndex = list.indexOf(current);
if (currentIndex == -1) {
throw new IllegalArgumentException("Current value not found in list");
}
int nextIndex = (currentIndex + 1) % list.size();
return list.get(nextIndex);
}
} }

View file

@ -13,6 +13,7 @@ import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntTag; import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
@ -118,4 +119,12 @@ public class NBTHelper {
return compoundTag.getInt("V"); return compoundTag.getInt("V");
} }
public static void writeResourceLocation(CompoundTag nbt, String key, ResourceLocation location) {
nbt.putString(key, location.toString());
}
public static ResourceLocation readResourceLocation(CompoundTag nbt, String key) {
return new ResourceLocation(nbt.getString(key));
}
} }

View file

@ -0,0 +1,51 @@
package com.simibubi.create.foundation.utility.outliner;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.foundation.render.RenderTypes;
import com.simibubi.create.foundation.render.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import java.util.*;
public class ItemOutline extends Outline {
protected Vec3 pos;
protected ItemStack stack;
public ItemOutline(Vec3 pos, ItemStack stack) {
this.pos = pos;
this.stack = stack;
}
@Override
public void render(PoseStack ms, SuperRenderTypeBuffer buffer, Vec3 camera, float pt) {
Minecraft mc = Minecraft.getInstance();
ms.pushPose();
TransformStack.cast(ms)
.translate(pos.x - camera.x, pos.y - camera.y, pos.z - camera.z)
.scale(params.alpha);
mc.getItemRenderer().render(stack, ItemTransforms.TransformType.FIXED, false, ms,
buffer, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY,
mc.getItemRenderer().getModel(stack, null, null, 0));
ms.popPose();
}
}

View file

@ -14,6 +14,7 @@ import com.simibubi.create.foundation.utility.outliner.Outline.OutlineParams;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -81,6 +82,13 @@ public class Outliner {
// //
public OutlineParams showItem(Object slot, Vec3 pos, ItemStack stack) {
ItemOutline outline = new ItemOutline(pos, stack);
OutlineEntry entry = new OutlineEntry(outline);
outlines.put(slot, entry);
return entry.getOutline().getParams();
}
public void keep(Object slot) { public void keep(Object slot) {
if (outlines.containsKey(slot)) if (outlines.containsKey(slot))
outlines.get(slot).ticksTillRemoval = 1; outlines.get(slot).ticksTillRemoval = 1;

View file

@ -1002,8 +1002,14 @@
"create.contraption.minecart_contraption_too_big": "This Cart Contraption seems too big to pick up", "create.contraption.minecart_contraption_too_big": "This Cart Contraption seems too big to pick up",
"create.contraption.minecart_contraption_illegal_pickup": "A mystical force is binding this Cart Contraption to the world", "create.contraption.minecart_contraption_illegal_pickup": "A mystical force is binding this Cart Contraption to the world",
"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"
} }