Signal the Signal

- Changed redstone output type of signals to comparator
- Redstone powered signals are now forced to red
- Redstone powered signals now add a substantial path cost
- Increased render distance of signal nixie details
This commit is contained in:
simibubi 2022-06-07 19:20:09 +02:00
parent c1f0ab8d1a
commit 8c444f1476
8 changed files with 141 additions and 63 deletions

View file

@ -495,7 +495,7 @@ e815bfd854c2653f10828bb11950f7fb991d7efc assets/create/blockstates/stressometer.
a2454400b1cf9889f70aebdc89c52a1be25f543c assets/create/blockstates/tiled_glass_pane.json
96c45abe7a5d9273feaf5f747d14cee8e04b58da assets/create/blockstates/track.json
98b936d72da49ee02eacd0b3244fe133e5575bb1 assets/create/blockstates/track_observer.json
408ae1009ee8bb2f2b83753d5909c53744f7865f assets/create/blockstates/track_signal.json
2bc61c806a9a903c139a79d4d296c119d9f590b2 assets/create/blockstates/track_signal.json
60609cfbcc9be6f7e41fb493ef3147beb9750b60 assets/create/blockstates/track_station.json
b000a6cde143f8a12fc8996d1ac8b5164f75253b assets/create/blockstates/train_door.json
836c443ab8778f0ff2b16bdf5f3339a0871c273e assets/create/blockstates/train_trapdoor.json

View file

@ -1,9 +1,15 @@
{
"variants": {
"type=entry_signal": {
"powered=false,type=entry_signal": {
"model": "create:block/track_signal/block_entry_signal"
},
"type=cross_signal": {
"powered=true,type=entry_signal": {
"model": "create:block/track_signal/block_entry_signal"
},
"powered=false,type=cross_signal": {
"model": "create:block/track_signal/block_cross_signal"
},
"powered=true,type=cross_signal": {
"model": "create:block/track_signal/block_cross_signal"
}
}

View file

@ -160,7 +160,7 @@ public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntit
ms.pushPose();
ms.translate(flip ? 4 / 16f : -4 / 16f, 0, 0);
if (diff.lengthSqr() < 36 * 36) {
if (diff.lengthSqr() < 96 * 96) {
boolean vert = first ^ facing.getAxis()
.isHorizontal();
float longSide = yellow ? 1 : 4;

View file

@ -56,7 +56,7 @@ public class Navigation {
private TravellingPoint signalScout;
public Pair<UUID, Boolean> waitingForSignal;
private Map<UUID, SignalBoundary> waitingForChainedGroups;
private Map<UUID, Pair<SignalBoundary, Boolean>> waitingForChainedGroups;
public double distanceToSignal;
public int ticksWaitingForSignal;
@ -158,13 +158,14 @@ public class Navigation {
boolean primary = entering.equals(signal.groups.getFirst());
boolean crossSignal = signal.types.get(primary) == SignalType.CROSS_SIGNAL;
boolean occupied = signalEdgeGroup.isOccupiedUnless(train);
boolean occupied =
signal.isForcedRed(nodes.getSecond()) || signalEdgeGroup.isOccupiedUnless(train);
if (!crossSignalTracked) {
if (crossSignal) { // Now entering cross signal path
trackingCrossSignal.setValue(Pair.of(boundary.id, primary));
crossSignalDistanceTracker.setValue(distance);
waitingForChainedGroups.put(entering, signal);
waitingForChainedGroups.put(entering, Pair.of(signal, primary));
}
if (occupied) { // Section is occupied
waitingForSignal = Pair.of(boundary.id, primary);
@ -179,7 +180,7 @@ public class Navigation {
}
if (crossSignalTracked) {
waitingForChainedGroups.put(entering, signal); // Add group to chain
waitingForChainedGroups.put(entering, Pair.of(signal, primary)); // Add group to chain
if (occupied) { // Section is occupied, but wait at the cross signal that started the chain
waitingForSignal = trackingCrossSignal.getValue();
distanceToSignal = crossSignalDistanceTracker.doubleValue();
@ -272,7 +273,7 @@ public class Navigation {
waitingForChainedGroups.forEach((groupId, boundary) -> {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
if (signalEdgeGroup != null)
signalEdgeGroup.reserved = boundary;
signalEdgeGroup.reserved = boundary.getFirst();
});
waitingForChainedGroups.clear();
}
@ -286,12 +287,18 @@ public class Navigation {
// Cross Signal
if (signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL) {
for (UUID groupId : waitingForChainedGroups.keySet()) {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
for (Entry<UUID, Pair<SignalBoundary, Boolean>> entry : waitingForChainedGroups.entrySet()) {
Pair<SignalBoundary, Boolean> boundary = entry.getValue();
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(entry.getKey());
if (signalEdgeGroup == null) { // Migration, re-initialize chain
waitingForSignal.setFirst(null);
return true;
}
if (boundary.getFirst()
.isForcedRed(boundary.getSecond())) {
train.reservedSignalBlocks.clear();
return false;
}
if (signalEdgeGroup.isOccupiedUnless(train))
return false;
}
@ -607,6 +614,10 @@ public class Navigation {
if (!point.canNavigateVia(node2))
continue Search;
if (point instanceof SignalBoundary signal) {
if (signal.isForcedRed(node2)) {
penalty += Train.Penalties.REDSTONE_RED_SIGNAL;
continue;
}
UUID group = signal.getGroup(node2);
if (group == null)
continue;

View file

@ -989,7 +989,7 @@ public class Train {
public static class Penalties {
static final int STATION = 200, STATION_WITH_TRAIN = 300;
static final int MANUAL_TRAIN = 200, IDLE_TRAIN = 700, ARRIVING_TRAIN = 50, WAITING_TRAIN = 50, ANY_TRAIN = 25,
RED_SIGNAL = 25;
RED_SIGNAL = 25, REDSTONE_RED_SIGNAL = 400;
}
public int getNavigationPenalty() {

View file

@ -2,6 +2,8 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint.signal
import java.util.Random;
import javax.annotation.Nullable;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ITE;
@ -13,18 +15,22 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
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.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
public class SignalBlock extends Block implements ITE<SignalTileEntity>, IWrenchable {
public static final EnumProperty<SignalType> TYPE = EnumProperty.create("type", SignalType.class);
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
public enum SignalType implements StringRepresentable {
ENTRY_SIGNAL, CROSS_SIGNAL;
@ -37,7 +43,8 @@ public class SignalBlock extends Block implements ITE<SignalTileEntity>, IWrench
public SignalBlock(Properties p_53182_) {
super(p_53182_);
registerDefaultState(defaultBlockState().setValue(TYPE, SignalType.ENTRY_SIGNAL));
registerDefaultState(defaultBlockState().setValue(TYPE, SignalType.ENTRY_SIGNAL)
.setValue(POWERED, false));
}
@Override
@ -47,7 +54,41 @@ public class SignalBlock extends Block implements ITE<SignalTileEntity>, IWrench
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> pBuilder) {
super.createBlockStateDefinition(pBuilder.add(TYPE));
super.createBlockStateDefinition(pBuilder.add(TYPE, POWERED));
}
@Override
public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side) {
return false;
}
@Nullable
@Override
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
return this.defaultBlockState()
.setValue(POWERED, Boolean.valueOf(pContext.getLevel()
.hasNeighborSignal(pContext.getClickedPos())));
}
@Override
public void neighborChanged(BlockState pState, Level pLevel, BlockPos pPos, Block pBlock, BlockPos pFromPos,
boolean pIsMoving) {
if (pLevel.isClientSide)
return;
boolean powered = pState.getValue(POWERED);
if (powered == pLevel.hasNeighborSignal(pPos))
return;
if (powered) {
pLevel.scheduleTick(pPos, this, 4);
} else {
pLevel.setBlock(pPos, pState.cycle(POWERED), 2);
}
}
@Override
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, Random pRand) {
if (pState.getValue(POWERED) && !pLevel.hasNeighborSignal(pPos))
pLevel.setBlock(pPos, pState.cycle(POWERED), 2);
}
@Override
@ -76,23 +117,13 @@ public class SignalBlock extends Block implements ITE<SignalTileEntity>, IWrench
}
@Override
public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, Direction side) {
return side != null;
}
@Override
public boolean isSignalSource(BlockState state) {
public boolean hasAnalogOutputSignal(BlockState pState) {
return true;
}
@Override
public void tick(BlockState blockState, ServerLevel world, BlockPos pos, Random random) {
getTileEntityOptional(world, pos).ifPresent(SignalTileEntity::updatePowerAfterDelay);
}
@Override
public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
return getTileEntityOptional(blockAccess, pos).filter(SignalTileEntity::isPowered)
public int getAnalogOutputSignal(BlockState pState, Level blockAccess, BlockPos pPos) {
return getTileEntityOptional(blockAccess, pPos).filter(SignalTileEntity::isPowered)
.map($ -> 15)
.orElse(0);
}

View file

@ -1,9 +1,8 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import com.google.common.base.Objects;
@ -29,7 +28,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class SignalBoundary extends TrackEdgePoint {
public Couple<Set<BlockPos>> blockEntities;
public Couple<Map<BlockPos, Boolean>> blockEntities;
public Couple<SignalType> types;
public Couple<UUID> groups;
public Couple<Boolean> sidesToUpdate;
@ -38,7 +37,7 @@ public class SignalBoundary extends TrackEdgePoint {
private Couple<Map<UUID, Boolean>> chainedSignals;
public SignalBoundary() {
blockEntities = Couple.create(HashSet::new);
blockEntities = Couple.create(HashMap::new);
chainedSignals = Couple.create(null, null);
groups = Couple.create(null, null);
sidesToUpdate = Couple.create(true, true);
@ -83,7 +82,8 @@ public class SignalBoundary extends TrackEdgePoint {
@Override
public void invalidate(LevelAccessor level) {
blockEntities.forEach(s -> s.forEach(pos -> invalidateAt(level, pos)));
blockEntities.forEach(s -> s.keySet()
.forEach(p -> invalidateAt(level, p)));
groups.forEach(uuid -> {
if (Create.RAILWAYS.signalEdgeGroups.remove(uuid) != null)
Create.RAILWAYS.sync.edgeGroupRemoved(uuid);
@ -97,18 +97,24 @@ public class SignalBoundary extends TrackEdgePoint {
@Override
public void tileAdded(BlockEntity tile, boolean front) {
Set<BlockPos> tilesOnSide = blockEntities.get(front);
Map<BlockPos, Boolean> tilesOnSide = blockEntities.get(front);
if (tilesOnSide.isEmpty())
tile.getBlockState()
.getOptionalValue(SignalBlock.TYPE)
.ifPresent(type -> types.set(front, type));
tilesOnSide.add(tile.getBlockPos());
tilesOnSide.put(tile.getBlockPos(), tile instanceof SignalTileEntity ste && ste.getReportedPower());
}
public void updateTilePower(SignalTileEntity tile) {
for (boolean front : Iterate.trueAndFalse)
blockEntities.get(front)
.computeIfPresent(tile.getBlockPos(), (p, c) -> tile.getReportedPower());
}
@Override
public void tileRemoved(BlockPos tilePos, boolean front) {
blockEntities.forEach(s -> s.remove(tilePos));
if (blockEntities.both(Set::isEmpty))
if (blockEntities.both(Map::isEmpty))
removeFromAllGraphs();
}
@ -134,8 +140,8 @@ public class SignalBoundary extends TrackEdgePoint {
public OverlayState getOverlayFor(BlockPos tile) {
for (boolean first : Iterate.trueAndFalse) {
Set<BlockPos> set = blockEntities.get(first);
for (BlockPos blockPos : set) {
Map<BlockPos, Boolean> set = blockEntities.get(first);
for (BlockPos blockPos : set.keySet()) {
if (blockPos.equals(tile))
return blockEntities.get(!first)
.isEmpty() ? OverlayState.RENDER : OverlayState.DUAL;
@ -147,13 +153,13 @@ public class SignalBoundary extends TrackEdgePoint {
public SignalType getTypeFor(BlockPos tile) {
return types.get(blockEntities.getFirst()
.contains(tile));
.containsKey(tile));
}
public SignalState getStateFor(BlockPos tile) {
for (boolean first : Iterate.trueAndFalse) {
Set<BlockPos> set = blockEntities.get(first);
if (set.contains(tile))
Map<BlockPos, Boolean> set = blockEntities.get(first);
if (set.containsKey(tile))
return cachedStates.get(first);
}
return SignalState.INVALID;
@ -177,10 +183,14 @@ public class SignalBoundary extends TrackEdgePoint {
private void tickState(TrackGraph graph) {
for (boolean current : Iterate.trueAndFalse) {
Set<BlockPos> set = blockEntities.get(current);
Map<BlockPos, Boolean> set = blockEntities.get(current);
if (set.isEmpty())
continue;
boolean forcedRed = set.values()
.stream()
.anyMatch(Boolean::booleanValue);
UUID group = groups.get(current);
if (Objects.equal(group, groups.get(!current))) {
cachedStates.set(current, SignalState.INVALID);
@ -194,11 +204,22 @@ public class SignalBoundary extends TrackEdgePoint {
continue;
}
boolean occupiedUnlessBySelf = signalEdgeGroup.isOccupiedUnless(this);
boolean occupiedUnlessBySelf = forcedRed || signalEdgeGroup.isOccupiedUnless(this);
cachedStates.set(current, occupiedUnlessBySelf ? SignalState.RED : resolveSignalChain(graph, current));
}
}
public boolean isForcedRed(TrackNode side) {
return isForcedRed(isPrimary(side));
}
public boolean isForcedRed(boolean primary) {
return blockEntities.get(primary)
.values()
.stream()
.anyMatch(Boolean::booleanValue);
}
private SignalState resolveSignalChain(TrackGraph graph, boolean side) {
if (types.get(side) != SignalType.CROSS_SIGNAL)
return SignalState.GREEN;
@ -244,14 +265,14 @@ public class SignalBoundary extends TrackEdgePoint {
if (migration)
return;
blockEntities = Couple.create(HashSet::new);
blockEntities = Couple.create(HashMap::new);
groups = Couple.create(null, null);
for (int i = 1; i <= 2; i++)
if (nbt.contains("Tiles" + i)) {
boolean first = i == 1;
NBTHelper.iterateCompoundList(nbt.getList("Tiles" + i, Tag.TAG_COMPOUND), c -> blockEntities.get(first)
.add(NbtUtils.readBlockPos(c)));
.put(NbtUtils.readBlockPos(c), c.getBoolean("Power")));
}
for (int i = 1; i <= 2; i++)
@ -280,7 +301,12 @@ public class SignalBoundary extends TrackEdgePoint {
for (int i = 1; i <= 2; i++)
if (!blockEntities.get(i == 1)
.isEmpty())
nbt.put("Tiles" + i, NBTHelper.writeCompoundList(blockEntities.get(i == 1), NbtUtils::writeBlockPos));
nbt.put("Tiles" + i, NBTHelper.writeCompoundList(blockEntities.get(i == 1)
.entrySet(), e -> {
CompoundTag c = NbtUtils.writeBlockPos(e.getKey());
c.putBoolean("Power", e.getValue());
return c;
}));
for (int i = 1; i <= 2; i++)
if (groups.get(i == 1) != null)
nbt.putUUID("Group" + i, groups.get(i == 1));
@ -306,7 +332,7 @@ public class SignalBoundary extends TrackEdgePoint {
public void cycleSignalType(BlockPos pos) {
types.set(blockEntities.getFirst()
.contains(pos), SignalType.values()[(getTypeFor(pos).ordinal() + 1) % SignalType.values().length]);
.containsKey(pos), SignalType.values()[(getTypeFor(pos).ordinal() + 1) % SignalType.values().length]);
}
}

View file

@ -15,11 +15,9 @@ import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.ticks.TickPriority;
public class SignalTileEntity extends SmartTileEntity implements ITransformableTE {
@ -48,11 +46,13 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
private SignalState state;
private OverlayState overlay;
private int switchToRedAfterTrainEntered;
private boolean lastReportedPower;
public SignalTileEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
this.state = SignalState.INVALID;
this.overlay = OverlayState.SKIP;
this.lastReportedPower = false;
}
@Override
@ -60,6 +60,7 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
super.write(tag, clientPacket);
NBTHelper.writeEnum(tag, "State", state);
NBTHelper.writeEnum(tag, "Overlay", overlay);
tag.putBoolean("Power", lastReportedPower);
}
@Override
@ -67,6 +68,7 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
super.read(tag, clientPacket);
state = NBTHelper.readEnum(tag, "State", SignalState.class);
overlay = NBTHelper.readEnum(tag, "Overlay", OverlayState.class);
lastReportedPower = tag.getBoolean("Power");
invalidateRenderBoundingBox();
}
@ -79,17 +81,6 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
return state == SignalState.RED;
}
protected void scheduleBlockTick() {
Block block = getBlockState().getBlock();
if (!level.getBlockTicks()
.willTickThisTick(worldPosition, block))
level.scheduleTick(worldPosition, block, 2, TickPriority.NORMAL);
}
public void updatePowerAfterDelay() {
level.blockUpdated(worldPosition, getBlockState().getBlock());
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
edgePoint = new TrackTargetingBehaviour<>(this, EdgePointType.SIGNAL);
@ -109,11 +100,21 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
return;
}
getBlockState().getOptionalValue(SignalBlock.TYPE)
BlockState blockState = getBlockState();
blockState.getOptionalValue(SignalBlock.POWERED).ifPresent(powered -> {
if (lastReportedPower == powered)
return;
lastReportedPower = powered;
boundary.updateTilePower(this);
notifyUpdate();
});
blockState.getOptionalValue(SignalBlock.TYPE)
.ifPresent(stateType -> {
SignalType targetType = boundary.getTypeFor(worldPosition);
if (stateType != targetType) {
level.setBlock(worldPosition, getBlockState().setValue(SignalBlock.TYPE, targetType), 3);
level.setBlock(worldPosition, blockState.setValue(SignalBlock.TYPE, targetType), 3);
refreshBlockState();
}
});
@ -122,6 +123,10 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
setOverlay(boundary.getOverlayFor(worldPosition));
}
public boolean getReportedPower() {
return lastReportedPower;
}
public SignalState getState() {
return state;
}
@ -147,7 +152,6 @@ public class SignalTileEntity extends SmartTileEntity implements ITransformableT
this.state = state;
switchToRedAfterTrainEntered = state == SignalState.GREEN || state == SignalState.YELLOW ? 15 : 0;
notifyUpdate();
scheduleBlockTick();
}
@Override