Merge branch 'mc1.18/0.5.1' into mc1.18/0.5.1-repolish

This commit is contained in:
simibubi 2023-03-27 16:03:10 +02:00
commit 41c462ab1a
45 changed files with 1629 additions and 89 deletions

View File

@ -606,6 +606,7 @@
"item.create.chocolate_glazed_berries": "s\u01DD\u0131\u0279\u0279\u01DD\u15FA p\u01DDz\u0250\u05DF\u2141 \u01DD\u0287\u0250\u05DFo\u0254o\u0265\u0186",
"item.create.chromatic_compound": "punod\u026Fo\u0186 \u0254\u0131\u0287\u0250\u026Fo\u0279\u0265\u0186",
"item.create.cinder_flour": "\u0279no\u05DF\u2132 \u0279\u01DDpu\u0131\u0186",
"item.create.clipboard": "p\u0279\u0250oqd\u0131\u05DF\u0186",
"item.create.copper_backtank": "\u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186",
"item.create.copper_backtank_placeable": "\u01DD\u05DFq\u0250\u01DD\u0254\u0250\u05DF\u0500 \u029Eu\u0250\u0287\u029E\u0254\u0250\u15FA \u0279\u01DDddo\u0186",
"item.create.copper_diving_boots": "s\u0287oo\u15FA bu\u0131\u028C\u0131\u15E1 \u0279\u01DDddo\u0186",

View File

@ -613,6 +613,7 @@
"item.create.chocolate_glazed_berries": "Chocolate Glazed Berries",
"item.create.chromatic_compound": "Chromatic Compound",
"item.create.cinder_flour": "Cinder Flour",
"item.create.clipboard": "Clipboard",
"item.create.copper_backtank": "Copper Backtank",
"item.create.copper_backtank_placeable": "Copper Backtank Placeable",
"item.create.copper_diving_boots": "Copper Diving Boots",
@ -862,8 +863,6 @@
"advancement.create.train_portal.desc": "Ride a Train through a Nether portal",
"advancement.create.track_crafting_factory": "Track Factory",
"advancement.create.track_crafting_factory.desc": "Produce more than 1000 Train Tracks with the same Mechanical Press",
"advancement.create.long_bend": "The Longest Bend",
"advancement.create.long_bend.desc": "Create a curved track section that spans more than 30 blocks in length",
"advancement.create.long_train": "Ambitious Endeavours",
"advancement.create.long_train.desc": "Create a Train with at least 6 carriages",
"advancement.create.long_travel": "Field Trip",
@ -1165,6 +1164,7 @@
"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.gui.clipboard.erase_checked": "Erase checked items",
"create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s",
"create.schematicAndQuill.firstPos": "First position set.",
@ -1245,7 +1245,7 @@
"create.gui.schematicannon.option.skipMissing": "Skip missing Blocks",
"create.gui.schematicannon.option.skipBlockEntities": "Protect Block Entities",
"create.gui.schematicannon.slot.gunpowder": "Add gunpowder to fuel the cannon",
"create.gui.schematicannon.slot.listPrinter": "Place books here to print a Checklist for your Schematic",
"create.gui.schematicannon.slot.listPrinter": "Place a Clipboard or Book here to print a Checklist for your Schematic",
"create.gui.schematicannon.slot.schematic": "Add your Schematic here. Make sure it is deployed at a specific location.",
"create.gui.schematicannon.option.skipMissing.description": "If the cannon cannot find a required Block for placement, it will continue at the next Location.",
"create.gui.schematicannon.option.skipBlockEntities.description": "The cannon will avoid replacing data holding blocks such as Chests.",
@ -1611,6 +1611,7 @@
"create.track.turn_start": "Cannot start connection from a Turn",
"create.track.not_enough_tracks": "Not holding enough tracks",
"create.track.not_enough_pavement": "Not holding enough pavement blocks",
"create.track.hold_for_smooth_curve": "Hold %1$s for maximized turn",
"create.portal_track.failed": "Cannot place portal track:",
"create.portal_track.missing": "Target portal not generated yet",

View File

@ -0,0 +1,26 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/clipboard"
},
"overrides": [
{
"predicate": {
"create:clipboard_type": 0.0
},
"model": "create:item/clipboard_0"
},
{
"predicate": {
"create:clipboard_type": 1.0
},
"model": "create:item/clipboard_1"
},
{
"predicate": {
"create:clipboard_type": 2.0
},
"model": "create:item/clipboard_2"
}
]
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/empty_clipboard"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/clipboard"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "create:item/clipboard_and_quill"
}
}

View File

@ -1,30 +0,0 @@
{
"parent": "create:track_crafting_factory",
"display": {
"icon": {
"item": "create:track"
},
"title": {
"translate": "advancement.create.long_bend"
},
"description": {
"color": "#DBA213",
"translate": "advancement.create.long_bend.desc"
},
"frame": "goal",
"show_toast": true,
"announce_to_chat": true,
"hidden": false
},
"criteria": {
"0": {
"trigger": "create:long_bend_builtin",
"conditions": {}
}
},
"requirements": [
[
"0"
]
]
}

View File

@ -1,5 +1,5 @@
{
"parent": "create:long_bend",
"parent": "create:track_crafting_factory",
"display": {
"icon": {
"item": "minecraft:minecart"

View File

@ -0,0 +1,34 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"create:crafting/appliances/clipboard"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"items": [
"create:andesite_alloy"
]
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "create:crafting/appliances/clipboard"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View File

@ -0,0 +1,34 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"create:crafting/appliances/clipboard_clear"
]
},
"criteria": {
"has_item": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"items": [
"create:clipboard"
]
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "create:crafting/appliances/clipboard_clear"
}
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
]
}

View File

@ -0,0 +1,22 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"A",
"P",
"G"
],
"key": {
"G": {
"tag": "minecraft:planks"
},
"P": {
"item": "minecraft:paper"
},
"A": {
"item": "create:andesite_alloy"
}
},
"result": {
"item": "create:clipboard"
}
}

View File

@ -0,0 +1,11 @@
{
"type": "minecraft:crafting_shapeless",
"ingredients": [
{
"item": "create:clipboard"
}
],
"result": {
"item": "create:clipboard"
}
}

View File

@ -27,6 +27,7 @@ import com.simibubi.create.content.contraptions.components.actors.PloughMovement
import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.content.contraptions.components.actors.PortableStorageInterfaceMovement;
import com.simibubi.create.content.contraptions.components.actors.RollerBlock;
import com.simibubi.create.content.contraptions.components.actors.RollerBlockItem;
import com.simibubi.create.content.contraptions.components.actors.RollerMovementBehaviour;
import com.simibubi.create.content.contraptions.components.actors.SawMovementBehaviour;
import com.simibubi.create.content.contraptions.components.actors.SeatBlock;
@ -929,6 +930,7 @@ public class AllBlocks {
REGISTRATE.block("copper_valve_handle", ValveHandleBlock::copper)
.transform(pickaxeOnly())
.transform(BuilderTransformers.valveHandle(null))
.transform(BlockStressDefaults.setCapacity(8.0))
.register();
public static final DyedBlockList<ValveHandleBlock> DYED_VALVE_HANDLES = new DyedBlockList<>(colour -> {
@ -1401,7 +1403,7 @@ public class AllBlocks {
.onRegister(movementBehaviour(new RollerMovementBehaviour()))
.blockstate(BlockStateGen.horizontalBlockProvider(true))
.addLayer(() -> RenderType::cutoutMipped)
.item()
.item(RollerBlockItem::new)
.transform(customItemModel())
.register();

View File

@ -39,6 +39,8 @@ import com.simibubi.create.content.curiosities.armor.BacktankItem;
import com.simibubi.create.content.curiosities.armor.BacktankItem.BacktankBlockItem;
import com.simibubi.create.content.curiosities.armor.DivingBootsItem;
import com.simibubi.create.content.curiosities.armor.DivingHelmetItem;
import com.simibubi.create.content.curiosities.clipboard.ClipboardItem;
import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides;
import com.simibubi.create.content.curiosities.symmetry.SymmetryWandItem;
import com.simibubi.create.content.curiosities.tools.BlueprintItem;
import com.simibubi.create.content.curiosities.tools.ExtendoGripItem;
@ -72,7 +74,10 @@ public class AllItems {
REGISTRATE.creativeModeTab(() -> AllCreativeModeTabs.BASE_CREATIVE_TAB);
}
// Materials
public static final ItemEntry<ClipboardItem> CLIPBOARD = REGISTRATE.item("clipboard", ClipboardItem::new)
.onRegister(ClipboardItem::registerModelOverrides)
.model((c, p) -> ClipboardOverrides.addOverrideModels(c, p))
.register();
public static final ItemEntry<Item> WHEAT_FLOUR =
taggedIngredient("wheat_flour", forgeItemTag("flour/wheat"), forgeItemTag("flour")),

View File

@ -16,6 +16,7 @@ import com.simibubi.create.content.curiosities.weapons.PotatoCannonRenderHandler
import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler;
import com.simibubi.create.content.logistics.trains.GlobalRailwayManager;
import com.simibubi.create.content.logistics.trains.track.TrackPlacement;
import com.simibubi.create.content.schematics.ClientSchematicLoader;
import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler;
import com.simibubi.create.content.schematics.client.SchematicHandler;
@ -109,6 +110,7 @@ public class CreateClient {
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Schematics", SCHEMATIC_HANDLER.getOverlayRenderer());
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Toolboxes", ToolboxHandlerClient.OVERLAY);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Value Settings", VALUE_SETTINGS_HANDLER);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Track Placement", TrackPlacement.OVERLAY);
}
public static void invalidateRenderers() {

View File

@ -1,24 +1,43 @@
package com.simibubi.create.content.contraptions.components.actors;
import java.util.function.Predicate;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.curiosities.tools.ExtendoGripItem;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import com.simibubi.create.foundation.utility.placement.PlacementOffset;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.ForgeMod;
public class RollerBlock extends AttachedActorBlock implements IBE<RollerBlockEntity> {
public static DamageSource damageSourceRoller = new DamageSource("create.mechanical_roller");
private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper());
public RollerBlock(Properties p_i48377_1_) {
super(p_i48377_1_);
}
@ -50,4 +69,85 @@ public class RollerBlock extends AttachedActorBlock implements IBE<RollerBlockEn
return true;
}
@Override
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand,
BlockHitResult ray) {
ItemStack heldItem = player.getItemInHand(hand);
IPlacementHelper placementHelper = PlacementHelpers.get(placementHelperId);
if (!player.isShiftKeyDown() && player.mayBuild()) {
if (placementHelper.matchesItem(heldItem)) {
placementHelper.getOffset(player, world, state, pos, ray)
.placeInWorld(world, (BlockItem) heldItem.getItem(), player, hand, ray);
return InteractionResult.SUCCESS;
}
}
return InteractionResult.PASS;
}
private static class PlacementHelper implements IPlacementHelper {
@Override
public Predicate<ItemStack> getItemPredicate() {
return AllBlocks.MECHANICAL_ROLLER::isIn;
}
@Override
public Predicate<BlockState> getStatePredicate() {
return AllBlocks.MECHANICAL_ROLLER::has;
}
public int attachedSteps(Level world, BlockPos pos, Direction direction) {
BlockPos checkPos = pos.relative(direction);
BlockState state = world.getBlockState(checkPos);
int count = 0;
while (getStatePredicate().test(state)) {
count++;
checkPos = checkPos.relative(direction);
state = world.getBlockState(checkPos);
}
return count;
}
@Override
public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos,
BlockHitResult ray) {
Direction dir = null;
Direction facing = state.getValue(FACING);
for (Direction nearest : Direction.orderedByNearest(player)) {
if (nearest.getAxis() != facing.getClockWise()
.getAxis())
continue;
dir = nearest;
break;
}
int range = AllConfigs.server().curiosities.placementAssistRange.get();
if (player != null) {
AttributeInstance reach = player.getAttribute(ForgeMod.REACH_DISTANCE.get());
if (reach != null && reach.hasModifier(ExtendoGripItem.singleRangeAttributeModifier))
range += 4;
}
int row = attachedSteps(world, pos, dir);
if (row >= range)
return PlacementOffset.fail();
BlockPos newPos = pos.relative(dir, row + 1);
BlockState newState = world.getBlockState(newPos);
if (!state.canSurvive(world, newPos))
return PlacementOffset.fail();
if (newState.getMaterial()
.isReplaceable())
return PlacementOffset.success(newPos, bState -> bState.setValue(FACING, facing));
return PlacementOffset.fail();
}
}
}

View File

@ -0,0 +1,29 @@
package com.simibubi.create.content.contraptions.components.actors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
public class RollerBlockItem extends BlockItem {
public RollerBlockItem(Block pBlock, Properties pProperties) {
super(pBlock, pProperties);
}
@Override
public InteractionResult place(BlockPlaceContext ctx) {
BlockPos clickedPos = ctx.getClickedPos();
Level level = ctx.getLevel();
BlockState blockStateBelow = level.getBlockState(clickedPos.below());
if (!Block.isFaceFull(blockStateBelow.getCollisionShape(level, clickedPos.below()), Direction.UP))
return super.place(ctx);
Direction clickedFace = ctx.getClickedFace();
return super.place(BlockPlaceContext.at(ctx, clickedPos.relative(Direction.UP), clickedFace));
}
}

View File

@ -76,7 +76,8 @@ public class HandCrankBlockEntity extends GeneratingKineticBlockEntity {
@Override
protected Block getStressConfigKey() {
return AllBlocks.HAND_CRANK.get();
return AllBlocks.HAND_CRANK.has(getBlockState()) ? AllBlocks.HAND_CRANK.get()
: AllBlocks.COPPER_VALVE_HANDLE.get();
}
@Override

View File

@ -12,9 +12,13 @@ import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
@ -24,6 +28,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
@ -40,7 +45,8 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IBE<SawBloc
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockState stateForPlacement = super.getStateForPlacement(context);
Direction facing = stateForPlacement.getValue(FACING);
if (facing.getAxis().isVertical())
if (facing.getAxis()
.isVertical())
return stateForPlacement;
return stateForPlacement.setValue(AXIS_ALONG_FIRST_COORDINATE, facing.getAxis() == Axis.X);
}
@ -50,11 +56,34 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IBE<SawBloc
return AllShapes.CASING_12PX.get(state.getValue(FACING));
}
@Override
public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player player, InteractionHand handIn,
BlockHitResult hit) {
if (player.isSpectator() || !player.getItemInHand(handIn)
.isEmpty())
return InteractionResult.PASS;
if (state.getOptionalValue(FACING)
.orElse(Direction.WEST) != Direction.UP)
return InteractionResult.PASS;
return onBlockEntityUse(worldIn, pos, be -> {
for (int i = 0; i < be.inventory.getSlots(); i++) {
ItemStack heldItemStack = be.inventory.getStackInSlot(i);
if (!worldIn.isClientSide && !heldItemStack.isEmpty())
player.getInventory()
.placeItemBackInInventory(heldItemStack);
}
be.inventory.clear();
be.notifyUpdate();
return InteractionResult.SUCCESS;
});
}
@Override
public void entityInside(BlockState state, Level worldIn, BlockPos pos, Entity entityIn) {
if (entityIn instanceof ItemEntity)
return;
if (!new AABB(pos).deflate(.1f).intersects(entityIn.getBoundingBox()))
if (!new AABB(pos).deflate(.1f)
.intersects(entityIn.getBoundingBox()))
return;
withBlockEntityDo(worldIn, pos, be -> {
if (be.getSpeed() == 0)
@ -85,30 +114,33 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IBE<SawBloc
}
public static boolean isHorizontal(BlockState state) {
return state.getValue(FACING).getAxis().isHorizontal();
return state.getValue(FACING)
.getAxis()
.isHorizontal();
}
@Override
public Axis getRotationAxis(BlockState state) {
return isHorizontal(state) ? state.getValue(FACING).getAxis() : super.getRotationAxis(state);
return isHorizontal(state) ? state.getValue(FACING)
.getAxis() : super.getRotationAxis(state);
}
@Override
public boolean hasShaftTowards(LevelReader world, BlockPos pos, BlockState state, Direction face) {
return isHorizontal(state) ? face == state.getValue(FACING).getOpposite()
: super.hasShaftTowards(world, pos, state, face);
return isHorizontal(state) ? face == state.getValue(FACING)
.getOpposite() : super.hasShaftTowards(world, pos, state, face);
}
@Override
public Class<SawBlockEntity> getBlockEntityClass() {
return SawBlockEntity.class;
}
@Override
public BlockEntityType<? extends SawBlockEntity> getBlockEntityType() {
return AllBlockEntityTypes.SAW.get();
}
@Override
public boolean isPathfindable(BlockState state, BlockGetter reader, BlockPos pos, PathComputationType type) {
return false;

View File

@ -241,7 +241,7 @@ public class MechanicalBearingBlockEntity extends GeneratingKineticBlockEntity
}
public boolean isNearInitialAngle() {
return Math.abs(angle) < 45 || Math.abs(angle) > 7 * 45;
return Math.abs(angle) < 22.5 || Math.abs(angle) > 360 - 22.5;
}
@Override

View File

@ -6,7 +6,9 @@ import com.simibubi.create.content.contraptions.processing.EmptyingByBasin;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.advancement.AdvancementBehaviour;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.ComparatorUtil;
import com.simibubi.create.foundation.blockEntity.behaviour.belt.DirectBeltInputBehaviour;
import com.simibubi.create.foundation.fluid.FluidHelper;
import net.minecraft.core.BlockPos;
@ -14,7 +16,9 @@ import net.minecraft.core.Direction;
import net.minecraft.world.Containers;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
@ -25,6 +29,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
@ -56,7 +61,8 @@ public class ItemDrainBlock extends Block implements IWrenchable, IBE<ItemDrainB
ItemStack heldItemStack = be.getHeldItemStack();
if (!worldIn.isClientSide && !heldItemStack.isEmpty()) {
player.getInventory().placeItemBackInInventory(heldItemStack);
player.getInventory()
.placeItemBackInInventory(heldItemStack);
be.heldItem = null;
be.notifyUpdate();
}
@ -64,6 +70,31 @@ public class ItemDrainBlock extends Block implements IWrenchable, IBE<ItemDrainB
});
}
@Override
public void updateEntityAfterFallOn(BlockGetter worldIn, Entity entityIn) {
super.updateEntityAfterFallOn(worldIn, entityIn);
if (!(entityIn instanceof ItemEntity))
return;
if (!entityIn.isAlive())
return;
if (entityIn.level.isClientSide)
return;
ItemEntity itemEntity = (ItemEntity) entityIn;
DirectBeltInputBehaviour inputBehaviour =
BlockEntityBehaviour.get(worldIn, entityIn.blockPosition(), DirectBeltInputBehaviour.TYPE);
if (inputBehaviour == null)
return;
Vec3 deltaMovement = entityIn.getDeltaMovement()
.multiply(1, 0, 1)
.normalize();
Direction nearest = Direction.getNearest(deltaMovement.x, deltaMovement.y, deltaMovement.z);
ItemStack remainder = inputBehaviour.handleInsertion(itemEntity.getItem(), nearest, false);
itemEntity.setItem(remainder);
if (remainder.isEmpty())
itemEntity.discard();
}
protected InteractionResult tryExchange(Level worldIn, Player player, InteractionHand handIn, ItemStack heldItem,
ItemDrainBlockEntity be) {
if (FluidHelper.tryEmptyItemIntoTE(worldIn, player, handIn, heldItem, be))
@ -95,13 +126,13 @@ public class ItemDrainBlock extends Block implements IWrenchable, IBE<ItemDrainB
public Class<ItemDrainBlockEntity> getBlockEntityClass() {
return ItemDrainBlockEntity.class;
}
@Override
public void setPlacedBy(Level pLevel, BlockPos pPos, BlockState pState, LivingEntity pPlacer, ItemStack pStack) {
super.setPlacedBy(pLevel, pPos, pState, pPlacer, pStack);
AdvancementBehaviour.setPlacedBy(pLevel, pPos, pPlacer);
}
@Override
public BlockEntityType<? extends ItemDrainBlockEntity> getBlockEntityType() {
return AllBlockEntityTypes.ITEM_DRAIN.get();

View File

@ -0,0 +1,44 @@
package com.simibubi.create.content.curiosities.clipboard;
import com.simibubi.create.AllItems;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent.Context;
public class ClipboardEditPacket extends SimplePacketBase {
private int hotbarSlot;
private CompoundTag data;
public ClipboardEditPacket(int hotbarSlot, CompoundTag data) {
this.hotbarSlot = hotbarSlot;
this.data = data;
}
public ClipboardEditPacket(FriendlyByteBuf buffer) {
hotbarSlot = buffer.readVarInt();
data = buffer.readNbt();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeVarInt(hotbarSlot);
buffer.writeNbt(data);
}
@Override
public boolean handle(Context context) {
ServerPlayer sender = context.getSender();
ItemStack itemStack = sender.getInventory()
.getItem(hotbarSlot);
if (!AllItems.CLIPBOARD.isIn(itemStack))
return true;
itemStack.setTag(data.isEmpty() ? null : data);
return true;
}
}

View File

@ -0,0 +1,77 @@
package com.simibubi.create.content.curiosities.clipboard;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.ItemStack;
public class ClipboardEntry {
public boolean checked;
public MutableComponent text;
public ItemStack icon;
public ClipboardEntry(boolean checked, MutableComponent text) {
this.checked = checked;
this.text = text;
this.icon = ItemStack.EMPTY;
}
public ClipboardEntry displayItem(ItemStack icon) {
this.icon = icon;
return this;
}
public static List<List<ClipboardEntry>> readAll(ItemStack clipboardItem) {
CompoundTag tag = clipboardItem.getTag();
if (tag == null)
return new ArrayList<>();
return NBTHelper.readCompoundList(tag.getList("Pages", Tag.TAG_COMPOUND), pageTag -> NBTHelper
.readCompoundList(pageTag.getList("Entries", Tag.TAG_COMPOUND), ClipboardEntry::readNBT));
}
public static List<ClipboardEntry> getLastViewedEntries(ItemStack heldItem) {
List<List<ClipboardEntry>> pages = ClipboardEntry.readAll(heldItem);
if (pages.isEmpty())
return new ArrayList<>();
int page = heldItem.getTag() == null ? 0
: Math.min(heldItem.getTag()
.getInt("PreviouslyOpenedPage"), pages.size() - 1);
List<ClipboardEntry> entries = pages.get(page);
return entries;
}
public static void saveAll(List<List<ClipboardEntry>> entries, ItemStack clipboardItem) {
CompoundTag tag = clipboardItem.getOrCreateTag();
tag.put("Pages", NBTHelper.writeCompoundList(entries, list -> {
CompoundTag pageTag = new CompoundTag();
pageTag.put("Entries", NBTHelper.writeCompoundList(list, ClipboardEntry::writeNBT));
return pageTag;
}));
}
public CompoundTag writeNBT() {
CompoundTag nbt = new CompoundTag();
nbt.putBoolean("Checked", checked);
nbt.putString("Text", Component.Serializer.toJson(text));
if (icon.isEmpty())
return nbt;
nbt.put("Icon", icon.serializeNBT());
return nbt;
}
public static ClipboardEntry readNBT(CompoundTag tag) {
ClipboardEntry clipboardEntry =
new ClipboardEntry(tag.getBoolean("Checked"), Component.Serializer.fromJson(tag.getString("Text")));
if (tag.contains("Icon"))
clipboardEntry.displayItem(ItemStack.of(tag.getCompound("Icon")));
return clipboardEntry;
}
}

View File

@ -0,0 +1,62 @@
package com.simibubi.create.content.curiosities.clipboard;
import javax.annotation.Nonnull;
import com.simibubi.create.foundation.gui.ScreenOpener;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public class ClipboardItem extends Item {
public ClipboardItem(Properties pProperties) {
super(pProperties);
}
@Nonnull
@Override
public InteractionResult useOn(UseOnContext context) {
if (context.getPlayer() == null)
return InteractionResult.PASS;
return use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
}
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
ItemStack heldItem = player.getItemInHand(hand);
if (hand == InteractionHand.OFF_HAND)
return InteractionResultHolder.pass(heldItem);
player.getCooldowns()
.addCooldown(heldItem.getItem(), 10);
if (world.isClientSide)
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> openScreen(player, heldItem));
CompoundTag tag = heldItem.getOrCreateTag();
tag.putInt("Type", ClipboardOverrides.ClipboardType.EDITING.ordinal());
heldItem.setTag(tag);
return InteractionResultHolder.success(heldItem);
}
@OnlyIn(Dist.CLIENT)
private void openScreen(Player player, ItemStack stack) {
if (Minecraft.getInstance().player == player)
ScreenOpener.open(new ClipboardScreen(player.getInventory().selected, stack));
}
public void registerModelOverrides() {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClipboardOverrides.registerModelOverridesClient(this));
}
}

View File

@ -0,0 +1,57 @@
package com.simibubi.create.content.curiosities.clipboard;
import com.simibubi.create.Create;
import com.tterrag.registrate.providers.DataGenContext;
import com.tterrag.registrate.providers.RegistrateItemModelProvider;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.model.generators.ItemModelBuilder;
import net.minecraftforge.client.model.generators.ModelFile.UncheckedModelFile;
public class ClipboardOverrides {
public enum ClipboardType {
EMPTY("empty_clipboard"), WRITTEN("clipboard"), EDITING("clipboard_and_quill");
public String file;
public static ResourceLocation ID = Create.asResource("clipboard_type");
private ClipboardType(String file) {
this.file = file;
}
}
public static void switchTo(ClipboardType type, ItemStack clipboardItem) {
CompoundTag tag = clipboardItem.getOrCreateTag();
tag.putInt("Type", type.ordinal());
}
@OnlyIn(Dist.CLIENT)
public static void registerModelOverridesClient(ClipboardItem item) {
ItemProperties.register(item, ClipboardType.ID, (pStack, pLevel, pEntity, pSeed) -> {
CompoundTag tag = pStack.getTag();
return tag == null ? 0 : tag.getInt("Type");
});
}
public static ItemModelBuilder addOverrideModels(DataGenContext<Item, ClipboardItem> c,
RegistrateItemModelProvider p) {
ItemModelBuilder builder = p.generated(() -> c.get());
for (int i = 0; i < ClipboardType.values().length; i++) {
builder.override()
.predicate(ClipboardType.ID, i)
.model(p.getBuilder(c.getName() + "_" + i)
.parent(new UncheckedModelFile("item/generated"))
.texture("layer0", Create.asResource("item/" + ClipboardType.values()[i].file)))
.end();
}
return builder;
}
}

View File

@ -0,0 +1,792 @@
package com.simibubi.create.content.curiosities.clipboard;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableInt;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides.ClipboardType;
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.widget.IconButton;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.font.TextFieldHelper;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.PageButton;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class ClipboardScreen extends AbstractSimiScreen {
private ItemStack item;
List<List<ClipboardEntry>> pages;
List<ClipboardEntry> currentEntries;
int editingIndex;
int frameTick;
PageButton forward;
PageButton backward;
int currentPage;
long lastClickTime;
int lastIndex = -1;
int hoveredEntry;
boolean hoveredCheck;
boolean readonly;
DisplayCache displayCache = DisplayCache.EMPTY;
TextFieldHelper editContext;
IconButton closeBtn;
IconButton clearBtn;
private int targetSlot;
public ClipboardScreen(int targetSlot, ItemStack item) {
this.targetSlot = targetSlot;
this.item = item;
pages = ClipboardEntry.readAll(item);
if (pages.isEmpty())
pages.add(new ArrayList<>());
currentPage = item.getTag() == null ? 0
: item.getTag()
.getInt("PreviouslyOpenedPage");
currentPage = Mth.clamp(currentPage, 0, pages.size() - 1);
currentEntries = pages.get(currentPage);
boolean startEmpty = currentEntries.isEmpty();
if (startEmpty)
currentEntries.add(new ClipboardEntry(false, Components.empty()));
editingIndex = 0;
editContext = new TextFieldHelper(this::getCurrentEntryText, this::setCurrentEntryText, this::getClipboard,
this::setClipboard, this::validateTextForEntry);
editingIndex = startEmpty ? 0 : -1;
readonly = item.getTag() != null && item.getTag()
.getBoolean("Readonly");
if (readonly)
editingIndex = -1;
}
@Override
protected void init() {
setWindowSize(256, 256);
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui(true);
clearDisplayCache();
int x = guiLeft;
int y = guiTop - 8;
clearWidgets();
clearBtn = new IconButton(x + 234, y + 153, AllIcons.I_CLEAR_CHECKED).withCallback(() -> {
editingIndex = -1;
currentEntries.removeIf(ce -> ce.checked);
if (currentEntries.isEmpty())
currentEntries.add(new ClipboardEntry(false, Components.empty()));
});
clearBtn.setToolTip(Lang.translateDirect("gui.clipboard.erase_checked"));
closeBtn = new IconButton(x + 234, y + 175, AllIcons.I_PRIORITY_VERY_LOW)
.withCallback(() -> minecraft.setScreen(null));
closeBtn.setToolTip(Lang.translateDirect("station.close"));
addRenderableWidget(closeBtn);
addRenderableWidget(clearBtn);
forward = new PageButton(x + 176, y + 229, true, $ -> changePage(true), true);
backward = new PageButton(x + 53, y + 229, false, $ -> changePage(false), true);
addRenderableWidget(forward);
addRenderableWidget(backward);
forward.visible = currentPage < 50 && (!readonly || currentPage + 1 < pages.size());
backward.visible = currentPage > 0;
}
private int getNumPages() {
return pages.size();
}
public void tick() {
super.tick();
frameTick++;
int mx = (int) (this.minecraft.mouseHandler.xpos() * (double) this.minecraft.getWindow()
.getGuiScaledWidth() / (double) this.minecraft.getWindow()
.getScreenWidth());
int my = (int) (this.minecraft.mouseHandler.ypos() * (double) this.minecraft.getWindow()
.getGuiScaledHeight() / (double) this.minecraft.getWindow()
.getScreenHeight());
mx -= guiLeft + 35;
my -= guiTop + 41;
hoveredCheck = false;
hoveredEntry = -1;
if (mx > 0 && mx < 183 && my > 0 && my < 190) {
hoveredCheck = mx < 20;
int totalHeight = 0;
for (int i = 0; i < currentEntries.size(); i++) {
ClipboardEntry clipboardEntry = currentEntries.get(i);
String text = clipboardEntry.text.getString();
totalHeight +=
Math.max(12, font.split(Components.literal(text), clipboardEntry.icon.isEmpty() ? 150 : 130)
.size() * 9 + 3);
if (totalHeight > my) {
hoveredEntry = i;
return;
}
}
hoveredEntry = currentEntries.size();
}
}
private String getCurrentEntryText() {
return currentEntries.get(editingIndex).text.getString();
}
private void setCurrentEntryText(String text) {
currentEntries.get(editingIndex).text = Components.literal(text);
}
private void setClipboard(String p_98148_) {
if (minecraft != null)
TextFieldHelper.setClipboardContents(minecraft, p_98148_);
}
private String getClipboard() {
return minecraft != null ? TextFieldHelper.getClipboardContents(minecraft) : "";
}
private boolean validateTextForEntry(String newText) {
int totalHeight = 0;
for (int i = 0; i < currentEntries.size(); i++) {
ClipboardEntry clipboardEntry = currentEntries.get(i);
String text = i == editingIndex ? newText : clipboardEntry.text.getString();
totalHeight += Math.max(12, font.split(Components.literal(text), 150)
.size() * 9 + 3);
}
return totalHeight < 185;
}
private int yOffsetOfEditingEntry() {
int totalHeight = 0;
for (int i = 0; i < currentEntries.size(); i++) {
if (i == editingIndex)
break;
ClipboardEntry clipboardEntry = currentEntries.get(i);
totalHeight += Math.max(12, font.split(clipboardEntry.text, 150)
.size() * 9 + 3);
}
return totalHeight;
}
private void changePage(boolean next) {
int previously = currentPage;
currentPage = Mth.clamp(currentPage + (next ? 1 : -1), 0, 50);
if (currentPage == previously)
return;
editingIndex = -1;
if (pages.size() <= currentPage) {
if (readonly) {
currentPage = previously;
return;
}
pages.add(new ArrayList<>());
}
currentEntries = pages.get(currentPage);
if (currentEntries.isEmpty()) {
currentEntries.add(new ClipboardEntry(false, Components.empty()));
if (!readonly) {
editingIndex = 0;
editContext.setCursorToEnd();
clearDisplayCacheAfterChange();
}
}
forward.visible = currentPage < 50 && (!readonly || currentPage + 1 < pages.size());
backward.visible = currentPage > 0;
if (next)
return;
if (pages.get(currentPage + 1)
.stream()
.allMatch(ce -> ce.text.getString()
.isBlank()))
pages.remove(currentPage + 1);
}
@Override
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
int x = guiLeft;
int y = guiTop - 8;
AllGuiTextures.CLIPBOARD.render(ms, x, y);
font.draw(ms, new TranslatableComponent("book.pageIndicator", currentPage + 1, getNumPages()), x + 150, y + 9,
0x43ffffff);
for (int i = 0; i < currentEntries.size(); i++) {
ClipboardEntry clipboardEntry = currentEntries.get(i);
boolean checked = clipboardEntry.checked;
int iconOffset = clipboardEntry.icon.isEmpty() ? 0 : 16;
font.draw(ms, "\u25A1", x + 45, y + 51, checked ? 0x668D7F6B : 0xff8D7F6B);
if (checked)
font.draw(ms, "\u2714", x + 45, y + 50, 0x31B25D);
List<FormattedCharSequence> split = font.split(clipboardEntry.text, 150 - iconOffset);
if (split.isEmpty()) {
y += 12;
continue;
}
if (!clipboardEntry.icon.isEmpty())
itemRenderer.renderGuiItem(clipboardEntry.icon, x + 54, y + 50);
for (FormattedCharSequence sequence : split) {
if (i != editingIndex)
font.draw(ms, sequence, x + 58 + iconOffset, y + 50, checked ? 0x31B25D : 0x311A00);
y += 9;
}
y += 3;
}
if (editingIndex == -1)
return;
boolean checked = currentEntries.get(editingIndex).checked;
setFocused(null);
DisplayCache cache = getDisplayCache();
for (LineInfo line : cache.lines)
font.draw(ms, line.asComponent, line.x, line.y, checked ? 0x31B25D : 0x311A00);
renderHighlight(cache.selection);
renderCursor(ms, cache.cursor, cache.cursorAtEnd);
}
@Override
public void removed() {
minecraft.keyboardHandler.setSendRepeatsToGui(false);
pages.forEach(list -> list.removeIf(ce -> ce.text.getString()
.isBlank()));
pages.removeIf(List::isEmpty);
ClipboardEntry.saveAll(pages, item);
ClipboardOverrides.switchTo(ClipboardType.WRITTEN, item);
for (int i = 0; i < pages.size(); i++)
if (pages.get(i) == currentEntries)
item.getTag()
.putInt("PreviouslyOpenedPage", i);
if (pages.isEmpty())
item.setTag(new CompoundTag());
AllPackets.getChannel()
.sendToServer(new ClipboardEditPacket(targetSlot, item.getOrCreateTag()));
super.removed();
}
@Override
public boolean isPauseScreen() {
return false;
}
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
if (pKeyCode == 266) {
backward.onPress();
return true;
}
if (pKeyCode == 267) {
forward.onPress();
return true;
}
if (editingIndex != -1 && pKeyCode != 256) {
keyPressedWhileEditing(pKeyCode, pScanCode, pModifiers);
clearDisplayCache();
return true;
}
if (super.keyPressed(pKeyCode, pScanCode, pModifiers))
return true;
return true;
}
@Override
public boolean charTyped(char pCodePoint, int pModifiers) {
if (super.charTyped(pCodePoint, pModifiers))
return true;
if (!SharedConstants.isAllowedChatCharacter(pCodePoint))
return false;
if (editingIndex == -1)
return false;
editContext.insertText(Character.toString(pCodePoint));
clearDisplayCache();
return true;
}
private boolean keyPressedWhileEditing(int pKeyCode, int pScanCode, int pModifiers) {
if (Screen.isSelectAll(pKeyCode)) {
editContext.selectAll();
return true;
} else if (Screen.isCopy(pKeyCode)) {
editContext.copy();
return true;
} else if (Screen.isPaste(pKeyCode)) {
editContext.paste();
return true;
} else if (Screen.isCut(pKeyCode)) {
editContext.cut();
return true;
} else {
switch (pKeyCode) {
case 257:
case 335:
if (hasShiftDown()) {
editContext.insertText("\n");
return true;
} else if (!hasControlDown()) {
if (currentEntries.size() <= editingIndex + 1
|| !currentEntries.get(editingIndex + 1).text.getString()
.isEmpty())
currentEntries.add(editingIndex + 1, new ClipboardEntry(false, Components.empty()));
editingIndex += 1;
editContext.setCursorToEnd();
if (validateTextForEntry(" "))
return true;
currentEntries.remove(editingIndex);
editingIndex -= 1;
editContext.setCursorToEnd();
return true;
}
editingIndex = -1;
return true;
case 259:
if (currentEntries.get(editingIndex).text.getString()
.isEmpty() && currentEntries.size() > 1) {
currentEntries.remove(editingIndex);
editingIndex -= 1;
editContext.setCursorToEnd();
return true;
} else if (hasControlDown()) {
int prevPos = editContext.getCursorPos();
editContext.moveByWords(-1);
if (prevPos != editContext.getCursorPos())
editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos());
return true;
}
editContext.removeCharsFromCursor(-1);
return true;
case 261:
if (hasControlDown()) {
int prevPos = editContext.getCursorPos();
editContext.moveByWords(1);
if (prevPos != editContext.getCursorPos())
editContext.removeCharsFromCursor(prevPos - editContext.getCursorPos());
return true;
}
editContext.removeCharsFromCursor(1);
return true;
case 262:
if (hasControlDown()) {
editContext.moveByWords(1, Screen.hasShiftDown());
return true;
}
editContext.moveByChars(1, Screen.hasShiftDown());
return true;
case 263:
if (hasControlDown()) {
editContext.moveByWords(-1, Screen.hasShiftDown());
return true;
}
editContext.moveByChars(-1, Screen.hasShiftDown());
return true;
case 264:
keyDown();
return true;
case 265:
keyUp();
return true;
case 268:
keyHome();
return true;
case 269:
keyEnd();
return true;
default:
return false;
}
}
}
private void keyUp() {
changeLine(-1);
}
private void keyDown() {
changeLine(1);
}
private void changeLine(int pYChange) {
int i = editContext.getCursorPos();
int j = getDisplayCache().changeLine(i, pYChange);
editContext.setCursorPos(j, Screen.hasShiftDown());
}
private void keyHome() {
int i = editContext.getCursorPos();
int j = getDisplayCache().findLineStart(i);
editContext.setCursorPos(j, Screen.hasShiftDown());
}
private void keyEnd() {
DisplayCache cache = getDisplayCache();
int i = editContext.getCursorPos();
int j = cache.findLineEnd(i);
editContext.setCursorPos(j, Screen.hasShiftDown());
}
private void renderCursor(PoseStack pPoseStack, Pos2i pCursorPos, boolean pIsEndOfText) {
if (frameTick / 6 % 2 != 0)
return;
pCursorPos = convertLocalToScreen(pCursorPos);
if (!pIsEndOfText) {
GuiComponent.fill(pPoseStack, pCursorPos.x, pCursorPos.y - 1, pCursorPos.x + 1, pCursorPos.y + 9,
-16777216);
} else {
font.draw(pPoseStack, "_", (float) pCursorPos.x, (float) pCursorPos.y, 0);
}
}
private void renderHighlight(Rect2i[] pSelected) {
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder bufferbuilder = tesselator.getBuilder();
RenderSystem.setShader(GameRenderer::getPositionShader);
RenderSystem.setShaderColor(0.0F, 0.0F, 255.0F, 255.0F);
RenderSystem.disableTexture();
RenderSystem.enableColorLogicOp();
RenderSystem.logicOp(GlStateManager.LogicOp.OR_REVERSE);
bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION);
for (Rect2i rect2i : pSelected) {
int i = rect2i.getX();
int j = rect2i.getY();
int k = i + rect2i.getWidth();
int l = j + rect2i.getHeight();
bufferbuilder.vertex((double) i, (double) l, 0.0D)
.endVertex();
bufferbuilder.vertex((double) k, (double) l, 0.0D)
.endVertex();
bufferbuilder.vertex((double) k, (double) j, 0.0D)
.endVertex();
bufferbuilder.vertex((double) i, (double) j, 0.0D)
.endVertex();
}
tesselator.end();
RenderSystem.disableColorLogicOp();
RenderSystem.enableTexture();
}
private Pos2i convertScreenToLocal(Pos2i pScreenPos) {
return new Pos2i(pScreenPos.x - (width - 192) / 2 - 36 + 10, pScreenPos.y - 32 - 24 - yOffsetOfEditingEntry());
}
private Pos2i convertLocalToScreen(Pos2i pLocalScreenPos) {
return new Pos2i(pLocalScreenPos.x + (width - 192) / 2 + 36 - 10,
pLocalScreenPos.y + 32 + 24 + yOffsetOfEditingEntry());
}
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (super.mouseClicked(pMouseX, pMouseY, pButton))
return true;
if (pButton != 0)
return true;
if (hoveredEntry != -1) {
if (hoveredCheck) {
editingIndex = -1;
if (hoveredEntry < currentEntries.size())
currentEntries.get(hoveredEntry).checked ^= true;
return true;
}
if (hoveredEntry != editingIndex && !readonly) {
editingIndex = hoveredEntry;
if (hoveredEntry >= currentEntries.size()) {
currentEntries.add(new ClipboardEntry(false, Components.empty()));
if (!validateTextForEntry(" ")) {
currentEntries.remove(hoveredEntry);
editingIndex = -1;
return true;
}
}
clearDisplayCacheAfterChange();
}
}
if (editingIndex == -1)
return false;
long i = Util.getMillis();
DisplayCache cache = getDisplayCache();
int j = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY)));
if (j >= 0) {
if (j == lastIndex && i - lastClickTime < 250L) {
if (!editContext.isSelecting()) {
selectWord(j);
} else {
editContext.selectAll();
}
} else {
editContext.setCursorPos(j, Screen.hasShiftDown());
}
clearDisplayCache();
}
lastIndex = j;
lastClickTime = i;
return true;
}
private void selectWord(int pIndex) {
String s = getCurrentEntryText();
editContext.setSelectionRange(StringSplitter.getWordPosition(s, -1, pIndex, false),
StringSplitter.getWordPosition(s, 1, pIndex, false));
}
public boolean mouseDragged(double pMouseX, double pMouseY, int pButton, double pDragX, double pDragY) {
if (super.mouseDragged(pMouseX, pMouseY, pButton, pDragX, pDragY))
return true;
if (pButton != 0)
return true;
if (editingIndex == -1)
return false;
DisplayCache cache = getDisplayCache();
int i = cache.getIndexAtPosition(font, convertScreenToLocal(new Pos2i((int) pMouseX, (int) pMouseY)));
editContext.setCursorPos(i, true);
clearDisplayCache();
return true;
}
private DisplayCache getDisplayCache() {
if (displayCache == null)
displayCache = rebuildDisplayCache();
return displayCache;
}
private void clearDisplayCache() {
displayCache = null;
}
private void clearDisplayCacheAfterChange() {
editContext.setCursorToEnd();
clearDisplayCache();
}
private DisplayCache rebuildDisplayCache() {
String s = getCurrentEntryText();
if (s.isEmpty())
return DisplayCache.EMPTY;
int i = editContext.getCursorPos();
int j = editContext.getSelectionPos();
IntList intlist = new IntArrayList();
List<LineInfo> list = Lists.newArrayList();
MutableInt mutableint = new MutableInt();
MutableBoolean mutableboolean = new MutableBoolean();
StringSplitter stringsplitter = font.getSplitter();
stringsplitter.splitLines(s, 150, Style.EMPTY, true, (p_98132_, p_98133_, p_98134_) -> {
int k3 = mutableint.getAndIncrement();
String s2 = s.substring(p_98133_, p_98134_);
mutableboolean.setValue(s2.endsWith("\n"));
String s3 = StringUtils.stripEnd(s2, " \n");
int l3 = k3 * 9;
Pos2i pos1 = convertLocalToScreen(new Pos2i(0, l3));
intlist.add(p_98133_);
list.add(new LineInfo(p_98132_, s3, pos1.x, pos1.y));
});
int[] aint = intlist.toIntArray();
boolean flag = i == s.length();
Pos2i pos;
if (flag && mutableboolean.isTrue()) {
pos = new Pos2i(0, list.size() * 9);
} else {
int k = findLineFromPos(aint, i);
int l = font.width(s.substring(aint[k], i));
pos = new Pos2i(l, k * 9);
}
List<Rect2i> list1 = Lists.newArrayList();
if (i != j) {
int l2 = Math.min(i, j);
int i1 = Math.max(i, j);
int j1 = findLineFromPos(aint, l2);
int k1 = findLineFromPos(aint, i1);
if (j1 == k1) {
int l1 = j1 * 9;
int i2 = aint[j1];
list1.add(createPartialLineSelection(s, stringsplitter, l2, i1, l1, i2));
} else {
int i3 = j1 + 1 > aint.length ? s.length() : aint[j1 + 1];
list1.add(createPartialLineSelection(s, stringsplitter, l2, i3, j1 * 9, aint[j1]));
for (int j3 = j1 + 1; j3 < k1; ++j3) {
int j2 = j3 * 9;
String s1 = s.substring(aint[j3], aint[j3 + 1]);
int k2 = (int) stringsplitter.stringWidth(s1);
list1.add(createSelection(new Pos2i(0, j2), new Pos2i(k2, j2 + 9)));
}
list1.add(createPartialLineSelection(s, stringsplitter, aint[k1], i1, k1 * 9, aint[k1]));
}
}
return new DisplayCache(s, pos, flag, aint, list.toArray(new LineInfo[0]), list1.toArray(new Rect2i[0]));
}
static int findLineFromPos(int[] pLineStarts, int pFind) {
int i = Arrays.binarySearch(pLineStarts, pFind);
return i < 0 ? -(i + 2) : i;
}
private Rect2i createPartialLineSelection(String pInput, StringSplitter pSplitter, int p_98122_, int p_98123_,
int p_98124_, int p_98125_) {
String s = pInput.substring(p_98125_, p_98122_);
String s1 = pInput.substring(p_98125_, p_98123_);
Pos2i firstPos = new Pos2i((int) pSplitter.stringWidth(s), p_98124_);
Pos2i secondPos = new Pos2i((int) pSplitter.stringWidth(s1), p_98124_ + 9);
return createSelection(firstPos, secondPos);
}
private Rect2i createSelection(Pos2i pCorner1, Pos2i pCorner2) {
Pos2i firstPos = convertLocalToScreen(pCorner1);
Pos2i secondPos = convertLocalToScreen(pCorner2);
int i = Math.min(firstPos.x, secondPos.x);
int j = Math.max(firstPos.x, secondPos.x);
int k = Math.min(firstPos.y, secondPos.y);
int l = Math.max(firstPos.y, secondPos.y);
return new Rect2i(i, k, j - i, l - k);
}
@OnlyIn(Dist.CLIENT)
static class DisplayCache {
static final DisplayCache EMPTY = new DisplayCache("", new Pos2i(0, 0), true, new int[] { 0 },
new LineInfo[] { new LineInfo(Style.EMPTY, "", 0, 0) }, new Rect2i[0]);
private final String fullText;
final Pos2i cursor;
final boolean cursorAtEnd;
private final int[] lineStarts;
final LineInfo[] lines;
final Rect2i[] selection;
public DisplayCache(String pFullText, Pos2i pCursor, boolean pCursorAtEnd, int[] pLineStarts, LineInfo[] pLines,
Rect2i[] pSelection) {
fullText = pFullText;
cursor = pCursor;
cursorAtEnd = pCursorAtEnd;
lineStarts = pLineStarts;
lines = pLines;
selection = pSelection;
}
public int getIndexAtPosition(Font pFont, Pos2i pCursorPosition) {
int i = pCursorPosition.y / 9;
if (i < 0)
return 0;
if (i >= lines.length)
return fullText.length();
LineInfo line = lines[i];
return lineStarts[i] + pFont.getSplitter()
.plainIndexAtWidth(line.contents, pCursorPosition.x, line.style);
}
public int changeLine(int pXChange, int pYChange) {
int i = findLineFromPos(lineStarts, pXChange);
int j = i + pYChange;
int k;
if (0 <= j && j < lineStarts.length) {
int l = pXChange - lineStarts[i];
int i1 = lines[j].contents.length();
k = lineStarts[j] + Math.min(l, i1);
} else {
k = pXChange;
}
return k;
}
public int findLineStart(int pLine) {
int i = findLineFromPos(lineStarts, pLine);
return lineStarts[i];
}
public int findLineEnd(int pLine) {
int i = findLineFromPos(lineStarts, pLine);
return lineStarts[i] + lines[i].contents.length();
}
}
@OnlyIn(Dist.CLIENT)
static class LineInfo {
final Style style;
final String contents;
final Component asComponent;
final int x;
final int y;
public LineInfo(Style pStyle, String pContents, int pX, int pY) {
style = pStyle;
contents = pContents;
x = pX;
y = pY;
asComponent = (new TextComponent(pContents)).setStyle(pStyle);
}
}
@OnlyIn(Dist.CLIENT)
static class Pos2i {
public final int x;
public final int y;
Pos2i(int pX, int pY) {
x = pX;
y = pY;
}
}
}

View File

@ -2,13 +2,16 @@ package com.simibubi.create.content.logistics.block.redstone;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry;
import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
@ -19,6 +22,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@ -76,20 +80,31 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock
return InteractionResult.SUCCESS;
}
boolean display = heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName();
boolean display =
heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName() || AllItems.CLIPBOARD.isIn(heldItem);
DyeColor dye = DyeColor.getColor(heldItem);
if (!display && dye == null)
return InteractionResult.PASS;
if (world.isClientSide)
return InteractionResult.SUCCESS;
CompoundTag tag = heldItem.getTagElement("display");
String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null;
if (AllItems.CLIPBOARD.isIn(heldItem)) {
List<ClipboardEntry> entries = ClipboardEntry.getLastViewedEntries(heldItem);
for (int i = 0; i < entries.size();) {
tagElement = Component.Serializer.toJson(entries.get(i).text);
break;
}
}
if (world.isClientSide)
return InteractionResult.SUCCESS;
String tagUsed = tagElement;
walkNixies(world, pos, (currentPos, rowPosition) -> {
if (display)
withBlockEntityDo(world, currentPos, be -> be.displayCustomText(tagElement, rowPosition));
withBlockEntityDo(world, currentPos, be -> be.displayCustomText(tagUsed, rowPosition));
if (dye != null)
world.setBlockAndUpdate(currentPos, withColor(state, dye));
});

View File

@ -8,12 +8,15 @@ import java.util.function.Predicate;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry;
import com.simibubi.create.foundation.block.IBE;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
@ -27,6 +30,7 @@ import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
@ -158,7 +162,8 @@ public class FlapDisplayBlock extends HorizontalKineticBlock
return InteractionResult.SUCCESS;
}
boolean display = heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName();
boolean display =
heldItem.getItem() == Items.NAME_TAG && heldItem.hasCustomHoverName() || AllItems.CLIPBOARD.isIn(heldItem);
DyeColor dye = DyeColor.getColor(heldItem);
if (!display && dye == null)
@ -171,8 +176,20 @@ public class FlapDisplayBlock extends HorizontalKineticBlock
CompoundTag tag = heldItem.getTagElement("display");
String tagElement = tag != null && tag.contains("Name", Tag.TAG_STRING) ? tag.getString("Name") : null;
if (display)
if (display) {
if (AllItems.CLIPBOARD.isIn(heldItem)) {
List<ClipboardEntry> entries = ClipboardEntry.getLastViewedEntries(heldItem);
int line = lineIndex;
for (int i = 0; i < entries.size(); i++) {
for (String string : entries.get(i).text.getString()
.split("\n"))
flapBE.applyTextManually(line++, Component.Serializer.toJson(Components.literal(string)));
}
return InteractionResult.SUCCESS;
}
flapBE.applyTextManually(lineIndex, tagElement);
}
if (dye != null) {
world.playSound(null, pos, SoundEvents.DYE_USE, SoundSource.BLOCKS, 1.0F, 1.0F);
flapBE.setColour(lineIndex, dye);
@ -315,7 +332,8 @@ public class FlapDisplayBlock extends HorizontalKineticBlock
BlockPos relative = pPos.relative(d);
BlockState adjacent = pLevel.getBlockState(relative);
if (canConnect(pState, adjacent))
KineticBlockEntity.switchToBlockState(pLevel, relative, updateColumn(pLevel, relative, adjacent, false));
KineticBlockEntity.switchToBlockState(pLevel, relative,
updateColumn(pLevel, relative, adjacent, false));
}
}

View File

@ -6,17 +6,19 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.jozufozu.flywheel.util.Color;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.logistics.trains.BezierConnection;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.block.ProperWaterloggedBlock;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
@ -27,6 +29,7 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -35,6 +38,7 @@ import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Inventory;
@ -42,6 +46,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
@ -53,6 +58,9 @@ import net.minecraft.world.phys.HitResult.Type;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.gui.ForgeIngameGui;
import net.minecraftforge.client.gui.IIngameOverlay;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import net.minecraftforge.items.ItemHandlerHelper;
public class TrackPlacement {
@ -98,6 +106,8 @@ public class TrackPlacement {
static int hoveringAngle;
static ItemStack lastItem;
static int extraTipWarmup;
public static PlacementInfo tryConnect(Level level, Player player, BlockPos pos2, BlockState state2,
ItemStack stack, boolean girder, boolean maximiseTurn) {
Vec3 lookVec = player.getLookAngle();
@ -283,10 +293,10 @@ public class TrackPlacement {
if (skipCurve && !Mth.equal(ascend, 0)) {
int hDistance = info.end1Extent;
if (axis1.y == 0 || !Mth.equal(absAscend + 1, dist / axis1.length())) {
if (axis1.y != 0 && axis1.y == -axis2.y)
return info.withMessage("ascending_s_curve");
info.end1Extent = 0;
double minHDistance = Math.max(absAscend < 4 ? absAscend * 4 : absAscend * 3, 6) / axis1.length();
if (hDistance < minHDistance)
@ -447,10 +457,6 @@ public class TrackPlacement {
BlockItem paveItem = (BlockItem) offhandItem.getItem();
paveTracks(level, info, paveItem, false);
}
if (info.curve != null && info.curve.getLength() > 29)
AllAdvancements.LONG_BEND.awardTo(player);
return placeTracks(level, info, state1, state2, targetPos1, targetPos2, false);
}
@ -576,6 +582,8 @@ public class TrackPlacement {
LocalPlayer player = Minecraft.getInstance().player;
ItemStack stack = player.getMainHandItem();
HitResult hitResult = Minecraft.getInstance().hitResult;
int restoreWarmup = extraTipWarmup;
extraTipWarmup = 0;
if (hitResult == null)
return;
@ -609,8 +617,14 @@ public class TrackPlacement {
if (!(hitState.getBlock() instanceof TrackBlock))
return;
extraTipWarmup = restoreWarmup;
boolean maxTurns = Minecraft.getInstance().options.keySprint.isDown();
PlacementInfo info = tryConnect(level, player, pos, hitState, stack, false, maxTurns);
if (extraTipWarmup < 20)
extraTipWarmup++;
if (!info.valid || !hoveringMaxed && (info.end1Extent == 0 || info.end2Extent == 0))
extraTipWarmup = 0;
if (!player.isCreative() && (info.valid || !info.hasRequiredTracks || !info.hasRequiredPavement))
BlueprintOverlayRenderer.displayTrackRequirements(info, player.getOffhandItem());
@ -625,7 +639,7 @@ public class TrackPlacement {
if (bhr.getDirection() == Direction.UP) {
Vec3 lookVec = player.getLookAngle();
int lookAngle = (int) (22.5 + AngleHelper.deg(Mth.atan2(lookVec.z, lookVec.x)) % 360) / 8;
if (!pos.equals(hintPos) || lookAngle != hintAngle) {
hints = Couple.create(ArrayList::new);
hintAngle = lookAngle;
@ -761,4 +775,38 @@ public class TrackPlacement {
.colored(color);
}
@OnlyIn(Dist.CLIENT)
public static final IIngameOverlay OVERLAY = TrackPlacement::renderOverlay;
@OnlyIn(Dist.CLIENT)
public static void renderOverlay(ForgeIngameGui gui, PoseStack poseStack, float partialTicks, int width,
int height) {
Minecraft mc = Minecraft.getInstance();
if (mc.options.hideGui || mc.gameMode.getPlayerMode() == GameType.SPECTATOR)
return;
if (hoveringPos == null)
return;
if (cached == null || cached.curve == null || !cached.valid)
return;
if (extraTipWarmup < 4)
return;
if (ObfuscationReflectionHelper.getPrivateValue(Gui.class, gui,
"toolHighlightTimer") instanceof Integer toolHighlightTimer && toolHighlightTimer > 0)
return;
boolean active = mc.options.keySprint.isDown();
MutableComponent text = Lang.translateDirect("track.hold_for_smooth_curve", Components.keybind("key.sprint")
.withStyle(active ? ChatFormatting.WHITE : ChatFormatting.GRAY));
Window window = mc.getWindow();
int x = (window.getGuiScaledWidth() - gui.getFont()
.width(text)) / 2;
int y = window.getGuiScaledHeight() - 61;
Color color = new Color(0x4ADB4A).setAlpha(Mth.clamp((extraTipWarmup - 4) / 3f, 0.1f, 1));
gui.getFont()
.draw(poseStack, text, x, y, color.getRGB());
}
}

View File

@ -6,6 +6,10 @@ import java.util.List;
import java.util.Locale;
import com.google.common.collect.Sets;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.curiosities.clipboard.ClipboardEntry;
import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides;
import com.simibubi.create.content.curiosities.clipboard.ClipboardOverrides.ClipboardType;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
@ -27,6 +31,7 @@ import net.minecraft.world.item.Items;
public class MaterialChecklist {
public static final int MAX_ENTRIES_PER_PAGE = 5;
public static final int MAX_ENTRIES_PER_CLIPBOARD_PAGE = 7;
public Object2IntMap<Item> gathered = new Object2IntArrayMap<>();
public Object2IntMap<Item> required = new Object2IntArrayMap<>();
@ -70,7 +75,7 @@ public class MaterialChecklist {
gathered.put(item, stack.getCount());
}
public ItemStack createItem() {
public ItemStack createWrittenBook() {
ItemStack book = new ItemStack(Items.WRITTEN_BOOK);
CompoundTag tag = book.getOrCreateTag();
@ -88,9 +93,11 @@ public class MaterialChecklist {
List<Item> keys = new ArrayList<>(Sets.union(required.keySet(), damageRequired.keySet()));
Collections.sort(keys, (item1, item2) -> {
Locale locale = Locale.ENGLISH;
String name1 = item1.getDescription().getString()
String name1 = item1.getDescription()
.getString()
.toLowerCase(locale);
String name2 = item2.getDescription().getString()
String name2 = item2.getDescription()
.getString()
.toLowerCase(locale);
return name1.compareTo(name2);
});
@ -109,30 +116,33 @@ public class MaterialChecklist {
if (itemsWritten == MAX_ENTRIES_PER_PAGE) {
itemsWritten = 0;
textComponent.append(Components.literal("\n >>>").withStyle(ChatFormatting.BLUE));
textComponent.append(Components.literal("\n >>>")
.withStyle(ChatFormatting.BLUE));
pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent)));
textComponent = Components.empty();
}
itemsWritten++;
textComponent.append(entry(new ItemStack(item), amount, true));
textComponent.append(entry(new ItemStack(item), amount, true, true));
}
for (Item item : completed) {
if (itemsWritten == MAX_ENTRIES_PER_PAGE) {
itemsWritten = 0;
textComponent.append(Components.literal("\n >>>").withStyle(ChatFormatting.DARK_GREEN));
textComponent.append(Components.literal("\n >>>")
.withStyle(ChatFormatting.DARK_GREEN));
pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent)));
textComponent = Components.empty();
}
itemsWritten++;
textComponent.append(entry(new ItemStack(item), getRequiredAmount(item), false));
textComponent.append(entry(new ItemStack(item), getRequiredAmount(item), false, true));
}
pages.add(StringTag.valueOf(Component.Serializer.toJson(textComponent)));
tag.put("pages", pages);
tag.putBoolean("readonly", true);
tag.putString("author", "Schematicannon");
tag.putString("title", ChatFormatting.BLUE + "Material Checklist");
textComponent = Lang.translateDirect("materialChecklist")
@ -145,6 +155,80 @@ public class MaterialChecklist {
return book;
}
public ItemStack createWrittenClipboard() {
ItemStack clipboard = AllItems.CLIPBOARD.asStack();
CompoundTag tag = clipboard.getOrCreateTag();
int itemsWritten = 0;
List<List<ClipboardEntry>> pages = new ArrayList<>();
List<ClipboardEntry> currentPage = new ArrayList<>();
if (blocksNotLoaded) {
currentPage.add(new ClipboardEntry(false, Lang.translateDirect("materialChecklist.blocksNotLoaded")
.withStyle(ChatFormatting.RED)));
}
List<Item> keys = new ArrayList<>(Sets.union(required.keySet(), damageRequired.keySet()));
Collections.sort(keys, (item1, item2) -> {
Locale locale = Locale.ENGLISH;
String name1 = item1.getDescription()
.getString()
.toLowerCase(locale);
String name2 = item2.getDescription()
.getString()
.toLowerCase(locale);
return name1.compareTo(name2);
});
List<Item> completed = new ArrayList<>();
for (Item item : keys) {
int amount = getRequiredAmount(item);
if (gathered.containsKey(item))
amount -= gathered.getInt(item);
if (amount <= 0) {
completed.add(item);
continue;
}
if (itemsWritten == MAX_ENTRIES_PER_CLIPBOARD_PAGE) {
itemsWritten = 0;
currentPage.add(new ClipboardEntry(false, Components.literal(">>>")
.withStyle(ChatFormatting.DARK_GRAY)));
pages.add(currentPage);
currentPage = new ArrayList<>();
}
itemsWritten++;
currentPage.add(new ClipboardEntry(false, entry(new ItemStack(item), amount, true, false))
.displayItem(new ItemStack(item)));
}
for (Item item : completed) {
if (itemsWritten == MAX_ENTRIES_PER_PAGE) {
itemsWritten = 0;
currentPage.add(new ClipboardEntry(true, Components.literal(">>>")
.withStyle(ChatFormatting.DARK_GREEN)));
pages.add(currentPage);
currentPage = new ArrayList<>();
}
itemsWritten++;
currentPage.add(new ClipboardEntry(true, entry(new ItemStack(item), getRequiredAmount(item), false, false))
.displayItem(new ItemStack(item)));
}
pages.add(currentPage);
ClipboardEntry.saveAll(pages, clipboard);
ClipboardOverrides.switchTo(ClipboardType.WRITTEN, clipboard);
clipboard.getOrCreateTagElement("display")
.putString("Name", Component.Serializer.toJson(Lang.translateDirect("materialChecklist")
.setStyle(Style.EMPTY.withItalic(Boolean.FALSE))));
tag.putBoolean("Readonly", true);
clipboard.setTag(tag);
return clipboard;
}
public int getRequiredAmount(Item item) {
int amount = required.getOrDefault(item, 0);
if (damageRequired.containsKey(item))
@ -152,7 +236,7 @@ public class MaterialChecklist {
return amount;
}
private Component entry(ItemStack item, int amount, boolean unfinished) {
private MutableComponent entry(ItemStack item, int amount, boolean unfinished, boolean forBook) {
int stacks = amount / 64;
int remainder = amount % 64;
MutableComponent tc = Components.empty();
@ -160,11 +244,14 @@ public class MaterialChecklist {
.setStyle(Style.EMPTY
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new HoverEvent.ItemStackInfo(item)))));
if (!unfinished)
if (!unfinished && forBook)
tc.append(" \u2714");
tc.withStyle(unfinished ? ChatFormatting.BLUE : ChatFormatting.DARK_GREEN);
return tc.append(Components.literal("\n" + " x" + amount).withStyle(ChatFormatting.BLACK))
.append(Components.literal(" | " + stacks + "\u25A4 +" + remainder + "\n").withStyle(ChatFormatting.GRAY));
if (!unfinished || forBook)
tc.withStyle(unfinished ? ChatFormatting.BLUE : ChatFormatting.DARK_GREEN);
return tc.append(Components.literal("\n" + " x" + amount)
.withStyle(ChatFormatting.BLACK))
.append(Components.literal(" | " + stacks + "\u25A4 +" + remainder + (forBook ? "\n" : ""))
.withStyle(ChatFormatting.GRAY));
}
}

View File

@ -700,8 +700,9 @@ public class SchematicannonBlockEntity extends SmartBlockEntity implements MenuP
updateChecklist();
dontUpdateChecklist = true;
inventory.extractItem(BookInput, 1, false);
ItemStack stack = checklist.createItem();
ItemStack extractItem = inventory.extractItem(BookInput, 1, false);
ItemStack stack = AllItems.CLIPBOARD.isIn(extractItem) ? checklist.createWrittenClipboard()
: checklist.createWrittenBook();
stack.setCount(inventory.getStackInSlot(BookOutput)
.getCount() + 1);
inventory.setStackInSlot(BookOutput, stack);

View File

@ -28,7 +28,8 @@ public class SchematicannonInventory extends ItemStackHandler {
case 1: // Blueprint output
return false;
case 2: // Book input
return stack.sameItem(new ItemStack(Items.BOOK)) || stack.sameItem(new ItemStack(Items.WRITTEN_BOOK));
return AllItems.CLIPBOARD.isIn(stack) || stack.sameItem(new ItemStack(Items.BOOK))
|| stack.sameItem(new ItemStack(Items.WRITTEN_BOOK));
case 3: // Material List output
return false;
case 4: // Gunpowder

View File

@ -565,16 +565,10 @@ public class AllAdvancements implements DataProvider {
.after(STURDY_SHEET)
.special(EXPERT)),
LONG_BEND = create("long_bend", b -> b.icon(AllBlocks.TRACK)
.title("The Longest Bend")
.description("Create a curved track section that spans more than 30 blocks in length")
.after(TRACK_CRAFTING)
.special(EXPERT)),
LONG_TRAIN = create("long_train", b -> b.icon(Items.MINECART)
.title("Ambitious Endeavours")
.description("Create a Train with at least 6 carriages")
.after(LONG_BEND)
.after(TRACK_CRAFTING)
.special(EXPERT)),
LONG_TRAVEL = create("long_travel", b -> b.icon(AllBlocks.SEATS.get(DyeColor.GREEN))

View File

@ -278,7 +278,6 @@ public class BuilderTransformers {
.texture("3", p.modLoc("block/valve_handle/valve_handle_" + variant)));
})
.tag(AllBlockTags.BRITTLE.tag, AllBlockTags.VALVE_HANDLES.tag)
.transform(BlockStressDefaults.setCapacity(8.0))
.transform(BlockStressDefaults.setGeneratorSpeed(ValveHandleBlock::getSpeedRange))
.onRegister(ItemUseOverrides::addBlock)
.item()

View File

@ -1011,6 +1011,18 @@ public class StandardRecipeGen extends CreateRecipeProvider {
.viaShapeless(b -> b.requires(I.wheatFlour())
.requires(Items.WATER_BUCKET)),
CLIPBOARD = create(AllItems.CLIPBOARD).unlockedBy(I::andesite)
.viaShaped(b -> b.define('G', I.planks())
.define('P', Items.PAPER)
.define('A', I.andesite())
.pattern("A")
.pattern("P")
.pattern("G")),
CLIPBOARD_CLEAR = create(AllItems.CLIPBOARD).withSuffix("_clear")
.unlockedBy(AllItems.CLIPBOARD::get)
.viaShapeless(b -> b.requires(AllItems.CLIPBOARD.get())),
DIVING_HELMET = create(AllItems.COPPER_DIVING_HELMET).unlockedBy(I::copper)
.viaShaped(b -> b.define('G', Tags.Items.GLASS)
.define('P', I.copper())

View File

@ -72,6 +72,8 @@ public enum AllGuiTextures implements ScreenElement {
LINKED_CONTROLLER("curiosities_2", 179, 109),
BLUEPRINT("curiosities_2", 0, 109, 179, 109),
CLIPBOARD("clipboard", 0, 0, 256, 256),
DATA_GATHERER("display_link", 235, 162),
DATA_AREA_START("display_link", 0, 163, 2, 18),

View File

@ -127,6 +127,8 @@ public class AllIcons implements ScreenElement {
I_PATTERN_CHANCE_75 = next(),
I_FOLLOW_DIAGONAL = next(),
I_FOLLOW_MATERIAL = next(),
I_CLEAR_CHECKED = next(),
I_SCHEMATIC = newRow(),
I_SEQ_REPEAT = next(),

View File

@ -8,8 +8,10 @@ import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.IRotate;
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.content.contraptions.components.crank.ValveHandleBlock;
import com.simibubi.create.content.contraptions.components.steam.SteamEngineBlock;
import com.simibubi.create.content.contraptions.goggles.GogglesItem;
import com.simibubi.create.foundation.block.BlockStressValues;
@ -71,6 +73,9 @@ public class KineticStats implements TooltipModifier {
showStressImpact = true;
}
if (block instanceof ValveHandleBlock)
block = AllBlocks.COPPER_VALVE_HANDLE.get();
boolean hasStressImpact =
StressImpact.isEnabled() && showStressImpact && BlockStressValues.getImpact(block) > 0;
boolean hasStressCapacity = StressImpact.isEnabled() && BlockStressValues.hasCapacity(block);

View File

@ -37,6 +37,7 @@ import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Config
import com.simibubi.create.content.contraptions.relays.gauge.GaugeObservedPacket;
import com.simibubi.create.content.curiosities.armor.NetheriteDivingHandler;
import com.simibubi.create.content.curiosities.bell.SoulPulseEffectPacket;
import com.simibubi.create.content.curiosities.clipboard.ClipboardEditPacket;
import com.simibubi.create.content.curiosities.symmetry.ConfigureSymmetryWandPacket;
import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket;
import com.simibubi.create.content.curiosities.toolbox.ToolboxDisposeAllPacket;
@ -155,6 +156,7 @@ public enum AllPackets {
PLAY_TO_SERVER),
ELEVATOR_SET_FLOOR(ElevatorTargetFloorPacket.class, ElevatorTargetFloorPacket::new, PLAY_TO_SERVER),
VALUE_SETTINGS(ValueSettingsPacket.class, ValueSettingsPacket::new, PLAY_TO_SERVER),
CLIPBOARD_EDIT(ClipboardEditPacket.class, ClipboardEditPacket::new, PLAY_TO_SERVER),
// Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),

View File

@ -301,6 +301,8 @@
"create.gui.sequenced_gearshift.speed.back": "Input speed, Reversed",
"create.gui.sequenced_gearshift.speed.back_fast": "Double speed, Reversed",
"create.gui.clipboard.erase_checked": "Erase checked items",
"create.schematicAndQuill.dimensions": "Schematic Size: %1$sx%2$sx%3$s",
"create.schematicAndQuill.firstPos": "First position set.",
"create.schematicAndQuill.secondPos": "Second position set.",
@ -384,7 +386,7 @@
"create.gui.schematicannon.option.skipBlockEntities": "Protect Block Entities",
"create.gui.schematicannon.slot.gunpowder": "Add gunpowder to fuel the cannon",
"create.gui.schematicannon.slot.listPrinter": "Place books here to print a Checklist for your Schematic",
"create.gui.schematicannon.slot.listPrinter": "Place a Clipboard or Book here to print a Checklist for your Schematic",
"create.gui.schematicannon.slot.schematic": "Add your Schematic here. Make sure it is deployed at a specific location.",
"create.gui.schematicannon.option.skipMissing.description": "If the cannon cannot find a required Block for placement, it will continue at the next Location.",
@ -763,6 +765,7 @@
"create.track.turn_start": "Cannot start connection from a Turn",
"create.track.not_enough_tracks": "Not holding enough tracks",
"create.track.not_enough_pavement": "Not holding enough pavement blocks",
"create.track.hold_for_smooth_curve": "Hold %1$s for maximized turn",
"create.portal_track.failed": "Cannot place portal track:",
"create.portal_track.missing": "Target portal not generated yet",

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B