Decreasing itemstacks in survival. Disabled replacing blocks in survival. Expanded config options for survival.

Added keybinds to switch to previous build mode, and to switch between disabled and previous.
Updated RandomizerBags to use new placement method in vanilla mode. Removed SurvivalHelper placement methods.
This commit is contained in:
Christian Knaapen
2023-06-11 17:23:01 +02:00
parent 410d154276
commit 594b5e1c0d
23 changed files with 271 additions and 385 deletions

View File

@@ -40,13 +40,15 @@ public class ClientEvents {
EffortlessBuilding.log("Registering KeyMappings!"); EffortlessBuilding.log("Registering KeyMappings!");
// register key bindings // register key bindings
keyBindings = new KeyMapping[4]; keyBindings = new KeyMapping[6];
// instantiate the key bindings // instantiate the key bindings
keyBindings[0] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category"); keyBindings[0] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category");
keyBindings[1] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category"); keyBindings[1] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category");
keyBindings[2] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category"); keyBindings[2] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category");
keyBindings[3] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category"); keyBindings[3] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category");
keyBindings[4] = new KeyMapping("key.effortlessbuilding.previous_build_mode.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
keyBindings[5] = new KeyMapping("key.effortlessbuilding.disable_build_mode_toggle.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
for (KeyMapping keyBinding : keyBindings) { for (KeyMapping keyBinding : keyBindings) {
event.register(keyBinding); event.register(keyBinding);
@@ -158,6 +160,16 @@ public class ClientEvents {
if (keyBindings[3].consumeClick()) { if (keyBindings[3].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.REDO); ModeOptions.performAction(player, ModeOptions.ActionEnum.REDO);
} }
//Previous build mode
if (keyBindings[4].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.PREVIOUS_BUILD_MODE);
}
//Disable build mode toggle
if (keyBindings[5].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.DISABLE_BUILD_MODE_TOGGLE);
}
} }
public static void openModifierSettings() { public static void openModifierSettings() {

View File

@@ -8,67 +8,100 @@ public class CommonConfig {
private static final Builder builder = new Builder(); private static final Builder builder = new Builder();
public static final Reach reach = new Reach(builder); public static final Reach reach = new Reach(builder);
public static final SurvivalBalancers survivalBalancers = new SurvivalBalancers(builder); public static final MaxBlocksPlacedAtOnce maxBlocksPlacedAtOnce = new MaxBlocksPlacedAtOnce(builder);
public static final MaxBlocksPerAxis maxBlocksPerAxis = new MaxBlocksPerAxis(builder);
public static final ForgeConfigSpec spec = builder.build(); public static final ForgeConfigSpec spec = builder.build();
public static class Reach { public static class Reach {
public final BooleanValue enableReachUpgrades; public final IntValue reachCreative;
public final IntValue maxReachCreative; public final IntValue reachLevel0;
public final IntValue maxReachLevel0; public final IntValue reachLevel1;
public final IntValue maxReachLevel1; public final IntValue reachLevel2;
public final IntValue maxReachLevel2; public final IntValue reachLevel3;
public final IntValue maxReachLevel3;
public Reach(Builder builder) { public Reach(Builder builder) {
builder.push("Reach"); builder.push("Reach");
enableReachUpgrades = builder
.comment("Reach: how far away the player can place blocks using mirror/array etc.",
"Enable the crafting of reach upgrades to increase reach.",
"If disabled, reach is set to level 3 for survival players.")
.define("enableReachUpgrades", true);
maxReachCreative = builder reachCreative = builder
.comment("Maximum reach in creative", .comment("How far away the player can place and break blocks.")
"Keep in mind that chunks need to be loaded to be able to place blocks inside.") .defineInRange("maxReachCreative", 200, 0, 10000);
.defineInRange("maxReachCreative", 200, 0, 1000);
maxReachLevel0 = builder reachLevel0 = builder
.comment("Maximum reach in survival without upgrades", .comment("Maximum reach in survival without upgrades",
"Reach upgrades are craftable consumables that permanently increase reach.", "Reach upgrades are craftable consumables that permanently increase reach.",
"Set to 0 to disable Effortless Building until the player has consumed a reach upgrade.") "Set to 0 to disable Effortless Building until the player has consumed a reach upgrade.")
.defineInRange("maxReachLevel0", 20, 0, 1000); .defineInRange("reachLevel0", 20, 0, 10000);
maxReachLevel1 = builder reachLevel1 = builder
.comment("Maximum reach in survival with one upgrade") .defineInRange("reachLevel1", 50, 0, 10000);
.defineInRange("maxReachLevel1", 50, 0, 1000);
maxReachLevel2 = builder reachLevel2 = builder
.comment("Maximum reach in survival with two upgrades") .defineInRange("reachLevel2", 100, 0, 10000);
.defineInRange("maxReachLevel2", 100, 0, 1000);
maxReachLevel3 = builder reachLevel3 = builder
.comment("Maximum reach in survival with three upgrades") .defineInRange("reachLevel3", 200, 0, 10000);
.defineInRange("maxReachLevel3", 200, 0, 1000);
builder.pop(); builder.pop();
} }
} }
public static class SurvivalBalancers { public static class MaxBlocksPlacedAtOnce {
public final IntValue quickReplaceMiningLevel; public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public SurvivalBalancers(Builder builder) { public MaxBlocksPlacedAtOnce(Builder builder) {
builder.push("SurvivalBalancers"); builder.push("MaxBlocksPlacedAtOnce");
quickReplaceMiningLevel = builder creative = builder
.comment("Determines what blocks can be replaced in survival.", .comment("How many blocks can be placed in one click.")
"-1: only blocks that can be harvested by hand (default)", .defineInRange("maxBlocksPlacedAtOnceCreative", 10000, 0, 10000);
"0: blocks that can be harvested with wooden or gold tools",
"1: blocks that can be harvested with stone tools", level0 = builder
"2: blocks that can be harvested with iron tools", .comment("Maximum blocks placed at once in survival without upgrades")
"3: blocks that can be harvested with diamond tools", .defineInRange("maxBlocksPlacedAtOnceLevel0", 100, 0, 10000);
"4: blocks that can be harvested with netherite tools")
.defineInRange("quickReplaceMiningLevel", -1, -1, 3); level1 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel1", 200, 0, 10000);
level2 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel2", 500, 0, 10000);
level3 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel3", 1000, 0, 10000);
builder.pop();
}
}
public static class MaxBlocksPerAxis {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxBlocksPerAxis(Builder builder) {
builder.push("MaxBlocksPerAxis");
creative = builder
.comment("How many blocks can be placed at once per axis.")
.defineInRange("maxBlocksPerAxisCreative", 10000, 0, 10000);
level0 = builder
.comment("Maximum blocks placed at once in survival without upgrades")
.defineInRange("maxBlocksPerAxisLevel0", 100, 0, 10000);
level1 = builder
.defineInRange("maxBlocksPerAxisLevel1", 200, 0, 10000);
level2 = builder
.defineInRange("maxBlocksPerAxisLevel2", 500, 0, 10000);
level3 = builder
.defineInRange("maxBlocksPerAxisLevel3", 1000, 0, 10000);
builder.pop(); builder.pop();
} }

View File

@@ -27,6 +27,7 @@ import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.proxy.ClientProxy; import nl.requios.effortlessbuilding.proxy.ClientProxy;
import nl.requios.effortlessbuilding.proxy.IProxy; import nl.requios.effortlessbuilding.proxy.IProxy;
import nl.requios.effortlessbuilding.proxy.ServerProxy; import nl.requios.effortlessbuilding.proxy.ServerProxy;
import nl.requios.effortlessbuilding.systems.ItemUsageTracker;
import nl.requios.effortlessbuilding.systems.ServerBlockPlacer; import nl.requios.effortlessbuilding.systems.ServerBlockPlacer;
import nl.requios.effortlessbuilding.systems.UndoRedo; import nl.requios.effortlessbuilding.systems.UndoRedo;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -43,6 +44,7 @@ public class EffortlessBuilding {
public static final ServerBlockPlacer SERVER_BLOCK_PLACER = new ServerBlockPlacer(); public static final ServerBlockPlacer SERVER_BLOCK_PLACER = new ServerBlockPlacer();
public static final UndoRedo UNDO_REDO = new UndoRedo(); public static final UndoRedo UNDO_REDO = new UndoRedo();
public static final ItemUsageTracker ITEM_USAGE_TRACKER = new ItemUsageTracker();
//Registration //Registration
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
@@ -107,4 +109,8 @@ public class EffortlessBuilding {
proxy.logTranslate(player, prefix, translationKey, suffix, actionBar); proxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
} }
public static void logError(String msg) {
logger.error(msg);
}
} }

View File

@@ -35,7 +35,7 @@ public class ServerConfig {
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true); .defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
maxBlocksPlacedAtOnce = builder maxBlocksPlacedAtOnce = builder
.comment("Maximum number of blocks that can be placed at once.") .comment("Maximum number of blocks that can be placed at once. This is a last check. If you want the player to receive visual feedback instead of an error message, change values in the common config.")
.defineInRange("maxBlocksPlacedAtOnce", 10000, 1, 100000); .defineInRange("maxBlocksPlacedAtOnce", 10000, 1, 100000);
builder.pop(); builder.pop();

View File

@@ -1,22 +1,23 @@
package nl.requios.effortlessbuilding.buildmode; package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.IsUsingBuildModePacket; import nl.requios.effortlessbuilding.network.IsUsingBuildModePacket;
import nl.requios.effortlessbuilding.network.PacketHandler; import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet; import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import java.util.*;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public class BuildModes { public class BuildModes {
private BuildModeEnum buildMode = BuildModeEnum.DISABLED; private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
private BuildModeEnum previousBuildMode = BuildModeEnum.DISABLED;
private BuildModeEnum beforeDisabledBuildMode = BuildModeEnum.SINGLE;
public void findCoordinates(BlockSet blocks, Player player) { public void findCoordinates(BlockSet blocks, Player player) {
buildMode.instance.findCoordinates(blocks); buildMode.instance.findCoordinates(blocks);
@@ -30,6 +31,23 @@ public class BuildModes {
this.buildMode = buildMode; this.buildMode = buildMode;
PacketHandler.INSTANCE.sendToServer(new IsUsingBuildModePacket(this.buildMode != BuildModeEnum.DISABLED)); PacketHandler.INSTANCE.sendToServer(new IsUsingBuildModePacket(this.buildMode != BuildModeEnum.DISABLED));
EffortlessBuilding.log(Minecraft.getInstance().player, I18n.get(buildMode.getNameKey()), true);
}
public void activatePreviousBuildMode() {
var temp = buildMode;
setBuildMode(previousBuildMode);
previousBuildMode = temp;
}
public void activateDisableBuildModeToggle(){
if (buildMode == BuildModeEnum.DISABLED) {
setBuildMode(beforeDisabledBuildMode);
} else {
beforeDisabledBuildMode = buildMode;
setBuildMode(BuildModeEnum.DISABLED);
}
} }
public void onCancel() { public void onCancel() {

View File

@@ -73,6 +73,8 @@ public class ModeOptions {
case REDO -> PacketHandler.INSTANCE.sendToServer(new PerformRedoPacket()); case REDO -> PacketHandler.INSTANCE.sendToServer(new PerformRedoPacket());
case OPEN_MODIFIER_SETTINGS -> ClientEvents.openModifierSettings(); case OPEN_MODIFIER_SETTINGS -> ClientEvents.openModifierSettings();
case OPEN_PLAYER_SETTINGS -> ClientEvents.openPlayerSettings(); case OPEN_PLAYER_SETTINGS -> ClientEvents.openPlayerSettings();
case PREVIOUS_BUILD_MODE -> EffortlessBuildingClient.BUILD_MODES.activatePreviousBuildMode();
case DISABLE_BUILD_MODE_TOGGLE -> EffortlessBuildingClient.BUILD_MODES.activateDisableBuildModeToggle();
case REPLACE_ONLY_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_AIR); case REPLACE_ONLY_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_AIR);
case REPLACE_BLOCKS_AND_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.BLOCKS_AND_AIR); case REPLACE_BLOCKS_AND_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.BLOCKS_AND_AIR);
@@ -103,7 +105,9 @@ public class ModeOptions {
if (player.level.isClientSide && if (player.level.isClientSide &&
action != ActionEnum.OPEN_MODIFIER_SETTINGS && action != ActionEnum.OPEN_MODIFIER_SETTINGS &&
action != ActionEnum.OPEN_PLAYER_SETTINGS) { action != ActionEnum.OPEN_PLAYER_SETTINGS &&
action != ActionEnum.PREVIOUS_BUILD_MODE &&
action != ActionEnum.DISABLE_BUILD_MODE_TOGGLE) {
EffortlessBuilding.logTranslate(player, "", action.getNameKey(), "", true); EffortlessBuilding.logTranslate(player, "", action.getNameKey(), "", true);
} }
@@ -114,6 +118,8 @@ public class ModeOptions {
REDO("redo", AllIcons.I_REDO), REDO("redo", AllIcons.I_REDO),
OPEN_MODIFIER_SETTINGS("open_modifier_settings", AllIcons.I_SETTINGS), OPEN_MODIFIER_SETTINGS("open_modifier_settings", AllIcons.I_SETTINGS),
OPEN_PLAYER_SETTINGS("open_player_settings", AllIcons.I_SETTINGS), OPEN_PLAYER_SETTINGS("open_player_settings", AllIcons.I_SETTINGS),
PREVIOUS_BUILD_MODE("previous_build_mode", AllIcons.I_SINGLE),
DISABLE_BUILD_MODE_TOGGLE("disable_build_mode_toggle", AllIcons.I_DISABLE),
REPLACE_ONLY_AIR("replace_only_air", AllIcons.I_REPLACE_AIR), REPLACE_ONLY_AIR("replace_only_air", AllIcons.I_REPLACE_AIR),
REPLACE_BLOCKS_AND_AIR("replace_blocks_and_air", AllIcons.I_REPLACE_BLOCKS_AND_AIR), REPLACE_BLOCKS_AND_AIR("replace_blocks_and_air", AllIcons.I_REPLACE_BLOCKS_AND_AIR),

View File

@@ -285,9 +285,8 @@ public class BlockHelper {
} }
try { try {
state.getBlock() state.getBlock().setPlacedBy(world, target, state, null, stack);
.setPlacedBy(world, target, state, null, stack); } catch (Exception ignored) {
} catch (Exception e) {
} }
return true; return true;
} }

View File

@@ -3,7 +3,6 @@ package nl.requios.effortlessbuilding.gui.buildmode;
import com.mojang.blaze3d.vertex.*; import com.mojang.blaze3d.vertex.*;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Vector4f; import com.mojang.math.Vector4f;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
@@ -14,23 +13,20 @@ import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.MutableComponent;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.create.foundation.item.ItemDescription; import nl.requios.effortlessbuilding.create.foundation.item.ItemDescription;
import nl.requios.effortlessbuilding.create.foundation.item.TooltipHelper; import nl.requios.effortlessbuilding.create.foundation.item.TooltipHelper;
import nl.requios.effortlessbuilding.create.foundation.utility.Components; import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang; import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import org.apache.commons.lang3.text.WordUtils; import nl.requios.effortlessbuilding.systems.PowerLevel;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList; import java.util.ArrayList;
import static nl.requios.effortlessbuilding.buildmode.ModeOptions.*; import static nl.requios.effortlessbuilding.buildmode.ModeOptions.*;
@@ -154,10 +150,12 @@ public class RadialMenu extends Screen {
buttons.add(new MenuButton(ActionEnum.UNDO, -buttonDistance - 26, -13, Direction.UP)); buttons.add(new MenuButton(ActionEnum.UNDO, -buttonDistance - 26, -13, Direction.UP));
buttons.add(new MenuButton(ActionEnum.REDO, -buttonDistance, -13, Direction.UP)); buttons.add(new MenuButton(ActionEnum.REDO, -buttonDistance, -13, Direction.UP));
if (Minecraft.getInstance().player != null && PowerLevel.canReplaceBlocks(Minecraft.getInstance().player)) {
buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_AIR, -buttonDistance - 78, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_AIR, -buttonDistance - 78, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_BLOCKS_AND_AIR, -buttonDistance - 52, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.REPLACE_BLOCKS_AND_AIR, -buttonDistance - 52, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_BLOCKS, -buttonDistance - 26, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.REPLACE_ONLY_BLOCKS, -buttonDistance - 26, 13, Direction.DOWN));
buttons.add(new MenuButton(ActionEnum.REPLACE_FILTERED_BY_OFFHAND, -buttonDistance, 13, Direction.DOWN)); buttons.add(new MenuButton(ActionEnum.REPLACE_FILTERED_BY_OFFHAND, -buttonDistance, 13, Direction.DOWN));
}
//Add buildmode dependent options //Add buildmode dependent options
OptionEnum[] options = currentBuildMode.options; OptionEnum[] options = currentBuildMode.options;
@@ -426,8 +424,6 @@ public class RadialMenu extends Screen {
EffortlessBuildingClient.BUILD_MODES.setBuildMode(switchTo); EffortlessBuildingClient.BUILD_MODES.setBuildMode(switchTo);
EffortlessBuilding.log(player, I18n.get(switchTo.getNameKey()), true);
if (fromMouseClick) performedActionUsingMouse = true; if (fromMouseClick) performedActionUsingMouse = true;
} }

View File

@@ -25,15 +25,16 @@ import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkHooks; import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.systems.ServerBuildState; import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper; import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList; import java.util.*;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
@@ -132,15 +133,14 @@ public abstract class AbstractRandomizerBagItem extends Item {
} else { } else {
if (world.isClientSide) return InteractionResult.SUCCESS; if (world.isClientSide) return InteractionResult.SUCCESS;
//Only place manually if in normal vanilla mode //---Only place manually if in normal vanilla mode---
if (!ServerBuildState.isLikeVanilla(player)) { if (!ServerBuildState.isLikeVanilla(player)) {
return InteractionResult.FAIL; return InteractionResult.FAIL;
} }
//Use item //Use item
//Get bag inventory //Get bag inventory
//TODO offhand support ItemStack bag = ctx.getItemInHand();
ItemStack bag = player.getItemInHand(InteractionHand.MAIN_HAND);
IItemHandler bagInventory = getBagInventory(bag); IItemHandler bagInventory = getBagInventory(bag);
if (bagInventory == null) if (bagInventory == null)
return InteractionResult.FAIL; return InteractionResult.FAIL;
@@ -148,26 +148,16 @@ public abstract class AbstractRandomizerBagItem extends Item {
ItemStack toPlace = pickRandomStack(bagInventory); ItemStack toPlace = pickRandomStack(bagInventory);
if (toPlace.isEmpty()) return InteractionResult.FAIL; if (toPlace.isEmpty()) return InteractionResult.FAIL;
//Previously: use onItemUse to place block (no synergy)
//bag.setItemDamage(toPlace.getMetadata());
//toPlace.onItemUse(player, world, pos, hand, facing, hitX, hitY, hitZ);
//TODO replaceable
if (!world.getBlockState(pos).getBlock().canBeReplaced(world.getBlockState(pos), Fluids.EMPTY)) { if (!world.getBlockState(pos).getBlock().canBeReplaced(world.getBlockState(pos), Fluids.EMPTY)) {
pos = pos.relative(facing); pos = pos.relative(facing);
} }
BlockPlaceContext blockItemUseContext = new BlockPlaceContext(new UseOnContext(player, InteractionHand.MAIN_HAND, new BlockHitResult(hitVec, facing, pos, false))); BlockPlaceContext blockItemUseContext = new BlockPlaceContext(new UseOnContext(player, ctx.getHand(), new BlockHitResult(hitVec, facing, pos, false)));
BlockState blockState = Block.byItem(toPlace.getItem()).getStateForPlacement(blockItemUseContext); BlockState blockState = Block.byItem(toPlace.getItem()).getStateForPlacement(blockItemUseContext);
SurvivalHelper.placeBlock(world, player, pos, blockState, toPlace, false, false, true); var blockEntry = new BlockEntry(pos, blockState, toPlace.getItem());
var blockSet = new BlockSet(List.of(blockEntry), pos, pos, false);
//Synergy EffortlessBuilding.SERVER_BLOCK_PLACER.applyBlockSet(player, blockSet);
//Works without calling
// BlockSnapshot blockSnapshot = new BlockSnapshot(player.world, pos, blockState);
// BlockEvent.PlaceEvent placeEvent = new BlockEvent.PlaceEvent(blockSnapshot, blockState, player, hand);
// Mirror.onBlockPlaced(placeEvent);
// Array.onBlockPlaced(placeEvent);
} }
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
} }

View File

@@ -61,7 +61,7 @@ public class ReachUpgrade1Item extends Item {
@Override @Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) { public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel1.get())); tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.reachLevel1.get()));
} }
} }

View File

@@ -66,7 +66,7 @@ public class ReachUpgrade2Item extends Item {
@Override @Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) { public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel2.get())); tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.reachLevel2.get()));
tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first")); tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first"));
} }
} }

View File

@@ -69,7 +69,7 @@ public class ReachUpgrade3Item extends Item {
@Override @Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) { public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag) {
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel3.get())); tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.reachLevel3.get()));
tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first")); tooltip.add(Component.literal(ChatFormatting.GRAY + "Previous upgrades need to be consumed first"));
} }

View File

@@ -1,5 +1,6 @@
package nl.requios.effortlessbuilding.systems; package nl.requios.effortlessbuilding.systems;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeOptions;
@@ -19,7 +20,7 @@ public class BuildSettings {
private boolean protectTileEntities = true; private boolean protectTileEntities = true;
public boolean isQuickReplacing() { public boolean isQuickReplacing() {
return replaceMode != ReplaceMode.ONLY_AIR; return getReplaceMode() != ReplaceMode.ONLY_AIR;
} }
public void setReplaceMode(ReplaceMode replaceMode) { public void setReplaceMode(ReplaceMode replaceMode) {
@@ -28,11 +29,12 @@ public class BuildSettings {
} }
public ReplaceMode getReplaceMode() { public ReplaceMode getReplaceMode() {
if (!canReplaceBlocks()) return ReplaceMode.ONLY_AIR;
return replaceMode; return replaceMode;
} }
public ModeOptions.ActionEnum getReplaceModeActionEnum() { public ModeOptions.ActionEnum getReplaceModeActionEnum() {
return switch (replaceMode) { return switch (getReplaceMode()) {
case ONLY_AIR -> ModeOptions.ActionEnum.REPLACE_ONLY_AIR; case ONLY_AIR -> ModeOptions.ActionEnum.REPLACE_ONLY_AIR;
case BLOCKS_AND_AIR -> ModeOptions.ActionEnum.REPLACE_BLOCKS_AND_AIR; case BLOCKS_AND_AIR -> ModeOptions.ActionEnum.REPLACE_BLOCKS_AND_AIR;
case ONLY_BLOCKS -> ModeOptions.ActionEnum.REPLACE_ONLY_BLOCKS; case ONLY_BLOCKS -> ModeOptions.ActionEnum.REPLACE_ONLY_BLOCKS;
@@ -45,15 +47,15 @@ public class BuildSettings {
} }
public boolean shouldReplaceAir() { public boolean shouldReplaceAir() {
return replaceMode == ReplaceMode.ONLY_AIR || replaceMode == ReplaceMode.BLOCKS_AND_AIR; return getReplaceMode() == ReplaceMode.ONLY_AIR || getReplaceMode() == ReplaceMode.BLOCKS_AND_AIR;
} }
public boolean shouldReplaceBlocks() { public boolean shouldReplaceBlocks() {
return replaceMode == ReplaceMode.ONLY_BLOCKS || replaceMode == ReplaceMode.BLOCKS_AND_AIR; return getReplaceMode() == ReplaceMode.ONLY_BLOCKS || getReplaceMode() == ReplaceMode.BLOCKS_AND_AIR;
} }
public boolean shouldReplaceFiltered() { public boolean shouldReplaceFiltered() {
return replaceMode == ReplaceMode.FILTERED_BY_OFFHAND; return getReplaceMode() == ReplaceMode.FILTERED_BY_OFFHAND;
} }
public boolean shouldProtectTileEntities() { public boolean shouldProtectTileEntities() {
@@ -61,6 +63,10 @@ public class BuildSettings {
} }
public boolean shouldOffsetStartPosition() { public boolean shouldOffsetStartPosition() {
return replaceMode != ReplaceMode.ONLY_AIR; return getReplaceMode() != ReplaceMode.ONLY_AIR;
}
public boolean canReplaceBlocks(){
return Minecraft.getInstance().player != null && PowerLevel.canReplaceBlocks(Minecraft.getInstance().player);
} }
} }

View File

@@ -262,7 +262,11 @@ public class BuilderChain {
Vec3 relativeHitVec = lookingAt.getLocation().subtract(Vec3.atLowerCornerOf(lookingAt.getBlockPos())); Vec3 relativeHitVec = lookingAt.getLocation().subtract(Vec3.atLowerCornerOf(lookingAt.getBlockPos()));
//Keep track of itemstack usage //Keep track of itemstack usage
EffortlessBuildingClient.ITEM_USAGE_TRACKER.initialize(player, heldItem); EffortlessBuildingClient.ITEM_USAGE_TRACKER.initialize();
if (CompatHelper.isItemBlockProxy(heldItem, false)) {
AbstractRandomizerBagItem.resetRandomness();
}
var iter = blocks.entrySet().iterator(); var iter = blocks.entrySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {

View File

@@ -3,8 +3,6 @@ package nl.requios.effortlessbuilding.systems;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.utilities.InventoryHelper; import nl.requios.effortlessbuilding.utilities.InventoryHelper;
@@ -12,7 +10,7 @@ import nl.requios.effortlessbuilding.utilities.InventoryHelper;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@OnlyIn(Dist.CLIENT) //Common, both client and server have an instance of this
public class ItemUsageTracker { public class ItemUsageTracker {
//How many blocks we want to place //How many blocks we want to place
@@ -21,17 +19,17 @@ public class ItemUsageTracker {
//How many blocks we have in inventory in total //How many blocks we have in inventory in total
public Map<Item, Integer> inInventory = new HashMap<>(); public Map<Item, Integer> inInventory = new HashMap<>();
//How many blocks we can place or have placed
public Map<Item, Integer> placed = new HashMap<>();
//How many blocks are missing from our inventory //How many blocks are missing from our inventory
public Map<Item, Integer> missing = new HashMap<>(); public Map<Item, Integer> missing = new HashMap<>();
public void initialize(Player player, ItemStack heldItem) { public void initialize() {
total.clear(); total.clear();
inInventory.clear(); inInventory.clear();
placed.clear();
missing.clear(); missing.clear();
if (CompatHelper.isItemBlockProxy(heldItem, false)) {
AbstractRandomizerBagItem.resetRandomness();
}
} }
//returns if we have enough items in inventory to use count more //returns if we have enough items in inventory to use count more
@@ -57,6 +55,7 @@ public class ItemUsageTracker {
for (Item item : total.keySet()) { for (Item item : total.keySet()) {
int used = total.get(item); int used = total.get(item);
int have = inInventory.getOrDefault(item, 0); int have = inInventory.getOrDefault(item, 0);
placed.put(item, Math.min(used, have));
if (used > have) { if (used > have) {
missing.put(item, used - have); missing.put(item, used - have);
} }

View File

@@ -9,10 +9,7 @@ import net.minecraftforge.items.ItemHandlerHelper;
import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.ServerConfig; import nl.requios.effortlessbuilding.ServerConfig;
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper; import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.utilities.*;
import nl.requios.effortlessbuilding.utilities.BlockPlacerHelper;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.BlockUtilities;
import java.util.*; import java.util.*;
@@ -61,6 +58,7 @@ public class ServerBlockPlacer {
if (!checkAndNotifyAllowedToUseMod(player)) return; if (!checkAndNotifyAllowedToUseMod(player)) return;
if (!validateBlockSet(player, blocks)) return; if (!validateBlockSet(player, blocks)) return;
EffortlessBuilding.ITEM_USAGE_TRACKER.initialize();
var undoSet = new BlockSet(); var undoSet = new BlockSet();
for (BlockEntry block : blocks) { for (BlockEntry block : blocks) {
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue; if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
@@ -69,14 +67,22 @@ public class ServerBlockPlacer {
undoSet.add(block); undoSet.add(block);
} }
} }
if (isAllowedToUndo(player, false))
//Remove items from inventory
//(Adding items is done during BlockPlacerHelper.breakBlock)
EffortlessBuilding.ITEM_USAGE_TRACKER.calculateMissingItems(player);
if (!player.isCreative()) {
InventoryHelper.removeFromInventory(player, EffortlessBuilding.ITEM_USAGE_TRACKER.placed);
}
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet); EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
} }
public void undoBlockSet(Player player, BlockSet blocks) { public void undoBlockSet(Player player, BlockSet blocks) {
if (!isAllowedToUndo(player, true)) return; if (!EffortlessBuilding.UNDO_REDO.isAllowedToUndo(player)) return;
EffortlessBuilding.ITEM_USAGE_TRACKER.initialize();
var redoSet = new BlockSet(); var redoSet = new BlockSet();
for (BlockEntry block : blocks) { for (BlockEntry block : blocks) {
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue; if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
@@ -85,6 +91,14 @@ public class ServerBlockPlacer {
redoSet.add(block); redoSet.add(block);
} }
} }
//Remove items from inventory
//(Adding items is done during BlockPlacerHelper.breakBlock)
EffortlessBuilding.ITEM_USAGE_TRACKER.calculateMissingItems(player);
if (!player.isCreative()) {
InventoryHelper.removeFromInventory(player, EffortlessBuilding.ITEM_USAGE_TRACKER.placed);
}
EffortlessBuilding.UNDO_REDO.addRedo(player, redoSet); EffortlessBuilding.UNDO_REDO.addRedo(player, redoSet);
} }
@@ -99,7 +113,15 @@ public class ServerBlockPlacer {
if (breaking) { if (breaking) {
success = BlockPlacerHelper.breakBlock(player, block); success = BlockPlacerHelper.breakBlock(player, block);
} else { } else {
//If we have the item in our inventory, place it
if (EffortlessBuilding.ITEM_USAGE_TRACKER.increaseUsageCount(block.item, 1, player)) {
success = BlockPlacerHelper.placeBlock(player, block); success = BlockPlacerHelper.placeBlock(player, block);
} else {
success = false;
//Not having the item at this point would be a bit weird, so we notify the player
//It could mean the client/server are out of sync, or the inventory changed during the short delay period
EffortlessBuilding.log(player, ChatFormatting.RED + block.item.toString() + " not found in inventory.");
}
} }
isPlacingOrBreakingBlocks = false; isPlacingOrBreakingBlocks = false;
return success; return success;
@@ -124,7 +146,14 @@ public class ServerBlockPlacer {
if (breaking) { if (breaking) {
success = BlockPlacerHelper.breakBlock(player, tempBlockEntry); success = BlockPlacerHelper.breakBlock(player, tempBlockEntry);
} else { } else {
//If we have the item in our inventory, place it
if (EffortlessBuilding.ITEM_USAGE_TRACKER.increaseUsageCount(tempBlockEntry.item, 1, player)) {
success = BlockPlacerHelper.placeBlock(player, tempBlockEntry); success = BlockPlacerHelper.placeBlock(player, tempBlockEntry);
} else {
success = false;
//Not having the item at this point would be a bit weird, so we notify the player
EffortlessBuilding.log(player, ChatFormatting.RED + tempBlockEntry.item.toString() + " not found in inventory.");
}
} }
isPlacingOrBreakingBlocks = false; isPlacingOrBreakingBlocks = false;
@@ -156,16 +185,6 @@ public class ServerBlockPlacer {
return true; return true;
} }
private boolean isAllowedToUndo(Player player, boolean log) {
if (!player.isCreative()) {
if (log) EffortlessBuilding.log(player, ChatFormatting.RED + "Undo is not available in survival mode.");
return false;
}
return true;
}
private boolean validateBlockSet(Player player, BlockSet blocks) { private boolean validateBlockSet(Player player, BlockSet blocks) {
if (blocks.isEmpty()) { if (blocks.isEmpty()) {

View File

@@ -25,6 +25,7 @@ public class ServerBuildState {
} }
public static boolean isQuickReplacing(Player player) { public static boolean isQuickReplacing(Player player) {
if (!PowerLevel.canReplaceBlocks(player)) return false;
return player.getPersistentData().contains(IS_QUICK_REPLACING_KEY); return player.getPersistentData().contains(IS_QUICK_REPLACING_KEY);
} }

View File

@@ -30,10 +30,7 @@ public class UndoRedo {
public final Map<UUID, FixedStack<BlockSet>> undoStacks = new HashMap<>(); public final Map<UUID, FixedStack<BlockSet>> undoStacks = new HashMap<>();
public final Map<UUID, FixedStack<BlockSet>> redoStacks = new HashMap<>(); public final Map<UUID, FixedStack<BlockSet>> redoStacks = new HashMap<>();
private boolean isAllowedToUndo(Player player) { public boolean isAllowedToUndo(Player player) {
if (!player.isCreative()) {
return false;
}
return true; return true;
} }

View File

@@ -31,6 +31,12 @@ public class BlockEntry {
this.blockPos = blockPos; this.blockPos = blockPos;
} }
public BlockEntry(BlockPos blockPos, BlockState blockState, Item item) {
this.blockPos = blockPos;
this.newBlockState = blockState;
this.item = item;
}
public void copyRotationSettingsFrom(BlockEntry blockEntry) { public void copyRotationSettingsFrom(BlockEntry blockEntry) {
mirrorX = blockEntry.mirrorX; mirrorX = blockEntry.mirrorX;
mirrorY = blockEntry.mirrorY; mirrorY = blockEntry.mirrorY;

View File

@@ -1,35 +1,23 @@
package nl.requios.effortlessbuilding.utilities; package nl.requios.effortlessbuilding.utilities;
import com.google.common.collect.Lists;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BucketItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.Half;
import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.ForgeEventFactory;
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
import java.util.List;
//Common //Common
public class BlockUtilities { public class BlockUtilities {

View File

@@ -5,6 +5,9 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import java.util.Map;
public class InventoryHelper { public class InventoryHelper {
@@ -39,4 +42,53 @@ public class InventoryHelper {
} }
return total; return total;
} }
public static void removeFromInventory(Player player, Map<Item, Integer> items) {
for (Item item : items.keySet()) {
int count = items.get(item);
removeFromInventory(player, item, count);
}
}
public static void removeFromInventory(Player player, Item item, int amount) {
if (player.isCreative()) return;
//From BlockHelper.findAndRemoveInInventory
int amountFound = 0;
{
// Try held Item first
int preferredSlot = player.getInventory().selected;
ItemStack itemstack = player.getInventory()
.getItem(preferredSlot);
int count = itemstack.getCount();
if (itemstack.getItem() == item && count > 0) {
int taken = Math.min(count, amount - amountFound);
player.getInventory()
.setItem(preferredSlot, new ItemStack(itemstack.getItem(), count - taken));
amountFound += taken;
}
}
// Search inventory
for (int i = 0; i < player.getInventory()
.getContainerSize(); ++i) {
if (amountFound == amount)
break;
ItemStack itemstack = player.getInventory()
.getItem(i);
int count = itemstack.getCount();
if (itemstack.getItem() == item && count > 0) {
int taken = Math.min(count, amount - amountFound);
player.getInventory()
.setItem(i, new ItemStack(itemstack.getItem(), count - taken));
amountFound += taken;
}
}
if (amountFound != amount) {
EffortlessBuilding.logError(player.getDisplayName().getString() + " tried to remove " + amount + " " + item + " from inventory but only removed " + amountFound);
}
}
} }

View File

@@ -30,258 +30,9 @@ import javax.annotation.Nullable;
public class SurvivalHelper { public class SurvivalHelper {
//Used for all placing of blocks in this mod.
//Checks if area is loaded, if player has the right permissions, if existing block can be replaced (drops it if so) and consumes an item from the stack.
//Based on ItemBlock#onItemUse
public static boolean placeBlock(Level world, Player player, BlockPos pos, BlockState blockState,
ItemStack origstack, boolean skipPlaceCheck,
boolean skipCollisionCheck, boolean playSound) {
if (!world.isLoaded(pos)) return false;
ItemStack itemstack = origstack;
if (blockState.isAir() || itemstack.isEmpty()) {
dropBlock(world, player, pos);
world.removeBlock(pos, false);
return true;
}
//Randomizer bag, other proxy item synergy
//Preliminary compatibility code for other items that hold blocks
if (CompatHelper.isItemBlockProxy(itemstack))
itemstack = CompatHelper.getItemBlockByState(itemstack, blockState);
if (!(itemstack.getItem() instanceof BlockItem))
return false;
Block block = ((BlockItem) itemstack.getItem()).getBlock();
//More manual with ItemBlock#placeBlockAt
if (skipPlaceCheck || canPlace(world, player, pos, blockState, itemstack, skipCollisionCheck)) {
//Drop existing block
dropBlock(world, player, pos);
//TryPlace sets block with offset and reduces itemstack count in creative, so we copy only parts of it
// BlockItemUseContext blockItemUseContext = new BlockItemUseContext(world, player, itemstack, pos, facing, (float) hitVec.x, (float) hitVec.y, (float) hitVec.z);
// EnumActionResult result = ((ItemBlock) itemstack.getItem()).tryPlace(blockItemUseContext);
if (!world.setBlock(pos, blockState, 3)) return false;
BlockItem.updateCustomBlockEntityTag(world, player, pos, itemstack); //Actually BlockItem::onBlockPlaced but that is protected
block.setPlacedBy(world, pos, blockState, player, itemstack);
if (player instanceof ServerPlayer) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer) player, pos, itemstack);
}
BlockState afterState = world.getBlockState(pos);
if (playSound) {
SoundType soundtype = afterState.getBlock().getSoundType(afterState, world, pos, player);
world.playSound(null, pos, soundtype.getPlaceSound(), SoundSource.BLOCKS, (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F);
}
if (!player.isCreative() && Block.byItem(itemstack.getItem()) == block) {
itemstack.shrink(1);
}
return true;
}
return false;
//Using ItemBlock#onItemUse
// EnumActionResult result;
// PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock(player, EnumHand.MAIN_HAND, pos, facing, net.minecraftforge.common.ForgeHooks.rayTraceEyeHitVec(player, ReachHelper.getPlacementReach(player)));
// if (player.isCreative())
// {
// int i = itemstack.getMetadata();
// int j = itemstack.getCount();
// if (event.getUseItem() != net.minecraftforge.fml.common.eventhandler.Event.Result.DENY) {
// EnumActionResult enumactionresult = itemstack.getItem().onItemUse(player, world, pos, EnumHand.MAIN_HAND, facing, (float) hitVec.x, (float) hitVec.y, (float) hitVec.z);
// itemstack.setItemDamage(i);
// itemstack.setCount(j);
// return enumactionresult == EnumActionResult.SUCCESS;
// } else return false;
// }
// else
// {
// ItemStack copyForUse = itemstack.copy();
// if (event.getUseItem() != net.minecraftforge.fml.common.eventhandler.Event.Result.DENY)
// result = itemstack.getItem().onItemUse(player, world, pos, EnumHand.MAIN_HAND, facing, (float) hitVec.x, (float) hitVec.y, (float) hitVec.z);
// if (itemstack.isEmpty()) net.minecraftforge.event.ForgeEventFactory.onPlayerDestroyItem(player, copyForUse, EnumHand.MAIN_HAND);
// return false;
// }
}
//Used for all breaking of blocks in this mod.
//Checks if area is loaded, if appropriate tool is used in survival mode, and drops the block directly into the players inventory
public static boolean breakBlock(Level world, Player player, BlockPos pos, boolean skipChecks) {
if (!world.isLoaded(pos) && !world.isEmptyBlock(pos)) return false;
//Check if can break
if (skipChecks || canBreak(world, player, pos)) {
// player.addStat(StatList.getBlockStats(world.getNewBlockState(pos).getBlock()));
// player.addExhaustion(0.005F);
//Drop existing block
dropBlock(world, player, pos);
//Damage tool
player.getMainHandItem().mineBlock(world, world.getBlockState(pos), pos, player);
world.removeBlock(pos, false);
return true;
}
return false;
}
//Gives items directly to player
public static void dropBlock(Level world, Player player, BlockPos pos) {
if (player.isCreative()) return;
BlockState blockState = world.getBlockState(pos);
Block block = blockState.getBlock();
block.playerDestroy(world, player, pos, blockState, world.getBlockEntity(pos), player.getMainHandItem());
//TODO drop items in inventory instead of world
// List<ItemStack> drops = new ArrayList<>();
//
// //From Block#harvestBlock
// int silktouch = EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, player.getHeldItemMainhand());
// if (block.canSilkHarvest(world, pos, blockState, player) && silktouch > 0) {
//
// //From Block#getSilkTouchDrop (protected)
// Item item = Item.getItemFromBlock(block);
// int i = 0;
//
// if (item.getHasSubtypes())
// {
// i = block.getMetaFromState(blockState);
// }
//
// drops.add(new ItemStack(item, 1, i));
//
// net.minecraftforge.event.ForgeEventFactory.fireBlockHarvesting(drops, world, pos, blockState, 0, 1.0f, true, player);
// }
//
// int fortune = EnchantmentHelper.getEnchantmentLevel(Enchantments.FORTUNE, player.getHeldItemMainhand());
// drops.addAll(block.getDrops(world, pos, blockState, fortune));
// for (ItemStack drop : drops)
// {
// ItemHandlerHelper.giveItemToPlayer(player, drop);
// }
}
/**
* Check if player can place a block.
* Turn randomizer bag into itemstack inside before.
*
* @param world
* @param player
* @param pos
* @param newBlockState the blockstate that is going to be placed
* @param itemStack the itemstack used for placing
* @param skipCollisionCheck skips collision check with entities
* @return Whether the player may place the block at pos with itemstack
*/
public static boolean canPlace(Level world, Player player, BlockPos pos, BlockState newBlockState, ItemStack itemStack, boolean skipCollisionCheck) {
if (!player.isCreative()) {
//Check if itemstack is correct
if (itemStack.isEmpty() || !(itemStack.getItem() instanceof BlockItem) ||
Block.byItem(itemStack.getItem()) != newBlockState.getBlock()) {
return false;
}
}
Block block = null;
if (itemStack != null && !itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem)
block = ((BlockItem) itemStack.getItem()).getBlock();
else //In creative we might not have an itemstack
block = newBlockState.getBlock();
return canPlayerEdit(player, world, pos, itemStack) &&
mayPlace(world, block, newBlockState, pos, skipCollisionCheck, player) &&
canReplace(world, player, pos);
}
//Can be harvested with hand? (or in creative)
private static boolean canReplace(Level world, Player player, BlockPos pos) {
if (player.isCreative()) return true;
BlockState state = world.getBlockState(pos);
int miningLevel = CommonConfig.survivalBalancers.quickReplaceMiningLevel.get();
switch (miningLevel) {
case -1:
return !state.requiresCorrectToolForDrops();
case 0:
return !state.is(BlockTags.NEEDS_STONE_TOOL) &&
!state.is(BlockTags.NEEDS_IRON_TOOL) &&
!state.is(BlockTags.NEEDS_DIAMOND_TOOL);
case 1:
return !state.is(BlockTags.NEEDS_IRON_TOOL) &&
!state.is(BlockTags.NEEDS_DIAMOND_TOOL);
case 2:
return !state.is(BlockTags.NEEDS_DIAMOND_TOOL);
case 3:
case 4:
return true;
}
return false;
}
//From Player#mayUseItemAt
private static boolean canPlayerEdit(Player player, Level world, BlockPos pos, ItemStack stack) {
if (!world.mayInteract(player, pos)) return false;
if (player.getAbilities().mayBuild) {
//True in creative and survival mode
return true;
} else {
//Adventure mode
BlockInWorld blockinworld = new BlockInWorld(world, pos, false);
return stack.hasAdventureModePlaceTagForBlock(world.registryAccess().registryOrThrow(Registry.BLOCK_REGISTRY), blockinworld);
}
}
//From World#mayPlace
private static boolean mayPlace(Level world, Block blockIn, BlockState newBlockState, BlockPos pos, boolean skipCollisionCheck, @Nullable Entity placer) {
BlockState currentBlockState = world.getBlockState(pos);
VoxelShape voxelShape = skipCollisionCheck ? null : blockIn.defaultBlockState().getCollisionShape(world, pos);
if (voxelShape != null && !world.isUnobstructed(placer, voxelShape)) {
return false;
}
//Check if double slab
if (placer != null && doesBecomeDoubleSlab(((Player) placer), pos)) {
return true;
}
//Check if same block
//Necessary otherwise extra items will be dropped
if (currentBlockState == newBlockState) {
return false;
}
if (currentBlockState.getMaterial() == Material.BUILDABLE_GLASS && blockIn == Blocks.ANVIL) {
return true;
}
//Check quickreplace
if (placer instanceof Player player) {
boolean isQuickReplacing = world.isClientSide ? EffortlessBuildingClient.BUILD_SETTINGS.isQuickReplacing()
: ServerBuildState.isQuickReplacing(player);
if (isQuickReplacing) return true;
}
return currentBlockState.getMaterial().isReplaceable() /*&& canPlaceBlockOnSide(world, pos, sidePlacedOn)*/;
}
//Can break using held tool? (or in creative) //Can break using held tool? (or in creative)
public static boolean canBreak(Level world, Player player, BlockPos pos) { public static boolean canBreak(Level world, Player player, BlockPos pos) {
BlockState blockState = world.getBlockState(pos); BlockState blockState = world.getBlockState(pos);
if (!world.getFluidState(pos).isEmpty()) return false; if (!world.getFluidState(pos).isEmpty()) return false;
@@ -291,6 +42,7 @@ public class SurvivalHelper {
} }
public static boolean doesBecomeDoubleSlab(Player player, BlockPos pos) { public static boolean doesBecomeDoubleSlab(Player player, BlockPos pos) {
BlockState placedBlockState = player.level.getBlockState(pos); BlockState placedBlockState = player.level.getBlockState(pos);
ItemStack itemstack = player.getItemInHand(InteractionHand.MAIN_HAND); ItemStack itemstack = player.getItemInHand(InteractionHand.MAIN_HAND);

View File

@@ -10,6 +10,8 @@
"key.effortlessbuilding.undo.desc": "Undo", "key.effortlessbuilding.undo.desc": "Undo",
"key.effortlessbuilding.redo.desc": "Redo", "key.effortlessbuilding.redo.desc": "Redo",
"key.effortlessbuilding.altplacement.desc": "Alternative placement", "key.effortlessbuilding.altplacement.desc": "Alternative placement",
"key.effortlessbuilding.previous_build_mode.desc": "Activate Previous Build Mode",
"key.effortlessbuilding.disable_build_mode_toggle.desc": "Toggle Disabled <> Previous Build Mode",
"item.effortlessbuilding.randomizer_bag": "Leather Randomizer Bag", "item.effortlessbuilding.randomizer_bag": "Leather Randomizer Bag",
"item.effortlessbuilding.golden_randomizer_bag": "Golden Randomizer Bag", "item.effortlessbuilding.golden_randomizer_bag": "Golden Randomizer Bag",