mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
Add ComputerCraft integration to Train Signals
- Makes computer-controlled signals unable to be changed by external factors - Emit CC event train_signal_state_change with the new state as parameter whenever the signal changes - Add CC getState() function to get the current signal state - Add CC isForcedRed()/setForcedRed(forced) functions to force the signal to be red, replacing redstone control - Add CC getSignalType()/cycleSignalType() function to change the signal signal type in the same manner as using the wrench - Add CC listBlockingTrainNames() function to list names of trains blocking the track group ahead of the signal
This commit is contained in:
parent
75081320ac
commit
7c385510d4
13 changed files with 208 additions and 6 deletions
|
@ -3052,6 +3052,7 @@
|
|||
"create.track_signal.cannot_change_mode": "Unable to switch mode of this Signal",
|
||||
"create.track_signal.mode_change.cross_signal": "-> Allow passage if section fully traversable",
|
||||
"create.track_signal.mode_change.entry_signal": "-> Allow passage if section unoccupied",
|
||||
"create.track_signal.mode_controlled_by_computer": "Signal mode is controlled by computer",
|
||||
"create.track_target.clear": "Cleared track selection",
|
||||
"create.track_target.invalid": "Cannot target this track here",
|
||||
"create.track_target.missing": "Right-click the targeted train track first",
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package com.simibubi.create.compat.computercraft;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
import com.simibubi.create.compat.computercraft.events.ComputerEvent;
|
||||
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
|
||||
import com.simibubi.create.foundation.blockEntity.behaviour.BehaviourType;
|
||||
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
|
||||
|
@ -49,6 +53,8 @@ public class AbstractComputerBehaviour extends BlockEntityBehaviour {
|
|||
return hasAttachedComputer;
|
||||
}
|
||||
|
||||
public void prepareComputerEvent(@NotNull ComputerEvent event) {}
|
||||
|
||||
@Override
|
||||
public BehaviourType<?> getType() {
|
||||
return TYPE;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package com.simibubi.create.compat.computercraft.events;
|
||||
|
||||
public interface ComputerEvent {}
|
|
@ -0,0 +1,13 @@
|
|||
package com.simibubi.create.compat.computercraft.events;
|
||||
|
||||
import com.simibubi.create.content.trains.signal.SignalBlockEntity;
|
||||
|
||||
public class SignalStateChangeEvent implements ComputerEvent {
|
||||
|
||||
public SignalBlockEntity.SignalState state;
|
||||
|
||||
public SignalStateChangeEvent(SignalBlockEntity.SignalState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,26 @@
|
|||
package com.simibubi.create.compat.computercraft.implementation;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||
import com.simibubi.create.compat.computercraft.events.ComputerEvent;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.DisplayLinkPeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.NixieTubePeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.SequencedGearshiftPeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.SignalPeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedControllerPeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedGaugePeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.StationPeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.StressGaugePeripheral;
|
||||
import com.simibubi.create.compat.computercraft.implementation.peripherals.SyncedPeripheral;
|
||||
import com.simibubi.create.content.kinetics.gauge.SpeedGaugeBlockEntity;
|
||||
import com.simibubi.create.content.kinetics.gauge.StressGaugeBlockEntity;
|
||||
import com.simibubi.create.content.kinetics.speedController.SpeedControllerBlockEntity;
|
||||
import com.simibubi.create.content.kinetics.transmission.sequencer.SequencedGearshiftBlockEntity;
|
||||
import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlockEntity;
|
||||
import com.simibubi.create.content.redstone.nixieTube.NixieTubeBlockEntity;
|
||||
import com.simibubi.create.content.trains.signal.SignalBlockEntity;
|
||||
import com.simibubi.create.content.trains.station.StationBlockEntity;
|
||||
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
|
||||
|
||||
|
@ -30,15 +37,15 @@ public class ComputerBehaviour extends AbstractComputerBehaviour {
|
|||
protected static final Capability<IPeripheral> PERIPHERAL_CAPABILITY =
|
||||
CapabilityManager.get(new CapabilityToken<>() {
|
||||
});
|
||||
LazyOptional<IPeripheral> peripheral;
|
||||
NonNullSupplier<IPeripheral> peripheralSupplier;
|
||||
LazyOptional<SyncedPeripheral<?>> peripheral;
|
||||
NonNullSupplier<SyncedPeripheral<?>> peripheralSupplier;
|
||||
|
||||
public ComputerBehaviour(SmartBlockEntity te) {
|
||||
super(te);
|
||||
this.peripheralSupplier = getPeripheralFor(te);
|
||||
}
|
||||
|
||||
public static NonNullSupplier<IPeripheral> getPeripheralFor(SmartBlockEntity be) {
|
||||
public static NonNullSupplier<SyncedPeripheral<?>> getPeripheralFor(SmartBlockEntity be) {
|
||||
if (be instanceof SpeedControllerBlockEntity scbe)
|
||||
return () -> new SpeedControllerPeripheral(scbe, scbe.targetSpeed);
|
||||
if (be instanceof DisplayLinkBlockEntity dlbe)
|
||||
|
@ -47,6 +54,8 @@ public class ComputerBehaviour extends AbstractComputerBehaviour {
|
|||
return () -> new NixieTubePeripheral(ntbe);
|
||||
if (be instanceof SequencedGearshiftBlockEntity sgbe)
|
||||
return () -> new SequencedGearshiftPeripheral(sgbe);
|
||||
if (be instanceof SignalBlockEntity sbe)
|
||||
return () -> new SignalPeripheral(sbe);
|
||||
if (be instanceof SpeedGaugeBlockEntity sgbe)
|
||||
return () -> new SpeedGaugePeripheral(sgbe);
|
||||
if (be instanceof StressGaugeBlockEntity sgbe)
|
||||
|
@ -76,4 +85,10 @@ public class ComputerBehaviour extends AbstractComputerBehaviour {
|
|||
peripheral.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareComputerEvent(@NotNull ComputerEvent event) {
|
||||
if (peripheral != null)
|
||||
peripheral.ifPresent(p -> p.prepareComputerEvent(event));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.compat.computercraft.events.ComputerEvent;
|
||||
import com.simibubi.create.compat.computercraft.events.SignalStateChangeEvent;
|
||||
import com.simibubi.create.compat.computercraft.implementation.CreateLuaTable;
|
||||
import com.simibubi.create.content.trains.entity.Train;
|
||||
import com.simibubi.create.content.trains.signal.SignalBlock;
|
||||
import com.simibubi.create.content.trains.signal.SignalBlockEntity;
|
||||
import com.simibubi.create.content.trains.signal.SignalBoundary;
|
||||
import com.simibubi.create.content.trains.signal.SignalEdgeGroup;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import net.createmod.catnip.data.Iterate;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public class SignalPeripheral extends SyncedPeripheral<SignalBlockEntity> {
|
||||
|
||||
public SignalPeripheral(SignalBlockEntity blockEntity) {
|
||||
super(blockEntity);
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final String getState() {
|
||||
return blockEntity.getState().toString();
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final boolean isForcedRed() {
|
||||
return blockEntity.getBlockState().getValue(SignalBlock.POWERED);
|
||||
}
|
||||
|
||||
@LuaFunction(mainThread = true)
|
||||
public final void setForcedRed(boolean powered) {
|
||||
Level level = blockEntity.getLevel();
|
||||
if (level != null)
|
||||
level.setBlock(blockEntity.getBlockPos(),
|
||||
blockEntity.getBlockState().setValue(SignalBlock.POWERED, powered), 2);
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final CreateLuaTable listBlockingTrainNames() throws LuaException {
|
||||
SignalBoundary signal = blockEntity.getSignal();
|
||||
if (signal == null)
|
||||
throw new LuaException("no signal");
|
||||
CreateLuaTable trainList = new CreateLuaTable();
|
||||
int trainCounter = 1;
|
||||
for (boolean current : Iterate.trueAndFalse) {
|
||||
Map<BlockPos, Boolean> set = signal.blockEntities.get(current);
|
||||
if (!set.containsKey(blockEntity.getBlockPos()))
|
||||
continue;
|
||||
UUID group = signal.groups.get(current);
|
||||
Map<UUID, SignalEdgeGroup> signalEdgeGroups = Create.RAILWAYS.signalEdgeGroups;
|
||||
SignalEdgeGroup signalEdgeGroup = signalEdgeGroups.get(group);
|
||||
for (Train train : signalEdgeGroup.trains) {
|
||||
trainList.put(trainCounter, train.name.getString());
|
||||
trainCounter += 1;
|
||||
}
|
||||
}
|
||||
return trainList;
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final String getSignalType() throws LuaException {
|
||||
SignalBoundary signal = blockEntity.getSignal();
|
||||
if (signal != null) {
|
||||
return signal.getTypeFor(blockEntity.getBlockPos()).toString();
|
||||
} else {
|
||||
throw new LuaException("no signal");
|
||||
}
|
||||
}
|
||||
|
||||
@LuaFunction(mainThread = true)
|
||||
public final void cycleSignalType() throws LuaException {
|
||||
SignalBoundary signal = blockEntity.getSignal();
|
||||
if (signal != null) {
|
||||
signal.cycleSignalType(blockEntity.getBlockPos());
|
||||
} else {
|
||||
throw new LuaException("no signal");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareComputerEvent(@NotNull ComputerEvent event) {
|
||||
if (event instanceof SignalStateChangeEvent ssce) {
|
||||
queueEvent("train_signal_state_change", ssce.state.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getType() {
|
||||
return "Create_Signal";
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.simibubi.create.AllPackets;
|
||||
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
|
||||
import com.simibubi.create.compat.computercraft.events.ComputerEvent;
|
||||
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
|
||||
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
|
||||
|
||||
|
@ -61,4 +62,21 @@ public abstract class SyncedPeripheral<T extends SmartBlockEntity> implements IP
|
|||
return this == other;
|
||||
}
|
||||
|
||||
public void prepareComputerEvent(@NotNull ComputerEvent event) {}
|
||||
|
||||
/**
|
||||
* Queue an event to all attached computers. Adds the peripheral attachment name as 1st event argument, followed by
|
||||
* any optional arguments passed to this method.
|
||||
*/
|
||||
protected void queueEvent(@NotNull String event, @Nullable Object... arguments) {
|
||||
Object[] sourceAndArgs = new Object[arguments.length + 1];
|
||||
System.arraycopy(arguments, 0, sourceAndArgs, 1, arguments.length);
|
||||
synchronized (computers) {
|
||||
for (IComputerAccess computer : computers) {
|
||||
sourceAndArgs[0] = computer.getAttachmentName();
|
||||
computer.queueEvent(event, sourceAndArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.simibubi.create.content.trains.signal;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.AllBlockEntityTypes;
|
||||
|
@ -76,7 +79,12 @@ public class SignalBlock extends Block implements IBE<SignalBlockEntity>, IWrenc
|
|||
if (pLevel.isClientSide)
|
||||
return;
|
||||
boolean powered = pState.getValue(POWERED);
|
||||
if (powered == pLevel.hasNeighborSignal(pPos))
|
||||
Optional<SignalBlockEntity> ste = getBlockEntityOptional(pLevel, pPos);
|
||||
boolean neighborPowered = false;
|
||||
if (ste.isEmpty() || !ste.get().computerBehaviour.hasAttachedComputer()) {
|
||||
powered = pLevel.hasNeighborSignal(pPos);
|
||||
}
|
||||
if (powered == neighborPowered)
|
||||
return;
|
||||
if (powered) {
|
||||
pLevel.scheduleTick(pPos, this, 4);
|
||||
|
@ -87,7 +95,8 @@ public class SignalBlock extends Block implements IBE<SignalBlockEntity>, IWrenc
|
|||
|
||||
@Override
|
||||
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, RandomSource pRand) {
|
||||
if (pState.getValue(POWERED) && !pLevel.hasNeighborSignal(pPos))
|
||||
Optional<SignalBlockEntity> ste = getBlockEntityOptional(pLevel, pPos);
|
||||
if ((ste.isEmpty() || !ste.get().computerBehaviour.hasAttachedComputer()) && pState.getValue(POWERED) && !pLevel.hasNeighborSignal(pPos))
|
||||
pLevel.setBlock(pPos, pState.cycle(POWERED), 2);
|
||||
}
|
||||
|
||||
|
@ -108,8 +117,13 @@ public class SignalBlock extends Block implements IBE<SignalBlockEntity>, IWrenc
|
|||
if (level.isClientSide)
|
||||
return InteractionResult.SUCCESS;
|
||||
withBlockEntityDo(level, pos, ste -> {
|
||||
SignalBoundary signal = ste.getSignal();
|
||||
Player player = context.getPlayer();
|
||||
if (ste.computerBehaviour.hasAttachedComputer()) {
|
||||
if (player != null)
|
||||
player.displayClientMessage(CreateLang.translateDirect("track_signal.mode_controlled_by_computer"), true);
|
||||
return;
|
||||
}
|
||||
SignalBoundary signal = ste.getSignal();
|
||||
if (signal != null) {
|
||||
signal.cycleSignalType(pos);
|
||||
if (player != null)
|
||||
|
|
|
@ -3,8 +3,12 @@ package com.simibubi.create.content.trains.signal;
|
|||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.simibubi.create.api.contraption.transformable.TransformableBlockEntity;
|
||||
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||
import com.simibubi.create.compat.computercraft.events.SignalStateChangeEvent;
|
||||
import com.simibubi.create.content.contraptions.StructureTransform;
|
||||
import com.simibubi.create.content.trains.graph.EdgePointType;
|
||||
import com.simibubi.create.content.trains.signal.SignalBlock.SignalType;
|
||||
|
@ -14,11 +18,14 @@ import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour
|
|||
|
||||
import net.createmod.catnip.nbt.NBTHelper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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.phys.AABB;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
public class SignalBlockEntity extends SmartBlockEntity implements TransformableBlockEntity {
|
||||
|
||||
|
@ -48,6 +55,7 @@ public class SignalBlockEntity extends SmartBlockEntity implements Transformable
|
|||
private OverlayState overlay;
|
||||
private int switchToRedAfterTrainEntered;
|
||||
private boolean lastReportedPower;
|
||||
public AbstractComputerBehaviour computerBehaviour;
|
||||
|
||||
public SignalBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||
super(type, pos, state);
|
||||
|
@ -86,6 +94,7 @@ public class SignalBlockEntity extends SmartBlockEntity implements Transformable
|
|||
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
||||
edgePoint = new TrackTargetingBehaviour<>(this, EdgePointType.SIGNAL);
|
||||
behaviours.add(edgePoint);
|
||||
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,9 +161,24 @@ public class SignalBlockEntity extends SmartBlockEntity implements Transformable
|
|||
return;
|
||||
this.state = state;
|
||||
switchToRedAfterTrainEntered = state == SignalState.GREEN || state == SignalState.YELLOW ? 15 : 0;
|
||||
if (computerBehaviour.hasAttachedComputer())
|
||||
computerBehaviour.prepareComputerEvent(new SignalStateChangeEvent(state));
|
||||
notifyUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @NotNull LazyOptional<T> getCapability(@NotNull Capability<T> cap, Direction side) {
|
||||
if (computerBehaviour.isPeripheralCap(cap))
|
||||
return computerBehaviour.getPeripheralCapability();
|
||||
return super.getCapability(cap, side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCaps() {
|
||||
super.invalidateCaps();
|
||||
computerBehaviour.removePeripheral();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AABB createRenderBoundingBox() {
|
||||
return new AABB(worldPosition, edgePoint.getGlobalPosition()).inflate(2);
|
||||
|
|
|
@ -997,6 +997,7 @@
|
|||
"create.track_signal.cannot_change_mode": "Unable to switch mode of this Signal",
|
||||
"create.track_signal.mode_change.entry_signal": "-> Allow passage if section unoccupied",
|
||||
"create.track_signal.mode_change.cross_signal": "-> Allow passage if section fully traversable",
|
||||
"create.track_signal.mode_controlled_by_computer": "Signal mode is controlled by computer",
|
||||
|
||||
"create.contraption.controls.start_controlling": "Now controlling: %1$s",
|
||||
"create.contraption.controls.stop_controlling": "Stopped controlling contraption",
|
||||
|
|
|
@ -2597,6 +2597,7 @@
|
|||
"create.track_signal.cannot_change_mode": "No se puede cambiar el modo de esta señal",
|
||||
"create.track_signal.mode_change.cross_signal": "-> Permitir entrada si la sección se puede atravesar por completo",
|
||||
"create.track_signal.mode_change.entry_signal": "-> Permitir entrada si la sección está desocupada",
|
||||
"create.track_signal.mode_controlled_by_computer": "El modo de esta señal está siendo controlado por un ordenador",
|
||||
"create.track_target.clear": "Selección de vía borrada",
|
||||
"create.track_target.invalid": "No se puede marcar esta vía como objetivo",
|
||||
"create.track_target.missing": "Primero haz clic derecho en la vía objetivo",
|
||||
|
|
|
@ -2594,6 +2594,7 @@
|
|||
"create.track_signal.cannot_change_mode": "No se puede cambiar el modo de esta señal",
|
||||
"create.track_signal.mode_change.cross_signal": "-> Permitir entrada si la sección se puede atravesar por completo",
|
||||
"create.track_signal.mode_change.entry_signal": "-> Permitir entrada si la sección está desocupada",
|
||||
"create.track_signal.mode_controlled_by_computer": "El modo de esta señal está siendo controlado por un ordenador",
|
||||
"create.track_target.clear": "Selección de vía borrada",
|
||||
"create.track_target.invalid": "No se puede marcar esta vía como objetivo",
|
||||
"create.track_target.missing": "Primero haz clic derecho en la vía objetivo",
|
||||
|
|
|
@ -2597,6 +2597,7 @@
|
|||
"create.track_signal.cannot_change_mode": "Incapable de changer le mode de ce signal",
|
||||
"create.track_signal.mode_change.cross_signal": "-> Autoriser le passage si la section est entièrement traversable",
|
||||
"create.track_signal.mode_change.entry_signal": "-> Autoriser le passage si la section est inoccupée",
|
||||
"create.track_signal.mode_controlled_by_computer": "Le mode de ce signal est contrôlé par un ordinateur",
|
||||
"create.track_target.clear": "Sélection du rail effacée",
|
||||
"create.track_target.invalid": "Ne peut pas cibler ce rail ici",
|
||||
"create.track_target.missing": "Faites clic droit sur le rail ciblé d'abord",
|
||||
|
|
Loading…
Add table
Reference in a new issue