Merge pull request #1 from Rabbitminers/mc1.18/dev

Bogey API
This commit is contained in:
techno-sam 2023-05-01 06:49:21 -07:00 committed by GitHub
commit 206de01311
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1323 additions and 546 deletions

View file

@ -207,6 +207,8 @@ import com.simibubi.create.content.logistics.block.vault.ItemVaultBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultCTBehaviour;
import com.simibubi.create.content.logistics.block.vault.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;
@ -668,7 +670,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();
@ -927,7 +929,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();
@ -1581,13 +1583,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,114 @@
package com.simibubi.create;
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.*;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
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.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import static com.simibubi.create.Create.LOGGER;
public class AllBogeyStyles {
public static final Map<ResourceLocation, BogeyStyle> BOGEY_STYLES = new HashMap<>();
public static BogeyStyle STANDARD = create("standard")
.commonRenderer(CommonStandardBogeyRenderer::new)
.displayName(new TranslatableComponent("create.bogeys.styles.standard"))
.size(BogeySizes.SMALL, SmallStandardBogeyRenderer::new, AllBlocks.SMALL_BOGEY)
.size(BogeySizes.LARGE, LargeStandardBogeyRenderer::new, AllBlocks.LARGE_BOGEY)
.build();
public static BogeyStyleBuilder create(String name) {
return create(Create.asResource(name));
}
public static BogeyStyleBuilder create(ResourceLocation name) {
return new BogeyStyleBuilder(name);
}
public static void register() {
LOGGER.info("Registered bogey styles from " + Create.ID);
}
public static class BogeyStyleBuilder {
protected final Map<BogeySizes.BogeySize, BogeyStyle.SizeData> sizes = new HashMap<>();
protected final ResourceLocation name;
protected Component displayName = Lang.translateDirect("create.bogeys.invalid");
protected ResourceLocation soundType = AllSoundEvents.TRAIN2.getId();
protected CompoundTag defaultData = new CompoundTag();
protected ParticleOptions contactParticle = ParticleTypes.CRIT;
protected ParticleOptions smokeParticle = ParticleTypes.POOF;
protected Optional<CommonRenderer> commonRenderer = Optional.empty();
public BogeyStyleBuilder(ResourceLocation name) {
this.name = name;
}
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.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.get());
return this;
}
public BogeyStyle build() {
BogeyStyle entry =
new BogeyStyle(name, displayName, soundType, contactParticle, smokeParticle, defaultData, sizes, commonRenderer);
BOGEY_STYLES.put(name, entry);
return entry;
}
}
}

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;
@ -121,6 +123,8 @@ public class Create {
AllFeatures.register(modEventBus);
AllPlacementModifiers.register(modEventBus);
BuiltinRegistration.register(modEventBus);
BogeySizes.init();
AllBogeyStyles.register();
AllConfigs.register(modLoadingContext);

View file

@ -30,7 +30,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;
@ -337,7 +337,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

@ -63,7 +63,7 @@ import com.simibubi.create.content.curiosities.deco.SlidingDoorBlock;
import com.simibubi.create.content.logistics.block.inventories.CreativeCrateTileEntity;
import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock;
import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.IMultiTileContainer;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
@ -342,7 +342,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));
@ -1006,7 +1006,7 @@ public abstract class Contraption {
if (disassembled)
return;
disassembled = true;
for (boolean nonBrittles : Iterate.trueAndFalse) {
for (StructureBlockInfo block : blocks.values()) {
if (nonBrittles == BlockMovementChecks.isBrittle(block.state))

View file

@ -0,0 +1,282 @@
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.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.track.StandardBogeyTileEntity;
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.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 extends Block implements ITE<StandardBogeyTileEntity>, 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 static void register(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 abstract Vec3 getConnectorAnchorOffset();
public boolean allowsSingleBogeyCarriage() {
return true;
}
@OnlyIn(Dist.CLIENT)
public void render(@Nullable BlockState state, float wheelAngle, PoseStack ms, float partialTicks,
MultiBufferSource buffers, int light, int overlay, StandardBogeyTileEntity sbte) {
BogeyStyle style = sbte.getStyle();
final Optional<BogeyRenderer.CommonRenderer> commonRenderer
= style.getNewCommonRenderInstance();
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());
renderer.render(sbte.getBogeyData(), wheelAngle, ms, light, vb);
commonRenderer.ifPresent(common ->
common.render(sbte.getBogeyData(), wheelAngle, ms, light, vb));
}
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) {
Collection<BogeyStyle> styles = AllBogeyStyles.BOGEY_STYLES.values();
if (styles.size() <= 1)
return InteractionResult.PASS;
BlockEntity be = level.getBlockEntity(pos);
if (!(be instanceof StandardBogeyTileEntity sbte))
return InteractionResult.FAIL;
player.getCooldowns().addCooldown(stack.getItem(), 20);
BogeyStyle currentStyle = sbte.getStyle();
BogeySizes.BogeySize size = getSize();
BogeyStyle style = this.getNextStyle(currentStyle);
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("create.bogey.style.updated_style")
.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 StandardBogeyTileEntity newTileEntity))
return InteractionResult.FAIL;
newTileEntity.setBogeyData(oldData);
player.displayClientMessage(Lang.translateDirect("create.bogey.style.updated_style_and_size")
.append(": " + style.displayName), true);
}
return InteractionResult.CONSUME;
}
return InteractionResult.PASS;
}
@Override
public 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 AbstractBogeyBlock 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;
}
public BlockState getNextSize(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof StandardBogeyTileEntity sbte)
return this.getNextSize(sbte);
return level.getBlockState(pos);
}
public BlockState getNextSize(StandardBogeyTileEntity sbte) {
BogeySizes.BogeySize size = this.getSize();
BogeyStyle style = sbte.getStyle();
BlockState nextBlock = style.getNextBlock(size).defaultBlockState();
return nextBlock.hasProperty(WATERLOGGED)
? nextBlock.setValue(WATERLOGGED, sbte.getBlockState().getValue(WATERLOGGED))
: nextBlock;
}
public BlockState getStateOfSize(StandardBogeyTileEntity sbte, BogeySizes.BogeySize size) {
BogeyStyle style = sbte.getStyle();
BlockState state = style.getBlockOfSize(size).defaultBlockState();
return state.hasProperty(WATERLOGGED)
? state.setValue(WATERLOGGED, sbte.getBlockState().getValue(WATERLOGGED))
: state;
}
public BogeyStyle getNextStyle(Level level, BlockPos pos) {
BlockEntity te = level.getBlockEntity(pos);
if (te instanceof StandardBogeyTileEntity sbte)
return this.getNextStyle(sbte.getStyle());
return AllBogeyStyles.STANDARD;
}
public BogeyStyle getNextStyle(BogeyStyle style) {
Collection<BogeyStyle> allStyles = AllBogeyStyles.BOGEY_STYLES.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());
}
}

View file

@ -0,0 +1,298 @@
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 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<?>[] getTransformsFromPartial(PartialModel model, PoseStack ms, boolean inContraption, int size) {
return (inContraption) ? 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);
/**
* 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);
}
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 inContraption 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 inContraption) {
BlockState air = Blocks.AIR.defaultBlockState();
return inContraption ? contraptionModelData.get(keyFromModel(model))[0].setTransform(ms)
: CachedBufferer.partial(model, air);
}
/**
* 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-world 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);
}
/**
* 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();
}
/**
* Used for rendering in contraptions allowing for individual instances of models for each component
*
* @return new Instance of renderer
*/
public abstract BogeyRenderer createNewInstance();
public static abstract class CommonRenderer extends BogeyRenderer {
@Override
public BogeySizes.BogeySize getSize() {
return null;
}
@Override
public abstract CommonRenderer createNewInstance();
}
}

View file

@ -0,0 +1,69 @@
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 void addSize(String modId, String name, float size) {
ResourceLocation location = new ResourceLocation(modId, name);
addSize(location, size);
}
public static void addSize(ResourceLocation location, float size) {
BogeySize customSize = new BogeySize(location, size);
BOGEY_SIZES.add(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

@ -17,11 +17,11 @@ public class BogeyTileEntityRenderer<T extends BlockEntity> extends SafeTileEnti
protected void renderSafe(T te, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light,
int overlay) {
BlockState blockState = te.getBlockState();
float angle = 0;
if (te instanceof StandardBogeyTileEntity sbte)
angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock()instanceof IBogeyBlock bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay);
if (te instanceof StandardBogeyTileEntity sbte) {
float angle = sbte.getVirtualAngle(partialTicks);
if (blockState.getBlock() instanceof AbstractBogeyBlock bogey)
bogey.render(blockState, angle, ms, partialTicks, buffer, light, overlay, sbte);
}
}
}

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

@ -0,0 +1,151 @@
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 CommonStandardBogeyRenderer createNewInstance() {
return new CommonStandardBogeyRenderer();
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb) {
boolean inContraption = vb == null;
Transform<?>[] shafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.Z), ms, inContraption, 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 BogeyRenderer createNewInstance() {
return new SmallStandardBogeyRenderer();
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.SMALL;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb) {
boolean inContraption = vb == null;
Transform<?> transform = getTransformFromPartial(BOGEY_FRAME, ms, inContraption);
finalize(transform, ms, light, vb);
Transform<?>[] wheels = getTransformsFromPartial(SMALL_BOGEY_WHEELS, ms, inContraption, 2);
for (int side : Iterate.positiveAndNegative) {
if (!inContraption)
ms.pushPose();
Transform<?> wheel = wheels[(side + 1)/2];
wheel.translate(0, 12 / 16f, side)
.rotateX(wheelAngle);
finalize(wheel, ms, light, vb);
if (!inContraption)
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 BogeyRenderer createNewInstance() {
return new LargeStandardBogeyRenderer();
}
@Override
public BogeySizes.BogeySize getSize() {
return BogeySizes.LARGE;
}
@Override
public void render(CompoundTag bogeyData, float wheelAngle, PoseStack ms, int light, VertexConsumer vb) {
boolean inContraption = vb == null;
Transform<?>[] secondaryShafts = getTransformsFromBlockState(AllBlocks.SHAFT.getDefaultState()
.setValue(ShaftBlock.AXIS, Direction.Axis.X), ms, inContraption, 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, inContraption);
finalize(bogeyDrive, ms, light, vb);
Transform<?> bogeyPiston = getTransformFromPartial(BOGEY_PISTON, ms, inContraption)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)));
finalize(bogeyPiston, ms, light, vb);
if (!inContraption)
ms.pushPose();
Transform<?> bogeyWheels = getTransformFromPartial(LARGE_BOGEY_WHEELS, ms, inContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle);
finalize(bogeyWheels, ms, light, vb);
Transform<?> bogeyPin = getTransformFromPartial(BOGEY_PIN, ms, inContraption)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle);
finalize(bogeyPin, ms, light, vb);
if (!inContraption)
ms.popPose();
}
}
}

View file

@ -0,0 +1,27 @@
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) {
}
@Override
public void initialiseContraptionModelData(MaterialManager materialManager) {
}
@Override
public CommonRenderer createNewInstance() {
return new BackupBogeyRenderer();
}
}

View file

@ -1,235 +1,72 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.Material;
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.AllBlockPartials;
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 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 {
import java.util.Optional;
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(AllBlockPartials.BOGEY_FRAME)
.createInstance();
wheels = new ModelData[2];
materialManager.defaultSolid()
.material(Materials.TRANSFORMED)
.getModel(AllBlockPartials.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(AllBlockPartials.BOGEY_DRIVE)
.createInstance();
piston = mat.getModel(AllBlockPartials.BOGEY_PISTON)
.createInstance();
wheels = mat.getModel(AllBlockPartials.LARGE_BOGEY_WHEELS)
.createInstance();
pin = mat.getModel(AllBlockPartials.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,95 @@
package com.simibubi.create.content.logistics.trains.entity;
import com.jozufozu.flywheel.api.MaterialManager;
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.stream.Stream;
public class BogeyStyle {
public final ResourceLocation name;
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, Component displayName, ResourceLocation soundType, ParticleOptions contactParticle, ParticleOptions smokeParticle,
CompoundTag defaultData, Map<BogeySizes.BogeySize, SizeData> sizes, Optional<CommonRenderer> commonRenderer) {
this.name = name;
this.displayName = displayName;
this.soundType = soundType;
this.contactParticle = contactParticle;
this.smokeParticle = smokeParticle;
this.defaultData = defaultData;
this.sizes = sizes;
this.commonRenderer = commonRenderer;
}
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.getInWorldRenderInstance(size).createNewInstance();
}
public BogeyRenderer getInWorldRenderInstance(BogeySizes.BogeySize size) {
SizeData sizeData = this.sizes.get(size);
return sizeData != null ? sizeData.renderer() : BackupBogeyRenderer.INSTANCE;
}
public Optional<CommonRenderer> getInWorldCommonRenderInstance() {
return this.commonRenderer;
}
public Optional<CommonRenderer> getNewCommonRenderInstance() {
return this.getInWorldCommonRenderInstance().map(CommonRenderer::createNewInstance);
}
public BogeyInstance createInstance(CarriageBogey bogey, BogeySizes.BogeySize size, MaterialManager materialManager) {
return new BogeyInstance(bogey, this, size, materialManager);
}
public record SizeData(ResourceLocation block, BogeyRenderer renderer) {
}
}

View file

@ -3,13 +3,16 @@ package com.simibubi.create.content.logistics.trains.entity;
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.DimensionPalette;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity;
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;
@ -25,13 +28,16 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.registries.ForgeRegistries;
import static com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity.BOGEY_STYLE_KEY;
public class CarriageBogey {
public Carriage carriage;
boolean isLeading;
IBogeyBlock type;
public CompoundTag bogeyData;
AbstractBogeyBlock type;
Couple<TravellingPoint> points;
LerpedFloat wheelAngle;
@ -42,7 +48,10 @@ public class CarriageBogey {
int derailAngle;
public CarriageBogey(IBogeyBlock type, TravellingPoint point, TravellingPoint point2) {
public CarriageBogey(AbstractBogeyBlock type, CompoundTag bogeyData, TravellingPoint point, TravellingPoint point2) {
if (bogeyData == null || bogeyData.isEmpty())
bogeyData = this.createBogeyData(); // Prevent Crash When Updating
this.bogeyData = bogeyData;
this.type = type;
points = Couple.create(point, point2);
wheelAngle = LerpedFloat.angular();
@ -150,20 +159,32 @@ public class CarriageBogey {
tag.putString("Type", RegisteredObjects.getKeyOrThrow((Block) type)
.toString());
tag.put("Points", points.serializeEach(tp -> tp.write(dimensions)));
tag.put(BOGEY_STYLE_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);
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(StandardBogeyTileEntity.BOGEY_DATA_KEY);
return new CarriageBogey(type, 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);
return AllBogeyStyles.BOGEY_STYLES.get(location);
}
private CompoundTag createBogeyData() {
CompoundTag nbt = new CompoundTag();
NBTHelper.writeResourceLocation(nbt, BOGEY_STYLE_KEY, AllBogeyStyles.STANDARD.name);
return nbt;
}
void setLeading() {

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,7 +162,7 @@ public class CarriageContraption extends Contraption {
.getStep(), toLocalPos(pos));
}
if (blockState.getBlock() instanceof IBogeyBlock) {
if (blockState.getBlock() instanceof AbstractBogeyBlock) {
bogeys++;
if (bogeys == 2)
secondBogeyPos = pos;
@ -235,7 +235,7 @@ public class CarriageContraption extends Contraption {
protected MountedStorageManager getStorageForSpawnPacket() {
return storageProxy;
}
@Override
protected ContraptionType getType() {
return ContraptionType.CARRIAGE;

View file

@ -7,12 +7,15 @@ import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionEntityRenderer;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
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 +40,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 +68,10 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer
translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks);
int light = getBogeyLightCoords(entity, bogey, partialTicks);
BlockEntity be = entity.getContraption().presentTileEntities.get(bogeyPos);
bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light,
overlay);
overlay, (StandardBogeyTileEntity) be);
ms.popPose();
}

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

@ -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().getSoundType()
: AllSoundEvents.TRAIN2.getMainEvent());
closestBogeySound = bogeySounds.getFirst();
distanceFactor = LerpedFloat.linear();
speedFactor = LerpedFloat.linear();
approachFactor = LerpedFloat.linear();
@ -79,6 +87,9 @@ public class CarriageSounds {
double distance1 = toBogey1.length();
double distance2 = toBogey2.length();
Couple<CarriageBogey> bogeys = entity.getCarriage().bogeys;
closestBogeySound = bogeys.get(distance1 > distance2).getStyle().getSoundType();
Vec3 toCarriage = distance1 > distance2 ? toBogey2 : toBogey1;
double distance = Math.min(distance1, distance2);
Vec3 soundLocation = cam.add(toCarriage);
@ -97,7 +108,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 +216,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

@ -17,6 +17,10 @@ import java.util.function.Consumer;
import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.track.StandardBogeyTileEntity;
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;
@ -317,7 +321,7 @@ public class Train {
entries++;
}
}
if (entries > 0)
actual = total / entries;
@ -369,7 +373,7 @@ 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;
@ -725,6 +729,15 @@ public class Train {
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset)));
entity.disassemble();
for (CarriageBogey bogey : carriage.bogeys) {
Vec3 bogeyPosition = bogey.getAnchorPosition();
if (bogeyPosition == null) continue;
BlockEntity be = level.getBlockEntity(new BlockPos(bogeyPosition));
if (!(be instanceof StandardBogeyTileEntity sbte))
continue;
sbte.setBogeyData(bogey.bogeyData);
}
offset += carriage.bogeySpacing;
if (i < carriageSpacing.size())

View file

@ -7,12 +7,13 @@ import java.util.UUID;
import java.util.function.Supplier;
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;
@ -44,11 +45,12 @@ 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());
CompoundTag data = buffer.readNbt();
bogies.set(isFirst, new CarriageBogey(type, data, new TravellingPoint(), new TravellingPoint()));
}
int spacing = buffer.readVarInt();
carriages.add(new Carriage(bogies.getFirst(), bogies.getSecond(), spacing));
@ -86,6 +88,7 @@ public class TrainPacket extends SimplePacketBase {
}
CarriageBogey bogey = carriage.bogeys.get(first);
buffer.writeResourceLocation(RegisteredObjects.getKeyOrThrow((Block) bogey.type));
buffer.writeNbt(bogey.bogeyData);
}
buffer.writeVarInt(carriage.bogeySpacing);
}

View file

@ -21,7 +21,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;
@ -38,6 +38,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.StandardBogeyTileEntity;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.config.AllConfigs;
@ -51,6 +52,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;
@ -65,7 +67,9 @@ import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
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;
@ -189,7 +193,7 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
Direction assemblyDirection;
int assemblyLength;
int[] bogeyLocations;
IBogeyBlock[] bogeyTypes;
AbstractBogeyBlock[] bogeyTypes;
int bogeyCount;
@Override
@ -272,8 +276,20 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
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);
if (blockState.getBlock() instanceof AbstractBogeyBlock bogey) {
BlockEntity be = level.getBlockEntity(bogeyPos);
if (!(be instanceof StandardBogeyTileEntity oldTE))
continue;
CompoundTag oldData = oldTE.getBogeyData();
BlockState newBlock = bogey.getNextSize(oldTE);
if (newBlock.getBlock() == bogey)
player.displayClientMessage(Lang.translateDirect("create.bogey.style.no_other_sizes")
.withStyle(ChatFormatting.RED), true);
level.setBlock(bogeyPos, newBlock, 3);
BlockEntity newEntity = level.getBlockEntity(bogeyPos);
if (!(newEntity instanceof StandardBogeyTileEntity newTE))
continue;
newTE.setBogeyData(oldData);
bogey.playRotateSound(level, bogeyPos);
return true;
}
@ -370,7 +386,7 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
if (bogeyLocations == null)
bogeyLocations = new int[maxBogeyCount];
if (bogeyTypes == null)
bogeyTypes = new IBogeyBlock[maxBogeyCount];
bogeyTypes = new AbstractBogeyBlock[maxBogeyCount];
Arrays.fill(bogeyLocations, -1);
Arrays.fill(bogeyTypes, null);
@ -385,7 +401,7 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
}
BlockState potentialBogeyState = level.getBlockState(bogeyOffset.offset(currentPos));
if (potentialBogeyState.getBlock() instanceof IBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
if (potentialBogeyState.getBlock() instanceof AbstractBogeyBlock bogey && bogeyIndex < bogeyLocations.length) {
bogeyTypes[bogeyIndex] = bogey;
bogeyLocations[bogeyIndex] = i;
bogeyIndex++;
@ -591,9 +607,11 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
return;
}
IBogeyBlock typeOfFirstBogey = bogeyTypes[bogeyIndex];
AbstractBogeyBlock typeOfFirstBogey = bogeyTypes[bogeyIndex];
BlockPos firstBogeyPos = contraption.anchor;
StandardBogeyTileEntity firstBogeyTileEntity = (StandardBogeyTileEntity) level.getBlockEntity(firstBogeyPos);
CarriageBogey firstBogey =
new CarriageBogey(typeOfFirstBogey, points.get(pointIndex), points.get(pointIndex + 1));
new CarriageBogey(typeOfFirstBogey, firstBogeyTileEntity.getBogeyData(), points.get(pointIndex), points.get(pointIndex + 1));
CarriageBogey secondBogey = null;
BlockPos secondBogeyPos = contraption.getSecondBogeyPos();
int bogeySpacing = 0;
@ -605,10 +623,11 @@ public class StationTileEntity extends SmartTileEntity implements ITransformable
contraptions.size() + 1);
return;
}
StandardBogeyTileEntity secondBogeyTileEntity =
(StandardBogeyTileEntity) 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], secondBogeyTileEntity.getBogeyData(),
points.get(pointIndex + 2), points.get(pointIndex + 3));
bogeyIndex++;
} else if (!typeOfFirstBogey.allowsSingleBogeyCarriage()) {

View file

@ -1,89 +1,30 @@
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.AllBlockPartials;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities;
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.content.logistics.trains.BogeyRenderer;
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
import com.simibubi.create.content.logistics.trains.BogeySizes;
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.ITE;
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, ITE<StandardBogeyTileEntity>, ProperWaterloggedBlock, ISpecialBlockItemRequirement {
public class StandardBogeyBlock extends AbstractBogeyBlock implements ITE<StandardBogeyTileEntity>, 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);
}
@Override
public double getWheelPointSpacing() {
return 2;
@ -91,7 +32,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
@ -99,125 +40,6 @@ public class StandardBogeyBlock extends Block
return new Vec3(0, 7 / 32f, 1);
}
@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(AllBlockPartials.BOGEY_FRAME, air)
.scale(1 - 1 / 512f)
.light(light)
.renderInto(ms, vb);
for (int side : Iterate.positiveAndNegative) {
ms.pushPose();
CachedBufferer.partial(AllBlockPartials.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(AllBlockPartials.BOGEY_DRIVE, air)
.scale(1 - 1 / 512f)
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllBlockPartials.BOGEY_PISTON, air)
.translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle)))
.light(light)
.renderInto(ms, vb);
ms.pushPose();
CachedBufferer.partial(AllBlockPartials.LARGE_BOGEY_WHEELS, air)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllBlockPartials.BOGEY_PIN, air)
.translate(0, 1, 0)
.rotateX(wheelAngle)
.translate(0, 1 / 4f, 0)
.rotateX(-wheelAngle)
.light(light)
.renderInto(ms, vb);
ms.popPose();
}
@Override
public BogeyInstance createInstance(MaterialManager materialManager, CarriageBogey bogey) {
if (large) {
return new BogeyInstance.Drive(bogey, materialManager);
} else {
return new BogeyInstance.Frame(bogey, materialManager);
}
}
@Override
public BlockState rotate(BlockState pState, Rotation pRotation) {
return switch (pRotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> pState.cycle(AXIS);
default -> pState;
};
}
@Override
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos,
Player player) {
@ -233,10 +55,4 @@ public class StandardBogeyBlock extends Block
public BlockEntityType<? extends StandardBogeyTileEntity> getTileEntityType() {
return AllTileEntities.BOGEY.get();
}
@Override
public ItemRequirement getRequiredItems(BlockState state, BlockEntity te) {
return new ItemRequirement(ItemUseType.CONSUME, AllBlocks.RAILWAY_CASING.asStack());
}
}

View file

@ -1,40 +1,112 @@
package com.simibubi.create.content.logistics.trains.track;
import com.simibubi.create.content.logistics.trains.IBogeyBlock;
import com.simibubi.create.AllBogeyStyles;
import com.simibubi.create.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;
public class StandardBogeyTileEntity extends CachedRenderBBTileEntity {
public static String BOGEY_STYLE_KEY = "BogeyStyle";
public static String BOGEY_DATA_KEY = "BogeyData";
private CompoundTag bogeyData;
public StandardBogeyTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
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 = AllBogeyStyles.STANDARD.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(AllBogeyStyles.STANDARD);
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, AllBogeyStyles.STANDARD.name);
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 IBogeyBlock type))
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

@ -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

@ -33,7 +33,7 @@ import com.simibubi.create.content.curiosities.deco.SlidingDoorMovementBehaviour
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;
@ -88,7 +88,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.register(RegisteredObjects.getKeyOrThrow(block)));
}
public static <B extends TrapDoorBlock, P> NonNullUnaryOperator<BlockBuilder<B, P>> trapdoor(boolean orientable) {

View file

@ -16,8 +16,10 @@ import com.simibubi.create.CreateClient;
import com.simibubi.create.content.AllSections;
import com.simibubi.create.content.contraptions.fluids.VirtualFluid;
import com.simibubi.create.content.contraptions.relays.encased.CasingConnectivity;
import com.simibubi.create.content.logistics.trains.entity.BogeyStyle;
import com.simibubi.create.foundation.block.connected.CTModel;
import com.simibubi.create.foundation.block.connected.ConnectedTextureBehaviour;
import com.simibubi.create.foundation.utility.CreateRegistry;
import com.simibubi.create.foundation.utility.RegisteredObjects;
import com.tterrag.registrate.AbstractRegistrate;
import com.tterrag.registrate.builders.BlockBuilder;

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;
@ -108,4 +109,18 @@ public class NBTHelper {
return new CompoundTag();
}
public static void writeResourceLocation(CompoundTag nbt, String key, ResourceLocation location) {
// Ensure correct format
nbt.putString(key, location.toString());
}
public static ResourceLocation readResourceLocation(CompoundTag nbt, String key) {
if (!nbt.contains(key))
return null;
String[] data = nbt.getString(key).split(":");
if (data.length != 2)
return null;
return new ResourceLocation(data[0], data[1]);
}
}