diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index b0316a9a86..c0052b55b7 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -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", diff --git a/src/main/java/com/simibubi/create/compat/computercraft/AbstractComputerBehaviour.java b/src/main/java/com/simibubi/create/compat/computercraft/AbstractComputerBehaviour.java index 956aad0b6c..1de5544733 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/AbstractComputerBehaviour.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/AbstractComputerBehaviour.java @@ -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; diff --git a/src/main/java/com/simibubi/create/compat/computercraft/events/ComputerEvent.java b/src/main/java/com/simibubi/create/compat/computercraft/events/ComputerEvent.java new file mode 100644 index 0000000000..0f7a50d8ca --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/events/ComputerEvent.java @@ -0,0 +1,3 @@ +package com.simibubi.create.compat.computercraft.events; + +public interface ComputerEvent {} diff --git a/src/main/java/com/simibubi/create/compat/computercraft/events/SignalStateChangeEvent.java b/src/main/java/com/simibubi/create/compat/computercraft/events/SignalStateChangeEvent.java new file mode 100644 index 0000000000..56033f6961 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/events/SignalStateChangeEvent.java @@ -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; + } + +} diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java index b20916816d..401926aa39 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java @@ -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 PERIPHERAL_CAPABILITY = CapabilityManager.get(new CapabilityToken<>() { }); - LazyOptional peripheral; - NonNullSupplier peripheralSupplier; + LazyOptional> peripheral; + NonNullSupplier> peripheralSupplier; public ComputerBehaviour(SmartBlockEntity te) { super(te); this.peripheralSupplier = getPeripheralFor(te); } - public static NonNullSupplier getPeripheralFor(SmartBlockEntity be) { + public static NonNullSupplier> 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)); + } + } diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SignalPeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SignalPeripheral.java new file mode 100644 index 0000000000..138cbbfa7d --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SignalPeripheral.java @@ -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 { + + 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 set = signal.blockEntities.get(current); + if (!set.containsKey(blockEntity.getBlockPos())) + continue; + UUID group = signal.groups.get(current); + Map 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"; + } + +} diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java index b855c6515a..20ce964e14 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java @@ -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 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); + } + } + } + } diff --git a/src/main/java/com/simibubi/create/content/trains/signal/SignalBlock.java b/src/main/java/com/simibubi/create/content/trains/signal/SignalBlock.java index cf76148d16..fc18dc198d 100644 --- a/src/main/java/com/simibubi/create/content/trains/signal/SignalBlock.java +++ b/src/main/java/com/simibubi/create/content/trains/signal/SignalBlock.java @@ -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, IWrenc if (pLevel.isClientSide) return; boolean powered = pState.getValue(POWERED); - if (powered == pLevel.hasNeighborSignal(pPos)) + Optional 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, IWrenc @Override public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, RandomSource pRand) { - if (pState.getValue(POWERED) && !pLevel.hasNeighborSignal(pPos)) + Optional 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, 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) diff --git a/src/main/java/com/simibubi/create/content/trains/signal/SignalBlockEntity.java b/src/main/java/com/simibubi/create/content/trains/signal/SignalBlockEntity.java index 79b23434c3..b7e754b13e 100644 --- a/src/main/java/com/simibubi/create/content/trains/signal/SignalBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/signal/SignalBlockEntity.java @@ -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 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 @NotNull LazyOptional getCapability(@NotNull Capability 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); diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index 20a93cb239..b16d121fc4 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -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", diff --git a/src/main/resources/assets/create/lang/es_es.json b/src/main/resources/assets/create/lang/es_es.json index 4aa73bdc74..60798a5ef7 100644 --- a/src/main/resources/assets/create/lang/es_es.json +++ b/src/main/resources/assets/create/lang/es_es.json @@ -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", diff --git a/src/main/resources/assets/create/lang/es_mx.json b/src/main/resources/assets/create/lang/es_mx.json index 7c6fb124c7..f1d7462e9a 100644 --- a/src/main/resources/assets/create/lang/es_mx.json +++ b/src/main/resources/assets/create/lang/es_mx.json @@ -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", diff --git a/src/main/resources/assets/create/lang/fr_fr.json b/src/main/resources/assets/create/lang/fr_fr.json index 3c81102396..a27622ae73 100644 --- a/src/main/resources/assets/create/lang/fr_fr.json +++ b/src/main/resources/assets/create/lang/fr_fr.json @@ -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",