Custom Nixies

- Rows of nixie tubes will now display text from a name tag used on them
- Nixie tubes will dynamically update score/selector/nbt components in the displayed text
This commit is contained in:
simibubi 2020-10-11 17:50:48 +02:00
parent 22ed233969
commit 1b84bbf16f
4 changed files with 254 additions and 61 deletions

View file

@ -8,7 +8,6 @@ import java.util.Set;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.fluids.pipes.AxisPipeBlock;
import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock;
import com.simibubi.create.foundation.config.AllConfigs;
@ -27,13 +26,10 @@ import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fml.DistExecutor;
public class FluidPropagator {
@ -136,20 +132,21 @@ public class FluidPropagator {
return AllConfigs.SERVER.fluids.mechanicalPumpRange.get();
}
@Deprecated // Remove after pipes are fixed; comment out for production
public static OutlineParams showBlockFace(BlockFace face) {
MutableObject<OutlineParams> params = new MutableObject<>(new OutlineParams());
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
Vec3d directionVec = new Vec3d(face.getFace()
.getDirectionVec());
Vec3d scaleVec = directionVec.scale(-.25f * face.getFace()
.getAxisDirection()
.getOffset());
directionVec = directionVec.scale(.5f);
params.setValue(CreateClient.outliner.showAABB(face,
FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(face.getPos())))
.grow(scaleVec.x, scaleVec.y, scaleVec.z)
.grow(1 / 16f)));
});
// DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
// Vec3d directionVec = new Vec3d(face.getFace()
// .getDirectionVec());
// Vec3d scaleVec = directionVec.scale(-.25f * face.getFace()
// .getAxisDirection()
// .getOffset());
// directionVec = directionVec.scale(.5f);
// params.setValue(CreateClient.outliner.showAABB(face,
// FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(face.getPos())))
// .grow(scaleVec.x, scaleVec.y, scaleVec.z)
// .grow(1 / 16f)));
// });
return params.getValue();
}

View file

@ -8,12 +8,18 @@ import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
@ -29,6 +35,60 @@ public class NixieTubeBlock extends HorizontalBlock implements ITE<NixieTubeTile
setDefaultState(getDefaultState().with(CEILING, false));
}
@Override
public ActionResultType onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand,
BlockRayTraceResult ray) {
try {
ItemStack heldItem = player.getHeldItem(hand);
NixieTubeTileEntity nixie = getTileEntity(world, pos);
if (player.isSneaking())
return ActionResultType.PASS;
if (heldItem.isEmpty()) {
if (nixie.reactsToRedstone())
return ActionResultType.PASS;
nixie.clearCustomText();
updateDisplayedRedstoneValue(state, world, pos);
return ActionResultType.SUCCESS;
}
if (heldItem.getItem() == Items.NAME_TAG && heldItem.hasDisplayName()) {
Direction left = state.get(HORIZONTAL_FACING)
.rotateY();
Direction right = left.getOpposite();
if (world.isRemote)
return ActionResultType.SUCCESS;
BlockPos currentPos = pos;
while (true) {
BlockPos nextPos = currentPos.offset(left);
if (world.getBlockState(nextPos) != state)
break;
currentPos = nextPos;
}
int index = 0;
while (true) {
final int rowPosition = index;
withTileEntityDo(world, currentPos, te -> te.displayCustomNameOf(heldItem, rowPosition));
BlockPos nextPos = currentPos.offset(right);
if (world.getBlockState(nextPos) != state)
break;
currentPos = nextPos;
index++;
}
}
} catch (TileEntityException e) {
}
return ActionResultType.PASS;
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
super.fillStateContainer(builder.add(CEILING, HORIZONTAL_FACING));
@ -57,12 +117,12 @@ public class NixieTubeBlock extends HorizontalBlock implements ITE<NixieTubeTile
@Override
public void neighborChanged(BlockState p_220069_1_, World p_220069_2_, BlockPos p_220069_3_, Block p_220069_4_,
BlockPos p_220069_5_, boolean p_220069_6_) {
updateDisplayedValue(p_220069_1_, p_220069_2_, p_220069_3_);
updateDisplayedRedstoneValue(p_220069_1_, p_220069_2_, p_220069_3_);
}
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
updateDisplayedValue(state, worldIn, pos);
updateDisplayedRedstoneValue(state, worldIn, pos);
}
@Override
@ -75,12 +135,13 @@ public class NixieTubeBlock extends HorizontalBlock implements ITE<NixieTubeTile
return true;
}
private void updateDisplayedValue(BlockState state, World worldIn, BlockPos pos) {
private void updateDisplayedRedstoneValue(BlockState state, World worldIn, BlockPos pos) {
if (worldIn.isRemote)
return;
int power = getPower(worldIn, pos);
String display = (power < 10 ? "0" : "") + power;
withTileEntityDo(worldIn, pos, te -> te.display(display.charAt(0), display.charAt(1)));
withTileEntityDo(worldIn, pos, te -> {
if (te.reactsToRedstone())
te.displayRedstoneStrength(getPower(worldIn, pos));
});
}
static boolean isValidBlock(IBlockReader world, BlockPos pos, boolean above) {

View file

@ -6,6 +6,7 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.MatrixStacker;
import net.minecraft.block.BlockState;
@ -17,7 +18,7 @@ import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntity> {
Random r = new Random();
public NixieTubeRenderer(TileEntityRendererDispatcher dispatcher) {
super(dispatcher);
}
@ -25,64 +26,64 @@ public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntit
@Override
protected void renderSafe(NixieTubeTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer,
int light, int overlay) {
ms.push();
BlockState blockState = te.getBlockState();
MatrixStacker.of(ms)
.centre()
.rotateY(AngleHelper.horizontalAngle(blockState
.get(NixieTubeBlock.HORIZONTAL_FACING)));
.rotateY(AngleHelper.horizontalAngle(blockState.get(NixieTubeBlock.HORIZONTAL_FACING)));
float height = blockState.get(NixieTubeBlock.CEILING) ? 2 : 6;
float scale = 1 / 20f;
Couple<String> s = te.getVisibleText();
ms.push();
ms.translate(-4/16f, 0, 0);
ms.translate(-4 / 16f, 0, 0);
ms.scale(scale, -scale, scale);
drawTube(ms, buffer, te.tube1, height);
drawTube(ms, buffer, s.getFirst(), height);
ms.pop();
ms.push();
ms.translate(4/16f, 0, 0);
ms.translate(4 / 16f, 0, 0);
ms.scale(scale, -scale, scale);
drawTube(ms, buffer, te.tube2, height);
drawTube(ms, buffer, s.getSecond(), height);
ms.pop();
ms.pop();
}
private void drawTube(MatrixStack ms, IRenderTypeBuffer buffer, char c, float height) {
private void drawTube(MatrixStack ms, IRenderTypeBuffer buffer, String c, float height) {
FontRenderer fontRenderer = Minecraft.getInstance().fontRenderer;
float charWidth = fontRenderer.getCharWidth(c);
float charWidth = fontRenderer.getStringWidth(c);
float shadowOffset = .5f;
float flicker = r.nextFloat();
int brightColor = 0xFF982B;
int darkColor = 0xE03221;
int flickeringBrightColor = ColorHelper.mixColors(brightColor, darkColor, flicker / 4);
ms.push();
ms.translate((charWidth - shadowOffset) / -2f, -height, 0);
drawChar(ms, buffer, c, flickeringBrightColor);
ms.push();
ms.translate(shadowOffset, shadowOffset, -1/16f);
ms.translate(shadowOffset, shadowOffset, -1 / 16f);
drawChar(ms, buffer, c, darkColor);
ms.pop();
ms.pop();
ms.push();
ms.scale(-1, 1, 1);
ms.translate((charWidth - shadowOffset) / -2f, -height, 0);
drawChar(ms, buffer, c, darkColor);
ms.push();
ms.translate(-shadowOffset, shadowOffset, -1/16f);
ms.translate(-shadowOffset, shadowOffset, -1 / 16f);
drawChar(ms, buffer, c, 0x99180F);
ms.pop();
ms.pop();
}
private static void drawChar(MatrixStack ms, IRenderTypeBuffer buffer, char c, int color) {
private static void drawChar(MatrixStack ms, IRenderTypeBuffer buffer, String c, int color) {
FontRenderer fontRenderer = Minecraft.getInstance().fontRenderer;
fontRenderer.draw(String.valueOf(c), 0, 0, color, false, ms.peek()
fontRenderer.draw(c, 0, 0, color, false, ms.peek()
.getModel(), buffer, false, 0, 15728880);
}

View file

@ -1,41 +1,175 @@
package com.simibubi.create.content.logistics.block.redstone;
import com.simibubi.create.foundation.tileEntity.SyncedTileEntity;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.command.CommandSource;
import net.minecraft.command.ICommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextComponentUtils;
import net.minecraft.world.server.ServerWorld;
public class NixieTubeTileEntity extends SyncedTileEntity {
public class NixieTubeTileEntity extends SmartTileEntity {
char tube1;
char tube2;
Optional<Pair<ITextComponent, Integer>> customText;
JsonElement rawCustomText;
Couple<String> renderText;
int redstoneStrength;
public NixieTubeTileEntity(TileEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
tube1 = '0';
tube2 = '0';
redstoneStrength = 0;
customText = Optional.empty();
}
@Override
public CompoundNBT write(CompoundNBT nbt) {
super.write(nbt);
nbt.putInt("tube1", tube1);
nbt.putInt("tube2", tube2);
return nbt;
public void tick() {
super.tick();
// Dynamic text components have to be ticked manually and re-sent to the client
if (customText.isPresent() && world instanceof ServerWorld) {
Pair<ITextComponent, Integer> textSection = customText.get();
textSection.setFirst(updateDynamicTextComponents(ITextComponent.Serializer.fromJson(rawCustomText)));
Couple<String> currentText = getVisibleText();
if (renderText != null && renderText.equals(currentText))
return;
renderText = currentText;
sendData();
}
}
public void display(char tube1, char tube2) {
this.tube1 = tube1;
this.tube2 = tube2;
markDirty();
sendData();
//
public void clearCustomText() {
if (!customText.isPresent())
return;
displayRedstoneStrength(0);
}
public void displayCustomNameOf(ItemStack stack, int nixiePositionInRow) {
CompoundNBT compoundnbt = stack.getChildTag("display");
if (compoundnbt != null && compoundnbt.contains("Name", 8)) {
JsonElement fromJson = getJsonFromString(compoundnbt.getString("Name"));
ITextComponent displayed = ITextComponent.Serializer.fromJson(fromJson);
if (this.world instanceof ServerWorld)
displayed = updateDynamicTextComponents(displayed);
this.customText = Optional.of(Pair.of(displayed, nixiePositionInRow));
this.rawCustomText = fromJson;
notifyUpdate();
}
}
public void displayRedstoneStrength(int signalStrength) {
customText = Optional.empty();
redstoneStrength = signalStrength;
notifyUpdate();
}
public boolean reactsToRedstone() {
return !customText.isPresent();
}
public Couple<String> getVisibleText() {
if (!customText.isPresent())
return Couple.create(redstoneStrength < 10 ? "0" : "1", redstoneStrength % 10 + "");
String fullText = createStringFromComponentText(customText.get()
.getFirst());
int index = customText.get()
.getSecond() * 2;
return Couple.create(charOrEmpty(fullText, index), charOrEmpty(fullText, index + 1));
}
//
@Override
protected void read(CompoundNBT nbt, boolean clientPacket) {
customText = Optional.empty();
redstoneStrength = nbt.getInt("RedstoneStrength");
if (nbt.contains("CustomText")) {
ITextComponent displayed = ITextComponent.Serializer.fromJson(nbt.getString("CustomText"));
rawCustomText = getJsonFromString(nbt.getString("RawCustomText"));
customText = Optional.of(Pair.of(displayed, nbt.getInt("CustomTextIndex")));
}
super.read(nbt, clientPacket);
}
@Override
public void read(CompoundNBT nbt) {
tube1 = (char) nbt.getInt("tube1");
tube2 = (char) nbt.getInt("tube2");
super.read(nbt);
protected void write(CompoundNBT nbt, boolean clientPacket) {
super.write(nbt, clientPacket);
nbt.putInt("RedstoneStrength", redstoneStrength);
if (customText.isPresent()) {
nbt.putString("RawCustomText", rawCustomText.toString());
nbt.putString("CustomText", ITextComponent.Serializer.toJson(customText.get()
.getFirst()));
nbt.putInt("CustomTextIndex", customText.get()
.getSecond());
}
}
private JsonElement getJsonFromString(String string) {
return new JsonParser().parse(string);
}
protected ITextComponent updateDynamicTextComponents(ITextComponent customText) {
try {
return TextComponentUtils.updateForEntity(this.getCommandSource((ServerPlayerEntity) null), customText,
(Entity) null, 0);
} catch (CommandSyntaxException e) {
}
return customText;
}
// From SignTileEntity
protected CommandSource getCommandSource(@Nullable ServerPlayerEntity p_195539_1_) {
String s = p_195539_1_ == null ? "Sign"
: p_195539_1_.getName()
.getString();
ITextComponent itextcomponent =
(ITextComponent) (p_195539_1_ == null ? new StringTextComponent("Sign") : p_195539_1_.getDisplayName());
return new CommandSource(ICommandSource.field_213139_a_,
new Vec3d((double) this.pos.getX() + 0.5D, (double) this.pos.getY() + 0.5D,
(double) this.pos.getZ() + 0.5D),
Vec2f.ZERO, (ServerWorld) this.world, 2, s, itextcomponent, this.world.getServer(), p_195539_1_);
}
protected String createStringFromComponentText(ITextComponent iTextComponent) {
StringBuilder stringbuilder = new StringBuilder();
Iterator<ITextComponent> iterator = iTextComponent.stream()
.iterator();
while (iterator.hasNext())
stringbuilder.append(iterator.next()
.getUnformattedComponentText());
return stringbuilder.toString();
}
private String charOrEmpty(String string, int index) {
return string.length() <= index ? " " : string.substring(index, index + 1);
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {}
}