From ddbe201155e618acf564919ccc8826e8d1f6ef83 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Wed, 31 Mar 2021 21:09:03 +0200 Subject: [PATCH 01/35] Fixed Bug #33 Preview broken in locations beyond 100k --- gradle.properties | 2 +- .../client/RenderBlockPreview.java | 36 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/gradle.properties b/gradle.properties index 6505f86..b7b7785 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,4 +11,4 @@ mcp_mappings=20200723-1.16.1 botania=1.16.2-405 version_major=2 -version_minor=0 \ No newline at end of file +version_minor=1 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java index c79006f..d381c60 100644 --- a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java +++ b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java @@ -2,7 +2,6 @@ package thetadev.constructionwand.client; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.IVertexBuilder; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.entity.Entity; @@ -33,7 +32,7 @@ public class RenderBlockPreview Entity entity = event.getInfo().getRenderViewEntity(); if(!(entity instanceof PlayerEntity)) return; PlayerEntity player = (PlayerEntity) entity; - Set blocks = null; + Set blocks; float colorR = 0, colorG = 0, colorB = 0; ItemStack wand = WandUtil.holdingWand(player); @@ -52,7 +51,22 @@ public class RenderBlockPreview if(blocks == null || blocks.isEmpty()) return; - renderBlockList(blocks, event.getMatrix(), event.getBuffers(), colorR, colorG, colorB); + MatrixStack ms = event.getMatrix(); + IRenderTypeBuffer buffer = event.getBuffers(); + IVertexBuilder lineBuilder = buffer.getBuffer(RenderTypes.TRANSLUCENT_LINES); + + double partialTicks = event.getPartialTicks(); + double d0 = player.lastTickPosX + (player.getPosX() - player.lastTickPosX) * partialTicks; + double d1 = player.lastTickPosY + player.getEyeHeight() + (player.getPosY() - player.lastTickPosY) * partialTicks; + double d2 = player.lastTickPosZ + (player.getPosZ() - player.lastTickPosZ) * partialTicks; + + ms.push(); + + for(BlockPos block : blocks) { + AxisAlignedBB aabb = new AxisAlignedBB(block).offset(-d0, -d1, -d2); + WorldRenderer.drawBoundingBox(ms, lineBuilder, aabb, colorR, colorG, colorB, 0.4F); + } + ms.pop(); event.setCanceled(true); } @@ -60,20 +74,4 @@ public class RenderBlockPreview private static boolean compareRTR(BlockRayTraceResult rtr1, BlockRayTraceResult rtr2) { return rtr1.getPos().equals(rtr2.getPos()) && rtr1.getFace().equals(rtr2.getFace()); } - - private void renderBlockList(Set blocks, MatrixStack ms, IRenderTypeBuffer buffer, float red, float green, float blue) { - double renderPosX = Minecraft.getInstance().getRenderManager().info.getProjectedView().getX(); - double renderPosY = Minecraft.getInstance().getRenderManager().info.getProjectedView().getY(); - double renderPosZ = Minecraft.getInstance().getRenderManager().info.getProjectedView().getZ(); - - ms.push(); - ms.translate(-renderPosX, -renderPosY, -renderPosZ); - - for(BlockPos block : blocks) { - AxisAlignedBB aabb = new AxisAlignedBB(block); - IVertexBuilder lineBuilder = buffer.getBuffer(RenderTypes.TRANSLUCENT_LINES); - WorldRenderer.drawBoundingBox(ms, lineBuilder, aabb, red, green, blue, 0.4F); - } - ms.pop(); - } } From f2d99a8251add1ac8b6fe0595e5944750a083174 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 22 Apr 2021 07:12:28 +0200 Subject: [PATCH 02/35] Fixed Bug #34 Crash on client startup --- gradle.properties | 2 +- .../java/thetadev/constructionwand/ConstructionWand.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index b7b7785..e97a662 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,4 +11,4 @@ mcp_mappings=20200723-1.16.1 botania=1.16.2-405 version_major=2 -version_minor=1 \ No newline at end of file +version_minor=2 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/ConstructionWand.java b/src/main/java/thetadev/constructionwand/ConstructionWand.java index 95e49de..7f811d7 100644 --- a/src/main/java/thetadev/constructionwand/ConstructionWand.java +++ b/src/main/java/thetadev/constructionwand/ConstructionWand.java @@ -66,7 +66,7 @@ public class ConstructionWand int packetIndex = 0; HANDLER.registerMessage(packetIndex++, PacketUndoBlocks.class, PacketUndoBlocks::encode, PacketUndoBlocks::decode, PacketUndoBlocks.Handler::handle); HANDLER.registerMessage(packetIndex++, PacketQueryUndo.class, PacketQueryUndo::encode, PacketQueryUndo::decode, PacketQueryUndo.Handler::handle); - HANDLER.registerMessage(packetIndex++, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle); + HANDLER.registerMessage(packetIndex, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle); // Container registry ContainerRegistrar.register(); @@ -82,8 +82,9 @@ public class ConstructionWand renderBlockPreview = new RenderBlockPreview(); MinecraftForge.EVENT_BUS.register(renderBlockPreview); MinecraftForge.EVENT_BUS.register(new ClientEvents()); - ModItems.registerModelProperties(); - ModItems.registerItemColors(); + + event.enqueueWork(ModItems::registerModelProperties); + event.enqueueWork(ModItems::registerItemColors); } public static ResourceLocation loc(String name) { From 489e050a7f95280b56256396148804f2eed62155 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 22 Apr 2021 07:26:32 +0200 Subject: [PATCH 03/35] Updated version info --- src/main/resources/META-INF/mods.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index e3238ee..bcbc6c5 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -25,7 +25,7 @@ This is my first minecraft mod. May the odds be ever in your favor. side="BOTH" [[dependencies.constructionwand]] modId="minecraft" - mandatory=true - versionRange="[1.16.2, 1.16.5]" - ordering="NONE" + mandatory = true +versionRange = "[1.16.2, 1.17)" +ordering = "NONE" side="BOTH" From 6b555d207777c0c68b5e25da748d7fb979e59ca0 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Mon, 2 Aug 2021 09:34:26 +0200 Subject: [PATCH 04/35] Updated Gradle, Forge and MCP for MC 1.17 --- build.gradle | 8 ++-- gradle.properties | 11 ++--- gradle/wrapper/gradle-wrapper.jar | Bin 54708 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 55 ++++++++++++++--------- gradlew.bat | 43 ++++++++++-------- 6 files changed, 67 insertions(+), 52 deletions(-) diff --git a/build.gradle b/build.gradle index f075063..555679a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,10 @@ buildscript { repositories { maven { url = 'https://files.minecraftforge.net/maven' } - jcenter() mavenCentral() } dependencies { - classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true } } apply plugin: 'net.minecraftforge.gradle' @@ -17,10 +16,11 @@ version = "${mcversion}-${version_major}.${version_minor}" group = "${author}.${modid}" archivesBaseName = "${modid}" -sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. +// Mojang ships Java 16 to end users in 1.17+ instead of Java 8 in 1.16 or lower, so your mod should target Java 16. +java.toolchain.languageVersion = JavaLanguageVersion.of(16) minecraft { - mappings channel: 'snapshot', version: project.mcp_mappings + mappings channel: project.mcp_channel, version: project.mcp_mappings runs { client { diff --git a/gradle.properties b/gradle.properties index e97a662..d9915f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,11 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false - author=thetadev modid=constructionwand - -mcversion=1.16.5 -forgeversion=36.0.46 -mcp_mappings=20200723-1.16.1 - +mcversion=1.17.1 +forgeversion=37.0.19 +mcp_channel=official +mcp_mappings=1.17.1 botania=1.16.2-405 - version_major=2 version_minor=2 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee94c0ab25cf079ac8ccdf87f41d455d42..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 54708 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

_x$+l{{cH8$W#CT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1d5b29f..0595cf7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-7.2-20210702220150+0000-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip diff --git a/gradlew b/gradlew index cccdd3d..744e882 100644 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -56,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From e2394ff05ee2f8d53a313bc29e574824487fc38d Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Mon, 2 Aug 2021 23:48:18 +0200 Subject: [PATCH 05/35] Ported to 1.17 --- .../constructionwand/ConstructionWand.java | 6 +- .../api/IContainerHandler.java | 10 +- .../constructionwand/api/IWandAction.java | 12 +- .../constructionwand/api/IWandSupplier.java | 14 +- .../constructionwand/api/IWandUpgrade.java | 2 +- .../constructionwand/basics/CommonEvents.java | 6 +- .../constructionwand/basics/ConfigServer.java | 12 +- .../constructionwand/basics/ModStats.java | 8 +- .../basics/ReplacementRegistry.java | 11 +- .../constructionwand/basics/WandUtil.java | 149 ++++++++---------- .../basics/option/OptionBoolean.java | 8 +- .../basics/option/OptionEnum.java | 8 +- .../basics/option/WandOptions.java | 12 +- .../basics/option/WandUpgrades.java | 20 +-- .../basics/option/WandUpgradesSelectable.java | 4 +- .../constructionwand/client/ClientEvents.java | 26 +-- .../client/RenderBlockPreview.java | 63 ++++---- .../constructionwand/client/RenderTypes.java | 37 ----- .../constructionwand/client/ScreenWand.java | 37 ++--- .../containers/ContainerManager.java | 8 +- .../containers/ContainerRegistrar.java | 6 +- .../containers/handlers/HandlerBotania.java | 23 +-- .../handlers/HandlerCapability.java | 27 ++-- .../handlers/HandlerShulkerbox.java | 36 ++--- .../crafting/RecipeWandUpgrade.java | 34 ++-- .../thetadev/constructionwand/data/Inp.java | 18 +-- .../data/ItemModelGenerator.java | 4 +- .../constructionwand/data/ModData.java | 2 +- .../data/RecipeGenerator.java | 64 ++++---- .../constructionwand/items/ItemBase.java | 2 +- .../constructionwand/items/ModItems.java | 30 ++-- .../items/core/CoreDefault.java | 2 +- .../constructionwand/items/core/ItemCore.java | 22 +-- .../constructionwand/items/wand/ItemWand.java | 106 +++++++------ .../items/wand/ItemWandBasic.java | 16 +- .../items/wand/ItemWandInfinity.java | 2 +- .../network/PacketQueryUndo.java | 12 +- .../network/PacketUndoBlocks.java | 10 +- .../network/PacketWandOption.java | 22 +-- .../wand/WandItemUseContext.java | 32 ++-- .../constructionwand/wand/WandJob.java | 41 ++--- .../wand/action/ActionAngel.java | 32 ++-- .../wand/action/ActionConstruction.java | 77 +++++---- .../wand/action/ActionDestruction.java | 75 +++++---- .../wand/supplier/SupplierInventory.java | 30 ++-- .../wand/supplier/SupplierRandom.java | 10 +- .../wand/undo/DestroySnapshot.java | 32 ++-- .../constructionwand/wand/undo/ISnapshot.java | 20 +-- .../wand/undo/PlaceSnapshot.java | 60 +++---- .../wand/undo/UndoHistory.java | 58 +++---- src/main/resources/META-INF/mods.toml | 16 +- 51 files changed, 660 insertions(+), 714 deletions(-) delete mode 100644 src/main/java/thetadev/constructionwand/client/RenderTypes.java diff --git a/src/main/java/thetadev/constructionwand/ConstructionWand.java b/src/main/java/thetadev/constructionwand/ConstructionWand.java index 7f811d7..bcfdeab 100644 --- a/src/main/java/thetadev/constructionwand/ConstructionWand.java +++ b/src/main/java/thetadev/constructionwand/ConstructionWand.java @@ -1,6 +1,6 @@ package thetadev.constructionwand; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; @@ -8,8 +8,8 @@ import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.network.NetworkRegistry; -import net.minecraftforge.fml.network.simple.SimpleChannel; +import net.minecraftforge.fmllegacy.network.NetworkRegistry; +import net.minecraftforge.fmllegacy.network.simple.SimpleChannel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import thetadev.constructionwand.basics.ConfigClient; diff --git a/src/main/java/thetadev/constructionwand/api/IContainerHandler.java b/src/main/java/thetadev/constructionwand/api/IContainerHandler.java index 598d080..befd8d5 100644 --- a/src/main/java/thetadev/constructionwand/api/IContainerHandler.java +++ b/src/main/java/thetadev/constructionwand/api/IContainerHandler.java @@ -1,13 +1,13 @@ package thetadev.constructionwand.api; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; public interface IContainerHandler { - boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack); + boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack); - int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack); + int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack); - int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count); + int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count); } diff --git a/src/main/java/thetadev/constructionwand/api/IWandAction.java b/src/main/java/thetadev/constructionwand/api/IWandAction.java index fc2067d..beac255 100644 --- a/src/main/java/thetadev/constructionwand/api/IWandAction.java +++ b/src/main/java/thetadev/constructionwand/api/IWandAction.java @@ -1,9 +1,9 @@ package thetadev.constructionwand.api; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.wand.undo.ISnapshot; @@ -15,10 +15,10 @@ public interface IWandAction int getLimit(ItemStack wand); @Nonnull - List getSnapshots(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + List getSnapshots(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit); @Nonnull - List getSnapshotsFromAir(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + List getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit); } diff --git a/src/main/java/thetadev/constructionwand/api/IWandSupplier.java b/src/main/java/thetadev/constructionwand/api/IWandSupplier.java index 26370a0..8578ba6 100644 --- a/src/main/java/thetadev/constructionwand/api/IWandSupplier.java +++ b/src/main/java/thetadev/constructionwand/api/IWandSupplier.java @@ -1,11 +1,11 @@ package thetadev.constructionwand.api; -import net.minecraft.block.BlockState; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.wand.undo.PlaceSnapshot; import javax.annotation.Nullable; @@ -20,7 +20,7 @@ public interface IWandSupplier * in that position. */ @Nullable - PlaceSnapshot getPlaceSnapshot(World world, BlockPos pos, BlockRayTraceResult rayTraceResult, + PlaceSnapshot getPlaceSnapshot(Level world, BlockPos pos, BlockHitResult rayTraceResult, @Nullable BlockState supportingBlock); /** diff --git a/src/main/java/thetadev/constructionwand/api/IWandUpgrade.java b/src/main/java/thetadev/constructionwand/api/IWandUpgrade.java index 48b7a8d..7aab489 100644 --- a/src/main/java/thetadev/constructionwand/api/IWandUpgrade.java +++ b/src/main/java/thetadev/constructionwand/api/IWandUpgrade.java @@ -1,6 +1,6 @@ package thetadev.constructionwand.api; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.ResourceLocation; public interface IWandUpgrade { diff --git a/src/main/java/thetadev/constructionwand/basics/CommonEvents.java b/src/main/java/thetadev/constructionwand/basics/CommonEvents.java index e698c27..77a9071 100644 --- a/src/main/java/thetadev/constructionwand/basics/CommonEvents.java +++ b/src/main/java/thetadev/constructionwand/basics/CommonEvents.java @@ -1,6 +1,6 @@ package thetadev.constructionwand.basics; -import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.world.entity.player.Player; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @@ -11,8 +11,8 @@ public class CommonEvents { @SubscribeEvent public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) { - PlayerEntity player = e.getPlayer(); - if(player.getEntityWorld().isRemote) return; + Player player = e.getPlayer(); + if(player.level.isClientSide) return; ConstructionWand.instance.undoHistory.removePlayer(player); } } diff --git a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java index dea4c06..5cbec16 100644 --- a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java +++ b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java @@ -1,7 +1,7 @@ package thetadev.constructionwand.basics; -import net.minecraft.item.Item; -import net.minecraft.item.ItemTier; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Tiers; import net.minecraftforge.common.ForgeConfigSpec; import thetadev.constructionwand.items.ModItems; @@ -20,7 +20,7 @@ public class ConfigServer public static final ForgeConfigSpec.ConfigValue> SIMILAR_BLOCKS; private static final String[] SIMILAR_BLOCKS_DEFAULT = { - "minecraft:dirt;minecraft:grass_block;minecraft:coarse_dirt;minecraft:podzol;minecraft:mycelium;minecraft:farmland;minecraft:grass_path" + "minecraft:dirt;minecraft:grass_block;minecraft:coarse_dirt;minecraft:podzol;minecraft:mycelium;minecraft:farmland;minecraft:dirt_path;minecraft:rooted_dirt" }; public static final ForgeConfigSpec.BooleanValue TE_WHITELIST; @@ -104,9 +104,9 @@ public class ConfigServer "in the /saves/myworld/serverconfig folder. If you want to change the serverconfig for all", "new worlds, copy the config files in the /defaultconfigs folder."); - new WandProperties(BUILDER, ModItems.WAND_STONE, ItemTier.STONE.getMaxUses(), 9, 0, 0, false); - new WandProperties(BUILDER, ModItems.WAND_IRON, ItemTier.IRON.getMaxUses(), 27, 2, 9, true); - new WandProperties(BUILDER, ModItems.WAND_DIAMOND, ItemTier.DIAMOND.getMaxUses(), 128, 8, 25, true); + new WandProperties(BUILDER, ModItems.WAND_STONE, Tiers.STONE.getUses(), 9, 0, 0, false); + new WandProperties(BUILDER, ModItems.WAND_IRON, Tiers.IRON.getUses(), 27, 2, 9, true); + new WandProperties(BUILDER, ModItems.WAND_DIAMOND, Tiers.DIAMOND.getUses(), 128, 8, 25, true); new WandProperties(BUILDER, ModItems.WAND_INFINITY, 0, 1024, 16, 81, true); BUILDER.push("misc"); diff --git a/src/main/java/thetadev/constructionwand/basics/ModStats.java b/src/main/java/thetadev/constructionwand/basics/ModStats.java index c41fbd7..ccfa739 100644 --- a/src/main/java/thetadev/constructionwand/basics/ModStats.java +++ b/src/main/java/thetadev/constructionwand/basics/ModStats.java @@ -1,9 +1,9 @@ package thetadev.constructionwand.basics; -import net.minecraft.stats.IStatFormatter; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.stats.StatFormatter; import net.minecraft.stats.Stats; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; import thetadev.constructionwand.ConstructionWand; public class ModStats @@ -17,6 +17,6 @@ public class ModStats private static void registerStat(ResourceLocation registryName) { // Compare with net.minecraft.stats.Stats#registerCustom Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName); - Stats.CUSTOM.get(registryName, IStatFormatter.DEFAULT); + Stats.CUSTOM.get(registryName, StatFormatter.DEFAULT); } } diff --git a/src/main/java/thetadev/constructionwand/basics/ReplacementRegistry.java b/src/main/java/thetadev/constructionwand/basics/ReplacementRegistry.java index e767913..3677d09 100644 --- a/src/main/java/thetadev/constructionwand/basics/ReplacementRegistry.java +++ b/src/main/java/thetadev/constructionwand/basics/ReplacementRegistry.java @@ -1,8 +1,10 @@ package thetadev.constructionwand.basics; -import net.minecraft.block.Block; -import net.minecraft.item.Item; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraftforge.registries.ForgeRegistries; import thetadev.constructionwand.ConstructionWand; @@ -20,7 +22,7 @@ public class ReplacementRegistry for(String id : ((String) key).split(";")) { Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id)); - if(item == null) { + if(item == null || item == Items.AIR) { ConstructionWand.LOGGER.warn("Replacement Registry: Could not find item " + id); continue; } @@ -42,6 +44,7 @@ public class ReplacementRegistry public static boolean matchBlocks(Block b1, Block b2) { if(b1 == b2) return true; + if(b1 == Blocks.AIR || b2 == Blocks.AIR) return false; for(HashSet set : replacements) { if(set.contains(b1.asItem()) && set.contains(b2.asItem())) return true; diff --git a/src/main/java/thetadev/constructionwand/basics/WandUtil.java b/src/main/java/thetadev/constructionwand/basics/WandUtil.java index 6f8a2fe..6b4db45 100644 --- a/src/main/java/thetadev/constructionwand/basics/WandUtil.java +++ b/src/main/java/thetadev/constructionwand/basics/WandUtil.java @@ -1,46 +1,37 @@ package thetadev.constructionwand.basics; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.entity.Entity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.BlockItemUseContext; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.state.Property; -import net.minecraft.state.properties.BlockStateProperties; -import net.minecraft.state.properties.SlabType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.stats.Stats; -import net.minecraft.util.Direction; -import net.minecraft.util.EntityPredicates; -import net.minecraft.util.Hand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.shapes.VoxelShape; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.world.World; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.event.world.BlockEvent; import thetadev.constructionwand.ConstructionWand; -import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.containers.ContainerManager; import thetadev.constructionwand.items.wand.ItemWand; -import thetadev.constructionwand.wand.WandItemUseContext; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; public class WandUtil { public static boolean stackEquals(ItemStack stackA, ItemStack stackB) { - return ItemStack.areItemsEqual(stackA, stackB) && ItemStack.areItemStackTagsEqual(stackA, stackB); + return ItemStack.isSameItemSameTags(stackA, stackB); } public static boolean stackEquals(ItemStack stackA, Item item) { @@ -48,45 +39,45 @@ public class WandUtil return stackEquals(stackA, stackB); } - public static ItemStack holdingWand(PlayerEntity player) { - if(player.getHeldItem(Hand.MAIN_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.MAIN_HAND).getItem() instanceof ItemWand) { - return player.getHeldItem(Hand.MAIN_HAND); + public static ItemStack holdingWand(Player player) { + if(player.getItemInHand(InteractionHand.MAIN_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.MAIN_HAND).getItem() instanceof ItemWand) { + return player.getItemInHand(InteractionHand.MAIN_HAND); } - else if(player.getHeldItem(Hand.OFF_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.OFF_HAND).getItem() instanceof ItemWand) { - return player.getHeldItem(Hand.OFF_HAND); + else if(player.getItemInHand(InteractionHand.OFF_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.OFF_HAND).getItem() instanceof ItemWand) { + return player.getItemInHand(InteractionHand.OFF_HAND); } return null; } - public static BlockPos playerPos(PlayerEntity player) { - return new BlockPos(player.getPositionVec()); + public static BlockPos playerPos(Player player) { + return new BlockPos(player.position()); } - public static Vector3d entityPositionVec(Entity entity) { - return new Vector3d(entity.getPosX(), entity.getPosY() - entity.getYOffset() + entity.getHeight() / 2, entity.getPosZ()); + public static Vec3 entityPositionVec(Entity entity) { + return new Vec3(entity.getX(), entity.getY() - entity.getMyRidingOffset() + entity.getBbHeight() / 2, entity.getZ()); } - public static Vector3d blockPosVec(BlockPos pos) { - return new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + public static Vec3 blockPosVec(BlockPos pos) { + return new Vec3(pos.getX(), pos.getY(), pos.getZ()); } - public static List getHotbar(PlayerEntity player) { - return player.inventory.mainInventory.subList(0, 9); + public static List getHotbar(Player player) { + return player.getInventory().items.subList(0, 9); } - public static List getHotbarWithOffhand(PlayerEntity player) { - ArrayList inventory = new ArrayList<>(player.inventory.mainInventory.subList(0, 9)); - inventory.addAll(player.inventory.offHandInventory); + public static List getHotbarWithOffhand(Player player) { + ArrayList inventory = new ArrayList<>(player.getInventory().items.subList(0, 9)); + inventory.addAll(player.getInventory().offhand); return inventory; } - public static List getMainInv(PlayerEntity player) { - return player.inventory.mainInventory.subList(9, player.inventory.mainInventory.size()); + public static List getMainInv(Player player) { + return player.getInventory().items.subList(9, player.getInventory().items.size()); } - public static List getFullInv(PlayerEntity player) { - ArrayList inventory = new ArrayList<>(player.inventory.offHandInventory); - inventory.addAll(player.inventory.mainInventory); + public static List getFullInv(Player player) { + ArrayList inventory = new ArrayList<>(player.getInventory().offhand); + inventory.addAll(player.getInventory().items); return inventory; } @@ -95,7 +86,7 @@ public class WandUtil } public static boolean isTEAllowed(BlockState state) { - if(!state.hasTileEntity()) return true; + if(!state.hasBlockEntity()) return true; ResourceLocation name = state.getBlock().getRegistryName(); if(name == null) return false; @@ -109,14 +100,14 @@ public class WandUtil return isWhitelist == inList; } - public static boolean placeBlock(World world, PlayerEntity player, BlockState block, BlockPos pos, @Nullable BlockItem item) { - if(!world.setBlockState(pos, block)) { + public static boolean placeBlock(Level world, Player player, BlockState block, BlockPos pos, @Nullable BlockItem item) { + if(!world.setBlockAndUpdate(pos, block)) { ConstructionWand.LOGGER.info("Block could not be placed"); return false; } // Remove block if placeEvent is canceled - BlockSnapshot snapshot = BlockSnapshot.create(world.func_234923_W_(), world, pos); + BlockSnapshot snapshot = BlockSnapshot.create(world.dimension(), world, pos); BlockEvent.EntityPlaceEvent placeEvent = new BlockEvent.EntityPlaceEvent(snapshot, block, player); MinecraftForge.EVENT_BUS.post(placeEvent); if(placeEvent.isCanceled()) { @@ -128,22 +119,22 @@ public class WandUtil if(item == null) stack = new ItemStack(block.getBlock().asItem()); else { stack = new ItemStack(item); - player.addStat(Stats.ITEM_USED.get(item)); + player.awardStat(Stats.ITEM_USED.get(item)); } // Call OnBlockPlaced method - block.getBlock().onBlockPlacedBy(world, pos, block, player, stack); + block.getBlock().setPlacedBy(world, pos, block, player, stack); return true; } - public static boolean removeBlock(World world, PlayerEntity player, @Nullable BlockState block, BlockPos pos) { + public static boolean removeBlock(Level world, Player player, @Nullable BlockState block, BlockPos pos) { BlockState currentBlock = world.getBlockState(pos); - if(!world.isBlockModifiable(player, pos)) return false; + if(!world.mayInteract(player, pos)) return false; if(!player.isCreative()) { - if(currentBlock.getBlockHardness(world, pos) <= -1 || world.getTileEntity(pos) != null) return false; + if(currentBlock.getDestroySpeed(world, pos) <= -1 || world.getBlockEntity(pos) != null) return false; if(block != null) if(!ReplacementRegistry.matchBlocks(currentBlock.getBlock(), block.getBlock())) return false; @@ -157,8 +148,8 @@ public class WandUtil return true; } - public static int countItem(PlayerEntity player, Item item) { - if(player.inventory == null || player.inventory.mainInventory == null) return 0; + public static int countItem(Player player, Item item) { + if(player.getInventory().items == null) return 0; if(player.isCreative()) return Integer.MAX_VALUE; int total = 0; @@ -180,28 +171,16 @@ public class WandUtil return total; } - public static boolean matchBlocks(WandOptions options, Block b1, Block b2) { - switch(options.match.get()) { - case EXACT: - return b1 == b2; - case SIMILAR: - return ReplacementRegistry.matchBlocks(b1, b2); - case ANY: - return b1 != Blocks.AIR && b2 != Blocks.AIR; - } - return false; - } - - private static boolean isPositionModifiable(World world, PlayerEntity player, BlockPos pos) { + private static boolean isPositionModifiable(Level world, Player player, BlockPos pos) { // Is position out of world? - if(!world.isBlockPresent(pos)) return false; + if(!world.isInWorldBounds(pos)) return false; // Is block modifiable? - if(!world.isBlockModifiable(player, pos)) return false; + if(!world.mayInteract(player, pos)) return false; // Limit range if(ConfigServer.MAX_RANGE.get() > 0 && - WandUtil.blockDistance(player.getPosition(), pos) > ConfigServer.MAX_RANGE.get()) return false; + WandUtil.blockDistance(player.blockPosition(), pos) > ConfigServer.MAX_RANGE.get()) return false; return true; } @@ -210,32 +189,32 @@ public class WandUtil * Tests if a wand can place a block at a certain position. * This check is independent from the used block. */ - public static boolean isPositionPlaceable(World world, PlayerEntity player, BlockPos pos, boolean replace) { - if(!isPositionModifiable(world,player, pos)) return false; + public static boolean isPositionPlaceable(Level world, Player player, BlockPos pos, boolean replace) { + if(!isPositionModifiable(world, player, pos)) return false; // If replace mode is off, target has to be air - return replace || world.isAirBlock(pos); + return replace || world.isEmptyBlock(pos); } - public static boolean isBlockRemovable(World world, PlayerEntity player, BlockPos pos) { - if(!isPositionModifiable(world,player, pos)) return false; + public static boolean isBlockRemovable(Level world, Player player, BlockPos pos) { + if(!isPositionModifiable(world, player, pos)) return false; if(!player.isCreative()) { - return !(world.getBlockState(pos).getBlockHardness(world, pos) <= -1) && world.getTileEntity(pos) == null; + return !(world.getBlockState(pos).getDestroySpeed(world, pos) <= -1) && world.getBlockEntity(pos) == null; } return true; } - public static boolean entitiesCollidingWithBlock(World world, BlockState blockState, BlockPos pos) { + public static boolean entitiesCollidingWithBlock(Level world, BlockState blockState, BlockPos pos) { VoxelShape shape = blockState.getCollisionShape(world, pos); if(!shape.isEmpty()) { - AxisAlignedBB blockBB = shape.getBoundingBox().offset(pos); - return !world.getEntitiesWithinAABB(LivingEntity.class, blockBB, EntityPredicates.NOT_SPECTATING).isEmpty(); + AABB blockBB = shape.bounds().move(pos); + return !world.getEntitiesOfClass(LivingEntity.class, blockBB, Predicate.not(Entity::isSpectator)).isEmpty(); } return false; } - public static Direction fromVector(Vector3d vector) { - return Direction.getFacingFromVector(vector.x, vector.y, vector.z); + public static Direction fromVector(Vec3 vector) { + return Direction.getNearest(vector.x, vector.y, vector.z); } } diff --git a/src/main/java/thetadev/constructionwand/basics/option/OptionBoolean.java b/src/main/java/thetadev/constructionwand/basics/option/OptionBoolean.java index dfb5d00..6823509 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/OptionBoolean.java +++ b/src/main/java/thetadev/constructionwand/basics/option/OptionBoolean.java @@ -1,15 +1,15 @@ package thetadev.constructionwand.basics.option; -import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompoundTag; public class OptionBoolean implements IOption { - private final CompoundNBT tag; + private final CompoundTag tag; private final String key; private final boolean enabled; private boolean value; - public OptionBoolean(CompoundNBT tag, String key, boolean dval, boolean enabled) { + public OptionBoolean(CompoundTag tag, String key, boolean dval, boolean enabled) { this.tag = tag; this.key = key; this.enabled = enabled; @@ -18,7 +18,7 @@ public class OptionBoolean implements IOption else value = dval; } - public OptionBoolean(CompoundNBT tag, String key, boolean dval) { + public OptionBoolean(CompoundTag tag, String key, boolean dval) { this(tag, key, dval, true); } diff --git a/src/main/java/thetadev/constructionwand/basics/option/OptionEnum.java b/src/main/java/thetadev/constructionwand/basics/option/OptionEnum.java index 7042554..5c79dcd 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/OptionEnum.java +++ b/src/main/java/thetadev/constructionwand/basics/option/OptionEnum.java @@ -1,18 +1,18 @@ package thetadev.constructionwand.basics.option; import com.google.common.base.Enums; -import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompoundTag; public class OptionEnum> implements IOption { - private final CompoundNBT tag; + private final CompoundTag tag; private final String key; private final Class enumClass; private final boolean enabled; private final E dval; private E value; - public OptionEnum(CompoundNBT tag, String key, Class enumClass, E dval, boolean enabled) { + public OptionEnum(CompoundTag tag, String key, Class enumClass, E dval, boolean enabled) { this.tag = tag; this.key = key; this.enumClass = enumClass; @@ -22,7 +22,7 @@ public class OptionEnum> implements IOption value = Enums.getIfPresent(enumClass, tag.getString(key).toUpperCase()).or(dval); } - public OptionEnum(CompoundNBT tag, String key, Class enumClass, E dval) { + public OptionEnum(CompoundTag tag, String key, Class enumClass, E dval) { this(tag, key, enumClass, dval, true); } diff --git a/src/main/java/thetadev/constructionwand/basics/option/WandOptions.java b/src/main/java/thetadev/constructionwand/basics/option/WandOptions.java index 0cb920f..2a9b39e 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/WandOptions.java +++ b/src/main/java/thetadev/constructionwand/basics/option/WandOptions.java @@ -1,9 +1,9 @@ package thetadev.constructionwand.basics.option; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import thetadev.constructionwand.api.IWandCore; import thetadev.constructionwand.api.IWandUpgrade; import thetadev.constructionwand.basics.ReplacementRegistry; @@ -13,7 +13,7 @@ import javax.annotation.Nullable; public class WandOptions { - public final CompoundNBT tag; + public final CompoundTag tag; private static final String TAG_ROOT = "wand_options"; @@ -50,7 +50,7 @@ public class WandOptions public final IOption[] allOptions; public WandOptions(ItemStack wandStack) { - tag = wandStack.getOrCreateChildTag(TAG_ROOT); + tag = wandStack.getOrCreateTagElement(TAG_ROOT); cores = new WandUpgradesSelectable<>(tag, "cores", new CoreDefault()); diff --git a/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java b/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java index 19626c2..043907d 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java +++ b/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java @@ -1,10 +1,10 @@ package thetadev.constructionwand.basics.option; -import net.minecraft.item.Item; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; -import net.minecraft.nbt.StringNBT; -import net.minecraft.util.ResourceLocation; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; import net.minecraftforge.common.util.Constants; import net.minecraftforge.registries.ForgeRegistries; import thetadev.constructionwand.ConstructionWand; @@ -14,12 +14,12 @@ import java.util.ArrayList; public class WandUpgrades { - protected final CompoundNBT tag; + protected final CompoundTag tag; protected final String key; protected final ArrayList upgrades; protected final T dval; - public WandUpgrades(CompoundNBT tag, String key, T dval) { + public WandUpgrades(CompoundTag tag, String key, T dval) { this.tag = tag; this.key = key; this.dval = dval; @@ -31,7 +31,7 @@ public class WandUpgrades } protected void deserialize() { - ListNBT listnbt = tag.getList(key, Constants.NBT.TAG_STRING); + ListTag listnbt = tag.getList(key, Constants.NBT.TAG_STRING); boolean require_fix = false; for(int i = 0; i < listnbt.size(); i++) { @@ -52,11 +52,11 @@ public class WandUpgrades } protected void serialize() { - ListNBT listnbt = new ListNBT(); + ListTag listnbt = new ListTag(); for(T item : upgrades) { if(item == dval) continue; - listnbt.add(StringNBT.valueOf(item.getRegistryName().toString())); + listnbt.add(StringTag.valueOf(item.getRegistryName().toString())); } tag.put(key, listnbt); } diff --git a/src/main/java/thetadev/constructionwand/basics/option/WandUpgradesSelectable.java b/src/main/java/thetadev/constructionwand/basics/option/WandUpgradesSelectable.java index 8d80eda..3fec9a6 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/WandUpgradesSelectable.java +++ b/src/main/java/thetadev/constructionwand/basics/option/WandUpgradesSelectable.java @@ -1,13 +1,13 @@ package thetadev.constructionwand.basics.option; -import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompoundTag; import thetadev.constructionwand.api.IWandUpgrade; public class WandUpgradesSelectable extends WandUpgrades implements IOption { private byte selector; - public WandUpgradesSelectable(CompoundNBT tag, String key, T dval) { + public WandUpgradesSelectable(CompoundTag tag, String key, T dval) { super(tag, key, dval); } diff --git a/src/main/java/thetadev/constructionwand/client/ClientEvents.java b/src/main/java/thetadev/constructionwand/client/ClientEvents.java index 954ace1..8cc184f 100644 --- a/src/main/java/thetadev/constructionwand/client/ClientEvents.java +++ b/src/main/java/thetadev/constructionwand/client/ClientEvents.java @@ -1,9 +1,9 @@ package thetadev.constructionwand.client; +import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.Minecraft; -import net.minecraft.client.util.InputMappings; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.EventPriority; @@ -27,7 +27,7 @@ public class ClientEvents // Send state of OPT key to server @SubscribeEvent public void KeyEvent(InputEvent.KeyInputEvent event) { - PlayerEntity player = Minecraft.getInstance().player; + Player player = Minecraft.getInstance().player; if(player == null) return; if(WandUtil.holdingWand(player) == null) return; @@ -43,7 +43,7 @@ public class ClientEvents // Sneak+(OPT)+Scroll to change direction lock @SubscribeEvent(priority = EventPriority.HIGHEST) public void MouseScrollEvent(InputEvent.MouseScrollEvent event) { - PlayerEntity player = Minecraft.getInstance().player; + Player player = Minecraft.getInstance().player; double scroll = event.getScrollDelta(); if(player == null || !modeKeyCombDown(player) || scroll == 0) return; @@ -60,7 +60,7 @@ public class ClientEvents // Sneak+(OPT)+Left click wand to change core @SubscribeEvent public void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) { - PlayerEntity player = event.getPlayer(); + Player player = event.getPlayer(); if(player == null || !modeKeyCombDown(player)) return; @@ -75,29 +75,29 @@ public class ClientEvents // Sneak+(OPT)+Right click wand to open GUI @SubscribeEvent public void onRightClickItem(PlayerInteractEvent.RightClickItem event) { - PlayerEntity player = event.getPlayer(); + Player player = event.getPlayer(); if(player == null || !guiKeyCombDown(player)) return; ItemStack wand = event.getItemStack(); if(!(wand.getItem() instanceof ItemWand)) return; - Minecraft.getInstance().displayGuiScreen(new ScreenWand(wand)); + Minecraft.getInstance().setScreen(new ScreenWand(wand)); event.setCanceled(true); } private static boolean isKeyDown(int id) { - return InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), id); + return InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), id); } public static boolean isOptKeyDown() { return isKeyDown(ConfigClient.OPT_KEY.get()); } - public static boolean modeKeyCombDown(PlayerEntity player) { - return player.isSneaking() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_MODE.get()); + public static boolean modeKeyCombDown(Player player) { + return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_MODE.get()); } - public static boolean guiKeyCombDown(PlayerEntity player) { - return player.isSneaking() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_GUI.get()); + public static boolean guiKeyCombDown(Player player) { + return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_GUI.get()); } } diff --git a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java index d381c60..1fe6b07 100644 --- a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java +++ b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java @@ -1,17 +1,18 @@ package thetadev.constructionwand.client; -import com.mojang.blaze3d.matrix.MatrixStack; -import com.mojang.blaze3d.vertex.IVertexBuilder; -import net.minecraft.client.renderer.IRenderTypeBuffer; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.RayTraceResult; -import net.minecraftforge.client.event.DrawHighlightEvent; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraftforge.client.event.DrawSelectionEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.items.wand.ItemWand; @@ -25,22 +26,21 @@ public class RenderBlockPreview public Set undoBlocks; @SubscribeEvent - public void renderBlockHighlight(DrawHighlightEvent event) { - if(event.getTarget().getType() != RayTraceResult.Type.BLOCK) return; + public void renderBlockHighlight(DrawSelectionEvent.HighlightBlock event) { + if(event.getTarget().getType() != HitResult.Type.BLOCK) return; - BlockRayTraceResult rtr = (BlockRayTraceResult) event.getTarget(); - Entity entity = event.getInfo().getRenderViewEntity(); - if(!(entity instanceof PlayerEntity)) return; - PlayerEntity player = (PlayerEntity) entity; + BlockHitResult rtr = event.getTarget(); + Entity entity = event.getInfo().getEntity(); + if(!(entity instanceof Player player)) return; Set blocks; float colorR = 0, colorG = 0, colorB = 0; ItemStack wand = WandUtil.holdingWand(player); if(wand == null) return; - if(!(player.isSneaking() && ClientEvents.isOptKeyDown())) { + if(!(player.isCrouching() && ClientEvents.isOptKeyDown())) { if(wandJob == null || !compareRTR(wandJob.rayTraceResult, rtr) || !(wandJob.wand.equals(wand))) { - wandJob = ItemWand.getWandJob(player, player.getEntityWorld(), rtr, wand); + wandJob = ItemWand.getWandJob(player, player.level, rtr, wand); } blocks = wandJob.getBlockPositions(); } @@ -51,27 +51,24 @@ public class RenderBlockPreview if(blocks == null || blocks.isEmpty()) return; - MatrixStack ms = event.getMatrix(); - IRenderTypeBuffer buffer = event.getBuffers(); - IVertexBuilder lineBuilder = buffer.getBuffer(RenderTypes.TRANSLUCENT_LINES); + PoseStack ms = event.getMatrix(); + MultiBufferSource buffer = event.getBuffers(); + VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES); double partialTicks = event.getPartialTicks(); - double d0 = player.lastTickPosX + (player.getPosX() - player.lastTickPosX) * partialTicks; - double d1 = player.lastTickPosY + player.getEyeHeight() + (player.getPosY() - player.lastTickPosY) * partialTicks; - double d2 = player.lastTickPosZ + (player.getPosZ() - player.lastTickPosZ) * partialTicks; - - ms.push(); + double d0 = player.xOld + (player.getX() - player.xOld) * partialTicks; + double d1 = player.yOld + player.getEyeHeight() + (player.getY() - player.yOld) * partialTicks; + double d2 = player.zOld + (player.getZ() - player.zOld) * partialTicks; for(BlockPos block : blocks) { - AxisAlignedBB aabb = new AxisAlignedBB(block).offset(-d0, -d1, -d2); - WorldRenderer.drawBoundingBox(ms, lineBuilder, aabb, colorR, colorG, colorB, 0.4F); + AABB aabb = new AABB(block).move(-d0, -d1, -d2); + LevelRenderer.renderLineBox(ms, lineBuilder, aabb, colorR, colorG, colorB, 0.4F); } - ms.pop(); event.setCanceled(true); } - private static boolean compareRTR(BlockRayTraceResult rtr1, BlockRayTraceResult rtr2) { - return rtr1.getPos().equals(rtr2.getPos()) && rtr1.getFace().equals(rtr2.getFace()); + private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) { + return rtr1.getBlockPos().equals(rtr2.getBlockPos()) && rtr1.getDirection().equals(rtr2.getDirection()); } } diff --git a/src/main/java/thetadev/constructionwand/client/RenderTypes.java b/src/main/java/thetadev/constructionwand/client/RenderTypes.java deleted file mode 100644 index c901bd3..0000000 --- a/src/main/java/thetadev/constructionwand/client/RenderTypes.java +++ /dev/null @@ -1,37 +0,0 @@ -package thetadev.constructionwand.client; - -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.renderer.RenderState; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import org.lwjgl.opengl.GL11; -import thetadev.constructionwand.ConstructionWand; - -import java.util.OptionalDouble; - -public class RenderTypes -{ - public static final RenderType TRANSLUCENT_LINES; - - protected static final RenderState.TransparencyState TRANSLUCENT_TRANSPARENCY = new RenderState.TransparencyState("translucent_transparency", () -> { - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - }, RenderSystem::disableBlend); - protected static final RenderState.DepthTestState DEPTH_ALWAYS = new RenderState.DepthTestState("always", GL11.GL_ALWAYS); - - static { - RenderType.State translucentNoDepthState = RenderType.State.getBuilder().transparency(TRANSLUCENT_TRANSPARENCY) - .line(new RenderState.LineState(OptionalDouble.of(2))) - .texture(new RenderState.TextureState()) - .depthTest(DEPTH_ALWAYS) - .build(false); - - TRANSLUCENT_LINES = RenderType.makeType( - ConstructionWand.MODID + ":translucent_lines", - DefaultVertexFormats.POSITION_COLOR, - GL11.GL_LINES, - 256, - translucentNoDepthState - ); - } -} diff --git a/src/main/java/thetadev/constructionwand/client/ScreenWand.java b/src/main/java/thetadev/constructionwand/client/ScreenWand.java index b987e49..a746bab 100644 --- a/src/main/java/thetadev/constructionwand/client/ScreenWand.java +++ b/src/main/java/thetadev/constructionwand/client/ScreenWand.java @@ -1,13 +1,12 @@ package thetadev.constructionwand.client; -import com.mojang.blaze3d.matrix.MatrixStack; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.button.Button; -import net.minecraft.item.ItemStack; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TranslationTextComponent; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.item.ItemStack; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.WandOptions; @@ -31,15 +30,13 @@ public class ScreenWand extends Screen private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT + SPACING_HEIGHT) - SPACING_HEIGHT; public ScreenWand(ItemStack wand) { - super(new StringTextComponent("ScreenWand")); + super(new TextComponent("ScreenWand")); this.wand = wand; wandOptions = new WandOptions(wand); } @Override - public void init(@Nonnull Minecraft minecraft, int width, int height) { - super.init(minecraft, width, height); - + protected void init() { createButton(0, 0, wandOptions.cores); createButton(0, 1, wandOptions.lock); createButton(0, 2, wandOptions.direction); @@ -49,22 +46,22 @@ public class ScreenWand extends Screen } @Override - public void render(@Nonnull MatrixStack matrixStack, int mouseX, int mouseY, float partialTicks) { + public void render(@Nonnull PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { renderBackground(matrixStack); - super.render(matrixStack, mouseX, mouseY, partialTicks); drawCenteredString(matrixStack, font, wand.getDisplayName(), width / 2, height / 2 - FIELD_HEIGHT / 2 - SPACING_HEIGHT, 16777215); + super.render(matrixStack, mouseX, mouseY, partialTicks); } @Override public boolean charTyped(char character, int code) { - if(character == 'e') closeScreen(); + if(character == 'e') onClose(); return super.charTyped(character, code); } private void createButton(int cx, int cy, IOption option) { Button button = new Button(getX(cx), getY(cy), BUTTON_WIDTH, BUTTON_HEIGHT, getButtonLabel(option), bt -> clickButton(bt, option), (bt, ms, x, y) -> drawTooltip(ms, x, y, option)); button.active = option.isEnabled(); - addButton(button); + addRenderableWidget(button); } private void clickButton(Button button, IOption option) { @@ -73,9 +70,9 @@ public class ScreenWand extends Screen button.setMessage(getButtonLabel(option)); } - private void drawTooltip(MatrixStack matrixStack, int mouseX, int mouseY, IOption option) { + private void drawTooltip(PoseStack matrixStack, int mouseX, int mouseY, IOption option) { if(isMouseOver(mouseX, mouseY)) { - renderTooltip(matrixStack, new TranslationTextComponent(option.getDescTranslation()), mouseX, mouseY); + renderTooltip(matrixStack, new TranslatableComponent(option.getDescTranslation()), mouseX, mouseY); } } @@ -87,7 +84,7 @@ public class ScreenWand extends Screen return height / 2 - FIELD_HEIGHT / 2 + n * (BUTTON_HEIGHT + SPACING_HEIGHT); } - private ITextComponent getButtonLabel(IOption option) { - return new TranslationTextComponent(option.getKeyTranslation()).append(new TranslationTextComponent(option.getValueTranslation())); + private Component getButtonLabel(IOption option) { + return new TranslatableComponent(option.getKeyTranslation()).append(new TranslatableComponent(option.getValueTranslation())); } } diff --git a/src/main/java/thetadev/constructionwand/containers/ContainerManager.java b/src/main/java/thetadev/constructionwand/containers/ContainerManager.java index f7283af..ab2ee9a 100644 --- a/src/main/java/thetadev/constructionwand/containers/ContainerManager.java +++ b/src/main/java/thetadev/constructionwand/containers/ContainerManager.java @@ -1,7 +1,7 @@ package thetadev.constructionwand.containers; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import thetadev.constructionwand.api.IContainerHandler; import java.util.ArrayList; @@ -18,7 +18,7 @@ public class ContainerManager return handlers.add(handler); } - public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { for(IContainerHandler handler : handlers) { if(handler.matches(player, itemStack, inventoryStack)) { return handler.countItems(player, itemStack, inventoryStack); @@ -27,7 +27,7 @@ public class ContainerManager return 0; } - public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { for(IContainerHandler handler : handlers) { if(handler.matches(player, itemStack, inventoryStack)) { return handler.useItems(player, itemStack, inventoryStack, count); diff --git a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java index 42642f6..6996fd2 100644 --- a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java +++ b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java @@ -1,8 +1,6 @@ package thetadev.constructionwand.containers; -import net.minecraftforge.fml.ModList; import thetadev.constructionwand.ConstructionWand; -import thetadev.constructionwand.containers.handlers.HandlerBotania; import thetadev.constructionwand.containers.handlers.HandlerCapability; import thetadev.constructionwand.containers.handlers.HandlerShulkerbox; @@ -12,9 +10,13 @@ public class ContainerRegistrar ConstructionWand.instance.containerManager.register(new HandlerCapability()); ConstructionWand.instance.containerManager.register(new HandlerShulkerbox()); + /* + TODO: Reenable this when Botania gets ported to 1.17 + if(ModList.get().isLoaded("botania")) { ConstructionWand.instance.containerManager.register(new HandlerBotania()); ConstructionWand.LOGGER.info("Botania integration added"); } + */ } } diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java index 359cb67..2d41e4b 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java @@ -1,35 +1,36 @@ +/* +TODO: Reenable this when Botania gets ported to 1.17 + package thetadev.constructionwand.containers.handlers; -import net.minecraft.block.Block; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import thetadev.constructionwand.api.IContainerHandler; import vazkii.botania.api.item.IBlockProvider; -/** - * Created by james on 28/12/16. - */ public class HandlerBotania implements IContainerHandler { @Override - public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() instanceof IBlockProvider; } @Override - public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); - int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem())); + int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem())); if(provCount == -1) return Integer.MAX_VALUE; return provCount; } @Override - public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); - if(prov.provideBlock(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem()), true)) + if(prov.provideBlock(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem()), true)) return 0; return count; } } +*/ \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java index c5c8b51..a968655 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java @@ -1,11 +1,12 @@ package thetadev.constructionwand.containers.handlers; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import thetadev.constructionwand.api.IContainerHandler; +import thetadev.constructionwand.basics.WandUtil; /** * Created by james on 28/12/16. @@ -13,22 +14,22 @@ import thetadev.constructionwand.api.IContainerHandler; public class HandlerCapability implements IContainerHandler { @Override - public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { return inventoryStack != null && inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).isPresent(); } @Override - public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { LazyOptional itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); if(!itemHandlerLazyOptional.isPresent()) return 0; int total = 0; - IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance()); + IItemHandler itemHandler = itemHandlerLazyOptional.orElse((IItemHandler) CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); for(int i = 0; i < itemHandler.getSlots(); i++) { ItemStack containerStack = itemHandler.getStackInSlot(i); - if(containerStack != null && itemStack.isItemEqual(containerStack)) { + if(WandUtil.stackEquals(itemStack, containerStack)) { total += Math.max(0, containerStack.getCount()); } @@ -38,24 +39,20 @@ public class HandlerCapability implements IContainerHandler } @Override - public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { int toUse = itemStack.getCount(); LazyOptional itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); if(!itemHandlerLazyOptional.isPresent()) return 0; - IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance()); + IItemHandler itemHandler = itemHandlerLazyOptional.orElse((IItemHandler) CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); for(int i = 0; i < itemHandler.getSlots(); i++) { ItemStack handlerStack = itemHandler.getStackInSlot(i); - if(handlerStack != null && handlerStack.isItemEqual(itemStack)) { + if(WandUtil.stackEquals(itemStack, handlerStack)) { ItemStack extracted = itemHandler.extractItem(i, count, false); - if(extracted != null) { - count -= extracted.getCount(); - } - if(count <= 0) { - break; - } + count -= extracted.getCount(); + if(count <= 0) break; } } return count; diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java index 601a9bd..329d6a8 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java @@ -1,12 +1,12 @@ package thetadev.constructionwand.containers.handlers; -import net.minecraft.block.Block; -import net.minecraft.block.ShulkerBoxBlock; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.ItemStackHelper; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.util.NonNullList; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraftforge.common.util.Constants; import thetadev.constructionwand.api.IContainerHandler; import thetadev.constructionwand.basics.WandUtil; @@ -16,12 +16,12 @@ public class HandlerShulkerbox implements IContainerHandler private final int SLOTS = 27; @Override - public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { - return inventoryStack != null && inventoryStack.getCount() == 1 && Block.getBlockFromItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock; + public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { + return inventoryStack != null && inventoryStack.getCount() == 1 && Block.byItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock; } @Override - public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) { + public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { int count = 0; for(ItemStack stack : getItemList(inventoryStack)) { @@ -32,7 +32,7 @@ public class HandlerShulkerbox implements IContainerHandler } @Override - public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { NonNullList itemList = getItemList(inventoryStack); boolean changed = false; @@ -47,7 +47,7 @@ public class HandlerShulkerbox implements IContainerHandler } if(changed) { setItemList(inventoryStack, itemList); - player.inventory.markDirty(); + player.getInventory().setChanged(); } return count; @@ -55,21 +55,21 @@ public class HandlerShulkerbox implements IContainerHandler private NonNullList getItemList(ItemStack itemStack) { NonNullList itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY); - CompoundNBT rootTag = itemStack.getTag(); + CompoundTag rootTag = itemStack.getTag(); if(rootTag != null && rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) { - CompoundNBT entityTag = rootTag.getCompound("BlockEntityTag"); + CompoundTag entityTag = rootTag.getCompound("BlockEntityTag"); if(entityTag.contains("Items", Constants.NBT.TAG_LIST)) { - ItemStackHelper.loadAllItems(entityTag, itemStacks); + ContainerHelper.loadAllItems(entityTag, itemStacks); } } return itemStacks; } private void setItemList(ItemStack itemStack, NonNullList itemStacks) { - CompoundNBT rootTag = itemStack.getOrCreateTag(); + CompoundTag rootTag = itemStack.getOrCreateTag(); if(!rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) { - rootTag.put("BlockEntityTag", new CompoundNBT()); + rootTag.put("BlockEntityTag", new CompoundTag()); } - ItemStackHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks); + ContainerHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks); } } diff --git a/src/main/java/thetadev/constructionwand/crafting/RecipeWandUpgrade.java b/src/main/java/thetadev/constructionwand/crafting/RecipeWandUpgrade.java index 5e15f3e..7fafdaf 100644 --- a/src/main/java/thetadev/constructionwand/crafting/RecipeWandUpgrade.java +++ b/src/main/java/thetadev/constructionwand/crafting/RecipeWandUpgrade.java @@ -1,12 +1,12 @@ package thetadev.constructionwand.crafting; -import net.minecraft.inventory.CraftingInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.IRecipeSerializer; -import net.minecraft.item.crafting.SpecialRecipe; -import net.minecraft.item.crafting.SpecialRecipeSerializer; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CustomRecipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.SimpleRecipeSerializer; +import net.minecraft.world.level.Level; import thetadev.constructionwand.api.IWandUpgrade; import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.option.WandOptions; @@ -14,21 +14,21 @@ import thetadev.constructionwand.items.wand.ItemWand; import javax.annotation.Nonnull; -public class RecipeWandUpgrade extends SpecialRecipe +public class RecipeWandUpgrade extends CustomRecipe { - public static final SpecialRecipeSerializer SERIALIZER = new SpecialRecipeSerializer<>(RecipeWandUpgrade::new); + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>(RecipeWandUpgrade::new); public RecipeWandUpgrade(ResourceLocation resourceLocation) { super(resourceLocation); } @Override - public boolean matches(@Nonnull CraftingInventory inv, @Nonnull World worldIn) { + public boolean matches(@Nonnull CraftingContainer inv, @Nonnull Level worldIn) { ItemStack wand = null; IWandUpgrade upgrade = null; - for(int i = 0; i < inv.getSizeInventory(); i++) { - ItemStack stack = inv.getStackInSlot(i); + for(int i = 0; i < inv.getContainerSize(); i++) { + ItemStack stack = inv.getItem(i); if(!stack.isEmpty()) { if(wand == null && stack.getItem() instanceof ItemWand) wand = stack; else if(upgrade == null && stack.getItem() instanceof IWandUpgrade) @@ -43,12 +43,12 @@ public class RecipeWandUpgrade extends SpecialRecipe @Nonnull @Override - public ItemStack getCraftingResult(@Nonnull CraftingInventory inv) { + public ItemStack assemble(@Nonnull CraftingContainer inv) { ItemStack wand = null; IWandUpgrade upgrade = null; - for(int i = 0; i < inv.getSizeInventory(); i++) { - ItemStack stack = inv.getStackInSlot(i); + for(int i = 0; i < inv.getContainerSize(); i++) { + ItemStack stack = inv.getItem(i); if(!stack.isEmpty()) { if(stack.getItem() instanceof ItemWand) wand = stack; else if(stack.getItem() instanceof IWandUpgrade) upgrade = (IWandUpgrade) stack.getItem(); @@ -63,13 +63,13 @@ public class RecipeWandUpgrade extends SpecialRecipe } @Override - public boolean canFit(int width, int height) { + public boolean canCraftInDimensions(int width, int height) { return width * height >= 2; } @Nonnull @Override - public IRecipeSerializer getSerializer() { + public RecipeSerializer getSerializer() { return SERIALIZER; } } diff --git a/src/main/java/thetadev/constructionwand/data/Inp.java b/src/main/java/thetadev/constructionwand/data/Inp.java index cab5dbf..d85270a 100644 --- a/src/main/java/thetadev/constructionwand/data/Inp.java +++ b/src/main/java/thetadev/constructionwand/data/Inp.java @@ -1,10 +1,10 @@ package thetadev.constructionwand.data; -import net.minecraft.advancements.criterion.ItemPredicate; -import net.minecraft.item.Item; -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.tags.ITag; -import net.minecraft.util.IItemProvider; +import net.minecraft.advancements.critereon.ItemPredicate; +import net.minecraft.tags.Tag; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; public class Inp { @@ -18,11 +18,11 @@ public class Inp this.predicate = predicate; } - public static Inp fromItem(IItemProvider in) { - return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.fromItems(in), ItemPredicate.Builder.create().item(in).build()); + public static Inp fromItem(ItemLike in) { + return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build()); } - public static Inp fromTag(ITag.INamedTag in) { - return new Inp(in.getName().getPath(), Ingredient.fromTag(in), ItemPredicate.Builder.create().tag(in).build()); + public static Inp fromTag(Tag.Named in) { + return new Inp(in.getName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build()); } } \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java b/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java index 507bc4b..b224ac8 100644 --- a/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java +++ b/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java @@ -1,8 +1,8 @@ package thetadev.constructionwand.data; import net.minecraft.data.DataGenerator; -import net.minecraft.item.BlockItem; -import net.minecraft.item.Item; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; import net.minecraftforge.client.model.generators.ItemModelProvider; import net.minecraftforge.common.data.ExistingFileHelper; import thetadev.constructionwand.ConstructionWand; diff --git a/src/main/java/thetadev/constructionwand/data/ModData.java b/src/main/java/thetadev/constructionwand/data/ModData.java index 45ccd4a..e389e41 100644 --- a/src/main/java/thetadev/constructionwand/data/ModData.java +++ b/src/main/java/thetadev/constructionwand/data/ModData.java @@ -4,7 +4,7 @@ import net.minecraft.data.DataGenerator; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; +import net.minecraftforge.forge.event.lifecycle.GatherDataEvent; @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public class ModData diff --git a/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java b/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java index 649fcce..4407958 100644 --- a/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java +++ b/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java @@ -1,13 +1,17 @@ package thetadev.constructionwand.data; -import net.minecraft.data.*; -import net.minecraft.item.Items; -import net.minecraft.item.crafting.SpecialRecipeSerializer; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.data.recipes.ShapedRecipeBuilder; +import net.minecraft.data.recipes.SpecialRecipeBuilder; +import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; -import net.minecraft.util.IItemProvider; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.registry.Registry; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.SimpleRecipeSerializer; +import net.minecraft.world.level.ItemLike; import net.minecraftforge.common.Tags; +import net.minecraftforge.registries.ForgeRegistries; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.crafting.RecipeWandUpgrade; import thetadev.constructionwand.items.ModItems; @@ -22,8 +26,8 @@ public class RecipeGenerator extends RecipeProvider } @Override - protected void registerRecipes(@Nonnull Consumer consumer) { - wandRecipe(consumer, ModItems.WAND_STONE, Inp.fromTag(ItemTags.field_232909_aa_)); //stone_tool_materials + protected void buildCraftingRecipes(@Nonnull Consumer consumer) { + wandRecipe(consumer, ModItems.WAND_STONE, Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS)); wandRecipe(consumer, ModItems.WAND_IRON, Inp.fromTag(Tags.Items.INGOTS_IRON)); wandRecipe(consumer, ModItems.WAND_DIAMOND, Inp.fromTag(Tags.Items.GEMS_DIAMOND)); wandRecipe(consumer, ModItems.WAND_INFINITY, Inp.fromTag(Tags.Items.NETHER_STARS)); @@ -34,32 +38,32 @@ public class RecipeGenerator extends RecipeProvider specialRecipe(consumer, RecipeWandUpgrade.SERIALIZER); } - private void wandRecipe(Consumer consumer, IItemProvider wand, Inp material) { - ShapedRecipeBuilder.shapedRecipe(wand) - .key('X', material.ingredient) - .key('#', Tags.Items.RODS_WOODEN) - .patternLine(" X") - .patternLine(" # ") - .patternLine("# ") - .addCriterion("has_item", hasItem(material.predicate)) - .build(consumer); + private void wandRecipe(Consumer consumer, ItemLike wand, Inp material) { + ShapedRecipeBuilder.shaped(wand) + .define('X', material.ingredient) + .define('#', Tags.Items.RODS_WOODEN) + .pattern(" X") + .pattern(" # ") + .pattern("# ") + .unlockedBy("has_item", inventoryTrigger(material.predicate)) + .save(consumer); } - private void coreRecipe(Consumer consumer, IItemProvider core, Inp item1, Inp item2) { - ShapedRecipeBuilder.shapedRecipe(core) - .key('O', item1.ingredient) - .key('X', item2.ingredient) - .key('#', Tags.Items.GLASS_PANES) - .patternLine(" #X") - .patternLine("#O#") - .patternLine("X# ") - .addCriterion("has_item", hasItem(item1.predicate)) - .build(consumer); + private void coreRecipe(Consumer consumer, ItemLike core, Inp item1, Inp item2) { + ShapedRecipeBuilder.shaped(core) + .define('O', item1.ingredient) + .define('X', item2.ingredient) + .define('#', Tags.Items.GLASS_PANES) + .pattern(" #X") + .pattern("#O#") + .pattern("X# ") + .unlockedBy("has_item", inventoryTrigger(item1.predicate)) + .save(consumer); } - private void specialRecipe(Consumer consumer, SpecialRecipeSerializer serializer) { - ResourceLocation name = Registry.RECIPE_SERIALIZER.getKey(serializer); - CustomRecipeBuilder.customRecipe(serializer).build(consumer, ConstructionWand.loc("dynamic/" + name.getPath()).toString()); + private void specialRecipe(Consumer consumer, SimpleRecipeSerializer serializer) { + ResourceLocation name = ForgeRegistries.RECIPE_SERIALIZERS.getKey(serializer); + SpecialRecipeBuilder.special(serializer).save(consumer, ConstructionWand.loc("dynamic/" + name.getPath()).toString()); } @Nonnull diff --git a/src/main/java/thetadev/constructionwand/items/ItemBase.java b/src/main/java/thetadev/constructionwand/items/ItemBase.java index ef40066..f47d0dc 100644 --- a/src/main/java/thetadev/constructionwand/items/ItemBase.java +++ b/src/main/java/thetadev/constructionwand/items/ItemBase.java @@ -1,6 +1,6 @@ package thetadev.constructionwand.items; -import net.minecraft.item.Item; +import net.minecraft.world.item.Item; import thetadev.constructionwand.ConstructionWand; public class ItemBase extends Item diff --git a/src/main/java/thetadev/constructionwand/items/ModItems.java b/src/main/java/thetadev/constructionwand/items/ModItems.java index 43408dd..10d7916 100644 --- a/src/main/java/thetadev/constructionwand/items/ModItems.java +++ b/src/main/java/thetadev/constructionwand/items/ModItems.java @@ -1,12 +1,12 @@ package thetadev.constructionwand.items; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.color.ItemColors; -import net.minecraft.item.Item; -import net.minecraft.item.ItemGroup; -import net.minecraft.item.ItemModelsProperties; -import net.minecraft.item.ItemTier; -import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.client.color.item.ItemColors; +import net.minecraft.client.renderer.item.ItemProperties; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Tiers; +import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.RegistryEvent; @@ -30,9 +30,9 @@ import java.util.HashSet; public class ModItems { // Wands - public static final Item WAND_STONE = new ItemWandBasic("stone_wand", propWand(), ItemTier.STONE); - public static final Item WAND_IRON = new ItemWandBasic("iron_wand", propWand(), ItemTier.IRON); - public static final Item WAND_DIAMOND = new ItemWandBasic("diamond_wand", propWand(), ItemTier.DIAMOND); + public static final Item WAND_STONE = new ItemWandBasic("stone_wand", propWand(), Tiers.STONE); + public static final Item WAND_IRON = new ItemWandBasic("iron_wand", propWand(), Tiers.IRON); + public static final Item WAND_DIAMOND = new ItemWandBasic("diamond_wand", propWand(), Tiers.DIAMOND); public static final Item WAND_INFINITY = new ItemWandInfinity("infinity_wand", propWand()); // Cores @@ -56,11 +56,11 @@ public class ModItems } public static Item.Properties propWand() { - return new Item.Properties().group(ItemGroup.TOOLS); + return new Item.Properties().tab(CreativeModeTab.TAB_TOOLS); } private static Item.Properties propUpgrade() { - return new Item.Properties().group(ItemGroup.MISC).maxStackSize(1); + return new Item.Properties().tab(CreativeModeTab.TAB_MISC).stacksTo(1); } private static void registerItem(IForgeRegistry reg, Item item) { @@ -69,17 +69,17 @@ public class ModItems } @SubscribeEvent - public static void registerRecipeSerializers(RegistryEvent.Register> event) { - IForgeRegistry> r = event.getRegistry(); + public static void registerRecipeSerializers(RegistryEvent.Register> event) { + IForgeRegistry> r = event.getRegistry(); register(r, "wand_upgrade", RecipeWandUpgrade.SERIALIZER); } @OnlyIn(Dist.CLIENT) public static void registerModelProperties() { for(Item item : WANDS) { - ItemModelsProperties.func_239418_a_( + ItemProperties.register( item, ConstructionWand.loc("using_core"), - (stack, world, entity) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 : + (stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 : new WandOptions(stack).cores.get().getColor() > -1 ? 1 : 0 ); } diff --git a/src/main/java/thetadev/constructionwand/items/core/CoreDefault.java b/src/main/java/thetadev/constructionwand/items/core/CoreDefault.java index 4053eee..02924af 100644 --- a/src/main/java/thetadev/constructionwand/items/core/CoreDefault.java +++ b/src/main/java/thetadev/constructionwand/items/core/CoreDefault.java @@ -1,6 +1,6 @@ package thetadev.constructionwand.items.core; -import net.minecraft.util.ResourceLocation; +import net.minecraft.resources.ResourceLocation; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandCore; diff --git a/src/main/java/thetadev/constructionwand/items/core/ItemCore.java b/src/main/java/thetadev/constructionwand/items/core/ItemCore.java index 4df952d..fb3b93b 100644 --- a/src/main/java/thetadev/constructionwand/items/core/ItemCore.java +++ b/src/main/java/thetadev/constructionwand/items/core/ItemCore.java @@ -1,11 +1,11 @@ package thetadev.constructionwand.items.core; -import net.minecraft.client.util.ITooltipFlag; -import net.minecraft.item.ItemStack; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextFormatting; -import net.minecraft.util.text.TranslationTextComponent; -import net.minecraft.world.World; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import thetadev.constructionwand.ConstructionWand; @@ -22,10 +22,10 @@ public abstract class ItemCore extends ItemBase implements IWandCore } @OnlyIn(Dist.CLIENT) - public void addInformation(@Nonnull ItemStack itemstack, World worldIn, @Nonnull List lines, @Nonnull ITooltipFlag extraInfo) { - lines.add(new TranslationTextComponent(ConstructionWand.MODID + ".option.cores." + getRegistryName().toString() + ".desc") - .mergeStyle(TextFormatting.GRAY)); - lines.add(new TranslationTextComponent(ConstructionWand.MODID + ".tooltip.core_tip") - .mergeStyle(TextFormatting.AQUA)); + public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List lines, @Nonnull TooltipFlag extraInfo) { + lines.add(new TranslatableComponent(ConstructionWand.MODID + ".option.cores." + getRegistryName().toString() + ".desc") + .withStyle(ChatFormatting.GRAY)); + lines.add(new TranslatableComponent(ConstructionWand.MODID + ".tooltip.core_tip") + .withStyle(ChatFormatting.AQUA)); } } diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java index 6cc75a9..539c96d 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java @@ -1,20 +1,20 @@ package thetadev.constructionwand.items.wand; -import net.minecraft.block.BlockState; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.util.ITooltipFlag; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUseContext; -import net.minecraft.util.ActionResult; -import net.minecraft.util.ActionResultType; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.StringTextComponent; -import net.minecraft.util.text.TextFormatting; -import net.minecraft.util.text.TranslationTextComponent; -import net.minecraft.world.World; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.model.generators.ModelFile; @@ -40,41 +40,41 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel @Nonnull @Override - public ActionResultType onItemUse(ItemUseContext context) { - PlayerEntity player = context.getPlayer(); - Hand hand = context.getHand(); - World world = context.getWorld(); + public InteractionResult useOn(UseOnContext context) { + Player player = context.getPlayer(); + InteractionHand hand = context.getHand(); + Level world = context.getLevel(); - if(world.isRemote || player == null) return ActionResultType.FAIL; + if(world.isClientSide || player == null) return InteractionResult.FAIL; - ItemStack stack = player.getHeldItem(hand); + ItemStack stack = player.getItemInHand(hand); - if(player.isSneaking() && ConstructionWand.instance.undoHistory.isUndoActive(player)) { - return ConstructionWand.instance.undoHistory.undo(player, world, context.getPos()) ? ActionResultType.SUCCESS : ActionResultType.FAIL; + if(player.isCrouching() && ConstructionWand.instance.undoHistory.isUndoActive(player)) { + return ConstructionWand.instance.undoHistory.undo(player, world, context.getClickedPos()) ? InteractionResult.SUCCESS : InteractionResult.FAIL; } else { - WandJob job = getWandJob(player, world, new BlockRayTraceResult(context.getHitVec(), context.getFace(), context.getPos(), false), stack); - return job.doIt() ? ActionResultType.SUCCESS : ActionResultType.FAIL; + WandJob job = getWandJob(player, world, new BlockHitResult(context.getClickLocation(), context.getClickedFace(), context.getClickedPos(), false), stack); + return job.doIt() ? InteractionResult.SUCCESS : InteractionResult.FAIL; } } @Nonnull @Override - public ActionResult onItemRightClick(@Nonnull World world, PlayerEntity player, @Nonnull Hand hand) { - ItemStack stack = player.getHeldItem(hand); + public InteractionResultHolder use(@Nonnull Level world, Player player, @Nonnull InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); - if(!player.isSneaking()) { - if(world.isRemote) return ActionResult.resultFail(stack); + if(!player.isCrouching()) { + if(world.isClientSide) return InteractionResultHolder.fail(stack); // Right click: Place angel block - WandJob job = getWandJob(player, world, BlockRayTraceResult.createMiss(player.getLookVec(), - WandUtil.fromVector(player.getLookVec()), WandUtil.playerPos(player)), stack); - return job.doIt() ? ActionResult.resultSuccess(stack) : ActionResult.resultFail(stack); + WandJob job = getWandJob(player, world, BlockHitResult.miss(player.getLookAngle(), + WandUtil.fromVector(player.getLookAngle()), WandUtil.playerPos(player)), stack); + return job.doIt() ? InteractionResultHolder.success(stack) : InteractionResultHolder.fail(stack); } - return ActionResult.resultFail(stack); + return InteractionResultHolder.fail(stack); } - public static WandJob getWandJob(PlayerEntity player, World world, @Nullable BlockRayTraceResult rayTraceResult, ItemStack wand) { + public static WandJob getWandJob(Player player, Level world, @Nullable BlockHitResult rayTraceResult, ItemStack wand) { WandJob wandJob = new WandJob(player, world, rayTraceResult, wand); wandJob.getSnapshots(); @@ -82,12 +82,12 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel } @Override - public boolean canHarvestBlock(@Nonnull BlockState blockIn) { + public boolean isCorrectToolForDrops(@Nonnull BlockState blockIn) { return false; } @Override - public boolean getIsRepairable(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) { + public boolean isValidRepairItem(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) { return false; } @@ -95,8 +95,9 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel return Integer.MAX_VALUE; } + @Override @OnlyIn(Dist.CLIENT) - public void addInformation(@Nonnull ItemStack itemstack, World worldIn, @Nonnull List lines, @Nonnull ITooltipFlag extraInfo) { + public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List lines, @Nonnull TooltipFlag extraInfo) { WandOptions options = new WandOptions(itemstack); int limit = options.cores.get().getWandAction().getLimit(itemstack); @@ -106,35 +107,36 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel if(Screen.hasShiftDown()) { for(int i = 1; i < options.allOptions.length; i++) { IOption opt = options.allOptions[i]; - lines.add(new TranslationTextComponent(opt.getKeyTranslation()).mergeStyle(TextFormatting.AQUA) - .append(new TranslationTextComponent(opt.getValueTranslation()).mergeStyle(TextFormatting.GRAY)) + lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA) + .append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.GRAY)) ); } if(!options.cores.getUpgrades().isEmpty()) { - lines.add(new StringTextComponent("")); - lines.add(new TranslationTextComponent(langTooltip + "cores").mergeStyle(TextFormatting.GRAY)); + lines.add(new TextComponent("")); + lines.add(new TranslatableComponent(langTooltip + "cores").withStyle(ChatFormatting.GRAY)); for(IWandCore core : options.cores.getUpgrades()) { - lines.add(new TranslationTextComponent(options.cores.getKeyTranslation() + "." + core.getRegistryName().toString())); + lines.add(new TranslatableComponent(options.cores.getKeyTranslation() + "." + core.getRegistryName().toString())); } } } // Default tooltip: show block limit + active wand core else { IOption opt = options.allOptions[0]; - lines.add(new TranslationTextComponent(langTooltip + "blocks", limit).mergeStyle(TextFormatting.GRAY)); - lines.add(new TranslationTextComponent(opt.getKeyTranslation()).mergeStyle(TextFormatting.AQUA) - .append(new TranslationTextComponent(opt.getValueTranslation()).mergeStyle(TextFormatting.WHITE))); - lines.add(new TranslationTextComponent(langTooltip + "shift").mergeStyle(TextFormatting.AQUA)); + lines.add(new TranslatableComponent(langTooltip + "blocks", limit).withStyle(ChatFormatting.GRAY)); + lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA) + .append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.WHITE))); + lines.add(new TranslatableComponent(langTooltip + "shift").withStyle(ChatFormatting.AQUA)); } } - public static void optionMessage(PlayerEntity player, IOption option) { - player.sendStatusMessage( - new TranslationTextComponent(option.getKeyTranslation()).mergeStyle(TextFormatting.AQUA) - .append(new TranslationTextComponent(option.getValueTranslation()).mergeStyle(TextFormatting.WHITE)) - .append(new StringTextComponent(" - ").mergeStyle(TextFormatting.GRAY)) - .append(new TranslationTextComponent(option.getDescTranslation()).mergeStyle(TextFormatting.WHITE)) + @OnlyIn(Dist.CLIENT) + public static void optionMessage(Player player, IOption option) { + player.displayClientMessage( + new TranslatableComponent(option.getKeyTranslation()).withStyle(ChatFormatting.AQUA) + .append(new TranslatableComponent(option.getValueTranslation()).withStyle(ChatFormatting.WHITE)) + .append(new TextComponent(" - ").withStyle(ChatFormatting.GRAY)) + .append(new TranslatableComponent(option.getDescTranslation()).withStyle(ChatFormatting.WHITE)) , true); } diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java index 66b66a8..518954b 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java @@ -1,17 +1,17 @@ package thetadev.constructionwand.items.wand; -import net.minecraft.item.IItemTier; -import net.minecraft.item.ItemStack; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Tier; import thetadev.constructionwand.basics.ConfigServer; import javax.annotation.Nonnull; public class ItemWandBasic extends ItemWand { - private final IItemTier tier; + private final Tier tier; - public ItemWandBasic(String name, Properties properties, IItemTier tier) { - super(name, properties.maxDamage(tier.getMaxUses())); + public ItemWandBasic(String name, Properties properties, Tier tier) { + super(name, properties.durability(tier.getUses())); this.tier = tier; } @@ -22,11 +22,11 @@ public class ItemWandBasic extends ItemWand @Override public int remainingDurability(ItemStack stack) { - return stack.getMaxDamage() - stack.getDamage(); + return stack.getMaxDamage() - stack.getDamageValue(); } @Override - public boolean getIsRepairable(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) { - return this.tier.getRepairMaterial().test(repair); + public boolean isValidRepairItem(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) { + return this.tier.getRepairIngredient().test(repair); } } diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java index f53cb39..8907001 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java @@ -4,6 +4,6 @@ package thetadev.constructionwand.items.wand; public class ItemWandInfinity extends ItemWand { public ItemWandInfinity(String name, Properties properties) { - super(name, properties.maxStackSize(1).isBurnable()); + super(name, properties.stacksTo(1).fireResistant()); } } diff --git a/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java b/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java index 0c17d5b..488b89e 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java +++ b/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java @@ -1,8 +1,8 @@ package thetadev.constructionwand.network; -import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.network.PacketBuffer; -import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.fmllegacy.network.NetworkEvent; import thetadev.constructionwand.ConstructionWand; import java.util.function.Supplier; @@ -15,11 +15,11 @@ public class PacketQueryUndo this.undoPressed = undoPressed; } - public static void encode(PacketQueryUndo msg, PacketBuffer buffer) { + public static void encode(PacketQueryUndo msg, FriendlyByteBuf buffer) { buffer.writeBoolean(msg.undoPressed); } - public static PacketQueryUndo decode(PacketBuffer buffer) { + public static PacketQueryUndo decode(FriendlyByteBuf buffer) { return new PacketQueryUndo(buffer.readBoolean()); } @@ -28,7 +28,7 @@ public class PacketQueryUndo public static void handle(final PacketQueryUndo msg, final Supplier ctx) { if(!ctx.get().getDirection().getReceptionSide().isServer()) return; - ServerPlayerEntity player = ctx.get().getSender(); + ServerPlayer player = ctx.get().getSender(); if(player == null) return; ConstructionWand.instance.undoHistory.updateClient(player, msg.undoPressed); diff --git a/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java b/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java index 7d831e9..b6ca550 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java +++ b/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java @@ -1,8 +1,8 @@ package thetadev.constructionwand.network; -import net.minecraft.network.PacketBuffer; -import net.minecraft.util.math.BlockPos; -import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.fmllegacy.network.NetworkEvent; import thetadev.constructionwand.ConstructionWand; import java.util.HashSet; @@ -21,13 +21,13 @@ public class PacketUndoBlocks this.undoBlocks = undoBlocks; } - public static void encode(PacketUndoBlocks msg, PacketBuffer buffer) { + public static void encode(PacketUndoBlocks msg, FriendlyByteBuf buffer) { for(BlockPos pos : msg.undoBlocks) { buffer.writeBlockPos(pos); } } - public static PacketUndoBlocks decode(PacketBuffer buffer) { + public static PacketUndoBlocks decode(FriendlyByteBuf buffer) { HashSet undoBlocks = new HashSet<>(); while(buffer.isReadable()) { diff --git a/src/main/java/thetadev/constructionwand/network/PacketWandOption.java b/src/main/java/thetadev/constructionwand/network/PacketWandOption.java index cf20b15..e2bde2e 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketWandOption.java +++ b/src/main/java/thetadev/constructionwand/network/PacketWandOption.java @@ -1,9 +1,9 @@ package thetadev.constructionwand.network; -import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.network.PacketBuffer; -import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fmllegacy.network.NetworkEvent; import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.WandOptions; @@ -27,14 +27,14 @@ public class PacketWandOption this.notify = notify; } - public static void encode(PacketWandOption msg, PacketBuffer buffer) { - buffer.writeString(msg.key); - buffer.writeString(msg.value); + public static void encode(PacketWandOption msg, FriendlyByteBuf buffer) { + buffer.writeUtf(msg.key); + buffer.writeUtf(msg.value); buffer.writeBoolean(msg.notify); } - public static PacketWandOption decode(PacketBuffer buffer) { - return new PacketWandOption(buffer.readString(100), buffer.readString(100), buffer.readBoolean()); + public static PacketWandOption decode(FriendlyByteBuf buffer) { + return new PacketWandOption(buffer.readUtf(100), buffer.readUtf(100), buffer.readBoolean()); } public static class Handler @@ -42,7 +42,7 @@ public class PacketWandOption public static void handle(final PacketWandOption msg, final Supplier ctx) { if(!ctx.get().getDirection().getReceptionSide().isServer()) return; - ServerPlayerEntity player = ctx.get().getSender(); + ServerPlayer player = ctx.get().getSender(); if(player == null) return; ItemStack wand = WandUtil.holdingWand(player); @@ -54,7 +54,7 @@ public class PacketWandOption option.setValueString(msg.value); if(msg.notify) ItemWand.optionMessage(player, option); - player.inventory.markDirty(); + player.getInventory().setChanged(); } } } diff --git a/src/main/java/thetadev/constructionwand/wand/WandItemUseContext.java b/src/main/java/thetadev/constructionwand/wand/WandItemUseContext.java index b8c08f7..b1d5f50 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandItemUseContext.java +++ b/src/main/java/thetadev/constructionwand/wand/WandItemUseContext.java @@ -1,27 +1,27 @@ package thetadev.constructionwand.wand; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.BlockItemUseContext; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; import thetadev.constructionwand.basics.WandUtil; -public class WandItemUseContext extends BlockItemUseContext +public class WandItemUseContext extends BlockPlaceContext { - public WandItemUseContext(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, BlockPos pos, BlockItem item) { - super(world, player, Hand.MAIN_HAND, new ItemStack(item), - new BlockRayTraceResult(getBlockHitVec(rayTraceResult, pos), rayTraceResult.getFace(), pos, false)); + public WandItemUseContext(Level world, Player player, BlockHitResult rayTraceResult, BlockPos pos, BlockItem item) { + super(world, player, InteractionHand.MAIN_HAND, new ItemStack(item), + new BlockHitResult(getBlockHitVec(rayTraceResult, pos), rayTraceResult.getDirection(), pos, false)); } - private static Vector3d getBlockHitVec(BlockRayTraceResult rayTraceResult, BlockPos pos) { - Vector3d hitVec = rayTraceResult.getHitVec(); // Absolute coords of hit target + private static Vec3 getBlockHitVec(BlockHitResult rayTraceResult, BlockPos pos) { + Vec3 hitVec = rayTraceResult.getLocation(); // Absolute coords of hit target - Vector3d blockDelta = WandUtil.blockPosVec(rayTraceResult.getPos()).subtract(WandUtil.blockPosVec(pos)); // Vector between start and current block + Vec3 blockDelta = WandUtil.blockPosVec(rayTraceResult.getBlockPos()).subtract(WandUtil.blockPosVec(pos)); // Vector between start and current block return blockDelta.add(hitVec); // Absolute coords of current block hit target } diff --git a/src/main/java/thetadev/constructionwand/wand/WandJob.java b/src/main/java/thetadev/constructionwand/wand/WandJob.java index edcb566..52cdbf3 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandJob.java +++ b/src/main/java/thetadev/constructionwand/wand/WandJob.java @@ -1,15 +1,16 @@ package thetadev.constructionwand.wand; -import net.minecraft.block.SoundType; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandSupplier; @@ -31,9 +32,9 @@ import java.util.stream.Collectors; public class WandJob { - public final PlayerEntity player; - public final World world; - public final BlockRayTraceResult rayTraceResult; + public final Player player; + public final Level world; + public final BlockHitResult rayTraceResult; public final WandOptions options; public final ItemStack wand; public final ItemWand wandItem; @@ -43,7 +44,7 @@ public class WandJob private List placeSnapshots; - public WandJob(PlayerEntity player, World world, BlockRayTraceResult rayTraceResult, ItemStack wand) { + public WandJob(Player player, Level world, BlockHitResult rayTraceResult, ItemStack wand) { this.player = player; this.world = world; this.rayTraceResult = rayTraceResult; @@ -63,9 +64,9 @@ public class WandJob } @Nullable - private static BlockItem getTargetItem(World world, BlockRayTraceResult rayTraceResult) { + private static BlockItem getTargetItem(Level world, BlockHitResult rayTraceResult) { // Get target item - Item tgitem = world.getBlockState(rayTraceResult.getPos()).getBlock().asItem(); + Item tgitem = world.getBlockState(rayTraceResult.getBlockPos()).getBlock().asItem(); if(!(tgitem instanceof BlockItem)) return null; return (BlockItem) tgitem; } @@ -76,7 +77,7 @@ public class WandJob if(player.isCreative() && wandItem == ModItems.WAND_INFINITY) limit = ConfigServer.LIMIT_CREATIVE.get(); else limit = Math.min(wandItem.remainingDurability(wand), wandAction.getLimit(wand)); - if(rayTraceResult.getType() == RayTraceResult.Type.BLOCK) + if(rayTraceResult.getType() == HitResult.Type.BLOCK) placeSnapshots = wandAction.getSnapshots(world, player, rayTraceResult, wand, options, wandSupplier, limit); else placeSnapshots = wandAction.getSnapshotsFromAir(world, player, rayTraceResult, wand, options, wandSupplier, limit); @@ -101,8 +102,8 @@ public class WandJob snapshot.forceRestore(world); } - wand.damageItem(1, player, (e) -> e.sendBreakAnimation(player.swingingHand)); - player.addStat(ModStats.USE_WAND); + wand.hurt(1, player.getRandom(), (ServerPlayer) player); + player.awardStat(ModStats.USE_WAND); } } placeSnapshots = executed; @@ -110,7 +111,7 @@ public class WandJob // Play place sound if(!placeSnapshots.isEmpty()) { SoundType sound = placeSnapshots.get(0).getBlockState().getSoundType(); - world.playSound(null, WandUtil.playerPos(player), sound.getPlaceSound(), SoundCategory.BLOCKS, sound.volume, sound.pitch); + world.playSound(null, WandUtil.playerPos(player), sound.getPlaceSound(), SoundSource.BLOCKS, sound.volume, sound.pitch); // Add to job history for undo ConstructionWand.instance.undoHistory.add(player, world, placeSnapshots); diff --git a/src/main/java/thetadev/constructionwand/wand/action/ActionAngel.java b/src/main/java/thetadev/constructionwand/wand/action/ActionAngel.java index f333d99..36033ab 100644 --- a/src/main/java/thetadev/constructionwand/wand/action/ActionAngel.java +++ b/src/main/java/thetadev/constructionwand/wand/action/ActionAngel.java @@ -1,13 +1,13 @@ package thetadev.constructionwand.wand.action; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.world.World; +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.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.basics.ConfigServer; @@ -29,16 +29,16 @@ public class ActionAngel implements IWandAction @Nonnull @Override - public List getSnapshots(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshots(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { LinkedList placeSnapshots = new LinkedList<>(); - Direction placeDirection = rayTraceResult.getFace(); - BlockPos currentPos = rayTraceResult.getPos(); + Direction placeDirection = rayTraceResult.getDirection(); + BlockPos currentPos = rayTraceResult.getBlockPos(); BlockState supportingBlock = world.getBlockState(currentPos); for(int i = 0; i < limit; i++) { - currentPos = currentPos.offset(placeDirection.getOpposite()); + currentPos = currentPos.offset(placeDirection.getOpposite().getNormal()); PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentPos, rayTraceResult, supportingBlock); if(snapshot != null) { @@ -51,15 +51,15 @@ public class ActionAngel implements IWandAction @Nonnull @Override - public List getSnapshotsFromAir(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { LinkedList placeSnapshots = new LinkedList<>(); if(!player.isCreative() && !ConfigServer.ANGEL_FALLING.get() && player.fallDistance > 10) return placeSnapshots; - Vector3d playerVec = WandUtil.entityPositionVec(player); - Vector3d lookVec = player.getLookVec().mul(2, 2, 2); - Vector3d placeVec = playerVec.add(lookVec); + Vec3 playerVec = WandUtil.entityPositionVec(player); + Vec3 lookVec = player.getLookAngle().multiply(2, 2, 2); + Vec3 placeVec = playerVec.add(lookVec); BlockPos currentPos = new BlockPos(placeVec); diff --git a/src/main/java/thetadev/constructionwand/wand/action/ActionConstruction.java b/src/main/java/thetadev/constructionwand/wand/action/ActionConstruction.java index bbd1cf1..c842ea0 100644 --- a/src/main/java/thetadev/constructionwand/wand/action/ActionConstruction.java +++ b/src/main/java/thetadev/constructionwand/wand/action/ActionConstruction.java @@ -1,16 +1,15 @@ package thetadev.constructionwand.wand.action; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +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.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.basics.ConfigServer; -import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.wand.undo.ISnapshot; import thetadev.constructionwand.wand.undo.PlaceSnapshot; @@ -33,15 +32,15 @@ public class ActionConstruction implements IWandAction @Nonnull @Override - public List getSnapshots(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshots(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { LinkedList placeSnapshots = new LinkedList<>(); LinkedList candidates = new LinkedList<>(); HashSet allCandidates = new HashSet<>(); - Direction placeDirection = rayTraceResult.getFace(); - BlockState targetBlock = world.getBlockState(rayTraceResult.getPos()); - BlockPos startingPoint = rayTraceResult.getPos().offset(placeDirection); + Direction placeDirection = rayTraceResult.getDirection(); + BlockState targetBlock = world.getBlockState(rayTraceResult.getBlockPos()); + BlockPos startingPoint = rayTraceResult.getBlockPos().offset(placeDirection.getNormal()); // Is place direction allowed by lock? if(placeDirection == Direction.UP || placeDirection == Direction.DOWN) { @@ -54,10 +53,10 @@ public class ActionConstruction implements IWandAction while(!candidates.isEmpty() && placeSnapshots.size() < limit) { BlockPos currentCandidate = candidates.removeFirst(); try { - BlockPos supportingPoint = currentCandidate.offset(placeDirection.getOpposite()); + BlockPos supportingPoint = currentCandidate.offset(placeDirection.getOpposite().getNormal()); BlockState candidateSupportingBlock = world.getBlockState(supportingPoint); - if(WandUtil.matchBlocks(options, targetBlock.getBlock(), candidateSupportingBlock.getBlock()) && + if(options.matchBlocks(targetBlock.getBlock(), candidateSupportingBlock.getBlock()) && allCandidates.add(currentCandidate)) { PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentCandidate, rayTraceResult, candidateSupportingBlock); if(snapshot == null) continue; @@ -67,52 +66,52 @@ public class ActionConstruction implements IWandAction case DOWN: case UP: if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) { - candidates.add(currentCandidate.offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal())); } if(options.testLock(WandOptions.LOCK.EASTWEST)) { - candidates.add(currentCandidate.offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.WEST.getNormal())); } if(options.testLock(WandOptions.LOCK.NORTHSOUTH) && options.testLock(WandOptions.LOCK.EASTWEST)) { - candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.WEST)); - candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.WEST.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.WEST.getNormal())); } break; case NORTH: case SOUTH: if(options.testLock(WandOptions.LOCK.HORIZONTAL)) { - candidates.add(currentCandidate.offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.WEST.getNormal())); } if(options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP)); - candidates.add(currentCandidate.offset(Direction.DOWN)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal())); } if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.WEST)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.WEST.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.WEST.getNormal())); } break; case EAST: case WEST: if(options.testLock(WandOptions.LOCK.HORIZONTAL)) { - candidates.add(currentCandidate.offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal())); } if(options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP)); - candidates.add(currentCandidate.offset(Direction.DOWN)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal())); } if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.SOUTH)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.SOUTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.SOUTH.getNormal())); } break; } @@ -127,7 +126,7 @@ public class ActionConstruction implements IWandAction @Nonnull @Override - public List getSnapshotsFromAir(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { return new ArrayList<>(); } diff --git a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java index 72340fd..335d908 100644 --- a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java +++ b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java @@ -1,16 +1,15 @@ package thetadev.constructionwand.wand.action; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +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.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.basics.ConfigServer; -import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.wand.undo.DestroySnapshot; import thetadev.constructionwand.wand.undo.ISnapshot; @@ -30,7 +29,7 @@ public class ActionDestruction implements IWandAction @Nonnull @Override - public List getSnapshots(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshots(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { LinkedList destroySnapshots = new LinkedList<>(); // Current list of block positions to process @@ -39,10 +38,10 @@ public class ActionDestruction implements IWandAction HashSet allCandidates = new HashSet<>(); // Block face the wand was pointed at - Direction breakDirection = rayTraceResult.getFace(); + Direction breakDirection = rayTraceResult.getDirection(); // Block the wand was pointed at - BlockPos startingPoint = rayTraceResult.getPos(); - BlockState targetBlock = world.getBlockState(rayTraceResult.getPos()); + BlockPos startingPoint = rayTraceResult.getBlockPos(); + BlockState targetBlock = world.getBlockState(rayTraceResult.getBlockPos()); // Is break direction allowed by lock? // Tried to break blocks from top/bottom face, so the wand should allow breaking in NS/EW direction @@ -61,7 +60,7 @@ public class ActionDestruction implements IWandAction BlockState candidateBlock = world.getBlockState(currentCandidate); // If target and candidate blocks match and the current candidate has not been processed - if(WandUtil.matchBlocks(options, targetBlock.getBlock(), candidateBlock.getBlock()) && + if(options.matchBlocks(targetBlock.getBlock(), candidateBlock.getBlock()) && allCandidates.add(currentCandidate)) { DestroySnapshot snapshot = DestroySnapshot.get(world, player, currentCandidate); if(snapshot == null) continue; @@ -71,52 +70,52 @@ public class ActionDestruction implements IWandAction case DOWN: case UP: if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) { - candidates.add(currentCandidate.offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal())); } if(options.testLock(WandOptions.LOCK.EASTWEST)) { - candidates.add(currentCandidate.offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.WEST.getNormal())); } if(options.testLock(WandOptions.LOCK.NORTHSOUTH) && options.testLock(WandOptions.LOCK.EASTWEST)) { - candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.WEST)); - candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.WEST.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.WEST.getNormal())); } break; case NORTH: case SOUTH: if(options.testLock(WandOptions.LOCK.HORIZONTAL)) { - candidates.add(currentCandidate.offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.WEST.getNormal())); } if(options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP)); - candidates.add(currentCandidate.offset(Direction.DOWN)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal())); } if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.WEST)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.EAST)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.WEST)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.WEST.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.EAST.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.WEST.getNormal())); } break; case EAST: case WEST: if(options.testLock(WandOptions.LOCK.HORIZONTAL)) { - candidates.add(currentCandidate.offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal())); } if(options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP)); - candidates.add(currentCandidate.offset(Direction.DOWN)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal())); } if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) { - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.SOUTH)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.NORTH)); - candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.SOUTH)); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.SOUTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.NORTH.getNormal())); + candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.SOUTH.getNormal())); } break; } @@ -131,7 +130,7 @@ public class ActionDestruction implements IWandAction @Nonnull @Override - public List getSnapshotsFromAir(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public List getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult, ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) { return new ArrayList<>(); } diff --git a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java index 19105de..4e158ef 100644 --- a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java +++ b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java @@ -1,14 +1,14 @@ package thetadev.constructionwand.wand.supplier; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.basics.ReplacementRegistry; @@ -29,20 +29,20 @@ import java.util.List; */ public class SupplierInventory implements IWandSupplier { - protected final PlayerEntity player; + protected final Player player; protected final WandOptions options; protected HashMap itemCounts; protected IPool itemPool; - public SupplierInventory(PlayerEntity player, WandOptions options) { + public SupplierInventory(Player player, WandOptions options) { this.player = player; this.options = options; } public void getSupply(@Nullable BlockItem target) { itemCounts = new LinkedHashMap<>(); - ItemStack offhandStack = player.getHeldItem(Hand.OFF_HAND); + ItemStack offhandStack = player.getItemInHand(InteractionHand.OFF_HAND); itemPool = new OrderedPool<>(); @@ -73,7 +73,7 @@ public class SupplierInventory implements IWandSupplier @Override @Nullable - public PlaceSnapshot getPlaceSnapshot(World world, BlockPos pos, BlockRayTraceResult rayTraceResult, + public PlaceSnapshot getPlaceSnapshot(Level world, BlockPos pos, BlockHitResult rayTraceResult, @Nullable BlockState supportingBlock) { if(!WandUtil.isPositionPlaceable(world, player, pos, options.replace.get())) return null; itemPool.reset(); @@ -104,7 +104,7 @@ public class SupplierInventory implements IWandSupplier int count = stack.getCount(); Item item = stack.getItem(); - if(player.inventory == null || player.inventory.mainInventory == null) return count; + if(player.getInventory().items == null) return count; if(player.isCreative()) return 0; List hotbar = WandUtil.getHotbarWithOffhand(player); @@ -135,7 +135,7 @@ public class SupplierInventory implements IWandSupplier int toTake = Math.min(count, stack.getCount()); stack.shrink(toTake); count -= toTake; - player.inventory.markDirty(); + player.getInventory().setChanged(); } } return count; diff --git a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierRandom.java b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierRandom.java index 07343a4..8760aa4 100644 --- a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierRandom.java +++ b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierRandom.java @@ -1,8 +1,8 @@ package thetadev.constructionwand.wand.supplier; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemStack; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.pool.RandomPool; @@ -12,7 +12,7 @@ import java.util.LinkedHashMap; public class SupplierRandom extends SupplierInventory { - public SupplierRandom(PlayerEntity player, WandOptions options) { + public SupplierRandom(Player player, WandOptions options) { super(player, options); } @@ -21,7 +21,7 @@ public class SupplierRandom extends SupplierInventory itemCounts = new LinkedHashMap<>(); // Random mode -> add all items from hotbar - itemPool = new RandomPool<>(player.getRNG()); + itemPool = new RandomPool<>(player.getRandom()); for(ItemStack stack : WandUtil.getHotbarWithOffhand(player)) { if(stack.getItem() instanceof BlockItem) addBlockItem((BlockItem) stack.getItem()); diff --git a/src/main/java/thetadev/constructionwand/wand/undo/DestroySnapshot.java b/src/main/java/thetadev/constructionwand/wand/undo/DestroySnapshot.java index af62703..1606784 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/DestroySnapshot.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/DestroySnapshot.java @@ -1,12 +1,12 @@ package thetadev.constructionwand.wand.undo; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.fluid.Fluids; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +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.state.BlockState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.basics.WandUtil; import javax.annotation.Nullable; @@ -22,7 +22,7 @@ public class DestroySnapshot implements ISnapshot } @Nullable - public static DestroySnapshot get(World world, PlayerEntity player, BlockPos pos) { + public static DestroySnapshot get(Level world, Player player, BlockPos pos) { if(!WandUtil.isBlockRemovable(world, player, pos)) return null; return new DestroySnapshot(world.getBlockState(pos), pos); @@ -44,34 +44,34 @@ public class DestroySnapshot implements ISnapshot } @Override - public boolean execute(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult) { + public boolean execute(Level world, Player player, BlockHitResult rayTraceResult) { return WandUtil.removeBlock(world, player, block, pos); } @Override - public boolean canRestore(World world, PlayerEntity player) { + public boolean canRestore(Level world, Player player) { // Is position out of world? - if(!world.isBlockPresent(pos)) return false; + if(!world.isInWorldBounds(pos)) return false; // Is block modifiable? - if(!world.isBlockModifiable(player, pos)) return false; + if(!world.mayInteract(player, pos)) return false; // Ignore blocks and entities when in creative if(player.isCreative()) return true; // Is block empty or fluid? - if(!world.isAirBlock(pos) && !world.getBlockState(pos).isReplaceable(Fluids.EMPTY)) return false; + if(!world.isEmptyBlock(pos) && !world.getBlockState(pos).canBeReplaced(Fluids.EMPTY)) return false; return !WandUtil.entitiesCollidingWithBlock(world, block, pos); } @Override - public boolean restore(World world, PlayerEntity player) { + public boolean restore(Level world, Player player) { return WandUtil.placeBlock(world, player, block, pos, null); } @Override - public void forceRestore(World world) { - world.setBlockState(pos, block); + public void forceRestore(Level world) { + world.setBlockAndUpdate(pos, block); } } diff --git a/src/main/java/thetadev/constructionwand/wand/undo/ISnapshot.java b/src/main/java/thetadev/constructionwand/wand/undo/ISnapshot.java index feec07d..c7ed971 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/ISnapshot.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/ISnapshot.java @@ -1,11 +1,11 @@ package thetadev.constructionwand.wand.undo; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +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.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; public interface ISnapshot { @@ -15,11 +15,11 @@ public interface ISnapshot ItemStack getRequiredItems(); - boolean execute(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult); + boolean execute(Level world, Player player, BlockHitResult rayTraceResult); - boolean canRestore(World world, PlayerEntity player); + boolean canRestore(Level world, Player player); - boolean restore(World world, PlayerEntity player); + boolean restore(Level world, Player player); - void forceRestore(World world); + void forceRestore(Level world); } diff --git a/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java b/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java index 63fae19..ca17038 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java @@ -1,18 +1,18 @@ package thetadev.constructionwand.wand.undo; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.BlockItem; -import net.minecraft.item.BlockItemUseContext; -import net.minecraft.item.ItemStack; -import net.minecraft.state.Property; -import net.minecraft.state.properties.BlockStateProperties; -import net.minecraft.state.properties.SlabType; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.BlockRayTraceResult; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.wand.WandItemUseContext; @@ -35,7 +35,7 @@ public class PlaceSnapshot implements ISnapshot this.targetMode = targetMode; } - public static PlaceSnapshot get(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, + public static PlaceSnapshot get(Level world, Player player, BlockHitResult rayTraceResult, BlockPos pos, BlockItem item, @Nullable BlockState supportingBlock, @Nullable WandOptions options) { boolean targetMode = options != null && supportingBlock != null && options.direction.get() == WandOptions.DIRECTION.TARGET; @@ -61,7 +61,7 @@ public class PlaceSnapshot implements ISnapshot } @Override - public boolean execute(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult) { + public boolean execute(Level world, Player player, BlockHitResult rayTraceResult) { // Recalculate PlaceBlockState, because other blocks might be placed nearby // Not doing this may cause game crashes (StackOverflowException) when placing lots of blocks // with changing orientation like panes, iron bars or redstone. @@ -71,17 +71,17 @@ public class PlaceSnapshot implements ISnapshot } @Override - public boolean canRestore(World world, PlayerEntity player) { + public boolean canRestore(Level world, Player player) { return true; } @Override - public boolean restore(World world, PlayerEntity player) { + public boolean restore(Level world, Player player) { return WandUtil.removeBlock(world, player, block, pos); } @Override - public void forceRestore(World world) { + public void forceRestore(Level world) { world.removeBlock(pos, false); } @@ -91,11 +91,11 @@ public class PlaceSnapshot implements ISnapshot */ @SuppressWarnings({"rawtypes", "unchecked"}) @Nullable - private static BlockState getPlaceBlockstate(World world, PlayerEntity player, BlockRayTraceResult rayTraceResult, - BlockPos pos, BlockItem item, - @Nullable BlockState supportingBlock, boolean targetMode) { + private static BlockState getPlaceBlockstate(Level world, Player player, BlockHitResult rayTraceResult, + BlockPos pos, BlockItem item, + @Nullable BlockState supportingBlock, boolean targetMode) { // Is block at pos replaceable? - BlockItemUseContext ctx = new WandItemUseContext(world, player, rayTraceResult, pos, item); + BlockPlaceContext ctx = new WandItemUseContext(world, player, rayTraceResult, pos, item); if(!ctx.canPlace()) return null; // Can block be placed? @@ -109,25 +109,27 @@ public class PlaceSnapshot implements ISnapshot if(WandUtil.entitiesCollidingWithBlock(world, blockState, pos)) return null; // Adjust blockstate to neighbors - blockState = Block.getValidBlockForPosition(blockState, world, pos); - if(blockState.getBlock() == Blocks.AIR || !blockState.isValidPosition(world, pos)) return null; + // TODO: verify that + blockState = Block.updateFromNeighbourShapes(blockState, world, pos); + if(blockState.getBlock() == Blocks.AIR || !blockState.canSurvive(world, pos)) return null; // Copy block properties from supporting block if(targetMode && supportingBlock != null) { // Block properties to be copied (alignment/rotation properties) for(Property property : new Property[]{ - BlockStateProperties.HORIZONTAL_FACING, BlockStateProperties.FACING, BlockStateProperties.FACING_EXCEPT_UP, - BlockStateProperties.ROTATION_0_15, BlockStateProperties.AXIS, BlockStateProperties.HALF, BlockStateProperties.STAIRS_SHAPE}) { + BlockStateProperties.HORIZONTAL_FACING, BlockStateProperties.FACING, BlockStateProperties.FACING_HOPPER, + BlockStateProperties.ROTATION_16, BlockStateProperties.AXIS, BlockStateProperties.HALF, BlockStateProperties.STAIRS_SHAPE}) { if(supportingBlock.hasProperty(property) && blockState.hasProperty(property)) { - blockState = blockState.with(property, supportingBlock.get(property)); + blockState = blockState.setValue(property, supportingBlock.getValue(property)); } } // Dont dupe double slabs if(supportingBlock.hasProperty(BlockStateProperties.SLAB_TYPE) && blockState.hasProperty(BlockStateProperties.SLAB_TYPE)) { - SlabType slabType = supportingBlock.get(BlockStateProperties.SLAB_TYPE); - if(slabType != SlabType.DOUBLE) blockState = blockState.with(BlockStateProperties.SLAB_TYPE, slabType); + SlabType slabType = supportingBlock.getValue(BlockStateProperties.SLAB_TYPE); + if(slabType != SlabType.DOUBLE) + blockState = blockState.setValue(BlockStateProperties.SLAB_TYPE, slabType); } } return blockState; diff --git a/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java b/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java index edf444c..5be6fdb 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java @@ -1,14 +1,14 @@ package thetadev.constructionwand.wand.undo; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.SoundEvents; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraftforge.fml.network.PacketDistributor; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.fmllegacy.network.PacketDistributor; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.WandUtil; @@ -25,23 +25,23 @@ public class UndoHistory history = new HashMap<>(); } - private PlayerEntry getEntryFromPlayer(PlayerEntity player) { - return history.computeIfAbsent(player.getUniqueID(), k -> new PlayerEntry()); + private PlayerEntry getEntryFromPlayer(Player player) { + return history.computeIfAbsent(player.getUUID(), k -> new PlayerEntry()); } - public void add(PlayerEntity player, World world, List placeSnapshots) { + public void add(Player player, Level world, List placeSnapshots) { LinkedList list = getEntryFromPlayer(player).entries; list.add(new HistoryEntry(placeSnapshots, world)); while(list.size() > ConfigServer.UNDO_HISTORY.get()) list.removeFirst(); } - public void removePlayer(PlayerEntity player) { - history.remove(player.getUniqueID()); + public void removePlayer(Player player) { + history.remove(player.getUUID()); } - public void updateClient(PlayerEntity player, boolean ctrlDown) { - World world = player.getEntityWorld(); - if(world.isRemote) return; + public void updateClient(Player player, boolean ctrlDown) { + Level world = player.level; + if(world.isClientSide) return; // Set state of CTRL key PlayerEntry playerEntry = getEntryFromPlayer(player); @@ -60,14 +60,14 @@ public class UndoHistory } PacketUndoBlocks packet = new PacketUndoBlocks(positions); - ConstructionWand.instance.HANDLER.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), packet); + ConstructionWand.instance.HANDLER.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), packet); } - public boolean isUndoActive(PlayerEntity player) { + public boolean isUndoActive(Player player) { return getEntryFromPlayer(player).undoActive; } - public boolean undo(PlayerEntity player, World world, BlockPos pos) { + public boolean undo(Player player, Level world, BlockPos pos) { // If CTRL key is not pressed, return PlayerEntry playerEntry = getEntryFromPlayer(player); if(!playerEntry.undoActive) return false; @@ -102,9 +102,9 @@ public class UndoHistory private static class HistoryEntry { public final List placeSnapshots; - public final World world; + public final Level world; - public HistoryEntry(List placeSnapshots, World world) { + public HistoryEntry(List placeSnapshots, Level world) { this.placeSnapshots = placeSnapshots; this.world = world; } @@ -119,12 +119,12 @@ public class UndoHistory if(positions.contains(pos)) return true; for(BlockPos p : positions) { - if(pos.withinDistance(p, 3)) return true; + if(pos.closerThan(p, 3)) return true; } return false; } - public boolean undo(PlayerEntity player) { + public boolean undo(Player player) { // Check first if all snapshots can be restored for(ISnapshot snapshot : placeSnapshots) { if(!snapshot.canRestore(world, player)) return false; @@ -133,16 +133,16 @@ public class UndoHistory if(snapshot.restore(world, player) && !player.isCreative()) { ItemStack stack = snapshot.getRequiredItems(); - if(!player.inventory.addItemStackToInventory(stack)) { - player.dropItem(stack, false); + if(!player.getInventory().add(stack)) { + player.drop(stack, false); } } } - player.inventory.markDirty(); + player.getInventory().setChanged(); // Play teleport sound - SoundEvent sound = SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT; - world.playSound(null, WandUtil.playerPos(player), sound, SoundCategory.PLAYERS, 1.0F, 1.0F); + SoundEvent sound = SoundEvents.CHORUS_FRUIT_TELEPORT; + world.playSound(null, WandUtil.playerPos(player), sound, SoundSource.PLAYERS, 1.0F, 1.0F); return true; } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index bcbc6c5..867d8b2 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,6 +1,6 @@ -modLoader="javafml" -loaderVersion="[33,)" -license="MIT License" +modLoader = "javafml" +loaderVersion = "[37,)" +license = "MIT License" [[mods]] modId="constructionwand" version="${file.jarVersion}" @@ -19,13 +19,13 @@ This is my first minecraft mod. May the odds be ever in your favor. ''' [[dependencies.constructionwand]] modId="forge" - mandatory=true - versionRange="[33,)" - ordering="NONE" +mandatory = true +versionRange = "[37,)" +ordering = "NONE" side="BOTH" [[dependencies.constructionwand]] modId="minecraft" - mandatory = true -versionRange = "[1.16.2, 1.17)" +mandatory = true +versionRange = "[1.17.1, 1.18)" ordering = "NONE" side="BOTH" From 45da4ad7a7f16584a377b822aa7761008965da86 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sat, 21 Aug 2021 14:27:35 +0200 Subject: [PATCH 06/35] Add support for bundle Fix crash when opening wand GUI --- .../constructionwand/client/ClientEvents.java | 2 + .../containers/ContainerRegistrar.java | 2 + .../containers/handlers/HandlerBundle.java | 68 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/main/java/thetadev/constructionwand/containers/handlers/HandlerBundle.java diff --git a/src/main/java/thetadev/constructionwand/client/ClientEvents.java b/src/main/java/thetadev/constructionwand/client/ClientEvents.java index 8cc184f..a92bca2 100644 --- a/src/main/java/thetadev/constructionwand/client/ClientEvents.java +++ b/src/main/java/thetadev/constructionwand/client/ClientEvents.java @@ -75,6 +75,8 @@ public class ClientEvents // Sneak+(OPT)+Right click wand to open GUI @SubscribeEvent public void onRightClickItem(PlayerInteractEvent.RightClickItem event) { + if(event.getSide().isServer()) return; + Player player = event.getPlayer(); if(player == null || !guiKeyCombDown(player)) return; diff --git a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java index 6996fd2..4c3b8e6 100644 --- a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java +++ b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java @@ -1,6 +1,7 @@ package thetadev.constructionwand.containers; import thetadev.constructionwand.ConstructionWand; +import thetadev.constructionwand.containers.handlers.HandlerBundle; import thetadev.constructionwand.containers.handlers.HandlerCapability; import thetadev.constructionwand.containers.handlers.HandlerShulkerbox; @@ -9,6 +10,7 @@ public class ContainerRegistrar public static void register() { ConstructionWand.instance.containerManager.register(new HandlerCapability()); ConstructionWand.instance.containerManager.register(new HandlerShulkerbox()); + ConstructionWand.instance.containerManager.register(new HandlerBundle()); /* TODO: Reenable this when Botania gets ported to 1.17 diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBundle.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBundle.java new file mode 100644 index 0000000..27e5989 --- /dev/null +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBundle.java @@ -0,0 +1,68 @@ +package thetadev.constructionwand.containers.handlers; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import thetadev.constructionwand.api.IContainerHandler; +import thetadev.constructionwand.basics.WandUtil; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +public class HandlerBundle implements IContainerHandler +{ + @Override + public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { + return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() == Items.BUNDLE; + } + + @Override + public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { + return getContents(inventoryStack).filter((stack) -> WandUtil.stackEquals(stack, itemStack)) + .map(ItemStack::getCount).reduce(0, Integer::sum); + } + + @Override + public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { + AtomicInteger newCount = new AtomicInteger(count); + + List itemStacks = getContents(inventoryStack).filter((stack -> { + if(WandUtil.stackEquals(stack, itemStack)) { + int toTake = Math.min(newCount.get(), stack.getCount()); + stack.shrink(toTake); + newCount.set(newCount.get() - toTake); + } + return !stack.isEmpty(); + })).toList(); + + setItemList(inventoryStack, itemStacks); + + return newCount.get(); + } + + private Stream getContents(ItemStack bundleStack) { + CompoundTag compoundtag = bundleStack.getTag(); + if(compoundtag == null) { + return Stream.empty(); + } + else { + ListTag listtag = compoundtag.getList("Items", 10); + return listtag.stream().map(CompoundTag.class::cast).map(ItemStack::of); + } + } + + private void setItemList(ItemStack itemStack, List itemStacks) { + CompoundTag rootTag = itemStack.getOrCreateTag(); + ListTag listTag = new ListTag(); + rootTag.put("Items", listTag); + + for(ItemStack stack : itemStacks) { + CompoundTag itemTag = new CompoundTag(); + stack.save(itemTag); + listTag.add(itemTag); + } + } +} From 9b036cdc50f7b19cfaba80b171993f23182b538c Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sat, 21 Aug 2021 15:12:29 +0200 Subject: [PATCH 07/35] Fixed server crash when changing wand direction --- README.md | 8 ++++---- .../thetadev/constructionwand/items/wand/ItemWand.java | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fc58c42..75169d2 100644 --- a/README.md +++ b/README.md @@ -97,11 +97,11 @@ around them. SHIFT+CTRL+Right clickking any of them will undo the operation, giv If you used the Destruction core, it will restore the blocks. ## Additional features -- If you have shulker boxes in your inventory filled with blocks, the wand can pull them out and place them +- If you have shulker boxes (or bundles in MC 1.17+) in your inventory filled with blocks, the wand can pull them out + and place them -- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. - Having a Rod of the Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble - at the cost of Mana. +- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the + Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana. - Having blocks in your offhand will place them instead of the block you're looking at diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java index 539c96d..5e19cc7 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java @@ -130,7 +130,6 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel } } - @OnlyIn(Dist.CLIENT) public static void optionMessage(Player player, IOption option) { player.displayClientMessage( new TranslatableComponent(option.getKeyTranslation()).withStyle(ChatFormatting.AQUA) From 5cad07d34d301efd7a73d2b354f5552245817c96 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 16 Sep 2021 19:56:54 +0200 Subject: [PATCH 08/35] Fixed crash caused by HandlerCapability --- gradle.properties | 2 +- .../handlers/HandlerCapability.java | 22 +++++++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/gradle.properties b/gradle.properties index d9915f8..29491ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,4 @@ mcp_channel=official mcp_mappings=1.17.1 botania=1.16.2-405 version_major=2 -version_minor=2 \ No newline at end of file +version_minor=3 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java index a968655..d67ae0e 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerCapability.java @@ -2,15 +2,13 @@ package thetadev.constructionwand.containers.handlers; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import thetadev.constructionwand.api.IContainerHandler; import thetadev.constructionwand.basics.WandUtil; -/** - * Created by james on 28/12/16. - */ +import java.util.Optional; + public class HandlerCapability implements IContainerHandler { @Override @@ -20,32 +18,28 @@ public class HandlerCapability implements IContainerHandler @Override public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { - LazyOptional itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); - if(!itemHandlerLazyOptional.isPresent()) return 0; + Optional itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve(); + if(itemHandlerOptional.isEmpty()) return 0; int total = 0; - IItemHandler itemHandler = itemHandlerLazyOptional.orElse((IItemHandler) CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); + IItemHandler itemHandler = itemHandlerOptional.get(); for(int i = 0; i < itemHandler.getSlots(); i++) { ItemStack containerStack = itemHandler.getStackInSlot(i); if(WandUtil.stackEquals(itemStack, containerStack)) { total += Math.max(0, containerStack.getCount()); } - - // Already in a container. Don't inception this thing. } return total; } @Override public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { - int toUse = itemStack.getCount(); + Optional itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve(); + if(itemHandlerOptional.isEmpty()) return 0; - LazyOptional itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); - if(!itemHandlerLazyOptional.isPresent()) return 0; - - IItemHandler itemHandler = itemHandlerLazyOptional.orElse((IItemHandler) CapabilityItemHandler.ITEM_HANDLER_CAPABILITY); + IItemHandler itemHandler = itemHandlerOptional.get(); for(int i = 0; i < itemHandler.getSlots(); i++) { ItemStack handlerStack = itemHandler.getStackInSlot(i); From 8fd5a093635dc5bfb850f0e283dd04ed017c8e8d Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Tue, 28 Sep 2021 20:49:31 +0200 Subject: [PATCH 09/35] Update Forge --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 29491ac..137e9cf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.daemon=false author=thetadev modid=constructionwand mcversion=1.17.1 -forgeversion=37.0.19 +forgeversion=37.0.73 mcp_channel=official mcp_mappings=1.17.1 botania=1.16.2-405 From c60260ea5ce7645d5f39a1aad669388149600721 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Fri, 22 Oct 2021 13:35:05 +0200 Subject: [PATCH 10/35] Fix #40 wand replacing half slabs Fix destruction wand removing blocks not facing the player --- gradle.properties | 2 +- .../thetadev/constructionwand/basics/WandUtil.java | 13 +++++++++++-- .../wand/action/ActionDestruction.java | 10 +++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 137e9cf..a893bfc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,4 @@ mcp_channel=official mcp_mappings=1.17.1 botania=1.16.2-405 version_major=2 -version_minor=3 \ No newline at end of file +version_minor=4 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/basics/WandUtil.java b/src/main/java/thetadev/constructionwand/basics/WandUtil.java index 6b4db45..0ce3056 100644 --- a/src/main/java/thetadev/constructionwand/basics/WandUtil.java +++ b/src/main/java/thetadev/constructionwand/basics/WandUtil.java @@ -11,9 +11,11 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.common.MinecraftForge; @@ -22,6 +24,7 @@ import net.minecraftforge.event.world.BlockEvent; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.containers.ContainerManager; import thetadev.constructionwand.items.wand.ItemWand; +import thetadev.constructionwand.wand.WandItemUseContext; import javax.annotation.Nullable; import java.util.ArrayList; @@ -187,13 +190,19 @@ public class WandUtil /** * Tests if a wand can place a block at a certain position. - * This check is independent from the used block. + * This check is independent of the used block. */ public static boolean isPositionPlaceable(Level world, Player player, BlockPos pos, boolean replace) { if(!isPositionModifiable(world, player, pos)) return false; // If replace mode is off, target has to be air - return replace || world.isEmptyBlock(pos); + if(world.isEmptyBlock(pos)) return true; + + // Otherwise, check if the block can be replaced by a generic block + return replace && world.getBlockState(pos).canBeReplaced( + new WandItemUseContext(world, player, + new BlockHitResult(new Vec3(0, 0, 0), Direction.DOWN, pos, false), + pos, (BlockItem) Items.STONE)); } public static boolean isBlockRemovable(Level world, Player player, BlockPos pos) { diff --git a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java index 335d908..182d632 100644 --- a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java +++ b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java @@ -38,14 +38,14 @@ public class ActionDestruction implements IWandAction HashSet allCandidates = new HashSet<>(); // Block face the wand was pointed at - Direction breakDirection = rayTraceResult.getDirection(); + Direction breakFace = rayTraceResult.getDirection(); // Block the wand was pointed at BlockPos startingPoint = rayTraceResult.getBlockPos(); BlockState targetBlock = world.getBlockState(rayTraceResult.getBlockPos()); // Is break direction allowed by lock? // Tried to break blocks from top/bottom face, so the wand should allow breaking in NS/EW direction - if(breakDirection == Direction.UP || breakDirection == Direction.DOWN) { + if(breakFace == Direction.UP || breakFace == Direction.DOWN) { if(options.testLock(WandOptions.LOCK.NORTHSOUTH) || options.testLock(WandOptions.LOCK.EASTWEST)) candidates.add(startingPoint); } @@ -56,6 +56,10 @@ public class ActionDestruction implements IWandAction // Process current candidates, stop when none are avaiable or block limit is reached while(!candidates.isEmpty() && destroySnapshots.size() < limit) { BlockPos currentCandidate = candidates.removeFirst(); + + // Only break blocks facing the player, with no blocks in between + if(!world.isEmptyBlock(currentCandidate.offset(breakFace.getNormal()))) continue; + try { BlockState candidateBlock = world.getBlockState(currentCandidate); @@ -66,7 +70,7 @@ public class ActionDestruction implements IWandAction if(snapshot == null) continue; destroySnapshots.add(snapshot); - switch(breakDirection) { + switch(breakFace) { case DOWN: case UP: if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) { From bb48292c6f31854c2fa337fb9ae6a7b939b8abe5 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Fri, 22 Oct 2021 17:36:35 +0200 Subject: [PATCH 11/35] Fix destruction behind permeable blocks --- src/main/java/thetadev/constructionwand/basics/WandUtil.java | 4 ++++ .../constructionwand/wand/action/ActionDestruction.java | 5 +++-- .../constructionwand/wand/supplier/SupplierInventory.java | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/thetadev/constructionwand/basics/WandUtil.java b/src/main/java/thetadev/constructionwand/basics/WandUtil.java index 0ce3056..418ec5c 100644 --- a/src/main/java/thetadev/constructionwand/basics/WandUtil.java +++ b/src/main/java/thetadev/constructionwand/basics/WandUtil.java @@ -214,6 +214,10 @@ public class WandUtil return true; } + public static boolean isBlockPermeable(Level world, BlockPos pos) { + return world.isEmptyBlock(pos) || world.getBlockState(pos).getCollisionShape(world, pos).isEmpty(); + } + public static boolean entitiesCollidingWithBlock(Level world, BlockState blockState, BlockPos pos) { VoxelShape shape = blockState.getCollisionShape(world, pos); if(!shape.isEmpty()) { diff --git a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java index 182d632..9904c6d 100644 --- a/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java +++ b/src/main/java/thetadev/constructionwand/wand/action/ActionDestruction.java @@ -10,6 +10,7 @@ import net.minecraft.world.phys.BlockHitResult; import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.basics.ConfigServer; +import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.wand.undo.DestroySnapshot; import thetadev.constructionwand.wand.undo.ISnapshot; @@ -57,8 +58,8 @@ public class ActionDestruction implements IWandAction while(!candidates.isEmpty() && destroySnapshots.size() < limit) { BlockPos currentCandidate = candidates.removeFirst(); - // Only break blocks facing the player, with no blocks in between - if(!world.isEmptyBlock(currentCandidate.offset(breakFace.getNormal()))) continue; + // Only break blocks facing the player, with no collidable blocks in between + if(!WandUtil.isBlockPermeable(world, currentCandidate.offset(breakFace.getNormal()))) continue; try { BlockState candidateBlock = world.getBlockState(currentCandidate); diff --git a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java index 4e158ef..8a74155 100644 --- a/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java +++ b/src/main/java/thetadev/constructionwand/wand/supplier/SupplierInventory.java @@ -6,6 +6,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -51,7 +52,7 @@ public class SupplierInventory implements IWandSupplier addBlockItem((BlockItem) offhandStack.getItem()); } // Otherwise use target block - else { + else if(target != null && target != Items.AIR) { addBlockItem(target); // Add replacement items From 5be6d5c788e4fbec2d43a738c101d11641f711f4 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Wed, 8 Dec 2021 09:15:35 +0100 Subject: [PATCH 12/35] Ported to 1.18 --- build.gradle | 3 +-- gradle.properties | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/thetadev/constructionwand/ConstructionWand.java | 4 ++-- .../constructionwand/basics/option/WandUpgrades.java | 4 ++-- .../constructionwand/client/RenderBlockPreview.java | 6 +++--- .../containers/handlers/HandlerShulkerbox.java | 8 ++++---- .../constructionwand/network/PacketQueryUndo.java | 2 +- .../constructionwand/network/PacketUndoBlocks.java | 2 +- .../constructionwand/network/PacketWandOption.java | 2 +- .../thetadev/constructionwand/wand/undo/UndoHistory.java | 2 +- src/main/resources/META-INF/mods.toml | 6 +++--- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 555679a..b548a91 100644 --- a/build.gradle +++ b/build.gradle @@ -16,8 +16,7 @@ version = "${mcversion}-${version_major}.${version_minor}" group = "${author}.${modid}" archivesBaseName = "${modid}" -// Mojang ships Java 16 to end users in 1.17+ instead of Java 8 in 1.16 or lower, so your mod should target Java 16. -java.toolchain.languageVersion = JavaLanguageVersion.of(16) +java.toolchain.languageVersion = JavaLanguageVersion.of(17) minecraft { mappings channel: project.mcp_channel, version: project.mcp_mappings diff --git a/gradle.properties b/gradle.properties index a893bfc..da71d72 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,10 +2,10 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false author=thetadev modid=constructionwand -mcversion=1.17.1 -forgeversion=37.0.73 +mcversion=1.18 +forgeversion=38.0.16 mcp_channel=official -mcp_mappings=1.17.1 +mcp_mappings=1.18 botania=1.16.2-405 version_major=2 version_minor=4 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0595cf7..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-7.2-20210702220150+0000-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/thetadev/constructionwand/ConstructionWand.java b/src/main/java/thetadev/constructionwand/ConstructionWand.java index bcfdeab..e8e71af 100644 --- a/src/main/java/thetadev/constructionwand/ConstructionWand.java +++ b/src/main/java/thetadev/constructionwand/ConstructionWand.java @@ -8,8 +8,8 @@ import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fmllegacy.network.NetworkRegistry; -import net.minecraftforge.fmllegacy.network.simple.SimpleChannel; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.simple.SimpleChannel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import thetadev.constructionwand.basics.ConfigClient; diff --git a/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java b/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java index 043907d..5b7f83b 100644 --- a/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java +++ b/src/main/java/thetadev/constructionwand/basics/option/WandUpgrades.java @@ -3,9 +3,9 @@ package thetadev.constructionwand.basics.option; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraftforge.common.util.Constants; import net.minecraftforge.registries.ForgeRegistries; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.api.IWandUpgrade; @@ -31,7 +31,7 @@ public class WandUpgrades } protected void deserialize() { - ListTag listnbt = tag.getList(key, Constants.NBT.TAG_STRING); + ListTag listnbt = tag.getList(key, Tag.TAG_STRING); boolean require_fix = false; for(int i = 0; i < listnbt.size(); i++) { diff --git a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java index 1fe6b07..b8dec20 100644 --- a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java +++ b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java @@ -30,7 +30,7 @@ public class RenderBlockPreview if(event.getTarget().getType() != HitResult.Type.BLOCK) return; BlockHitResult rtr = event.getTarget(); - Entity entity = event.getInfo().getEntity(); + Entity entity = event.getCamera().getEntity(); if(!(entity instanceof Player player)) return; Set blocks; float colorR = 0, colorG = 0, colorB = 0; @@ -51,8 +51,8 @@ public class RenderBlockPreview if(blocks == null || blocks.isEmpty()) return; - PoseStack ms = event.getMatrix(); - MultiBufferSource buffer = event.getBuffers(); + PoseStack ms = event.getPoseStack(); + MultiBufferSource buffer = event.getMultiBufferSource(); VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES); double partialTicks = event.getPartialTicks(); diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java index 329d6a8..c4881bd 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerShulkerbox.java @@ -2,12 +2,12 @@ package thetadev.constructionwand.containers.handlers; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.world.ContainerHelper; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.ShulkerBoxBlock; -import net.minecraftforge.common.util.Constants; import thetadev.constructionwand.api.IContainerHandler; import thetadev.constructionwand.basics.WandUtil; @@ -56,9 +56,9 @@ public class HandlerShulkerbox implements IContainerHandler private NonNullList getItemList(ItemStack itemStack) { NonNullList itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY); CompoundTag rootTag = itemStack.getTag(); - if(rootTag != null && rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) { + if(rootTag != null && rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) { CompoundTag entityTag = rootTag.getCompound("BlockEntityTag"); - if(entityTag.contains("Items", Constants.NBT.TAG_LIST)) { + if(entityTag.contains("Items", Tag.TAG_LIST)) { ContainerHelper.loadAllItems(entityTag, itemStacks); } } @@ -67,7 +67,7 @@ public class HandlerShulkerbox implements IContainerHandler private void setItemList(ItemStack itemStack, NonNullList itemStacks) { CompoundTag rootTag = itemStack.getOrCreateTag(); - if(!rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) { + if(!rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) { rootTag.put("BlockEntityTag", new CompoundTag()); } ContainerHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks); diff --git a/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java b/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java index 488b89e..396d4a2 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java +++ b/src/main/java/thetadev/constructionwand/network/PacketQueryUndo.java @@ -2,7 +2,7 @@ package thetadev.constructionwand.network; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.fmllegacy.network.NetworkEvent; +import net.minecraftforge.network.NetworkEvent; import thetadev.constructionwand.ConstructionWand; import java.util.function.Supplier; diff --git a/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java b/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java index b6ca550..566ca6f 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java +++ b/src/main/java/thetadev/constructionwand/network/PacketUndoBlocks.java @@ -2,7 +2,7 @@ package thetadev.constructionwand.network; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; -import net.minecraftforge.fmllegacy.network.NetworkEvent; +import net.minecraftforge.network.NetworkEvent; import thetadev.constructionwand.ConstructionWand; import java.util.HashSet; diff --git a/src/main/java/thetadev/constructionwand/network/PacketWandOption.java b/src/main/java/thetadev/constructionwand/network/PacketWandOption.java index e2bde2e..e81d012 100644 --- a/src/main/java/thetadev/constructionwand/network/PacketWandOption.java +++ b/src/main/java/thetadev/constructionwand/network/PacketWandOption.java @@ -3,7 +3,7 @@ package thetadev.constructionwand.network; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.fmllegacy.network.NetworkEvent; +import net.minecraftforge.network.NetworkEvent; import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.WandOptions; diff --git a/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java b/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java index 5be6fdb..9443df5 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/UndoHistory.java @@ -8,7 +8,7 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraftforge.fmllegacy.network.PacketDistributor; +import net.minecraftforge.network.PacketDistributor; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.WandUtil; diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 867d8b2..bb40c85 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[37,)" +loaderVersion = "[38,)" license = "MIT License" [[mods]] modId="constructionwand" @@ -20,12 +20,12 @@ This is my first minecraft mod. May the odds be ever in your favor. [[dependencies.constructionwand]] modId="forge" mandatory = true -versionRange = "[37,)" +versionRange = "[38,)" ordering = "NONE" side="BOTH" [[dependencies.constructionwand]] modId="minecraft" mandatory = true -versionRange = "[1.17.1, 1.18)" +versionRange = "[1.18, 1.19)" ordering = "NONE" side="BOTH" From 3d62df6ac5b620b63264410ba3b6d7d16a6ebc1d Mon Sep 17 00:00:00 2001 From: "A. Regnander" Date: Tue, 7 Dec 2021 02:21:48 +0100 Subject: [PATCH 13/35] Create sv_se.json --- .../assets/constructionwand/lang/sv_se.json | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/sv_se.json diff --git a/src/main/resources/assets/constructionwand/lang/sv_se.json b/src/main/resources/assets/constructionwand/lang/sv_se.json new file mode 100644 index 0000000..bab195b --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/sv_se.json @@ -0,0 +1,61 @@ +{ + "item.constructionwand.stone_wand": "Stenstav", + "item.constructionwand.iron_wand": "Järnstav", + "item.constructionwand.diamond_wand": "Diamantstav", + "item.constructionwand.infinity_wand": "Oändlighetsstav", + "item.constructionwand.core_angel": "Änglastavskärna", + "item.constructionwand.core_destruction": "Rivningsstavskärna", + + "constructionwand.tooltip.blocks": "Max. %d block", + "constructionwand.tooltip.shift": "Håll ned [SHIFT]", + "constructionwand.tooltip.cores": "Stavkärnor:", + "constructionwand.tooltip.core_tip": "Kombinera kärnan med din stav i ett tillverkningsrutnät", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "Byggkärna", + "constructionwand.option.cores.constructionwand:default.desc": "Utvidga din byggnad åt sidan som är riktad mot dig", + "constructionwand.option.cores.constructionwand:core_angel": "§6Änglakärna", + "constructionwand.option.cores.constructionwand:core_angel.desc": "Placera block bakom befintliga block och i luften", + "constructionwand.option.cores.constructionwand:core_destruction": "§cRivningskärna", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "Förstör block på sidan som är riktad mot dig", + + "constructionwand.option.lock": "Begränsning: ", + "constructionwand.option.lock.horizontal": "§aVänster/Höger", + "constructionwand.option.lock.horizontal.desc": "Bygg en horisontal kolumn framför originalblocket", + "constructionwand.option.lock.vertical": "§aUppåt/Nedåt", + "constructionwand.option.lock.vertical.desc": "Bygg en vertikal kolumn framför originalblocket", + "constructionwand.option.lock.northsouth": "§6Nord/Syd", + "constructionwand.option.lock.northsouth.desc": "Bygg en nord-/sydriktad rad ovanpå originalblocket", + "constructionwand.option.lock.eastwest": "§6Öst/Väst", + "constructionwand.option.lock.eastwest.desc": "Bygg en öst-/västriktad rad ovanpå originalblocket", + "constructionwand.option.lock.nolock": "§cIngen", + "constructionwand.option.lock.nolock.desc": "Utvidga från en valfri sida av originalblocket", + + "constructionwand.option.direction": "Riktning: ", + "constructionwand.option.direction.target": "§6Mål", + "constructionwand.option.direction.target.desc": "Placera block i samma riktning som målblocket", + "constructionwand.option.direction.player": "§aSpelare", + "constructionwand.option.direction.player.desc": "Placera block i samma riktning som spelaren tittar åt", + + "constructionwand.option.replace": "Ersättning: ", + "constructionwand.option.replace.yes": "§aJa", + "constructionwand.option.replace.yes.desc": "Ersätt vissa block, t.ex. vätskor, snö och högt gräs", + "constructionwand.option.replace.no": "§cNej", + "constructionwand.option.replace.no.desc": "Ersätt inte något block", + + "constructionwand.option.match": "Matchning: ", + "constructionwand.option.match.exact": "§aExakt", + "constructionwand.option.match.exact.desc": "Utvidga endast block som är exakt likadana", + "constructionwand.option.match.similar": "§6Liknande", + "constructionwand.option.match.similar.desc": "Behandla liknande block (jord-/grästyper) likadant", + "constructionwand.option.match.any": "§cAllting", + "constructionwand.option.match.any.desc": "Utvidga alla block", + + "constructionwand.option.random": "Slumpa: ", + "constructionwand.option.random.yes": "§aJa", + "constructionwand.option.random.yes.desc": "Placera slumpartade block från din föremålsmeny", + "constructionwand.option.random.no": "§cNej", + "constructionwand.option.random.no.desc": "Slumpa inte block som ska placeras ut", + + "stat.constructionwand.use_wand": "Block placerade med stavar" +} From 0ca129122ed7bba6a3b85aa605415b3706ceb7a8 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Wed, 8 Dec 2021 18:52:39 +0100 Subject: [PATCH 14/35] add JEI ingame documentation --- README.md | 27 +++++--- build.gradle | 9 +++ gradle.properties | 3 +- .../constructionwand/basics/ConfigServer.java | 4 +- .../jei/ConstructionWandJeiPlugin.java | 67 +++++++++++++++++++ .../constructionwand/items/ModItems.java | 1 + src/main/resources/META-INF/mods.toml | 6 +- .../assets/constructionwand/lang/de_de.json | 9 +++ .../assets/constructionwand/lang/en_us.json | 9 +++ 9 files changed, 118 insertions(+), 17 deletions(-) create mode 100644 src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java diff --git a/README.md b/README.md index 75169d2..e664f13 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Construction Wand With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're -facing. Sneak+Right click to activate angel mode which allows you to place a block at the opposite side of the block -facing you. If you concentrate enough, you can even conjure a block in mid air! +facing. If that's not enough: you can upgrade your wand with additional cores, allowing you to place a block behind the +block you are facing, conjure blocks in mid air or destroy lots of blocks very fast. ![](images/wands.png) @@ -73,8 +73,8 @@ SNEAK+OPTKEY+Right clicking empty space opens the option screen of your wand. **Restriction:** If restriction is enabled the wand will only place blocks in one row or column (choose between North/South, East/West on a horizontal plane and Horizontal, Vertical on a vertical plane). -If the direction lock is switched off, the wand will extend the entire face of the building it's pointed at. -This option has no effect in Angel mode. +If the direction lock is switched off, the wand will extend the entire face of the building it's pointed at. This option +has no effect if the angel core is enabled. **Direction:** If set to "Player" the wand places blocks in the same direction as if they were placed by yourself. Target mode places the blocks in the same direction as their supporting block. See the picture below: @@ -92,13 +92,14 @@ are exactly the same as the selected block.
~~Shamelessly stolen~~ Inspired by the Trowel from Quark. ## Undo -Holding down SHIFT+CTRL while looking at a blocks will show you the last blocks you placed with a green border -around them. SHIFT+CTRL+Right clickking any of them will undo the operation, giving you all the items back. -If you used the Destruction core, it will restore the blocks. + +Holding down Sneak+OPTKEY while looking at a block will show you the last blocks you placed with a green border around +them. Sneak+OPTKEY+Right clicking any of them will undo the operation, giving you all the items back. If you used the +Destruction core, it will restore the blocks. ## Additional features -- If you have shulker boxes (or bundles in MC 1.17+) in your inventory filled with blocks, the wand can pull them out - and place them + +- Shulker boxes, bundles (MC 1.17+) and many containers from other mods can provide building blocks for the wand - Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana. @@ -109,14 +110,18 @@ If you used the Destruction core, it will restore the blocks. - **1.16+ only:** The Infinity Wand won't burn in lava just like netherite gear. +- Ingame documentation with Just Enough Items (JEI) + ## Contributions and #Hacktoberfest + As #Hacktoberfest now requires repo owners to opt in, I added the tag to this repository. -I'd really appreciate translations. Currently, ConstructionWand only has English and German, -so if you speak any other language you can help translate the mod and add a new language file +I'd really appreciate translations. Currently, ConstructionWand only has English and German, so if you speak any other +language you can help translate the mod and add a new language file under `src/main/resources/assets/constructionwand/lang/`. ## TileEntity Blacklist + Some modded TileEntitys can cause issues when placed using a wand. They may turn into invisible and unremovable ghost blocks, become unbreakable or cause other unwanted effects. diff --git a/build.gradle b/build.gradle index 555679a..ee5bfc5 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,10 @@ repositories { maven { url = "https://maven.theillusivec4.top/" } + maven { + name = "JEI Maven" + url "https://dvs1.progwml6.com/files/maven" + } } dependencies { @@ -98,12 +102,17 @@ dependencies { version: "${project.mcversion}-${project.forgeversion}" ]) + compileOnly fg.deobf("mezz.jei:${jei_version}:api") + runtimeOnly fg.deobf("mezz.jei:${jei_version}") + + /* compileOnly fg.deobf([ group: "vazkii.botania", name: "Botania", version: "${project.botania}", classifier: "api" ]) + */ } jar { diff --git a/gradle.properties b/gradle.properties index a893bfc..abddd8c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,7 @@ mcversion=1.17.1 forgeversion=37.0.73 mcp_channel=official mcp_mappings=1.17.1 -botania=1.16.2-405 +# botania=1.16.2-405 +jei_version=jei-1.18:9.0.0.40 version_major=2 version_minor=4 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java index 5cbec16..3e10b10 100644 --- a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java +++ b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java @@ -64,7 +64,7 @@ public class ConfigServer else durability = null; builder.comment("Wand block limit"); limit = builder.defineInRange("limit", defLimit, 1, Integer.MAX_VALUE); - builder.comment("Max placement distance with angel mode (0 to disable angel core)"); + builder.comment("Max placement distance with angel core (0 to disable angel core)"); angel = builder.defineInRange("angel", defAngel, 0, Integer.MAX_VALUE); builder.comment("Wand destruction block limit (0 to disable destruction core)"); destruction = builder.defineInRange("destruction", defDestruction, 0, Integer.MAX_VALUE); @@ -116,7 +116,7 @@ public class ConfigServer MAX_RANGE = BUILDER.defineInRange("MaxRange", 100, 0, Integer.MAX_VALUE); BUILDER.comment("Number of operations that can be undone"); UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE); - BUILDER.comment("Place blocks below you while falling > 10 blocks with angel mode (Can be used to save you from drops/the void)"); + BUILDER.comment("Place blocks below you while falling > 10 blocks with angel core (Can be used to save you from drops/the void)"); ANGEL_FALLING = BUILDER.define("AngelFalling", false); BUILDER.comment("Blocks to treat equally when in Similar mode. Enter block IDs seperated by ;"); SIMILAR_BLOCKS = BUILDER.defineList("SimilarBlocks", Arrays.asList(SIMILAR_BLOCKS_DEFAULT), obj -> true); diff --git a/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java b/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java new file mode 100644 index 0000000..682d758 --- /dev/null +++ b/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java @@ -0,0 +1,67 @@ +package thetadev.constructionwand.integrations.jei; + +import com.mojang.blaze3d.platform.InputConstants; +import mezz.jei.api.IModPlugin; +import mezz.jei.api.JeiPlugin; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.registration.IRecipeRegistration; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import thetadev.constructionwand.ConstructionWand; +import thetadev.constructionwand.basics.ConfigClient; +import thetadev.constructionwand.basics.ConfigServer; +import thetadev.constructionwand.items.ModItems; + +import javax.annotation.Nonnull; + +@JeiPlugin +public class ConstructionWandJeiPlugin implements IModPlugin +{ + private static final ResourceLocation pluginId = new ResourceLocation(ConstructionWand.MODID, ConstructionWand.MODID); + private static final String baseKey = ConstructionWand.MODID + ".description."; + private static final String baseKeyItem = "item." + ConstructionWand.MODID + "."; + + @Nonnull + @Override + public ResourceLocation getPluginUid() { + return pluginId; + } + + private Component keyComboComponent(boolean shiftOpt, Component optkeyComponent) { + String key = shiftOpt ? "sneak_opt" : "sneak"; + return new TranslatableComponent(baseKey + "key." + key, optkeyComponent).withStyle(ChatFormatting.BLUE); + } + + @Override + public void registerRecipes(IRecipeRegistration registration) { + Component optkeyComponent = new TranslatableComponent(InputConstants.getKey(ConfigClient.OPT_KEY.get(), -1).getName()) + .withStyle(ChatFormatting.BLUE); + Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent); + Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent); + + for(Item wand : ModItems.WANDS) { + ConfigServer.WandProperties wandProperties = ConfigServer.getWandProperties(wand); + + String durabilityKey = wand == ModItems.WAND_INFINITY ? "unlimited" : "limited"; + Component durabilityComponent = new TranslatableComponent(baseKey + "durability." + durabilityKey, wandProperties.getDurability()); + + registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM, + new TranslatableComponent(baseKey + "wand", + new TranslatableComponent(baseKeyItem + wand.getRegistryName().getPath()), + wandProperties.getLimit(), durabilityComponent, + optkeyComponent, wandModeComponent, wandGuiComponent) + ); + } + + for(Item core : ModItems.CORES) { + registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM, + new TranslatableComponent(baseKey + core.getRegistryName().getPath()), + new TranslatableComponent(baseKey + "core", wandModeComponent) + ); + } + } +} diff --git a/src/main/java/thetadev/constructionwand/items/ModItems.java b/src/main/java/thetadev/constructionwand/items/ModItems.java index 10d7916..14c3255 100644 --- a/src/main/java/thetadev/constructionwand/items/ModItems.java +++ b/src/main/java/thetadev/constructionwand/items/ModItems.java @@ -41,6 +41,7 @@ public class ModItems // Collections public static final Item[] WANDS = {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY}; + public static final Item[] CORES = {CORE_ANGEL, CORE_DESTRUCTION}; public static final HashSet ALL_ITEMS = new HashSet<>(); diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 867d8b2..76b0eba 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -11,9 +11,9 @@ authors="ThetaDev" description=''' Construction Wands make building easier! -With a Construction Wand you can place multiple blocks (up to 1024) at once, extending you build on the side you're facing. -Sneak+Right click to activate angel mode which allows you to place a block at the opposite side of the block facing you. -If you concentrate enough, you can even conjure a block in mid air! +With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're +facing. If that's not enough: you can upgrade your wand with additional cores, allowing you to place a block +behind the block you are facing, conjure blocks in mid air or destroy lots of blocks very fast. This is my first minecraft mod. May the odds be ever in your favor. ''' diff --git a/src/main/resources/assets/constructionwand/lang/de_de.json b/src/main/resources/assets/constructionwand/lang/de_de.json index 74865f3..a4d2f8f 100644 --- a/src/main/resources/assets/constructionwand/lang/de_de.json +++ b/src/main/resources/assets/constructionwand/lang/de_de.json @@ -57,5 +57,14 @@ "constructionwand.option.random.no": "§cAus", "constructionwand.option.random.no.desc": "Platziere Blöcke normal", + "constructionwand.description.wand": "Ein %1$s kann maximal %2$d Blöcke auf der dir zugewandten Seite eines Bauwerks platzieren und hält %3$s.\n\nHalte %5$s gedrückt und scrolle, um die Platzierung zu beschränken (Horizontal, Vertikal, Nord/Süd, Ost/West, Keine).\n\nÖffne den Optionsbildschirm mit %6$s§9+Rechtsklick§0.\n\n§5§nRÜCKGÄNGIG§0§r\nHalte §9Schleichen+§0%4$s während du einen Block fokussierst. Die letzten platzierten Blöcke werden mit einem grünen Rahmen markiert. §9Schleichen+§0%4$s§9+Rechtsklick§0 auf einen dieser Blöcke macht diese Operation rückgängig und gibt dir alle Items zurück. Wenn du den Kristall der Zerstörung benutzt hast, werden die zerstörten Blöcke wiederhergestellt.\n\n§5§nCONTAINER§0§r\nShulkerkisten, Bündel und viele Container von anderen Mods können Baumaterial für deinen Stab bereitstellen.\n\n§5§nLINKE-HAND-PRIORITÄT§0§r\nWenn du einen Block in der linken Hand hältst, wird der Stab diesen anstatt des Blocks, den du anschaust, platzieren.", + "constructionwand.description.durability.limited": "für %d Blöcke", + "constructionwand.description.durability.unlimited": "unendlich lang", + "constructionwand.description.key.sneak": "Schleichen", + "constructionwand.description.key.sneak_opt": "Schleichen+%s", + "constructionwand.description.core": "§5§nINSTALLATION§0§r\nLege deinen neuen Kristall zusammen mit dem Stab auf eine Werkbank, um ihn einzusetzen. Um zwischen den Kristallen zu wechseln, halte %s gedrückt und klicke mit der linken Maustaste ins Leere. Alternativ kannst du den Kristall auch im Optionsbildschirm auswählen.", + "constructionwand.description.core_angel": "Der Kristall der Engel platziert einen Block auf der gegenüberliegenden Seites des Blocks (oder der Blockreihe) den du anschaust. Die maximale Entfernung hängt vom Material des Stabes ab. Ein Rechtsklick ins Leere platziert einen Block mitten in der Luft. Hierfür musst du den Block, den du platzieren willst, in der linken Hand halten.", + "constructionwand.description.core_destruction": "Der Kristall der Zerstörung zerstört Blöcke (keine Tile Entities) auf der dir zugewandten Seite. Die maximale Anzahl Blöcke hängt vonm Material des Stabes ab. Zerstörte Blöcke verschwinden im Nichts, du kannst Fehler jedoch rückgängig machen.", + "stat.constructionwand.use_wand": "Blöcke mithilfe des Stabs platziert" } \ No newline at end of file diff --git a/src/main/resources/assets/constructionwand/lang/en_us.json b/src/main/resources/assets/constructionwand/lang/en_us.json index 5331b7b..0f274af 100644 --- a/src/main/resources/assets/constructionwand/lang/en_us.json +++ b/src/main/resources/assets/constructionwand/lang/en_us.json @@ -57,5 +57,14 @@ "constructionwand.option.random.no": "§cNo", "constructionwand.option.random.no.desc": "Don't randomize placed blocks", + "constructionwand.description.wand": "A %1$s can place up to %2$d blocks at the side of a building facing you and lasts %3$s.\n\nHold down %5$s and scroll to change placement restriction (Horizontal, Vertical, North/South, East/West, No lock).\n\nOpen the option screen with %6$s§9+Right click§0.\n\n§5§nUNDO§0§r\nHolding down §9Sneak+§0%4$s while looking at a blocks will show you the last blocks you placed with a green border around them. §9Sneak+§0%4$s§9+Right clicking§0 any of them will undo the operation, giving you all the items back. If you used the Destruction core, it will restore the blocks.\n\n§5§nCONTAINERS§0§r\nShulker boxes, bundles and many containers from other mods can provide building blocks for the wand.\n\n§5§nOFFHAND PRIORITY§0§r\nHaving blocks in your offhand will place them instead of the block you're looking at.", + "constructionwand.description.durability.limited": "for %d blocks", + "constructionwand.description.durability.unlimited": "forever", + "constructionwand.description.key.sneak": "Sneak", + "constructionwand.description.key.sneak_opt": "Sneak+%s", + "constructionwand.description.core": "§5§nINSTALLATION§0§r\nPut your new core together with your wand in a crafting grid to install it. To switch between cores, hold down %s and left click empty space with your wand or use the option screen.", + "constructionwand.description.core_angel": "The angel core places a block on the opposite side of the block (or row of blocks) you are facing. Maximum distance depends on wand tier. Right click empty space to place a block in midair. To do that, you'll need to have the block you want to place in your offhand.", + "constructionwand.description.core_destruction": "The destruction core destroys blocks (no tile entities) on the side facing you. Maximum number of blocks depends on wand tier. Destroyed blocks disappear into the void, you can use the undo feature if you've made a mistake.", + "stat.constructionwand.use_wand": "Blocks placed using Wand" } \ No newline at end of file From bc0668a1b760be8c453c324cf9c67cf27e71b6d3 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Wed, 8 Dec 2021 19:48:12 +0100 Subject: [PATCH 15/35] fix wand getting damaged in creative mode --- .../constructionwand/wand/WandJob.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/thetadev/constructionwand/wand/WandJob.java b/src/main/java/thetadev/constructionwand/wand/WandJob.java index 52cdbf3..313b6c8 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandJob.java +++ b/src/main/java/thetadev/constructionwand/wand/WandJob.java @@ -94,15 +94,19 @@ public class WandJob if(wand.isEmpty() || wandItem.remainingDurability(wand) == 0) break; if(snapshot.execute(world, player, rayTraceResult)) { - // If the item cant be taken, undo the placement - if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) executed.add(snapshot); + if(player.isCreative()) executed.add(snapshot); else { - ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " + - snapshot.getBlockState().getBlock().toString()); - snapshot.forceRestore(world); + // If the item cant be taken, undo the placement + if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) { + executed.add(snapshot); + wand.hurt(1, player.getRandom(), (ServerPlayer) player); + } + else { + ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " + + snapshot.getBlockState().getBlock().toString()); + snapshot.forceRestore(world); + } } - - wand.hurt(1, player.getRandom(), (ServerPlayer) player); player.awardStat(ModStats.USE_WAND); } } From 439783744ce1001f94d153322a2f0a3200604955 Mon Sep 17 00:00:00 2001 From: "A. Regnander" Date: Thu, 9 Dec 2021 01:14:01 +0100 Subject: [PATCH 16/35] Update sv_se.json Added new strings --- .../resources/assets/constructionwand/lang/sv_se.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/assets/constructionwand/lang/sv_se.json b/src/main/resources/assets/constructionwand/lang/sv_se.json index bab195b..23eb3bf 100644 --- a/src/main/resources/assets/constructionwand/lang/sv_se.json +++ b/src/main/resources/assets/constructionwand/lang/sv_se.json @@ -57,5 +57,14 @@ "constructionwand.option.random.no": "§cNej", "constructionwand.option.random.no.desc": "Slumpa inte block som ska placeras ut", + "constructionwand.description.wand": "En %1$s kan placera upp till %2$d block på sidan av en byggnad som är riktad mot dig och räcker %3$s.\n\nHåll ned %5$s och rulla med mushjulet för att ändra placeringsbegränsningen (horisontal, vertikal, nord/syd, öst/väst, ingen låsning).\n\nÖppna alternativmenyn med %6$s§9+Högerklick§0.\n\n§5§nÅNGRA§0§r\nNär du håller ned §9Smyga+§0%4$s medan du tittar på ett block kommer du se de senaste blocken du placerade omgivna av en grön ram. §9Smyg+§0%4$s§9+högerklicka§0 på något av dem för att ångra handlingen och få tillbaka alla föremål. Om du har använt rivningskärnan kommer blocken att återställas.\n\n§5§nBEHÅLLARE§0§r\nShulkerlådor, påsar och många behållare från andra moddar kan tillhandahålla byggblock för staven.\n\n§5§nPRIO FÖR SEKUNDÄR HAND§0§r\nBlocken i din sekundära hand placeras i stället för blocket du tittar på.", + "constructionwand.description.durability.limited": "för %d block", + "constructionwand.description.durability.unlimited": "för alltid", + "constructionwand.description.key.sneak": "Smyg", + "constructionwand.description.key.sneak_opt": "Smyg+%s", + "constructionwand.description.core": "§5§nINSTALLATION§0§r\nLägg din nya kärna tillsammans med din stav i ett tillverkningsrutnät för att installera den. Håll ned %s och vänsterklicka i luften med din stav eller använd alternativmenyn för att byta kärna.", + "constructionwand.description.core_angel": "Änglakärnan placerar ett block på den motsatta sidan av blocket (eller blockraden) som är riktad mot dig. Det maximala avståndet beror på stavens nivå. Högerklicka i luften för att placera ett block i luften. För att göra detta behöver du hålla blocket du vill placera i din sekundära hand.", + "constructionwand.description.core_destruction": "Rivningskärnan förstör block (inte blockentiteter) på sidan som är riktad mot dig. Det maximala antalet block beror på stavens nivå. Förstörda block försvinner helt och hållet, men du kan använda ångrafunktionen om du har gjort ett misstag.", + "stat.constructionwand.use_wand": "Block placerade med stavar" } From 138e66e4b7f6ad44abd22e0edab7ffadeee85c29 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 9 Dec 2021 09:52:49 +0100 Subject: [PATCH 17/35] update readme --- README.md | 18 +++++++++--------- gradle.properties | 6 ++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e664f13..a1263e3 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ and last longer. These properties can be changed in the config. ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting5.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting6.png) -## OPTKEY +## Keybindings To change a wand's core and options or undo your placement you need to hold down the wand option key (refered as OPTKEY). By default, this is CTRL, but it can be changed in the client config file. To prevent unwanted @@ -112,14 +112,6 @@ Destruction core, it will restore the blocks. - Ingame documentation with Just Enough Items (JEI) -## Contributions and #Hacktoberfest - -As #Hacktoberfest now requires repo owners to opt in, I added the tag to this repository. - -I'd really appreciate translations. Currently, ConstructionWand only has English and German, so if you speak any other -language you can help translate the mod and add a new language file -under `src/main/resources/assets/constructionwand/lang/`. - ## TileEntity Blacklist Some modded TileEntitys can cause issues when placed using a wand. They may turn into invisible and @@ -132,3 +124,11 @@ If you find some of them you can tell me by creating an issue, commenting on Curse or editing the default blacklist yourself (it is located at https://github.com/Theta-Dev/ConstructionWand/blob/1.16.2/src/main/java/thetadev/constructionwand/basics/ConfigServer.java#L28) and making a PR. + +## Contributions and #Hacktoberfest + +As #Hacktoberfest now requires repo owners to opt in, I added the tag to this repository. + +I'd really appreciate translations. Currently, ConstructionWand is available in English, German and Swedish. +If you speak any other language you can help translate the mod and add a new language file +under `src/main/resources/assets/constructionwand/lang/`. diff --git a/gradle.properties b/gradle.properties index abddd8c..058f847 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,18 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false + author=thetadev modid=constructionwand + mcversion=1.17.1 forgeversion=37.0.73 mcp_channel=official mcp_mappings=1.17.1 + +# Source: https://maven.blamejared.com/vazkii/botania/Botania/ # botania=1.16.2-405 +# Source: https://dvs1.progwml6.com/files/maven/mezz/jei/ jei_version=jei-1.18:9.0.0.40 + version_major=2 version_minor=4 \ No newline at end of file From d944f21655beab20ae3aced6aa162e037b5b8728 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 9 Dec 2021 10:21:08 +0100 Subject: [PATCH 18/35] bump version 2.4 -> 2.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 058f847..12526d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ mcp_mappings=1.17.1 jei_version=jei-1.18:9.0.0.40 version_major=2 -version_minor=4 \ No newline at end of file +version_minor=5 \ No newline at end of file From 721d7543a990d10acbfc59245e83ddcb30402c9c Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 9 Dec 2021 10:45:30 +0100 Subject: [PATCH 19/35] update forge and jei --- gradle.properties | 4 ++-- src/main/resources/assets/constructionwand/lang/en_us.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 12526d8..dcc329f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,14 +5,14 @@ author=thetadev modid=constructionwand mcversion=1.17.1 -forgeversion=37.0.73 +forgeversion=37.1.0 mcp_channel=official mcp_mappings=1.17.1 # Source: https://maven.blamejared.com/vazkii/botania/Botania/ # botania=1.16.2-405 # Source: https://dvs1.progwml6.com/files/maven/mezz/jei/ -jei_version=jei-1.18:9.0.0.40 +jei_version=jei-1.17.1:8.3.0.39 version_major=2 version_minor=5 \ No newline at end of file diff --git a/src/main/resources/assets/constructionwand/lang/en_us.json b/src/main/resources/assets/constructionwand/lang/en_us.json index 0f274af..619fc45 100644 --- a/src/main/resources/assets/constructionwand/lang/en_us.json +++ b/src/main/resources/assets/constructionwand/lang/en_us.json @@ -57,7 +57,7 @@ "constructionwand.option.random.no": "§cNo", "constructionwand.option.random.no.desc": "Don't randomize placed blocks", - "constructionwand.description.wand": "A %1$s can place up to %2$d blocks at the side of a building facing you and lasts %3$s.\n\nHold down %5$s and scroll to change placement restriction (Horizontal, Vertical, North/South, East/West, No lock).\n\nOpen the option screen with %6$s§9+Right click§0.\n\n§5§nUNDO§0§r\nHolding down §9Sneak+§0%4$s while looking at a blocks will show you the last blocks you placed with a green border around them. §9Sneak+§0%4$s§9+Right clicking§0 any of them will undo the operation, giving you all the items back. If you used the Destruction core, it will restore the blocks.\n\n§5§nCONTAINERS§0§r\nShulker boxes, bundles and many containers from other mods can provide building blocks for the wand.\n\n§5§nOFFHAND PRIORITY§0§r\nHaving blocks in your offhand will place them instead of the block you're looking at.", + "constructionwand.description.wand": "The %1$s can place up to %2$d blocks at the side of a building facing you and lasts %3$s.\n\nHold down %5$s and scroll to change placement restriction (Horizontal, Vertical, North/South, East/West, No lock).\n\nOpen the option screen with %6$s§9+Right click§0.\n\n§5§nUNDO§0§r\nHolding down §9Sneak+§0%4$s while looking at a blocks will show you the last blocks you placed with a green border around them. §9Sneak+§0%4$s§9+Right clicking§0 any of them will undo the operation, giving you all the items back. If you used the Destruction core, it will restore the blocks.\n\n§5§nCONTAINERS§0§r\nShulker boxes, bundles and many containers from other mods can provide building blocks for the wand.\n\n§5§nOFFHAND PRIORITY§0§r\nHaving blocks in your offhand will place them instead of the block you're looking at.", "constructionwand.description.durability.limited": "for %d blocks", "constructionwand.description.durability.unlimited": "forever", "constructionwand.description.key.sneak": "Sneak", From f86a429beb693bc54a623166167abacdef9e1dff Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Thu, 9 Dec 2021 12:02:39 +0100 Subject: [PATCH 20/35] update pack.mcmeta version --- src/main/resources/pack.mcmeta | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 89051f3..665308b 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,7 +1,6 @@ { "pack": { "description": "ConstructionWand resources", - "pack_format": 5, - "_comment": "A pack_format of 5 requires json lang files and some texture changes from 1.15. Note: we require v5 pack meta for all mods." + "pack_format": 8 } } From 030d5f72f2925c94179058dc585fcc28209c7cd4 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sun, 12 Dec 2021 10:47:22 +0100 Subject: [PATCH 21/35] fix #47 crash when pressing E inside wand gui --- gradle.properties | 2 +- .../thetadev/constructionwand/client/ScreenWand.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index b8ebd1b..f361ed1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ mcp_mappings=1.18 jei_version=jei-1.18:9.0.0.40 version_major=2 -version_minor=5 \ No newline at end of file +version_minor=6 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/client/ScreenWand.java b/src/main/java/thetadev/constructionwand/client/ScreenWand.java index a746bab..d665e83 100644 --- a/src/main/java/thetadev/constructionwand/client/ScreenWand.java +++ b/src/main/java/thetadev/constructionwand/client/ScreenWand.java @@ -1,6 +1,7 @@ package thetadev.constructionwand.client; import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; @@ -53,9 +54,13 @@ public class ScreenWand extends Screen } @Override - public boolean charTyped(char character, int code) { - if(character == 'e') onClose(); - return super.charTyped(character, code); + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (Minecraft.getInstance().options.keyInventory.matches(keyCode, scanCode)) { + this.onClose(); + return true; + } else { + return super.keyPressed(keyCode, scanCode, modifiers); + } } private void createButton(int cx, int cy, IOption option) { From 0a1c0f2926511aa6d1531737e4f7977e28a32f01 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sun, 12 Dec 2021 11:01:42 +0100 Subject: [PATCH 22/35] removed unused block texture --- .../textures/block/conjured_block.png | Bin 5594 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/resources/assets/constructionwand/textures/block/conjured_block.png diff --git a/src/main/resources/assets/constructionwand/textures/block/conjured_block.png b/src/main/resources/assets/constructionwand/textures/block/conjured_block.png deleted file mode 100644 index 7fc945cb73067de2671ef016b8fa8bd764c9255e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 From 210baba4cd5e5ec6bae881e994c5332c40b8014f Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sun, 12 Dec 2021 11:26:46 +0100 Subject: [PATCH 23/35] update MC and Forge to 1.18.1 --- build.gradle | 2 +- gradle.properties | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index ddf9394..5b50303 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { repositories { - maven { url = 'https://files.minecraftforge.net/maven' } + maven { url = 'https://maven.minecraftforge.net' } mavenCentral() } dependencies { diff --git a/gradle.properties b/gradle.properties index f361ed1..40a8f6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,15 +4,15 @@ org.gradle.daemon=false author=thetadev modid=constructionwand -mcversion=1.18 -forgeversion=38.0.16 +mcversion=1.18.1 +forgeversion=39.0.0 mcp_channel=official -mcp_mappings=1.18 +mcp_mappings=1.18.1 # Source: https://maven.blamejared.com/vazkii/botania/Botania/ # botania=1.16.2-405 # Source: https://dvs1.progwml6.com/files/maven/mezz/jei/ -jei_version=jei-1.18:9.0.0.40 +jei_version=jei-1.18.1:9.1.0.41 version_major=2 version_minor=6 \ No newline at end of file From 7a89175bf1272819127d9330756f9f3bc8f0cb67 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Fri, 25 Mar 2022 22:00:47 +0100 Subject: [PATCH 24/35] use DeferredRegister for registering items --- .../constructionwand/ConstructionWand.java | 3 ++ .../constructionwand/basics/ConfigServer.java | 13 +++-- .../data/ItemModelGenerator.java | 4 +- .../data/RecipeGenerator.java | 12 ++--- .../jei/ConstructionWandJeiPlugin.java | 9 ++-- .../constructionwand/items/ItemBase.java | 12 ----- .../constructionwand/items/ModItems.java | 48 ++++++------------- .../constructionwand/items/core/ItemCore.java | 8 ++-- .../items/core/ItemCoreAngel.java | 4 +- .../items/core/ItemCoreDestruction.java | 4 +- .../constructionwand/items/wand/ItemWand.java | 8 ++-- .../items/wand/ItemWandBasic.java | 4 +- .../items/wand/ItemWandInfinity.java | 4 +- .../constructionwand/wand/WandJob.java | 2 +- 14 files changed, 58 insertions(+), 77 deletions(-) delete mode 100644 src/main/java/thetadev/constructionwand/items/ItemBase.java diff --git a/src/main/java/thetadev/constructionwand/ConstructionWand.java b/src/main/java/thetadev/constructionwand/ConstructionWand.java index e8e71af..c5b9f76 100644 --- a/src/main/java/thetadev/constructionwand/ConstructionWand.java +++ b/src/main/java/thetadev/constructionwand/ConstructionWand.java @@ -53,6 +53,9 @@ public class ConstructionWand FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup); MinecraftForge.EVENT_BUS.register(this); + // Register Item DeferredRegister + ModItems.ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); + // Config setup ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigServer.SPEC); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigClient.SPEC); diff --git a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java index 3e10b10..6fc0de9 100644 --- a/src/main/java/thetadev/constructionwand/basics/ConfigServer.java +++ b/src/main/java/thetadev/constructionwand/basics/ConfigServer.java @@ -1,8 +1,10 @@ package thetadev.constructionwand.basics; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.Tiers; import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.registries.RegistryObject; import thetadev.constructionwand.items.ModItems; import java.util.Arrays; @@ -27,10 +29,10 @@ public class ConfigServer public static final ForgeConfigSpec.ConfigValue> TE_LIST; private static final String[] TE_LIST_DEFAULT = {"chiselsandbits"}; - private static final HashMap wandProperties = new HashMap<>(); + private static final HashMap wandProperties = new HashMap<>(); public static WandProperties getWandProperties(Item wand) { - return wandProperties.getOrDefault(wand, WandProperties.DEFAULT); + return wandProperties.getOrDefault(wand.getRegistryName(), WandProperties.DEFAULT); } public static class WandProperties @@ -53,9 +55,10 @@ public class ConfigServer this.upgradeable = upgradeable; } - public WandProperties(ForgeConfigSpec.Builder builder, Item wand, int defDurability, int defLimit, + public WandProperties(ForgeConfigSpec.Builder builder, RegistryObject wandSupplier, int defDurability, int defLimit, int defAngel, int defDestruction, boolean defUpgradeable) { - builder.push(wand.getRegistryName().getPath()); + ResourceLocation registryName = wandSupplier.getId(); + builder.push(registryName.getPath()); if(defDurability > 0) { builder.comment("Wand durability"); @@ -72,7 +75,7 @@ public class ConfigServer upgradeable = builder.define("upgradeable", defUpgradeable); builder.pop(); - wandProperties.put(wand, this); + wandProperties.put(registryName, this); } public int getDurability() { diff --git a/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java b/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java index b224ac8..c007b70 100644 --- a/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java +++ b/src/main/java/thetadev/constructionwand/data/ItemModelGenerator.java @@ -5,6 +5,7 @@ import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraftforge.client.model.generators.ItemModelProvider; import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.registries.RegistryObject; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.items.ModItems; @@ -18,7 +19,8 @@ public class ItemModelGenerator extends ItemModelProvider @Override protected void registerModels() { - for(Item item : ModItems.ALL_ITEMS) { + for(RegistryObject itemObject : ModItems.ITEMS.getEntries()) { + Item item = itemObject.get(); String name = item.getRegistryName().getPath(); if(item instanceof ICustomItemModel) diff --git a/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java b/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java index 4407958..aaff00c 100644 --- a/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java +++ b/src/main/java/thetadev/constructionwand/data/RecipeGenerator.java @@ -27,13 +27,13 @@ public class RecipeGenerator extends RecipeProvider @Override protected void buildCraftingRecipes(@Nonnull Consumer consumer) { - wandRecipe(consumer, ModItems.WAND_STONE, Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS)); - wandRecipe(consumer, ModItems.WAND_IRON, Inp.fromTag(Tags.Items.INGOTS_IRON)); - wandRecipe(consumer, ModItems.WAND_DIAMOND, Inp.fromTag(Tags.Items.GEMS_DIAMOND)); - wandRecipe(consumer, ModItems.WAND_INFINITY, Inp.fromTag(Tags.Items.NETHER_STARS)); + wandRecipe(consumer, ModItems.WAND_STONE.get(), Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS)); + wandRecipe(consumer, ModItems.WAND_IRON.get(), Inp.fromTag(Tags.Items.INGOTS_IRON)); + wandRecipe(consumer, ModItems.WAND_DIAMOND.get(), Inp.fromTag(Tags.Items.GEMS_DIAMOND)); + wandRecipe(consumer, ModItems.WAND_INFINITY.get(), Inp.fromTag(Tags.Items.NETHER_STARS)); - coreRecipe(consumer, ModItems.CORE_ANGEL, Inp.fromTag(Tags.Items.FEATHERS), Inp.fromTag(Tags.Items.INGOTS_GOLD)); - coreRecipe(consumer, ModItems.CORE_DESTRUCTION, Inp.fromTag(Tags.Items.STORAGE_BLOCKS_DIAMOND), Inp.fromItem(Items.DIAMOND_PICKAXE)); + coreRecipe(consumer, ModItems.CORE_ANGEL.get(), Inp.fromTag(Tags.Items.FEATHERS), Inp.fromTag(Tags.Items.INGOTS_GOLD)); + coreRecipe(consumer, ModItems.CORE_DESTRUCTION.get(), Inp.fromTag(Tags.Items.STORAGE_BLOCKS_DIAMOND), Inp.fromItem(Items.DIAMOND_PICKAXE)); specialRecipe(consumer, RecipeWandUpgrade.SERIALIZER); } diff --git a/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java b/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java index 682d758..ed0ca4d 100644 --- a/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java +++ b/src/main/java/thetadev/constructionwand/integrations/jei/ConstructionWandJeiPlugin.java @@ -11,6 +11,7 @@ import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraftforge.registries.RegistryObject; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.basics.ConfigClient; import thetadev.constructionwand.basics.ConfigServer; @@ -43,10 +44,11 @@ public class ConstructionWandJeiPlugin implements IModPlugin Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent); Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent); - for(Item wand : ModItems.WANDS) { + for(RegistryObject wandSupplier : ModItems.WANDS) { + Item wand = wandSupplier.get(); ConfigServer.WandProperties wandProperties = ConfigServer.getWandProperties(wand); - String durabilityKey = wand == ModItems.WAND_INFINITY ? "unlimited" : "limited"; + String durabilityKey = wand == ModItems.WAND_INFINITY.get() ? "unlimited" : "limited"; Component durabilityComponent = new TranslatableComponent(baseKey + "durability." + durabilityKey, wandProperties.getDurability()); registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM, @@ -57,7 +59,8 @@ public class ConstructionWandJeiPlugin implements IModPlugin ); } - for(Item core : ModItems.CORES) { + for(RegistryObject coreSupplier : ModItems.CORES) { + Item core = coreSupplier.get(); registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM, new TranslatableComponent(baseKey + core.getRegistryName().getPath()), new TranslatableComponent(baseKey + "core", wandModeComponent) diff --git a/src/main/java/thetadev/constructionwand/items/ItemBase.java b/src/main/java/thetadev/constructionwand/items/ItemBase.java deleted file mode 100644 index f47d0dc..0000000 --- a/src/main/java/thetadev/constructionwand/items/ItemBase.java +++ /dev/null @@ -1,12 +0,0 @@ -package thetadev.constructionwand.items; - -import net.minecraft.world.item.Item; -import thetadev.constructionwand.ConstructionWand; - -public class ItemBase extends Item -{ - public ItemBase(String name, Properties properties) { - super(properties); - setRegistryName(ConstructionWand.MODID, name); - } -} diff --git a/src/main/java/thetadev/constructionwand/items/ModItems.java b/src/main/java/thetadev/constructionwand/items/ModItems.java index 14c3255..aabd39d 100644 --- a/src/main/java/thetadev/constructionwand/items/ModItems.java +++ b/src/main/java/thetadev/constructionwand/items/ModItems.java @@ -12,8 +12,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.registries.IForgeRegistry; -import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.*; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.crafting.RecipeWandUpgrade; @@ -23,38 +22,24 @@ import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWandBasic; import thetadev.constructionwand.items.wand.ItemWandInfinity; -import java.util.Arrays; -import java.util.HashSet; - @Mod.EventBusSubscriber(modid = ConstructionWand.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModItems { + public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ConstructionWand.MODID); + // Wands - public static final Item WAND_STONE = new ItemWandBasic("stone_wand", propWand(), Tiers.STONE); - public static final Item WAND_IRON = new ItemWandBasic("iron_wand", propWand(), Tiers.IRON); - public static final Item WAND_DIAMOND = new ItemWandBasic("diamond_wand", propWand(), Tiers.DIAMOND); - public static final Item WAND_INFINITY = new ItemWandInfinity("infinity_wand", propWand()); + public static final RegistryObject WAND_STONE = ITEMS.register("stone_wand", () -> new ItemWandBasic(propWand(), Tiers.STONE)); + public static final RegistryObject WAND_IRON = ITEMS.register("iron_wand", () -> new ItemWandBasic(propWand(), Tiers.IRON)); + public static final RegistryObject WAND_DIAMOND = ITEMS.register("diamond_wand", () -> new ItemWandBasic(propWand(), Tiers.DIAMOND)); + public static final RegistryObject WAND_INFINITY = ITEMS.register("infinity_wand", () -> new ItemWandInfinity(propWand())); // Cores - public static final Item CORE_ANGEL = new ItemCoreAngel("core_angel", propUpgrade()); - public static final Item CORE_DESTRUCTION = new ItemCoreDestruction("core_destruction", propUpgrade()); + public static final RegistryObject CORE_ANGEL = ITEMS.register("core_angel", () -> new ItemCoreAngel(propUpgrade())); + public static final RegistryObject CORE_DESTRUCTION = ITEMS.register("core_destruction", () -> new ItemCoreDestruction(propUpgrade())); // Collections - public static final Item[] WANDS = {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY}; - public static final Item[] CORES = {CORE_ANGEL, CORE_DESTRUCTION}; - public static final HashSet ALL_ITEMS = new HashSet<>(); - - - @SubscribeEvent - public static void registerItems(RegistryEvent.Register event) { - IForgeRegistry r = event.getRegistry(); - - r.registerAll(WANDS); - ALL_ITEMS.addAll(Arrays.asList(WANDS)); - - registerItem(r, CORE_ANGEL); - registerItem(r, CORE_DESTRUCTION); - } + public static final RegistryObject[] WANDS = new RegistryObject[] {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY}; + public static final RegistryObject[] CORES = new RegistryObject[] {CORE_ANGEL, CORE_DESTRUCTION}; public static Item.Properties propWand() { return new Item.Properties().tab(CreativeModeTab.TAB_TOOLS); @@ -64,11 +49,6 @@ public class ModItems return new Item.Properties().tab(CreativeModeTab.TAB_MISC).stacksTo(1); } - private static void registerItem(IForgeRegistry reg, Item item) { - reg.register(item); - ALL_ITEMS.add(item); - } - @SubscribeEvent public static void registerRecipeSerializers(RegistryEvent.Register> event) { IForgeRegistry> r = event.getRegistry(); @@ -77,7 +57,8 @@ public class ModItems @OnlyIn(Dist.CLIENT) public static void registerModelProperties() { - for(Item item : WANDS) { + for(RegistryObject itemSupplier : WANDS) { + Item item = itemSupplier.get(); ItemProperties.register( item, ConstructionWand.loc("using_core"), (stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 : @@ -90,7 +71,8 @@ public class ModItems public static void registerItemColors() { ItemColors colors = Minecraft.getInstance().getItemColors(); - for(Item item : WANDS) { + for(RegistryObject itemSupplier : WANDS) { + Item item = itemSupplier.get(); colors.register((stack, layer) -> (layer == 1 && stack.getItem() instanceof ItemWand) ? new WandOptions(stack).cores.get().getColor() : -1, item); } diff --git a/src/main/java/thetadev/constructionwand/items/core/ItemCore.java b/src/main/java/thetadev/constructionwand/items/core/ItemCore.java index fb3b93b..6b3362c 100644 --- a/src/main/java/thetadev/constructionwand/items/core/ItemCore.java +++ b/src/main/java/thetadev/constructionwand/items/core/ItemCore.java @@ -3,6 +3,7 @@ package thetadev.constructionwand.items.core; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; @@ -10,15 +11,14 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.api.IWandCore; -import thetadev.constructionwand.items.ItemBase; import javax.annotation.Nonnull; import java.util.List; -public abstract class ItemCore extends ItemBase implements IWandCore +public abstract class ItemCore extends Item implements IWandCore { - public ItemCore(String name, Properties properties) { - super(name, properties); + public ItemCore(Properties properties) { + super(properties); } @OnlyIn(Dist.CLIENT) diff --git a/src/main/java/thetadev/constructionwand/items/core/ItemCoreAngel.java b/src/main/java/thetadev/constructionwand/items/core/ItemCoreAngel.java index fc7a05a..5062761 100644 --- a/src/main/java/thetadev/constructionwand/items/core/ItemCoreAngel.java +++ b/src/main/java/thetadev/constructionwand/items/core/ItemCoreAngel.java @@ -5,8 +5,8 @@ import thetadev.constructionwand.wand.action.ActionAngel; public class ItemCoreAngel extends ItemCore { - public ItemCoreAngel(String name, Properties properties) { - super(name, properties); + public ItemCoreAngel(Properties properties) { + super(properties); } @Override diff --git a/src/main/java/thetadev/constructionwand/items/core/ItemCoreDestruction.java b/src/main/java/thetadev/constructionwand/items/core/ItemCoreDestruction.java index e243f06..bb2b5d3 100644 --- a/src/main/java/thetadev/constructionwand/items/core/ItemCoreDestruction.java +++ b/src/main/java/thetadev/constructionwand/items/core/ItemCoreDestruction.java @@ -5,8 +5,8 @@ import thetadev.constructionwand.wand.action.ActionDestruction; public class ItemCoreDestruction extends ItemCore { - public ItemCoreDestruction(String name, Properties properties) { - super(name, properties); + public ItemCoreDestruction(Properties properties) { + super(properties); } @Override diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java index 5e19cc7..4fa3529 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWand.java @@ -9,6 +9,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; @@ -25,17 +26,16 @@ import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.data.ICustomItemModel; import thetadev.constructionwand.data.ItemModelGenerator; -import thetadev.constructionwand.items.ItemBase; import thetadev.constructionwand.wand.WandJob; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -public abstract class ItemWand extends ItemBase implements ICustomItemModel +public abstract class ItemWand extends Item implements ICustomItemModel { - public ItemWand(String name, Properties properties) { - super(name, properties); + public ItemWand(Properties properties) { + super(properties); } @Nonnull diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java index 518954b..3db9d28 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWandBasic.java @@ -10,8 +10,8 @@ public class ItemWandBasic extends ItemWand { private final Tier tier; - public ItemWandBasic(String name, Properties properties, Tier tier) { - super(name, properties.durability(tier.getUses())); + public ItemWandBasic(Properties properties, Tier tier) { + super(properties.durability(tier.getUses())); this.tier = tier; } diff --git a/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java b/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java index 8907001..16bcc8a 100644 --- a/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java +++ b/src/main/java/thetadev/constructionwand/items/wand/ItemWandInfinity.java @@ -3,7 +3,7 @@ package thetadev.constructionwand.items.wand; public class ItemWandInfinity extends ItemWand { - public ItemWandInfinity(String name, Properties properties) { - super(name, properties.stacksTo(1).fireResistant()); + public ItemWandInfinity(Properties properties) { + super(properties.stacksTo(1).fireResistant()); } } diff --git a/src/main/java/thetadev/constructionwand/wand/WandJob.java b/src/main/java/thetadev/constructionwand/wand/WandJob.java index 313b6c8..b35f6f6 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandJob.java +++ b/src/main/java/thetadev/constructionwand/wand/WandJob.java @@ -74,7 +74,7 @@ public class WandJob public void getSnapshots() { int limit; // Infinity wand gets enhanced limit in creative mode - if(player.isCreative() && wandItem == ModItems.WAND_INFINITY) limit = ConfigServer.LIMIT_CREATIVE.get(); + if(player.isCreative() && wandItem == ModItems.WAND_INFINITY.get()) limit = ConfigServer.LIMIT_CREATIVE.get(); else limit = Math.min(wandItem.remainingDurability(wand), wandAction.getLimit(wand)); if(rayTraceResult.getType() == HitResult.Type.BLOCK) From 710a6a7ba15e16646aea2b57a0c67ab5746eda9b Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sat, 26 Mar 2022 00:17:52 +0100 Subject: [PATCH 25/35] remove superfluous inventory checks --- src/main/java/thetadev/constructionwand/basics/WandUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/thetadev/constructionwand/basics/WandUtil.java b/src/main/java/thetadev/constructionwand/basics/WandUtil.java index 418ec5c..aa98cbe 100644 --- a/src/main/java/thetadev/constructionwand/basics/WandUtil.java +++ b/src/main/java/thetadev/constructionwand/basics/WandUtil.java @@ -160,7 +160,7 @@ public class WandUtil List inventory = WandUtil.getFullInv(player); for(ItemStack stack : inventory) { - if(stack == null) continue; + if(stack == null || stack.isEmpty()) continue; if(WandUtil.stackEquals(stack, item)) { total += stack.getCount(); From aefb3e138b421063d1b193989688eca5384ee858 Mon Sep 17 00:00:00 2001 From: Theta-Dev Date: Sat, 26 Mar 2022 00:18:18 +0100 Subject: [PATCH 26/35] update Botania integration for MC1.18.1 --- build.gradle | 2 -- gradle.properties | 6 ++--- .../containers/ContainerRegistrar.java | 6 ++--- .../containers/handlers/HandlerBotania.java | 27 +++++++++++-------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 5b50303..546e606 100644 --- a/build.gradle +++ b/build.gradle @@ -104,14 +104,12 @@ dependencies { compileOnly fg.deobf("mezz.jei:${jei_version}:api") runtimeOnly fg.deobf("mezz.jei:${jei_version}") - /* compileOnly fg.deobf([ group: "vazkii.botania", name: "Botania", version: "${project.botania}", classifier: "api" ]) - */ } jar { diff --git a/gradle.properties b/gradle.properties index 40a8f6b..379cdfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,14 +5,14 @@ author=thetadev modid=constructionwand mcversion=1.18.1 -forgeversion=39.0.0 +forgeversion=39.1.0 mcp_channel=official mcp_mappings=1.18.1 # Source: https://maven.blamejared.com/vazkii/botania/Botania/ -# botania=1.16.2-405 +botania=1.18.1-429 # Source: https://dvs1.progwml6.com/files/maven/mezz/jei/ jei_version=jei-1.18.1:9.1.0.41 version_major=2 -version_minor=6 \ No newline at end of file +version_minor=7 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java index 4c3b8e6..e9e87f6 100644 --- a/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java +++ b/src/main/java/thetadev/constructionwand/containers/ContainerRegistrar.java @@ -1,6 +1,8 @@ package thetadev.constructionwand.containers; +import net.minecraftforge.fml.ModList; import thetadev.constructionwand.ConstructionWand; +import thetadev.constructionwand.containers.handlers.HandlerBotania; import thetadev.constructionwand.containers.handlers.HandlerBundle; import thetadev.constructionwand.containers.handlers.HandlerCapability; import thetadev.constructionwand.containers.handlers.HandlerShulkerbox; @@ -12,13 +14,9 @@ public class ContainerRegistrar ConstructionWand.instance.containerManager.register(new HandlerShulkerbox()); ConstructionWand.instance.containerManager.register(new HandlerBundle()); - /* - TODO: Reenable this when Botania gets ported to 1.17 - if(ModList.get().isLoaded("botania")) { ConstructionWand.instance.containerManager.register(new HandlerBotania()); ConstructionWand.LOGGER.info("Botania integration added"); } - */ } } diff --git a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java index 2d41e4b..8ffb651 100644 --- a/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java +++ b/src/main/java/thetadev/constructionwand/containers/handlers/HandlerBotania.java @@ -1,25 +1,28 @@ -/* -TODO: Reenable this when Botania gets ported to 1.17 - package thetadev.constructionwand.containers.handlers; -import net.minecraft.world.level.block.Block; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; import thetadev.constructionwand.api.IContainerHandler; +import vazkii.botania.api.BotaniaForgeCapabilities; import vazkii.botania.api.item.IBlockProvider; +import java.util.Optional; + public class HandlerBotania implements IContainerHandler { @Override public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { - return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() instanceof IBlockProvider; + return inventoryStack != null && inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).isPresent(); } @Override public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { - IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); - int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem())); + Optional provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve(); + if(provOptional.isEmpty()) return 0; + + IBlockProvider prov = provOptional.get(); + int provCount = prov.getBlockCount(player, inventoryStack, Block.byItem(itemStack.getItem())); if(provCount == -1) return Integer.MAX_VALUE; return provCount; @@ -27,10 +30,12 @@ public class HandlerBotania implements IContainerHandler @Override public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { - IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); - if(prov.provideBlock(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem()), true)) + Optional provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve(); + if(provOptional.isEmpty()) return 0; + + IBlockProvider prov = provOptional.get(); + if(prov.provideBlock(player, inventoryStack, Block.byItem(itemStack.getItem()), true)) return 0; return count; } -} -*/ \ No newline at end of file +} \ No newline at end of file From d69901c0fa2798329f282d91c699a675dce18683 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 12 Feb 2023 17:47:50 +0100 Subject: [PATCH 27/35] fix: wands not breaking when out of durability --- src/main/java/thetadev/constructionwand/wand/WandJob.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/thetadev/constructionwand/wand/WandJob.java b/src/main/java/thetadev/constructionwand/wand/WandJob.java index b35f6f6..b294afa 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandJob.java +++ b/src/main/java/thetadev/constructionwand/wand/WandJob.java @@ -1,8 +1,8 @@ package thetadev.constructionwand.wand; import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; @@ -99,7 +99,7 @@ public class WandJob // If the item cant be taken, undo the placement if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) { executed.add(snapshot); - wand.hurt(1, player.getRandom(), (ServerPlayer) player); + wand.hurtAndBreak(1, player, e -> e.broadcastBreakEvent(InteractionHand.MAIN_HAND)); } else { ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " + From a9f703e28f4f203230878af07cd4f5687975564b Mon Sep 17 00:00:00 2001 From: gjeodnd12165 <61226524+gjeodnd12165@users.noreply.github.com> Date: Mon, 18 Apr 2022 17:17:01 +0900 Subject: [PATCH 28/35] Create ko_kr.json --- .../assets/constructionwand/lang/ko_kr.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/ko_kr.json diff --git a/src/main/resources/assets/constructionwand/lang/ko_kr.json b/src/main/resources/assets/constructionwand/lang/ko_kr.json new file mode 100644 index 0000000..47df0b2 --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/ko_kr.json @@ -0,0 +1,70 @@ +{ + "item.constructionwand.stone_wand": "돌 완드", + "item.constructionwand.iron_wand": "철 완드", + "item.constructionwand.diamond_wand": "다이아몬드 완드", + "item.constructionwand.infinity_wand": "무한의 완드", + "item.constructionwand.core_angel": "천사 완드 코어", + "item.constructionwand.core_destruction": "파괴 완드 코어", + + "constructionwand.tooltip.blocks": "최대. %d 블록", + "constructionwand.tooltip.shift": "[SHIFT]를 누르세요.", + "constructionwand.tooltip.cores": "완드 코어:", + "constructionwand.tooltip.core_tip": "조합창에서 코어와 완드를 합치세요.", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "생성 코어", + "constructionwand.option.cores.constructionwand:default.desc": "당신 쪽으로 건물을 확장합니다.", + "constructionwand.option.cores.constructionwand:core_angel": "§6천사 코어", + "constructionwand.option.cores.constructionwand:core_angel.desc": "블록 뒤와 공중에 배치합니다.", + "constructionwand.option.cores.constructionwand:core_destruction": "§c파괴 코어", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "당신 쪽의 블록을 파괴합니다.", + + "constructionwand.option.lock": "제한: ", + "constructionwand.option.lock.horizontal": "§a오른쪽/왼쪽", + "constructionwand.option.lock.horizontal.desc": "원래 블록의 앞에 수평한 열을 만듭니다.", + "constructionwand.option.lock.vertical": "§a위/아래", + "constructionwand.option.lock.vertical.desc": "원래 블록의 앞에 수직한 열을 만듭니다.", + "constructionwand.option.lock.northsouth": "§6북쪽/남쪽", + "constructionwand.option.lock.northsouth.desc": "원래 블록의 위에 북/남 방향으로 행을 만듭니다.", + "constructionwand.option.lock.eastwest": "§6동쪽/서쪽", + "constructionwand.option.lock.eastwest.desc": "원래 블록의 위에 동/서 방향으로 행을 만듭니다.", + "constructionwand.option.lock.nolock": "§c없음", + "constructionwand.option.lock.nolock.desc": "원래 블록의 어느 방향으로도 확장합니다.", + + "constructionwand.option.direction": "방향: ", + "constructionwand.option.direction.target": "§6대상", + "constructionwand.option.direction.target.desc": "대상 블록과 같은 방향으로 블록을 배치합니다.", + "constructionwand.option.direction.player": "§a플레이어", + "constructionwand.option.direction.player.desc": "플레이어를 향해 블록을 배치합니다.", + + "constructionwand.option.replace": "재배치: ", + "constructionwand.option.replace.yes": "§a예", + "constructionwand.option.replace.yes.desc": "유체, 눈, 키 큰 잔디와 같은 특정 블록을 교체합니다.", + "constructionwand.option.replace.no": "§c아니오", + "constructionwand.option.replace.no.desc": "블록을 재배치하지 않습니다.", + + "constructionwand.option.match": "비교: ", + "constructionwand.option.match.exact": "§a정확", + "constructionwand.option.match.exact.desc": "완전히 같은 블록만 확장합니다.", + "constructionwand.option.match.similar": "§6유사", + "constructionwand.option.match.similar.desc": "비슷한 블록(흙/잔디)을 똑같이 취급합니다.", + "constructionwand.option.match.any": "§c아무거나", + "constructionwand.option.match.any.desc": "아무 블록이나 확장합니다.", + + "constructionwand.option.random": "무작위: ", + "constructionwand.option.random.yes": "§a예", + "constructionwand.option.random.yes.desc": "핫바에 있는 블록 중 무작위적으로 배치합니다.", + "constructionwand.option.random.no": "§c아니오", + "constructionwand.option.random.no.desc": "배치할 블록을 무작위적으로 하지 않습니다.", + + "constructionwand.description.wand": "%1$s는 당신 쪽으로 최대 %2$d 블록까지 배치할 수 있고, %3$s 지속됩니다.\n\n%5$s을(를) 누르고 스크롤 하여 배치 제한을 바꾸세요 (수평, 수직, 북쪽/남쪽, 동쪽/서쪽, 제한 없음).\n\n%6$s§9+우클릭§0으로 옵션 스크린을 여세요.\n\n§5§n실행 취소§0§r\n블록을 보면서 §9웅크리기+§0%4$s를 누르고 있으면 마지막으로 배치했던 블록들이 녹색 테두리로 표시됩니다. 그 중 아무거나 §9S웅크리기+§0%4$s§9+우클릭§0 하면 그 작업을 실행 취소하고, 모든 아이템을 돌려줍니다. 파괴 코어를 사용했다면, 블록들을 복원합니다.\n\n§5§n컨테이너§0§r\n셜커 상자, 꾸러미, 그리고 다른 모드의 컨테이너들은 완드에 건설 블록을 제공할 수 있습니다.\n\n§5§n보조손 우선도§0§r\n보조 손에 블록을 가지고 있으면 보고 있는 블록을 배치하는 대신에 보조 손의 블록을 배치할 것입니다.", + "constructionwand.description.durability.limited": "%d 블록 만큼", + "constructionwand.description.durability.unlimited": "영원히", + "constructionwand.description.key.sneak": "웅크리기", + "constructionwand.description.key.sneak_opt": "웅크리기+%s", + "constructionwand.description.core": "§5§n설치§0§r\n새 코어를 완드와 함께 조합창에 넣어 설치하세요. 코어 간에 전환하려면 %s 키를 누른 상태에서 완드로 빈 공간을 좌클릭하거나 옵션 화면을 사용하십시오.", + "constructionwand.description.core_angel": "엔젤 코어는 마주보고 있는 블록(또는 블록 행)의 반대쪽에 블록을 배치합니다. 최대 거리는 완드의 티어에 따라 다릅니다. 빈 공간을 우클릭하면 공중에 블록을 배치할 수 있습니다. 그렇게 하려면 보조 손에 배치하려는 블록이 있어야 합니다.", + "constructionwand.description.core_destruction": "파괴 코어는 당신 쪽의 (타일 엔티티가 없는)블록을 파괴합니다. 최대 블록 수는 완드의 티어에 따라 다릅니다. 파괴된 블록은 공허로 사라지며 실수를 했다면 실행 취소 기능을 사용할 수 있습니다.", + + "stat.constructionwand.use_wand": "완드로 배치한 블록 수" + } From e83cae7ae21022c8c358b58323a29805442364c6 Mon Sep 17 00:00:00 2001 From: mango_buff <106330917+buff-mango@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:53:40 +0800 Subject: [PATCH 29/35] Create zh_cn.json --- .../assets/constructionwand/lang/zh_cn.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/zh_cn.json diff --git a/src/main/resources/assets/constructionwand/lang/zh_cn.json b/src/main/resources/assets/constructionwand/lang/zh_cn.json new file mode 100644 index 0000000..013bf8a --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/zh_cn.json @@ -0,0 +1,70 @@ +{ + "item.constructionwand.stone_wand": "石制手杖", + "item.constructionwand.iron_wand": "铁制手杖", + "item.constructionwand.diamond_wand": "钻石手杖", + "item.constructionwand.infinity_wand": "无尽手杖", + "item.constructionwand.core_angel": "天使手杖核心", + "item.constructionwand.core_destruction": "破坏手杖核心", + + "constructionwand.tooltip.blocks": "最多放置%d个方块", + "constructionwand.tooltip.shift": "按 [SHIFT]", + "constructionwand.tooltip.cores": "手杖核心:", + "constructionwand.tooltip.core_tip": "将手杖核心与手杖组合在一起", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "建筑核心", + "constructionwand.option.cores.constructionwand:default.desc": "在面向你的一侧放置方块", + "constructionwand.option.cores.constructionwand:core_angel": "§6天使核心", + "constructionwand.option.cores.constructionwand:core_angel.desc": "在面向你的方块的背面放置方块,还可以悬空放置方块", + "constructionwand.option.cores.constructionwand:core_destruction": "§c毁灭核心", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "破坏面向你一侧的方块", + + "constructionwand.option.lock": "锁定: ", + "constructionwand.option.lock.horizontal": "§a左 / 右", + "constructionwand.option.lock.horizontal.desc": "在起始方块的前面延伸一行水平方块", + "constructionwand.option.lock.vertical": "§a上 / 下", + "constructionwand.option.lock.vertical.desc": "在起始方块的前面延伸一列竖直方块", + "constructionwand.option.lock.northsouth": "§6南 / 北", + "constructionwand.option.lock.northsouth.desc": "在起始方块的上面,向南 / 北方向延伸一行", + "constructionwand.option.lock.eastwest": "§6东 / 西", + "constructionwand.option.lock.eastwest.desc": "在起始方块的上面,向东 / 西方向延伸一行", + "constructionwand.option.lock.nolock": "§c无", + "constructionwand.option.lock.nolock.desc": "从原始块的任意一面延伸", + + "constructionwand.option.direction": "方向: ", + "constructionwand.option.direction.target": "§6目标", + "constructionwand.option.direction.target.desc": "放置与的方块方向与目标方块的方向相同", + "constructionwand.option.direction.player": "§a玩家", + "constructionwand.option.direction.player.desc": "放置的方块面向玩家", + + "constructionwand.option.replace": "替换: ", + "constructionwand.option.replace.yes": "§a是", + "constructionwand.option.replace.yes.desc": "替换某些方块,如液体、雪、高草丛", + "constructionwand.option.replace.no": "§c否", + "constructionwand.option.replace.no.desc": "不替换方块", + + "constructionwand.option.match": "匹配: ", + "constructionwand.option.match.exact": "§a精确", + "constructionwand.option.match.exact.desc": "仅放置完全相同的方块", + "constructionwand.option.match.similar": "§6模糊", + "constructionwand.option.match.similar.desc": "相似的方块被认为是相同的(草方块 / 泥土类型)", + "constructionwand.option.match.any": "§c任意", + "constructionwand.option.match.any.desc": "放置任何方块", + + "constructionwand.option.random": "随机: ", + "constructionwand.option.random.yes": "§a是", + "constructionwand.option.random.yes.desc": "随机放置快捷栏中的方块", + "constructionwand.option.random.no": "§c否", + "constructionwand.option.random.no.desc": "不会随机放置方块", + + "constructionwand.description.wand": "%1$s可以在建筑物面向你的一侧放置最多%2$d个方块,持续时间为%3$s。\n\n按住%5$s并滚动以更改放置限制(水平、垂直、北/南、东/西、无锁定)。\n\n在选项配置GUI上打开%6$s§9+右键单击§0。\n\n§5§nUNDO§0§r\n在查看方块时向下折叠§9Sneak+§0%4$s将显示你放置的最后一个方块,并在其周围加上绿色边框。§9潜行+§0%4$s§9+右键单击其中任何一个方块将撤消操作,并将所有以此法放置的方块重返至玩家背包。如果你使用了破坏核心,它将恢复方块。\n\n§5§n容器§0§r\n潜影盒、收纳袋和许多其它模组存在于玩家背包内的容器都可以为建筑手杖提供构建所需的方块。\n\n§5§非即时优先级§0§r\n如果玩家在使用手杖时副手栏持有所需方块将被放置,而不是只是在你的手里放着。", + "constructionwand.description.durability.limited": "需要%d方块", + "constructionwand.description.durability.unlimited": "无限", + "constructionwand.description.key.sneak": "潜行", + "constructionwand.description.key.sneak_opt": "潜行+%s", + "constructionwand.description.core": "§5§n安装§0§r\n将新的手杖核心与你的手杖一起放入工作台中进行组装。如果你想要在核心功能之间切换,请按住%s并用手杖左键单击空地或使用手杖的选项配置GUI。", + "constructionwand.description.core_angel": "天使核心可将一个方块放置在你所面对的方块(或一排方块)的对面。最大距离取决于手杖材质。在空地上手持手杖并单击鼠标右键即可在空中放置方块。要做到这一点。你需要将想要被在空中放置的方块放在你的副手栏中。", + "constructionwand.description.core_destruction": "毁灭核心会破坏面向你一侧的方块(破坏时被破坏的方块不可存在实体)。最大破坏方块数取决于手杖材质。被使用毁灭核心破坏的方块会消失。如果你只是不小心使用了毁灭核心。可以使用“撤消”功能以撤回被破坏并消失的物品返回原处。", + + "stat.constructionwand.use_wand": "使用建筑手杖所放置的方块" +} From d006a24ab722744f5d5d67750a9523b7e5d9a301 Mon Sep 17 00:00:00 2001 From: FITFC <101124415+FITFC@users.noreply.github.com> Date: Sat, 29 Oct 2022 17:19:59 -0500 Subject: [PATCH 30/35] added pt_br.json --- .../assets/constructionwand/lang/pt_br.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/pt_br.json diff --git a/src/main/resources/assets/constructionwand/lang/pt_br.json b/src/main/resources/assets/constructionwand/lang/pt_br.json new file mode 100644 index 0000000..b4c06aa --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/pt_br.json @@ -0,0 +1,70 @@ +{ + "item.constructionwand.stone_wand": "Varinha de pedra", + "item.constructionwand.iron_wand": "Varinha de ferro", + "item.constructionwand.diamond_wand": "diamondWand", + "item.constructionwand.infinity_wand": "Varinha infinita", + "item.constructionwand.core_angel": "Angel Wand Core", + "item.constructionwand.core_destruction": "Destruction Wand Core", + + "constructionwand.tooltip.blocks": "Max. %d blocos", + "constructionwand.tooltip.shift": "Pressione Shift]", + "constructionwand.tooltip.cores": "Núcleos de varinhas:", + "constructionwand.tooltip.core_tip": "Combine o núcleo com sua varinha em uma grade de criação", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "Núcleo de construção", + "constructionwand.option.cores.constructionwand:default.desc": "Estender seu prédio do lado de frente para você", + "constructionwand.option.cores.constructionwand:core_angel": "§6angelCore", + "constructionwand.option.cores.constructionwand:core_angel.desc": "Coloque atrás dos quarteirões e no meio do ar", + "constructionwand.option.cores.constructionwand:core_destruction": "§cNúcleo de destruição", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "Destrói blocos do lado de frente para você", + + "constructionwand.option.lock": "Restrição: ", + "constructionwand.option.lock.horizontal": "§aEsquerda direita", + "constructionwand.option.lock.horizontal.desc": "Construa uma coluna horizontal em frente ao bloco original", + "constructionwand.option.lock.vertical": "§aCima baixo", + "constructionwand.option.lock.vertical.desc": "Construa uma coluna vertical em frente ao bloco original", + "constructionwand.option.lock.northsouth": "§6Norte Sul", + "constructionwand.option.lock.northsouth.desc": "Construa uma linha na direção N/s no topo do bloco original", + "constructionwand.option.lock.eastwest": "§6Leste Oeste", + "constructionwand.option.lock.eastwest.desc": "Construa uma linha na direção E/W no topo do bloco original", + "constructionwand.option.lock.nolock": "§cNenhum", + "constructionwand.option.lock.nolock.desc": "Estender de qualquer lado do bloco original", + + "constructionwand.option.direction": "Direção: ", + "constructionwand.option.direction.target": "§6Alvo", + "constructionwand.option.direction.target.desc": "Coloque blocos com a mesma direção que o bloco de destino", + "constructionwand.option.direction.player": "§aJogadora", + "constructionwand.option.direction.player.desc": "Coloque blocos de frente para o jogador", + + "constructionwand.option.replace": "Substituição: ", + "constructionwand.option.replace.yes": "§aSim", + "constructionwand.option.replace.yes.desc": "Substitua certos blocos como fluidos, neve e capim alto", + "constructionwand.option.replace.no": "§cNão", + "constructionwand.option.replace.no.desc": "Não substitua blocos", + + "constructionwand.option.match": "Coincidindo: ", + "constructionwand.option.match.exact": "§aExata", + "constructionwand.option.match.exact.desc": "Estender apenas blocos que são exatamente iguais", + "constructionwand.option.match.similar": "§6Semelhante", + "constructionwand.option.match.similar.desc": "Tratar blocos semelhantes (tipos de sujeira/grama) igualmente", + "constructionwand.option.match.any": "§cAlguma", + "constructionwand.option.match.any.desc": "Estender qualquer bloco", + + "constructionwand.option.random": "Aleatório: ", + "constructionwand.option.random.yes": "§aSim", + "constructionwand.option.random.yes.desc": "Coloque blocos aleatórios presentes em seu hotbar", + "constructionwand.option.random.no": "§cNão", + "constructionwand.option.random.no.desc": "Não randomize blocos colocados", + + "constructionwand.description.wand": "o %1$s pode colocar até %2$d bloqueios ao lado de um prédio de frente para você e dura %3$s.\n\nCalma %5$s e role para alterar a restrição de posicionamento (horizontal, vertical, norte/sul, leste/oeste, sem fechadura).\n\nAbra a tela de opção com %6$s§9+Clique com o botão direito do mouse§0.\n\n§5§nDESFAZER§0§r\nMantendo pressionada §9Esgueirar-se+§0%4$s Enquanto olha para um bloco, mostrará os últimos blocos que você colocou com uma borda verde ao redor deles. §9Esgueirar-se+§0%4$s§9+Certa clicando§0 Qualquer um deles desfazerá a operação, oferecendo todos os itens de volta.Se você usou o núcleo de destruição, ele restaurará os blocos.\n\n§5§nRECIPIENTES§0§r\nCaixas Shulker, pacotes e muitos contêineres de outros mods podem fornecer blocos de construção para a varinha.\n\n§5§nPrioridade imediata§0§r\nTer blocos em sua mão os colocará em vez do bloco que você está olhando.", + "constructionwand.description.durability.limited": "por %d blocos", + "constructionwand.description.durability.unlimited": "para todo sempre", + "constructionwand.description.key.sneak": "Esgueirar-se", + "constructionwand.description.key.sneak_opt": "Esgueirar-se+%s", + "constructionwand.description.core": "§5§nINSTALAÇÃO§0§r\nColoque seu novo núcleo junto com sua varinha em uma grade de criação para instalá -la.Para alternar entre núcleos, mantenha pressionado %s e o clique esquerdo, esvazie o espaço com sua varinha ou use a tela de opção.", + "constructionwand.description.core_angel": "O núcleo do anjo coloca um bloco no lado oposto do bloco (ou fileira de blocos) que você está enfrentando.A distância máxima depende da camada de varinha.Clique com o botão direito do mouse em espaço vazio para colocar um bloco no ar.Para fazer isso, você precisará ter o bloco que deseja colocar em sua mão.", + "constructionwand.description.core_destruction": "O núcleo de destruição destrói blocos (sem entidades de ladrilhos) do lado de frente para você.O número máximo de blocos depende da camada de varinha.Blocos destruídos desaparecem no vazio, você pode usar o recurso de desfazer se cometer um erro.", + + "stat.constructionwand.use_wand": "Blocos colocados usando varinha" + } \ No newline at end of file From 2497f85800fcf21348d8b8a0a68e28f38c2e58db Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 12 Feb 2023 18:06:17 +0100 Subject: [PATCH 31/35] bump version to 2.9 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 379cdfb..7531e6a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ botania=1.18.1-429 jei_version=jei-1.18.1:9.1.0.41 version_major=2 -version_minor=7 \ No newline at end of file +version_minor=9 \ No newline at end of file From dc21a8882f70b0a9734e9e51c09cfc7582e428de Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 1 Jun 2023 14:30:44 +0200 Subject: [PATCH 32/35] update translations --- .../assets/constructionwand/lang/ru_ru.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/ru_ru.json diff --git a/src/main/resources/assets/constructionwand/lang/ru_ru.json b/src/main/resources/assets/constructionwand/lang/ru_ru.json new file mode 100644 index 0000000..5e7ec8c --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/ru_ru.json @@ -0,0 +1,70 @@ +{ + "item.constructionwand.stone_wand": "Каменный жезл", + "item.constructionwand.iron_wand": "Железный жезл", + "item.constructionwand.diamond_wand": "Алмазный жезл", + "item.constructionwand.infinity_wand": "Бесконечный жезл", + "item.constructionwand.core_angel": "Ангельское ядро для жезла", + "item.constructionwand.core_destruction": "Ядро разрушения для жезла", + + "constructionwand.tooltip.blocks": "Максимум %d блоков", + "constructionwand.tooltip.shift": "Нажмите [SHIFT]", + "constructionwand.tooltip.cores": "Ядер жезла:", + "constructionwand.tooltip.core_tip": "Объедините ядро со своим жезлом в сетке создания.", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "Ядро строительства", + "constructionwand.option.cores.constructionwand:default.desc": "Расширяйте свои строения на стороне, обращённой к Вам.", + "constructionwand.option.cores.constructionwand:core_angel": "§6Ангельское ядро", + "constructionwand.option.cores.constructionwand:core_angel.desc": "Размещает за блоками и в воздухе.", + "constructionwand.option.cores.constructionwand:core_destruction": "§cЯдро разрушения", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "Уничтожает блоки на стороне, обращённой к Вам.", + + "constructionwand.option.lock": "Ограничение: ", + "constructionwand.option.lock.horizontal": "§aВлево/Вправо", + "constructionwand.option.lock.horizontal.desc": "Строить горизонтальную колонну перед основным блоком.", + "constructionwand.option.lock.vertical": "§aВверх/Вниз", + "constructionwand.option.lock.vertical.desc": "Строить вертикальную колонну перед основным блоком.", + "constructionwand.option.lock.northsouth": "§6Север/Юг", + "constructionwand.option.lock.northsouth.desc": "Строить ряд в С/Ю направлении непосредственно за основным блоком.", + "constructionwand.option.lock.eastwest": "§6Восток/Запад", + "constructionwand.option.lock.eastwest.desc": "Строить ряд в В/З направлении непосредственно за основным блоком.", + "constructionwand.option.lock.nolock": "§cНичего", + "constructionwand.option.lock.nolock.desc": "Расширять с любой стороны основного блока.", + + "constructionwand.option.direction": "Направление: ", + "constructionwand.option.direction.target": "§6Цель", + "constructionwand.option.direction.target.desc": "Размещать блоки с таким же направлением как целевой блок.", + "constructionwand.option.direction.player": "§aИгрок", + "constructionwand.option.direction.player.desc": "Размещать блоки, обращённые к игроку.", + + "constructionwand.option.replace": "Замена: ", + "constructionwand.option.replace.yes": "§aДа", + "constructionwand.option.replace.yes.desc": "Заменять некоторые блоки как жидкости, снег и высокорослая трава.", + "constructionwand.option.replace.no": "§cНет", + "constructionwand.option.replace.no.desc": "Не заменять блоки.", + + "constructionwand.option.match": "Совпадение: ", + "constructionwand.option.match.exact": "§aТочное", + "constructionwand.option.match.exact.desc": "Расширять только абсолютно одинаковые блоки.", + "constructionwand.option.match.similar": "§6Похожее", + "constructionwand.option.match.similar.desc": "Подносить аналогичные блоки (пример: земля/трава) поровну.", + "constructionwand.option.match.any": "§cНикакое", + "constructionwand.option.match.any.desc": "Расширять любой блок.", + + "constructionwand.option.random": "Случайно: ", + "constructionwand.option.random.yes": "§aДа", + "constructionwand.option.random.yes.desc": "Размещать случайные блоки, имеющиеся в Вашей горячей панели.", + "constructionwand.option.random.no": "§cНет", + "constructionwand.option.random.no.desc": "Не располагать блоки в случайном порядке.", + + "constructionwand.description.wand": "%1$s может размещать до %2$d блоков сбоку от строения, обращённое к Вам и его хватит на %3$s блоков.\n\nУдерживайте %5$s и прокрутите колёсиком для изменения ограничения по размещении (Горизонтально, Вертикально, Север/Юг, Восток/Запад, Без ограничивания).\n\nОткройте экран настроек при помощи %6$s§9+щелчок правой кнопкой мыши§0.\n\n§5§nОТМЕНА§0§r\nУдерживайте §9Приседание+§0%4$s пока смотрите на блоки, установленные Вами, они будут выделены зелёным контуром. §9Приседание+§0%4$s§9+щелчок правой кнопкой мыши§0 на любой из них отменит операцию, вернув Вам все предметы обратно. Если использовать Ядро разрушения, то он вернёт блоки.\n\n§5§nКОНТЕЙНЕР§0§r\nШалкеровые ящики, мешки и множество контейнеров из других модов могут предоставлять строительные блоки в жезл.\n\n§5§nПРИОРИТЕТ ЛЕВОЙ РУКИ§0§r\nЕсли у Вас в левой руке находятся блоки, то они будут размещаться вместо блока, на который Вы смотрите.", + "constructionwand.description.durability.limited": "на %d блоков", + "constructionwand.description.durability.unlimited": "вечно", + "constructionwand.description.key.sneak": "Приседание", + "constructionwand.description.key.sneak_opt": "Приседание+%s", + "constructionwand.description.core": "§5§nУСТАНОВКА§0§r\nПоложите своё новое ядро вместе со своим жезлом в сетку создания для его установки. Для того, чтобы переключаться между ядрами, удерживайте %s и нажмите левую кнопку мыши по пустому пространству с жезлом в руке или используйте экран настроек.", + "constructionwand.description.core_angel": "Ангельское ядро размещает блоки на противоположной стороне блока (или ряда блоков), обращённые к Вам. Максимальное расстояние зависит от уровня жезла. Щелчок правой кнопкой мыши по пустому воздуху разместит блок в воздухе. Чтобы это сделать, Вам нужно иметь необходимые блоки в левой руке, чтобы разместить их.", + "constructionwand.description.core_destruction": "Ядро разрушения разрушает блоки (не функциональные блоки), обращённые к Вам. Максимально количество блоков зависит от уровня жезла. Разрушенные блоки исчезают в пустоту, можно использовать функцию отмены в случае допущенной ошибки.", + + "stat.constructionwand.use_wand": "Блоки, размещённые при помощи Жезла" +} From ce0012dc3ce624037e958b8a0bffdc0103af0623 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 24 Mar 2023 23:33:28 +0100 Subject: [PATCH 33/35] fix: 1-block previews on consecutive placements --- .../constructionwand/client/RenderBlockPreview.java | 8 ++++++-- src/main/java/thetadev/constructionwand/wand/WandJob.java | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java index b8dec20..56797eb 100644 --- a/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java +++ b/src/main/java/thetadev/constructionwand/client/RenderBlockPreview.java @@ -22,7 +22,7 @@ import java.util.Set; public class RenderBlockPreview { - public WandJob wandJob; + private WandJob wandJob; public Set undoBlocks; @SubscribeEvent @@ -39,7 +39,11 @@ public class RenderBlockPreview if(wand == null) return; if(!(player.isCrouching() && ClientEvents.isOptKeyDown())) { - if(wandJob == null || !compareRTR(wandJob.rayTraceResult, rtr) || !(wandJob.wand.equals(wand))) { + // Use cached wandJob for previews of the same target pos/dir + // Exception: always update if blockCount < 2 to prevent 1-block previews when block updates + // from the last placement are lagging + if(wandJob == null || !compareRTR(wandJob.rayTraceResult, rtr) || !(wandJob.wand.equals(wand)) + || wandJob.blockCount() < 2) { wandJob = ItemWand.getWandJob(player, player.level, rtr, wand); } blocks = wandJob.getBlockPositions(); diff --git a/src/main/java/thetadev/constructionwand/wand/WandJob.java b/src/main/java/thetadev/constructionwand/wand/WandJob.java index b294afa..0eb6269 100644 --- a/src/main/java/thetadev/constructionwand/wand/WandJob.java +++ b/src/main/java/thetadev/constructionwand/wand/WandJob.java @@ -87,6 +87,10 @@ public class WandJob return placeSnapshots.stream().map(ISnapshot::getPos).collect(Collectors.toSet()); } + public int blockCount() { + return placeSnapshots.size(); + } + public boolean doIt() { ArrayList executed = new ArrayList<>(); From 31bfbdb7f1e4debd7313e32d379683862c56a60d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 1 Jun 2023 13:52:40 +0200 Subject: [PATCH 34/35] fix: #76 Logic Issue with Placement --- gradle.properties | 2 +- .../constructionwand/wand/undo/PlaceSnapshot.java | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7531e6a..1d83096 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ botania=1.18.1-429 jei_version=jei-1.18.1:9.1.0.41 version_major=2 -version_minor=9 \ No newline at end of file +version_minor=11 \ No newline at end of file diff --git a/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java b/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java index ca17038..732c2b0 100644 --- a/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java +++ b/src/main/java/thetadev/constructionwand/wand/undo/PlaceSnapshot.java @@ -6,8 +6,6 @@ import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.Property; @@ -100,7 +98,7 @@ public class PlaceSnapshot implements ISnapshot // Can block be placed? BlockState blockState = item.getBlock().getStateForPlacement(ctx); - if(blockState == null) return null; + if(blockState == null || !blockState.canSurvive(world, pos)) return null; // Forbidden Tile Entity? if(!WandUtil.isTEAllowed(blockState)) return null; @@ -108,11 +106,6 @@ public class PlaceSnapshot implements ISnapshot // No entities colliding? if(WandUtil.entitiesCollidingWithBlock(world, blockState, pos)) return null; - // Adjust blockstate to neighbors - // TODO: verify that - blockState = Block.updateFromNeighbourShapes(blockState, world, pos); - if(blockState.getBlock() == Blocks.AIR || !blockState.canSurvive(world, pos)) return null; - // Copy block properties from supporting block if(targetMode && supportingBlock != null) { // Block properties to be copied (alignment/rotation properties) From 5d4fe7454a8fff37f9bff4499e2bd5661c96c017 Mon Sep 17 00:00:00 2001 From: RuyaSavascisi Date: Tue, 16 Jul 2024 09:27:21 +0300 Subject: [PATCH 35/35] tr_tr.json Turkish Localization for the mod --- .../assets/constructionwand/lang/tr_tr.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/resources/assets/constructionwand/lang/tr_tr.json diff --git a/src/main/resources/assets/constructionwand/lang/tr_tr.json b/src/main/resources/assets/constructionwand/lang/tr_tr.json new file mode 100644 index 0000000..05f5044 --- /dev/null +++ b/src/main/resources/assets/constructionwand/lang/tr_tr.json @@ -0,0 +1,70 @@ +{ + "item.constructionwand.stone_wand": "Taş Asa", + "item.constructionwand.iron_wand": "Demir Asa", + "item.constructionwand.diamond_wand": "Elmas Asa", + "item.constructionwand.infinity_wand": "Sonsuzluk Asası", + "item.constructionwand.core_angel": "Melek Asa Çekirdeği", + "item.constructionwand.core_destruction": "Yıkım Asa Çekirdeği", + + "constructionwand.tooltip.blocks": "Maks. %d blok", + "constructionwand.tooltip.shift": "[SHIFT] bas", + "constructionwand.tooltip.cores": "Asa çekirdekleri:", + "constructionwand.tooltip.core_tip": "Çekirdeği asanızla birlikte üretim ızgarasında birleştirin", + + "constructionwand.option.cores": "", + "constructionwand.option.cores.constructionwand:default": "İnşa Çekirdeği", + "constructionwand.option.cores.constructionwand:default.desc": "Yapınızın size bakan tarafını uzatır", + "constructionwand.option.cores.constructionwand:core_angel": "§6Melek Çekirdeği", + "constructionwand.option.cores.constructionwand:core_angel.desc": "Blokların arkasına ve havaya yerleştirir", + "constructionwand.option.cores.constructionwand:core_destruction": "§cYıkım Çekirdeği", + "constructionwand.option.cores.constructionwand:core_destruction.desc": "Size bakan taraftaki blokları yok eder", + + "constructionwand.option.lock": "Sınırlama: ", + "constructionwand.option.lock.horizontal": "§aSol/Sağ", + "constructionwand.option.lock.horizontal.desc": "Orijinal bloğun önüne yatay bir sütun oluşturur", + "constructionwand.option.lock.vertical": "§aYukarı/Aşağı", + "constructionwand.option.lock.vertical.desc": "Orijinal bloğun önünde dikey bir sütun oluşturur", + "constructionwand.option.lock.northsouth": "§6Kuzey/Güney", + "constructionwand.option.lock.northsouth.desc": "Orijinal bloğun üstüne K/G yönünde bir sıra oluşturun", + "constructionwand.option.lock.eastwest": "§6Doğu/Batı", + "constructionwand.option.lock.eastwest.desc": "Orijinal bloğun üstüne D/B yönünde bir sıra oluşturur", + "constructionwand.option.lock.nolock": "§cYok", + "constructionwand.option.lock.nolock.desc": "Orijinal bloğun herhangi bir tarafından uzatır", + + "constructionwand.option.direction": "Yön: ", + "constructionwand.option.direction.target": "§6Hedef", + "constructionwand.option.direction.target.desc": "Blokları hedef blokla aynı yönde yerleştirir", + "constructionwand.option.direction.player": "§aOyuncu", + "constructionwand.option.direction.player.desc": "Blokları oyuncuya bakacak şekilde yerleştirir", + + "constructionwand.option.replace": "Değiştirme: ", + "constructionwand.option.replace.yes": "§aEvet", + "constructionwand.option.replace.yes.desc": "Sıvılar, kar ve uzun otlar gibi belirli blokları değiştirir", + "constructionwand.option.replace.no": "§cHayır", + "constructionwand.option.replace.no.desc": "Blokları değiştirmez", + + "constructionwand.option.match": "Eşleşen: ", + "constructionwand.option.match.exact": "§aAynı", + "constructionwand.option.match.exact.desc": "Yalnızca tamamen aynı olan blokları uzatır", + "constructionwand.option.match.similar": "§6Benzer", + "constructionwand.option.match.similar.desc": "Benzer bloklara (toprak/çimen türleri) eşit davranır", + "constructionwand.option.match.any": "§cHerhangi", + "constructionwand.option.match.any.desc": "Herhangi bir bloğu uzatır", + + "constructionwand.option.random": "Rastgele: ", + "constructionwand.option.random.yes": "§aEvet", + "constructionwand.option.random.yes.desc": "Hotbar'ınızdan rastgele bloklar yerleştirir", + "constructionwand.option.random.no": "§cHayır", + "constructionwand.option.random.no.desc": "Yerleştirilen blokları rastgeleleştirmez", + + "constructionwand.description.wand": "%1$s, bir yapının size bakan tarafına en fazla %2$d blok yerleştirebilir ve %3$s dayanıklılığı vardır.\n\n%5$s tuşunu basılı tutun ve yerleştirme sınırlamasını değiştirmek için kaydırın (Yatay, Dikey, Kuzey/Güney, Doğu/Batı, Kilitsiz).\n\n%6$s§9+Sağ tıklama ile seçenek ekranını açın§0.\n\n§5§nGERİ ALMA§0§r\nBir bloğa bakarken §9Eğil+§0%4$s tuşunu basılı tuttuğunuzda, yerleştirdiğiniz son bloklar, çevresinde yeşil bir çerçeveyle gösterilecektir. §9Eğil+§0%4$s§9+Bunlardan herhangi birine sağ tıklama§0 işlemi geri alacak ve tüm öğeleri size geri verecektir. Yıkım çekirdeğini kullandıysanız blokları geri koyacaktır.\n\n§5§nKONTEYNERLER§0§r\nShulker kutuları, paketler ve diğer modlardan birçok konteyner, asa için yapı taşları sağlar.\n\n§5§nBOŞTAKİ EL ÖNCELİĞݧ0§r\nBoştaki elinizde blok olduğunda, baktığınız blok yerine boştaki elinizdekini yerleştirirsiniz.", + "constructionwand.description.durability.limited": "%d blok için", + "constructionwand.description.durability.unlimited": "sonsuza kadar", + "constructionwand.description.key.sneak": "Eğil", + "constructionwand.description.key.sneak_opt": "Eğil+%s", + "constructionwand.description.core": "§5§nKURULUM§0§r\nTakmak için yeni çekirdeğinizi asanızla birlikte bir üretim ızgarasına koyun. Çekirdekler arasında geçiş yapmak için %s tuşunu basılı tutun ve asanızla boş alana sol tıklayın veya seçenek ekranını kullanın", + "constructionwand.description.core_angel": "Melek çekirdeği, karşı karşıya olduğunuz bloğun (veya blok sırasının) karşı tarafına bir blok yerleştirir. Maksimum mesafe asa seviyesine bağlıdır. Havada bir blok yerleştirmek için boş alana sağ tıklayın. Bunu yapmak için, yerleştirmek istediğiniz bloğu boştaki elinize almalısınız.", + "constructionwand.description.core_destruction": "Yıkım çekirdeği, size bakan taraftaki blokları (tile entities haricinde) yok eder. Maksimum blok sayısı asa seviyesine bağlıdır. Yok edilen bloklar boşluğa kaybolur, hata yaptıysanız geri alma özelliğini kullanabilirsiniz.", + + "stat.constructionwand.use_wand": "Asa kullanılarak yerleştirilen bloklar" +} \ No newline at end of file