Sequenced Gearshift
- Added Sequenced Gearshifts - Fixed custom swords breaking blocks when held in creative mode - Fixed belts cancelling flight when touched - Fixed Entities being blocked by contraption boundaries when moved on belts - Fixed Flexpeater going funky when edited while active - Fixed Links not firing when placed into a powered location
|
@ -41,6 +41,7 @@ import com.simibubi.create.modules.contraptions.processing.BasinBlock;
|
|||
import com.simibubi.create.modules.contraptions.redstone.AnalogLeverBlock;
|
||||
import com.simibubi.create.modules.contraptions.redstone.ContactBlock;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.SpeedControllerBlock;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.sequencer.SequencedGearshiftBlock;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltTunnelBlock;
|
||||
import com.simibubi.create.modules.contraptions.relays.elementary.CogWheelBlock;
|
||||
|
@ -164,6 +165,7 @@ public enum AllBlocks {
|
|||
BRASS_CASING(new CasingBlock("crafter_top")),
|
||||
|
||||
MECHANICAL_CRAFTER(new MechanicalCrafterBlock()),
|
||||
SEQUENCED_GEARSHIFT(new SequencedGearshiftBlock()),
|
||||
FLYWHEEL(new FlywheelBlock()),
|
||||
FURNACE_ENGINE(new FurnaceEngineBlock()),
|
||||
ROTATION_SPEED_CONTROLLER(new SpeedControllerBlock()),
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.simibubi.create.foundation.packet.NbtPacket;
|
|||
import com.simibubi.create.foundation.packet.SimplePacketBase;
|
||||
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
|
||||
import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionStallPacket;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.sequencer.ConfigureSequencedGearshiftPacket;
|
||||
import com.simibubi.create.modules.curiosities.symmetry.SymmetryEffectPacket;
|
||||
import com.simibubi.create.modules.curiosities.zapper.ZapperBeamPacket;
|
||||
import com.simibubi.create.modules.logistics.item.filter.FilterScreenPacket;
|
||||
|
@ -34,6 +35,7 @@ public enum AllPackets {
|
|||
CONFIGURE_SCHEMATICANNON(ConfigureSchematicannonPacket.class, ConfigureSchematicannonPacket::new),
|
||||
CONFIGURE_FLEXCRATE(ConfigureFlexcratePacket.class, ConfigureFlexcratePacket::new),
|
||||
CONFIGURE_STOCKSWITCH(ConfigureStockswitchPacket.class, ConfigureStockswitchPacket::new),
|
||||
CONFIGURE_SEQUENCER(ConfigureSequencedGearshiftPacket.class, ConfigureSequencedGearshiftPacket::new),
|
||||
PLACE_SCHEMATIC(SchematicPlacePacket.class, SchematicPlacePacket::new),
|
||||
UPLOAD_SCHEMATIC(SchematicUploadPacket.class, SchematicUploadPacket::new),
|
||||
CONFIGURE_FILTER(FilterScreenPacket.class, FilterScreenPacket::new),
|
||||
|
|
|
@ -50,6 +50,7 @@ import com.simibubi.create.modules.contraptions.redstone.AnalogLeverTileEntity;
|
|||
import com.simibubi.create.modules.contraptions.redstone.AnalogLeverTileEntityRenderer;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.SpeedControllerRenderer;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.SpeedControllerTileEntity;
|
||||
import com.simibubi.create.modules.contraptions.relays.advanced.sequencer.SequencedGearshiftTileEntity;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntityRenderer;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltTunnelTileEntity;
|
||||
|
@ -121,10 +122,7 @@ public enum AllTileEntities {
|
|||
MECHANICAL_BEARING(MechanicalBearingTileEntity::new, AllBlocks.MECHANICAL_BEARING),
|
||||
CLOCKWORK_BEARING(ClockworkBearingTileEntity::new, AllBlocks.CLOCKWORK_BEARING),
|
||||
ROPE_PULLEY(PulleyTileEntity::new, AllBlocks.ROPE_PULLEY),
|
||||
CHASSIS(
|
||||
ChassisTileEntity::new,
|
||||
AllBlocks.ROTATION_CHASSIS,
|
||||
AllBlocks.TRANSLATION_CHASSIS,
|
||||
CHASSIS(ChassisTileEntity::new, AllBlocks.ROTATION_CHASSIS, AllBlocks.TRANSLATION_CHASSIS,
|
||||
AllBlocks.TRANSLATION_CHASSIS_SECONDARY),
|
||||
DRILL(DrillTileEntity::new, AllBlocks.DRILL),
|
||||
SAW(SawTileEntity::new, AllBlocks.SAW),
|
||||
|
@ -139,6 +137,7 @@ public enum AllTileEntities {
|
|||
DEPLOYER(DeployerTileEntity::new, AllBlocks.DEPLOYER),
|
||||
BASIN(BasinTileEntity::new, AllBlocks.BASIN),
|
||||
MECHANICAL_CRAFTER(MechanicalCrafterTileEntity::new, AllBlocks.MECHANICAL_CRAFTER),
|
||||
SEQUENCED_GEARSHIFT(SequencedGearshiftTileEntity::new, AllBlocks.SEQUENCED_GEARSHIFT),
|
||||
ROTATION_SPEED_CONTROLLER(SpeedControllerTileEntity::new, AllBlocks.ROTATION_SPEED_CONTROLLER),
|
||||
SPEED_GAUGE(SpeedGaugeTileEntity::new, AllBlocks.SPEED_GAUGE),
|
||||
STRESS_GAUGE(StressGaugeTileEntity::new, AllBlocks.STRESS_GAUGE),
|
||||
|
@ -151,9 +150,7 @@ public enum AllTileEntities {
|
|||
EXTRACTOR(ExtractorTileEntity::new, AllBlocks.EXTRACTOR, AllBlocks.VERTICAL_EXTRACTOR),
|
||||
LINKED_EXTRACTOR(LinkedExtractorTileEntity::new, AllBlocks.LINKED_EXTRACTOR, AllBlocks.VERTICAL_LINKED_EXTRACTOR),
|
||||
TRANSPOSER(TransposerTileEntity::new, AllBlocks.TRANSPOSER, AllBlocks.VERTICAL_TRANSPOSER),
|
||||
LINKED_TRANSPOSER(
|
||||
LinkedTransposerTileEntity::new,
|
||||
AllBlocks.LINKED_TRANSPOSER,
|
||||
LINKED_TRANSPOSER(LinkedTransposerTileEntity::new, AllBlocks.LINKED_TRANSPOSER,
|
||||
AllBlocks.VERTICAL_LINKED_TRANSPOSER),
|
||||
BELT_FUNNEL(FunnelTileEntity::new, AllBlocks.BELT_FUNNEL, AllBlocks.VERTICAL_FUNNEL),
|
||||
ENTITY_DETECTOR(BeltObserverTileEntity::new, AllBlocks.ENTITY_DETECTOR),
|
||||
|
@ -206,6 +203,7 @@ public enum AllTileEntities {
|
|||
bind(GearboxTileEntity.class, new GearboxTileEntityRenderer());
|
||||
bind(GearshiftTileEntity.class, new SplitShaftTileEntityRenderer());
|
||||
bind(ClutchTileEntity.class, new SplitShaftTileEntityRenderer());
|
||||
bind(SequencedGearshiftTileEntity.class, new SplitShaftTileEntityRenderer());
|
||||
bind(BeltTileEntity.class, new BeltTileEntityRenderer());
|
||||
bind(WaterWheelTileEntity.class, new KineticTileEntityRenderer());
|
||||
bind(HandCrankTileEntity.class, new HandCrankTileEntityRenderer());
|
||||
|
|
|
@ -41,6 +41,12 @@ public enum ScreenResources {
|
|||
|
||||
FILTER("filter.png", 200, 100),
|
||||
ATTRIBUTE_FILTER("filter.png", 0, 100, 200, 86),
|
||||
|
||||
SEQUENCER("sequencer.png", 156, 128),
|
||||
SEQUENCER_INSTRUCTION("sequencer.png", 14, 47, 131, 18),
|
||||
SEQUENCER_WAIT("sequencer.png", 14, 65, 131, 18),
|
||||
SEQUENCER_END("sequencer.png", 14, 83, 131, 18),
|
||||
SEQUENCER_EMPTY("sequencer.png", 14, 101, 131, 18),
|
||||
|
||||
// Logistical Index
|
||||
INDEX_TOP("index.png", 41, 0, 174, 22),
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package com.simibubi.create.foundation.gui.widgets;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.simibubi.create.AllKeys;
|
||||
import com.simibubi.create.foundation.behaviour.scrollvalue.ScrollValueBehaviour.StepContext;
|
||||
import com.simibubi.create.foundation.utility.Lang;
|
||||
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
|
@ -18,6 +20,7 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
|
||||
protected int min, max;
|
||||
protected int shiftStep;
|
||||
Function<StepContext, Integer> step;
|
||||
|
||||
public ScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
|
||||
super(xIn, yIn, widthIn, heightIn);
|
||||
|
@ -25,8 +28,13 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
min = 0;
|
||||
max = 1;
|
||||
shiftStep = 5;
|
||||
step = standardStep();
|
||||
}
|
||||
|
||||
|
||||
public Function<StepContext, Integer> standardStep() {
|
||||
return c -> c.shift ? shiftStep : 1;
|
||||
}
|
||||
|
||||
public ScrollInput withRange(int min, int max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
@ -44,6 +52,11 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ScrollInput withStepFunction(Function<StepContext, Integer> step) {
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScrollInput writingTo(Label label) {
|
||||
this.displayLabel = label;
|
||||
writeToLabel();
|
||||
|
@ -62,7 +75,7 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
writeToLabel();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ScrollInput withShiftStep(int step) {
|
||||
shiftStep = step;
|
||||
return this;
|
||||
|
@ -73,18 +86,25 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
if (!isHovered)
|
||||
return false;
|
||||
|
||||
StepContext context = new StepContext();
|
||||
context.control = AllKeys.ctrlDown();
|
||||
context.shift = AllKeys.shiftDown();
|
||||
context.currentValue = state;
|
||||
context.forward = delta > 0;
|
||||
|
||||
int priorState = state;
|
||||
boolean shifted = AllKeys.shiftDown();
|
||||
int step = (int) Math.signum(delta) * (shifted ? shiftStep : 1);
|
||||
int step = (int) Math.signum(delta) * this.step.apply(context);
|
||||
|
||||
state += step;
|
||||
if (shifted)
|
||||
state -= state % shiftStep;
|
||||
|
||||
clampState();
|
||||
|
||||
|
||||
if (priorState != state)
|
||||
onChanged();
|
||||
|
||||
|
||||
return priorState != state;
|
||||
}
|
||||
|
||||
|
@ -94,7 +114,7 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
if (state < min)
|
||||
state = min;
|
||||
}
|
||||
|
||||
|
||||
public void onChanged() {
|
||||
if (displayLabel != null)
|
||||
writeToLabel();
|
||||
|
@ -102,7 +122,7 @@ public class ScrollInput extends AbstractSimiWidget {
|
|||
onScroll.accept(state);
|
||||
updateTooltip();
|
||||
}
|
||||
|
||||
|
||||
protected void writeToLabel() {
|
||||
displayLabel.text = "" + state;
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ public abstract class AbstractToolItem extends ToolItem {
|
|||
|
||||
@Override
|
||||
public boolean canPlayerBreakBlockWhileHolding(BlockState state, World worldIn, BlockPos pos, PlayerEntity player) {
|
||||
return !(hasType(SWORD) && !player.isCreative());
|
||||
return !hasType(SWORD) || !player.isCreative();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,7 +126,8 @@ public abstract class AbstractToolItem extends ToolItem {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void modifyDrops(final Collection<ItemStack> drops, IWorld world, BlockPos pos, ItemStack tool, BlockState state) {
|
||||
public void modifyDrops(final Collection<ItemStack> drops, IWorld world, BlockPos pos, ItemStack tool,
|
||||
BlockState state) {
|
||||
}
|
||||
|
||||
public void spawnParticles(IWorld world, BlockPos pos, ItemStack tool, BlockState state) {
|
||||
|
|
|
@ -67,7 +67,10 @@ public class RotationPropagator {
|
|||
|
||||
// Axis <-> Axis
|
||||
if (connectedByAxis) {
|
||||
return getAxisModifier(from, direction) * getAxisModifier(to, direction.getOpposite());
|
||||
float axisModifier = getAxisModifier(to, direction.getOpposite());
|
||||
if (axisModifier != 0)
|
||||
axisModifier = 1 / axisModifier;
|
||||
return getAxisModifier(from, direction) * axisModifier;
|
||||
}
|
||||
|
||||
// Attached Encased Belts
|
||||
|
|
|
@ -39,7 +39,7 @@ public abstract class HorizontalAxisKineticBlock extends KineticBlock {
|
|||
return this.getDefaultState().with(HORIZONTAL_AXIS, context.getPlacementHorizontalFacing().rotateY().getAxis());
|
||||
}
|
||||
|
||||
public Axis getPreferredHorizontalAxis(BlockItemUseContext context) {
|
||||
public static Axis getPreferredHorizontalAxis(BlockItemUseContext context) {
|
||||
Direction prefferedSide = null;
|
||||
for (Direction side : Direction.values()) {
|
||||
if (side.getAxis().isVertical())
|
||||
|
|
|
@ -40,7 +40,7 @@ public abstract class RotatedPillarKineticBlock extends KineticBlock {
|
|||
}
|
||||
}
|
||||
|
||||
public Axis getPreferredAxis(BlockItemUseContext context) {
|
||||
public static Axis getPreferredAxis(BlockItemUseContext context) {
|
||||
Axis prefferedAxis = null;
|
||||
for (Direction side : Direction.values()) {
|
||||
BlockState blockState = context.getWorld().getBlockState(context.getPos().offset(side));
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import com.simibubi.create.foundation.packet.TileEntityConfigurationPacket;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
|
||||
public class ConfigureSequencedGearshiftPacket extends TileEntityConfigurationPacket<SequencedGearshiftTileEntity> {
|
||||
|
||||
private ListNBT instructions;
|
||||
|
||||
public ConfigureSequencedGearshiftPacket(BlockPos pos, Vector<Instruction> instructions) {
|
||||
super(pos);
|
||||
this.instructions = Instruction.serializeAll(instructions);
|
||||
}
|
||||
|
||||
public ConfigureSequencedGearshiftPacket(PacketBuffer buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readSettings(PacketBuffer buffer) {
|
||||
instructions = buffer.readCompoundTag().getList("data", NBT.TAG_COMPOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeSettings(PacketBuffer buffer) {
|
||||
CompoundNBT tag = new CompoundNBT();
|
||||
tag.put("data", instructions);
|
||||
buffer.writeCompoundTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applySettings(SequencedGearshiftTileEntity te) {
|
||||
te.run(-1);
|
||||
te.instructions = Instruction.deserializeAll(instructions);
|
||||
te.sendData();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
|
||||
public class Instruction {
|
||||
|
||||
SequencerInstructions instruction;
|
||||
InstructionSpeedModifiers speedModifier;
|
||||
int value;
|
||||
|
||||
public Instruction(SequencerInstructions instruction) {
|
||||
this(instruction, 1);
|
||||
}
|
||||
|
||||
public Instruction(SequencerInstructions instruction, int value) {
|
||||
this.instruction = instruction;
|
||||
speedModifier = InstructionSpeedModifiers.FORWARD;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
int getDuration(float initialProgress, float speed) {
|
||||
int offset = speed > 0 && speedModifier.value < 0 ? 1 : 2;
|
||||
speed *= speedModifier.value;
|
||||
speed = Math.abs(speed);
|
||||
|
||||
double degreesPerTick = (speed * 360) / 60 / 20;
|
||||
double metersPerTick = speed / 512;
|
||||
switch (instruction) {
|
||||
|
||||
case TURN_ANGLE:
|
||||
return (int) ((1 - initialProgress) * value / degreesPerTick + 1);
|
||||
|
||||
case TURN_DISTANCE:
|
||||
return (int) ((1 - initialProgress) * value / metersPerTick + offset);
|
||||
|
||||
case WAIT:
|
||||
return (int) ((1 - initialProgress) * value + 1);
|
||||
|
||||
case END:
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getSpeedModifier() {
|
||||
switch (instruction) {
|
||||
|
||||
case TURN_ANGLE:
|
||||
case TURN_DISTANCE:
|
||||
return speedModifier.value;
|
||||
|
||||
case END:
|
||||
case WAIT:
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static ListNBT serializeAll(Vector<Instruction> instructions) {
|
||||
ListNBT list = new ListNBT();
|
||||
instructions.forEach(i -> list.add(i.serialize()));
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Vector<Instruction> deserializeAll(ListNBT list) {
|
||||
if (list.isEmpty())
|
||||
return createDefault();
|
||||
Vector<Instruction> instructions = new Vector<>(5);
|
||||
list.forEach(inbt -> instructions.add(deserialize((CompoundNBT) inbt)));
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public static Vector<Instruction> createDefault() {
|
||||
Vector<Instruction> instructions = new Vector<>(5);
|
||||
instructions.add(new Instruction(SequencerInstructions.TURN_ANGLE, 90));
|
||||
instructions.add(new Instruction(SequencerInstructions.END));
|
||||
return instructions;
|
||||
}
|
||||
|
||||
CompoundNBT serialize() {
|
||||
CompoundNBT tag = new CompoundNBT();
|
||||
tag.putString("Type", NBTHelper.writeEnum(instruction));
|
||||
tag.putString("Modifier", NBTHelper.writeEnum(speedModifier));
|
||||
tag.putInt("Value", value);
|
||||
return tag;
|
||||
}
|
||||
|
||||
static Instruction deserialize(CompoundNBT tag) {
|
||||
Instruction instruction =
|
||||
new Instruction(NBTHelper.readEnum(tag.getString("Type"), SequencerInstructions.class));
|
||||
instruction.speedModifier = NBTHelper.readEnum(tag.getString("Modifier"), InstructionSpeedModifiers.class);
|
||||
instruction.value = tag.getInt("Value");
|
||||
return instruction;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.foundation.utility.Lang;
|
||||
|
||||
public enum InstructionSpeedModifiers {
|
||||
|
||||
FORWARD_FAST(2, ">>"), FORWARD(1, "->"), BACK(-1, "<-"), BACK_FAST(-2, "<<"),
|
||||
|
||||
;
|
||||
|
||||
String translationKey;
|
||||
int value;
|
||||
String label;
|
||||
|
||||
private InstructionSpeedModifiers(int modifier, String label) {
|
||||
this.label = label;
|
||||
translationKey = "gui.sequenced_gearshift.speed." + Lang.asId(name());
|
||||
value = modifier;
|
||||
}
|
||||
|
||||
static List<String> getOptions() {
|
||||
List<String> options = new ArrayList<>();
|
||||
for (InstructionSpeedModifiers entry : values())
|
||||
options.add(Lang.translate(entry.translationKey));
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import com.simibubi.create.AllItems;
|
||||
import com.simibubi.create.foundation.block.IWithTileEntity;
|
||||
import com.simibubi.create.foundation.gui.ScreenOpener;
|
||||
import com.simibubi.create.modules.contraptions.base.HorizontalAxisKineticBlock;
|
||||
import com.simibubi.create.modules.contraptions.base.KineticBlock;
|
||||
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.modules.contraptions.base.RotatedPillarKineticBlock;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUseContext;
|
||||
import net.minecraft.state.BooleanProperty;
|
||||
import net.minecraft.state.IntegerProperty;
|
||||
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.Direction.Axis;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.IWorldReader;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
|
||||
public class SequencedGearshiftBlock extends HorizontalAxisKineticBlock
|
||||
implements IWithTileEntity<SequencedGearshiftTileEntity> {
|
||||
|
||||
public static final BooleanProperty VERTICAL = BooleanProperty.create("vertical");
|
||||
public static final IntegerProperty STATE = IntegerProperty.create("state", 0, 5);
|
||||
|
||||
public SequencedGearshiftBlock() {
|
||||
super(Properties.from(Blocks.ANDESITE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillStateContainer(Builder<Block, BlockState> builder) {
|
||||
super.fillStateContainer(builder.add(STATE, VERTICAL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
|
||||
return new SequencedGearshiftTileEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
|
||||
boolean isMoving) {
|
||||
if (worldIn.isRemote)
|
||||
return;
|
||||
|
||||
boolean previouslyPowered = state.get(STATE) != 0;
|
||||
if (previouslyPowered != worldIn.isBlockPowered(pos))
|
||||
withTileEntityDo(worldIn, pos, SequencedGearshiftTileEntity::onRedstoneUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasShaftTowards(IWorldReader world, BlockPos pos, BlockState state, Direction face) {
|
||||
if (state.get(VERTICAL))
|
||||
return face.getAxis().isVertical();
|
||||
return super.hasShaftTowards(world, pos, state, face);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
|
||||
BlockRayTraceResult hit) {
|
||||
ItemStack held = player.getHeldItemMainhand();
|
||||
if (AllItems.WRENCH.typeOf(held))
|
||||
return false;
|
||||
if (held.getItem() instanceof BlockItem) {
|
||||
BlockItem blockItem = (BlockItem) held.getItem();
|
||||
if (blockItem.getBlock() instanceof KineticBlock && hasShaftTowards(worldIn, pos, state, hit.getFace()))
|
||||
return false;
|
||||
}
|
||||
|
||||
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
|
||||
displayScreen((SequencedGearshiftTileEntity) worldIn.getTileEntity(pos));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@OnlyIn(value = Dist.CLIENT)
|
||||
protected void displayScreen(SequencedGearshiftTileEntity te) {
|
||||
ScreenOpener.open(new SequencedGearshiftScreen(te));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockItemUseContext context) {
|
||||
Axis preferredAxis = RotatedPillarKineticBlock.getPreferredAxis(context);
|
||||
if (preferredAxis != null && !context.isPlacerSneaking())
|
||||
return withAxis(preferredAxis, context);
|
||||
return withAxis(context.getNearestLookingDirection().getAxis(), context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType onWrenched(BlockState state, ItemUseContext context) {
|
||||
Direction facing = context.getFace();
|
||||
if (facing.getAxis().isVertical() && !state.get(VERTICAL)) {
|
||||
KineticTileEntity.switchToBlockState(context.getWorld(), context.getPos(), state.cycle(VERTICAL));
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
return super.onWrenched(state, context);
|
||||
}
|
||||
|
||||
private BlockState withAxis(Axis axis, BlockItemUseContext context) {
|
||||
BlockState state = getDefaultState().with(VERTICAL, axis.isVertical());
|
||||
if (axis.isVertical())
|
||||
return state.with(HORIZONTAL_AXIS, context.getPlacementHorizontalFacing().getAxis());
|
||||
return state.with(HORIZONTAL_AXIS, axis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Axis getRotationAxis(BlockState state) {
|
||||
if (state.get(VERTICAL))
|
||||
return Axis.Y;
|
||||
return super.getRotationAxis(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasStaticPart() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.AllPackets;
|
||||
import com.simibubi.create.ScreenResources;
|
||||
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
|
||||
import com.simibubi.create.foundation.gui.ScreenElementRenderer;
|
||||
import com.simibubi.create.foundation.gui.widgets.ScrollInput;
|
||||
import com.simibubi.create.foundation.gui.widgets.SelectionScrollInput;
|
||||
import com.simibubi.create.foundation.utility.Lang;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||
|
||||
private static final ItemStack renderedItem = new ItemStack(AllBlocks.SEQUENCED_GEARSHIFT.get());
|
||||
private static final ScreenResources background = ScreenResources.SEQUENCER;
|
||||
|
||||
private final String title = Lang.translate("gui.sequenced_gearshift.title");
|
||||
private int lastModification;
|
||||
private Vector<Instruction> instructions;
|
||||
private BlockPos pos;
|
||||
|
||||
private Vector<Vector<ScrollInput>> inputs;
|
||||
|
||||
public SequencedGearshiftScreen(SequencedGearshiftTileEntity te) {
|
||||
this.instructions = te.instructions;
|
||||
this.pos = te.getPos();
|
||||
lastModification = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
setWindowSize(background.width + 50, background.height);
|
||||
super.init();
|
||||
widgets.clear();
|
||||
|
||||
inputs = new Vector<>(5);
|
||||
for (int row = 0; row < inputs.capacity(); row++)
|
||||
inputs.add(new Vector<>(3));
|
||||
|
||||
for (int row = 0; row < instructions.size(); row++)
|
||||
initInputsOfRow(row);
|
||||
}
|
||||
|
||||
public void initInputsOfRow(int row) {
|
||||
int x = guiLeft + 28;
|
||||
int y = guiTop + 29;
|
||||
int rowHeight = 18;
|
||||
|
||||
Vector<ScrollInput> rowInputs = inputs.get(row);
|
||||
rowInputs.forEach(widgets::remove);
|
||||
rowInputs.clear();
|
||||
int index = row;
|
||||
Instruction instruction = instructions.get(row);
|
||||
|
||||
ScrollInput type =
|
||||
new SelectionScrollInput(x, y + rowHeight * row, 50, 14).forOptions(SequencerInstructions.getOptions())
|
||||
.calling(state -> instructionUpdated(index, state)).setState(instruction.instruction.ordinal())
|
||||
.titled(Lang.translate("gui.sequenced_gearshift.instruction"));
|
||||
ScrollInput value =
|
||||
new ScrollInput(x + 54, y + rowHeight * row, 30, 14).calling(state -> instruction.value = state);
|
||||
ScrollInput direction = new SelectionScrollInput(x + 88, y + rowHeight * row, 18, 14)
|
||||
.forOptions(InstructionSpeedModifiers.getOptions())
|
||||
.calling(state -> instruction.speedModifier = InstructionSpeedModifiers.values()[state])
|
||||
.titled(Lang.translate("gui.sequenced_gearshift.speed"));
|
||||
|
||||
rowInputs.add(type);
|
||||
rowInputs.add(value);
|
||||
rowInputs.add(direction);
|
||||
|
||||
widgets.addAll(rowInputs);
|
||||
updateParamsOfRow(row);
|
||||
}
|
||||
|
||||
public void updateParamsOfRow(int row) {
|
||||
Instruction instruction = instructions.get(row);
|
||||
Vector<ScrollInput> rowInputs = inputs.get(row);
|
||||
SequencerInstructions def = instruction.instruction;
|
||||
boolean hasValue = def.hasValueParameter;
|
||||
boolean hasModifier = def.hasSpeedParameter;
|
||||
|
||||
ScrollInput value = rowInputs.get(1);
|
||||
value.active = value.visible = hasValue;
|
||||
if (hasValue)
|
||||
value.withRange(1, def.maxValue + 1).titled(Lang.translate(def.parameterKey)).withShiftStep(def.shiftStep)
|
||||
.setState(instruction.value).onChanged();
|
||||
if (def == SequencerInstructions.WAIT) {
|
||||
value.withStepFunction(context -> {
|
||||
int v = context.currentValue;
|
||||
if (!context.forward)
|
||||
v--;
|
||||
if (v < 20)
|
||||
return context.shift ? 20 : 1;
|
||||
return context.shift ? 100 : 20;
|
||||
});
|
||||
} else
|
||||
value.withStepFunction(value.standardStep());
|
||||
|
||||
ScrollInput modifier = rowInputs.get(2);
|
||||
modifier.active = modifier.visible = hasModifier;
|
||||
if (hasModifier)
|
||||
modifier.setState(instruction.speedModifier.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderWindow(int mouseX, int mouseY, float partialTicks) {
|
||||
int hFontColor = 0xD3CBBE;
|
||||
background.draw(this, guiLeft, guiTop);
|
||||
|
||||
for (int row = 0; row < instructions.capacity(); row++) {
|
||||
ScreenResources toDraw = ScreenResources.SEQUENCER_EMPTY;
|
||||
int yOffset = toDraw.height * row;
|
||||
|
||||
if (row < instructions.size()) {
|
||||
Instruction instruction = instructions.get(row);
|
||||
SequencerInstructions def = instruction.instruction;
|
||||
def.background.draw(guiLeft + 14, guiTop + 29 + yOffset);
|
||||
|
||||
label(32, 6 + yOffset, Lang.translate(def.translationKey));
|
||||
if (def.hasValueParameter) {
|
||||
String text = def.formatValue(instruction.value);
|
||||
int stringWidth = font.getStringWidth(text);
|
||||
label(85 + (12 - stringWidth / 2), 6 + yOffset, text);
|
||||
}
|
||||
if (def.hasSpeedParameter)
|
||||
label(120, 6 + yOffset, instruction.speedModifier.label);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
toDraw.draw(guiLeft + 14, guiTop + 29 + yOffset);
|
||||
}
|
||||
|
||||
font.drawStringWithShadow(title, guiLeft - 3 + (background.width - font.getStringWidth(title)) / 2, guiTop + 10,
|
||||
hFontColor);
|
||||
ScreenElementRenderer.render3DItem(this::getRenderedBlock);
|
||||
}
|
||||
|
||||
private void label(int x, int y, String text) {
|
||||
font.drawStringWithShadow(text, guiLeft + x, guiTop + 26 + y, 0xFFFFEE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (lastModification >= 0)
|
||||
lastModification++;
|
||||
|
||||
if (lastModification >= 20) {
|
||||
lastModification = -1;
|
||||
sendPacket();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket() {
|
||||
AllPackets.channel.sendToServer(new ConfigureSequencedGearshiftPacket(pos, instructions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed() {
|
||||
sendPacket();
|
||||
}
|
||||
|
||||
public ItemStack getRenderedBlock() {
|
||||
GlStateManager.translated(guiLeft + background.width + 20, guiTop + 50, 0);
|
||||
GlStateManager.scaled(5, 5, 5);
|
||||
return renderedItem;
|
||||
}
|
||||
|
||||
private void instructionUpdated(int index, int state) {
|
||||
SequencerInstructions newValue = SequencerInstructions.values()[state];
|
||||
instructions.get(index).instruction = newValue;
|
||||
updateParamsOfRow(index);
|
||||
if (newValue == SequencerInstructions.END) {
|
||||
for (int i = instructions.size() - 1; i > index; i--) {
|
||||
instructions.remove(i);
|
||||
Vector<ScrollInput> rowInputs = inputs.get(i);
|
||||
rowInputs.forEach(widgets::remove);
|
||||
rowInputs.clear();
|
||||
}
|
||||
} else {
|
||||
if (index + 1 < instructions.capacity() && index + 1 == instructions.size()) {
|
||||
instructions.add(new Instruction(SequencerInstructions.END));
|
||||
initInputsOfRow(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import com.simibubi.create.AllTileEntities;
|
||||
import com.simibubi.create.modules.contraptions.relays.encased.SplitShaftTileEntity;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
|
||||
public class SequencedGearshiftTileEntity extends SplitShaftTileEntity {
|
||||
|
||||
Vector<Instruction> instructions;
|
||||
int currentInstruction;
|
||||
int currentInstructionDuration;
|
||||
int timer;
|
||||
|
||||
public SequencedGearshiftTileEntity() {
|
||||
super(AllTileEntities.SEQUENCED_GEARSHIFT.type);
|
||||
instructions = Instruction.createDefault();
|
||||
currentInstruction = -1;
|
||||
currentInstructionDuration = -1;
|
||||
timer = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (isIdle())
|
||||
return;
|
||||
if (world.isRemote)
|
||||
return;
|
||||
if (timer < currentInstructionDuration) {
|
||||
timer++;
|
||||
return;
|
||||
}
|
||||
run(currentInstruction + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSpeedChanged(float previousSpeed) {
|
||||
super.onSpeedChanged(previousSpeed);
|
||||
if (isIdle())
|
||||
return;
|
||||
float currentSpeed = Math.abs(speed);
|
||||
if (Math.abs(previousSpeed) == currentSpeed)
|
||||
return;
|
||||
Instruction instruction = getInstruction(currentInstruction);
|
||||
if (instruction == null)
|
||||
return;
|
||||
|
||||
// Update instruction time with regards to new speed
|
||||
float initialProgress = timer / (float) currentInstructionDuration;
|
||||
currentInstructionDuration = instruction.getDuration(initialProgress, getTheoreticalSpeed());
|
||||
timer = 0;
|
||||
}
|
||||
|
||||
public boolean isIdle() {
|
||||
return currentInstruction == -1;
|
||||
}
|
||||
|
||||
public void onRedstoneUpdate() {
|
||||
if (!isIdle())
|
||||
return;
|
||||
if (!world.isBlockPowered(pos)) {
|
||||
world.setBlockState(pos, getBlockState().with(SequencedGearshiftBlock.STATE, 0), 3);
|
||||
return;
|
||||
}
|
||||
run(0);
|
||||
}
|
||||
|
||||
protected void run(int instructionIndex) {
|
||||
Instruction instruction = getInstruction(instructionIndex);
|
||||
if (instruction == null || instruction.instruction == SequencerInstructions.END) {
|
||||
if (getModifier() != 0)
|
||||
detachKinetics();
|
||||
currentInstruction = -1;
|
||||
currentInstructionDuration = -1;
|
||||
timer = 0;
|
||||
if (!world.isBlockPowered(pos))
|
||||
world.setBlockState(pos, getBlockState().with(SequencedGearshiftBlock.STATE, 0), 3);
|
||||
else
|
||||
sendData();
|
||||
return;
|
||||
}
|
||||
|
||||
detachKinetics();
|
||||
currentInstructionDuration = instruction.getDuration(0, getTheoreticalSpeed());
|
||||
currentInstruction = instructionIndex;
|
||||
timer = 0;
|
||||
world.setBlockState(pos, getBlockState().with(SequencedGearshiftBlock.STATE, instructionIndex + 1), 3);
|
||||
}
|
||||
|
||||
public Instruction getInstruction(int instructionIndex) {
|
||||
return instructionIndex >= 0 && instructionIndex < instructions.size() ? instructions.get(instructionIndex)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT compound) {
|
||||
compound.putInt("InstructionIndex", currentInstruction);
|
||||
compound.putInt("InstructionDuration", currentInstructionDuration);
|
||||
compound.putInt("Timer", timer);
|
||||
compound.put("Instructions", Instruction.serializeAll(instructions));
|
||||
return super.write(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
currentInstruction = compound.getInt("InstructionIndex");
|
||||
currentInstructionDuration = compound.getInt("InstructionDuration");
|
||||
timer = compound.getInt("Timer");
|
||||
instructions = Instruction.deserializeAll(compound.getList("Instructions", NBT.TAG_COMPOUND));
|
||||
super.read(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotationSpeedModifier(Direction face) {
|
||||
return (!hasSource() || face == getSourceFacing()) ? 1 : getModifier();
|
||||
}
|
||||
|
||||
public int getModifier() {
|
||||
return isIdle() ? 0 : instructions.get(currentInstruction).getSpeedModifier();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.simibubi.create.modules.contraptions.relays.advanced.sequencer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.ScreenResources;
|
||||
import com.simibubi.create.foundation.utility.Lang;
|
||||
|
||||
public enum SequencerInstructions {
|
||||
|
||||
TURN_ANGLE("angle", ScreenResources.SEQUENCER_INSTRUCTION, true, true, 360, 45),
|
||||
TURN_DISTANCE("distance", ScreenResources.SEQUENCER_INSTRUCTION, true, true, 50, 5),
|
||||
WAIT("duration", ScreenResources.SEQUENCER_WAIT, true, false, 600, 20),
|
||||
END("", ScreenResources.SEQUENCER_END),
|
||||
|
||||
;
|
||||
|
||||
String translationKey;
|
||||
String parameterKey;
|
||||
boolean hasValueParameter;
|
||||
boolean hasSpeedParameter;
|
||||
ScreenResources background;
|
||||
int maxValue;
|
||||
int shiftStep;
|
||||
|
||||
private SequencerInstructions(String parameterName, ScreenResources background) {
|
||||
this(parameterName, background, false, false, -1, -1);
|
||||
}
|
||||
|
||||
private SequencerInstructions(String parameterName, ScreenResources background, boolean hasValueParameter,
|
||||
boolean hasSpeedParameter, int maxValue, int shiftStep) {
|
||||
this.hasValueParameter = hasValueParameter;
|
||||
this.hasSpeedParameter = hasSpeedParameter;
|
||||
this.background = background;
|
||||
this.maxValue = maxValue;
|
||||
this.shiftStep = shiftStep;
|
||||
translationKey = "gui.sequenced_gearshift.instruction." + Lang.asId(name());
|
||||
parameterKey = translationKey + "." + parameterName;
|
||||
}
|
||||
|
||||
static List<String> getOptions() {
|
||||
List<String> options = new ArrayList<>();
|
||||
for (SequencerInstructions entry : values())
|
||||
options.add(Lang.translate(entry.translationKey));
|
||||
return options;
|
||||
}
|
||||
|
||||
String formatValue(int value) {
|
||||
if (this == TURN_ANGLE)
|
||||
return value + "°";
|
||||
if (this == TURN_DISTANCE)
|
||||
return value + "m";
|
||||
if (this == WAIT) {
|
||||
if (value >= 20)
|
||||
return (value / 20) + "s";
|
||||
return value + "t";
|
||||
}
|
||||
return "" + value;
|
||||
}
|
||||
|
||||
}
|
|
@ -153,8 +153,13 @@ public class BeltBlock extends HorizontalKineticBlock
|
|||
|
||||
if (state.get(SLOPE) == Slope.VERTICAL)
|
||||
return;
|
||||
if (entityIn instanceof PlayerEntity && entityIn.isSneaking())
|
||||
return;
|
||||
if (entityIn instanceof PlayerEntity) {
|
||||
PlayerEntity player = (PlayerEntity) entityIn;
|
||||
if (player.isSneaking())
|
||||
return;
|
||||
if (player.abilities.isFlying)
|
||||
return;
|
||||
}
|
||||
if (belt == null || belt.getSpeed() == 0)
|
||||
return;
|
||||
if (entityIn instanceof ItemEntity && entityIn.isAlive()) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import static net.minecraft.util.Direction.AxisDirection.POSITIVE;
|
|||
import java.util.List;
|
||||
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part;
|
||||
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope;
|
||||
|
@ -14,6 +15,7 @@ import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope;
|
|||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.item.HangingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.potion.EffectInstance;
|
||||
import net.minecraft.potion.Effects;
|
||||
|
@ -150,7 +152,7 @@ public class BeltMovementHandler {
|
|||
checkBB = checkBB.offset(checkDistance).grow(-Math.abs(checkDistance.x), -Math.abs(checkDistance.y),
|
||||
-Math.abs(checkDistance.z));
|
||||
List<Entity> list = world.getEntitiesWithinAABBExcludingEntity(entityIn, checkBB);
|
||||
list.removeIf(e -> entityIn.isRidingOrBeingRiddenBy(e));
|
||||
list.removeIf(e -> shouldIgnoreBlocking(entityIn, e));
|
||||
if (!list.isEmpty()) {
|
||||
entityIn.setMotion(0, 0, 0);
|
||||
info.ticksSinceLastCollision--;
|
||||
|
@ -186,4 +188,12 @@ public class BeltMovementHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean shouldIgnoreBlocking(Entity me, Entity other) {
|
||||
if (other instanceof ContraptionEntity)
|
||||
return true;
|
||||
if (other instanceof HangingEntity)
|
||||
return true;
|
||||
return me.isRidingOrBeingRiddenBy(other);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ package com.simibubi.create.modules.contraptions.relays.encased;
|
|||
import com.simibubi.create.AllBlockPartials;
|
||||
import com.simibubi.create.foundation.utility.AnimationTickHolder;
|
||||
import com.simibubi.create.foundation.utility.SuperByteBuffer;
|
||||
import com.simibubi.create.modules.contraptions.base.IRotate;
|
||||
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
|
||||
import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
@ -15,9 +16,10 @@ import net.minecraft.util.math.BlockPos;
|
|||
public class SplitShaftTileEntityRenderer extends KineticTileEntityRenderer {
|
||||
|
||||
@Override
|
||||
public void renderFast(KineticTileEntity te, double x, double y, double z, float partialTicks,
|
||||
int destroyStage, BufferBuilder buffer) {
|
||||
final Axis boxAxis = te.getBlockState().get(BlockStateProperties.AXIS);
|
||||
public void renderFast(KineticTileEntity te, double x, double y, double z, float partialTicks, int destroyStage,
|
||||
BufferBuilder buffer) {
|
||||
Block block = te.getBlockState().getBlock();
|
||||
final Axis boxAxis = ((IRotate) block).getRotationAxis(te.getBlockState());
|
||||
final BlockPos pos = te.getPos();
|
||||
float time = AnimationTickHolder.getRenderTick();
|
||||
|
||||
|
@ -37,8 +39,8 @@ public class SplitShaftTileEntityRenderer extends KineticTileEntityRenderer {
|
|||
angle += offset;
|
||||
angle = angle / 180f * (float) Math.PI;
|
||||
|
||||
SuperByteBuffer superByteBuffer = AllBlockPartials.SHAFT_HALF.renderOnDirectional(te.getBlockState(),
|
||||
direction);
|
||||
SuperByteBuffer superByteBuffer =
|
||||
AllBlockPartials.SHAFT_HALF.renderOnDirectional(te.getBlockState(), direction);
|
||||
kineticRotationTransform(superByteBuffer, te, axis, angle, getWorld());
|
||||
superByteBuffer.translate(x, y, z).renderInto(buffer);
|
||||
|
||||
|
|
|
@ -60,7 +60,8 @@ public class RedstoneLinkTileEntity extends SmartTileEntity {
|
|||
|
||||
public void transmit(boolean signal) {
|
||||
transmittedSignal = signal;
|
||||
link.notifySignalChange();
|
||||
if (link != null)
|
||||
link.notifySignalChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.simibubi.create.foundation.utility.Lang;
|
|||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.TileEntityType;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class FlexpeaterTileEntity extends SmartTileEntity {
|
||||
|
||||
|
@ -36,8 +37,15 @@ public class FlexpeaterTileEntity extends SmartTileEntity {
|
|||
maxState.withStepFunction(this::step);
|
||||
maxState.withFormatter(this::format);
|
||||
maxState.withUnit(this::getUnit);
|
||||
maxState.withCallback(this::onMaxDelayChanged);
|
||||
|
||||
behaviours.add(maxState);
|
||||
}
|
||||
|
||||
private void onMaxDelayChanged(int newMax) {
|
||||
state = MathHelper.clamp(state, 0, newMax);
|
||||
sendData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(CompoundNBT compound) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"variants": {
|
||||
"vertical=false,axis=x,state=0" : { "model": "create:block/sequenced_gearshift/idle", "y": 90 },
|
||||
"vertical=false,axis=x,state=1" : { "model": "create:block/sequenced_gearshift/seq_1", "y": 90 },
|
||||
"vertical=false,axis=x,state=2" : { "model": "create:block/sequenced_gearshift/seq_2", "y": 90 },
|
||||
"vertical=false,axis=x,state=3" : { "model": "create:block/sequenced_gearshift/seq_3", "y": 90 },
|
||||
"vertical=false,axis=x,state=4" : { "model": "create:block/sequenced_gearshift/seq_4", "y": 90 },
|
||||
"vertical=false,axis=x,state=5" : { "model": "create:block/sequenced_gearshift/seq_5", "y": 90 },
|
||||
|
||||
"vertical=false,axis=z,state=0" : { "model": "create:block/sequenced_gearshift/idle" },
|
||||
"vertical=false,axis=z,state=1" : { "model": "create:block/sequenced_gearshift/seq_1" },
|
||||
"vertical=false,axis=z,state=2" : { "model": "create:block/sequenced_gearshift/seq_2" },
|
||||
"vertical=false,axis=z,state=3" : { "model": "create:block/sequenced_gearshift/seq_3" },
|
||||
"vertical=false,axis=z,state=4" : { "model": "create:block/sequenced_gearshift/seq_4" },
|
||||
"vertical=false,axis=z,state=5" : { "model": "create:block/sequenced_gearshift/seq_5" },
|
||||
|
||||
"vertical=true,axis=x,state=0" : { "model": "create:block/sequenced_gearshift/idle", "x": 90, "y": 90 },
|
||||
"vertical=true,axis=x,state=1" : { "model": "create:block/sequenced_gearshift/seq_1", "x": 90, "y": 90 },
|
||||
"vertical=true,axis=x,state=2" : { "model": "create:block/sequenced_gearshift/seq_2", "x": 90, "y": 90 },
|
||||
"vertical=true,axis=x,state=3" : { "model": "create:block/sequenced_gearshift/seq_3", "x": 90, "y": 90 },
|
||||
"vertical=true,axis=x,state=4" : { "model": "create:block/sequenced_gearshift/seq_4", "x": 90, "y": 90 },
|
||||
"vertical=true,axis=x,state=5" : { "model": "create:block/sequenced_gearshift/seq_5", "x": 90, "y": 90 },
|
||||
|
||||
"vertical=true,axis=z,state=0" : { "model": "create:block/sequenced_gearshift/idle", "x": 90 },
|
||||
"vertical=true,axis=z,state=1" : { "model": "create:block/sequenced_gearshift/seq_1", "x": 90 },
|
||||
"vertical=true,axis=z,state=2" : { "model": "create:block/sequenced_gearshift/seq_2", "x": 90 },
|
||||
"vertical=true,axis=z,state=3" : { "model": "create:block/sequenced_gearshift/seq_3", "x": 90 },
|
||||
"vertical=true,axis=z,state=4" : { "model": "create:block/sequenced_gearshift/seq_4", "x": 90 },
|
||||
"vertical=true,axis=z,state=5" : { "model": "create:block/sequenced_gearshift/seq_5", "x": 90 }
|
||||
}
|
||||
}
|
|
@ -151,6 +151,7 @@
|
|||
"block.create.logisticians_table": "Logisticians Table",
|
||||
"block.create.package_funnel": "Package Funnel",
|
||||
"block.create.belt_tunnel": "Conveyor Tunnel",
|
||||
"block.create.sequenced_gearshift": "Sequenced Gearshift",
|
||||
|
||||
"block.create.tiled_glass": "Tiled Glass",
|
||||
"block.create.framed_glass": "Large Glass Window",
|
||||
|
@ -421,6 +422,21 @@
|
|||
"create.gui.stockswitch.startAbove": "Start Signal above",
|
||||
"create.gui.stockswitch.stopAt": "Stop Signal at",
|
||||
"create.gui.stockswitch.stopBelow": "Stop Signal below",
|
||||
|
||||
"create.gui.sequenced_gearshift.title": "Sequenced Gearshift",
|
||||
"create.gui.sequenced_gearshift.instruction": "Instruction",
|
||||
"create.gui.sequenced_gearshift.instruction.turn_angle": "Turn",
|
||||
"create.gui.sequenced_gearshift.instruction.turn_angle.angle": "Angle",
|
||||
"create.gui.sequenced_gearshift.instruction.turn_distance": "Piston",
|
||||
"create.gui.sequenced_gearshift.instruction.turn_distance.distance": "Distance",
|
||||
"create.gui.sequenced_gearshift.instruction.wait": "Wait",
|
||||
"create.gui.sequenced_gearshift.instruction.wait.duration": "Duration",
|
||||
"create.gui.sequenced_gearshift.instruction.end": "End",
|
||||
"create.gui.sequenced_gearshift.speed": "Speed, Direction",
|
||||
"create.gui.sequenced_gearshift.speed.forward": "Input speed, Forwards",
|
||||
"create.gui.sequenced_gearshift.speed.forward_fast": "Double speed, Forwards",
|
||||
"create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed",
|
||||
"create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed",
|
||||
|
||||
"create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s",
|
||||
"create.schematicAndQuill.firstPos": "First position set.",
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"0": "create:block/brass_casing",
|
||||
"1": "create:block/brass_gearbox",
|
||||
"4": "create:block/sequenced_gearshift",
|
||||
"particle": "create:block/brass_gearbox"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "Core",
|
||||
"from": [1, 1, 1],
|
||||
"to": [15, 15, 15],
|
||||
"faces": {
|
||||
"north": {"uv": [1, 1, 15, 15], "texture": "#1"},
|
||||
"south": {"uv": [1, 1, 15, 15], "texture": "#1"},
|
||||
"up": {"uv": [1, 1, 15, 15], "texture": "#4"},
|
||||
"down": {"uv": [1, 1, 15, 15], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Top",
|
||||
"from": [0, 14, 0],
|
||||
"to": [5, 16, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [11, 0, 16, 2], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [0, 0, 5, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 2, 16], "rotation": 270, "texture": "#4"},
|
||||
"up": {"uv": [0, 0, 5, 16], "texture": "#4"},
|
||||
"down": {"uv": [0, 0, 16, 16], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bottom",
|
||||
"from": [0, 0, 0],
|
||||
"to": [5, 2, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [11, 14, 16, 16], "texture": "#0"},
|
||||
"east": {"uv": [2, 0, 0, 16], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [0, 14, 5, 16], "texture": "#0"},
|
||||
"west": {"uv": [2, 0, 0, 16], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [0, 16, 16, 0], "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 5, 0], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Top",
|
||||
"from": [11, 14, 0],
|
||||
"to": [16, 16, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [16, 0, 11, 2], "texture": "#0"},
|
||||
"east": {"uv": [0, 16, 2, 0], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [5, 0, 0, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 16, 2, 0], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [11, 0, 16, 16], "texture": "#4"},
|
||||
"down": {"uv": [16, 0, 0, 16], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bottom",
|
||||
"from": [11, 0, 0],
|
||||
"to": [16, 2, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 14, 5, 16], "texture": "#0"},
|
||||
"east": {"uv": [2, 16, 0, 0], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [11, 14, 16, 16], "texture": "#0"},
|
||||
"west": {"uv": [2, 16, 0, 0], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [16, 16, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [11, 16, 16, 0], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SideWest",
|
||||
"from": [0, 2, 0],
|
||||
"to": [2, 14, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [14, 2, 16, 14], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 16, 14], "texture": "#0"},
|
||||
"south": {"uv": [0, 2, 2, 14], "texture": "#0"},
|
||||
"west": {"uv": [0, 2, 16, 14], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SideEast",
|
||||
"from": [14, 2, 0],
|
||||
"to": [16, 14, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 2, 2, 14], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 16, 14], "texture": "#0"},
|
||||
"south": {"uv": [14, 2, 16, 14], "texture": "#0"},
|
||||
"west": {"uv": [0, 2, 16, 14], "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"name": "encased_shaft",
|
||||
"origin": [8, 8, 8],
|
||||
"children": [0, 1, 2, 3, 4, 5, 6,
|
||||
{
|
||||
"name": "shaft",
|
||||
"origin": [8, 8, 8],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "create:block/sequenced_gearshift/idle",
|
||||
"textures": {
|
||||
"4": "create:block/sequenced_gearshift_1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "create:block/sequenced_gearshift/idle",
|
||||
"textures": {
|
||||
"4": "create:block/sequenced_gearshift_2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "create:block/sequenced_gearshift/idle",
|
||||
"textures": {
|
||||
"4": "create:block/sequenced_gearshift_3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "create:block/sequenced_gearshift/idle",
|
||||
"textures": {
|
||||
"4": "create:block/sequenced_gearshift_4"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "create:block/sequenced_gearshift/idle",
|
||||
"textures": {
|
||||
"4": "create:block/sequenced_gearshift_5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"0": "create:block/brass_casing",
|
||||
"1": "create:block/brass_gearbox",
|
||||
"4": "create:block/sequenced_gearshift",
|
||||
"particle": "create:block/axis",
|
||||
"1_0": "create:block/axis",
|
||||
"1_1": "create:block/axis_top"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "Core",
|
||||
"from": [1, 1, 1],
|
||||
"to": [15, 15, 15],
|
||||
"faces": {
|
||||
"north": {"uv": [1, 1, 15, 15], "texture": "#1"},
|
||||
"south": {"uv": [1, 1, 15, 15], "texture": "#1"},
|
||||
"up": {"uv": [1, 1, 15, 15], "texture": "#4"},
|
||||
"down": {"uv": [1, 1, 15, 15], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Top",
|
||||
"from": [0, 14, 0],
|
||||
"to": [5, 16, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [11, 0, 16, 2], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 2, 16], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [0, 0, 5, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 2, 16], "rotation": 270, "texture": "#4"},
|
||||
"up": {"uv": [0, 0, 5, 16], "texture": "#4"},
|
||||
"down": {"uv": [0, 0, 16, 16], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bottom",
|
||||
"from": [0, 0, 0],
|
||||
"to": [5, 2, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [11, 14, 16, 16], "texture": "#0"},
|
||||
"east": {"uv": [2, 0, 0, 16], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [0, 14, 5, 16], "texture": "#0"},
|
||||
"west": {"uv": [2, 0, 0, 16], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [0, 16, 16, 0], "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 5, 0], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Top",
|
||||
"from": [11, 14, 0],
|
||||
"to": [16, 16, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [16, 0, 11, 2], "texture": "#0"},
|
||||
"east": {"uv": [0, 16, 2, 0], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [5, 0, 0, 2], "texture": "#0"},
|
||||
"west": {"uv": [0, 16, 2, 0], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [11, 0, 16, 16], "texture": "#4"},
|
||||
"down": {"uv": [16, 0, 0, 16], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bottom",
|
||||
"from": [11, 0, 0],
|
||||
"to": [16, 2, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 14, 5, 16], "texture": "#0"},
|
||||
"east": {"uv": [2, 16, 0, 0], "rotation": 90, "texture": "#4"},
|
||||
"south": {"uv": [11, 14, 16, 16], "texture": "#0"},
|
||||
"west": {"uv": [2, 16, 0, 0], "rotation": 90, "texture": "#4"},
|
||||
"up": {"uv": [16, 16, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [11, 16, 16, 0], "texture": "#4"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SideWest",
|
||||
"from": [0, 2, 0],
|
||||
"to": [2, 14, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [14, 2, 16, 14], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 16, 14], "texture": "#0"},
|
||||
"south": {"uv": [0, 2, 2, 14], "texture": "#0"},
|
||||
"west": {"uv": [0, 2, 16, 14], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SideEast",
|
||||
"from": [14, 2, 0],
|
||||
"to": [16, 14, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 2, 2, 14], "texture": "#0"},
|
||||
"east": {"uv": [0, 2, 16, 14], "texture": "#0"},
|
||||
"south": {"uv": [14, 2, 16, 14], "texture": "#0"},
|
||||
"west": {"uv": [0, 2, 16, 14], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Axis",
|
||||
"from": [6, 6, 0],
|
||||
"to": [10, 10, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [6, 6, 10, 10], "rotation": 180, "texture": "#1_1"},
|
||||
"east": {"uv": [6, 0, 10, 16], "rotation": 90, "texture": "#1_0"},
|
||||
"south": {"uv": [6, 6, 10, 10], "texture": "#1_1"},
|
||||
"west": {"uv": [6, 0, 10, 16], "rotation": 270, "texture": "#1_0"},
|
||||
"up": {"uv": [6, 0, 10, 16], "texture": "#1_0"},
|
||||
"down": {"uv": [6, 0, 10, 16], "rotation": 180, "texture": "#1_0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"name": "encased_shaft",
|
||||
"origin": [8, 8, 8],
|
||||
"children": [0, 1, 2, 3, 4, 5, 6,
|
||||
{
|
||||
"name": "shaft",
|
||||
"origin": [8, 8, 8],
|
||||
"children": [7]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 578 B |
After Width: | Height: | Size: 590 B |
After Width: | Height: | Size: 598 B |
After Width: | Height: | Size: 578 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 566 B |
BIN
src/main/resources/assets/create/textures/gui/sequencer.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "create:sequenced_gearshift"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:survives_explosion"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"type": "crafting_shaped",
|
||||
"type": "crafting_shaped",
|
||||
"pattern": [
|
||||
" B ",
|
||||
"SCS",
|
||||
" I "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "create:cogwheel"
|
||||
},
|
||||
"B": {
|
||||
"item": "create:electron_tube"
|
||||
},
|
||||
"C": {
|
||||
"item": "create:brass_casing"
|
||||
},
|
||||
"I": {
|
||||
"item": "minecraft:clock"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "create:sequenced_gearshift",
|
||||
"count": 1
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"type": "create:module",
|
||||
"module": "contraptions"
|
||||
}
|
||||
]
|
||||
}
|