Contraption bugs and balance

- Added a config option to disable the stress/torque mechanic entirely
- Water wheel speed is halved and now configurable
- Water wheels now validate their current speed from time to time
- Fixed windmill bearings counting their sail blocks incorrectly
- Fixed windmill bearings not assembling when placed in a powered block position
- Fixed Contraption controllers not removing their entity reference on the client when disassembled
- Bearings no longer start rotating when no block was found
- Contraptions no longer move unbreakable blocks
- Pistons no longer assemble the whole line of blocks when retracting
- Non-sticky pistons no longer pull blocks back when assembled
- Pulley no longer assembles when the structure by itself is immovable
- Fixed pistons applying reversed motion in some orientations
- Fixed piston contraptions not including the poles into its render bounds
- Fixed Contraptions not culling backfaces when rendered
- Translucent blocks now get moved to the back of the render order in contraptions, fixes hidden block faces between translucent and non-translucent blocks
- Fixed Contraptions being movable through water, fans and other means of applying motion to entities.
- Belt segments can no longer be rotated by a wrench
- Hand cranks are now gated behind brass and sheet production
This commit is contained in:
simibubi 2020-02-16 18:26:06 +01:00
parent 9308d3ca2b
commit 81a2e314bc
29 changed files with 191 additions and 130 deletions

View file

@ -2,9 +2,11 @@ package com.simibubi.create.config;
public class CKinetics extends ConfigBase { public class CKinetics extends ConfigBase {
public ConfigBool disableStress = b(false, "disableStress", Comments.disableStress);
public ConfigInt maxBeltLength = i(20, 5, "maxBeltLength", Comments.maxBeltLength); public ConfigInt maxBeltLength = i(20, 5, "maxBeltLength", Comments.maxBeltLength);
public ConfigInt crushingDamage = i(4, 0, "crushingDamage", Comments.crushingDamage); public ConfigInt crushingDamage = i(4, 0, "crushingDamage", Comments.crushingDamage);
public ConfigInt maxMotorSpeed = i(256, 64, "maxMotorSpeed", Comments.rpm, Comments.maxMotorSpeed); public ConfigInt maxMotorSpeed = i(256, 64, "maxMotorSpeed", Comments.rpm, Comments.maxMotorSpeed);
public ConfigInt waterWheelSpeed = i(5, 1, "waterWheelSpeed", Comments.rpm, Comments.waterWheelSpeed);
public ConfigInt maxRotationSpeed = i(256, 64, "maxRotationSpeed", Comments.rpm, Comments.maxRotationSpeed); public ConfigInt maxRotationSpeed = i(256, 64, "maxRotationSpeed", Comments.rpm, Comments.maxRotationSpeed);
public ConfigEnum<DeployerAggroSetting> ignoreDeployerAttacks = public ConfigEnum<DeployerAggroSetting> ignoreDeployerAttacks =
e(DeployerAggroSetting.CREEPERS, "ignoreDeployerAttacks", Comments.ignoreDeployerAttacks); e(DeployerAggroSetting.CREEPERS, "ignoreDeployerAttacks", Comments.ignoreDeployerAttacks);
@ -67,6 +69,8 @@ public class CKinetics extends ConfigBase {
static String highCapacity = "Minimum added Capacity by sources to be considered 'high'"; static String highCapacity = "Minimum added Capacity by sources to be considered 'high'";
static String stress = "Fine tune the kinetic stats of individual components"; static String stress = "Fine tune the kinetic stats of individual components";
static String ignoreDeployerAttacks = "Select what mobs should ignore Deployers when attacked by them."; static String ignoreDeployerAttacks = "Select what mobs should ignore Deployers when attacked by them.";
static String waterWheelSpeed = "Rotation speed gained by a water wheel for each side with running water. (halved if not against blades)";
static String disableStress = "Disable the Stress mechanic altogether.";
} }
public static enum DeployerAggroSetting { public static enum DeployerAggroSetting {

View file

@ -87,13 +87,14 @@ public class ItemDescription {
boolean isEngine = block instanceof EngineBlock; boolean isEngine = block instanceof EngineBlock;
CKinetics config = AllConfigs.SERVER.kinetics; CKinetics config = AllConfigs.SERVER.kinetics;
SpeedLevel minimumRequiredSpeedLevel = isEngine ? SpeedLevel.NONE : ((IRotate) block).getMinimumRequiredSpeedLevel(); SpeedLevel minimumRequiredSpeedLevel =
isEngine ? SpeedLevel.NONE : ((IRotate) block).getMinimumRequiredSpeedLevel();
boolean hasSpeedRequirement = minimumRequiredSpeedLevel != SpeedLevel.NONE; boolean hasSpeedRequirement = minimumRequiredSpeedLevel != SpeedLevel.NONE;
ResourceLocation id = ((Block) block).getRegistryName(); ResourceLocation id = ((Block) block).getRegistryName();
Map<ResourceLocation, ConfigValue<Double>> impacts = config.stressValues.impacts; Map<ResourceLocation, ConfigValue<Double>> impacts = config.stressValues.impacts;
Map<ResourceLocation, ConfigValue<Double>> capacities = config.stressValues.capacities; Map<ResourceLocation, ConfigValue<Double>> capacities = config.stressValues.capacities;
boolean hasStressImpact = impacts.containsKey(id) && impacts.get(id).get() > 0; boolean hasStressImpact = impacts.containsKey(id) && impacts.get(id).get() > 0 && StressImpact.isEnabled();
boolean hasStressCapacity = capacities.containsKey(id); boolean hasStressCapacity = capacities.containsKey(id) && StressImpact.isEnabled();
boolean hasGlasses = boolean hasGlasses =
AllItems.GOGGLES.typeOf(Minecraft.getInstance().player.getItemStackFromSlot(EquipmentSlotType.HEAD)); AllItems.GOGGLES.typeOf(Minecraft.getInstance().player.getItemStackFromSlot(EquipmentSlotType.HEAD));

View file

@ -51,7 +51,7 @@ public class SuperByteBufferCache {
return get(compartment, toRender, () -> standardBlockRender(toRender)); return get(compartment, toRender, () -> standardBlockRender(toRender));
} }
SuperByteBuffer getGeneric(BlockState key, Supplier<SuperByteBuffer> supplier) { SuperByteBuffer getGeneric(BlockState key, Supplier<SuperByteBuffer> supplier) {
return get(GENERIC_TILE, key, supplier); return get(GENERIC_TILE, key, supplier);
} }

View file

@ -74,8 +74,11 @@ public abstract class DirectionalKineticBlock extends KineticBlock {
@Override @Override
public BlockState getStateForPlacement(BlockItemUseContext context) { public BlockState getStateForPlacement(BlockItemUseContext context) {
Direction preferred = getPreferredFacing(context); Direction preferred = getPreferredFacing(context);
if (preferred == null || context.isPlacerSneaking()) if (preferred == null) {
return getDefaultState().with(FACING, context.getNearestLookingDirection().getOpposite()); Direction nearestLookingDirection = context.getNearestLookingDirection();
return getDefaultState().with(FACING,
context.isPlacerSneaking() ? nearestLookingDirection : nearestLookingDirection.getOpposite());
}
return getDefaultState().with(FACING, preferred.getOpposite()); return getDefaultState().with(FACING, preferred.getOpposite());
} }

View file

@ -63,6 +63,10 @@ public interface IRotate extends IWrenchable {
public TextFormatting getColor() { public TextFormatting getColor() {
return this == LOW ? TextFormatting.YELLOW : this == MEDIUM ? TextFormatting.GOLD : TextFormatting.RED; return this == LOW ? TextFormatting.YELLOW : this == MEDIUM ? TextFormatting.GOLD : TextFormatting.RED;
} }
public static boolean isEnabled() {
return !AllConfigs.SERVER.kinetics.disableStress.get();
}
} }
public boolean hasShaftTowards(IWorldReader world, BlockPos pos, BlockState state, Direction face); public boolean hasShaftTowards(IWorldReader world, BlockPos pos, BlockState state, Direction face);

View file

@ -17,6 +17,7 @@ import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.KineticNetwork; import com.simibubi.create.modules.contraptions.KineticNetwork;
import com.simibubi.create.modules.contraptions.RotationPropagator; import com.simibubi.create.modules.contraptions.RotationPropagator;
import com.simibubi.create.modules.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.modules.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.modules.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.modules.contraptions.particle.RotationIndicatorParticleData; import com.simibubi.create.modules.contraptions.particle.RotationIndicatorParticleData;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@ -71,7 +72,7 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
public void sync(float maxStress, float currentStress) { public void sync(float maxStress, float currentStress) {
this.maxStress = maxStress; this.maxStress = maxStress;
this.currentStress = currentStress; this.currentStress = currentStress;
boolean overStressed = maxStress < currentStress; boolean overStressed = maxStress < currentStress && StressImpact.isEnabled();
if (overStressed != this.overStressed) { if (overStressed != this.overStressed) {
float prevSpeed = getSpeed(); float prevSpeed = getSpeed();
this.overStressed = overStressed; this.overStressed = overStressed;
@ -153,7 +154,7 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
if (compound.contains("Id")) { if (compound.contains("Id")) {
maxStress = compound.getFloat("MaxStress"); maxStress = compound.getFloat("MaxStress");
currentStress = compound.getFloat("Stress"); currentStress = compound.getFloat("Stress");
overStressed = maxStress < currentStress; overStressed = maxStress < currentStress && StressImpact.isEnabled();
setNetworkID(NBTUtil.readUniqueId(compound.getCompound("Id"))); setNetworkID(NBTUtil.readUniqueId(compound.getCompound("Id")));
newNetworkID = networkID; newNetworkID = networkID;
initNetwork = true; initNetwork = true;

View file

@ -40,6 +40,7 @@ import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
@ -66,10 +67,13 @@ public abstract class Contraption {
protected Direction cachedColliderDirection; protected Direction cachedColliderDirection;
protected BlockPos anchor; protected BlockPos anchor;
List<BlockPos> renderOrder;
public Contraption() { public Contraption() {
blocks = new HashMap<>(); blocks = new HashMap<>();
storage = new HashMap<>(); storage = new HashMap<>();
actors = new ArrayList<>(); actors = new ArrayList<>();
renderOrder = new ArrayList<>();
} }
private static List<BlockInfo> getChassisClusterAt(World world, BlockPos pos) { private static List<BlockInfo> getChassisClusterAt(World world, BlockPos pos) {
@ -432,6 +436,10 @@ public abstract class Contraption {
return true; return true;
if (blockState.getBlock() instanceof ShulkerBoxBlock) if (blockState.getBlock() instanceof ShulkerBoxBlock)
return false; return false;
if (blockState.getBlockHardness(world, pos) == -1)
return false;
if (blockState.getBlock() == Blocks.OBSIDIAN)
return false;
return blockState.getPushReaction() != PushReaction.BLOCK; return blockState.getPushReaction() != PushReaction.BLOCK;
} }
@ -481,12 +489,21 @@ public abstract class Contraption {
public void readNBT(World world, CompoundNBT nbt) { public void readNBT(World world, CompoundNBT nbt) {
blocks.clear(); blocks.clear();
renderOrder.clear();
nbt.getList("Blocks", 10).forEach(c -> { nbt.getList("Blocks", 10).forEach(c -> {
CompoundNBT comp = (CompoundNBT) c; CompoundNBT comp = (CompoundNBT) c;
BlockInfo info = new BlockInfo(NBTUtil.readBlockPos(comp.getCompound("Pos")), BlockInfo info = new BlockInfo(NBTUtil.readBlockPos(comp.getCompound("Pos")),
NBTUtil.readBlockState(comp.getCompound("Block")), NBTUtil.readBlockState(comp.getCompound("Block")),
comp.contains("Data") ? comp.getCompound("Data") : null); comp.contains("Data") ? comp.getCompound("Data") : null);
blocks.put(info.pos, info); blocks.put(info.pos, info);
if (world.isRemote) {
BlockRenderLayer renderLayer = info.state.getBlock().getRenderLayer();
if (renderLayer == BlockRenderLayer.TRANSLUCENT)
renderOrder.add(info.pos);
else
renderOrder.add(0, info.pos);
}
}); });
actors.clear(); actors.clear();
@ -626,7 +643,7 @@ public abstract class Contraption {
} }
} }
public AxisAlignedBB getCollisionBoxFront() { public AxisAlignedBB getCollisionBox() {
return constructCollisionBox; return constructCollisionBox;
} }

View file

@ -260,7 +260,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (this.isAddedToWorld() && !this.world.isRemote && world instanceof ServerWorld) if (this.isAddedToWorld() && !this.world.isRemote && world instanceof ServerWorld)
((ServerWorld) this.world).chunkCheck(this); // Forge - Process chunk registration after moving. ((ServerWorld) this.world).chunkCheck(this); // Forge - Process chunk registration after moving.
if (contraption != null) { if (contraption != null) {
AxisAlignedBB cbox = contraption.getCollisionBoxFront(); AxisAlignedBB cbox = contraption.getCollisionBox();
if (cbox != null) if (cbox != null)
this.setBoundingBox(cbox.offset(x, y, z)); this.setBoundingBox(cbox.offset(x, y, z));
} }
@ -401,4 +401,13 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
ce.roll = packet.roll; ce.roll = packet.roll;
} }
@Override
public void setMotion(Vec3d motionIn) {
// Make sure nothing can move contraptions out of the way
}
public void setContraptionMotion(Vec3d vec) {
super.setMotion(vec);
}
} }

View file

@ -74,6 +74,7 @@ public class ContraptionEntityRenderer extends EntityRenderer<ContraptionEntity>
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO); Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
TessellatorHelper.prepareFastRender(); TessellatorHelper.prepareFastRender();
GlStateManager.enableCull();
TessellatorHelper.begin(DefaultVertexFormats.BLOCK); TessellatorHelper.begin(DefaultVertexFormats.BLOCK);
ContraptionRenderer.render(entity.world, entity.getContraption(), superByteBuffer -> { ContraptionRenderer.render(entity.world, entity.getContraption(), superByteBuffer -> {
superByteBuffer.translate(-rotationOffset.x, -rotationOffset.y, -rotationOffset.z); superByteBuffer.translate(-rotationOffset.x, -rotationOffset.y, -rotationOffset.z);
@ -87,6 +88,7 @@ public class ContraptionEntityRenderer extends EntityRenderer<ContraptionEntity>
}, Tessellator.getInstance().getBuffer()); }, Tessellator.getInstance().getBuffer());
TessellatorHelper.draw(); TessellatorHelper.draw();
GlStateManager.disableCull();
GlStateManager.popMatrix(); GlStateManager.popMatrix();
GlStateManager.shadeModel(7424); GlStateManager.shadeModel(7424);
GlStateManager.alphaFunc(516, 0.1F); GlStateManager.alphaFunc(516, 0.1F);

View file

@ -17,6 +17,7 @@ import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockPos.MutableBlockPos; import net.minecraft.util.math.BlockPos.MutableBlockPos;
import net.minecraft.world.LightType; import net.minecraft.world.LightType;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -48,7 +49,8 @@ public class ContraptionRenderer {
for (BlockInfo info : c.blocks.values()) for (BlockInfo info : c.blocks.values())
renderWorld.setBlockState(info.pos, info.state); renderWorld.setBlockState(info.pos, info.state);
for (BlockInfo info : c.blocks.values()) { for (BlockPos pos : c.renderOrder) {
BlockInfo info = c.blocks.get(pos);
IBakedModel originalModel = dispatcher.getModelForState(info.state); IBakedModel originalModel = dispatcher.getModelForState(info.state);
blockRenderer.renderModel(renderWorld, originalModel, info.state, info.pos, builder, true, random, 42, blockRenderer.renderModel(renderWorld, originalModel, info.state, info.pos, builder, true, random, 42,
EmptyModelData.INSTANCE); EmptyModelData.INSTANCE);

View file

@ -36,7 +36,8 @@ public class BearingContraption extends Contraption {
@Override @Override
public void add(BlockPos pos, Pair<BlockInfo, TileEntity> capture) { public void add(BlockPos pos, Pair<BlockInfo, TileEntity> capture) {
if (AllBlockTags.WINDMILL_SAILS.matches(capture.getKey().state)) BlockPos localPos = pos.subtract(anchor);
if (!blocks.containsKey(localPos) && AllBlockTags.WINDMILL_SAILS.matches(capture.getKey().state))
sailBlocks++; sailBlocks++;
super.add(pos, capture); super.add(pos, capture);
} }

View file

@ -146,6 +146,8 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
return; return;
if (contraption.getLeft() == null) if (contraption.getLeft() == null)
return; return;
if (contraption.getLeft().blocks.isEmpty())
return;
BlockPos anchor = pos.offset(direction); BlockPos anchor = pos.offset(direction);
contraption.getLeft().removeBlocksFromWorld(world, BlockPos.ZERO); contraption.getLeft().removeBlocksFromWorld(world, BlockPos.ZERO);
@ -229,6 +231,9 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
clientMinuteAngleDiff = AngleHelper.getShortestAngleDiff(minuteAngleBefore, minuteAngle); clientMinuteAngleDiff = AngleHelper.getShortestAngleDiff(minuteAngleBefore, minuteAngle);
hourAngle = hourAngleBefore; hourAngle = hourAngleBefore;
minuteAngle = minuteAngleBefore; minuteAngle = minuteAngleBefore;
} else {
hourHand = null;
minuteHand = null;
} }
} }
@ -245,7 +250,7 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
@Override @Override
public float getInterpolatedAngle(float partialTicks) { public float getInterpolatedAngle(float partialTicks) {
if (hourHand != null && hourHand.isStalled()) if (hourHand == null || hourHand.isStalled())
partialTicks = 0; partialTicks = 0;
return MathHelper.lerp(partialTicks, hourAngle, hourAngle + getHourArmSpeed()); return MathHelper.lerp(partialTicks, hourAngle, hourAngle + getHourArmSpeed());
} }

View file

@ -16,6 +16,11 @@ public class MechanicalBearingBlock extends BearingBlock implements IWithTileEnt
return new MechanicalBearingTileEntity(); return new MechanicalBearingTileEntity();
} }
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
withTileEntityDo(worldIn, pos, MechanicalBearingTileEntity::neighbourChanged);
}
@Override @Override
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos, public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) { boolean isMoving) {

View file

@ -98,13 +98,16 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
public void readClientUpdate(CompoundNBT tag) { public void readClientUpdate(CompoundNBT tag) {
float angleBefore = angle; float angleBefore = angle;
super.readClientUpdate(tag); super.readClientUpdate(tag);
clientAngleDiff = AngleHelper.getShortestAngleDiff(angleBefore, angle); if (running) {
angle = angleBefore; clientAngleDiff = AngleHelper.getShortestAngleDiff(angleBefore, angle);
angle = angleBefore;
} else
movedContraption = null;
} }
@Override @Override
public float getInterpolatedAngle(float partialTicks) { public float getInterpolatedAngle(float partialTicks) {
if (movedContraption != null && movedContraption.isStalled()) if (movedContraption == null || movedContraption.isStalled())
partialTicks = 0; partialTicks = 0;
return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed()); return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed());
} }
@ -133,6 +136,8 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
return; return;
if (isWindmill && contraption.getSailBlocks() == 0) if (isWindmill && contraption.getSailBlocks() == 0)
return; return;
if (contraption.blocks.isEmpty())
return;
contraption.removeBlocksFromWorld(world, BlockPos.ZERO); contraption.removeBlocksFromWorld(world, BlockPos.ZERO);
movedContraption = ContraptionEntity.createStationary(world, contraption).controlledBy(this); movedContraption = ContraptionEntity.createStationary(world, contraption).controlledBy(this);
BlockPos anchor = pos.offset(direction); BlockPos anchor = pos.offset(direction);

View file

@ -136,7 +136,8 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
if (running) { if (running) {
clientOffsetDiff = offset - offsetBefore; clientOffsetDiff = offset - offsetBefore;
offset = offsetBefore; offset = offsetBefore;
} } else
movedContraption = null;
if (tag.contains("ForceMovement")) if (tag.contains("ForceMovement"))
if (movedContraption != null) if (movedContraption != null)
@ -157,9 +158,9 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
protected void applyContraptionMotion() { protected void applyContraptionMotion() {
if (movedContraption.isStalled()) if (movedContraption.isStalled())
movedContraption.setMotion(Vec3d.ZERO); movedContraption.setContraptionMotion(Vec3d.ZERO);
else else
movedContraption.setMotion(getMotionVector()); movedContraption.setContraptionMotion(getMotionVector());
} }
protected void applyContraptionPosition() { protected void applyContraptionPosition() {

View file

@ -2,6 +2,7 @@ package com.simibubi.create.modules.contraptions.components.contraptions.piston;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity;
import com.simibubi.create.modules.contraptions.components.contraptions.piston.MechanicalPistonBlock.PistonState; import com.simibubi.create.modules.contraptions.components.contraptions.piston.MechanicalPistonBlock.PistonState;
@ -82,10 +83,13 @@ public class MechanicalPistonTileEntity extends LinearActuatorTileEntity {
@Override @Override
public float getMovementSpeed() { public float getMovementSpeed() {
float movementSpeed = getSpeed() / 512f;
if (world.isRemote)
movementSpeed *= ServerSpeedProvider.get();
Direction pistonDirection = getBlockState().get(BlockStateProperties.FACING); Direction pistonDirection = getBlockState().get(BlockStateProperties.FACING);
int movementModifier = int movementModifier =
pistonDirection.getAxisDirection().getOffset() * (pistonDirection.getAxis() == Axis.Z ? -1 : 1); pistonDirection.getAxisDirection().getOffset() * (pistonDirection.getAxis() == Axis.Z ? -1 : 1);
return super.getMovementSpeed() * -movementModifier; return movementSpeed * -movementModifier + clientOffsetDiff / 2f;
} }
@Override @Override
@ -99,7 +103,8 @@ public class MechanicalPistonTileEntity extends LinearActuatorTileEntity {
@Override @Override
protected Vec3d toMotionVector(float speed) { protected Vec3d toMotionVector(float speed) {
return new Vec3d(getBlockState().get(BlockStateProperties.FACING).getDirectionVec()).scale(speed); Direction pistonDirection = getBlockState().get(BlockStateProperties.FACING);
return new Vec3d(pistonDirection.getDirectionVec()).scale(speed);
} }
@Override @Override

View file

@ -32,7 +32,7 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo;
public class PistonContraption extends Contraption { public class PistonContraption extends Contraption {
protected AxisAlignedBB pistonCollisionBox; protected AxisAlignedBB pistonExtensionCollisionBox;
protected int extensionLength; protected int extensionLength;
protected int initialExtensionProgress; protected int initialExtensionProgress;
@ -100,33 +100,39 @@ public class PistonContraption extends Contraption {
return false; return false;
} }
anchor = pos.offset(direction, initialExtensionProgress + 1);
extensionLength = extensionsInBack + extensionsInFront; extensionLength = extensionsInBack + extensionsInFront;
initialExtensionProgress = extensionsInFront; initialExtensionProgress = extensionsInFront;
pistonCollisionBox = new AxisAlignedBB(end.offset(direction, -extensionsInFront)); pistonExtensionCollisionBox = new AxisAlignedBB(end.offset(direction, -extensionsInFront).subtract(anchor));
anchor = pos.offset(direction, initialExtensionProgress + 1);
if (extensionLength == 0) if (extensionLength == 0)
return false; return false;
constructCollisionBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
for (BlockInfo pole : poles) { for (BlockInfo pole : poles) {
BlockPos polePos = pole.pos.offset(direction, -extensionsInFront).subtract(anchor); BlockPos relPos = pole.pos.offset(direction, -extensionsInFront);
blocks.put(polePos, new BlockInfo(polePos, pole.state, null)); BlockPos localPos = relPos.subtract(anchor);
pistonCollisionBox = pistonCollisionBox.union(new AxisAlignedBB(polePos)); blocks.put(localPos, new BlockInfo(localPos, pole.state, null));
pistonExtensionCollisionBox = pistonExtensionCollisionBox.union(new AxisAlignedBB(localPos));
} }
constructCollisionBox = new AxisAlignedBB(BlockPos.ZERO.offset(direction, -initialExtensionProgress));
return true; return true;
} }
@Override @Override
protected boolean addToInitialFrontier(World world, BlockPos pos, Direction direction, List<BlockPos> frontier) { protected boolean addToInitialFrontier(World world, BlockPos pos, Direction direction, List<BlockPos> frontier) {
for (int offset = 1; offset <= AllConfigs.SERVER.kinetics.maxChassisRange.get(); offset++) { frontier.clear();
BlockPos currentPos = pos.offset(direction, offset); boolean sticky = STICKY_MECHANICAL_PISTON.typeOf(world.getBlockState(pos.offset(orientation, -1)));
if (!world.isAreaLoaded(currentPos, 1)) boolean retracting = direction != orientation;
return false; if (retracting && !sticky)
if (!world.isBlockPresent(currentPos)) return true;
for (int offset = 0; offset <= AllConfigs.SERVER.kinetics.maxChassisRange.get(); offset++) {
if (offset == 1 && retracting)
break; break;
BlockPos currentPos = pos.offset(orientation, offset + initialExtensionProgress);
if (!world.isBlockPresent(currentPos))
return false;
BlockState state = world.getBlockState(currentPos); BlockState state = world.getBlockState(currentPos);
if (state.getMaterial().isReplaceable()) if (state.getMaterial().isReplaceable())
break; break;
@ -135,7 +141,7 @@ public class PistonContraption extends Contraption {
if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(state) && state.get(FACING) == direction.getOpposite()) if (AllBlocks.MECHANICAL_PISTON_HEAD.typeOf(state) && state.get(FACING) == direction.getOpposite())
break; break;
if (!canPush(world, currentPos, direction)) if (!canPush(world, currentPos, direction))
return false; return retracting;
frontier.add(currentPos); frontier.add(currentPos);
} }
return true; return true;
@ -149,7 +155,7 @@ public class PistonContraption extends Contraption {
@Override @Override
public void disassemble(World world, BlockPos offset, float yaw, float pitch) { public void disassemble(World world, BlockPos offset, float yaw, float pitch) {
super.disassemble(world, offset, yaw, pitch, (pos, state) -> { super.disassemble(world, offset, yaw, pitch, (pos, state) -> {
BlockPos pistonPos = anchor.offset(orientation, -initialExtensionProgress - 1); BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState pistonState = world.getBlockState(pistonPos); BlockState pistonState = world.getBlockState(pistonPos);
TileEntity te = world.getTileEntity(pistonPos); TileEntity te = world.getTileEntity(pistonPos);
if (pos.equals(pistonPos)) { if (pos.equals(pistonPos)) {
@ -167,7 +173,7 @@ public class PistonContraption extends Contraption {
@Override @Override
public void removeBlocksFromWorld(IWorld world, BlockPos offset) { public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
super.removeBlocksFromWorld(world, offset, (pos, state) -> { super.removeBlocksFromWorld(world, offset, (pos, state) -> {
BlockPos pistonPos = anchor.offset(orientation, -initialExtensionProgress - 1); BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState blockState = world.getBlockState(pos); BlockState blockState = world.getBlockState(pos);
if (pos.equals(pistonPos) && blockState.getBlock() instanceof MechanicalPistonBlock) { if (pos.equals(pistonPos) && blockState.getBlock() instanceof MechanicalPistonBlock) {
world.setBlockState(pos, blockState.with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66); world.setBlockState(pos, blockState.with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66);
@ -184,15 +190,15 @@ public class PistonContraption extends Contraption {
initialExtensionProgress = nbt.getInt("InitialLength"); initialExtensionProgress = nbt.getInt("InitialLength");
orientation = Direction.byIndex(nbt.getInt("Orientation")); orientation = Direction.byIndex(nbt.getInt("Orientation"));
if (nbt.contains("BoundsBack")) if (nbt.contains("BoundsBack"))
pistonCollisionBox = NBTHelper.readAABB(nbt.getList("BoundsBack", 5)); pistonExtensionCollisionBox = NBTHelper.readAABB(nbt.getList("BoundsBack", 5));
} }
@Override @Override
public CompoundNBT writeNBT() { public CompoundNBT writeNBT() {
CompoundNBT nbt = super.writeNBT(); CompoundNBT nbt = super.writeNBT();
if (pistonCollisionBox != null) { if (pistonExtensionCollisionBox != null) {
ListNBT bb = NBTHelper.writeAABB(pistonCollisionBox); ListNBT bb = NBTHelper.writeAABB(pistonExtensionCollisionBox);
nbt.put("BoundsBack", bb); nbt.put("BoundsBack", bb);
} }
nbt.putInt("InitialLength", initialExtensionProgress); nbt.putInt("InitialLength", initialExtensionProgress);
@ -202,7 +208,8 @@ public class PistonContraption extends Contraption {
return nbt; return nbt;
} }
public AxisAlignedBB getCollisionBoxBack() { @Override
return pistonCollisionBox; public AxisAlignedBB getCollisionBox() {
return super.getCollisionBox().union(pistonExtensionCollisionBox);
} }
} }

View file

@ -32,15 +32,18 @@ public class PulleyTileEntity extends LinearActuatorTileEntity {
if (offset <= 0 && getSpeed() < 0) if (offset <= 0 && getSpeed() < 0)
return; return;
// Collect Construct
if (!world.isRemote) { if (!world.isRemote) {
BlockPos anchor = pos.down((int) (offset + 1));
PulleyContraption contraption = PulleyContraption.assemblePulleyAt(world, anchor, (int) offset);
if (contraption == null && getSpeed() > 0)
return;
for (int i = ((int) offset); i > 0; i--) { for (int i = ((int) offset); i > 0; i--) {
BlockPos offset = pos.down(i); BlockPos offset = pos.down(i);
world.setBlockState(offset, Blocks.AIR.getDefaultState(), 66); world.setBlockState(offset, Blocks.AIR.getDefaultState(), 66);
} }
// Collect Construct
BlockPos anchor = pos.down((int) (offset + 1));
PulleyContraption contraption = PulleyContraption.assemblePulleyAt(world, anchor, (int) offset);
if (contraption != null && !contraption.blocks.isEmpty()) { if (contraption != null && !contraption.blocks.isEmpty()) {
contraption.removeBlocksFromWorld(world, BlockPos.ZERO); contraption.removeBlocksFromWorld(world, BlockPos.ZERO);
movedContraption = ContraptionEntity.createStationary(world, contraption).controlledBy(this); movedContraption = ContraptionEntity.createStationary(world, contraption).controlledBy(this);

View file

@ -1,6 +1,7 @@
package com.simibubi.create.modules.contraptions.components.waterwheel; package com.simibubi.create.modules.contraptions.components.waterwheel;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock; import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -65,6 +66,10 @@ public class WaterWheelBlock extends HorizontalKineticBlock {
@Override @Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
updateAllSides(state, worldIn, pos);
}
public void updateAllSides(BlockState state, World worldIn, BlockPos pos) {
for (Direction d : Direction.values()) for (Direction d : Direction.values())
updateFlowAt(state, worldIn, pos, d); updateFlowAt(state, worldIn, pos, d);
updateWheelSpeed(worldIn, pos); updateWheelSpeed(worldIn, pos);
@ -99,15 +104,16 @@ public class WaterWheelBlock extends HorizontalKineticBlock {
flow = flowVec.y > 0 ^ !clockwise ? -flowVec.y * clockwiseMultiplier : -flowVec.y; flow = flowVec.y > 0 ^ !clockwise ? -flowVec.y * clockwiseMultiplier : -flowVec.y;
} }
te.setFlow(f, (int) (flow * 5)); te.setFlow(f, (float) (flow * AllConfigs.SERVER.kinetics.waterWheelSpeed.get() / 2f));
} }
private void updateWheelSpeed(IWorld world, BlockPos pos) { private void updateWheelSpeed(IWorld world, BlockPos pos) {
if (world.isRemote()) if (world.isRemote())
return; return;
WaterWheelTileEntity te = (WaterWheelTileEntity) world.getTileEntity(pos); TileEntity tileEntity = world.getTileEntity(pos);
if (te == null) if (!(tileEntity instanceof WaterWheelTileEntity))
return; return;
WaterWheelTileEntity te = (WaterWheelTileEntity) tileEntity;
te.updateGeneratedRotation(); te.updateGeneratedRotation();
} }

View file

@ -3,6 +3,7 @@ package com.simibubi.create.modules.contraptions.components.waterwheel;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.contraptions.base.GeneratingKineticTileEntity; import com.simibubi.create.modules.contraptions.base.GeneratingKineticTileEntity;
@ -12,14 +13,14 @@ import net.minecraft.util.math.AxisAlignedBB;
public class WaterWheelTileEntity extends GeneratingKineticTileEntity { public class WaterWheelTileEntity extends GeneratingKineticTileEntity {
private Map<Direction, Integer> flows; private Map<Direction, Float> flows;
public WaterWheelTileEntity() { public WaterWheelTileEntity() {
super(AllTileEntities.WATER_WHEEL.type); super(AllTileEntities.WATER_WHEEL.type);
flows = new HashMap<Direction, Integer>(); flows = new HashMap<>();
for (Direction d : Direction.values()) for (Direction d : Direction.values())
setFlow(d, 0); setFlow(d, 0);
setLazyTickRate(20);
} }
@Override @Override
@ -27,7 +28,7 @@ public class WaterWheelTileEntity extends GeneratingKineticTileEntity {
super.read(compound); super.read(compound);
if (compound.contains("Flows")) { if (compound.contains("Flows")) {
for (Direction d : Direction.values()) for (Direction d : Direction.values())
setFlow(d, compound.getCompound("Flows").getInt(d.getName())); setFlow(d, compound.getCompound("Flows").getFloat(d.getName()));
} }
} }
@ -41,22 +42,29 @@ public class WaterWheelTileEntity extends GeneratingKineticTileEntity {
CompoundNBT flows = new CompoundNBT(); CompoundNBT flows = new CompoundNBT();
for (Direction d : Direction.values()) for (Direction d : Direction.values())
flows.putInt(d.getName(), this.flows.get(d)); flows.putFloat(d.getName(), this.flows.get(d));
compound.put("Flows", flows); compound.put("Flows", flows);
return super.write(compound); return super.write(compound);
} }
public void setFlow(Direction direction, int speed) { public void setFlow(Direction direction, float speed) {
flows.put(direction, speed); flows.put(direction, speed);
} }
@Override @Override
public float getGeneratedSpeed() { public float getGeneratedSpeed() {
float speed = 0; float speed = 0;
for (Integer i : flows.values()) for (Float f : flows.values())
speed += i; speed += f;
return speed; return speed;
} }
@Override
public void lazyTick() {
super.lazyTick();
WaterWheelBlock block = (WaterWheelBlock) AllBlocks.WATER_WHEEL.get();
block.updateAllSides(getBlockState(), world, pos);
}
} }

View file

@ -279,7 +279,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IHaveNoBlockIte
return ActionResultType.SUCCESS; return ActionResultType.SUCCESS;
} }
return super.onWrenched(state, context); return ActionResultType.FAIL;
} }
@Override @Override

View file

@ -102,7 +102,7 @@ public class GaugeInformationRenderer {
private static void addStressTooltip(BlockState state, List<String> tooltip, KineticTileEntity te) { private static void addStressTooltip(BlockState state, List<String> tooltip, KineticTileEntity te) {
String spacing = " "; String spacing = " ";
float stressApplied = te.getStressApplied(); float stressApplied = te.getStressApplied();
if (stressApplied == 0) if (stressApplied == 0 || !StressImpact.isEnabled())
return; return;
String _kineticStatsTitle = Lang.translate("gui.goggles.kinetic_stats"); String _kineticStatsTitle = Lang.translate("gui.goggles.kinetic_stats");
@ -115,8 +115,8 @@ public class GaugeInformationRenderer {
tooltip.add(spacing + GRAY + _stressImpact); tooltip.add(spacing + GRAY + _stressImpact);
String addedStress = AQUA + "" + format(stressApplied) + _stressUnit + " " + DARK_GRAY + _atCurrentSpeed; String addedStress = AQUA + "" + format(stressApplied) + _stressUnit + " " + DARK_GRAY + _atCurrentSpeed;
String addedStressAtBase = GRAY + "" + format(stressApplied * Math.abs(te.getSpeed())) + _stressUnit + " " String addedStressAtBase =
+ DARK_GRAY + _baseValue; GRAY + "" + format(stressApplied * Math.abs(te.getSpeed())) + _stressUnit + " " + DARK_GRAY + _baseValue;
tooltip.add(spacing + " " + addedStress); tooltip.add(spacing + " " + addedStress);
tooltip.add(spacing + " " + addedStressAtBase); tooltip.add(spacing + " " + addedStressAtBase);
} }
@ -124,7 +124,7 @@ public class GaugeInformationRenderer {
private static void addGeneratorTooltip(BlockState state, List<String> tooltip, GeneratingKineticTileEntity te) { private static void addGeneratorTooltip(BlockState state, List<String> tooltip, GeneratingKineticTileEntity te) {
String spacing = " "; String spacing = " ";
float addedStressCapacity = te.getAddedStressCapacity(); float addedStressCapacity = te.getAddedStressCapacity();
if (addedStressCapacity == 0) if (addedStressCapacity == 0 || !StressImpact.isEnabled())
return; return;
String _stressUnit = Lang.translate("generic.unit.stress"); String _stressUnit = Lang.translate("generic.unit.stress");
@ -144,8 +144,8 @@ public class GaugeInformationRenderer {
if (actualSpeed != 0) if (actualSpeed != 0)
relativeCap = addedStressCapacity * actualSpeed; relativeCap = addedStressCapacity * actualSpeed;
String addedCapacity = AQUA + "" + format(addedStressCapacity) + _stressUnit + " " + DARK_GRAY String addedCapacity =
+ _atCurrentSpeed; AQUA + "" + format(addedStressCapacity) + _stressUnit + " " + DARK_GRAY + _atCurrentSpeed;
String addedCapacityAtBase = GRAY + "" + format(relativeCap) + _stressUnit + " " + DARK_GRAY + _baseValue; String addedCapacityAtBase = GRAY + "" + format(relativeCap) + _stressUnit + " " + DARK_GRAY + _baseValue;
tooltip.add(spacing + " " + addedCapacity); tooltip.add(spacing + " " + addedCapacity);
tooltip.add(spacing + " " + addedCapacityAtBase); tooltip.add(spacing + " " + addedCapacityAtBase);
@ -170,6 +170,10 @@ public class GaugeInformationRenderer {
if (AllBlocks.STRESS_GAUGE.typeOf(state)) { if (AllBlocks.STRESS_GAUGE.typeOf(state)) {
if (!(te instanceof StressGaugeTileEntity)) if (!(te instanceof StressGaugeTileEntity))
return; return;
if (!StressImpact.isEnabled()) {
tooltip.clear();
return;
}
StressGaugeTileEntity stressGauge = (StressGaugeTileEntity) te; StressGaugeTileEntity stressGauge = (StressGaugeTileEntity) te;
List<String> stressLevels = Lang.translatedOptions("tooltip.stressImpact", "low", "medium", "high"); List<String> stressLevels = Lang.translatedOptions("tooltip.stressImpact", "low", "medium", "high");
double stress = stressGauge.currentStress; double stress = stressGauge.currentStress;

View file

@ -2,6 +2,7 @@ package com.simibubi.create.modules.contraptions.relays.gauge;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.ColorHelper;
import com.simibubi.create.modules.contraptions.base.IRotate.StressImpact;
public class StressGaugeTileEntity extends GaugeTileEntity { public class StressGaugeTileEntity extends GaugeTileEntity {
@ -13,7 +14,9 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
public void sync(float maxStress, float currentStress) { public void sync(float maxStress, float currentStress) {
super.sync(maxStress, currentStress); super.sync(maxStress, currentStress);
if (overStressed) if (!StressImpact.isEnabled())
dialTarget = 0;
else if (overStressed)
dialTarget = 1.125f; dialTarget = 1.125f;
else if (maxStress == 0) else if (maxStress == 0)
dialTarget = 0; dialTarget = 0;

View file

@ -3,9 +3,8 @@
"parent": "block/block", "parent": "block/block",
"textures": { "textures": {
"0": "create:block/axis", "0": "create:block/axis",
"1": "create:block/axis_top",
"2": "create:block/andesite_casing_short", "2": "create:block/andesite_casing_short",
"3": "block/spruce_log", "3": "create:block/brass_block",
"particle": "create:block/axis" "particle": "create:block/axis"
}, },
"elements": [ "elements": [
@ -14,12 +13,12 @@
"from": [5, 5, 11], "from": [5, 5, 11],
"to": [11, 11, 14], "to": [11, 11, 14],
"faces": { "faces": {
"north": {"uv": [0, 0, 6, 6], "texture": "#3"}, "north": {"uv": [5, 0, 11, 6], "texture": "#3"},
"east": {"uv": [0, 0, 3, 6], "texture": "#3"}, "east": {"uv": [13, 6, 16, 12], "texture": "#3"},
"south": {"uv": [0, 0, 6, 6], "texture": "#3"}, "south": {"uv": [3, 4, 9, 10], "texture": "#3"},
"west": {"uv": [0, 0, 3, 6], "texture": "#3"}, "west": {"uv": [0, 3, 3, 9], "texture": "#3"},
"up": {"uv": [0, 0, 6, 3], "texture": "#3"}, "up": {"uv": [6, 0, 12, 3], "texture": "#3"},
"down": {"uv": [0, 0, 6, 3], "texture": "#3"} "down": {"uv": [6, 13, 12, 16], "texture": "#3"}
} }
}, },
{ {
@ -69,5 +68,5 @@
"name": "shaft_half", "name": "shaft_half",
"origin": [8, 8, 8], "origin": [8, 8, 8],
"children": [0] "children": [0]
}, 1, 2, 3, 4] }, 1, 2, 3]
} }

View file

@ -5,7 +5,7 @@
"0": "create:block/axis", "0": "create:block/axis",
"1": "create:block/axis_top", "1": "create:block/axis_top",
"2": "create:block/andesite_casing_short", "2": "create:block/andesite_casing_short",
"3": "block/spruce_log", "4": "create:block/brass_block",
"particle": "create:block/axis" "particle": "create:block/axis"
}, },
"elements": [ "elements": [
@ -27,12 +27,12 @@
"from": [5, 5, 11], "from": [5, 5, 11],
"to": [11, 11, 14], "to": [11, 11, 14],
"faces": { "faces": {
"north": {"uv": [0, 0, 6, 6], "texture": "#3"}, "north": {"uv": [0, 0, 6, 6], "texture": "#4"},
"east": {"uv": [0, 0, 3, 6], "texture": "#3"}, "east": {"uv": [13, 5, 16, 11], "texture": "#4"},
"south": {"uv": [0, 0, 6, 6], "texture": "#3"}, "south": {"uv": [5, 5, 11, 11], "texture": "#4"},
"west": {"uv": [0, 0, 3, 6], "texture": "#3"}, "west": {"uv": [0, 5, 3, 11], "texture": "#4"},
"up": {"uv": [0, 0, 6, 3], "texture": "#3"}, "up": {"uv": [5, 0, 11, 3], "texture": "#4"},
"down": {"uv": [0, 0, 6, 3], "texture": "#3"} "down": {"uv": [4, 13, 10, 16], "texture": "#4"}
} }
}, },
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 261 B

View file

@ -1,19 +1,19 @@
{ {
"type": "crafting_shaped", "type": "crafting_shaped",
"pattern": [ "pattern": [
" L ", " A ",
"PPP", "PPP",
" A" " L"
], ],
"key": { "key": {
"L": { "L": {
"tag": "minecraft:logs" "item": "create:andesite_alloy"
}, },
"P": { "P": {
"tag": "minecraft:planks" "tag": "minecraft:planks"
}, },
"A": { "A": {
"item": "create:andesite_alloy" "tag": "forge:plates/brass"
} }
}, },
"result": { "result": {

View file

@ -1,34 +0,0 @@
{
"type": "create:mechanical_crafting",
"pattern": [
" PPP ",
"PSBSP",
"PBCBP",
"PSBSP",
" PPP "
],
"key": {
"P": {
"item": "minecraft:polished_andesite"
},
"S": {
"tag": "forge:rods/wooden"
},
"C": {
"item": "create:large_cogwheel"
},
"B": {
"item": "create:andesite_alloy"
}
},
"result": {
"item": "create:crushing_wheel",
"count": 1
},
"conditions": [
{
"type": "create:module",
"module": "contraptions"
}
]
}