Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworked Part Placement & Part/Facade Placement Previews #5853

Merged
merged 2 commits into from Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/appeng/api/parts/IFacadeContainer.java
Expand Up @@ -36,6 +36,13 @@
*/
public interface IFacadeContainer {

/**
* Checks if the {@link IFacadePart} can be added to the given side.
*
* @return true if the facade can be successfully added.
*/
boolean canAddFacade(IFacadePart a);

/**
* Attempts to add the {@link IFacadePart} to the given side.
*
Expand Down
74 changes: 52 additions & 22 deletions src/main/java/appeng/api/parts/PartHelper.java
Expand Up @@ -30,11 +30,9 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;

Expand All @@ -52,16 +50,11 @@ private PartHelper() {
* {@link net.minecraft.world.item.Item#useOn} of your parts item (if you're not using AE2s internal PartItem class)
* to implement part placement.
*
* @param is ItemStack of an item which implements {@link IPartItem}
* @param pos pos of part
* @param side side which the part should be on
* @param player player placing part
* @param level part in level
* @return true if placing was successful
* @return The result of placement suitable for returning from
* {@link net.minecraft.world.item.Item#useOn(UseOnContext)}.
*/
public static InteractionResult usePartItem(ItemStack is, BlockPos pos, Direction side,
Player player, InteractionHand hand, Level level) {
return PartPlacement.place(is, pos, side, player, hand, level, PartPlacement.PlaceType.PLACE_ITEM, 0);
public static InteractionResult usePartItem(UseOnContext context) {
return PartPlacement.place(context);
}

/**
Expand Down Expand Up @@ -109,7 +102,7 @@ public static <T extends IPart> T setPart(ServerLevel level, BlockPos pos, @Null
Objects.requireNonNull(level, "level");
Objects.requireNonNull(pos, "pos");

var host = getOrPlacePartHost(level, pos, true);
var host = getOrPlacePartHost(level, pos, true, null);
Technici4n marked this conversation as resolved.
Show resolved Hide resolved
if (host == null) {
return null;
}
Expand All @@ -128,28 +121,65 @@ public static <T extends IPart> T setPart(ServerLevel level, BlockPos pos, @Null
* <p/>
* Use {@link IPartHost#isEmpty()} and {@link IPartHost#cleanup()}.
*
* @param force If true, an existing non-cable-bus block will be unconditionally replaced.
* @param force If true, an existing non-cable-bus block will be unconditionally replaced.
* @param player The player trying to place the cable bus. Will be used to check if the player can actually place it
* if force is not true.
*/
@Nullable
public static IPartHost getOrPlacePartHost(ServerLevel level, BlockPos pos, boolean force) {
public static IPartHost getOrPlacePartHost(Level level, BlockPos pos, boolean force, @Nullable Player player) {
// Get or place part host
var blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof IPartHost partHost) {
return partHost;
} else {
if (!force) {
var whatToPlace = AEBlocks.CABLE_BUS.stack();
var ctx = new DirectionalPlaceContext(level, pos, Direction.DOWN, whatToPlace, Direction.UP);
if (!level.getBlockState(pos).canBeReplaced(ctx)) {
return null;
}
if (!force && !canPlacePartHost(player, level, pos)) {
return null;
}

level.setBlockAndUpdate(pos, AEBlocks.CABLE_BUS.block().defaultBlockState());
var state = AEBlocks.CABLE_BUS.block().getStateForPlacement(level, pos);
level.setBlockAndUpdate(pos, state);
return level.getBlockEntity(pos, AEBlockEntities.CABLE_BUS).orElse(null);
}
}

/**
* Tries placing a new part host at the given location as a player.
*
* @return null if placing a new bus fails (even if a bus already is at that location)
*/
@Nullable
public static IPartHost placePartHost(@Nullable Player player, Level level, BlockPos pos) {
// Get or place part host
if (!canPlacePartHost(player, level, pos)) {
return null;
}

var state = AEBlocks.CABLE_BUS.block().getStateForPlacement(level, pos);
level.setBlockAndUpdate(pos, state);
return level.getBlockEntity(pos, AEBlockEntities.CABLE_BUS).orElse(null);
}

public static boolean canPlacePartHost(@Nullable Player player, Level level, BlockPos pos) {
if (player != null && !level.mayInteract(player, pos)) {
return false;
}

return level.isEmptyBlock(pos) || level.getBlockState(pos).getMaterial().isReplaceable();
}

/**
* Gets a part host at the given position.
*/
@Nullable
public static IPartHost getPartHost(Level level, BlockPos pos) {
var blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof IPartHost partHost) {
return partHost;
}

return null;
}

/**
* @return the render mode
*/
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/appeng/block/networking/CableBusBlock.java
Expand Up @@ -300,12 +300,12 @@ protected BlockState updateBlockStateFromBlockEntity(BlockState currentState, Ca
@Override
@Nullable
public BlockState getStateForPlacement(BlockPlaceContext context) {
BlockPos pos = context.getClickedPos();
FluidState fluidState = context.getLevel().getFluidState(pos);
BlockState blockState = this.defaultBlockState()
.setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER);
return getStateForPlacement(context.getLevel(), context.getClickedPos());
}

return blockState;
public BlockState getStateForPlacement(Level level, BlockPos pos) {
var fluidState = level.getFluidState(pos);
return this.defaultBlockState().setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER);
}

@Override
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/appeng/core/AEConfig.java
Expand Up @@ -418,6 +418,13 @@ public int getPathfindingStepsPerTick() {
return COMMON.pathfindingStepsPerTick.get();
}

/**
* @return True if an in-world preview of parts and facade placement should be shown when holding one in hand.
*/
public boolean isPlacementPreviewEnabled() {
return CLIENT.showPlacementPreview.get();
}

// Setters keep visibility as low as possible.

private static class ClientConfig {
Expand All @@ -429,6 +436,7 @@ private static class ClientConfig {
public final BooleanOption disableColoredCableRecipesInJEI;
public final EnumOption<PowerUnits> selectedPowerUnit;
public final BooleanOption debugGuiOverlays;
public final BooleanOption showPlacementPreview;

// Terminal Settings
public final EnumOption<YesNo> searchTooltips;
Expand All @@ -443,6 +451,8 @@ public ClientConfig(ConfigSection root) {
this.useColoredCraftingStatus = client.addBoolean("useColoredCraftingStatus", true);
this.selectedPowerUnit = client.addEnum("PowerUnit", PowerUnits.AE, "Power unit shown in AE UIs");
this.debugGuiOverlays = client.addBoolean("showDebugGuiOverlays", false, "Show debugging GUI overlays");
this.showPlacementPreview = client.addBoolean("showPlacementPreview", true,
"Show a preview of part and facade placement");

ConfigSection terminals = root.subsection("terminals");
this.searchTooltips = terminals.addEnum("searchTooltips", YesNo.YES,
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/appeng/core/AppEngBase.java
Expand Up @@ -80,7 +80,6 @@
import appeng.init.worldgen.InitFeatures;
import appeng.init.worldgen.InitStructures;
import appeng.items.tools.NetworkToolItem;
import appeng.parts.PartPlacement;
import appeng.server.AECommand;
import appeng.server.testworld.GameTestPlotAdapter;
import appeng.services.ChunkLoadingService;
Expand Down Expand Up @@ -154,7 +153,6 @@ public AppEngBase() {

UseBlockCallback.EVENT.register(WrenchHook::onPlayerUseBlock);
UseBlockCallback.EVENT.register(ToolItemHook::onPlayerUseBlock);
UseBlockCallback.EVENT.register(PartPlacement::onPlayerUseBlock);
InitBiomeModifications.init();
}

Expand Down
2 changes: 0 additions & 2 deletions src/main/java/appeng/core/sync/BasePacketHandler.java
Expand Up @@ -44,8 +44,6 @@ public enum PacketTypes {

CONFIG_BUTTON(ConfigButtonPacket.class, ConfigButtonPacket::new),

PART_PLACEMENT(PartPlacementPacket.class, PartPlacementPacket::new),

LIGHTNING(LightningPacket.class, LightningPacket::new),

MATTER_CANNON(MatterCannonPacket.class, MatterCannonPacket::new),
Expand Down
80 changes: 0 additions & 80 deletions src/main/java/appeng/core/sync/packets/PartPlacementPacket.java

This file was deleted.

7 changes: 6 additions & 1 deletion src/main/java/appeng/facade/FacadeContainer.java
Expand Up @@ -42,9 +42,14 @@ public FacadeContainer(CableBusStorage cbs, Runnable changeCallback) {
this.changeCallback = changeCallback;
}

@Override
public boolean canAddFacade(IFacadePart a) {
return this.getFacade(a.getSide()) == null;
}

@Override
public boolean addFacade(IFacadePart a) {
if (this.getFacade(a.getSide()) == null) {
if (canAddFacade(a)) {
this.storage.setFacade(a.getSide(), a);
this.notifyChange();
return true;
Expand Down