Compare commits

...

45 commits

Author SHA1 Message Date
ced4ccf7e4 bump version to 2.12 2023-10-22 15:06:53 +02:00
06838cd433 Merge branch '1.20' into 1.20.2 2023-10-22 14:59:33 +02:00
6b6c7b5745
Merge pull request #78 from ALFEECLARE/1.20
add ja_jp translation.
2023-10-22 14:55:36 +02:00
ALFECLARE
d6c530b821
fix ja_jp translation. 2023-10-22 15:39:03 +09:00
e4f3a9fd7f ported to 1.20.2 2023-10-21 21:54:55 +02:00
d494599355 fix: Botania version 2023-10-21 20:59:09 +02:00
ALFECLARE
959af23e53
update ja_jp translation. 2023-09-20 15:31:26 +09:00
ALFECLARE
1a429a1865
update ja_jp translation. 2023-09-20 15:04:17 +09:00
ALFECLARE
07493cdae3
add ja_jp translation. 2023-09-20 13:03:12 +09:00
3ba418809b ported to MC 1.20 2023-07-08 00:19:13 +02:00
1c0c260f00 fix: #76 Logic Issue with Placement 2023-06-01 16:18:57 +02:00
eebfbf2745 update JEI dependency to 1.19.4 2023-04-05 17:53:40 +02:00
12b29cc185 fix: 1-block previews on consecutive placements 2023-03-25 01:10:05 +01:00
9f6344e7b2 ported to MC 1.19.4 2023-03-24 22:04:08 +01:00
25ecaa5c9b bump version to 2.10 2023-03-24 01:39:14 +01:00
Heimdallr-1
9e3fb14b72 Create ru_ru.json
The necessary information such as Stone wand, iron wand or infinity wand is not displayed in the key "construction and.description.and": "%1$s
2023-03-24 01:37:28 +01:00
f78cc3078a fix: JEI wand info showing raw translation key 2023-03-24 01:32:18 +01:00
2df42cd134 fix: gui tooltip updates 2023-02-15 21:47:40 +01:00
f1b9c9149b Merge branch '1.19' into 1.19.3 2023-02-15 21:35:06 +01:00
fcd2307db8 update jei dependency 2023-02-15 21:31:46 +01:00
bbae6c943d ported to 1.19.3 2023-02-12 23:08:37 +01:00
345e1ac30a update forge and mcp for MC 1.19.3 2023-02-12 18:37:22 +01:00
c53845ced9 fix: wands not breaking when out of durability 2023-02-12 17:47:50 +01:00
91c6901706 fix: replace deprecated CapabilityItemHandler 2023-02-12 17:15:42 +01:00
36de8df509 update Forge, fix compatibility with 1.19 Botania API 2023-02-12 17:07:04 +01:00
05658450c8 Merge pull request #59 from buff-mango/patch-1 2022-11-27 17:56:26 +01:00
f47273b0db
Merge pull request #57 from FITFC/1.19
added pt_br.json
2022-11-27 17:48:11 +01:00
mango_buff
00e621da71
Create zh_cn.json 2022-11-24 09:53:40 +08:00
FITFC
379285dbfe
added pt_br.json 2022-10-29 17:19:59 -05:00
61a95fcd1e add explaination to notes 2022-08-09 20:46:11 +02:00
44f1b531ec add notes 2022-08-09 20:39:54 +02:00
312d7597f9 Revert "update minecraft to 1.19.2"
This reverts commit 373be90a2e.
2022-08-09 20:28:52 +02:00
de61ff8fd6 fix dedicated server crash 2022-08-09 20:22:47 +02:00
373be90a2e update minecraft to 1.19.2 2022-08-09 20:21:07 +02:00
24eca43bb3 update minecraft to 1.19.1 2022-08-09 20:15:07 +02:00
4b04a9300e bump version to 2.8 2022-08-09 19:38:07 +02:00
f70347159e Merge branch '1.18.2' of github.com:Theta-Dev/ConstructionWand into 1.19 2022-08-09 19:37:29 +02:00
06d2488c6c fix config access before server start 2022-08-09 19:36:51 +02:00
f400310b59 fix text components 2022-08-09 18:40:44 +02:00
3a1316ce0f ported to Minecraft 1.19 2022-08-09 13:43:38 +02:00
28e97eca26
Merge pull request #50 from gjeodnd12165/patch-1
Korean Localization
2022-04-22 16:25:08 +02:00
gjeodnd12165
058cabe327
Create ko_kr.json 2022-04-18 17:17:01 +09:00
83d880cb2e update Botania integration for MC1.18.2 2022-03-26 00:28:07 +01:00
5f0cc62dbc remove superfluous inventory checks 2022-03-26 00:24:18 +01:00
Mrbysco
fc20293906 Port to 1.18.2
Convert item registering to DeferredRegister as vanilla freezes the registry which instantiating them outside of the RegistryEvent or DeferredRegister will result in an error.
2022-03-15 03:15:54 +01:00
52 changed files with 991 additions and 461 deletions

2
.gitignore vendored
View file

@ -19,7 +19,7 @@ build
# other # other
eclipse eclipse
run /run*
# Files from Forge MDK # Files from Forge MDK
forge*changelog.txt forge*changelog.txt

View file

@ -4,7 +4,7 @@ With a Construction Wand you can place multiple blocks (up to 1024) at once, ext
facing. If that's not enough: you can upgrade your wand with additional cores, allowing you to place a block behind the 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. block you are facing, conjure blocks in mid air or destroy lots of blocks very fast.
![](images/wands.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/wands.png)
**Note:** These are the instructions for ConstructionWand version 2.0+, which introduced some new features. **Note:** These are the instructions for ConstructionWand version 2.0+, which introduced some new features.
If you are still using version 1.x, refer to [those](https://github.com/Theta-Dev/ConstructionWand/tree/1.16.2-1.7) If you are still using version 1.x, refer to [those](https://github.com/Theta-Dev/ConstructionWand/tree/1.16.2-1.7)
@ -23,12 +23,12 @@ and last longer. These properties can be changed in the config.
| Infinity | Unbreakable | 1024 | Yes | 8 | 81 | | Infinity | Unbreakable | 1024 | Yes | 8 | 81 |
## Crafting ## Crafting
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting1.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting1.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting2.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting2.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting3.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting3.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting4.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting4.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting5.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting5.png)
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.16.2/images/crafting6.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/crafting6.png)
## Keybindings ## Keybindings
@ -69,7 +69,7 @@ you can use the undo feature if you've made a mistake.
## Options ## Options
SNEAK+OPTKEY+Right clicking empty space opens the option screen of your wand. SNEAK+OPTKEY+Right clicking empty space opens the option screen of your wand.
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/options.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/options.png)
**Restriction:** If restriction is enabled the wand will only place blocks in one row or column **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). (choose between North/South, East/West on a horizontal plane and Horizontal, Vertical on a vertical plane).
@ -79,7 +79,7 @@ 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. **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: Target mode places the blocks in the same direction as their supporting block. See the picture below:
![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.15/images/placedir.png) ![](https://raw.githubusercontent.com/Theta-Dev/ConstructionWand/1.19/images/placedir.png)
**Replacement:** Enables/disables the replacement of replaceable blocks (Fluids, snow, tallgrass). **Replacement:** Enables/disables the replacement of replaceable blocks (Fluids, snow, tallgrass).
@ -122,7 +122,7 @@ for TileEntities in CW Version 1.7. Chisels&Bits blocks are blacklisted by defau
There are probably a few other tile entities from other mods out there which may cause issues as well. There are probably a few other tile entities from other mods out there which may cause issues as well.
If you find some of them you can tell me by creating If you find some of them you can tell me by creating
an issue, commenting on Curse or editing the default blacklist yourself 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) (it is located at https://github.com/Theta-Dev/ConstructionWand/blob/1.19/src/main/java/thetadev/constructionwand/basics/ConfigServer.java#L25)
and making a PR. and making a PR.
## Contributions and #Hacktoberfest ## Contributions and #Hacktoberfest

View file

@ -7,20 +7,30 @@ buildscript {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
} }
} }
apply plugin: 'net.minecraftforge.gradle'
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. plugins {
apply plugin: 'eclipse' id 'eclipse'
apply plugin: 'maven-publish' id 'idea'
id 'maven-publish'
id 'net.minecraftforge.gradle' version '[6.0.14,6.2)'
}
version = "${mcversion}-${version_major}.${version_minor}" version = "${mcversion}-${version_major}.${version_minor}"
group = "${author}.${modid}" group = "${author}.${modid}"
archivesBaseName = "${modid}" archivesBaseName = "${modid}"
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17) java.toolchain.languageVersion = JavaLanguageVersion.of(17)
println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
minecraft { minecraft {
mappings channel: project.mcp_channel, version: project.mcp_mappings mappings channel: project.mcp_channel, version: project.mcp_mappings
// This property allows configuring Gradle's ProcessResources task(s) to run on IDE output locations before launching the game.
// It is REQUIRED to be set to true for this template to function.
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
copyIdeResources = true
runs { runs {
client { client {
workingDirectory project.file("run/client").canonicalPath workingDirectory project.file("run/client").canonicalPath
@ -31,6 +41,9 @@ minecraft {
// Recommended logging level for the console // Recommended logging level for the console
property 'forge.logging.console.level', 'debug' property 'forge.logging.console.level', 'debug'
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
// property 'forge.enabledGameTestNamespaces', modid
mods { mods {
constructionwand { constructionwand {
source sourceSets.main source sourceSets.main
@ -47,6 +60,8 @@ minecraft {
// Recommended logging level for the console // Recommended logging level for the console
property 'forge.logging.console.level', 'debug' property 'forge.logging.console.level', 'debug'
// property 'forge.enabledGameTestNamespaces', modid
mods { mods {
constructionwand { constructionwand {
source sourceSets.main source sourceSets.main
@ -54,6 +69,25 @@ minecraft {
} }
} }
// This run config launches GameTestServer and runs all registered gametests, then exits.
// By default, the server will crash when no gametests are provided.
// The gametest system is also enabled by default for other run configs under the /test command.
gameTestServer {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
property 'forge.enabledGameTestNamespaces', modid
mods {
examplemod {
source sourceSets.main
}
}
}
data { data {
workingDirectory project.file("run/client").canonicalPath workingDirectory project.file("run/client").canonicalPath
@ -88,10 +122,6 @@ repositories {
maven { maven {
url = "https://maven.theillusivec4.top/" url = "https://maven.theillusivec4.top/"
} }
maven {
name = "JEI Maven"
url "https://dvs1.progwml6.com/files/maven"
}
} }
dependencies { dependencies {
@ -101,17 +131,16 @@ dependencies {
version: "${project.mcversion}-${project.forgeversion}" version: "${project.mcversion}-${project.forgeversion}"
]) ])
compileOnly fg.deobf("mezz.jei:${jei_version}:api") compileOnly fg.deobf("mezz.jei:jei-${mcversion}-common-api:${jei_version}")
runtimeOnly fg.deobf("mezz.jei:${jei_version}") compileOnly fg.deobf("mezz.jei:jei-${mcversion}-forge-api:${jei_version}")
runtimeOnly fg.deobf("mezz.jei:jei-${mcversion}-forge:${jei_version}")
/*
compileOnly fg.deobf([ compileOnly fg.deobf([
group: "vazkii.botania", group: "vazkii.botania",
name: "Botania", name: "Botania",
version: "${project.botania}", version: "${project.botania}",
classifier: "api" classifier: "api"
]) ])
*/
} }
jar { jar {
@ -146,3 +175,7 @@ publishing {
} }
} }
} }
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}

View file

@ -4,15 +4,15 @@ org.gradle.daemon=false
author=thetadev author=thetadev
modid=constructionwand modid=constructionwand
mcversion=1.18.1 mcversion=1.20.2
forgeversion=39.0.0 forgeversion=48.0.30
mcp_channel=official mcp_channel=official
mcp_mappings=1.18.1 mcp_mappings=1.20.2
# Source: https://maven.blamejared.com/vazkii/botania/Botania/ # Source: https://maven.blamejared.com/vazkii/botania/Botania/
# botania=1.16.2-405 botania=1.20.1-441-FORGE-SNAPSHOT
# Source: https://dvs1.progwml6.com/files/maven/mezz/jei/ # Source: https://maven.blamejared.com/mezz/jei/
jei_version=jei-1.18.1:9.1.0.41 jei_version=16.0.0.28
version_major=2 version_major=2
version_minor=6 version_minor=12

Binary file not shown.

View file

@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

269
gradlew vendored
View file

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,67 +17,101 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -106,80 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# 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
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
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" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

13
notes/Builds.md Normal file
View file

@ -0,0 +1,13 @@
# Builds
Overview of all builds of ConstructionWand and their properties.
| Build | Java version | Supported MC versions |
|--------|--------------|-----------------------|
| 1.14.4 | 8 | 1.14.4 |
| 1.15.2 | 8 | 1.15.2 |
| 1.16.1 | 8 | 1.16.1 |
| 1.16.5 | 8 | 1.16.2 - 1.16.5 |
| 1.17.1 | 16 | 1.17.1 |
| 1.18 | 17 | 1.18 |
| 1.19 | 17 | 1.19 - 1.19.2 |

11
notes/test_command.md Normal file
View file

@ -0,0 +1,11 @@
# Testing item containers
- Install the toolbelt mod (https://www.curseforge.com/minecraft/mc-mods/tool-belt)
- Place a chest on top of a command block and enter this command:
- Get a toolbeld with 2 stacks of diamond blocks in it
The reason for this hack is that the toolbelt mod gets updated very quickly and is available for almost every
MC version supported by ConstructionWand.
```
data merge block ~ ~1 ~ {Items: [{Slot: 0, id: "toolbelt:belt", Count: 1, tag: {Items: [{Slot: 0, id: "minecraft:diamond_block", Count: 64}, {Slot: 1, id: "minecraft:diamond_block", Count: 64}]}}]}
```

6
settings.gradle Normal file
View file

@ -0,0 +1,6 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven { url = 'https://maven.minecraftforge.net/' }
}
}

View file

@ -8,35 +8,27 @@ import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import thetadev.constructionwand.basics.ConfigClient; import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.ModStats; import thetadev.constructionwand.basics.ModStats;
import thetadev.constructionwand.basics.ReplacementRegistry;
import thetadev.constructionwand.client.ClientEvents; import thetadev.constructionwand.client.ClientEvents;
import thetadev.constructionwand.client.RenderBlockPreview; import thetadev.constructionwand.client.RenderBlockPreview;
import thetadev.constructionwand.containers.ContainerManager; import thetadev.constructionwand.containers.ContainerManager;
import thetadev.constructionwand.containers.ContainerRegistrar; import thetadev.constructionwand.containers.ContainerRegistrar;
import thetadev.constructionwand.items.ModItems; import thetadev.constructionwand.items.ModItems;
import thetadev.constructionwand.network.PacketQueryUndo; import thetadev.constructionwand.network.ModMessages;
import thetadev.constructionwand.network.PacketUndoBlocks;
import thetadev.constructionwand.network.PacketWandOption;
import thetadev.constructionwand.wand.undo.UndoHistory; import thetadev.constructionwand.wand.undo.UndoHistory;
@Mod(ConstructionWand.MODID) @Mod(ConstructionWand.MODID)
public class ConstructionWand public class ConstructionWand {
{
public static final String MODID = "constructionwand"; public static final String MODID = "constructionwand";
public static final String MODNAME = "ConstructionWand"; public static final String MODNAME = "ConstructionWand";
public static ConstructionWand instance; public static ConstructionWand instance;
public static final Logger LOGGER = LogManager.getLogger(); public static final Logger LOGGER = LogManager.getLogger();
private static final String PROTOCOL_VERSION = "1";
public SimpleChannel HANDLER;
public ContainerManager containerManager; public ContainerManager containerManager;
public UndoHistory undoHistory; public UndoHistory undoHistory;
@ -53,6 +45,9 @@ public class ConstructionWand
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
// Register Item DeferredRegister
ModItems.ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
// Config setup // Config setup
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigServer.SPEC); ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigServer.SPEC);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigClient.SPEC); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigClient.SPEC);
@ -62,18 +57,11 @@ public class ConstructionWand
LOGGER.info("ConstructionWand says hello - may the odds be ever in your favor."); LOGGER.info("ConstructionWand says hello - may the odds be ever in your favor.");
// Register packets // Register packets
HANDLER = NetworkRegistry.newSimpleChannel(new ResourceLocation(MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); ModMessages.register();
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);
// Container registry // Container registry
ContainerRegistrar.register(); ContainerRegistrar.register();
//Replacement registry
ReplacementRegistry.init();
// Stats // Stats
ModStats.register(); ModStats.register();
} }
@ -84,7 +72,6 @@ public class ConstructionWand
MinecraftForge.EVENT_BUS.register(new ClientEvents()); MinecraftForge.EVENT_BUS.register(new ClientEvents());
event.enqueueWork(ModItems::registerModelProperties); event.enqueueWork(ModItems::registerModelProperties);
event.enqueueWork(ModItems::registerItemColors);
} }
public static ResourceLocation loc(String name) { public static ResourceLocation loc(String name) {

View file

@ -2,6 +2,7 @@ package thetadev.constructionwand.basics;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
@ -9,10 +10,15 @@ import thetadev.constructionwand.ConstructionWand;
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID) @Mod.EventBusSubscriber(modid = ConstructionWand.MODID)
public class CommonEvents public class CommonEvents
{ {
@SubscribeEvent
public static void serverStarting(ServerStartingEvent e) {
ReplacementRegistry.init();
}
@SubscribeEvent @SubscribeEvent
public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) { public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) {
Player player = e.getPlayer(); Player player = e.getEntity();
if(player.level.isClientSide) return; if(player.level().isClientSide) return;
ConstructionWand.instance.undoHistory.removePlayer(player); ConstructionWand.instance.undoHistory.removePlayer(player);
} }
} }

View file

@ -1,8 +1,11 @@
package thetadev.constructionwand.basics; package thetadev.constructionwand.basics;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tiers; import net.minecraft.world.item.Tiers;
import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.items.ModItems; import thetadev.constructionwand.items.ModItems;
import java.util.Arrays; import java.util.Arrays;
@ -11,7 +14,7 @@ import java.util.List;
public class ConfigServer public class ConfigServer
{ {
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder(); public static final ForgeConfigSpec SPEC;
public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE; public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE;
public static final ForgeConfigSpec.IntValue MAX_RANGE; public static final ForgeConfigSpec.IntValue MAX_RANGE;
@ -27,10 +30,10 @@ public class ConfigServer
public static final ForgeConfigSpec.ConfigValue<List<?>> TE_LIST; public static final ForgeConfigSpec.ConfigValue<List<?>> TE_LIST;
private static final String[] TE_LIST_DEFAULT = {"chiselsandbits"}; private static final String[] TE_LIST_DEFAULT = {"chiselsandbits"};
private static final HashMap<Item, WandProperties> wandProperties = new HashMap<>(); private static final HashMap<ResourceLocation, WandProperties> wandProperties = new HashMap<>();
public static WandProperties getWandProperties(Item wand) { public static WandProperties getWandProperties(Item wand) {
return wandProperties.getOrDefault(wand, WandProperties.DEFAULT); return wandProperties.getOrDefault(ForgeRegistries.ITEMS.getKey(wand), WandProperties.DEFAULT);
} }
public static class WandProperties public static class WandProperties
@ -53,9 +56,10 @@ public class ConfigServer
this.upgradeable = upgradeable; this.upgradeable = upgradeable;
} }
public WandProperties(ForgeConfigSpec.Builder builder, Item wand, int defDurability, int defLimit, public WandProperties(ForgeConfigSpec.Builder builder, RegistryObject<Item> wandSupplier, int defDurability, int defLimit,
int defAngel, int defDestruction, boolean defUpgradeable) { int defAngel, int defDestruction, boolean defUpgradeable) {
builder.push(wand.getRegistryName().getPath()); ResourceLocation registryName = wandSupplier.getId();
builder.push(registryName.getPath());
if(defDurability > 0) { if(defDurability > 0) {
builder.comment("Wand durability"); builder.comment("Wand durability");
@ -72,7 +76,7 @@ public class ConfigServer
upgradeable = builder.define("upgradeable", defUpgradeable); upgradeable = builder.define("upgradeable", defUpgradeable);
builder.pop(); builder.pop();
wandProperties.put(wand, this); wandProperties.put(registryName, this);
} }
public int getDurability() { public int getDurability() {
@ -97,39 +101,41 @@ public class ConfigServer
} }
static { static {
BUILDER.comment("This is the Server config for ConstructionWand.", final var builder = new ForgeConfigSpec.Builder();
builder.comment("This is the Server config for ConstructionWand.",
"If you're not familiar with Forge's new split client/server config, let me explain:", "If you're not familiar with Forge's new split client/server config, let me explain:",
"Client config is stored in the /config folder and only contains client specific settings like graphics and keybinds.", "Client config is stored in the /config folder and only contains client specific settings like graphics and keybinds.",
"Mod behavior is configured in the Server config, which is world-specific and thus located", "Mod behavior is configured in the Server config, which is world-specific and thus located",
"in the /saves/myworld/serverconfig folder. If you want to change the serverconfig for all", "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 worlds, copy the config files in the /defaultconfigs folder.");
new WandProperties(BUILDER, ModItems.WAND_STONE, Tiers.STONE.getUses(), 9, 0, 0, false); 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_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_DIAMOND, Tiers.DIAMOND.getUses(), 128, 8, 25, true);
new WandProperties(BUILDER, ModItems.WAND_INFINITY, 0, 1024, 16, 81, true); new WandProperties(builder, ModItems.WAND_INFINITY, 0, 1024, 16, 81, true);
BUILDER.push("misc"); builder.push("misc");
BUILDER.comment("Block limit for Infinity Wand used in creative mode"); builder.comment("Block limit for Infinity Wand used in creative mode");
LIMIT_CREATIVE = BUILDER.defineInRange("InfinityWandCreative", 2048, 1, Integer.MAX_VALUE); LIMIT_CREATIVE = builder.defineInRange("InfinityWandCreative", 2048, 1, Integer.MAX_VALUE);
BUILDER.comment("Maximum placement range (0: unlimited). Affects all wands and is meant for lag prevention, not game balancing."); builder.comment("Maximum placement range (0: unlimited). Affects all wands and is meant for lag prevention, not game balancing.");
MAX_RANGE = BUILDER.defineInRange("MaxRange", 100, 0, Integer.MAX_VALUE); MAX_RANGE = builder.defineInRange("MaxRange", 100, 0, Integer.MAX_VALUE);
BUILDER.comment("Number of operations that can be undone"); builder.comment("Number of operations that can be undone");
UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE); UNDO_HISTORY = builder.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE);
BUILDER.comment("Place blocks below you while falling > 10 blocks with angel core (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); ANGEL_FALLING = builder.define("AngelFalling", false);
BUILDER.comment("Blocks to treat equally when in Similar mode. Enter block IDs seperated by ;"); 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); SIMILAR_BLOCKS = builder.defineList("SimilarBlocks", Arrays.asList(SIMILAR_BLOCKS_DEFAULT), obj -> true);
BUILDER.pop(); builder.pop();
BUILDER.push("tileentity"); builder.push("tileentity");
BUILDER.comment("White/Blacklist for Tile Entities. Allow/Prevent blocks with TEs from being placed by wand.", builder.comment("White/Blacklist for Tile Entities. Allow/Prevent blocks with TEs from being placed by wand.",
"You can either add block ids like minecraft:chest or mod ids like minecraft"); "You can either add block ids like minecraft:chest or mod ids like minecraft");
TE_LIST = BUILDER.defineList("TEList", Arrays.asList(TE_LIST_DEFAULT), obj -> true); TE_LIST = builder.defineList("TEList", Arrays.asList(TE_LIST_DEFAULT), obj -> true);
BUILDER.comment("If set to TRUE, treat TEList as a whitelist, otherwise blacklist"); builder.comment("If set to TRUE, treat TEList as a whitelist, otherwise blacklist");
TE_WHITELIST = BUILDER.define("TEWhitelist", false); TE_WHITELIST = builder.define("TEWhitelist", false);
BUILDER.pop(); builder.pop();
}
public static final ForgeConfigSpec SPEC = BUILDER.build(); SPEC = builder.build();
}
} }

View file

@ -1,6 +1,7 @@
package thetadev.constructionwand.basics; package thetadev.constructionwand.basics;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.StatFormatter; import net.minecraft.stats.StatFormatter;
import net.minecraft.stats.Stats; import net.minecraft.stats.Stats;
@ -16,7 +17,7 @@ public class ModStats
private static void registerStat(ResourceLocation registryName) { private static void registerStat(ResourceLocation registryName) {
// Compare with net.minecraft.stats.Stats#registerCustom // Compare with net.minecraft.stats.Stats#registerCustom
Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName); Registry.register(BuiltInRegistries.CUSTOM_STAT, registryName.getPath(), registryName);
Stats.CUSTOM.get(registryName, StatFormatter.DEFAULT); Stats.CUSTOM.get(registryName, StatFormatter.DEFAULT);
} }
} }

View file

@ -16,6 +16,8 @@ public class ReplacementRegistry
private static final HashSet<HashSet<Item>> replacements = new HashSet<>(); private static final HashSet<HashSet<Item>> replacements = new HashSet<>();
public static void init() { public static void init() {
replacements.clear();
for(Object key : ConfigServer.SIMILAR_BLOCKS.get()) { for(Object key : ConfigServer.SIMILAR_BLOCKS.get()) {
if(!(key instanceof String)) continue; if(!(key instanceof String)) continue;
HashSet<Item> set = new HashSet<>(); HashSet<Item> set = new HashSet<>();

View file

@ -20,7 +20,8 @@ import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.registries.ForgeRegistries;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.containers.ContainerManager; import thetadev.constructionwand.containers.ContainerManager;
import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWand;
@ -52,12 +53,13 @@ public class WandUtil
return null; return null;
} }
public static BlockPos playerPos(Player player) { public static BlockPos posFromVec(Vec3 vec) {
return new BlockPos(player.position()); return new BlockPos(
(int) Math.round(vec.x), (int) Math.round(vec.y), (int) Math.round(vec.z));
} }
public static Vec3 entityPositionVec(Entity entity) { public static Vec3 entityPositionVec(Entity entity) {
return new Vec3(entity.getX(), entity.getY() - entity.getMyRidingOffset() + entity.getBbHeight() / 2, entity.getZ()); return new Vec3(entity.getX(), entity.getY() + entity.getBbHeight() / 2, entity.getZ());
} }
public static Vec3 blockPosVec(BlockPos pos) { public static Vec3 blockPosVec(BlockPos pos) {
@ -91,7 +93,7 @@ public class WandUtil
public static boolean isTEAllowed(BlockState state) { public static boolean isTEAllowed(BlockState state) {
if(!state.hasBlockEntity()) return true; if(!state.hasBlockEntity()) return true;
ResourceLocation name = state.getBlock().getRegistryName(); ResourceLocation name = ForgeRegistries.BLOCKS.getKey(state.getBlock());
if(name == null) return false; if(name == null) return false;
String fullId = name.toString(); String fullId = name.toString();
@ -160,7 +162,7 @@ public class WandUtil
List<ItemStack> inventory = WandUtil.getFullInv(player); List<ItemStack> inventory = WandUtil.getFullInv(player);
for(ItemStack stack : inventory) { for(ItemStack stack : inventory) {
if(stack == null) continue; if(stack == null || stack.isEmpty()) continue;
if(WandUtil.stackEquals(stack, item)) { if(WandUtil.stackEquals(stack, item)) {
total += stack.getCount(); total += stack.getCount();

View file

@ -1,17 +1,18 @@
package thetadev.constructionwand.basics.pool; package thetadev.constructionwand.basics.pool;
import net.minecraft.util.RandomSource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Random;
public class RandomPool<T> implements IPool<T> public class RandomPool<T> implements IPool<T>
{ {
private final Random rng; private final RandomSource rng;
private final HashMap<T, Integer> elements; private final HashMap<T, Integer> elements;
private HashSet<T> pool; private HashSet<T> pool;
public RandomPool(Random rng) { public RandomPool(RandomSource rng) {
this.rng = rng; this.rng = rng;
elements = new HashMap<>(); elements = new HashMap<>();
reset(); reset();

View file

@ -8,11 +8,11 @@ import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.ConfigClient; import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.network.ModMessages;
import thetadev.constructionwand.network.PacketQueryUndo; import thetadev.constructionwand.network.PacketQueryUndo;
import thetadev.constructionwand.network.PacketWandOption; import thetadev.constructionwand.network.PacketWandOption;
@ -26,7 +26,7 @@ public class ClientEvents
// Send state of OPT key to server // Send state of OPT key to server
@SubscribeEvent @SubscribeEvent
public void KeyEvent(InputEvent.KeyInputEvent event) { public void KeyEvent(InputEvent.Key event) {
Player player = Minecraft.getInstance().player; Player player = Minecraft.getInstance().player;
if(player == null) return; if(player == null) return;
if(WandUtil.holdingWand(player) == null) return; if(WandUtil.holdingWand(player) == null) return;
@ -34,17 +34,16 @@ public class ClientEvents
boolean optState = isOptKeyDown(); boolean optState = isOptKeyDown();
if(optPressed != optState) { if(optPressed != optState) {
optPressed = optState; optPressed = optState;
PacketQueryUndo packet = new PacketQueryUndo(optPressed); ModMessages.sendToServer(new PacketQueryUndo(optPressed));
ConstructionWand.instance.HANDLER.sendToServer(packet);
//ConstructionWand.LOGGER.debug("OPT key update: " + optPressed); //ConstructionWand.LOGGER.debug("OPT key update: " + optPressed);
} }
} }
// Sneak+(OPT)+Scroll to change direction lock // Sneak+(OPT)+Scroll to change direction lock
@SubscribeEvent(priority = EventPriority.HIGHEST) @SubscribeEvent(priority = EventPriority.HIGHEST)
public void MouseScrollEvent(InputEvent.MouseScrollEvent event) { public void MouseScrollEvent(InputEvent.MouseScrollingEvent event) {
Player player = Minecraft.getInstance().player; Player player = Minecraft.getInstance().player;
double scroll = event.getScrollDelta(); double scroll = event.getDeltaY();
if(player == null || !modeKeyCombDown(player) || scroll == 0) return; if(player == null || !modeKeyCombDown(player) || scroll == 0) return;
@ -53,14 +52,14 @@ public class ClientEvents
WandOptions wandOptions = new WandOptions(wand); WandOptions wandOptions = new WandOptions(wand);
wandOptions.lock.next(scroll < 0); wandOptions.lock.next(scroll < 0);
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.lock, true)); ModMessages.sendToServer(new PacketWandOption(wandOptions.lock, true));
event.setCanceled(true); event.setCanceled(true);
} }
// Sneak+(OPT)+Left click wand to change core // Sneak+(OPT)+Left click wand to change core
@SubscribeEvent @SubscribeEvent
public void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) { public void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) {
Player player = event.getPlayer(); Player player = event.getEntity();
if(player == null || !modeKeyCombDown(player)) return; if(player == null || !modeKeyCombDown(player)) return;
@ -69,7 +68,7 @@ public class ClientEvents
WandOptions wandOptions = new WandOptions(wand); WandOptions wandOptions = new WandOptions(wand);
wandOptions.cores.next(); wandOptions.cores.next();
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.cores, true)); ModMessages.sendToServer(new PacketWandOption(wandOptions.cores, true));
} }
// Sneak+(OPT)+Right click wand to open GUI // Sneak+(OPT)+Right click wand to open GUI
@ -77,7 +76,7 @@ public class ClientEvents
public void onRightClickItem(PlayerInteractEvent.RightClickItem event) { public void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
if(event.getSide().isServer()) return; if(event.getSide().isServer()) return;
Player player = event.getPlayer(); Player player = event.getEntity();
if(player == null || !guiKeyCombDown(player)) return; if(player == null || !guiKeyCombDown(player)) return;
ItemStack wand = event.getItemStack(); ItemStack wand = event.getItemStack();

View file

@ -12,7 +12,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraftforge.client.event.DrawSelectionEvent; import net.minecraftforge.client.event.RenderHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWand;
@ -22,11 +22,11 @@ import java.util.Set;
public class RenderBlockPreview public class RenderBlockPreview
{ {
public WandJob wandJob; private WandJob wandJob;
public Set<BlockPos> undoBlocks; public Set<BlockPos> undoBlocks;
@SubscribeEvent @SubscribeEvent
public void renderBlockHighlight(DrawSelectionEvent.HighlightBlock event) { public void renderBlockHighlight(RenderHighlightEvent.Block event) {
if(event.getTarget().getType() != HitResult.Type.BLOCK) return; if(event.getTarget().getType() != HitResult.Type.BLOCK) return;
BlockHitResult rtr = event.getTarget(); BlockHitResult rtr = event.getTarget();
@ -39,8 +39,12 @@ public class RenderBlockPreview
if(wand == null) return; if(wand == null) return;
if(!(player.isCrouching() && ClientEvents.isOptKeyDown())) { 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
wandJob = ItemWand.getWandJob(player, player.level, rtr, wand); // 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(); blocks = wandJob.getBlockPositions();
} }
@ -55,7 +59,7 @@ public class RenderBlockPreview
MultiBufferSource buffer = event.getMultiBufferSource(); MultiBufferSource buffer = event.getMultiBufferSource();
VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES); VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES);
double partialTicks = event.getPartialTicks(); double partialTicks = event.getPartialTick();
double d0 = player.xOld + (player.getX() - player.xOld) * partialTicks; double d0 = player.xOld + (player.getX() - player.xOld) * partialTicks;
double d1 = player.yOld + player.getEyeHeight() + (player.getY() - player.yOld) * partialTicks; double d1 = player.yOld + player.getEyeHeight() + (player.getY() - player.yOld) * partialTicks;
double d2 = player.zOld + (player.getZ() - player.zOld) * partialTicks; double d2 = player.zOld + (player.getZ() - player.zOld) * partialTicks;
@ -68,6 +72,10 @@ public class RenderBlockPreview
event.setCanceled(true); event.setCanceled(true);
} }
public void reset() {
wandJob = null;
}
private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) { private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) {
return rtr1.getBlockPos().equals(rtr2.getBlockPos()) && rtr1.getDirection().equals(rtr2.getDirection()); return rtr1.getBlockPos().equals(rtr2.getBlockPos()) && rtr1.getDirection().equals(rtr2.getDirection());
} }

View file

@ -1,22 +1,20 @@
package thetadev.constructionwand.client; package thetadev.constructionwand.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; 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 net.minecraft.world.item.ItemStack;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.IOption;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.network.ModMessages;
import thetadev.constructionwand.network.PacketWandOption; import thetadev.constructionwand.network.PacketWandOption;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class ScreenWand extends Screen public class ScreenWand extends Screen {
{
private final ItemStack wand; private final ItemStack wand;
private final WandOptions wandOptions; private final WandOptions wandOptions;
@ -31,7 +29,7 @@ public class ScreenWand extends Screen
private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT + SPACING_HEIGHT) - SPACING_HEIGHT; private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT + SPACING_HEIGHT) - SPACING_HEIGHT;
public ScreenWand(ItemStack wand) { public ScreenWand(ItemStack wand) {
super(new TextComponent("ScreenWand")); super(Component.literal("ScreenWand"));
this.wand = wand; this.wand = wand;
wandOptions = new WandOptions(wand); wandOptions = new WandOptions(wand);
} }
@ -47,10 +45,10 @@ public class ScreenWand extends Screen
} }
@Override @Override
public void render(@Nonnull PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { public void render(@Nonnull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(matrixStack); renderBackground(guiGraphics, mouseX, mouseY, partialTicks);
drawCenteredString(matrixStack, font, wand.getDisplayName(), width / 2, height / 2 - FIELD_HEIGHT / 2 - SPACING_HEIGHT, 16777215); guiGraphics.drawCenteredString(font, wand.getDisplayName(), width / 2, height / 2 - FIELD_HEIGHT / 2 - SPACING_HEIGHT, 16777215);
super.render(matrixStack, mouseX, mouseY, partialTicks); super.render(guiGraphics, mouseX, mouseY, partialTicks);
} }
@Override @Override
@ -64,21 +62,21 @@ public class ScreenWand extends Screen
} }
private void createButton(int cx, int cy, IOption<?> option) { 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 button = Button.builder(getButtonLabel(option), bt -> clickButton(bt, option))
.pos(getX(cx), getY(cy))
.size(BUTTON_WIDTH, BUTTON_HEIGHT)
.tooltip(getButtonTooltip(option))
.build();
button.active = option.isEnabled(); button.active = option.isEnabled();
addRenderableWidget(button); addRenderableWidget(button);
} }
private void clickButton(Button button, IOption<?> option) { private void clickButton(Button button, IOption<?> option) {
option.next(); option.next();
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(option, false)); ModMessages.sendToServer(new PacketWandOption(option, false));
button.setMessage(getButtonLabel(option)); button.setMessage(getButtonLabel(option));
} button.setTooltip(getButtonTooltip(option));
private void drawTooltip(PoseStack matrixStack, int mouseX, int mouseY, IOption<?> option) {
if(isMouseOver(mouseX, mouseY)) {
renderTooltip(matrixStack, new TranslatableComponent(option.getDescTranslation()), mouseX, mouseY);
}
} }
private int getX(int n) { private int getX(int n) {
@ -90,6 +88,10 @@ public class ScreenWand extends Screen
} }
private Component getButtonLabel(IOption<?> option) { private Component getButtonLabel(IOption<?> option) {
return new TranslatableComponent(option.getKeyTranslation()).append(new TranslatableComponent(option.getValueTranslation())); return Component.translatable(option.getKeyTranslation()).append(Component.translatable(option.getValueTranslation()));
}
private Tooltip getButtonTooltip(IOption<?> option) {
return Tooltip.create(Component.translatable(option.getDescTranslation()));
} }
} }

View file

@ -1,6 +1,8 @@
package thetadev.constructionwand.containers; package thetadev.constructionwand.containers;
import net.minecraftforge.fml.ModList;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.containers.handlers.HandlerBotania;
import thetadev.constructionwand.containers.handlers.HandlerBundle; import thetadev.constructionwand.containers.handlers.HandlerBundle;
import thetadev.constructionwand.containers.handlers.HandlerCapability; import thetadev.constructionwand.containers.handlers.HandlerCapability;
import thetadev.constructionwand.containers.handlers.HandlerShulkerbox; import thetadev.constructionwand.containers.handlers.HandlerShulkerbox;
@ -12,13 +14,9 @@ public class ContainerRegistrar
ConstructionWand.instance.containerManager.register(new HandlerShulkerbox()); ConstructionWand.instance.containerManager.register(new HandlerShulkerbox());
ConstructionWand.instance.containerManager.register(new HandlerBundle()); ConstructionWand.instance.containerManager.register(new HandlerBundle());
/*
TODO: Reenable this when Botania gets ported to 1.17
if(ModList.get().isLoaded("botania")) { if(ModList.get().isLoaded("botania")) {
ConstructionWand.instance.containerManager.register(new HandlerBotania()); ConstructionWand.instance.containerManager.register(new HandlerBotania());
ConstructionWand.LOGGER.info("Botania integration added"); ConstructionWand.LOGGER.info("Botania integration added");
} }
*/
} }
} }

View file

@ -1,25 +1,28 @@
/*
TODO: Reenable this when Botania gets ported to 1.17
package thetadev.constructionwand.containers.handlers; package thetadev.constructionwand.containers.handlers;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import thetadev.constructionwand.api.IContainerHandler; import thetadev.constructionwand.api.IContainerHandler;
import vazkii.botania.api.item.IBlockProvider; import vazkii.botania.api.BotaniaForgeCapabilities;
import vazkii.botania.api.item.BlockProvider;
import java.util.Optional;
public class HandlerBotania implements IContainerHandler public class HandlerBotania implements IContainerHandler
{ {
@Override @Override
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { 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 @Override
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); Optional<BlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem())); if(provOptional.isEmpty()) return 0;
BlockProvider prov = provOptional.get();
int provCount = prov.getBlockCount(player, inventoryStack, Block.byItem(itemStack.getItem()));
if(provCount == -1) if(provCount == -1)
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
return provCount; return provCount;
@ -27,10 +30,12 @@ public class HandlerBotania implements IContainerHandler
@Override @Override
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem(); Optional<BlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
if(prov.provideBlock(player, itemStack, inventoryStack, Block.byItem(itemStack.getItem()), true)) if(provOptional.isEmpty()) return 0;
BlockProvider prov = provOptional.get();
if(prov.provideBlock(player, inventoryStack, Block.byItem(itemStack.getItem()), true))
return 0; return 0;
return count; return count;
} }
} }
*/

View file

@ -2,7 +2,7 @@ package thetadev.constructionwand.containers.handlers;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import thetadev.constructionwand.api.IContainerHandler; import thetadev.constructionwand.api.IContainerHandler;
import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.WandUtil;
@ -13,12 +13,12 @@ public class HandlerCapability implements IContainerHandler
{ {
@Override @Override
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) { public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
return inventoryStack != null && inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).isPresent(); return inventoryStack != null && inventoryStack.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent();
} }
@Override @Override
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve(); Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve();
if(itemHandlerOptional.isEmpty()) return 0; if(itemHandlerOptional.isEmpty()) return 0;
int total = 0; int total = 0;
@ -36,7 +36,7 @@ public class HandlerCapability implements IContainerHandler
@Override @Override
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve(); Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve();
if(itemHandlerOptional.isEmpty()) return 0; if(itemHandlerOptional.isEmpty()) return 0;
IItemHandler itemHandler = itemHandlerOptional.get(); IItemHandler itemHandler = itemHandlerOptional.get();

View file

@ -1,11 +1,12 @@
package thetadev.constructionwand.crafting; package thetadev.constructionwand.crafting;
import net.minecraft.resources.ResourceLocation; import net.minecraft.core.RegistryAccess;
import net.minecraft.world.inventory.CraftingContainer; import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe; import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer; import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import thetadev.constructionwand.api.IWandUpgrade; import thetadev.constructionwand.api.IWandUpgrade;
import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.ConfigServer;
@ -16,10 +17,10 @@ import javax.annotation.Nonnull;
public class RecipeWandUpgrade extends CustomRecipe public class RecipeWandUpgrade extends CustomRecipe
{ {
public static final SimpleRecipeSerializer<RecipeWandUpgrade> SERIALIZER = new SimpleRecipeSerializer<>(RecipeWandUpgrade::new); public static final SimpleCraftingRecipeSerializer<RecipeWandUpgrade> SERIALIZER = new SimpleCraftingRecipeSerializer<>(RecipeWandUpgrade::new);
public RecipeWandUpgrade(ResourceLocation resourceLocation) { public RecipeWandUpgrade(CraftingBookCategory category) {
super(resourceLocation); super(category);
} }
@Override @Override
@ -43,7 +44,7 @@ public class RecipeWandUpgrade extends CustomRecipe
@Nonnull @Nonnull
@Override @Override
public ItemStack assemble(@Nonnull CraftingContainer inv) { public ItemStack assemble(@Nonnull CraftingContainer inv, @Nonnull RegistryAccess registryAccess) {
ItemStack wand = null; ItemStack wand = null;
IWandUpgrade upgrade = null; IWandUpgrade upgrade = null;

View file

@ -1,10 +1,11 @@
package thetadev.constructionwand.data; package thetadev.constructionwand.data;
import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.tags.Tag; import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.ItemLike;
import net.minecraftforge.registries.ForgeRegistries;
public class Inp public class Inp
{ {
@ -19,10 +20,10 @@ public class Inp
} }
public static Inp fromItem(ItemLike in) { public static Inp fromItem(ItemLike in) {
return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build()); return new Inp(ForgeRegistries.ITEMS.getKey(in.asItem()).getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
} }
public static Inp fromTag(Tag.Named<Item> in) { public static Inp fromTag(TagKey<Item> in) {
return new Inp(in.getName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build()); return new Inp(in.location().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
} }
} }

View file

@ -1,10 +1,12 @@
package thetadev.constructionwand.data; package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraftforge.client.model.generators.ItemModelProvider; import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.items.ModItems; import thetadev.constructionwand.items.ModItems;
@ -12,14 +14,15 @@ import javax.annotation.Nonnull;
public class ItemModelGenerator extends ItemModelProvider public class ItemModelGenerator extends ItemModelProvider
{ {
public ItemModelGenerator(DataGenerator generator, ExistingFileHelper existingFileHelper) { public ItemModelGenerator(PackOutput packOutput, ExistingFileHelper existingFileHelper) {
super(generator, ConstructionWand.MODID, existingFileHelper); super(packOutput, ConstructionWand.MODID, existingFileHelper);
} }
@Override @Override
protected void registerModels() { protected void registerModels() {
for(Item item : ModItems.ALL_ITEMS) { for(RegistryObject<Item> itemObject : ModItems.ITEMS.getEntries()) {
String name = item.getRegistryName().getPath(); Item item = itemObject.get();
String name = ForgeRegistries.ITEMS.getKey(item).getPath();
if(item instanceof ICustomItemModel) if(item instanceof ICustomItemModel)
((ICustomItemModel) item).generateCustomItemModel(this, name); ((ICustomItemModel) item).generateCustomItemModel(this, name);

View file

@ -1,10 +1,11 @@
package thetadev.constructionwand.data; package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModData public class ModData
@ -12,14 +13,15 @@ public class ModData
@SubscribeEvent @SubscribeEvent
public static void gatherData(GatherDataEvent event) { public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator(); DataGenerator generator = event.getGenerator();
PackOutput packOutput = generator.getPackOutput();
ExistingFileHelper fileHelper = event.getExistingFileHelper(); ExistingFileHelper fileHelper = event.getExistingFileHelper();
if(event.includeServer()) { if(event.includeServer()) {
generator.addProvider(new RecipeGenerator(generator)); generator.addProvider(true, new RecipeGenerator(packOutput));
} }
if(event.includeClient()) { if(event.includeClient()) {
generator.addProvider(new ItemModelGenerator(generator, fileHelper)); generator.addProvider(true, new ItemModelGenerator(packOutput, fileHelper));
} }
} }
} }

View file

@ -1,14 +1,20 @@
package thetadev.constructionwand.data; package thetadev.constructionwand.data;
import net.minecraft.data.DataGenerator; import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.advancements.Criterion;
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider; import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.ShapedRecipeBuilder; import net.minecraft.data.recipes.ShapedRecipeBuilder;
import net.minecraft.data.recipes.SpecialRecipeBuilder; import net.minecraft.data.recipes.SpecialRecipeBuilder;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.ItemTags; import net.minecraft.tags.ItemTags;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer; import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.Tags; import net.minecraftforge.common.Tags;
import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistries;
@ -16,41 +22,40 @@ import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.crafting.RecipeWandUpgrade; import thetadev.constructionwand.crafting.RecipeWandUpgrade;
import thetadev.constructionwand.items.ModItems; import thetadev.constructionwand.items.ModItems;
import javax.annotation.Nonnull; import java.util.List;
import java.util.function.Consumer; import java.util.Optional;
public class RecipeGenerator extends RecipeProvider public class RecipeGenerator extends RecipeProvider {
{ public RecipeGenerator(PackOutput packOutput) {
public RecipeGenerator(DataGenerator generatorIn) { super(packOutput);
super(generatorIn);
} }
@Override @Override
protected void buildCraftingRecipes(@Nonnull Consumer<FinishedRecipe> consumer) { protected void buildRecipes(RecipeOutput output) {
wandRecipe(consumer, ModItems.WAND_STONE, Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS)); wandRecipe(output, ModItems.WAND_STONE.get(), Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS));
wandRecipe(consumer, ModItems.WAND_IRON, Inp.fromTag(Tags.Items.INGOTS_IRON)); wandRecipe(output, ModItems.WAND_IRON.get(), Inp.fromTag(Tags.Items.INGOTS_IRON));
wandRecipe(consumer, ModItems.WAND_DIAMOND, Inp.fromTag(Tags.Items.GEMS_DIAMOND)); wandRecipe(output, ModItems.WAND_DIAMOND.get(), Inp.fromTag(Tags.Items.GEMS_DIAMOND));
wandRecipe(consumer, ModItems.WAND_INFINITY, Inp.fromTag(Tags.Items.NETHER_STARS)); wandRecipe(output, 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(output, ModItems.CORE_ANGEL.get(), 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(output, ModItems.CORE_DESTRUCTION.get(), Inp.fromTag(Tags.Items.STORAGE_BLOCKS_DIAMOND), Inp.fromItem(Items.DIAMOND_PICKAXE));
specialRecipe(consumer, RecipeWandUpgrade.SERIALIZER); specialRecipe(output, RecipeWandUpgrade.SERIALIZER);
} }
private void wandRecipe(Consumer<FinishedRecipe> consumer, ItemLike wand, Inp material) { private void wandRecipe(RecipeOutput output, ItemLike wand, Inp material) {
ShapedRecipeBuilder.shaped(wand) ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, wand)
.define('X', material.ingredient) .define('X', material.ingredient)
.define('#', Tags.Items.RODS_WOODEN) .define('#', Tags.Items.RODS_WOODEN)
.pattern(" X") .pattern(" X")
.pattern(" # ") .pattern(" # ")
.pattern("# ") .pattern("# ")
.unlockedBy("has_item", inventoryTrigger(material.predicate)) .unlockedBy("has_item", inventoryTrigger(material.predicate))
.save(consumer); .save(output);
} }
private void coreRecipe(Consumer<FinishedRecipe> consumer, ItemLike core, Inp item1, Inp item2) { private void coreRecipe(RecipeOutput output, ItemLike core, Inp item1, Inp item2) {
ShapedRecipeBuilder.shaped(core) ShapedRecipeBuilder.shaped(RecipeCategory.MISC, core)
.define('O', item1.ingredient) .define('O', item1.ingredient)
.define('X', item2.ingredient) .define('X', item2.ingredient)
.define('#', Tags.Items.GLASS_PANES) .define('#', Tags.Items.GLASS_PANES)
@ -58,17 +63,17 @@ public class RecipeGenerator extends RecipeProvider
.pattern("#O#") .pattern("#O#")
.pattern("X# ") .pattern("X# ")
.unlockedBy("has_item", inventoryTrigger(item1.predicate)) .unlockedBy("has_item", inventoryTrigger(item1.predicate))
.save(consumer); .save(output);
} }
private void specialRecipe(Consumer<FinishedRecipe> consumer, SimpleRecipeSerializer<?> serializer) { private void specialRecipe(RecipeOutput output, SimpleCraftingRecipeSerializer<?> serializer) {
ResourceLocation name = ForgeRegistries.RECIPE_SERIALIZERS.getKey(serializer); ResourceLocation name = ForgeRegistries.RECIPE_SERIALIZERS.getKey(serializer);
SpecialRecipeBuilder.special(serializer).save(consumer, ConstructionWand.loc("dynamic/" + name.getPath()).toString()); SpecialRecipeBuilder.special(serializer).save(output, ConstructionWand.loc("dynamic/" + name.getPath()).toString());
} }
@Nonnull private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryTrigger(ItemPredicate... predicate) {
@Override return CriteriaTriggers.INVENTORY_CHANGED.createCriterion(
public String getName() { new InventoryChangeTrigger.TriggerInstance(Optional.empty(), MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, MinMaxBounds.Ints.ANY, List.of(predicate))
return ConstructionWand.MODID + " crafting recipes"; );
} }
} }

View file

@ -7,10 +7,12 @@ import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.registration.IRecipeRegistration; import mezz.jei.api.registration.IRecipeRegistration;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.NotNull;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.ConfigClient; import thetadev.constructionwand.basics.ConfigClient;
import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.ConfigServer;
@ -33,34 +35,36 @@ public class ConstructionWandJeiPlugin implements IModPlugin
private Component keyComboComponent(boolean shiftOpt, Component optkeyComponent) { private Component keyComboComponent(boolean shiftOpt, Component optkeyComponent) {
String key = shiftOpt ? "sneak_opt" : "sneak"; String key = shiftOpt ? "sneak_opt" : "sneak";
return new TranslatableComponent(baseKey + "key." + key, optkeyComponent).withStyle(ChatFormatting.BLUE); return Component.translatable(baseKey + "key." + key, optkeyComponent).withStyle(ChatFormatting.BLUE);
} }
@Override @Override
public void registerRecipes(IRecipeRegistration registration) { public void registerRecipes(@NotNull IRecipeRegistration registration) {
Component optkeyComponent = new TranslatableComponent(InputConstants.getKey(ConfigClient.OPT_KEY.get(), -1).getName()) Component optkeyComponent = Component.translatable(InputConstants.getKey(ConfigClient.OPT_KEY.get(), -1).getName())
.withStyle(ChatFormatting.BLUE); .withStyle(ChatFormatting.BLUE);
Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent); Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent);
Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent); Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent);
for(Item wand : ModItems.WANDS) { for(RegistryObject<Item> wandSupplier : ModItems.WANDS) {
Item wand = wandSupplier.get();
ConfigServer.WandProperties wandProperties = ConfigServer.getWandProperties(wand); 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()); Component durabilityComponent = Component.translatable(baseKey + "durability." + durabilityKey, wandProperties.getDurability());
registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM, registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM_STACK,
new TranslatableComponent(baseKey + "wand", Component.translatable(baseKey + "wand",
new TranslatableComponent(baseKeyItem + wand.getRegistryName().getPath()), Component.translatable(baseKeyItem + ForgeRegistries.ITEMS.getKey(wand).getPath()),
wandProperties.getLimit(), durabilityComponent, wandProperties.getLimit(), durabilityComponent, optkeyComponent, wandModeComponent, wandGuiComponent)
optkeyComponent, wandModeComponent, wandGuiComponent)
); );
} }
for(Item core : ModItems.CORES) { for(RegistryObject<Item> coreSupplier : ModItems.CORES) {
registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM, Item core = coreSupplier.get();
new TranslatableComponent(baseKey + core.getRegistryName().getPath()), registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM_STACK,
new TranslatableComponent(baseKey + "core", wandModeComponent) Component.translatable(baseKey + ForgeRegistries.ITEMS.getKey(core).getPath())
.append("\n\n")
.append(Component.translatable(baseKey + "core", wandModeComponent))
); );
} }
} }

View file

@ -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);
}
}

View file

@ -1,19 +1,19 @@
package thetadev.constructionwand.items; package thetadev.constructionwand.items;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.renderer.item.ItemProperties; import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.Tiers; import net.minecraft.world.item.Tiers;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.client.event.RegisterColorHandlersEvent;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistryEntry; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegisterEvent;
import net.minecraftforge.registries.RegistryObject;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.crafting.RecipeWandUpgrade; import thetadev.constructionwand.crafting.RecipeWandUpgrade;
@ -23,61 +23,44 @@ import thetadev.constructionwand.items.wand.ItemWand;
import thetadev.constructionwand.items.wand.ItemWandBasic; import thetadev.constructionwand.items.wand.ItemWandBasic;
import thetadev.constructionwand.items.wand.ItemWandInfinity; import thetadev.constructionwand.items.wand.ItemWandInfinity;
import java.util.Arrays;
import java.util.HashSet;
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) @Mod.EventBusSubscriber(modid = ConstructionWand.MODID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModItems public class ModItems
{ {
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ConstructionWand.MODID);
// Wands // Wands
public static final Item WAND_STONE = new ItemWandBasic("stone_wand", propWand(), Tiers.STONE); public static final RegistryObject<Item> WAND_STONE = ITEMS.register("stone_wand", () -> new ItemWandBasic(propWand(), Tiers.STONE));
public static final Item WAND_IRON = new ItemWandBasic("iron_wand", propWand(), Tiers.IRON); public static final RegistryObject<Item> WAND_IRON = ITEMS.register("iron_wand", () -> new ItemWandBasic(propWand(), Tiers.IRON));
public static final Item WAND_DIAMOND = new ItemWandBasic("diamond_wand", propWand(), Tiers.DIAMOND); public static final RegistryObject<Item> WAND_DIAMOND = ITEMS.register("diamond_wand", () -> new ItemWandBasic(propWand(), Tiers.DIAMOND));
public static final Item WAND_INFINITY = new ItemWandInfinity("infinity_wand", propWand()); public static final RegistryObject<Item> WAND_INFINITY = ITEMS.register("infinity_wand", () -> new ItemWandInfinity(propWand()));
// Cores // Cores
public static final Item CORE_ANGEL = new ItemCoreAngel("core_angel", propUpgrade()); public static final RegistryObject<Item> CORE_ANGEL = ITEMS.register("core_angel", () -> new ItemCoreAngel(propUpgrade()));
public static final Item CORE_DESTRUCTION = new ItemCoreDestruction("core_destruction", propUpgrade()); public static final RegistryObject<Item> CORE_DESTRUCTION = ITEMS.register("core_destruction", () -> new ItemCoreDestruction(propUpgrade()));
// Collections // Collections
public static final Item[] WANDS = {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY}; public static final RegistryObject<Item>[] WANDS = new RegistryObject[] {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY};
public static final Item[] CORES = {CORE_ANGEL, CORE_DESTRUCTION}; public static final RegistryObject<Item>[] CORES = new RegistryObject[] {CORE_ANGEL, CORE_DESTRUCTION};
public static final HashSet<Item> ALL_ITEMS = new HashSet<>();
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
IForgeRegistry<Item> r = event.getRegistry();
r.registerAll(WANDS);
ALL_ITEMS.addAll(Arrays.asList(WANDS));
registerItem(r, CORE_ANGEL);
registerItem(r, CORE_DESTRUCTION);
}
public static Item.Properties propWand() { public static Item.Properties propWand() {
return new Item.Properties().tab(CreativeModeTab.TAB_TOOLS); return new Item.Properties();
} }
private static Item.Properties propUpgrade() { private static Item.Properties propUpgrade() {
return new Item.Properties().tab(CreativeModeTab.TAB_MISC).stacksTo(1); return new Item.Properties().stacksTo(1);
}
private static void registerItem(IForgeRegistry<Item> reg, Item item) {
reg.register(item);
ALL_ITEMS.add(item);
} }
@SubscribeEvent @SubscribeEvent
public static void registerRecipeSerializers(RegistryEvent.Register<RecipeSerializer<?>> event) { public static void registerRecipeSerializers(RegisterEvent event) {
IForgeRegistry<RecipeSerializer<?>> r = event.getRegistry(); event.register(ForgeRegistries.Keys.RECIPE_SERIALIZERS, registry -> {
register(r, "wand_upgrade", RecipeWandUpgrade.SERIALIZER); registry.register("wand_upgrade", RecipeWandUpgrade.SERIALIZER);
});
} }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public static void registerModelProperties() { public static void registerModelProperties() {
for(Item item : WANDS) { for(RegistryObject<Item> itemSupplier : WANDS) {
Item item = itemSupplier.get();
ItemProperties.register( ItemProperties.register(
item, ConstructionWand.loc("using_core"), item, ConstructionWand.loc("using_core"),
(stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 : (stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 :
@ -87,16 +70,25 @@ public class ModItems
} }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public static void registerItemColors() { @SubscribeEvent
ItemColors colors = Minecraft.getInstance().getItemColors(); public static void registerItemColors(RegisterColorHandlersEvent.Item event) {
for(RegistryObject<Item> itemSupplier : WANDS) {
for(Item item : WANDS) { Item item = itemSupplier.get();
colors.register((stack, layer) -> (layer == 1 && stack.getItem() instanceof ItemWand) ? event.register((stack, layer) -> (layer == 1 && stack.getItem() instanceof ItemWand) ?
new WandOptions(stack).cores.get().getColor() : -1, item); new WandOptions(stack).cores.get().getColor() : -1, item);
} }
} }
private static <V extends IForgeRegistryEntry<V>> void register(IForgeRegistry<V> reg, String name, IForgeRegistryEntry<V> thing) { @SubscribeEvent
reg.register(thing.setRegistryName(ConstructionWand.loc(name))); public static void addCreative(BuildCreativeModeTabContentsEvent event) {
if (event.getTabKey() == CreativeModeTabs.TOOLS_AND_UTILITIES) {
for(RegistryObject<Item> itemSupplier : WANDS) {
event.accept(itemSupplier);
}
} else if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) {
for(RegistryObject<Item> itemSupplier : CORES) {
event.accept(itemSupplier);
}
}
} }
} }

View file

@ -2,7 +2,7 @@ package thetadev.constructionwand.items.core;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -10,22 +10,24 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandCore; import thetadev.constructionwand.api.IWandCore;
import thetadev.constructionwand.items.ItemBase;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.List; 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) { public ItemCore(Properties properties) {
super(name, properties); super(properties);
} }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List<Component> lines, @Nonnull TooltipFlag extraInfo) { public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List<Component> lines, @Nonnull TooltipFlag extraInfo) {
lines.add(new TranslatableComponent(ConstructionWand.MODID + ".option.cores." + getRegistryName().toString() + ".desc") lines.add(
.withStyle(ChatFormatting.GRAY)); Component.translatable(ConstructionWand.MODID + ".option.cores." + getRegistryName().toString() + ".desc")
lines.add(new TranslatableComponent(ConstructionWand.MODID + ".tooltip.core_tip") .withStyle(ChatFormatting.GRAY)
.withStyle(ChatFormatting.AQUA)); );
lines.add(
Component.translatable(ConstructionWand.MODID + ".tooltip.core_tip").withStyle(ChatFormatting.AQUA)
);
} }
} }

View file

@ -1,12 +1,14 @@
package thetadev.constructionwand.items.core; package thetadev.constructionwand.items.core;
import net.minecraft.resources.ResourceLocation;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.wand.action.ActionAngel; import thetadev.constructionwand.wand.action.ActionAngel;
public class ItemCoreAngel extends ItemCore public class ItemCoreAngel extends ItemCore
{ {
public ItemCoreAngel(String name, Properties properties) { public ItemCoreAngel(Properties properties) {
super(name, properties); super(properties);
} }
@Override @Override
@ -18,4 +20,9 @@ public class ItemCoreAngel extends ItemCore
public IWandAction getWandAction() { public IWandAction getWandAction() {
return new ActionAngel(); return new ActionAngel();
} }
@Override
public ResourceLocation getRegistryName() {
return ConstructionWand.loc("core_angel");
}
} }

View file

@ -1,12 +1,14 @@
package thetadev.constructionwand.items.core; package thetadev.constructionwand.items.core;
import net.minecraft.resources.ResourceLocation;
import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.api.IWandAction; import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.wand.action.ActionDestruction; import thetadev.constructionwand.wand.action.ActionDestruction;
public class ItemCoreDestruction extends ItemCore public class ItemCoreDestruction extends ItemCore
{ {
public ItemCoreDestruction(String name, Properties properties) { public ItemCoreDestruction(Properties properties) {
super(name, properties); super(properties);
} }
@Override @Override
@ -18,4 +20,9 @@ public class ItemCoreDestruction extends ItemCore
public IWandAction getWandAction() { public IWandAction getWandAction() {
return new ActionDestruction(); return new ActionDestruction();
} }
@Override
public ResourceLocation getRegistryName() {
return ConstructionWand.loc("core_destruction");
}
} }

View file

@ -3,12 +3,11 @@ package thetadev.constructionwand.items.wand;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; 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.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.context.UseOnContext;
@ -25,17 +24,16 @@ import thetadev.constructionwand.basics.option.IOption;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.data.ICustomItemModel; import thetadev.constructionwand.data.ICustomItemModel;
import thetadev.constructionwand.data.ItemModelGenerator; import thetadev.constructionwand.data.ItemModelGenerator;
import thetadev.constructionwand.items.ItemBase;
import thetadev.constructionwand.wand.WandJob; import thetadev.constructionwand.wand.WandJob;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; 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) { public ItemWand(Properties properties) {
super(name, properties); super(properties);
} }
@Nonnull @Nonnull
@ -68,7 +66,7 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel
// Right click: Place angel block // Right click: Place angel block
WandJob job = getWandJob(player, world, BlockHitResult.miss(player.getLookAngle(), WandJob job = getWandJob(player, world, BlockHitResult.miss(player.getLookAngle(),
WandUtil.fromVector(player.getLookAngle()), WandUtil.playerPos(player)), stack); WandUtil.fromVector(player.getLookAngle()), player.blockPosition()), stack);
return job.doIt() ? InteractionResultHolder.success(stack) : InteractionResultHolder.fail(stack); return job.doIt() ? InteractionResultHolder.success(stack) : InteractionResultHolder.fail(stack);
} }
return InteractionResultHolder.fail(stack); return InteractionResultHolder.fail(stack);
@ -107,35 +105,35 @@ public abstract class ItemWand extends ItemBase implements ICustomItemModel
if(Screen.hasShiftDown()) { if(Screen.hasShiftDown()) {
for(int i = 1; i < options.allOptions.length; i++) { for(int i = 1; i < options.allOptions.length; i++) {
IOption<?> opt = options.allOptions[i]; IOption<?> opt = options.allOptions[i];
lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA) lines.add(Component.translatable(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
.append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.GRAY)) .append(Component.translatable(opt.getValueTranslation()).withStyle(ChatFormatting.GRAY))
); );
} }
if(!options.cores.getUpgrades().isEmpty()) { if(!options.cores.getUpgrades().isEmpty()) {
lines.add(new TextComponent("")); lines.add(Component.literal(""));
lines.add(new TranslatableComponent(langTooltip + "cores").withStyle(ChatFormatting.GRAY)); lines.add(Component.translatable(langTooltip + "cores").withStyle(ChatFormatting.GRAY));
for(IWandCore core : options.cores.getUpgrades()) { for(IWandCore core : options.cores.getUpgrades()) {
lines.add(new TranslatableComponent(options.cores.getKeyTranslation() + "." + core.getRegistryName().toString())); lines.add(Component.translatable(options.cores.getKeyTranslation() + "." + core.getRegistryName().toString()));
} }
} }
} }
// Default tooltip: show block limit + active wand core // Default tooltip: show block limit + active wand core
else { else {
IOption<?> opt = options.allOptions[0]; IOption<?> opt = options.allOptions[0];
lines.add(new TranslatableComponent(langTooltip + "blocks", limit).withStyle(ChatFormatting.GRAY)); lines.add(Component.translatable(langTooltip + "blocks", limit).withStyle(ChatFormatting.GRAY));
lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA) lines.add(Component.translatable(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
.append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.WHITE))); .append(Component.translatable(opt.getValueTranslation()).withStyle(ChatFormatting.WHITE)));
lines.add(new TranslatableComponent(langTooltip + "shift").withStyle(ChatFormatting.AQUA)); lines.add(Component.translatable(langTooltip + "shift").withStyle(ChatFormatting.AQUA));
} }
} }
public static void optionMessage(Player player, IOption<?> option) { public static void optionMessage(Player player, IOption<?> option) {
player.displayClientMessage( player.displayClientMessage(
new TranslatableComponent(option.getKeyTranslation()).withStyle(ChatFormatting.AQUA) Component.translatable(option.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
.append(new TranslatableComponent(option.getValueTranslation()).withStyle(ChatFormatting.WHITE)) .append(Component.translatable(option.getValueTranslation()).withStyle(ChatFormatting.WHITE))
.append(new TextComponent(" - ").withStyle(ChatFormatting.GRAY)) .append(Component.literal(" - ").withStyle(ChatFormatting.GRAY))
.append(new TranslatableComponent(option.getDescTranslation()).withStyle(ChatFormatting.WHITE)) .append(Component.translatable(option.getDescTranslation()).withStyle(ChatFormatting.WHITE))
, true); , true);
} }

View file

@ -10,8 +10,8 @@ public class ItemWandBasic extends ItemWand
{ {
private final Tier tier; private final Tier tier;
public ItemWandBasic(String name, Properties properties, Tier tier) { public ItemWandBasic(Properties properties, Tier tier) {
super(name, properties.durability(tier.getUses())); super(properties.durability(tier.getUses()));
this.tier = tier; this.tier = tier;
} }

View file

@ -3,7 +3,7 @@ package thetadev.constructionwand.items.wand;
public class ItemWandInfinity extends ItemWand public class ItemWandInfinity extends ItemWand
{ {
public ItemWandInfinity(String name, Properties properties) { public ItemWandInfinity(Properties properties) {
super(name, properties.stacksTo(1).fireResistant()); super(properties.stacksTo(1).fireResistant());
} }
} }

View file

@ -0,0 +1,50 @@
package thetadev.constructionwand.network;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.ChannelBuilder;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.SimpleChannel;
import static thetadev.constructionwand.ConstructionWand.MODID;
public final class ModMessages {
private static SimpleChannel INSTANCE;
private static final int PROTOCOL_VERSION = 1;
private ModMessages() {
}
public static void register() {
INSTANCE = ChannelBuilder.named(new ResourceLocation(MODID, "main")).networkProtocolVersion(PROTOCOL_VERSION).simpleChannel();
int packetIndex = 0;
// Server -> Client
INSTANCE.messageBuilder(PacketUndoBlocks.class, packetIndex++, NetworkDirection.PLAY_TO_CLIENT)
.encoder(PacketUndoBlocks::encode)
.decoder(PacketUndoBlocks::decode)
.consumerMainThread(PacketUndoBlocks.Handler::handle)
.add();
// Client -> Server
INSTANCE.messageBuilder(PacketQueryUndo.class, packetIndex++, NetworkDirection.PLAY_TO_SERVER)
.encoder(PacketQueryUndo::encode)
.decoder(PacketQueryUndo::decode)
.consumerMainThread(PacketQueryUndo.Handler::handle)
.add();
INSTANCE.messageBuilder(PacketWandOption.class, packetIndex, NetworkDirection.PLAY_TO_SERVER)
.encoder(PacketWandOption::encode)
.decoder(PacketWandOption::decode)
.consumerMainThread(PacketWandOption.Handler::handle)
.add();
}
public static <MSG> void sendToServer(MSG message) {
INSTANCE.send(message, PacketDistributor.SERVER.noArg());
}
public static <MSG> void sendToPlayer(MSG message, ServerPlayer player) {
INSTANCE.send(message, PacketDistributor.PLAYER.with(player));
}
}

View file

@ -2,11 +2,9 @@ package thetadev.constructionwand.network;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.event.network.CustomPayloadEvent;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import java.util.function.Supplier;
public class PacketQueryUndo public class PacketQueryUndo
{ {
public boolean undoPressed; public boolean undoPressed;
@ -25,10 +23,10 @@ public class PacketQueryUndo
public static class Handler public static class Handler
{ {
public static void handle(final PacketQueryUndo msg, final Supplier<NetworkEvent.Context> ctx) { public static void handle(final PacketQueryUndo msg, final CustomPayloadEvent.Context ctx) {
if(!ctx.get().getDirection().getReceptionSide().isServer()) return; if(!ctx.getDirection().getReceptionSide().isServer()) return;
ServerPlayer player = ctx.get().getSender(); ServerPlayer player = ctx.getSender();
if(player == null) return; if(player == null) return;
ConstructionWand.instance.undoHistory.updateClient(player, msg.undoPressed); ConstructionWand.instance.undoHistory.updateClient(player, msg.undoPressed);

View file

@ -2,12 +2,11 @@ package thetadev.constructionwand.network;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.event.network.CustomPayloadEvent;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
public class PacketUndoBlocks public class PacketUndoBlocks
{ {
@ -38,13 +37,13 @@ public class PacketUndoBlocks
public static class Handler public static class Handler
{ {
public static void handle(final PacketUndoBlocks msg, final Supplier<NetworkEvent.Context> ctx) { public static void handle(final PacketUndoBlocks msg, final CustomPayloadEvent.Context ctx) {
if(!ctx.get().getDirection().getReceptionSide().isClient()) return; if(!ctx.getDirection().getReceptionSide().isClient()) return;
//ConstructionWand.LOGGER.debug("PacketUndoBlocks received, Blocks: " + msg.undoBlocks.size()); //ConstructionWand.LOGGER.debug("PacketUndoBlocks received, Blocks: " + msg.undoBlocks.size());
ConstructionWand.instance.renderBlockPreview.undoBlocks = msg.undoBlocks; ConstructionWand.instance.renderBlockPreview.undoBlocks = msg.undoBlocks;
ctx.get().setPacketHandled(true); ctx.setPacketHandled(true);
} }
} }
} }

View file

@ -3,14 +3,12 @@ package thetadev.constructionwand.network;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.event.network.CustomPayloadEvent;
import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.basics.option.IOption; import thetadev.constructionwand.basics.option.IOption;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWand;
import java.util.function.Supplier;
public class PacketWandOption public class PacketWandOption
{ {
public final String key; public final String key;
@ -39,10 +37,10 @@ public class PacketWandOption
public static class Handler public static class Handler
{ {
public static void handle(final PacketWandOption msg, final Supplier<NetworkEvent.Context> ctx) { public static void handle(final PacketWandOption msg, final CustomPayloadEvent.Context ctx) {
if(!ctx.get().getDirection().getReceptionSide().isServer()) return; if(!ctx.getDirection().getReceptionSide().isServer()) return;
ServerPlayer player = ctx.get().getSender(); ServerPlayer player = ctx.getSender();
if(player == null) return; if(player == null) return;
ItemStack wand = WandUtil.holdingWand(player); ItemStack wand = WandUtil.holdingWand(player);

View file

@ -1,8 +1,8 @@
package thetadev.constructionwand.wand; package thetadev.constructionwand.wand;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
@ -16,7 +16,6 @@ import thetadev.constructionwand.api.IWandAction;
import thetadev.constructionwand.api.IWandSupplier; import thetadev.constructionwand.api.IWandSupplier;
import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.ModStats; import thetadev.constructionwand.basics.ModStats;
import thetadev.constructionwand.basics.WandUtil;
import thetadev.constructionwand.basics.option.WandOptions; import thetadev.constructionwand.basics.option.WandOptions;
import thetadev.constructionwand.items.ModItems; import thetadev.constructionwand.items.ModItems;
import thetadev.constructionwand.items.wand.ItemWand; import thetadev.constructionwand.items.wand.ItemWand;
@ -74,7 +73,7 @@ public class WandJob
public void getSnapshots() { public void getSnapshots() {
int limit; int limit;
// Infinity wand gets enhanced limit in creative mode // 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)); else limit = Math.min(wandItem.remainingDurability(wand), wandAction.getLimit(wand));
if(rayTraceResult.getType() == HitResult.Type.BLOCK) if(rayTraceResult.getType() == HitResult.Type.BLOCK)
@ -87,6 +86,10 @@ public class WandJob
return placeSnapshots.stream().map(ISnapshot::getPos).collect(Collectors.toSet()); return placeSnapshots.stream().map(ISnapshot::getPos).collect(Collectors.toSet());
} }
public int blockCount() {
return placeSnapshots.size();
}
public boolean doIt() { public boolean doIt() {
ArrayList<ISnapshot> executed = new ArrayList<>(); ArrayList<ISnapshot> executed = new ArrayList<>();
@ -99,7 +102,7 @@ public class WandJob
// If the item cant be taken, undo the placement // If the item cant be taken, undo the placement
if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) { if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) {
executed.add(snapshot); executed.add(snapshot);
wand.hurt(1, player.getRandom(), (ServerPlayer) player); wand.hurtAndBreak(1, player, e -> e.broadcastBreakEvent(InteractionHand.MAIN_HAND));
} }
else { else {
ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " + ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " +
@ -115,7 +118,7 @@ public class WandJob
// Play place sound // Play place sound
if(!placeSnapshots.isEmpty()) { if(!placeSnapshots.isEmpty()) {
SoundType sound = placeSnapshots.get(0).getBlockState().getSoundType(); SoundType sound = placeSnapshots.get(0).getBlockState().getSoundType();
world.playSound(null, WandUtil.playerPos(player), sound.getPlaceSound(), SoundSource.BLOCKS, sound.volume, sound.pitch); world.playSound(null, player.blockPosition(), sound.getPlaceSound(), SoundSource.BLOCKS, sound.volume, sound.pitch);
// Add to job history for undo // Add to job history for undo
ConstructionWand.instance.undoHistory.add(player, world, placeSnapshots); ConstructionWand.instance.undoHistory.add(player, world, placeSnapshots);

View file

@ -60,8 +60,7 @@ public class ActionAngel implements IWandAction
Vec3 playerVec = WandUtil.entityPositionVec(player); Vec3 playerVec = WandUtil.entityPositionVec(player);
Vec3 lookVec = player.getLookAngle().multiply(2, 2, 2); Vec3 lookVec = player.getLookAngle().multiply(2, 2, 2);
Vec3 placeVec = playerVec.add(lookVec); Vec3 placeVec = playerVec.add(lookVec);
BlockPos currentPos = WandUtil.posFromVec(placeVec);
BlockPos currentPos = new BlockPos(placeVec);
PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentPos, rayTraceResult, null); PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentPos, rayTraceResult, null);
if(snapshot != null) placeSnapshots.add(snapshot); if(snapshot != null) placeSnapshots.add(snapshot);

View file

@ -6,8 +6,6 @@ import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level; 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.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties; 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.Property;
@ -100,7 +98,7 @@ public class PlaceSnapshot implements ISnapshot
// Can block be placed? // Can block be placed?
BlockState blockState = item.getBlock().getStateForPlacement(ctx); BlockState blockState = item.getBlock().getStateForPlacement(ctx);
if(blockState == null) return null; if(blockState == null || !blockState.canSurvive(world, pos)) return null;
// Forbidden Tile Entity? // Forbidden Tile Entity?
if(!WandUtil.isTEAllowed(blockState)) return null; if(!WandUtil.isTEAllowed(blockState)) return null;
@ -108,11 +106,6 @@ public class PlaceSnapshot implements ISnapshot
// No entities colliding? // No entities colliding?
if(WandUtil.entitiesCollidingWithBlock(world, blockState, pos)) return null; 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 // Copy block properties from supporting block
if(targetMode && supportingBlock != null) { if(targetMode && supportingBlock != null) {
// Block properties to be copied (alignment/rotation properties) // Block properties to be copied (alignment/rotation properties)

View file

@ -11,7 +11,7 @@ import net.minecraft.world.level.Level;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import thetadev.constructionwand.ConstructionWand; import thetadev.constructionwand.ConstructionWand;
import thetadev.constructionwand.basics.ConfigServer; import thetadev.constructionwand.basics.ConfigServer;
import thetadev.constructionwand.basics.WandUtil; import thetadev.constructionwand.network.ModMessages;
import thetadev.constructionwand.network.PacketUndoBlocks; import thetadev.constructionwand.network.PacketUndoBlocks;
import java.util.*; import java.util.*;
@ -40,7 +40,7 @@ public class UndoHistory
} }
public void updateClient(Player player, boolean ctrlDown) { public void updateClient(Player player, boolean ctrlDown) {
Level world = player.level; Level world = player.level();
if(world.isClientSide) return; if(world.isClientSide) return;
// Set state of CTRL key // Set state of CTRL key
@ -60,7 +60,7 @@ public class UndoHistory
} }
PacketUndoBlocks packet = new PacketUndoBlocks(positions); PacketUndoBlocks packet = new PacketUndoBlocks(positions);
ConstructionWand.instance.HANDLER.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), packet); ModMessages.sendToPlayer(packet, (ServerPlayer) player);
} }
public boolean isUndoActive(Player player) { public boolean isUndoActive(Player player) {
@ -142,7 +142,7 @@ public class UndoHistory
// Play teleport sound // Play teleport sound
SoundEvent sound = SoundEvents.CHORUS_FRUIT_TELEPORT; SoundEvent sound = SoundEvents.CHORUS_FRUIT_TELEPORT;
world.playSound(null, WandUtil.playerPos(player), sound, SoundSource.PLAYERS, 1.0F, 1.0F); world.playSound(null, player.blockPosition(), sound, SoundSource.PLAYERS, 1.0F, 1.0F);
return true; return true;
} }

View file

@ -1,5 +1,5 @@
modLoader = "javafml" modLoader = "javafml"
loaderVersion = "[38,)" loaderVersion = "[48,)"
license = "MIT License" license = "MIT License"
[[mods]] [[mods]]
modId="constructionwand" modId="constructionwand"
@ -20,12 +20,12 @@ This is my first minecraft mod. May the odds be ever in your favor.
[[dependencies.constructionwand]] [[dependencies.constructionwand]]
modId="forge" modId="forge"
mandatory = true mandatory = true
versionRange = "[38,)" versionRange = "[48,)"
ordering = "NONE" ordering = "NONE"
side="BOTH" side="BOTH"
[[dependencies.constructionwand]] [[dependencies.constructionwand]]
modId="minecraft" modId="minecraft"
mandatory = true mandatory = true
versionRange = "[1.18, 1.19)" versionRange = "[1.20.2, 1.21)"
ordering = "NONE" ordering = "NONE"
side="BOTH" side="BOTH"

View file

@ -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$を押しながらホイールスクロールすると設置方向(水平、垂直、南北、東西、無制限)を変更できます。\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§コンテナ§0§r\nシュルカーボックス、バンドル、その他のModで追加されたコンテナは杖で建築を行った際に資材を供給できます。\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": "杖を用いてブロックを設置"
}

View file

@ -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": "완드로 배치한 블록 수"
}

View file

@ -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"
}

View file

@ -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": "Блоки, размещённые при помощи Жезла"
}

View file

@ -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": "使用建筑手杖所放置的方块"
}

View file

@ -1,6 +1,6 @@
{ {
"pack": { "pack": {
"description": "ConstructionWand resources", "description": "ConstructionWand resources",
"pack_format": 8 "pack_format": 18
} }
} }