Overhauled block previews to work with ticks. Added place and break animation.
Added DelayedBlockPlacer to play appear animation, then place. Split BuildConfig into CommonConfig and ClientConfig. Fixed itemstack not found error in creative. Added scale and color options to ghost blocks.
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
package nl.requios.effortlessbuilding;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.ForgeConfigSpec;
|
||||||
|
|
||||||
|
public class ClientConfig {
|
||||||
|
|
||||||
|
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
|
||||||
|
public static final Visuals visuals = new Visuals(builder);
|
||||||
|
public static final ForgeConfigSpec spec = builder.build();
|
||||||
|
|
||||||
|
public static class Visuals {
|
||||||
|
public final ForgeConfigSpec.ConfigValue<Boolean> showBlockPreviews;
|
||||||
|
public final ForgeConfigSpec.ConfigValue<Boolean> alwaysShowBlockPreview;
|
||||||
|
public final ForgeConfigSpec.ConfigValue<Integer> maxBlockPreviews;
|
||||||
|
|
||||||
|
public Visuals(ForgeConfigSpec.Builder builder) {
|
||||||
|
builder.push("Visuals");
|
||||||
|
|
||||||
|
showBlockPreviews = builder
|
||||||
|
.comment("Show previews of the blocks while placing them")
|
||||||
|
.define("useShaders", true);
|
||||||
|
|
||||||
|
alwaysShowBlockPreview = builder
|
||||||
|
.comment("Show a block preview if you have a block in hand even in the 'Disabled' build mode")
|
||||||
|
.define("alwaysShowBlockPreview", false);
|
||||||
|
|
||||||
|
maxBlockPreviews = builder
|
||||||
|
.comment("Don't show block previews when placing more than this many blocks. " +
|
||||||
|
"The outline will always be rendered.")
|
||||||
|
.define("shaderTreshold", 500);
|
||||||
|
|
||||||
|
|
||||||
|
builder.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,7 +133,7 @@ public class ClientEvents {
|
|||||||
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
||||||
|
|
||||||
if (mc.screen != null ||
|
if (mc.screen != null ||
|
||||||
buildMode == BuildModes.BuildModeEnum.NORMAL ||
|
buildMode == BuildModes.BuildModeEnum.DISABLED ||
|
||||||
RadialMenu.instance.isVisible()) {
|
RadialMenu.instance.isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ public class ClientEvents {
|
|||||||
PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage());
|
PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) {
|
} else if (buildMode == BuildModes.BuildModeEnum.SINGLE) {
|
||||||
placeCooldown--;
|
placeCooldown--;
|
||||||
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0;
|
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0;
|
||||||
}
|
}
|
||||||
@@ -210,7 +210,7 @@ public class ClientEvents {
|
|||||||
BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage());
|
BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage());
|
||||||
PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage());
|
PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage());
|
||||||
}
|
}
|
||||||
} else if (buildMode == BuildModes.BuildModeEnum.NORMAL_PLUS) {
|
} else if (buildMode == BuildModes.BuildModeEnum.SINGLE) {
|
||||||
breakCooldown--;
|
breakCooldown--;
|
||||||
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0;
|
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package nl.requios.effortlessbuilding;
|
package nl.requios.effortlessbuilding;
|
||||||
|
|
||||||
import net.minecraftforge.common.ForgeConfigSpec;
|
import net.minecraftforge.common.ForgeConfigSpec;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache;
|
||||||
|
|
||||||
public class BuildConfig {
|
public class CommonConfig {
|
||||||
|
|
||||||
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
|
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
|
||||||
public static final Reach reach = new Reach(builder);
|
public static final Reach reach = new Reach(builder);
|
||||||
@@ -80,33 +81,21 @@ public class BuildConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Visuals {
|
public static class Visuals {
|
||||||
public final ForgeConfigSpec.ConfigValue<Boolean> alwaysShowBlockPreview;
|
public final ForgeConfigSpec.ConfigValue<Integer> appearAnimationLength;
|
||||||
public final ForgeConfigSpec.ConfigValue<Double> dissolveTimeMultiplier;
|
public final ForgeConfigSpec.ConfigValue<Integer> breakAnimationLength;
|
||||||
public final ForgeConfigSpec.ConfigValue<Integer> shaderThreshold;
|
|
||||||
public final ForgeConfigSpec.ConfigValue<Boolean> useShaders;
|
|
||||||
|
|
||||||
public Visuals(ForgeConfigSpec.Builder builder) {
|
public Visuals(ForgeConfigSpec.Builder builder) {
|
||||||
builder.push("Visuals");
|
builder.push("Visuals");
|
||||||
|
|
||||||
alwaysShowBlockPreview = builder
|
appearAnimationLength = builder
|
||||||
.comment("Show a block preview if you have a block in hand even in the 'Normal' build mode")
|
.comment("How long it takes for a block to appear when placed in ticks.",
|
||||||
.define("alwaysShowBlockPreview", false);
|
"Set to 0 to disable animation.")
|
||||||
|
.define("appearAnimationLength", 5);
|
||||||
|
|
||||||
dissolveTimeMultiplier = builder
|
breakAnimationLength = builder
|
||||||
.comment("How long the dissolve effect takes when placing blocks.",
|
.comment("How long the break animation is in ticks.",
|
||||||
"Default between 30 and 60 ticks, you can multiply that here.",
|
"Set to 0 to disable animation.")
|
||||||
"Recommended values:",
|
.define("breakAnimationLength", 10);
|
||||||
"Snappy: 0.7",
|
|
||||||
"Relaxing: 1.5")
|
|
||||||
.define("dissolveTimeMultiplier", 1.0);
|
|
||||||
|
|
||||||
shaderThreshold = builder
|
|
||||||
.comment("Switch to using the simple performance shader when placing more than this many blocks.")
|
|
||||||
.define("shaderTreshold", 1500);
|
|
||||||
|
|
||||||
useShaders = builder
|
|
||||||
.comment("Use fancy shaders while placing blocks")
|
|
||||||
.define("useShaders", true);
|
|
||||||
|
|
||||||
builder.pop();
|
builder.pop();
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||||
import net.minecraftforge.common.util.FakePlayer;
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
import net.minecraftforge.event.level.BlockEvent;
|
import net.minecraftforge.event.level.BlockEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
@@ -52,6 +53,13 @@ public class CommonEvents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onTick(TickEvent.LevelTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.START) return;
|
||||||
|
|
||||||
|
EffortlessBuilding.DELAYED_BLOCK_PLACER.tick();
|
||||||
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
|
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
|
||||||
if (event.getLevel().isClientSide()) return;
|
if (event.getLevel().isClientSide()) return;
|
||||||
@@ -65,7 +73,7 @@ public class CommonEvents {
|
|||||||
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
||||||
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
|
|
||||||
if (buildMode != BuildModes.BuildModeEnum.NORMAL) {
|
if (buildMode != BuildModes.BuildModeEnum.DISABLED) {
|
||||||
|
|
||||||
//Only cancel if itemblock in hand
|
//Only cancel if itemblock in hand
|
||||||
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
|
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
|
||||||
@@ -104,7 +112,7 @@ public class CommonEvents {
|
|||||||
//Cancel event if necessary
|
//Cancel event if necessary
|
||||||
//If cant break far then dont cancel event ever
|
//If cant break far then dont cancel event ever
|
||||||
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(event.getPlayer()).getBuildMode();
|
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(event.getPlayer()).getBuildMode();
|
||||||
if (buildMode != BuildModes.BuildModeEnum.NORMAL && ReachHelper.canBreakFar(event.getPlayer())) {
|
if (buildMode != BuildModes.BuildModeEnum.DISABLED && ReachHelper.canBreakFar(event.getPlayer())) {
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
} else {
|
} else {
|
||||||
//NORMAL mode, let vanilla handle block breaking
|
//NORMAL mode, let vanilla handle block breaking
|
||||||
@@ -155,7 +163,7 @@ public class CommonEvents {
|
|||||||
|
|
||||||
//Set build mode to normal
|
//Set build mode to normal
|
||||||
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
||||||
modeSettings.setBuildMode(BuildModes.BuildModeEnum.NORMAL);
|
modeSettings.setBuildMode(BuildModes.BuildModeEnum.DISABLED);
|
||||||
ModeSettingsManager.setModeSettings(player, modeSettings);
|
ModeSettingsManager.setModeSettings(player, modeSettings);
|
||||||
|
|
||||||
//Disable modifiers
|
//Disable modifiers
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
|||||||
import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer;
|
import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer;
|
||||||
import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer;
|
import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer;
|
||||||
import nl.requios.effortlessbuilding.gui.RandomizerBagContainer;
|
import nl.requios.effortlessbuilding.gui.RandomizerBagContainer;
|
||||||
|
import nl.requios.effortlessbuilding.helper.DelayedBlockPlacer;
|
||||||
import nl.requios.effortlessbuilding.item.*;
|
import nl.requios.effortlessbuilding.item.*;
|
||||||
import nl.requios.effortlessbuilding.network.PacketHandler;
|
import nl.requios.effortlessbuilding.network.PacketHandler;
|
||||||
import nl.requios.effortlessbuilding.proxy.ClientProxy;
|
import nl.requios.effortlessbuilding.proxy.ClientProxy;
|
||||||
@@ -39,6 +40,8 @@ public class EffortlessBuilding {
|
|||||||
public static EffortlessBuilding instance;
|
public static EffortlessBuilding instance;
|
||||||
public static IProxy proxy = DistExecutor.safeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new);
|
public static IProxy proxy = DistExecutor.safeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new);
|
||||||
|
|
||||||
|
public static final DelayedBlockPlacer DELAYED_BLOCK_PLACER = new DelayedBlockPlacer();
|
||||||
|
|
||||||
//Registration
|
//Registration
|
||||||
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
|
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
|
||||||
private static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EffortlessBuilding.MODID);
|
private static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EffortlessBuilding.MODID);
|
||||||
@@ -69,7 +72,8 @@ public class EffortlessBuilding {
|
|||||||
CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus());
|
CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||||
|
|
||||||
//Register config
|
//Register config
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, CommonConfig.spec);
|
||||||
|
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setup(final FMLCommonSetupEvent event) {
|
public static void setup(final FMLCommonSetupEvent event) {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class BuildModes {
|
|||||||
|
|
||||||
//Check if player reach does not exceed startpos
|
//Check if player reach does not exceed startpos
|
||||||
int maxReach = ReachHelper.getMaxReach(player);
|
int maxReach = ReachHelper.getMaxReach(player);
|
||||||
if (buildMode != BuildModeEnum.NORMAL && player.blockPosition().distSqr(startPos) > maxReach * maxReach) {
|
if (buildMode != BuildModeEnum.DISABLED && player.blockPosition().distSqr(startPos) > maxReach * maxReach) {
|
||||||
EffortlessBuilding.log(player, "Placement exceeds your reach.");
|
EffortlessBuilding.log(player, "Placement exceeds your reach.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -186,9 +186,6 @@ public class BuildModes {
|
|||||||
return new Vec3(x, y, z);
|
return new Vec3(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-- Common build mode functionality --//
|
|
||||||
|
|
||||||
public static Vec3 findYBound(double y, Vec3 start, Vec3 look) {
|
public static Vec3 findYBound(double y, Vec3 start, Vec3 look) {
|
||||||
//then x and z are
|
//then x and z are
|
||||||
double x = (y - start.y) / look.y * look.x + start.x;
|
double x = (y - start.y) / look.y * look.x + start.x;
|
||||||
@@ -246,8 +243,8 @@ public class BuildModes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum BuildModeEnum {
|
public enum BuildModeEnum {
|
||||||
NORMAL("normal", new Normal(), BuildModeCategoryEnum.BASIC),
|
DISABLED("normal", new Disabled(), BuildModeCategoryEnum.BASIC),
|
||||||
NORMAL_PLUS("normal_plus", new NormalPlus(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED),
|
SINGLE("normal_plus", new Single(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED),
|
||||||
LINE("line", new Line(), BuildModeCategoryEnum.BASIC /*, OptionEnum.THICKNESS*/),
|
LINE("line", new Line(), BuildModeCategoryEnum.BASIC /*, OptionEnum.THICKNESS*/),
|
||||||
WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
|
WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
|
||||||
FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
|
FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class ModeSettingsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ModeSettings {
|
public static class ModeSettings {
|
||||||
private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.NORMAL;
|
private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.DISABLED;
|
||||||
|
|
||||||
public ModeSettings() {
|
public ModeSettings() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class NormalPlus implements IBuildMode {
|
public class Disabled implements IBuildMode {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Player player) {
|
public void initialize(Player player) {
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Normal implements IBuildMode {
|
public class Single implements IBuildMode {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Player player) {
|
public void initialize(Player player) {
|
||||||
|
|
||||||
@@ -14,11 +14,13 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||||
import nl.requios.effortlessbuilding.helper.InventoryHelper;
|
import nl.requios.effortlessbuilding.helper.DelayedBlockPlacer;
|
||||||
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
||||||
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -42,55 +44,17 @@ public class BuildModifiers {
|
|||||||
//check if valid blockstates
|
//check if valid blockstates
|
||||||
if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return;
|
if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return;
|
||||||
|
|
||||||
//remember previous blockstates for undo
|
|
||||||
List<BlockState> previousBlockStates = new ArrayList<>(coordinates.size());
|
|
||||||
List<BlockState> newBlockStates = new ArrayList<>(coordinates.size());
|
|
||||||
for (BlockPos coordinate : coordinates) {
|
|
||||||
previousBlockStates.add(world.getBlockState(coordinate));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (world.isClientSide) {
|
if (world.isClientSide) {
|
||||||
|
|
||||||
BlockPreviewRenderer.onBlocksPlaced();
|
BlockPreviews.onBlocksPlaced();
|
||||||
|
|
||||||
newBlockStates = blockStates;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
//place blocks
|
int delay = CommonConfig.visuals.appearAnimationLength.get() * 3; //DelayedBlockPlacer is called 3 times per tick?
|
||||||
for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) {
|
|
||||||
BlockPos blockPos = coordinates.get(i);
|
|
||||||
BlockState blockState = blockStates.get(i);
|
|
||||||
ItemStack itemStack = itemStacks.get(i);
|
|
||||||
|
|
||||||
if (world.isLoaded(blockPos)) {
|
//place blocks after delay
|
||||||
//check itemstack empty
|
EffortlessBuilding.DELAYED_BLOCK_PLACER.placeBlocksDelayed(new DelayedBlockPlacer.Entry(world, player, coordinates,
|
||||||
if (itemStack.isEmpty()) {
|
blockStates, itemStacks, hitVec, placeStartPos, delay));
|
||||||
//try to find new stack, otherwise continue
|
|
||||||
itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock());
|
|
||||||
if (itemStack.isEmpty()) continue;
|
|
||||||
}
|
|
||||||
SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//find actual new blockstates for undo
|
|
||||||
for (BlockPos coordinate : coordinates) {
|
|
||||||
newBlockStates.add(world.getBlockState(coordinate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work
|
|
||||||
//(Block is placed by the time it gets here, and unplaced after this)
|
|
||||||
if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState());
|
|
||||||
|
|
||||||
//If all new blockstates are air then no use in adding it, no block was actually placed
|
|
||||||
//Can happen when e.g. placing one block in yourself
|
|
||||||
if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) {
|
|
||||||
//add to undo stack
|
|
||||||
BlockPos firstPos = startCoordinates.get(0);
|
|
||||||
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
|
|
||||||
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +73,7 @@ public class BuildModifiers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (world.isClientSide) {
|
if (world.isClientSide) {
|
||||||
BlockPreviewRenderer.onBlocksBroken();
|
BlockPreviews.onBlocksBroken();
|
||||||
|
|
||||||
//list of air blockstates
|
//list of air blockstates
|
||||||
for (int i = 0; i < coordinates.size(); i++) {
|
for (int i = 0; i < coordinates.size(); i++) {
|
||||||
@@ -237,6 +201,7 @@ public class BuildModifiers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEnabled(ModifierSettingsManager.ModifierSettings modifierSettings, BlockPos startPos) {
|
public static boolean isEnabled(ModifierSettingsManager.ModifierSettings modifierSettings, BlockPos startPos) {
|
||||||
|
//startPos can be null
|
||||||
return Mirror.isEnabled(modifierSettings.getMirrorSettings(), startPos) ||
|
return Mirror.isEnabled(modifierSettings.getMirrorSettings(), startPos) ||
|
||||||
Array.isEnabled(modifierSettings.getArraySettings()) ||
|
Array.isEnabled(modifierSettings.getArraySettings()) ||
|
||||||
RadialMirror.isEnabled(modifierSettings.getRadialMirrorSettings(), startPos) ||
|
RadialMirror.isEnabled(modifierSettings.getRadialMirrorSettings(), startPos) ||
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import net.minecraft.server.level.ServerPlayer;
|
|||||||
import net.minecraftforge.common.util.LazyOptional;
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.network.PacketDistributor;
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
|
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
|
||||||
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
||||||
@@ -192,16 +192,16 @@ public class ModifierSettingsManager {
|
|||||||
int reach = 10;
|
int reach = 10;
|
||||||
switch (reachUpgrade) {
|
switch (reachUpgrade) {
|
||||||
case 0:
|
case 0:
|
||||||
reach = BuildConfig.reach.maxReachLevel0.get();
|
reach = CommonConfig.reach.maxReachLevel0.get();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
reach = BuildConfig.reach.maxReachLevel1.get();
|
reach = CommonConfig.reach.maxReachLevel1.get();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
reach = BuildConfig.reach.maxReachLevel2.get();
|
reach = CommonConfig.reach.maxReachLevel2.get();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
reach = BuildConfig.reach.maxReachLevel3.get();
|
reach = CommonConfig.reach.maxReachLevel3.get();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import net.minecraft.core.Direction;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.helper.FixedStack;
|
import nl.requios.effortlessbuilding.helper.FixedStack;
|
||||||
import nl.requios.effortlessbuilding.helper.InventoryHelper;
|
import nl.requios.effortlessbuilding.helper.InventoryHelper;
|
||||||
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ public class UndoRedo {
|
|||||||
|
|
||||||
//If no stack exists, make one
|
//If no stack exists, make one
|
||||||
if (!undoStacks.containsKey(player.getUUID())) {
|
if (!undoStacks.containsKey(player.getUUID())) {
|
||||||
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
|
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
undoStacks.get(player.getUUID()).push(blockSet);
|
undoStacks.get(player.getUUID()).push(blockSet);
|
||||||
@@ -63,7 +63,7 @@ public class UndoRedo {
|
|||||||
|
|
||||||
//If no stack exists, make one
|
//If no stack exists, make one
|
||||||
if (!redoStacks.containsKey(player.getUUID())) {
|
if (!redoStacks.containsKey(player.getUUID())) {
|
||||||
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
|
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
redoStacks.get(player.getUUID()).push(blockSet);
|
redoStacks.get(player.getUUID()).push(blockSet);
|
||||||
@@ -88,7 +88,7 @@ public class UndoRedo {
|
|||||||
List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
|
List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
|
||||||
|
|
||||||
if (player.level.isClientSide) {
|
if (player.level.isClientSide) {
|
||||||
BlockPreviewRenderer.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos());
|
BlockPreviews.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos());
|
||||||
} else {
|
} else {
|
||||||
//break all those blocks, reset to what they were
|
//break all those blocks, reset to what they were
|
||||||
for (int i = 0; i < coordinates.size(); i++) {
|
for (int i = 0; i < coordinates.size(); i++) {
|
||||||
@@ -148,7 +148,7 @@ public class UndoRedo {
|
|||||||
List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
|
List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
|
||||||
|
|
||||||
if (player.level.isClientSide) {
|
if (player.level.isClientSide) {
|
||||||
BlockPreviewRenderer.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos());
|
BlockPreviews.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos());
|
||||||
} else {
|
} else {
|
||||||
//place blocks
|
//place blocks
|
||||||
for (int i = 0; i < coordinates.size(); i++) {
|
for (int i = 0; i < coordinates.size(); i++) {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class ModeCapabilityManager {
|
|||||||
|
|
||||||
//TODO add mode settings
|
//TODO add mode settings
|
||||||
|
|
||||||
ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL);
|
ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.DISABLED);
|
||||||
instance.setModeData(modeSettings);
|
instance.setModeData(modeSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package nl.requios.effortlessbuilding.create.foundation.utility.ghost;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.utility.outliner.Outline;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -11,11 +13,15 @@ public class GhostBlockParams {
|
|||||||
protected final BlockState state;
|
protected final BlockState state;
|
||||||
protected BlockPos pos;
|
protected BlockPos pos;
|
||||||
protected Supplier<Float> alphaSupplier;
|
protected Supplier<Float> alphaSupplier;
|
||||||
|
protected Supplier<Float> scaleSupplier;
|
||||||
|
public Supplier<Color> rgbSupplier;
|
||||||
|
|
||||||
private GhostBlockParams(BlockState state) {
|
private GhostBlockParams(BlockState state) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.pos = BlockPos.ZERO;
|
this.pos = BlockPos.ZERO;
|
||||||
this.alphaSupplier = () -> 1f;
|
this.alphaSupplier = () -> 1f;
|
||||||
|
this.scaleSupplier = () -> 0.85f;
|
||||||
|
this.rgbSupplier = () -> Color.WHITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GhostBlockParams of(BlockState state) {
|
public static GhostBlockParams of(BlockState state) {
|
||||||
@@ -47,4 +53,31 @@ public class GhostBlockParams {
|
|||||||
public GhostBlockParams breathingAlpha() {
|
public GhostBlockParams breathingAlpha() {
|
||||||
return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha());
|
return this.alpha(() -> (float) GhostBlocks.getBreathingAlpha());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams scale(Supplier<Float> scaleSupplier) {
|
||||||
|
this.scaleSupplier = scaleSupplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams scale(float scale) {
|
||||||
|
return this.scale(() -> scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams colored(Supplier<Color> colorSupplier) {
|
||||||
|
this.rgbSupplier = colorSupplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams colored(Color color) {
|
||||||
|
return this.colored(() -> color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams colored(int color) {
|
||||||
|
var color2 = new Color(color, false);
|
||||||
|
return this.colored(() -> color2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GhostBlockParams breathingCyan() {
|
||||||
|
return this.colored(() -> new Color((float) GhostBlocks.getBreathingColor(), 1f, 1f, 1f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import net.minecraft.util.Mth;
|
|||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.util.RandomSource;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraftforge.client.model.data.ModelData;
|
import net.minecraftforge.client.model.data.ModelData;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -76,6 +77,8 @@ public abstract class GhostBlockRenderer {
|
|||||||
BlockState state = params.state;
|
BlockState state = params.state;
|
||||||
BlockPos pos = params.pos;
|
BlockPos pos = params.pos;
|
||||||
float alpha = params.alphaSupplier.get()/* * .75f* PlacementHelpers.getCurrentAlpha()*/;
|
float alpha = params.alphaSupplier.get()/* * .75f* PlacementHelpers.getCurrentAlpha()*/;
|
||||||
|
float scale = params.scaleSupplier.get();
|
||||||
|
Color color = params.rgbSupplier.get();
|
||||||
|
|
||||||
BakedModel model = dispatcher.getBlockModel(state);
|
BakedModel model = dispatcher.getBlockModel(state);
|
||||||
RenderType layer = RenderType.translucent();
|
RenderType layer = RenderType.translucent();
|
||||||
@@ -85,10 +88,10 @@ public abstract class GhostBlockRenderer {
|
|||||||
ms.translate(pos.getX(), pos.getY(), pos.getZ());
|
ms.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
|
||||||
ms.translate(.5, .5, .5);
|
ms.translate(.5, .5, .5);
|
||||||
ms.scale(.85f, .85f, .85f);
|
ms.scale(scale, scale, scale);
|
||||||
ms.translate(-.5, -.5, -.5);
|
ms.translate(-.5, -.5, -.5);
|
||||||
|
|
||||||
renderModel(ms.last(), vb, state, model, 0f, 1f, 1f, alpha,
|
renderModel(ms.last(), vb, state, model, color.getRedAsFloat(), color.getGreenAsFloat(), color.getBlueAsFloat(), alpha,
|
||||||
LevelRenderer.getLightColor(mc.level, pos), OverlayTexture.NO_OVERLAY,
|
LevelRenderer.getLightColor(mc.level, pos), OverlayTexture.NO_OVERLAY,
|
||||||
ModelUtil.VIRTUAL_DATA, layer);
|
ModelUtil.VIRTUAL_DATA, layer);
|
||||||
|
|
||||||
@@ -120,15 +123,15 @@ public abstract class GhostBlockRenderer {
|
|||||||
float f;
|
float f;
|
||||||
float f1;
|
float f1;
|
||||||
float f2;
|
float f2;
|
||||||
if (quad.isTinted()) {
|
// if (quad.isTinted()) {
|
||||||
f = Mth.clamp(red, 0.0F, 1.0F);
|
f = Mth.clamp(red, 0.0F, 1.0F);
|
||||||
f1 = Mth.clamp(green, 0.0F, 1.0F);
|
f1 = Mth.clamp(green, 0.0F, 1.0F);
|
||||||
f2 = Mth.clamp(blue, 0.0F, 1.0F);
|
f2 = Mth.clamp(blue, 0.0F, 1.0F);
|
||||||
} else {
|
// } else {
|
||||||
f = 1.0F;
|
// f = 1.0F;
|
||||||
f1 = 1.0F;
|
// f1 = 1.0F;
|
||||||
f2 = 1.0F;
|
// f2 = 1.0F;
|
||||||
}
|
// }
|
||||||
|
|
||||||
consumer.putBulkData(pose, quad, f, f1, f2, alpha, packedLight, packedOverlay, true);
|
consumer.putBulkData(pose, quad, f, f1, f2, alpha, packedLight, packedOverlay, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ public class GhostBlocks {
|
|||||||
return 0.55d - 0.2d * offset;
|
return 0.55d - 0.2d * offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double getBreathingColor() {
|
||||||
|
double period = 2500;
|
||||||
|
double timer = System.currentTimeMillis() % period;
|
||||||
|
double offset = Mth.cos((float) ((2d/period) * Math.PI * timer));
|
||||||
|
return 0.35d + 0.35d * offset;
|
||||||
|
}
|
||||||
|
|
||||||
final Map<Object, Entry> ghosts;
|
final Map<Object, Entry> ghosts;
|
||||||
|
|
||||||
public GhostBlockParams showGhostState(Object slot, BlockState state) {
|
public GhostBlockParams showGhostState(Object slot, BlockState state) {
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package nl.requios.effortlessbuilding.helper;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
|
||||||
|
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.utility.AnimationTickHolder;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class DelayedBlockPlacer {
|
||||||
|
|
||||||
|
private final Set<Entry> entries = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
private final Set<Entry> entriesView = Collections.unmodifiableSet(entries);
|
||||||
|
|
||||||
|
public void placeBlocksDelayed(Entry entry) {
|
||||||
|
if (entry.world.isClientSide) return;
|
||||||
|
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
for (Entry entry : entries) {
|
||||||
|
entry.ticksTillPlacement--;
|
||||||
|
if (entry.ticksTillPlacement <= 0) {
|
||||||
|
entry.place();
|
||||||
|
entries.remove(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Entry> getEntries() {
|
||||||
|
return entriesView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Entry {
|
||||||
|
private Level world;
|
||||||
|
private Player player;
|
||||||
|
private List<BlockPos> coordinates;
|
||||||
|
private List<BlockState> blockStates;
|
||||||
|
private List<ItemStack> itemStacks;
|
||||||
|
private Vec3 hitVec;
|
||||||
|
private boolean placeStartPos;
|
||||||
|
private int ticksTillPlacement;
|
||||||
|
|
||||||
|
public Entry(Level world, Player player, List<BlockPos> coordinates, List<BlockState> blockStates,
|
||||||
|
List<ItemStack> itemStacks, Vec3 hitVec, boolean placeStartPos, int ticksTillPlacement) {
|
||||||
|
this.world = world;
|
||||||
|
this.player = player;
|
||||||
|
this.coordinates = coordinates;
|
||||||
|
this.blockStates = blockStates;
|
||||||
|
this.itemStacks = itemStacks;
|
||||||
|
this.hitVec = hitVec;
|
||||||
|
this.placeStartPos = placeStartPos;
|
||||||
|
this.ticksTillPlacement = ticksTillPlacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void place() {
|
||||||
|
//remember previous blockstates for undo
|
||||||
|
List<BlockState> previousBlockStates = new ArrayList<>(coordinates.size());
|
||||||
|
for (BlockPos coordinate : coordinates) {
|
||||||
|
previousBlockStates.add(world.getBlockState(coordinate));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) {
|
||||||
|
BlockPos blockPos = coordinates.get(i);
|
||||||
|
BlockState blockState = blockStates.get(i);
|
||||||
|
ItemStack itemStack = itemStacks.get(i);
|
||||||
|
|
||||||
|
if (world.isLoaded(blockPos)) {
|
||||||
|
//check itemstack empty
|
||||||
|
if (itemStack.isEmpty()) {
|
||||||
|
//try to find new stack, otherwise continue
|
||||||
|
itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock());
|
||||||
|
if (itemStack.isEmpty()) continue;
|
||||||
|
}
|
||||||
|
SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//find actual new blockstates for undo
|
||||||
|
List<BlockState> newBlockStates = new ArrayList<>(coordinates.size());
|
||||||
|
for (BlockPos coordinate : coordinates) {
|
||||||
|
newBlockStates.add(world.getBlockState(coordinate));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work
|
||||||
|
//(Block is placed by the time it gets here, and unplaced after this)
|
||||||
|
if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState());
|
||||||
|
|
||||||
|
//If all new blockstates are air then no use in adding it, no block was actually placed
|
||||||
|
//Can happen when e.g. placing one block in yourself
|
||||||
|
if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) {
|
||||||
|
//add to undo stack
|
||||||
|
BlockPos firstPos = coordinates.get(0);
|
||||||
|
BlockPos secondPos = coordinates.get(coordinates.size() - 1);
|
||||||
|
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,28 +2,28 @@ package nl.requios.effortlessbuilding.helper;
|
|||||||
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
|
|
||||||
public class ReachHelper {
|
public class ReachHelper {
|
||||||
public static int getMaxReach(Player player) {
|
public static int getMaxReach(Player player) {
|
||||||
if (player.isCreative()) return BuildConfig.reach.maxReachCreative.get();
|
if (player.isCreative()) return CommonConfig.reach.maxReachCreative.get();
|
||||||
|
|
||||||
if (!BuildConfig.reach.enableReachUpgrades.get()) return BuildConfig.reach.maxReachLevel3.get();
|
if (!CommonConfig.reach.enableReachUpgrades.get()) return CommonConfig.reach.maxReachLevel3.get();
|
||||||
|
|
||||||
//Check buildsettings for reachUpgrade
|
//Check buildsettings for reachUpgrade
|
||||||
int reachUpgrade = ModifierSettingsManager.getModifierSettings(player).getReachUpgrade();
|
int reachUpgrade = ModifierSettingsManager.getModifierSettings(player).getReachUpgrade();
|
||||||
switch (reachUpgrade) {
|
switch (reachUpgrade) {
|
||||||
case 0:
|
case 0:
|
||||||
return BuildConfig.reach.maxReachLevel0.get();
|
return CommonConfig.reach.maxReachLevel0.get();
|
||||||
case 1:
|
case 1:
|
||||||
return BuildConfig.reach.maxReachLevel1.get();
|
return CommonConfig.reach.maxReachLevel1.get();
|
||||||
case 2:
|
case 2:
|
||||||
return BuildConfig.reach.maxReachLevel2.get();
|
return CommonConfig.reach.maxReachLevel2.get();
|
||||||
case 3:
|
case 3:
|
||||||
return BuildConfig.reach.maxReachLevel3.get();
|
return CommonConfig.reach.maxReachLevel3.get();
|
||||||
}
|
}
|
||||||
return BuildConfig.reach.maxReachLevel0.get();
|
return CommonConfig.reach.maxReachLevel0.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getPlacementReach(Player player) {
|
public static int getPlacementReach(Player player) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import net.minecraft.world.level.material.Material;
|
|||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
import net.minecraftforge.event.ForgeEventFactory;
|
import net.minecraftforge.event.ForgeEventFactory;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||||
|
|
||||||
@@ -187,17 +187,21 @@ public class SurvivalHelper {
|
|||||||
*/
|
*/
|
||||||
public static boolean canPlace(Level world, Player player, BlockPos pos, BlockState newBlockState, ItemStack itemStack, boolean skipCollisionCheck, Direction sidePlacedOn) {
|
public static boolean canPlace(Level world, Player player, BlockPos pos, BlockState newBlockState, ItemStack itemStack, boolean skipCollisionCheck, Direction sidePlacedOn) {
|
||||||
|
|
||||||
//Check if itemstack is correct
|
if (!player.isCreative()) {
|
||||||
if (!(itemStack.getItem() instanceof BlockItem) || Block.byItem(itemStack.getItem()) != newBlockState.getBlock()) {
|
//Check if itemstack is correct
|
||||||
// EffortlessBuilding.log(player, "Cannot (re)place block", true);
|
if (itemStack.isEmpty() || !(itemStack.getItem() instanceof BlockItem) ||
|
||||||
// EffortlessBuilding.log("SurvivalHelper#canPlace: itemstack " + itemStack.toString() + " does not match blockstate " + newBlockState.toString());
|
Block.byItem(itemStack.getItem()) != newBlockState.getBlock()) {
|
||||||
//Happens when breaking blocks, no need to notify in that case
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Block block = ((BlockItem) itemStack.getItem()).getBlock();
|
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 !itemStack.isEmpty() && canPlayerEdit(player, world, pos, itemStack) &&
|
return canPlayerEdit(player, world, pos, itemStack) &&
|
||||||
mayPlace(world, block, newBlockState, pos, skipCollisionCheck, sidePlacedOn, player) &&
|
mayPlace(world, block, newBlockState, pos, skipCollisionCheck, sidePlacedOn, player) &&
|
||||||
canReplace(world, player, pos);
|
canReplace(world, player, pos);
|
||||||
}
|
}
|
||||||
@@ -208,7 +212,7 @@ public class SurvivalHelper {
|
|||||||
|
|
||||||
BlockState state = world.getBlockState(pos);
|
BlockState state = world.getBlockState(pos);
|
||||||
|
|
||||||
int miningLevel = BuildConfig.survivalBalancers.quickReplaceMiningLevel.get();
|
int miningLevel = CommonConfig.survivalBalancers.quickReplaceMiningLevel.get();
|
||||||
switch (miningLevel) {
|
switch (miningLevel) {
|
||||||
case -1:
|
case -1:
|
||||||
return !state.requiresCorrectToolForDrops();
|
return !state.requiresCorrectToolForDrops();
|
||||||
@@ -245,7 +249,7 @@ public class SurvivalHelper {
|
|||||||
|
|
||||||
//From World#mayPlace
|
//From World#mayPlace
|
||||||
private static boolean mayPlace(Level world, Block blockIn, BlockState newBlockState, BlockPos pos, boolean skipCollisionCheck, Direction sidePlacedOn, @Nullable Entity placer) {
|
private static boolean mayPlace(Level world, Block blockIn, BlockState newBlockState, BlockPos pos, boolean skipCollisionCheck, Direction sidePlacedOn, @Nullable Entity placer) {
|
||||||
BlockState iblockstate1 = world.getBlockState(pos);
|
BlockState currentBlockState = world.getBlockState(pos);
|
||||||
VoxelShape voxelShape = skipCollisionCheck ? null : blockIn.defaultBlockState().getCollisionShape(world, pos);
|
VoxelShape voxelShape = skipCollisionCheck ? null : blockIn.defaultBlockState().getCollisionShape(world, pos);
|
||||||
|
|
||||||
if (voxelShape != null && !world.isUnobstructed(placer, voxelShape)) {
|
if (voxelShape != null && !world.isUnobstructed(placer, voxelShape)) {
|
||||||
@@ -259,22 +263,20 @@ public class SurvivalHelper {
|
|||||||
|
|
||||||
//Check if same block
|
//Check if same block
|
||||||
//Necessary otherwise extra items will be dropped
|
//Necessary otherwise extra items will be dropped
|
||||||
if (iblockstate1 == newBlockState) {
|
if (currentBlockState == newBlockState) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO 1.14 check what Material.CIRCUITS has become
|
if (currentBlockState.getMaterial() == Material.BUILDABLE_GLASS && blockIn == Blocks.ANVIL) {
|
||||||
if (iblockstate1.getMaterial() == Material.BUILDABLE_GLASS && blockIn == Blocks.ANVIL) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check quickreplace
|
//Check quickreplace
|
||||||
if (placer instanceof Player && ModifierSettingsManager.getModifierSettings(((Player) placer)).doQuickReplace()) {
|
if (placer != null && ModifierSettingsManager.getModifierSettings(((Player) placer)).doQuickReplace()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO 1.13 replaceable
|
return currentBlockState.getMaterial().isReplaceable() /*&& canPlaceBlockOnSide(world, pos, sidePlacedOn)*/;
|
||||||
return iblockstate1.getMaterial().isReplaceable() /*&& canPlaceBlockOnSide(world, pos, sidePlacedOn)*/;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public abstract class AbstractRandomizerBagItem extends Item {
|
|||||||
//Only place manually if in normal vanilla mode
|
//Only place manually if in normal vanilla mode
|
||||||
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
|
||||||
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
if (buildMode != BuildModes.BuildModeEnum.NORMAL || modifierSettings.doQuickReplace()) {
|
if (buildMode != BuildModes.BuildModeEnum.DISABLED || modifierSettings.doQuickReplace()) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
||||||
@@ -63,7 +63,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 + BuildConfig.reach.maxReachLevel1.get()));
|
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel1.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
import java.awt.*;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
@@ -68,7 +67,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 + BuildConfig.reach.maxReachLevel2.get()));
|
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel2.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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
||||||
@@ -70,7 +70,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 + BuildConfig.reach.maxReachLevel3.get()));
|
tooltip.add(Component.literal(ChatFormatting.GRAY + "Consume to increase reach to " + ChatFormatting.BLUE + CommonConfig.reach.maxReachLevel3.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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import net.minecraftforge.network.NetworkEvent;
|
|||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
|
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import net.minecraftforge.fml.DistExecutor;
|
|||||||
import net.minecraftforge.fml.LogicalSide;
|
import net.minecraftforge.fml.LogicalSide;
|
||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import nl.requios.effortlessbuilding.buildmode.BuildModes;
|
import nl.requios.effortlessbuilding.buildmode.BuildModes;
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
import nl.requios.effortlessbuilding.render.BlockPreviews;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ public class BlockPlacedMessage {
|
|||||||
public static class ClientHandler {
|
public static class ClientHandler {
|
||||||
public static void handle(BlockPlacedMessage message, Supplier<NetworkEvent.Context> ctx) {
|
public static void handle(BlockPlacedMessage message, Supplier<NetworkEvent.Context> ctx) {
|
||||||
//Nod RenderHandler to do the dissolve shader effect
|
//Nod RenderHandler to do the dissolve shader effect
|
||||||
BlockPreviewRenderer.onBlocksPlaced();
|
BlockPreviews.onBlocksPlaced();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import net.minecraftforge.fml.LogicalSide;
|
|||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import net.minecraftforge.fml.LogicalSide;
|
|||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import nl.requios.effortlessbuilding.ClientEvents;
|
import nl.requios.effortlessbuilding.ClientEvents;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
import nl.requios.effortlessbuilding.proxy.ClientProxy;
|
|
||||||
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|||||||
@@ -1,412 +0,0 @@
|
|||||||
package nl.requios.effortlessbuilding.render;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.block.SoundType;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
|
||||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.sounds.SoundSource;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.world.phys.HitResult;
|
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
||||||
import nl.requios.effortlessbuilding.BuildConfig;
|
|
||||||
import nl.requios.effortlessbuilding.ClientEvents;
|
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
|
|
||||||
import nl.requios.effortlessbuilding.buildmode.BuildModes;
|
|
||||||
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
|
||||||
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
|
|
||||||
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings;
|
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
|
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings;
|
|
||||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
|
||||||
import nl.requios.effortlessbuilding.create.AllSpecialTextures;
|
|
||||||
import nl.requios.effortlessbuilding.create.CreateClient;
|
|
||||||
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
|
|
||||||
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
|
||||||
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
|
||||||
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
|
||||||
import nl.requios.effortlessbuilding.proxy.ClientProxy;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
|
||||||
public class BlockPreviewRenderer {
|
|
||||||
private static final List<PlacedData> placedDataList = new ArrayList<>();
|
|
||||||
private static List<BlockPos> previousCoordinates;
|
|
||||||
private static List<BlockState> previousBlockStates;
|
|
||||||
private static List<ItemStack> previousItemStacks;
|
|
||||||
private static BlockPos previousFirstPos;
|
|
||||||
private static BlockPos previousSecondPos;
|
|
||||||
private static int soundTime = 0;
|
|
||||||
|
|
||||||
public static void render(PoseStack ms, MultiBufferSource.BufferSource buffer, Player player, ModifierSettings modifierSettings, ModeSettings modeSettings) {
|
|
||||||
|
|
||||||
//Render placed blocks with dissolve effect
|
|
||||||
//Use fancy shader if config allows, otherwise no dissolve
|
|
||||||
if (BuildConfig.visuals.useShaders.get()) {
|
|
||||||
for (int i = 0; i < placedDataList.size(); i++) {
|
|
||||||
PlacedData placed = placedDataList.get(i);
|
|
||||||
if (placed.coordinates != null && !placed.coordinates.isEmpty()) {
|
|
||||||
|
|
||||||
double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get();
|
|
||||||
float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime;
|
|
||||||
renderBlockPreviews(ms, buffer, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Expire
|
|
||||||
placedDataList.removeIf(placed -> {
|
|
||||||
double totalTime = Mth.clampedLerp(30, 60, placed.firstPos.distSqr(placed.secondPos) / 100.0) * BuildConfig.visuals.dissolveTimeMultiplier.get();
|
|
||||||
return placed.time + totalTime < ClientEvents.ticksInGame;
|
|
||||||
});
|
|
||||||
|
|
||||||
//Render block previews
|
|
||||||
HitResult lookingAt = ClientEvents.getLookingAt(player);
|
|
||||||
if (modeSettings.getBuildMode() == BuildModes.BuildModeEnum.NORMAL)
|
|
||||||
lookingAt = Minecraft.getInstance().hitResult;
|
|
||||||
|
|
||||||
ItemStack mainhand = player.getMainHandItem();
|
|
||||||
boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand));
|
|
||||||
|
|
||||||
BlockPos startPos = null;
|
|
||||||
Direction sideHit = null;
|
|
||||||
Vec3 hitVec = null;
|
|
||||||
|
|
||||||
//Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS)
|
|
||||||
if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) {
|
|
||||||
BlockHitResult blockLookingAt = (BlockHitResult) lookingAt;
|
|
||||||
startPos = blockLookingAt.getBlockPos();
|
|
||||||
|
|
||||||
//Check if tool (or none) in hand
|
|
||||||
//TODO 1.13 replaceable
|
|
||||||
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
|
|
||||||
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, blockLookingAt.getDirection());
|
|
||||||
if (!modifierSettings.doQuickReplace() && !noBlockInHand && !replaceable && !becomesDoubleSlab) {
|
|
||||||
startPos = startPos.relative(blockLookingAt.getDirection());
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get under tall grass and other replaceable blocks
|
|
||||||
if (modifierSettings.doQuickReplace() && !noBlockInHand && replaceable) {
|
|
||||||
startPos = startPos.below();
|
|
||||||
}
|
|
||||||
|
|
||||||
sideHit = blockLookingAt.getDirection();
|
|
||||||
hitVec = blockLookingAt.getLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Dont render if in normal mode and modifiers are disabled
|
|
||||||
//Unless alwaysShowBlockPreview is true in config
|
|
||||||
if (doRenderBlockPreviews(modifierSettings, modeSettings, startPos)) {
|
|
||||||
|
|
||||||
//Keep blockstate the same for every block in the buildmode
|
|
||||||
//So dont rotate blocks when in the middle of placing wall etc.
|
|
||||||
if (BuildModes.isActive(player)) {
|
|
||||||
IBuildMode buildModeInstance = modeSettings.getBuildMode().instance;
|
|
||||||
if (buildModeInstance.getSideHit(player) != null) sideHit = buildModeInstance.getSideHit(player);
|
|
||||||
if (buildModeInstance.getHitVec(player) != null) hitVec = buildModeInstance.getHitVec(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sideHit != null) {
|
|
||||||
|
|
||||||
//Should be red?
|
|
||||||
boolean breaking = BuildModes.currentlyBreakingClient.get(player) != null && BuildModes.currentlyBreakingClient.get(player);
|
|
||||||
|
|
||||||
//get coordinates
|
|
||||||
List<BlockPos> startCoordinates = BuildModes.findCoordinates(player, startPos, breaking || modifierSettings.doQuickReplace());
|
|
||||||
|
|
||||||
//Remember first and last point for the shader
|
|
||||||
BlockPos firstPos = BlockPos.ZERO, secondPos = BlockPos.ZERO;
|
|
||||||
if (!startCoordinates.isEmpty()) {
|
|
||||||
firstPos = startCoordinates.get(0);
|
|
||||||
secondPos = startCoordinates.get(startCoordinates.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Limit number of blocks you can place
|
|
||||||
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
|
|
||||||
if (startCoordinates.size() > limit) {
|
|
||||||
startCoordinates = startCoordinates.subList(0, limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BlockPos> newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates);
|
|
||||||
|
|
||||||
sortOnDistanceToPlayer(newCoordinates, player);
|
|
||||||
|
|
||||||
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)),
|
|
||||||
Math.abs(hitVec.z - ((int) hitVec.z)));
|
|
||||||
|
|
||||||
//Get blockstates
|
|
||||||
List<ItemStack> itemStacks = new ArrayList<>();
|
|
||||||
List<BlockState> blockStates = new ArrayList<>();
|
|
||||||
if (breaking) {
|
|
||||||
//Find blockstate of world
|
|
||||||
for (BlockPos coordinate : newCoordinates) {
|
|
||||||
blockStates.add(player.level.getBlockState(coordinate));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockStates = BuildModifiers.findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Check if they are different from previous
|
|
||||||
//TODO fix triggering when moving player
|
|
||||||
if (!BuildModifiers.compareCoordinates(previousCoordinates, newCoordinates)) {
|
|
||||||
previousCoordinates = newCoordinates;
|
|
||||||
//remember the rest for placed blocks
|
|
||||||
previousBlockStates = blockStates;
|
|
||||||
previousItemStacks = itemStacks;
|
|
||||||
previousFirstPos = firstPos;
|
|
||||||
previousSecondPos = secondPos;
|
|
||||||
|
|
||||||
//if so, renew randomness of randomizer bag
|
|
||||||
AbstractRandomizerBagItem.renewRandomness();
|
|
||||||
//and play sound (max once every tick)
|
|
||||||
if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientEvents.ticksInGame - 0) {
|
|
||||||
soundTime = ClientEvents.ticksInGame;
|
|
||||||
|
|
||||||
if (blockStates.get(0) != null) {
|
|
||||||
SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.level,
|
|
||||||
newCoordinates.get(0), player);
|
|
||||||
player.level.playSound(player, player.blockPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(),
|
|
||||||
SoundSource.BLOCKS, 0.3f, 0.8f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render block previews
|
|
||||||
if (blockStates.size() != 0 && newCoordinates.size() == blockStates.size()) {
|
|
||||||
int blockCount;
|
|
||||||
|
|
||||||
if (!breaking) {
|
|
||||||
//Use fancy shader if config allows, otherwise outlines
|
|
||||||
if (BuildConfig.visuals.useShaders.get() && newCoordinates.size() < BuildConfig.visuals.shaderThreshold.get()) {
|
|
||||||
blockCount = renderBlockPreviews(ms, buffer, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking);
|
|
||||||
|
|
||||||
CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates)
|
|
||||||
.withFaceTexture(AllSpecialTextures.CHECKERED)
|
|
||||||
.disableNormals()
|
|
||||||
.lineWidth(1 / 32f)
|
|
||||||
.colored(new Color(1f, 1f, 1f, 1f));
|
|
||||||
} else {
|
|
||||||
// VertexConsumer vc = RenderHandler.beginLines(buffer);
|
|
||||||
//
|
|
||||||
// Vec3 color = new Vec3(1f, 1f, 1f);
|
|
||||||
// if (breaking) color = new Vec3(1f, 0f, 0f);
|
|
||||||
//
|
|
||||||
// for (int i = newCoordinates.size() - 1; i >= 0; i--) {
|
|
||||||
// VoxelShape collisionShape = blockStates.get(i).getCollisionShape(player.level, newCoordinates.get(i));
|
|
||||||
// RenderHandler.renderBlockOutline(ms, vc, newCoordinates.get(i), collisionShape, color);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// RenderHandler.endLines(buffer);
|
|
||||||
|
|
||||||
CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates)
|
|
||||||
.withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED)
|
|
||||||
.disableNormals()
|
|
||||||
.lineWidth(1 / 16f)
|
|
||||||
.colored(new Color(1f, 1f, 1f, 1f));
|
|
||||||
|
|
||||||
blockCount = newCoordinates.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Breaking
|
|
||||||
CreateClient.OUTLINER.showCluster("blockpreviews", newCoordinates)
|
|
||||||
.withFaceTexture(AllSpecialTextures.THIN_CHECKERED)
|
|
||||||
.disableNormals()
|
|
||||||
.lineWidth(1 / 16f)
|
|
||||||
.colored(new Color(0.8f, 0.1f, 0.1f, 1f));
|
|
||||||
blockCount = newCoordinates.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Display block count and dimensions in actionbar
|
|
||||||
if (BuildModes.isActive(player)) {
|
|
||||||
|
|
||||||
//Find min and max values (not simply firstPos and secondPos because that doesn't work with circles)
|
|
||||||
int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE;
|
|
||||||
int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE;
|
|
||||||
int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE;
|
|
||||||
for (BlockPos pos : startCoordinates) {
|
|
||||||
if (pos.getX() < minX) minX = pos.getX();
|
|
||||||
if (pos.getX() > maxX) maxX = pos.getX();
|
|
||||||
if (pos.getY() < minY) minY = pos.getY();
|
|
||||||
if (pos.getY() > maxY) maxY = pos.getY();
|
|
||||||
if (pos.getZ() < minZ) minZ = pos.getZ();
|
|
||||||
if (pos.getZ() > maxZ) maxZ = pos.getZ();
|
|
||||||
}
|
|
||||||
BlockPos dim = new BlockPos(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1);
|
|
||||||
|
|
||||||
String dimensions = "(";
|
|
||||||
if (dim.getX() > 1) dimensions += dim.getX() + "x";
|
|
||||||
if (dim.getZ() > 1) dimensions += dim.getZ() + "x";
|
|
||||||
if (dim.getY() > 1) dimensions += dim.getY() + "x";
|
|
||||||
dimensions = dimensions.substring(0, dimensions.length() - 1);
|
|
||||||
if (dimensions.length() > 1) dimensions += ")";
|
|
||||||
|
|
||||||
EffortlessBuilding.log(player, blockCount + " blocks " + dimensions, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Draw outlines if no block in hand
|
|
||||||
//Find proper raytrace: either normal range or increased range depending on canBreakFar
|
|
||||||
// VertexConsumer vc = RenderHandler.beginLines(buffer);
|
|
||||||
// HitResult objectMouseOver = Minecraft.getInstance().hitResult;
|
|
||||||
// HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver;
|
|
||||||
//
|
|
||||||
// if (player.isCreative() && noBlockInHand && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) {
|
|
||||||
// BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace;
|
|
||||||
// List<BlockPos> breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos());
|
|
||||||
//
|
|
||||||
// //Only render first outline if further than normal reach
|
|
||||||
// boolean excludeFirst = objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK;
|
|
||||||
// for (int i = excludeFirst ? 1 : 0; i < breakCoordinates.size(); i++) {
|
|
||||||
// BlockPos coordinate = breakCoordinates.get(i);
|
|
||||||
//
|
|
||||||
// BlockState blockState = player.level.getBlockState(coordinate);
|
|
||||||
// if (!blockState.isAir()) {
|
|
||||||
// if (SurvivalHelper.canBreak(player.level, player, coordinate) || i == 0) {
|
|
||||||
// VoxelShape collisionShape = blockState.getCollisionShape(player.level, coordinate);
|
|
||||||
// RenderHandler.renderBlockOutline(ms, vc, coordinate, collisionShape, new Vec3(0f, 0f, 0f));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// RenderHandler.endLines(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Whether to draw any block previews or outlines
|
|
||||||
public static boolean doRenderBlockPreviews(ModifierSettings modifierSettings, ModeSettings modeSettings, BlockPos startPos) {
|
|
||||||
return modeSettings.getBuildMode() != BuildModes.BuildModeEnum.NORMAL ||
|
|
||||||
(startPos != null && BuildModifiers.isEnabled(modifierSettings, startPos)) ||
|
|
||||||
BuildConfig.visuals.alwaysShowBlockPreview.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static int renderBlockPreviews(PoseStack matrixStack, MultiBufferSource.BufferSource renderTypeBuffer, List<BlockPos> coordinates, List<BlockState> blockStates,
|
|
||||||
List<ItemStack> itemStacks, float dissolve, BlockPos firstPos,
|
|
||||||
BlockPos secondPos, boolean checkCanPlace, boolean red) {
|
|
||||||
Player player = Minecraft.getInstance().player;
|
|
||||||
ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
|
||||||
BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer();
|
|
||||||
int blocksValid = 0;
|
|
||||||
|
|
||||||
if (coordinates.isEmpty()) return blocksValid;
|
|
||||||
|
|
||||||
for (int i = coordinates.size() - 1; i >= 0; i--) {
|
|
||||||
BlockPos blockPos = coordinates.get(i);
|
|
||||||
BlockState blockState = blockStates.get(i);
|
|
||||||
ItemStack itemstack = itemStacks.isEmpty() ? ItemStack.EMPTY : itemStacks.get(i);
|
|
||||||
if (CompatHelper.isItemBlockProxy(itemstack))
|
|
||||||
itemstack = CompatHelper.getItemBlockByState(itemstack, blockState);
|
|
||||||
|
|
||||||
//Check if we can place
|
|
||||||
//If check is turned off, check if blockstate is the same (for dissolve effect)
|
|
||||||
if ((!checkCanPlace && player.level.getBlockState(blockPos) == blockState) || //TODO enable (breaks the breaking shader)
|
|
||||||
SurvivalHelper.canPlace(player.level, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), Direction.UP)) {
|
|
||||||
|
|
||||||
RenderHandler.renderBlockPreview(matrixStack, renderTypeBuffer, dispatcher, blockPos, blockState, dissolve, firstPos, secondPos, red);
|
|
||||||
blocksValid++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blocksValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onBlocksPlaced() {
|
|
||||||
onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onBlocksPlaced(List<BlockPos> coordinates, List<ItemStack> itemStacks, List<BlockState> blockStates,
|
|
||||||
BlockPos firstPos, BlockPos secondPos) {
|
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
|
||||||
ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
|
||||||
ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
|
||||||
|
|
||||||
//Check if block previews are enabled
|
|
||||||
if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) {
|
|
||||||
|
|
||||||
//Save current coordinates, blockstates and itemstacks
|
|
||||||
if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() &&
|
|
||||||
coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderThreshold.get()) {
|
|
||||||
|
|
||||||
placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates,
|
|
||||||
itemStacks, firstPos, secondPos, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onBlocksBroken() {
|
|
||||||
onBlocksBroken(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onBlocksBroken(List<BlockPos> coordinates, List<ItemStack> itemStacks, List<BlockState> blockStates,
|
|
||||||
BlockPos firstPos, BlockPos secondPos) {
|
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
|
||||||
ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
|
||||||
ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
|
||||||
|
|
||||||
//Check if block previews are enabled
|
|
||||||
if (doRenderBlockPreviews(modifierSettings, modeSettings, firstPos)) {
|
|
||||||
|
|
||||||
//Save current coordinates, blockstates and itemstacks
|
|
||||||
if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() &&
|
|
||||||
coordinates.size() > 1 && coordinates.size() < BuildConfig.visuals.shaderThreshold.get()) {
|
|
||||||
|
|
||||||
sortOnDistanceToPlayer(coordinates, player);
|
|
||||||
|
|
||||||
placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates,
|
|
||||||
itemStacks, firstPos, secondPos, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sortOnDistanceToPlayer(List<BlockPos> coordinates, Player player) {
|
|
||||||
|
|
||||||
Collections.sort(coordinates, (lhs, rhs) -> {
|
|
||||||
// -1 - less than, 1 - greater than, 0 - equal
|
|
||||||
double lhsDistanceToPlayer = Vec3.atLowerCornerOf(lhs).subtract(player.getEyePosition(1f)).lengthSqr();
|
|
||||||
double rhsDistanceToPlayer = Vec3.atLowerCornerOf(rhs).subtract(player.getEyePosition(1f)).lengthSqr();
|
|
||||||
return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class PlacedData {
|
|
||||||
float time;
|
|
||||||
List<BlockPos> coordinates;
|
|
||||||
List<BlockState> blockStates;
|
|
||||||
List<ItemStack> itemStacks;
|
|
||||||
BlockPos firstPos;
|
|
||||||
BlockPos secondPos;
|
|
||||||
boolean breaking;
|
|
||||||
|
|
||||||
public PlacedData(float time, List<BlockPos> coordinates, List<BlockState> blockStates,
|
|
||||||
List<ItemStack> itemStacks, BlockPos firstPos, BlockPos secondPos, boolean breaking) {
|
|
||||||
this.time = time;
|
|
||||||
this.coordinates = coordinates;
|
|
||||||
this.blockStates = blockStates;
|
|
||||||
this.itemStacks = itemStacks;
|
|
||||||
this.firstPos = firstPos;
|
|
||||||
this.secondPos = secondPos;
|
|
||||||
this.breaking = breaking;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
package nl.requios.effortlessbuilding.render;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import nl.requios.effortlessbuilding.ClientConfig;
|
||||||
|
import nl.requios.effortlessbuilding.ClientEvents;
|
||||||
|
import nl.requios.effortlessbuilding.CommonConfig;
|
||||||
|
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||||
|
import nl.requios.effortlessbuilding.buildmode.BuildModes;
|
||||||
|
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
|
||||||
|
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
|
||||||
|
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings;
|
||||||
|
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
|
||||||
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings;
|
||||||
|
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||||
|
import nl.requios.effortlessbuilding.create.AllSpecialTextures;
|
||||||
|
import nl.requios.effortlessbuilding.create.CreateClient;
|
||||||
|
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
|
||||||
|
import nl.requios.effortlessbuilding.helper.ReachHelper;
|
||||||
|
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
||||||
|
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public class BlockPreviews {
|
||||||
|
private static final List<PlacedData> placedDataList = new ArrayList<>();
|
||||||
|
private static List<BlockPos> previousCoordinates;
|
||||||
|
private static List<BlockState> previousBlockStates;
|
||||||
|
private static List<ItemStack> previousItemStacks;
|
||||||
|
private static BlockPos previousFirstPos;
|
||||||
|
private static BlockPos previousSecondPos;
|
||||||
|
private static int soundTime = 0;
|
||||||
|
|
||||||
|
public static void drawPlacedBlocks(Player player, ModifierSettings modifierSettings) {
|
||||||
|
//Render placed blocks with appear animation
|
||||||
|
if (ClientConfig.visuals.showBlockPreviews.get()) {
|
||||||
|
for (PlacedData placed : placedDataList) {
|
||||||
|
if (placed.coordinates != null && !placed.coordinates.isEmpty()) {
|
||||||
|
|
||||||
|
int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get();
|
||||||
|
if (totalTime <= 0) continue;
|
||||||
|
|
||||||
|
float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime;
|
||||||
|
renderBlockPreviews(player, modifierSettings, placed.coordinates, placed.blockStates, placed.itemStacks, dissolve, placed.firstPos, placed.secondPos, false, placed.breaking);
|
||||||
|
|
||||||
|
CreateClient.OUTLINER.showCluster(placed.time, placed.coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Expire
|
||||||
|
placedDataList.removeIf(placed -> {
|
||||||
|
int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get();
|
||||||
|
return placed.time + totalTime < ClientEvents.ticksInGame;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void drawLookAtPreview(Player player, ModeSettings modeSettings, ModifierSettings modifierSettings, BlockPos startPos, Direction sideHit, Vec3 hitVec) {
|
||||||
|
if (!doShowBlockPreviews(modifierSettings, modeSettings, startPos)) return;
|
||||||
|
|
||||||
|
//Keep blockstate the same for every block in the buildmode
|
||||||
|
//So dont rotate blocks when in the middle of placing wall etc.
|
||||||
|
if (BuildModes.isActive(player)) {
|
||||||
|
IBuildMode buildModeInstance = modeSettings.getBuildMode().instance;
|
||||||
|
if (buildModeInstance.getSideHit(player) != null) sideHit = buildModeInstance.getSideHit(player);
|
||||||
|
if (buildModeInstance.getHitVec(player) != null) hitVec = buildModeInstance.getHitVec(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sideHit == null) return;
|
||||||
|
|
||||||
|
//Should be red?
|
||||||
|
boolean breaking = BuildModes.currentlyBreakingClient.get(player) != null && BuildModes.currentlyBreakingClient.get(player);
|
||||||
|
|
||||||
|
//get coordinates
|
||||||
|
List<BlockPos> startCoordinates = BuildModes.findCoordinates(player, startPos, breaking || modifierSettings.doQuickReplace());
|
||||||
|
|
||||||
|
//Remember first and last point for the shader
|
||||||
|
BlockPos firstPos = BlockPos.ZERO, secondPos = BlockPos.ZERO;
|
||||||
|
if (!startCoordinates.isEmpty()) {
|
||||||
|
firstPos = startCoordinates.get(0);
|
||||||
|
secondPos = startCoordinates.get(startCoordinates.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Limit number of blocks you can place
|
||||||
|
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
|
||||||
|
if (startCoordinates.size() > limit) {
|
||||||
|
startCoordinates = startCoordinates.subList(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BlockPos> newCoordinates = BuildModifiers.findCoordinates(player, startCoordinates);
|
||||||
|
|
||||||
|
sortOnDistanceToPlayer(newCoordinates, player);
|
||||||
|
|
||||||
|
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)),
|
||||||
|
Math.abs(hitVec.z - ((int) hitVec.z)));
|
||||||
|
|
||||||
|
//Get blockstates
|
||||||
|
List<ItemStack> itemStacks = new ArrayList<>();
|
||||||
|
List<BlockState> blockStates = new ArrayList<>();
|
||||||
|
if (breaking) {
|
||||||
|
//Find blockstate of world
|
||||||
|
for (BlockPos coordinate : newCoordinates) {
|
||||||
|
blockStates.add(player.level.getBlockState(coordinate));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockStates = BuildModifiers.findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Check if they are different from previous
|
||||||
|
//TODO fix triggering when moving player
|
||||||
|
if (!BuildModifiers.compareCoordinates(previousCoordinates, newCoordinates)) {
|
||||||
|
previousCoordinates = newCoordinates;
|
||||||
|
//remember the rest for placed blocks
|
||||||
|
previousBlockStates = blockStates;
|
||||||
|
previousItemStacks = itemStacks;
|
||||||
|
previousFirstPos = firstPos;
|
||||||
|
previousSecondPos = secondPos;
|
||||||
|
|
||||||
|
//if so, renew randomness of randomizer bag
|
||||||
|
AbstractRandomizerBagItem.renewRandomness();
|
||||||
|
//and play sound (max once every tick)
|
||||||
|
if (newCoordinates.size() > 1 && blockStates.size() > 1 && soundTime < ClientEvents.ticksInGame - 0) {
|
||||||
|
soundTime = ClientEvents.ticksInGame;
|
||||||
|
|
||||||
|
if (blockStates.get(0) != null) {
|
||||||
|
SoundType soundType = blockStates.get(0).getBlock().getSoundType(blockStates.get(0), player.level,
|
||||||
|
newCoordinates.get(0), player);
|
||||||
|
player.level.playSound(player, player.blockPosition(), breaking ? soundType.getBreakSound() : soundType.getPlaceSound(),
|
||||||
|
SoundSource.BLOCKS, 0.3f, 0.8f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockStates.size() == 0 || newCoordinates.size() != blockStates.size()) return;
|
||||||
|
|
||||||
|
int blockCount;
|
||||||
|
|
||||||
|
Object outlineID = firstPos;
|
||||||
|
//Dont fade out the outline if we are still determining where to place
|
||||||
|
//Every outline with same ID will not fade out (because it gets replaced)
|
||||||
|
if (newCoordinates.size() == 1 || BuildModifiers.isEnabled(modifierSettings, firstPos)) outlineID = "single";
|
||||||
|
|
||||||
|
if (!breaking) {
|
||||||
|
//Use fancy shader if config allows, otherwise outlines
|
||||||
|
if (ClientConfig.visuals.showBlockPreviews.get() && newCoordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) {
|
||||||
|
blockCount = renderBlockPreviews(player, modifierSettings, newCoordinates, blockStates, itemStacks, 0f, firstPos, secondPos, !breaking, breaking);
|
||||||
|
|
||||||
|
CreateClient.OUTLINER.showCluster(outlineID, newCoordinates)
|
||||||
|
.withFaceTexture(AllSpecialTextures.CHECKERED)
|
||||||
|
.disableNormals()
|
||||||
|
.lineWidth(1 / 32f)
|
||||||
|
.colored(new Color(1f, 1f, 1f, 1f));
|
||||||
|
} else {
|
||||||
|
//Thicker outline without block previews
|
||||||
|
CreateClient.OUTLINER.showCluster(outlineID, newCoordinates)
|
||||||
|
.withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED)
|
||||||
|
.disableNormals()
|
||||||
|
.lineWidth(1 / 16f)
|
||||||
|
.colored(new Color(1f, 1f, 1f, 1f));
|
||||||
|
|
||||||
|
blockCount = newCoordinates.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Breaking
|
||||||
|
CreateClient.OUTLINER.showCluster(outlineID, newCoordinates)
|
||||||
|
.withFaceTexture(AllSpecialTextures.THIN_CHECKERED)
|
||||||
|
.disableNormals()
|
||||||
|
.lineWidth(1 / 16f)
|
||||||
|
.colored(new Color(0.8f, 0.1f, 0.1f, 1f));
|
||||||
|
blockCount = newCoordinates.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Display block count and dimensions in actionbar
|
||||||
|
if (BuildModes.isActive(player)) {
|
||||||
|
|
||||||
|
//Find min and max values (not simply firstPos and secondPos because that doesn't work with circles)
|
||||||
|
int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE;
|
||||||
|
int minY = Integer.MAX_VALUE, maxY = Integer.MIN_VALUE;
|
||||||
|
int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE;
|
||||||
|
for (BlockPos pos : startCoordinates) {
|
||||||
|
if (pos.getX() < minX) minX = pos.getX();
|
||||||
|
if (pos.getX() > maxX) maxX = pos.getX();
|
||||||
|
if (pos.getY() < minY) minY = pos.getY();
|
||||||
|
if (pos.getY() > maxY) maxY = pos.getY();
|
||||||
|
if (pos.getZ() < minZ) minZ = pos.getZ();
|
||||||
|
if (pos.getZ() > maxZ) maxZ = pos.getZ();
|
||||||
|
}
|
||||||
|
BlockPos dim = new BlockPos(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1);
|
||||||
|
|
||||||
|
String dimensions = "(";
|
||||||
|
if (dim.getX() > 1) dimensions += dim.getX() + "x";
|
||||||
|
if (dim.getZ() > 1) dimensions += dim.getZ() + "x";
|
||||||
|
if (dim.getY() > 1) dimensions += dim.getY() + "x";
|
||||||
|
dimensions = dimensions.substring(0, dimensions.length() - 1);
|
||||||
|
if (dimensions.length() > 1) dimensions += ")";
|
||||||
|
|
||||||
|
EffortlessBuilding.log(player, blockCount + " blocks " + dimensions, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void drawOutlinesIfNoBlockInHand(Player player, HitResult lookingAt) {
|
||||||
|
//Draw outlines if no block in hand
|
||||||
|
//Find proper raytrace: either normal range or increased range depending on canBreakFar
|
||||||
|
HitResult objectMouseOver = Minecraft.getInstance().hitResult;
|
||||||
|
HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver;
|
||||||
|
|
||||||
|
if (player.isCreative() && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) {
|
||||||
|
BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace;
|
||||||
|
List<BlockPos> breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos());
|
||||||
|
|
||||||
|
//Only render first outline if further than normal reach
|
||||||
|
if (objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK)
|
||||||
|
breakCoordinates.remove(0);
|
||||||
|
|
||||||
|
breakCoordinates.removeIf(pos -> {
|
||||||
|
BlockState blockState = player.level.getBlockState(pos);
|
||||||
|
if (blockState.isAir() || blockState.getMaterial().isLiquid()) return true;
|
||||||
|
return !SurvivalHelper.canBreak(player.level, player, pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!breakCoordinates.isEmpty()) {
|
||||||
|
CreateClient.OUTLINER.showCluster("break", breakCoordinates)
|
||||||
|
.disableNormals()
|
||||||
|
.lineWidth(1 / 64f)
|
||||||
|
.colored(0x222222);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Whether to draw any block previews or outlines
|
||||||
|
public static boolean doShowBlockPreviews(ModifierSettings modifierSettings, ModeSettings modeSettings, BlockPos startPos) {
|
||||||
|
if (!ClientConfig.visuals.showBlockPreviews.get()) return false;
|
||||||
|
return modeSettings.getBuildMode() != BuildModes.BuildModeEnum.DISABLED ||
|
||||||
|
(startPos != null && BuildModifiers.isEnabled(modifierSettings, startPos)) ||
|
||||||
|
ClientConfig.visuals.alwaysShowBlockPreview.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int renderBlockPreviews(Player player, ModifierSettings modifierSettings, List<BlockPos> coordinates,
|
||||||
|
List<BlockState> blockStates, List<ItemStack> itemStacks, float dissolve,
|
||||||
|
BlockPos firstPos, BlockPos secondPos, boolean checkCanPlace, boolean red) {
|
||||||
|
int blocksValid = 0;
|
||||||
|
|
||||||
|
if (coordinates.isEmpty()) return blocksValid;
|
||||||
|
|
||||||
|
for (int i = coordinates.size() - 1; i >= 0; i--) {
|
||||||
|
BlockPos blockPos = coordinates.get(i);
|
||||||
|
BlockState blockState = blockStates.get(i);
|
||||||
|
ItemStack itemstack = itemStacks.isEmpty() ? ItemStack.EMPTY : itemStacks.get(i);
|
||||||
|
if (CompatHelper.isItemBlockProxy(itemstack))
|
||||||
|
itemstack = CompatHelper.getItemBlockByState(itemstack, blockState);
|
||||||
|
|
||||||
|
//Check if we can place
|
||||||
|
boolean canPlace = true;
|
||||||
|
if (checkCanPlace) {
|
||||||
|
canPlace = SurvivalHelper.canPlace(player.level, player, blockPos, blockState, itemstack, modifierSettings.doQuickReplace(), Direction.UP);
|
||||||
|
} else {
|
||||||
|
//If check is turned off, check if blockstate is the same (for dissolve effect)
|
||||||
|
canPlace = player.level.getBlockState(blockPos) != blockState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canPlace) {
|
||||||
|
renderBlockPreview(blockPos, blockState, dissolve, firstPos, secondPos, red);
|
||||||
|
blocksValid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blocksValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void renderBlockPreview(BlockPos blockPos, BlockState blockState, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean breaking) {
|
||||||
|
if (blockState == null) return;
|
||||||
|
|
||||||
|
float scale = 0.5f;
|
||||||
|
float alpha = 0.7f;
|
||||||
|
if (dissolve > 0f) {
|
||||||
|
float animationLength = 0.7f;
|
||||||
|
|
||||||
|
double firstToSecond = secondPos.distSqr(firstPos);
|
||||||
|
double place = 0;
|
||||||
|
if (firstToSecond > 0.5) {
|
||||||
|
double placeFromFirst = firstPos.distSqr(blockPos) / firstToSecond;
|
||||||
|
double placeFromSecond = secondPos.distSqr(blockPos) / firstToSecond;
|
||||||
|
place = (placeFromFirst + (1.0 - placeFromSecond)) / 2.0;
|
||||||
|
} //else only one block
|
||||||
|
|
||||||
|
//Scale place so we start first animation at 0 and end last animation at 1
|
||||||
|
place *= 1f - animationLength;
|
||||||
|
float diff = dissolve - (float) place;
|
||||||
|
float t = diff / animationLength;
|
||||||
|
t = Mth.clamp(t, 0, 1);
|
||||||
|
//Now we got a usable t value for this block
|
||||||
|
|
||||||
|
float sine = Mth.sin((t + (1/7f) * t) * Mth.TWO_PI - (3/4f) * Mth.PI); // -1 to 1
|
||||||
|
sine = (sine + 1f) / 2f; // 0 to 1
|
||||||
|
|
||||||
|
// scale = 1f + (sine * 0.5f);
|
||||||
|
|
||||||
|
t = (float) Mth.smoothstep(t);
|
||||||
|
if (!breaking) {
|
||||||
|
scale = 0.5f + (t * 0.3f);
|
||||||
|
alpha = 0.7f + (t * 0.3f);
|
||||||
|
} else {
|
||||||
|
t = 1f - t;
|
||||||
|
scale = 0.5f + (t * 0.5f);
|
||||||
|
alpha = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState)
|
||||||
|
.at(blockPos)
|
||||||
|
.alpha(alpha)
|
||||||
|
.scale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBlocksPlaced() {
|
||||||
|
onBlocksPlaced(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBlocksPlaced(List<BlockPos> coordinates, List<ItemStack> itemStacks, List<BlockState> blockStates,
|
||||||
|
BlockPos firstPos, BlockPos secondPos) {
|
||||||
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
|
ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
||||||
|
|
||||||
|
//Check if block previews are enabled
|
||||||
|
if (doShowBlockPreviews(modifierSettings, modeSettings, firstPos)) {
|
||||||
|
|
||||||
|
//Save current coordinates, blockstates and itemstacks
|
||||||
|
if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() &&
|
||||||
|
coordinates.size() > 1 && coordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) {
|
||||||
|
|
||||||
|
placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates,
|
||||||
|
itemStacks, firstPos, secondPos, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBlocksBroken() {
|
||||||
|
onBlocksBroken(previousCoordinates, previousItemStacks, previousBlockStates, previousFirstPos, previousSecondPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBlocksBroken(List<BlockPos> coordinates, List<ItemStack> itemStacks, List<BlockState> blockStates,
|
||||||
|
BlockPos firstPos, BlockPos secondPos) {
|
||||||
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
|
ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
||||||
|
|
||||||
|
//Check if block previews are enabled
|
||||||
|
if (doShowBlockPreviews(modifierSettings, modeSettings, firstPos)) {
|
||||||
|
|
||||||
|
//Save current coordinates, blockstates and itemstacks
|
||||||
|
if (!coordinates.isEmpty() && blockStates.size() == coordinates.size() &&
|
||||||
|
coordinates.size() > 1 && coordinates.size() < ClientConfig.visuals.maxBlockPreviews.get()) {
|
||||||
|
|
||||||
|
sortOnDistanceToPlayer(coordinates, player);
|
||||||
|
|
||||||
|
placedDataList.add(new PlacedData(ClientEvents.ticksInGame, coordinates, blockStates,
|
||||||
|
itemStacks, firstPos, secondPos, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sortOnDistanceToPlayer(List<BlockPos> coordinates, Player player) {
|
||||||
|
|
||||||
|
Collections.sort(coordinates, (lhs, rhs) -> {
|
||||||
|
// -1 - less than, 1 - greater than, 0 - equal
|
||||||
|
double lhsDistanceToPlayer = Vec3.atLowerCornerOf(lhs).subtract(player.getEyePosition(1f)).lengthSqr();
|
||||||
|
double rhsDistanceToPlayer = Vec3.atLowerCornerOf(rhs).subtract(player.getEyePosition(1f)).lengthSqr();
|
||||||
|
return (int) Math.signum(lhsDistanceToPlayer - rhsDistanceToPlayer);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PlacedData {
|
||||||
|
float time;
|
||||||
|
List<BlockPos> coordinates;
|
||||||
|
List<BlockState> blockStates;
|
||||||
|
List<ItemStack> itemStacks;
|
||||||
|
BlockPos firstPos;
|
||||||
|
BlockPos secondPos;
|
||||||
|
boolean breaking;
|
||||||
|
|
||||||
|
public PlacedData(float time, List<BlockPos> coordinates, List<BlockState> blockStates,
|
||||||
|
List<ItemStack> itemStacks, BlockPos firstPos, BlockPos secondPos, boolean breaking) {
|
||||||
|
this.time = time;
|
||||||
|
this.coordinates = coordinates;
|
||||||
|
this.blockStates = blockStates;
|
||||||
|
this.itemStacks = itemStacks;
|
||||||
|
this.firstPos = firstPos;
|
||||||
|
this.secondPos = secondPos;
|
||||||
|
this.breaking = breaking;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,32 +5,25 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||||||
import com.mojang.blaze3d.vertex.Tesselator;
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.renderer.Sheets;
|
|
||||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.client.event.RenderLevelLastEvent;
|
|
||||||
import net.minecraftforge.client.event.RenderLevelStageEvent;
|
import net.minecraftforge.client.event.RenderLevelStageEvent;
|
||||||
import net.minecraftforge.client.model.data.ModelData;
|
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
import nl.requios.effortlessbuilding.ClientEvents;
|
||||||
|
import nl.requios.effortlessbuilding.buildmode.BuildModes;
|
||||||
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
|
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
|
||||||
import nl.requios.effortlessbuilding.create.CreateClient;
|
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||||
import nl.requios.effortlessbuilding.create.events.ClientEvents;
|
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Main render class for Effortless Building
|
* Main render class for Effortless Building
|
||||||
@@ -40,26 +33,64 @@ public class RenderHandler {
|
|||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onTick(TickEvent.ClientTickEvent event) {
|
public static void onTick(TickEvent.ClientTickEvent event) {
|
||||||
if (!ClientEvents.isGameActive()) return;
|
if (!nl.requios.effortlessbuilding.create.events.ClientEvents.isGameActive()) return;
|
||||||
|
|
||||||
Player player = Minecraft.getInstance().player;
|
var player = Minecraft.getInstance().player;
|
||||||
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
var modeSettings = ModeSettingsManager.getModeSettings(player);
|
||||||
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
|
|
||||||
BlockPreviewRenderer.render(null, null, player, modifierSettings, modeSettings);
|
BlockPreviews.drawPlacedBlocks(player, modifierSettings);
|
||||||
|
|
||||||
|
|
||||||
|
HitResult lookingAt = ClientEvents.getLookingAt(player);
|
||||||
|
if (modeSettings.getBuildMode() == BuildModes.BuildModeEnum.DISABLED)
|
||||||
|
lookingAt = Minecraft.getInstance().hitResult;
|
||||||
|
|
||||||
|
ItemStack mainhand = player.getMainHandItem();
|
||||||
|
boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand));
|
||||||
|
|
||||||
|
//Find start position, side hit and hit vector
|
||||||
|
BlockPos startPos = null;
|
||||||
|
Direction sideHit = null;
|
||||||
|
Vec3 hitVec = null;
|
||||||
|
|
||||||
|
//Checking for null is necessary! Even in vanilla when looking down ladders it is occasionally null (instead of Type MISS)
|
||||||
|
if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) {
|
||||||
|
BlockHitResult blockLookingAt = (BlockHitResult) lookingAt;
|
||||||
|
startPos = blockLookingAt.getBlockPos();
|
||||||
|
|
||||||
|
//Check if tool (or none) in hand
|
||||||
|
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
|
||||||
|
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, blockLookingAt.getDirection());
|
||||||
|
if (!modifierSettings.doQuickReplace() && !noBlockInHand && !replaceable && !becomesDoubleSlab) {
|
||||||
|
startPos = startPos.relative(blockLookingAt.getDirection());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get under tall grass and other replaceable blocks
|
||||||
|
if (modifierSettings.doQuickReplace() && !noBlockInHand && replaceable) {
|
||||||
|
startPos = startPos.below();
|
||||||
|
}
|
||||||
|
|
||||||
|
sideHit = blockLookingAt.getDirection();
|
||||||
|
hitVec = blockLookingAt.getLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockPreviews.drawLookAtPreview(player, modeSettings, modifierSettings, startPos, sideHit, hitVec);
|
||||||
|
|
||||||
|
if (noBlockInHand) BlockPreviews.drawOutlinesIfNoBlockInHand(player, lookingAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onRender(RenderLevelLastEvent event) {
|
public static void onRender(RenderLevelStageEvent event) {
|
||||||
|
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) return;
|
||||||
|
|
||||||
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
|
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
|
||||||
// float pt = AnimationTickHolder.getPartialTicks();
|
|
||||||
|
|
||||||
PoseStack ms = event.getPoseStack();
|
PoseStack ms = event.getPoseStack();
|
||||||
BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder();
|
BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder();
|
||||||
MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(bufferBuilder);
|
MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(bufferBuilder);
|
||||||
|
|
||||||
// SuperRenderTypeBuffer superBuffer = SuperRenderTypeBuffer.getInstance();
|
|
||||||
|
|
||||||
Player player = Minecraft.getInstance().player;
|
Player player = Minecraft.getInstance().player;
|
||||||
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
|
||||||
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
|
||||||
@@ -70,15 +101,6 @@ public class RenderHandler {
|
|||||||
//Mirror and radial mirror lines and areas
|
//Mirror and radial mirror lines and areas
|
||||||
ModifierRenderer.render(ms, buffer, modifierSettings);
|
ModifierRenderer.render(ms, buffer, modifierSettings);
|
||||||
|
|
||||||
//Render block previews
|
|
||||||
BlockPreviewRenderer.render(ms, buffer, player, modifierSettings, modeSettings);
|
|
||||||
|
|
||||||
//Create
|
|
||||||
// CreateClient.GHOST_BLOCKS.renderAll(ms, superBuffer);
|
|
||||||
// EffortlessBuildingClient.OUTLINER.renderOutlines(ms, superBuffer, pt);
|
|
||||||
// superBuffer.draw();
|
|
||||||
// RenderSystem.enableCull();
|
|
||||||
|
|
||||||
ms.popPose();
|
ms.popPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,82 +120,4 @@ public class RenderHandler {
|
|||||||
renderTypeBuffer.endBatch();
|
renderTypeBuffer.endBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void renderBlockPreview(PoseStack ms, MultiBufferSource.BufferSource buffer, BlockRenderDispatcher dispatcher,
|
|
||||||
BlockPos blockPos, BlockState blockState, float dissolve, BlockPos firstPos, BlockPos secondPos, boolean red) {
|
|
||||||
if (blockState == null) return;
|
|
||||||
|
|
||||||
CreateClient.GHOST_BLOCKS.showGhostState(blockPos.toShortString(), blockState)
|
|
||||||
// .breathingAlpha()
|
|
||||||
.alpha(0.7f)
|
|
||||||
.at(blockPos);
|
|
||||||
|
|
||||||
// ms.pushPose();
|
|
||||||
// ms.translate(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
|
||||||
//// ms.rotate(Vector3f.YP.rotationDegrees(-90f));
|
|
||||||
// ms.translate(-0.01f, -0.01f, -0.01f);
|
|
||||||
// ms.scale(1.02f, 1.02f, 1.02f);
|
|
||||||
//
|
|
||||||
// //Begin block preview rendering
|
|
||||||
// RenderType blockPreviewRenderType = BuildRenderTypes.getBlockPreviewRenderType(dissolve, blockPos, firstPos, secondPos, red);
|
|
||||||
// VertexConsumer vc = buffer.getBuffer(blockPreviewRenderType);
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// BakedModel model = dispatcher.getBlockModel(blockState);
|
|
||||||
// dispatcher.getModelRenderer().renderModel(ms.last(), vc, blockState, model,
|
|
||||||
// 1f, 1f, 1f, 0, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, blockPreviewRenderType);
|
|
||||||
// } catch (NullPointerException e) {
|
|
||||||
// EffortlessBuilding.logger.warn("RenderHandler::renderBlockPreview cannot render " + blockState.getBlock().toString());
|
|
||||||
//
|
|
||||||
// //Render outline as backup, escape out of the current renderstack
|
|
||||||
// ms.popPose();
|
|
||||||
// buffer.endBatch();
|
|
||||||
// VertexConsumer lineBuffer = beginLines(buffer);
|
|
||||||
// renderBlockOutline(ms, lineBuffer, blockPos, new Vec3(1f, 1f, 1f));
|
|
||||||
// endLines(buffer);
|
|
||||||
// vc = buffer.getBuffer(Sheets.translucentCullBlockSheet()); //any type will do, as long as we have something on the stack
|
|
||||||
// ms.pushPose();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// buffer.endBatch();
|
|
||||||
// ms.popPose();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos, Vec3 color) {
|
|
||||||
renderBlockOutline(matrixStack, buffer, pos, pos, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Renders outline. Pos1 has to be minimal x,y,z and pos2 maximal x,y,z
|
|
||||||
protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos1, BlockPos pos2, Vec3 color) {
|
|
||||||
AABB aabb = new AABB(pos1, pos2.offset(1, 1, 1)).inflate(0.0020000000949949026);
|
|
||||||
|
|
||||||
LevelRenderer.renderLineBox(matrixStack, buffer, aabb, (float) color.x, (float) color.y, (float) color.z, 0.4f);
|
|
||||||
// WorldRenderer.drawSelectionBoundingBox(aabb, (float) color.x, (float) color.y, (float) color.z, 0.4f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Renders outline with given bounding box
|
|
||||||
protected static void renderBlockOutline(PoseStack matrixStack, VertexConsumer buffer, BlockPos pos, VoxelShape collisionShape, Vec3 color) {
|
|
||||||
// WorldRenderer.drawShape(collisionShape, pos.getX(), pos.getY(), pos.getZ(), (float) color.x, (float) color.y, (float) color.z, 0.4f);
|
|
||||||
LevelRenderer.renderVoxelShape(matrixStack, buffer, collisionShape, pos.getX(), pos.getY(), pos.getZ(), (float) color.x, (float) color.y, (float) color.z, 0.4f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
//Sends breaking progress for all coordinates to renderglobal, so all blocks get visually broken
|
|
||||||
// @Override
|
|
||||||
// public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {
|
|
||||||
// Minecraft mc = Minecraft.getInstance();
|
|
||||||
//
|
|
||||||
// ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(mc.player);
|
|
||||||
// if (!BuildModifiers.isEnabled(modifierSettings, pos)) return;
|
|
||||||
//
|
|
||||||
// List<BlockPos> coordinates = BuildModifiers.findCoordinates(mc.player, pos);
|
|
||||||
// for (int i = 1; i < coordinates.size(); i++) {
|
|
||||||
// BlockPos coordinate = coordinates.get(i);
|
|
||||||
// if (SurvivalHelper.canBreak(mc.world, mc.player, coordinate)) {
|
|
||||||
// //Send i as entity id because only one block can be broken per id
|
|
||||||
// //Unless i happens to be the player id, then take something else
|
|
||||||
// int fakeId = mc.player.getEntityId() != i ? i : coordinates.size();
|
|
||||||
// mc.renderGlobal.sendBlockBreakProgress(fakeId, coordinate, progress);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 333 B |
|
Before Width: | Height: | Size: 317 B After Width: | Height: | Size: 317 B |
Reference in New Issue
Block a user