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
.buildpath
.DS_Store
.DS_Store
/libs/

View file

@ -135,6 +135,9 @@ repositories {
includeGroup "maven.modrinth"
}
}
flatDir {
dirs 'libs'
}
}
dependencies {
@ -169,6 +172,7 @@ dependencies {
// 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("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
// 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
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.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
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.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
d4a3b66f4b763b9a2dcdea74b7273f0ae85cb335 data/create/tags/blocks/copycat_deny.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
6e5d3b2123fbb00e7f439c091623619502551bca data/create/tags/blocks/non_movable.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
6cdeeac1689f7b5bfd9bc40b462143d8eaf3ad0b data/create/tags/blocks/seats.json
d063e12c9ef75f39518c6d129ea35d833464d547 data/create/tags/blocks/toolboxes.json
ad8fa04f7bbbafd70d0ce158af78a35e899301e2 data/create/tags/blocks/tracks.json
9460e92c8e483446318b849abe7e6f52dcd4a269 data/create/tags/blocks/tree_attachments.json
50936b211d94167a35ec78c89954082a336b6269 data/create/tags/blocks/valve_handles.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.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 [------------------------<-",

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)
.validBlocks(AllBlocks.TRACK_STATION)
.register();
public static final BlockEntityEntry<SlidingDoorBlockEntity> SLIDING_DOOR =
REGISTRATE.blockEntity("sliding_door", SlidingDoorBlockEntity::new)
.renderer(() -> SlidingDoorRenderer::new)
@ -859,10 +859,10 @@ public class AllBlockEntityTypes {
AllBlocks.BRASS_DOOR, AllBlocks.COPPER_DOOR)
.register();
public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT = REGISTRATE
.blockEntity("copycat", CopycatBlockEntity::new)
.validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP)
.register();
public static final BlockEntityEntry<CopycatBlockEntity> COPYCAT =
REGISTRATE.blockEntity("copycat", CopycatBlockEntity::new)
.validBlocks(AllBlocks.COPYCAT_PANEL, AllBlocks.COPYCAT_STEP)
.register();
public static final BlockEntityEntry<FlapDisplayBlockEntity> FLAP_DISPLAY = REGISTRATE
.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.ItemVaultItem;
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.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
@ -718,7 +721,7 @@ public class AllBlocks {
.onRegister(movementBehaviour(new BlazeBurnerMovementBehaviour()))
.onRegister(interactionBehaviour(new BlazeBurnerInteractionBehaviour()))
.item(BlazeBurnerBlockItem::withBlaze)
.model(AssetLookup.<BlazeBurnerBlockItem>customBlockItemModel("blaze_burner", "block_with_blaze"))
.model(AssetLookup.customBlockItemModel("blaze_burner", "block_with_blaze"))
.build()
.register();
@ -938,7 +941,7 @@ public class AllBlocks {
.onRegister(assignDataBehaviour(new BoilerDisplaySource(), "boiler_status"))
.addLayer(() -> RenderType::cutoutMipped)
.item(FluidTankItem::new)
.model(AssetLookup.<FluidTankItem>customBlockItemModel("_", "block_single_window"))
.model(AssetLookup.customBlockItemModel("_", "block_single_window"))
.build()
.register();
@ -1541,7 +1544,7 @@ public class AllBlocks {
.transform(customItemModel())
.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)
.properties(p -> p.color(MaterialColor.METAL)
.strength(0.8F)
@ -1552,6 +1555,8 @@ public class AllBlocks {
.onRegister(CreateRegistrate.blockModel(() -> TrackModel::new))
.blockstate(new TrackBlockStateGenerator()::generate)
.tag(AllBlockTags.RELOCATION_NOT_SUPPORTED.tag)
.tag(AllBlockTags.TRACKS.tag)
.tag(AllBlockTags.GIRDABLE_TRACKS.tag)
.lang("Train Track")
.item(TrackBlockItem::new)
.model((c, p) -> p.generated(c, Create.asResource("item/" + c.getName())))
@ -1620,13 +1625,13 @@ public class AllBlocks {
.register();
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))
.transform(BuilderTransformers.bogey())
.register();
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))
.transform(BuilderTransformers.bogey())
.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,
SEATS,
TOOLBOXES,
TRACKS,
GIRDABLE_TRACKS,
TREE_ATTACHMENTS,
VALVE_HANDLES,
WINDMILL_SAILS,
@ -132,6 +134,10 @@ public class AllTags {
.is(tag);
}
public boolean matches(ItemStack stack) {
return stack != null && stack.getItem() instanceof BlockItem blockItem && matches(blockItem.getBlock());
}
public boolean matches(BlockState state) {
return state.is(tag);
}

View file

@ -2,6 +2,8 @@ package com.simibubi.create;
import java.util.Random;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import org.slf4j.Logger;
import com.google.gson.Gson;
@ -127,6 +129,8 @@ public class Create {
AllFeatures.register(modEventBus);
AllPlacementModifiers.register(modEventBus);
BuiltinRegistration.register(modEventBus);
BogeySizes.init();
AllBogeyStyles.register();
AllConfigs.register(modLoadingContext);

View file

@ -4,7 +4,7 @@ import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager;
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.render.ActorInstance;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionMatrices;
@ -62,7 +62,7 @@ public class DrillMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override
public boolean canBreak(Level world, BlockPos breakingPos, BlockState state) {
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.Optional;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllRecipeTypes;
import com.simibubi.create.AllTags;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCraftingRecipe;
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) {
award(AllAdvancements.PRESS);
if (AllBlocks.TRACK.isIn(result))
if (AllTags.AllBlockTags.TRACKS.matches(result))
tracksCreated += result.getCount();
if (tracksCreated >= 1000) {
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.logistics.block.redstone.RedstoneLinkBlock;
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.management.edgePoint.station.StationBlock;
import com.simibubi.create.foundation.config.ContraptionMovementSetting;
@ -338,7 +338,7 @@ public class BlockMovementChecks {
return direction == state.getValue(StickerBlock.FACING)
&& !isNotSupportive(world.getBlockState(pos.relative(direction)), direction.getOpposite());
}
if (block instanceof IBogeyBlock bogey)
if (block instanceof AbstractBogeyBlock bogey)
return bogey.getStickySurfaces(world, pos, state)
.contains(direction);
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.redstone.RedstoneContactBlock;
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.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.config.AllConfigs;
@ -347,7 +347,7 @@ public abstract class Contraption {
}
// 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))
if (!visited.contains(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.AllItems;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.fluids.pipes.BracketBlock;
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)) {
BlockState above = level.getBlockState(pos.above()
.relative(d2));
if (AllBlocks.TRACK.has(above)) {
if (AllTags.AllBlockTags.GIRDABLE_TRACKS.matches(above)) {
TrackShape shape = above.getValue(TrackBlock.SHAPE);
if (shape == (axis == Axis.X ? TrackShape.XO : TrackShape.ZO))
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.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintCraftingInventory;
import com.simibubi.create.content.curiosities.tools.BlueprintEntity.BlueprintSection;
@ -106,7 +105,7 @@ public class BlueprintOverlayRenderer {
int tracks = info.requiredTracks;
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;
}

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.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
@ -42,6 +43,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public Couple<Integer> smoothing;
public boolean primary;
public boolean hasGirder;
protected TrackMaterial trackMaterial;
// runtime
@ -58,19 +60,20 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
private AABB bounds;
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;
this.starts = starts;
this.axes = axes;
this.normals = normals;
this.primary = primary;
this.hasGirder = girder;
this.trackMaterial = material;
resolved = false;
}
public BezierConnection secondary() {
BezierConnection bezierConnection =
new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(), normals.swap(), !primary, hasGirder);
BezierConnection bezierConnection = new BezierConnection(tePositions.swap(), starts.swap(), axes.swap(),
normals.swap(), !primary, hasGirder, trackMaterial);
if (smoothing != null)
bezierConnection.smoothing = smoothing.swap();
return bezierConnection;
@ -80,6 +83,26 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
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) {
this(Couple.deserializeEach(compound.getList("Positions", Tag.TAG_COMPOUND), NbtUtils::readBlockPos)
.map(b -> b.offset(localTo)),
@ -87,7 +110,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
.map(v -> v.add(Vec3.atLowerCornerOf(localTo))),
Couple.deserializeEach(compound.getList("Axes", 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"))
smoothing =
@ -105,6 +128,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
compound.put("Starts", starts.serializeEach(VecHelper::writeNBTCompound));
compound.put("Axes", axes.serializeEach(VecHelper::writeNBTCompound));
compound.put("Normals", normals.serializeEach(VecHelper::writeNBTCompound));
compound.putString("Material", getMaterial().id.toString());
if (smoothing != null)
compound.put("Smoothing", smoothing.serializeEach(NBTHelper::intToCompound));
@ -115,7 +139,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
public BezierConnection(FriendlyByteBuf buffer) {
this(Couple.create(buffer::readBlockPos), 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())
smoothing = Couple.create(buffer::readVarInt);
}
@ -127,6 +151,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
normals.forEach(v -> VecHelper.write(v, buffer));
buffer.writeBoolean(primary);
buffer.writeBoolean(hasGirder);
buffer.writeUtf(getMaterial().id.toString());
buffer.writeBoolean(smoothing != null);
if (smoothing != null)
smoothing.forEach(buffer::writeVarInt);
@ -333,7 +358,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
Inventory inv = player.getInventory();
int tracks = getTrackItemCost();
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;
}
int girders = getGirderItemCost();
@ -361,7 +386,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
continue;
Vec3 v = VecHelper.offsetRandomly(segment.position, level.random, .125f)
.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();
level.addFreshEntity(entity);
if (!hasGirder)
@ -375,7 +400,7 @@ public class BezierConnection implements Iterable<BezierConnection.Segment> {
}
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 =
new BlockParticleOption(ParticleTypes.BLOCK, AllBlocks.METAL_GIRDER.getDefaultState());
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 int index;

View file

@ -1,7 +1,7 @@
package com.simibubi.create.content.logistics.trains;
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 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,
int overlay) {
BlockState blockState = be.getBlockState();
float angle = 0;
if (be instanceof StandardBogeyBlockEntity sbte)
angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock()instanceof IBogeyBlock bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay);
if (be instanceof AbstractBogeyBlockEntity sbte) {
float angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay, sbte.getStyle(), sbte.getBogeyData());
}
}
}

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() {
if (isTrackGraphDebugActive())
for (TrackGraph trackGraph : trackNetworks.values())
TrackGraphVisualizer.debugViewGraph(trackGraph);
TrackGraphVisualizer.debugViewGraph(trackGraph, isTrackGraphDebugExtended());
}
private static boolean isTrackGraphDebugActive() {
return KineticDebugger.isF3DebugModeActive() && AllConfigs.client().showTrackGraphOnF3.get();
}
private static boolean isTrackGraphDebugExtended() {
return AllConfigs.CLIENT.showExtendedTrackGraphOnF3.get();
}
public GlobalRailwayManager sided(LevelAccessor level) {
if (level != null && !level.isClientSide())
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.world.level.BlockGetter;
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.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -85,20 +86,38 @@ public interface ITrackBlock {
Function<Vec3, Integer> yOffsetFactory = v -> getYOffsetAt(world, pos, state, v);
addToListIfConnected(connectedTo, list, offsetFactory, b -> shape.getNormal(), dimensionFactory,
yOffsetFactory, axis, null);
yOffsetFactory, axis, null, (b, v) -> getMaterialSimple(world, v));
});
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,
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
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);
DiscoveredLocation firstLocation =
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))
.withDirection(axis)
.withYOffset(yOffsetFactory.apply(firstOffset));
@ -106,6 +125,8 @@ public interface ITrackBlock {
Vec3 secondOffset = offsetFactory.apply(0.5d, false);
DiscoveredLocation secondLocation =
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))
.withDirection(axis)
.withYOffset(yOffsetFactory.apply(secondOffset));
@ -169,4 +190,6 @@ public interface ITrackBlock {
.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;
EdgeData edgeData;
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.edgeData = new EdgeData(this);
this.node1 = node1;
this.node2 = node2;
this.turn = turn;
this.trackMaterial = trackMaterial;
}
public TrackMaterial getTrackMaterial() {
return trackMaterial;
}
public boolean isTurn() {
@ -230,13 +236,15 @@ public class TrackEdge {
public CompoundTag write(DimensionPalette dimensions) {
CompoundTag baseCompound = isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag();
baseCompound.put("Signals", edgeData.write(dimensions));
baseCompound.putString("Material", getTrackMaterial().id.toString());
return baseCompound;
}
public static TrackEdge read(TrackNode node1, TrackNode node2, CompoundTag tag, TrackGraph graph,
DimensionPalette dimensions) {
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);
return trackEdge;
}

View file

@ -393,14 +393,15 @@ public class TrackGraph {
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) {
TrackNode node1 = nodes.get(location);
TrackNode node2 = nodes.get(location2);
boolean bezier = turn != null;
TrackEdge edge = new TrackEdge(node1, node2, turn);
TrackEdge edge2 = new TrackEdge(node2, node1, bezier ? turn.secondary() : null);
TrackMaterial material = bezier ? turn.getMaterial() : location2.materialA;
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 (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) {
flushGraphPacket(graph);
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++;
}
@ -82,7 +82,7 @@ public class TrackGraphSync {
if (currentGraphSyncPacket.addedNodes.remove(nodeId) == null)
currentGraphSyncPacket.removedNodes.add(nodeId);
currentGraphSyncPacket.addedEdges.removeIf(pair -> {
Couple<Integer> ids = pair.getFirst();
Couple<Integer> ids = pair.getFirst().getFirst();
return ids.getFirst()
.intValue() == nodeId
|| ids.getSecond()
@ -156,7 +156,7 @@ public class TrackGraphSync {
graph.connectionsByNode.get(node)
.forEach((node2, edge) -> {
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);
});

View file

@ -22,7 +22,7 @@ import net.minecraft.world.phys.Vec3;
public class TrackGraphSyncPacket extends TrackGraphPacket {
Map<Integer, Pair<TrackNodeLocation, Vec3>> addedNodes;
List<Pair<Couple<Integer>, BezierConnection>> addedEdges;
List<Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection>> addedEdges;
List<Integer> removedNodes;
List<TrackEdgePoint> addedEdgePoints;
List<UUID> removedEdgePoints;
@ -79,7 +79,7 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
size = buffer.readVarInt();
for (int i = 0; i < size; i++)
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();
for (int i = 0; i < size; i++)
@ -134,8 +134,9 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
buffer.writeVarInt(addedEdges.size());
addedEdges.forEach(pair -> {
pair.getFirst()
pair.getFirst().getFirst()
.forEach(buffer::writeVarInt);
buffer.writeUtf(pair.getFirst().getSecond().id.toString());
BezierConnection turn = pair.getSecond();
buffer.writeBoolean(turn != null);
if (turn != null)
@ -192,13 +193,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond());
}
for (Pair<Couple<Integer>, BezierConnection> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst()
for (Pair<Pair<Couple<Integer>, TrackMaterial>, BezierConnection> pair : addedEdges) {
Couple<TrackNode> nodes = pair.getFirst().getFirst()
.map(graph::getNode);
TrackNode node1 = nodes.getFirst();
TrackNode node2 = nodes.getSecond();
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)
@ -268,4 +269,4 @@ public class TrackGraphSyncPacket extends TrackGraphPacket {
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.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
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();
Entity cameraEntity = mc.cameraEntity;
if (cameraEntity == null)
@ -266,6 +267,17 @@ public class TrackGraphVisualizer {
yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
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)
.add(yOffset),
edge.getPosition(graph, 1)
@ -277,6 +289,17 @@ public class TrackGraphVisualizer {
Vec3 previous = null;
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++) {
Vec3 current = edge.getPosition(graph, i * 1f / turn.getSegmentCount());
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);
}
public TrackNodeLocation(double p_121865_, double p_121866_, double p_121867_) {
super(Math.round(p_121865_ * 2), Math.floor(p_121866_) * 2, Math.round(p_121867_ * 2));
public TrackNodeLocation(double x, double y, double z) {
super(Math.round(x * 2), Math.floor(y) * 2, Math.round(z * 2));
}
public TrackNodeLocation in(Level level) {
@ -122,9 +122,11 @@ public class TrackNodeLocation extends Vec3i {
boolean forceNode = false;
Vec3 direction;
Vec3 normal;
TrackMaterial materialA;
TrackMaterial materialB;
public DiscoveredLocation(Level level, double p_121865_, double p_121866_, double p_121867_) {
super(p_121865_, p_121866_, p_121867_);
public DiscoveredLocation(Level level, double x, double y, double z) {
super(x, y, z);
in(level);
}
@ -137,6 +139,22 @@ public class TrackNodeLocation extends Vec3i {
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) {
this.turn = turn;
if (turn != null)
@ -176,6 +194,10 @@ public class TrackNodeLocation extends Vec3i {
return forceNode;
}
public boolean differentMaterials() {
return materialA != materialB;
}
public boolean notInLineWith(Vec3 direction) {
return this.direction != null
&& 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;
if (location.shouldForceNode())
return true;
if (location.differentMaterials())
return true;
if (next.stream()
.anyMatch(DiscoveredLocation::shouldForceNode))
return true;
Vec3 direction = location.direction;
if (direction != null && next.stream()
.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;
import com.jozufozu.flywheel.api.Material;
import java.util.Optional;
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.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer;
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;
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.size = size;
this.style = style;
shafts = new ModelData[2];
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z))
.createInstances(shafts);
this.renderer = this.style.createRendererInstance(this.size);
this.commonRenderer = this.style.getNewCommonRenderInstance();
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.initialiseContraptionModelData(materialManager));
renderer.initialiseContraptionModelData(materialManager);
}
public void remove() {
for (ModelData shaft : shafts)
shaft.delete();
}
public void hiddenFrame() {
void hiddenFrame() {
beginFrame(0, null);
}
public void beginFrame(float wheelAngle, PoseStack ms) {
if (ms == null) {
for (int i : Iterate.zeroAndOne)
shafts[i].setEmptyTransform();
renderer.emptyTransforms();
return;
}
for (int i : Iterate.zeroAndOne)
shafts[i].setTransform(ms)
.translate(-.5f, .25f, i * -1)
.centre()
.rotateZ(wheelAngle)
.unCentre();
commonRenderer.ifPresent(bogeyRenderer -> bogeyRenderer.render(bogey.bogeyData, wheelAngle, ms));
renderer.render(bogey.bogeyData, wheelAngle, ms);
}
public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) {
var lightPos = new BlockPos(getLightPos(entity));
updateLight(world.getBrightness(LightLayer.BLOCK, lightPos), world.getBrightness(LightLayer.SKY, lightPos));
commonRenderer
.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) {
if (bogey.getAnchorPosition() != null) {
return bogey.getAnchorPosition();
} else {
return entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
return bogey.getAnchorPosition() != null ? bogey.getAnchorPosition()
: entity.getLightProbePosition(AnimationTickHolder.getPartialTicks());
}
public void updateLight(int blockLight, int skyLight) {
for (ModelData shaft : shafts) {
shaft.setBlockLight(blockLight)
.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();
}
@FunctionalInterface
interface BogeyInstanceFactory {
BogeyInstance create(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager);
}
}

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;
}
public boolean isOnIncompatibleTrack() {
return leadingBogey().type.isOnIncompatibleTrack(this, true)
|| trailingBogey().type.isOnIncompatibleTrack(this, false);
}
public void setTrain(Train train) {
this.train = train;
}
@ -306,6 +311,9 @@ public class Carriage {
double leadingWheelSpacing = leadingBogey.type.getWheelPointSpacing();
double trailingWheelSpacing = trailingBogey.type.getWheelPointSpacing();
boolean leadingUpsideDown = leadingBogey.isUpsideDown();
boolean trailingUpsideDown = trailingBogey.isUpsideDown();
for (boolean leading : Iterate.trueAndFalse) {
TravellingPoint point = leading ? getLeadingPoint() : getTrailingPoint();
TravellingPoint otherPoint = !leading ? getLeadingPoint() : getTrailingPoint();
@ -321,26 +329,31 @@ public class Carriage {
dce.positionAnchor = dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: 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()) {
dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition()
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2));
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition()
leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition(backAnchorFlip)
: pivoted(dce, dimension, point,
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2));
leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2,
leadingUpsideDown, trailingUpsideDown));
} else {
if (dimension.equals(otherDimension)) {
dce.rotationAnchors = leadingBogey.points.map(tp -> tp.getPosition(train.graph));
} else {
dce.rotationAnchors
.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors
.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing));
dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point
? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point
? point.getPosition(train.graph)
: pivoted(dce, dimension, point, leadingWheelSpacing, leadingUpsideDown, trailingUpsideDown));
}
}
@ -358,15 +371,16 @@ public class Carriage {
}
private Vec3 pivoted(DimensionalCarriageEntity dce, ResourceKey<Level> dimension, TravellingPoint start,
double offset) {
double offset, boolean leadingUpsideDown, boolean trailingUpsideDown) {
if (train.graph == null)
return dce.pivot == null ? null : dce.pivot.getLocation();
TrackNodeLocation pivot = dce.findPivot(dimension, start == getLeadingPoint());
if (pivot == 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()
.add(0, 1, 0);
.add(0, leadingUpsideDown ? -1.0 : 1.0, 0);
return VecHelper.lerp((float) (offset / startVec.distanceTo(portalVec)), startVec, portalVec);
}

View file

@ -1,15 +1,21 @@
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 com.jozufozu.flywheel.api.MaterialManager;
import com.simibubi.create.AllBogeyStyles;
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.IBogeyBlock;
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.Couple;
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.VecHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
@ -27,11 +33,15 @@ import net.minecraftforge.registries.ForgeRegistries;
public class CarriageBogey {
public Carriage carriage;
public static final String UPSIDE_DOWN_KEY = "UpsideDown";
public Carriage carriage;
boolean isLeading;
IBogeyBlock type;
public CompoundTag bogeyData;
AbstractBogeyBlock<?> type;
boolean upsideDown;
Couple<TravellingPoint> points;
LerpedFloat wheelAngle;
@ -42,8 +52,15 @@ public class CarriageBogey {
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.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);
wheelAngle = LerpedFloat.angular();
yaw = LerpedFloat.angular();
@ -100,11 +117,15 @@ public class CarriageBogey {
}
public TravellingPoint leading() {
return points.getFirst();
TravellingPoint point = points.getFirst();
point.upsideDown = isUpsideDown();
return point;
}
public TravellingPoint trailing() {
return points.getSecond();
TravellingPoint point = points.getSecond();
point.upsideDown = isUpsideDown();
return point;
}
public double getStress() {
@ -118,18 +139,25 @@ public class CarriageBogey {
@Nullable
public Vec3 getAnchorPosition() {
return getAnchorPosition(false);
}
@Nullable
public Vec3 getAnchorPosition(boolean flipUpsideDown) {
if (leading().edge == null)
return null;
return points.getFirst()
.getPosition(carriage.train.graph)
.getPosition(carriage.train.graph, flipUpsideDown)
.add(points.getSecond()
.getPosition(carriage.train.graph))
.getPosition(carriage.train.graph, flipUpsideDown))
.scale(.5);
}
public void updateCouplingAnchor(Vec3 entityPos, float entityXRot, float entityYRot, int bogeySpacing,
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 = 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, -entityXRot, Axis.X);
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));
}
@ -150,23 +180,46 @@ public class CarriageBogey {
tag.putString("Type", RegisteredObjects.getKeyOrThrow((Block) type)
.toString());
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;
}
public static CarriageBogey read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) {
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),
c -> TravellingPoint.read(c, graph, dimensions));
CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond());
return carriageBogey;
CompoundTag data = tag.getCompound(AbstractBogeyBlockEntity.BOGEY_DATA_KEY);
return new CarriageBogey(type, upsideDown, data, points.getFirst(), points.getSecond());
}
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() {
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.processing.burner.BlazeBurnerBlock;
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.Iterate;
import com.simibubi.create.foundation.utility.Lang;
@ -71,7 +71,7 @@ public class CarriageContraption extends Contraption {
// render
public int portalCutoffMin;
public int portalCutoffMax;
static final IItemHandlerModifiable fallbackItems = new ItemStackHandler();
static final IFluidHandler fallbackFluids = new FluidTank(0);
@ -162,11 +162,13 @@ public class CarriageContraption extends Contraption {
.getStep(), toLocalPos(pos));
}
if (blockState.getBlock() instanceof IBogeyBlock) {
if (blockState.getBlock() instanceof AbstractBogeyBlock<?> bogey) {
boolean captureTE = bogey.captureTileEntityForTrain();
bogeys++;
if (bogeys == 2)
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)
@ -235,7 +237,7 @@ public class CarriageContraption extends Contraption {
protected MountedStorageManager getStorageForSpawnPacket() {
return storageProxy;
}
@Override
protected ContraptionType getType() {
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.core.BlockPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer<CarriageContraptionEntity> {
@ -37,7 +38,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
MultiBufferSource buffers, int overlay) {
if (!entity.validForRender || entity.firstPositionUpdate)
return;
super.render(entity, yaw, partialTicks, ms, buffers, overlay);
Carriage carriage = entity.getCarriage();
@ -65,8 +66,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks);
int light = getBogeyLightCoords(entity, bogey, partialTicks);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light,
overlay);
overlay, bogey.getStyle(), bogey.bogeyData);
ms.popPose();
}
@ -80,6 +82,8 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot,
float viewXRot, float partialTicks) {
boolean selfUpsideDown = bogey.isUpsideDown();
boolean leadingUpsideDown = bogey.carriage.leadingBogey().isUpsideDown();
TransformStack.cast(ms)
.rotateY(viewYRot + 90)
.rotateX(-viewXRot)
@ -90,7 +94,9 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
.rotateY(-viewYRot - 90)
.rotateY(bogey.yaw.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) {

View file

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

View file

@ -79,7 +79,7 @@ public class CarriageCouplingRenderer {
float margin = 3 / 16f;
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);
double stretch = ((anchor2.distanceTo(anchor) - 2 * margin) * 4) / couplingSegments;
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.core.Direction.Axis;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
@ -110,7 +109,7 @@ public class CarriageParticles {
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.SoundEntry;
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.Chaser;
@ -29,6 +30,9 @@ public class CarriageSounds {
LoopingSound sharedWheelSoundSeated;
LoopingSound sharedHonkSound;
Couple<SoundEvent> bogeySounds;
SoundEvent closestBogeySound;
boolean arrived;
int tick;
@ -36,6 +40,10 @@ public class CarriageSounds {
public CarriageSounds(CarriageContraptionEntity 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();
speedFactor = LerpedFloat.linear();
approachFactor = LerpedFloat.linear();
@ -79,6 +87,15 @@ public class CarriageSounds {
double distance1 = toBogey1.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;
double distance = Math.min(distance1, distance2);
Vec3 soundLocation = cam.add(toCarriage);
@ -97,7 +114,7 @@ public class CarriageSounds {
seatCrossfade.tickChaser();
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());
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) {
Minecraft mc = Minecraft.getInstance();
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());
boolean approach = true;

View file

@ -14,6 +14,8 @@ import java.util.UUID;
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.MutableObject;
@ -555,6 +557,21 @@ public class Navigation {
if (graph == null)
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<>();
boolean costRelevant = maxCost >= 0;
if (costRelevant) {
@ -674,6 +691,8 @@ public class Navigation {
continue;
for (Entry<TrackNode, TrackEdge> target : validTargets) {
if (!validTypes.contains(target.getValue().getTrackMaterial().trackType))
continue;
TrackNode newNode = target.getKey();
TrackEdge newEdge = target.getValue();
double newDistance = newEdge.getLength() + distance;

View file

@ -17,6 +17,10 @@ import java.util.function.Consumer;
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.MutableObject;
@ -124,7 +128,7 @@ public class Train {
public int honkPitch;
public float accumulatedSteamRelease;
int tickOffset;
double[] stress;
@ -277,7 +281,7 @@ public class Train {
int carriageCount = carriages.size();
boolean stalled = false;
double maxStress = 0;
if (carriageWaitingForChunks != -1)
distance = 0;
@ -313,11 +317,17 @@ public class Train {
if (leadingAnchor == null || trailingAnchor == null)
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++;
}
}
if (entries > 0)
actual = total / entries;
@ -369,13 +379,13 @@ public class Train {
.getLeadingPoint();
double totalStress = derailed ? 0 : leadingStress + trailingStress;
boolean first = i == 0;
boolean last = i == carriageCount - 1;
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
double actualDistance =
carriage.travel(level, graph, distance + totalStress, toFollowForward, toFollowBackward, carriageType);
blocked |= carriage.blocked;
blocked |= carriage.blocked || carriage.isOnIncompatibleTrack();
boolean onTwoBogeys = carriage.isOnTwoBogeys();
maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0);
@ -722,9 +732,20 @@ public class Train {
if (entity.getContraption()instanceof CarriageContraption cc)
cc.returnStorageForDisassembly(carriage.storage);
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();
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;
if (i < carriageSpacing.size())
@ -944,7 +965,7 @@ public class Train {
occupiedObservers.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;
MutableObject<UUID> prevGroup = new MutableObject<>(null);

View file

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

View file

@ -115,10 +115,13 @@ public class TrainRelocator {
BlockPos blockPos = blockhit.getBlockPos();
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) {
for (int i = 0; i < toVisualise.size() - 1; i++) {
Vec3 vec1 = toVisualise.get(i);
Vec3 vec2 = toVisualise.get(i + 1);
Vec3 vec1 = toVisualise.get(i).add(offset);
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))
.colored(lastHoveredResult || i != toVisualise.size() - 2 ? 0x95CD41 : 0xEA5C2B)
.disableLineNormals()
@ -150,7 +153,7 @@ public class TrainRelocator {
boolean direction = bezierSelection != null && lookAngle.dot(bezierSelection.direction()) < 0;
boolean result = relocate(relocating, mc.level, blockPos, hoveredBezier, direction, lookAngle, true);
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,
direction, lookAngle, relocatingEntityId));
}
@ -182,7 +185,7 @@ public class TrainRelocator {
if (edge == null)
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();
ITurnListener ignoreTurns = probe.ignoreTurns();
List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>();

View file

@ -38,6 +38,7 @@ public class TravellingPoint {
public TrackEdge edge;
public double position;
public boolean blocked;
public boolean upsideDown;
public static enum SteerDirection {
NONE(0), LEFT(-1), RIGHT(1);
@ -64,11 +65,12 @@ public class 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.node2 = node2;
this.edge = edge;
this.position = position;
this.upsideDown = upsideDown;
}
public IEdgePointListener ignoreEdgePoints() {
@ -395,13 +397,18 @@ public class TravellingPoint {
}
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();
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) {
@ -422,12 +429,13 @@ public class TravellingPoint {
tag.put("Nodes", nodes.map(TrackNode::getLocation)
.serializeEach(loc -> loc.write(dimensions)));
tag.putDouble("Position", position);
tag.putBoolean("UpsideDown", upsideDown);
return tag;
}
public static TravellingPoint read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) {
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.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), c -> TrackNodeLocation.read(c, dimensions))
@ -435,11 +443,11 @@ public class TravellingPoint {
: Couple.create(null, null);
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");
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.logistics.block.depot.DepotBehaviour;
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.TrackEdge;
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.schedule.Schedule;
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.block.ProperWaterloggedBlock;
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.Chaser;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
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.item.ItemStack;
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.state.BlockState;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
@ -192,7 +195,8 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
Direction assemblyDirection;
int assemblyLength;
int[] bogeyLocations;
IBogeyBlock[] bogeyTypes;
AbstractBogeyBlock<?>[] bogeyTypes;
boolean[] upsideDownBogeys;
int bogeyCount;
@Override
@ -269,14 +273,30 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return false;
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;
if (!isValidBogeyOffset(bogeyOffset)) {
for (int i = -1; i <= 1; i++) {
BlockPos bogeyPos = pos.relative(assemblyDirection, i)
.offset(up);
BlockState blockState = level.getBlockState(bogeyPos);
if (blockState.getBlock() instanceof IBogeyBlock bogey) {
level.setBlock(bogeyPos, bogey.getRotatedBlockState(blockState, Direction.DOWN), 3);
for (boolean upsideDown : Iterate.falseAndTrue) {
for (int i = -1; i <= 1; i++) {
BlockPos bogeyPos = pos.relative(assemblyDirection, i)
.offset(upsideDown ? down : up);
BlockState blockState = level.getBlockState(bogeyPos);
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);
return true;
}
@ -291,7 +311,9 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
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)
.getDestroySpeed(level, targetPos) == -1) {
return false;
@ -299,7 +321,11 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
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);
player.displayClientMessage(Lang.translateDirect("train_assembly.bogey_created"), true);
SoundType soundtype = bogeyAnchor.getBlock()
@ -373,9 +399,12 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
if (bogeyLocations == null)
bogeyLocations = new int[maxBogeyCount];
if (bogeyTypes == null)
bogeyTypes = new IBogeyBlock[maxBogeyCount];
bogeyTypes = new AbstractBogeyBlock[maxBogeyCount];
if (upsideDownBogeys == null)
upsideDownBogeys = new boolean[maxBogeyCount];
Arrays.fill(bogeyLocations, -1);
Arrays.fill(bogeyTypes, null);
Arrays.fill(upsideDownBogeys, false);
for (int i = 0; i < MAX_LENGTH; i++) {
if (i == MAX_LENGTH - 1) {
@ -388,10 +417,19 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock() instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
bogeyIndex++;
BlockPos upsideDownBogeyOffset = new BlockPos(bogeyOffset.getX(), bogeyOffset.getY()*-1, bogeyOffset.getZ());
if (bogeyIndex < bogeyLocations.length) {
if (potentialBogeyState.getBlock() instanceof AbstractBogeyBlock<?> bogey && !bogey.isUpsideDown(potentialBogeyState)) {
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);
@ -551,7 +589,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
return;
}
points.add(new TravellingPoint(node, secondNode, edge, positionOnEdge));
points.add(new TravellingPoint(node, secondNode, edge, positionOnEdge, false));
}
secondNode = node;
@ -578,10 +616,11 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
spacing.add(bogeyLocations[bogeyIndex] - bogeyLocations[bogeyIndex - 1]);
CarriageContraption contraption = new CarriageContraption(assemblyDirection);
BlockPos bogeyPosOffset = trackPosition.offset(bogeyOffset);
BlockPos upsideDownBogeyPosOffset = trackPosition.offset(new BlockPos(bogeyOffset.getX(), bogeyOffset.getY() * -1, bogeyOffset.getZ()));
try {
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();
contraption.setSoundQueueOffset(offset);
if (!success) {
@ -594,24 +633,28 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
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 =
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;
BlockPos secondBogeyPos = contraption.getSecondBogeyPos();
int bogeySpacing = 0;
if (secondBogeyPos != null) {
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")),
contraptions.size() + 1);
return;
}
AbstractBogeyTileEntity secondBogeyTileEntity =
(AbstractBogeyTileEntity) level.getBlockEntity(secondBogeyPos);
bogeySpacing = bogeyLocations[bogeyIndex + 1] - bogeyLocations[bogeyIndex];
secondBogey = new CarriageBogey(bogeyTypes[bogeyIndex + 1], points.get(pointIndex + 2),
points.get(pointIndex + 3));
secondBogey = new CarriageBogey(bogeyTypes[bogeyIndex + 1], upsideDownBogeys[bogeyIndex + 1], secondBogeyTileEntity.getBogeyData(),
points.get(pointIndex + 2), points.get(pointIndex + 3));
bogeyIndex++;
} 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;
import com.simibubi.create.AllBlocks;
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.track.TrackBlockOutline.BezierPointSelection;
import com.simibubi.create.foundation.networking.AllPackets;
@ -117,7 +117,7 @@ public class CurvedTrackInteraction {
if (event.isUseItem()) {
ItemStack heldItem = player.getMainHandItem();
Item item = heldItem.getItem();
if (AllBlocks.TRACK.isIn(heldItem)) {
if (AllTags.AllBlockTags.TRACKS.matches(heldItem)) {
player.displayClientMessage(Lang.translateDirect("track.turn_start")
.withStyle(ChatFormatting.RED), true);
player.swing(InteractionHand.MAIN_HAND);

View file

@ -1,6 +1,6 @@
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 net.minecraft.nbt.CompoundTag;
@ -14,7 +14,7 @@ public class PlaceExtendedCurvePacket extends SimplePacketBase {
boolean mainHand;
boolean ctrlDown;
public PlaceExtendedCurvePacket(boolean mainHand, boolean ctrlDown) {
this.mainHand = mainHand;
this.ctrlDown = ctrlDown;
@ -36,7 +36,7 @@ public class PlaceExtendedCurvePacket extends SimplePacketBase {
context.enqueueWork(() -> {
ServerPlayer sender = context.getSender();
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;
CompoundTag tag = stack.getTag();
tag.putBoolean("ExtendCurve", true);

View file

@ -1,87 +1,36 @@
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.AllBlocks;
import com.simibubi.create.AllPartialModels;
import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.entity.BogeyInstance;
import com.simibubi.create.content.logistics.trains.entity.CarriageBogey;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.BogeySizes;
import com.simibubi.create.content.logistics.trains.TrackMaterial;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
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.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.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
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.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.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class StandardBogeyBlock extends Block
implements IBogeyBlock, IBE<StandardBogeyBlockEntity>, ProperWaterloggedBlock, ISpecialBlockItemRequirement {
public class StandardBogeyBlock extends AbstractBogeyBlock<StandardBogeyBlockEntity>
implements IBE<StandardBogeyBlockEntity>, ProperWaterloggedBlock, ISpecialBlockItemRequirement {
public static final EnumProperty<Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
private final boolean large;
public StandardBogeyBlock(Properties p_i48440_1_, boolean large) {
super(p_i48440_1_);
this.large = large;
public StandardBogeyBlock(Properties props, BogeySizes.BogeySize size) {
super(props, size);
registerDefaultState(defaultBlockState().setValue(WATERLOGGED, false));
}
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
builder.add(AXIS, WATERLOGGED);
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);
public TrackMaterial.TrackType getTrackType(BogeyStyle style) {
return TrackMaterial.TrackType.STANDARD;
}
@Override
@ -91,7 +40,7 @@ public class StandardBogeyBlock extends Block
@Override
public double getWheelRadius() {
return (large ? 12.5 : 6.5) / 16d;
return (size == BogeySizes.LARGE ? 12.5 : 6.5) / 16d;
}
@Override
@ -100,122 +49,8 @@ public class StandardBogeyBlock extends Block
}
@Override
public boolean allowsSingleBogeyCarriage() {
return true;
}
@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;
};
public BogeyStyle getDefaultStyle() {
return AllBogeyStyles.STANDARD;
}
@Override
@ -234,9 +69,4 @@ public class StandardBogeyBlock extends Block
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;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.foundation.blockEntity.CachedRenderBBBlockEntity;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
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) {
super(type, pos, state);
}
@Override
protected AABB createRenderBoundingBox() {
return super.createRenderBoundingBox().inflate(2);
public BogeyStyle getDefaultStyle() {
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.AllPartialModels;
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.particle.CubeParticleData;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.curiosities.girder.GirderBlock;
import com.simibubi.create.content.logistics.trains.BezierConnection;
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.DiscoveredLocation;
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.VecHelper;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.ChatFormatting;
import net.minecraft.client.multiplayer.ClientLevel;
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 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_);
registerDefaultState(defaultBlockState().setValue(SHAPE, TrackShape.ZO)
.setValue(HAS_BE, false)
.setValue(WATERLOGGED, false));
this.material = material;
}
@Override
@ -388,7 +395,7 @@ public class TrackBlock extends Block
(d, b) -> axis.scale(b ? 0 : fromCenter ? -d : d)
.add(center),
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
list = ITrackBlock.super.getConnected(world, pos, state, linear, connectedTo);
@ -404,7 +411,8 @@ public class TrackBlock extends Block
Map<BlockPos, BezierConnection> connections = trackTE.getConnections();
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,
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))
return list;
@ -416,7 +424,7 @@ public class TrackBlock extends Block
return list;
BlockPos boundPos = trackTE.boundLocation.getSecond();
BlockState boundState = otherLevel.getBlockState(boundPos);
if (!AllBlocks.TRACK.has(boundState))
if (!AllTags.AllBlockTags.TRACKS.matches(boundState))
return list;
Vec3 center = Vec3.atBottomCenterOf(pos)
@ -430,7 +438,8 @@ public class TrackBlock extends Block
getTrackAxes(world, pos, state).forEach(axis -> {
ITrackBlock.addToListIfConnected(connectedTo, list, (d, b) -> (b ? axis : boundAxis).scale(d)
.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;
@ -479,7 +488,7 @@ public class TrackBlock extends Block
if (!entry.getValue()
.isInside(pos))
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))
return InteractionResult.SUCCESS;
}
@ -495,7 +504,7 @@ public class TrackBlock extends Block
BlockPos girderPos = pPos.below()
.offset(vec3.z * side, 0, vec3.x * side);
BlockState girderState = pLevel.getBlockState(girderPos);
if (girderState.getBlock()instanceof GirderBlock girderBlock
if (girderState.getBlock() instanceof GirderBlock girderBlock
&& !blockTicks.hasScheduledTick(girderPos, girderBlock))
pLevel.scheduleTick(girderPos, girderBlock, 1);
}
@ -700,7 +709,7 @@ public class TrackBlock extends Block
Vec3 normal = 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());
if (bc != null) {
double length = Mth.floor(bc.getLength() * 2);
@ -745,7 +754,8 @@ public class TrackBlock extends Block
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;
for (BezierConnection bc : trackTE.connections.values())
yOffset += bc.starts.getFirst().y - pos.getY();
@ -771,7 +781,8 @@ public class TrackBlock extends Block
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity be) {
int trackAmount = 1;
int sameTypeTrackAmount = 1;
Object2IntMap<TrackMaterial> otherTrackAmounts = new Object2IntArrayMap<>();
int girderAmount = 0;
if (be instanceof TrackBlockEntity track) {
@ -779,15 +790,28 @@ public class TrackBlock extends Block
.values()) {
if (!bezierConnection.isPrimary())
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();
}
}
List<ItemStack> stacks = new ArrayList<>();
while (trackAmount > 0) {
stacks.add(AllBlocks.TRACK.asStack(Math.min(trackAmount, 64)));
trackAmount -= 64;
while (sameTypeTrackAmount > 0) {
stacks.add(new ItemStack(state.getBlock(), Math.min(sameTypeTrackAmount, 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) {
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);
}
@Override
public TrackMaterial getMaterial() {
return material;
}
public static class RenderProperties extends ReducedDestroyEffects implements MultiPosDestructionHandler {
@Override
@Nullable

View file

@ -10,6 +10,7 @@ import java.util.Set;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
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.StructureTransform;
import com.simibubi.create.content.logistics.trains.BezierConnection;
@ -125,6 +126,9 @@ public class TrackBlockEntity extends SmartBlockEntity implements ITransformable
}
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);
level.scheduleTick(worldPosition, getBlockState().getBlock(), 1);
notifyUpdate();
@ -326,7 +330,7 @@ public class TrackBlockEntity extends SmartBlockEntity implements ITransformable
.getLevel(boundLocation.getFirst());
if (otherLevel == null)
return;
if (AllBlocks.TRACK.has(otherLevel.getBlockState(boundLocation.getSecond())))
if (AllTags.AllBlockTags.TRACKS.matches(otherLevel.getBlockState(boundLocation.getSecond())))
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.AllSoundEvents;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.track.TrackPlacement.PlacementInfo;
import com.simibubi.create.foundation.networking.AllPackets;
@ -118,7 +119,7 @@ public class TrackBlockItem extends BlockItem {
return InteractionResult.SUCCESS;
stack = player.getMainHandItem();
if (AllBlocks.TRACK.isIn(stack)) {
if (AllTags.AllBlockTags.TRACKS.matches(stack)) {
stack.setTag(null);
player.setItemInHand(pContext.getHand(), stack);
}
@ -161,7 +162,7 @@ public class TrackBlockItem extends BlockItem {
@OnlyIn(Dist.CLIENT)
public static void sendExtenderPacket(PlayerInteractEvent.RightClickBlock event) {
ItemStack stack = event.getItemStack();
if (!AllBlocks.TRACK.isIn(stack) || !stack.hasTag())
if (!AllTags.AllBlockTags.TRACKS.matches(stack) || !stack.hasTag())
return;
if (Minecraft.getInstance().options.keySprint.isDown())
AllPackets.getChannel()

View file

@ -8,8 +8,8 @@ import java.util.function.Consumer;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTags;
import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
@ -162,7 +162,7 @@ public class TrackBlockOutline {
.rotateXRadians(angles.x)
.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);
ms.popPose();
}
@ -190,7 +190,7 @@ public class TrackBlockOutline {
ms.pushPose();
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);
boolean canConnectFrom = !shape.isJunction()
&& !(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.GirderAngles;
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.Iterate;
@ -38,7 +39,7 @@ public class TrackInstance extends BlockEntityInstance<TrackBlockEntity> {
@Override
public void update() {
if (blockEntity.connections.isEmpty())
if (blockEntity.connections.isEmpty())
return;
remove();
@ -110,11 +111,13 @@ public class TrackInstance extends BlockEntityInstance<TrackBlockEntity> {
leftLightPos = new BlockPos[segCount];
rightLightPos = new BlockPos[segCount];
mat.getModel(AllPartialModels.TRACK_TIE)
TrackMaterial.TrackModelHolder modelHolder = bc.getMaterial().getModelHolder();
mat.getModel(modelHolder.tie())
.createInstances(ties);
mat.getModel(AllPartialModels.TRACK_SEGMENT_LEFT)
mat.getModel(modelHolder.segment_left())
.createInstances(left);
mat.getModel(AllPartialModels.TRACK_SEGMENT_RIGHT)
mat.getModel(modelHolder.segment_right())
.createInstances(right);
SegmentAngles[] segments = bc.getBakedSegments();

View file

@ -6,16 +6,17 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.simibubi.create.AllBlocks;
import com.jozufozu.flywheel.util.Color;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.AllTags;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.logistics.trains.BezierConnection;
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.config.AllConfigs;
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.Iterate;
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.entity.BlockEntity;
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.HitResult;
import net.minecraft.world.phys.HitResult.Type;
@ -57,6 +59,11 @@ import net.minecraftforge.items.ItemHandlerHelper;
public class TrackPlacement {
public static class PlacementInfo {
public PlacementInfo(TrackMaterial material) {
this.trackMaterial = material;
}
BezierConnection curve = null;
boolean valid = false;
int end1Extent = 0;
@ -68,6 +75,7 @@ public class TrackPlacement {
public int requiredPavement = 0;
public boolean hasRequiredPavement = false;
public final TrackMaterial trackMaterial;
// for visualisation
Vec3 end1;
@ -109,7 +117,7 @@ public class TrackPlacement {
&& hoveringMaxed == maximiseTurn && lookAngle == hoveringAngle)
return cached;
PlacementInfo info = new PlacementInfo();
PlacementInfo info = new PlacementInfo(TrackMaterial.fromItem(stack.getItem()));
hoveringMaxed = maximiseTurn;
hoveringAngle = lookAngle;
hoveringPos = pos2;
@ -195,7 +203,7 @@ public class TrackPlacement {
BlockPos targetPos2 = pos2.offset(offset2.x, offset2.y, offset2.z);
info.curve = new BezierConnection(Couple.create(targetPos1, targetPos2),
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
@ -355,7 +363,7 @@ public class TrackPlacement {
info.curve = skipCurve ? null
: new BezierConnection(Couple.create(targetPos1, targetPos2),
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;
@ -400,7 +408,7 @@ public class TrackPlacement {
continue;
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()))
continue;
if (isTrack ? foundTracks >= tracks : foundPavement >= pavement)
@ -473,6 +481,18 @@ public class TrackPlacement {
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,
BlockPos targetPos1, BlockPos targetPos2, boolean simulate) {
info.requiredTracks = 0;
@ -500,7 +520,8 @@ public class TrackPlacement {
Vec3 offset = axis.scale(i);
BlockPos offsetPos = pos.offset(offset.x, offset.y, offset.z);
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()
.isReplaceable();
@ -523,15 +544,16 @@ public class TrackPlacement {
return info;
if (!simulate) {
BlockState onto = info.trackMaterial.getTrackBlock().get().defaultBlockState();
BlockState stateAtPos = level.getBlockState(targetPos1);
level.setBlock(targetPos1, ProperWaterloggedBlock.withWater(level,
(stateAtPos.getBlock() == state1.getBlock() ? stateAtPos : state1).setValue(TrackBlock.HAS_BE, true),
targetPos1), 3);
(AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state1, onto))
.setValue(TrackBlock.HAS_BE, true), targetPos1), 3);
stateAtPos = level.getBlockState(targetPos2);
level.setBlock(targetPos2, ProperWaterloggedBlock.withWater(level,
(stateAtPos.getBlock() == state2.getBlock() ? stateAtPos : state2).setValue(TrackBlock.HAS_BE, true),
targetPos2), 3);
(AllTags.AllBlockTags.TRACKS.matches(stateAtPos) ? stateAtPos : copyProperties(state2, onto))
.setValue(TrackBlock.HAS_BE, true), targetPos2), 3);
}
BlockEntity te1 = level.getBlockEntity(targetPos1);
@ -582,10 +604,10 @@ public class TrackPlacement {
return;
InteractionHand hand = InteractionHand.MAIN_HAND;
if (!AllBlocks.TRACK.isIn(stack)) {
if (!AllTags.AllBlockTags.TRACKS.matches(stack)) {
stack = player.getOffhandItem();
hand = InteractionHand.OFF_HAND;
if (!AllBlocks.TRACK.isIn(stack))
if (!AllTags.AllBlockTags.TRACKS.matches(stack))
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_MIDDLE;
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.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.GirderAngles;
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.render.CachedBufferer;
import com.simibubi.create.foundation.utility.AngleHelper;
@ -62,7 +60,9 @@ public class TrackRenderer extends SafeBlockEntityRenderer<TrackBlockEntity> {
SegmentAngles segment = segments[i];
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())
.mulNormal(segment.tieTransform.normal())
.light(light)
@ -70,7 +70,7 @@ public class TrackRenderer extends SafeBlockEntityRenderer<TrackBlockEntity> {
for (boolean first : Iterate.trueAndFalse) {
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())
.mulNormal(transform.normal())
.light(light)

View file

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

View file

@ -60,7 +60,8 @@ public class AllCommands {
.then(CameraDistanceCommand.register())
.then(CameraAngleCommand.register())
.then(FlySpeedCommand.register())
.then(KillTPSCommand.register())
//.then(DebugValueCommand.register())
//.then(KillTPSCommand.register())
.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 ConfigFloat mountedZoomMultiplier = f(3, 0, "mountedZoomMultiplier", Comments.mountedZoomMultiplier);
public final ConfigBool showTrackGraphOnF3 = b(false, "showTrackGraphOnF3", Comments.showTrackGraphOnF3);
public final ConfigBool showExtendedTrackGraphOnF3 = b(false, "showExtendedTrackGraphOnF3", Comments.showExtendedTrackGraphOnF3);
@Override
public String getName() {
@ -147,6 +148,7 @@ public class CClient extends ConfigBase {
static String trains = "Railway related settings";
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 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.Shape;
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.foundation.block.BlockStressDefaults;
import com.simibubi.create.foundation.block.ItemUseOverrides;
@ -95,6 +95,7 @@ public class BuilderTransformers {
.build();
}
@SuppressWarnings("deprecation")
public static <B extends StandardBogeyBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> bogey() {
return b -> b.initialProperties(SharedProperties::softMetal)
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK))
@ -103,7 +104,7 @@ public class BuilderTransformers {
.blockstate((c, p) -> BlockStateGen.horizontalAxisBlock(c, p, s -> p.models()
.getExistingFile(p.modLoc("block/track/bogey/top"))))
.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() {

View file

@ -3,6 +3,8 @@ package com.simibubi.create.foundation.ponder.content;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
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.ponder.PonderRegistrationHelper;
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.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.level.block.Blocks;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import java.util.stream.Collectors;
public class PonderIndex {
@ -320,7 +329,12 @@ public class PonderIndex {
.addStoryBoard("threshold_switch", DetectorScenes::thresholdSwitch);
// 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/portal", TrackScenes::portal)
.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.structureMovement.bearing.IBearingBlockEntity;
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.PonderWorld;
@ -34,7 +34,7 @@ public class AnimateBlockEntityInstruction extends TickingInstruction {
public static AnimateBlockEntityInstruction bogey(BlockPos location, float totalDelta, int ticks) {
float movedPerTick = 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)),
(w) -> 0f);
}

View file

@ -45,4 +45,13 @@ public class Iterate {
public static List<BlockPos> hereBelowAndAbove(BlockPos pos) {
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.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
@ -118,4 +119,12 @@ public class NBTHelper {
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.util.Mth;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
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) {
if (outlines.containsKey(slot))
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_illegal_pickup": "A mystical force is binding this Cart Contraption to the world",
"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"
}