Compare commits
68 commits
1.16.1-1.7
...
1.18
Author | SHA1 | Date | |
---|---|---|---|
|
5d4fe7454a | ||
31bfbdb7f1 | |||
ce0012dc3c | |||
dc21a8882f | |||
2497f85800 | |||
|
d006a24ab7 | ||
|
e83cae7ae2 | ||
|
a9f703e28f | ||
d69901c0fa | |||
aefb3e138b | |||
710a6a7ba1 | |||
7a89175bf1 | |||
210baba4cd | |||
0a1c0f2926 | |||
030d5f72f2 | |||
f86a429beb | |||
84af7b7227 | |||
721d7543a9 | |||
d944f21655 | |||
138e66e4b7 | |||
|
439783744c | ||
bc0668a1b7 | |||
0ca129122e | |||
|
3d62df6ac5 | ||
5be6d5c788 | |||
bb48292c6f | |||
c60260ea5c | |||
8fd5a09363 | |||
5cad07d34d | |||
9b036cdc50 | |||
45da4ad7a7 | |||
e2394ff05e | |||
6b555d2077 | |||
489e050a7f | |||
f2d99a8251 | |||
ddbe201155 | |||
f60692f6d8 | |||
cdba987f8d | |||
5b29ecca80 | |||
81d92aa494 | |||
503019af9a | |||
ba99d1e309 | |||
fff371abf6 | |||
77f1f03210 | |||
cd55ff3c09 | |||
82f83898fe | |||
39e02d8fe6 | |||
ebf3ae9518 | |||
243ac0e56e | |||
6675412bd1 | |||
28bd5a7c70 | |||
997aa5c105 | |||
5a7143aa82 | |||
a9c6a1c5be | |||
a0317115b7 | |||
6618784804 | |||
bb3d36fa56 | |||
39dc7204cd | |||
b914deb7cf | |||
a90ced6395 | |||
1af803f8bf | |||
933fbea382 | |||
a7dbdcec0d | |||
ea20d44d61 | |||
224c0350b3 | |||
5714f77092 | |||
94aa1a89e8 | |||
8026f877c0 |
3
.gitignore
vendored
|
@ -24,4 +24,5 @@ run
|
|||
# Files from Forge MDK
|
||||
forge*changelog.txt
|
||||
|
||||
.cache
|
||||
.cache
|
||||
logs
|
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
Copyright (c) 2021 ThetaDev
|
||||
MIT LICENSE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
132
README.md
|
@ -1,62 +1,134 @@
|
|||
# Construction Wand
|
||||
With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're facing.
|
||||
Sneak+Right click to activate angel mode which allows you to place a block at the opposite side of the block facing you.
|
||||
If you concentrate enough, you can even conjure a block in mid air!
|
||||
|
||||

|
||||
With a Construction Wand you can place multiple blocks (up to 1024) at once, extending your build on the side you're
|
||||
facing. If that's not enough: you can upgrade your wand with additional cores, allowing you to place a block behind the
|
||||
block you are facing, conjure blocks in mid air or destroy lots of blocks very fast.
|
||||
|
||||

|
||||
|
||||
**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)
|
||||
instructions.
|
||||
|
||||
## Wands
|
||||
There are basic wands made from stone, iron and diamond and the Infinity wand.
|
||||
Wand properties can be changed in the config.
|
||||
|
||||
| Wand | Durability | Max. Blocks | Angel distance |
|
||||
|----------|-------------|-------------|----------------|
|
||||
| Stone | 131 | 9 | No angel mode |
|
||||
| Iron | 250 | 27 | 1 |
|
||||
| Diamond | 1561 | 128 | 4 |
|
||||
| Infinity | Unbreakable | 1024 | 8 |
|
||||
There are basic wands made from stone, iron and diamond and the Infinity wand. Wands from higher tiers are more powerful
|
||||
and last longer. These properties can be changed in the config.
|
||||
|
||||
| Wand | Durability | Max. Blocks | Upgradeable | Angel distance | Max. Blocks (Destroy) |
|
||||
|----------|-------------|-------------|-------------|----------------|-----------------------|
|
||||
| Stone | 131 | 9 | No | - | - |
|
||||
| Iron | 250 | 27 | Yes | 1 | 9 |
|
||||
| Diamond | 1561 | 128 | Yes | 4 | 25 |
|
||||
| Infinity | Unbreakable | 1024 | Yes | 8 | 81 |
|
||||
|
||||
## Crafting
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Modes
|
||||
**Default mode:** Extends your build on the side facing you. Maximum number of blocks depends on wand tier. SHIFT+scroll to change the placement mode (Horizontal, Vertical, North/South, East/West, No lock).
|
||||
## Keybindings
|
||||
|
||||
**Angel mode:** Places a block on the opposite side of the block (or row of blocks) you are facing. Maximum distance depends on wand tier. Right click empty space to place a block in midair (similar to angel blocks, hence the name). To do that, you'll need to have the block you want to place in your offhand. You can't place a block in midair if you've fallen more than 10 blocks deep (no easy rescue tool from falling into the void).
|
||||
To change a wand's core and options or undo your placement you need to hold down the wand option key
|
||||
(refered as OPTKEY). By default, this is CTRL, but it can be changed in the client config file. To prevent unwanted
|
||||
changes to your options, you can configure that you have to hold down SNEAK as well to change options or open up the
|
||||
GUI.
|
||||
|
||||
You can change the wand mode using the option screen or by SHIFT+Left clicking empty space.
|
||||
## Wand cores
|
||||
|
||||
Wand cores are magical gemstones that make your wand do its thing.
|
||||
|
||||
Every wand comes with a Construction core. You can make your wand even better by putting
|
||||
aditional cores into it. Put your new core together with your wand in a
|
||||
crafting grid to install it. To switch between cores, hold down OPTKEY and left click empty space
|
||||
with your wand.
|
||||
Stone wands can't be upgraded.
|
||||
|
||||
**Construction core**
|
||||
|
||||
Extend your build on the side facing you. Maximum number of blocks depends on wand tier.
|
||||
Hold down OPTKEY and scroll to change placement restriction
|
||||
(Horizontal, Vertical, North/South, East/West, No lock).
|
||||
|
||||
**Angel core**
|
||||
|
||||
Place a block on the opposite side of the block (or row of blocks) you are facing. Maximum distance depends
|
||||
on wand tier. Right click empty space to place a block in midair (similar to angel blocks, hence the name).
|
||||
To do that, you'll need to have the block you want to place in your offhand. You can't place a block in midair
|
||||
if you've fallen more than 10 blocks deep (no easy rescue tool from falling into the void).
|
||||
|
||||
**Destruction core**
|
||||
|
||||
Destroy blocks (no tile entities) on the side facing you. Maximum number of blocks depends on wand tier.
|
||||
Restrictions work just like with the Construction core. Destroyed blocks disappear into the void,
|
||||
you can use the undo feature if you've made a mistake.
|
||||
|
||||
## Options
|
||||
SHIFT+Right clicking empty space opens the option screen of your wand.
|
||||
SNEAK+OPTKEY+Right clicking empty space opens the option screen of your wand.
|
||||
|
||||

|
||||
|
||||
**Restriction:** If restriction is enabled the wand will only place blocks in one row or column (choose between North/South, East/West on a horizontal plane and Horizontal, Vertical on a vertical plane). If the direction lock is switched off, the wand will extend the entire face of the building it's pointed at. This option has no effect in Angel mode.
|
||||
**Restriction:** If restriction is enabled the wand will only place blocks in one row or column
|
||||
(choose between North/South, East/West on a horizontal plane and Horizontal, Vertical on a vertical plane).
|
||||
If the direction lock is switched off, the wand will extend the entire face of the building it's pointed at. This option
|
||||
has no effect if the angel core is enabled.
|
||||
|
||||
**Direction:** If set to "Player" the wand places blocks in the same direction as if they were placed by yourself. Target mode places the blocks in the same direction as their supporting block. See the picture below:
|
||||
**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:
|
||||
|
||||

|
||||
|
||||
**Replacement:** Enables/disables the replacement of replaceable blocks (Fluids, snow, tallgrass).
|
||||
|
||||
**Matching:** Select which blocks are extended by the wand. If set to "EXACT" it will only extend blocks that are exactly the same as the selected block.
|
||||
"SIMILAR" will treat similar blocks equally (e.g. extend dirt and grass blocks).
|
||||
**Matching:** Select which blocks are extended by the wand. If set to "EXACT" it will only extend blocks that
|
||||
are exactly the same as the selected block.<br>
|
||||
"SIMILAR" will treat similar blocks equally (e.g. extend dirt and grass blocks).<br>
|
||||
"ANY" will extend any block on the face of the building you're looking at.
|
||||
|
||||
**Random:** If random mode is enabled, the wand places random blocks from your hotbar. ~~Shamelessly stolen~~ Inspired by the Trowel from Quark.
|
||||
**Random:** If random mode is enabled, the wand places random blocks from your hotbar.<br>
|
||||
~~Shamelessly stolen~~ Inspired by the Trowel from Quark.
|
||||
|
||||
## Undo
|
||||
|
||||
Holding down Sneak+OPTKEY while looking at a block will show you the last blocks you placed with a green border around
|
||||
them. Sneak+OPTKEY+Right clicking any of them will undo the operation, giving you all the items back. If you used the
|
||||
Destruction core, it will restore the blocks.
|
||||
|
||||
## Additional features
|
||||
- If you have shulker boxes in your inventory filled with blocks, the wand can pull them out and place them
|
||||
|
||||
- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana.
|
||||
- Shulker boxes, bundles (MC 1.17+) and many containers from other mods can provide building blocks for the wand
|
||||
|
||||
- Holding down SHIFT+CTRL while looking at a blocks will show you the last blocks you placed with a green border around them. SHIFT+CTRL+Right clickking any of them will undo the operation, giving you all the items back.
|
||||
- Botania compatibility: The Black Hole Talisman can supply blocks just like shulker boxes can. Having a Rod of the
|
||||
Lands / Rod of the Depths in your inventory will provide you with infinite dirt/cobble at the cost of Mana.
|
||||
|
||||
- Having blocks in your offhand will place them instead of the block you're looking at
|
||||
|
||||
- Look at your statisics to see how many blocks you have placed using your wand
|
||||
|
||||
- **1.16+ only:** The Infinity Wand won't burn in lava just like netherite gear.
|
||||
- **1.16+ only:** The Infinity Wand won't burn in lava just like netherite gear.
|
||||
|
||||
- Ingame documentation with Just Enough Items (JEI)
|
||||
|
||||
## TileEntity Blacklist
|
||||
|
||||
Some modded TileEntitys can cause issues when placed using a wand. They may turn into invisible and
|
||||
unremovable ghost blocks, become unbreakable or cause other unwanted effects.
|
||||
|
||||
That's why I've included a Black/Whitelist system
|
||||
for TileEntities in CW Version 1.7. Chisels&Bits blocks are blacklisted by default.
|
||||
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
|
||||
an issue, commenting on Curse or editing the default blacklist yourself
|
||||
(it is located at https://github.com/Theta-Dev/ConstructionWand/blob/1.16.2/src/main/java/thetadev/constructionwand/basics/ConfigServer.java#L28)
|
||||
and making a PR.
|
||||
|
||||
## Contributions and #Hacktoberfest
|
||||
|
||||
As #Hacktoberfest now requires repo owners to opt in, I added the tag to this repository.
|
||||
|
||||
I'd really appreciate translations. Currently, ConstructionWand is available in English, German and Swedish.
|
||||
If you speak any other language you can help translate the mod and add a new language file
|
||||
under `src/main/resources/assets/constructionwand/lang/`.
|
||||
|
|
16
build.gradle
|
@ -1,11 +1,10 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net/maven' }
|
||||
jcenter()
|
||||
maven { url = 'https://maven.minecraftforge.net' }
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
|
||||
}
|
||||
}
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
|
@ -17,10 +16,10 @@ version = "${mcversion}-${version_major}.${version_minor}"
|
|||
group = "${author}.${modid}"
|
||||
archivesBaseName = "${modid}"
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||
|
||||
minecraft {
|
||||
mappings channel: 'snapshot', version: project.mcp_mappings
|
||||
mappings channel: project.mcp_channel, version: project.mcp_mappings
|
||||
|
||||
runs {
|
||||
client {
|
||||
|
@ -89,6 +88,10 @@ repositories {
|
|||
maven {
|
||||
url = "https://maven.theillusivec4.top/"
|
||||
}
|
||||
maven {
|
||||
name = "JEI Maven"
|
||||
url "https://dvs1.progwml6.com/files/maven"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -98,6 +101,9 @@ dependencies {
|
|||
version: "${project.mcversion}-${project.forgeversion}"
|
||||
])
|
||||
|
||||
compileOnly fg.deobf("mezz.jei:${jei_version}:api")
|
||||
runtimeOnly fg.deobf("mezz.jei:${jei_version}")
|
||||
|
||||
compileOnly fg.deobf([
|
||||
group: "vazkii.botania",
|
||||
name: "Botania",
|
||||
|
|
|
@ -4,11 +4,15 @@ org.gradle.daemon=false
|
|||
author=thetadev
|
||||
modid=constructionwand
|
||||
|
||||
mcversion=1.16.2
|
||||
forgeversion=33.0.60
|
||||
mcp_mappings=20200723-1.16.1
|
||||
mcversion=1.18.1
|
||||
forgeversion=39.1.0
|
||||
mcp_channel=official
|
||||
mcp_mappings=1.18.1
|
||||
|
||||
botania=1.16.2-405
|
||||
# Source: https://maven.blamejared.com/vazkii/botania/Botania/
|
||||
botania=1.18.1-429
|
||||
# Source: https://dvs1.progwml6.com/files/maven/mezz/jei/
|
||||
jei_version=jei-1.18.1:9.1.0.41
|
||||
|
||||
version_major=1
|
||||
version_minor=5
|
||||
version_major=2
|
||||
version_minor=11
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
|
|
55
gradlew
vendored
|
@ -1,5 +1,21 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
|
@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
@ -56,7 +72,7 @@ case "`uname`" in
|
|||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
MSYS* | MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
|
@ -66,6 +82,7 @@ esac
|
|||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
|
@ -109,10 +126,11 @@ if $darwin; then
|
|||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
|
@ -138,19 +156,19 @@ if $cygwin ; then
|
|||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
@ -159,14 +177,9 @@ save () {
|
|||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
43
gradlew.bat
vendored
|
@ -1,3 +1,19 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
|
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
|||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -35,7 +54,7 @@ goto fail
|
|||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
|
@ -45,28 +64,14 @@ echo location of your Java installation.
|
|||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
|
BIN
images/crafting5.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
images/crafting6.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
images/textures/core_angel.png
Normal file
After Width: | Height: | Size: 788 B |
BIN
images/textures/core_destruction.png
Normal file
After Width: | Height: | Size: 783 B |
BIN
images/textures/core_overlay.png
Normal file
After Width: | Height: | Size: 613 B |
BIN
images/textures/wand_core.xcf
Normal file
Before Width: | Height: | Size: 449 B |
Before Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 437 B |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/core_angel"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/core_destruction"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/diamond_wand"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"constructionwand:using_core": 1.0
|
||||
},
|
||||
"model": "constructionwand:item/diamond_wand_core"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/diamond_wand",
|
||||
"layer1": "constructionwand:item/overlay_core"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/infinity_wand"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"constructionwand:using_core": 1.0
|
||||
},
|
||||
"model": "constructionwand:item/infinity_wand_core"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/infinity_wand",
|
||||
"layer1": "constructionwand:item/overlay_core"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/iron_wand"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"constructionwand:using_core": 1.0
|
||||
},
|
||||
"model": "constructionwand:item/iron_wand_core"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/iron_wand",
|
||||
"layer1": "constructionwand:item/overlay_core"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/stone_wand"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"constructionwand:using_core": 1.0
|
||||
},
|
||||
"model": "constructionwand:item/stone_wand_core"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "constructionwand:item/stone_wand",
|
||||
"layer1": "constructionwand:item/overlay_core"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"constructionwand:core_angel"
|
||||
]
|
||||
},
|
||||
"criteria": {
|
||||
"has_item": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"tag": "forge:feathers"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": {
|
||||
"recipe": "constructionwand:core_angel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_item",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"constructionwand:core_destruction"
|
||||
]
|
||||
},
|
||||
"criteria": {
|
||||
"has_item": {
|
||||
"trigger": "minecraft:inventory_changed",
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"tag": "forge:storage_blocks/diamond"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"trigger": "minecraft:recipe_unlocked",
|
||||
"conditions": {
|
||||
"recipe": "constructionwand:core_destruction"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_item",
|
||||
"has_the_recipe"
|
||||
]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" #X",
|
||||
"#O#",
|
||||
"X# "
|
||||
],
|
||||
"key": {
|
||||
"O": {
|
||||
"tag": "forge:feathers"
|
||||
},
|
||||
"X": {
|
||||
"tag": "forge:ingots/gold"
|
||||
},
|
||||
"#": {
|
||||
"tag": "forge:glass_panes"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "constructionwand:core_angel"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" #X",
|
||||
"#O#",
|
||||
"X# "
|
||||
],
|
||||
"key": {
|
||||
"O": {
|
||||
"tag": "forge:storage_blocks/diamond"
|
||||
},
|
||||
"X": {
|
||||
"item": "minecraft:diamond_pickaxe"
|
||||
},
|
||||
"#": {
|
||||
"tag": "forge:glass_panes"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "constructionwand:core_destruction"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "constructionwand:wand_upgrade"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package thetadev.constructionwand;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
@ -8,28 +8,31 @@ import net.minecraftforge.fml.config.ModConfig;
|
|||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.network.simple.SimpleChannel;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import thetadev.constructionwand.basics.ConfigClient;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.ModStats;
|
||||
import thetadev.constructionwand.basics.ReplacementRegistry;
|
||||
import thetadev.constructionwand.client.ClientEvents;
|
||||
import thetadev.constructionwand.client.RenderBlockPreview;
|
||||
import thetadev.constructionwand.containers.ContainerManager;
|
||||
import thetadev.constructionwand.containers.ContainerRegistrar;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
import thetadev.constructionwand.job.UndoHistory;
|
||||
import thetadev.constructionwand.network.PacketQueryUndo;
|
||||
import thetadev.constructionwand.network.PacketUndoBlocks;
|
||||
import thetadev.constructionwand.network.PacketWandOption;
|
||||
import thetadev.constructionwand.wand.undo.UndoHistory;
|
||||
|
||||
|
||||
@Mod(ConstructionWand.MODID)
|
||||
public class ConstructionWand
|
||||
{
|
||||
public static final String MODID = "constructionwand";
|
||||
public static final String MODNAME = "ConstructionWand";
|
||||
|
||||
public static ConstructionWand instance;
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final String PROTOCOL_VERSION = "1";
|
||||
|
@ -50,21 +53,23 @@ public class ConstructionWand
|
|||
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
// Register Item DeferredRegister
|
||||
ModItems.ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||
|
||||
// Config setup
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ConfigServer.SPEC);
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ConfigClient.SPEC);
|
||||
}
|
||||
|
||||
private void commonSetup(final FMLCommonSetupEvent event)
|
||||
{
|
||||
private void commonSetup(final FMLCommonSetupEvent event) {
|
||||
LOGGER.info("ConstructionWand says hello - may the odds be ever in your favor.");
|
||||
|
||||
// Register packets
|
||||
HANDLER = NetworkRegistry.newSimpleChannel(new ResourceLocation(MODID, "main"), ()->PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
|
||||
HANDLER = NetworkRegistry.newSimpleChannel(new ResourceLocation(MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
|
||||
int packetIndex = 0;
|
||||
HANDLER.registerMessage(packetIndex++, PacketUndoBlocks.class, PacketUndoBlocks::encode, PacketUndoBlocks::decode, PacketUndoBlocks.Handler::handle);
|
||||
HANDLER.registerMessage(packetIndex++, PacketQueryUndo.class, PacketQueryUndo::encode, PacketQueryUndo::decode, PacketQueryUndo.Handler::handle);
|
||||
HANDLER.registerMessage(packetIndex++, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle);
|
||||
HANDLER.registerMessage(packetIndex, PacketWandOption.class, PacketWandOption::encode, PacketWandOption::decode, PacketWandOption.Handler::handle);
|
||||
|
||||
// Container registry
|
||||
ContainerRegistrar.register();
|
||||
|
@ -76,11 +81,13 @@ public class ConstructionWand
|
|||
ModStats.register();
|
||||
}
|
||||
|
||||
private void clientSetup(final FMLClientSetupEvent event)
|
||||
{
|
||||
private void clientSetup(final FMLClientSetupEvent event) {
|
||||
renderBlockPreview = new RenderBlockPreview();
|
||||
MinecraftForge.EVENT_BUS.register(renderBlockPreview);
|
||||
ModItems.registerModelProperties();
|
||||
MinecraftForge.EVENT_BUS.register(new ClientEvents());
|
||||
|
||||
event.enqueueWork(ModItems::registerModelProperties);
|
||||
event.enqueueWork(ModItems::registerItemColors);
|
||||
}
|
||||
|
||||
public static ResourceLocation loc(String name) {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package thetadev.constructionwand.api;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Created by james on 28/12/16.
|
||||
*/
|
||||
public interface IContainerHandler
|
||||
{
|
||||
boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack);
|
||||
int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack);
|
||||
int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count);
|
||||
boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack);
|
||||
|
||||
int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack);
|
||||
|
||||
int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count);
|
||||
}
|
||||
|
|
24
src/main/java/thetadev/constructionwand/api/IWandAction.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package thetadev.constructionwand.api;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.wand.undo.ISnapshot;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public interface IWandAction
|
||||
{
|
||||
int getLimit(ItemStack wand);
|
||||
|
||||
@Nonnull
|
||||
List<ISnapshot> getSnapshots(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit);
|
||||
|
||||
@Nonnull
|
||||
List<ISnapshot> getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package thetadev.constructionwand.api;
|
||||
|
||||
public interface IWandCore extends IWandUpgrade
|
||||
{
|
||||
int getColor();
|
||||
|
||||
IWandAction getWandAction();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package thetadev.constructionwand.api;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import thetadev.constructionwand.wand.undo.PlaceSnapshot;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IWandSupplier
|
||||
{
|
||||
void getSupply(@Nullable BlockItem target);
|
||||
|
||||
/**
|
||||
* Tries to create a new PlaceSnapshot at the specified position.
|
||||
* Returns null if there aren't any blocks available that can be placed
|
||||
* in that position.
|
||||
*/
|
||||
@Nullable
|
||||
PlaceSnapshot getPlaceSnapshot(Level world, BlockPos pos, BlockHitResult rayTraceResult,
|
||||
@Nullable BlockState supportingBlock);
|
||||
|
||||
/**
|
||||
* Consumes an item stack if the placement was successful
|
||||
*/
|
||||
int takeItemStack(ItemStack stack);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package thetadev.constructionwand.api;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface IWandUpgrade
|
||||
{
|
||||
ResourceLocation getRegistryName();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package thetadev.constructionwand.basics;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
@ -9,10 +9,10 @@ import thetadev.constructionwand.ConstructionWand;
|
|||
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID)
|
||||
public class CommonEvents
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) {
|
||||
PlayerEntity player = e.getPlayer();
|
||||
if(player.getEntityWorld().isRemote) return;
|
||||
ConstructionWand.instance.undoHistory.removePlayer(player);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void logOut(PlayerEvent.PlayerLoggedOutEvent e) {
|
||||
Player player = e.getPlayer();
|
||||
if(player.level.isClientSide) return;
|
||||
ConstructionWand.instance.undoHistory.removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,29 @@ import net.minecraftforge.common.ForgeConfigSpec;
|
|||
|
||||
public class ConfigClient
|
||||
{
|
||||
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
|
||||
public static final ForgeConfigSpec.BooleanValue SHIFTCTRL_MODE;
|
||||
public static final ForgeConfigSpec.BooleanValue SHIFTCTRL_GUI;
|
||||
public static final ForgeConfigSpec.IntValue OPT_KEY;
|
||||
public static final ForgeConfigSpec.BooleanValue SHIFTOPT_MODE;
|
||||
public static final ForgeConfigSpec.BooleanValue SHIFTOPT_GUI;
|
||||
|
||||
static {
|
||||
BUILDER.push("keys");
|
||||
BUILDER.comment("Press SHIFT+CTRL instead of SHIFT for changing wand mode/direction lock");
|
||||
SHIFTCTRL_MODE = BUILDER.define("ShiftCtrl", false);
|
||||
BUILDER.comment("Press SHIFT+CTRL instead of SHIFT for opening wand GUI");
|
||||
SHIFTCTRL_GUI = BUILDER.define("ShiftCtrlGUI", false);
|
||||
BUILDER.pop();
|
||||
}
|
||||
static {
|
||||
BUILDER.comment("This is the Client config for ConstructionWand.",
|
||||
"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.",
|
||||
"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",
|
||||
"new worlds, copy the config files in the /defaultconfigs folder.");
|
||||
|
||||
public static final ForgeConfigSpec SPEC = BUILDER.build();
|
||||
BUILDER.push("keys");
|
||||
BUILDER.comment("Key code of OPTKEY (Default: Left Control). Look up key codes under https://www.glfw.org/docs/3.3/group__keys.html");
|
||||
OPT_KEY = BUILDER.defineInRange("OptKey", 341, 0, 350);
|
||||
BUILDER.comment("Press SNEAK+OPTKEY instead of SNEAK for changing wand mode/direction lock");
|
||||
SHIFTOPT_MODE = BUILDER.define("ShiftOpt", false);
|
||||
BUILDER.comment("Press SNEAK+OPTKEY instead of SNEAK for opening wand GUI");
|
||||
SHIFTOPT_GUI = BUILDER.define("ShiftOptGUI", true);
|
||||
BUILDER.pop();
|
||||
}
|
||||
|
||||
public static final ForgeConfigSpec SPEC = BUILDER.build();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package thetadev.constructionwand.basics;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemTier;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Tiers;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -12,85 +13,126 @@ import java.util.List;
|
|||
|
||||
public class ConfigServer
|
||||
{
|
||||
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
|
||||
public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE;
|
||||
public static final ForgeConfigSpec.IntValue MAX_RANGE;
|
||||
public static final ForgeConfigSpec.IntValue UNDO_HISTORY;
|
||||
public static final ForgeConfigSpec.BooleanValue ANGEL_FALLING;
|
||||
public static final ForgeConfigSpec.IntValue LIMIT_CREATIVE;
|
||||
public static final ForgeConfigSpec.IntValue MAX_RANGE;
|
||||
public static final ForgeConfigSpec.IntValue UNDO_HISTORY;
|
||||
public static final ForgeConfigSpec.BooleanValue ANGEL_FALLING;
|
||||
|
||||
public static final ForgeConfigSpec.ConfigValue<List<?>> SIMILAR_BLOCKS;
|
||||
private static final String[] SIMILAR_BLOCKS_DEFAULT = {
|
||||
"minecraft:dirt;minecraft:grass_block;minecraft:coarse_dirt;minecraft:podzol;minecraft:mycelium;minecraft:farmland;minecraft:grass_path"
|
||||
};
|
||||
public static final ForgeConfigSpec.ConfigValue<List<?>> SIMILAR_BLOCKS;
|
||||
private static final String[] SIMILAR_BLOCKS_DEFAULT = {
|
||||
"minecraft:dirt;minecraft:grass_block;minecraft:coarse_dirt;minecraft:podzol;minecraft:mycelium;minecraft:farmland;minecraft:dirt_path;minecraft:rooted_dirt"
|
||||
};
|
||||
|
||||
private static final HashMap<Item, WandProperties> wandProperties = new HashMap<>();
|
||||
public static final ForgeConfigSpec.BooleanValue TE_WHITELIST;
|
||||
public static final ForgeConfigSpec.ConfigValue<List<?>> TE_LIST;
|
||||
private static final String[] TE_LIST_DEFAULT = {"chiselsandbits"};
|
||||
|
||||
public static WandProperties getWandProperties(Item wand) {
|
||||
return wandProperties.getOrDefault(wand, WandProperties.DEFAULT);
|
||||
}
|
||||
private static final HashMap<ResourceLocation, WandProperties> wandProperties = new HashMap<>();
|
||||
|
||||
public static class WandProperties
|
||||
{
|
||||
public static final WandProperties DEFAULT = new WandProperties(null, null, null);
|
||||
public static WandProperties getWandProperties(Item wand) {
|
||||
return wandProperties.getOrDefault(wand.getRegistryName(), WandProperties.DEFAULT);
|
||||
}
|
||||
|
||||
private final ForgeConfigSpec.IntValue durability;
|
||||
private final ForgeConfigSpec.IntValue limit;
|
||||
private final ForgeConfigSpec.IntValue angel;
|
||||
public static class WandProperties
|
||||
{
|
||||
public static final WandProperties DEFAULT = new WandProperties(null, null, null, null, null);
|
||||
|
||||
private WandProperties(ForgeConfigSpec.IntValue durability, ForgeConfigSpec.IntValue limit, ForgeConfigSpec.IntValue angel) {
|
||||
this.durability = durability;
|
||||
this.limit = limit;
|
||||
this.angel = angel;
|
||||
}
|
||||
private final ForgeConfigSpec.IntValue durability;
|
||||
private final ForgeConfigSpec.IntValue limit;
|
||||
private final ForgeConfigSpec.IntValue angel;
|
||||
private final ForgeConfigSpec.IntValue destruction;
|
||||
private final ForgeConfigSpec.BooleanValue upgradeable;
|
||||
|
||||
public WandProperties(ForgeConfigSpec.Builder builder, Item wand, int defDurability, int defLimit, int defAngel) {
|
||||
builder.push(wand.getRegistryName().getPath());
|
||||
private WandProperties(ForgeConfigSpec.IntValue durability, ForgeConfigSpec.IntValue limit,
|
||||
ForgeConfigSpec.IntValue angel, ForgeConfigSpec.IntValue destruction,
|
||||
ForgeConfigSpec.BooleanValue upgradeable) {
|
||||
this.durability = durability;
|
||||
this.limit = limit;
|
||||
this.angel = angel;
|
||||
this.destruction = destruction;
|
||||
this.upgradeable = upgradeable;
|
||||
}
|
||||
|
||||
if(defDurability > 0) {
|
||||
builder.comment("Wand durability");
|
||||
durability = builder.defineInRange("durability", defDurability, 1, Integer.MAX_VALUE);
|
||||
}
|
||||
else durability = null;
|
||||
builder.comment("Wand block limit");
|
||||
limit = builder.defineInRange("limit", defLimit, 1, Integer.MAX_VALUE);
|
||||
builder.comment("Max placement distance with angel mode (0 to disable angel mode)");
|
||||
angel = builder.defineInRange("angel", defAngel, 0, Integer.MAX_VALUE);
|
||||
builder.pop();
|
||||
public WandProperties(ForgeConfigSpec.Builder builder, RegistryObject<Item> wandSupplier, int defDurability, int defLimit,
|
||||
int defAngel, int defDestruction, boolean defUpgradeable) {
|
||||
ResourceLocation registryName = wandSupplier.getId();
|
||||
builder.push(registryName.getPath());
|
||||
|
||||
wandProperties.put(wand, this);
|
||||
}
|
||||
if(defDurability > 0) {
|
||||
builder.comment("Wand durability");
|
||||
durability = builder.defineInRange("durability", defDurability, 1, Integer.MAX_VALUE);
|
||||
}
|
||||
else durability = null;
|
||||
builder.comment("Wand block limit");
|
||||
limit = builder.defineInRange("limit", defLimit, 1, Integer.MAX_VALUE);
|
||||
builder.comment("Max placement distance with angel core (0 to disable angel core)");
|
||||
angel = builder.defineInRange("angel", defAngel, 0, Integer.MAX_VALUE);
|
||||
builder.comment("Wand destruction block limit (0 to disable destruction core)");
|
||||
destruction = builder.defineInRange("destruction", defDestruction, 0, Integer.MAX_VALUE);
|
||||
builder.comment("Allow wand upgrading by putting the wand together with a wand core in a crafting grid.");
|
||||
upgradeable = builder.define("upgradeable", defUpgradeable);
|
||||
builder.pop();
|
||||
|
||||
public int getDurability() {
|
||||
return durability == null ? -1 : durability.get();
|
||||
}
|
||||
public int getLimit() {
|
||||
return limit == null ? 0 : limit.get();
|
||||
}
|
||||
public int getAngel() {
|
||||
return angel == null ? 0 : angel.get();
|
||||
}
|
||||
}
|
||||
wandProperties.put(registryName, this);
|
||||
}
|
||||
|
||||
static {
|
||||
new WandProperties(BUILDER, ModItems.WAND_STONE, ItemTier.STONE.getMaxUses(), 9, 0);
|
||||
new WandProperties(BUILDER, ModItems.WAND_IRON, ItemTier.IRON.getMaxUses(), 27, 1);
|
||||
new WandProperties(BUILDER, ModItems.WAND_DIAMOND, ItemTier.DIAMOND.getMaxUses(), 128, 4);
|
||||
new WandProperties(BUILDER, ModItems.WAND_INFINITY, 0, 1024, 8);
|
||||
public int getDurability() {
|
||||
return durability == null ? -1 : durability.get();
|
||||
}
|
||||
|
||||
BUILDER.push("misc");
|
||||
BUILDER.comment("Block limit for Infinity Wand used in creative mode");
|
||||
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.");
|
||||
MAX_RANGE = BUILDER.defineInRange("MaxRange", 256, 0, Integer.MAX_VALUE);
|
||||
BUILDER.comment("Number of operations that can be undone");
|
||||
UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE);
|
||||
BUILDER.comment("Place blocks below you while falling > 10 blocks with angel mode (Can be used to save you from drops/the void)");
|
||||
ANGEL_FALLING = BUILDER.define("AngelFalling", false);
|
||||
BUILDER.comment("Blocks to treat equally when in Similar mode. Enter block IDs seperated by ;");
|
||||
SIMILAR_BLOCKS = BUILDER.defineList("SimilarBlocks", Arrays.asList(SIMILAR_BLOCKS_DEFAULT), obj -> true);
|
||||
BUILDER.pop();
|
||||
}
|
||||
public int getLimit() {
|
||||
return limit == null ? 0 : limit.get();
|
||||
}
|
||||
|
||||
public static final ForgeConfigSpec SPEC = BUILDER.build();
|
||||
public int getAngel() {
|
||||
return angel == null ? 0 : angel.get();
|
||||
}
|
||||
|
||||
public int getDestruction() {
|
||||
return destruction == null ? 0 : destruction.get();
|
||||
}
|
||||
|
||||
public boolean isUpgradeable() {
|
||||
return upgradeable != null && upgradeable.get();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
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:",
|
||||
"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",
|
||||
"in the /saves/myworld/serverconfig folder. If you want to change the serverconfig for all",
|
||||
"new worlds, copy the config files in the /defaultconfigs folder.");
|
||||
|
||||
new WandProperties(BUILDER, ModItems.WAND_STONE, Tiers.STONE.getUses(), 9, 0, 0, false);
|
||||
new WandProperties(BUILDER, ModItems.WAND_IRON, Tiers.IRON.getUses(), 27, 2, 9, true);
|
||||
new WandProperties(BUILDER, ModItems.WAND_DIAMOND, Tiers.DIAMOND.getUses(), 128, 8, 25, true);
|
||||
new WandProperties(BUILDER, ModItems.WAND_INFINITY, 0, 1024, 16, 81, true);
|
||||
|
||||
BUILDER.push("misc");
|
||||
BUILDER.comment("Block limit for Infinity Wand used in creative mode");
|
||||
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.");
|
||||
MAX_RANGE = BUILDER.defineInRange("MaxRange", 100, 0, Integer.MAX_VALUE);
|
||||
BUILDER.comment("Number of operations that can be undone");
|
||||
UNDO_HISTORY = BUILDER.defineInRange("UndoHistory", 3, 0, Integer.MAX_VALUE);
|
||||
BUILDER.comment("Place blocks below you while falling > 10 blocks with angel core (Can be used to save you from drops/the void)");
|
||||
ANGEL_FALLING = BUILDER.define("AngelFalling", false);
|
||||
BUILDER.comment("Blocks to treat equally when in Similar mode. Enter block IDs seperated by ;");
|
||||
SIMILAR_BLOCKS = BUILDER.defineList("SimilarBlocks", Arrays.asList(SIMILAR_BLOCKS_DEFAULT), obj -> true);
|
||||
BUILDER.pop();
|
||||
|
||||
BUILDER.push("tileentity");
|
||||
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");
|
||||
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");
|
||||
TE_WHITELIST = BUILDER.define("TEWhitelist", false);
|
||||
BUILDER.pop();
|
||||
}
|
||||
|
||||
public static final ForgeConfigSpec SPEC = BUILDER.build();
|
||||
}
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
package thetadev.constructionwand.basics;
|
||||
|
||||
import net.minecraft.stats.IStatFormatter;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.stats.StatFormatter;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
|
||||
public class ModStats
|
||||
{
|
||||
public static final ResourceLocation USE_WAND = new ResourceLocation(ConstructionWand.MODID, "use_wand");
|
||||
public static final ResourceLocation USE_WAND = new ResourceLocation(ConstructionWand.MODID, "use_wand");
|
||||
|
||||
public static void register() {
|
||||
registerStat(USE_WAND);
|
||||
}
|
||||
public static void register() {
|
||||
registerStat(USE_WAND);
|
||||
}
|
||||
|
||||
private static void registerStat(ResourceLocation registryName) {
|
||||
// Compare with net.minecraft.stats.Stats#registerCustom
|
||||
Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName);
|
||||
Stats.CUSTOM.get(registryName, IStatFormatter.DEFAULT);
|
||||
}
|
||||
private static void registerStat(ResourceLocation registryName) {
|
||||
// Compare with net.minecraft.stats.Stats#registerCustom
|
||||
Registry.register(Registry.CUSTOM_STAT, registryName.getPath(), registryName);
|
||||
Stats.CUSTOM.get(registryName, StatFormatter.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package thetadev.constructionwand.basics;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
|
||||
|
@ -11,41 +13,42 @@ import java.util.Set;
|
|||
|
||||
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() {
|
||||
for(Object key : ConfigServer.SIMILAR_BLOCKS.get()) {
|
||||
if(!(key instanceof String)) continue;
|
||||
HashSet<Item> set = new HashSet<>();
|
||||
public static void init() {
|
||||
for(Object key : ConfigServer.SIMILAR_BLOCKS.get()) {
|
||||
if(!(key instanceof String)) continue;
|
||||
HashSet<Item> set = new HashSet<>();
|
||||
|
||||
for(String id : ((String)key).split(";")) {
|
||||
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id));
|
||||
if(item == null) {
|
||||
ConstructionWand.LOGGER.warn("Replacement Registry: Could not find item "+id);
|
||||
continue;
|
||||
}
|
||||
set.add(item);
|
||||
}
|
||||
if(!set.isEmpty()) replacements.add(set);
|
||||
}
|
||||
}
|
||||
for(String id : ((String) key).split(";")) {
|
||||
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id));
|
||||
if(item == null || item == Items.AIR) {
|
||||
ConstructionWand.LOGGER.warn("Replacement Registry: Could not find item " + id);
|
||||
continue;
|
||||
}
|
||||
set.add(item);
|
||||
}
|
||||
if(!set.isEmpty()) replacements.add(set);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<Item> getMatchingSet(Item item) {
|
||||
HashSet<Item> res = new HashSet<>();
|
||||
public static Set<Item> getMatchingSet(Item item) {
|
||||
HashSet<Item> res = new HashSet<>();
|
||||
|
||||
for(HashSet<Item> set : replacements) {
|
||||
if(set.contains(item)) res.addAll(set);
|
||||
}
|
||||
res.remove(item);
|
||||
return res;
|
||||
}
|
||||
for(HashSet<Item> set : replacements) {
|
||||
if(set.contains(item)) res.addAll(set);
|
||||
}
|
||||
res.remove(item);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean matchBlocks(Block b1, Block b2) {
|
||||
if(b1 == b2) return true;
|
||||
public static boolean matchBlocks(Block b1, Block b2) {
|
||||
if(b1 == b2) return true;
|
||||
if(b1 == Blocks.AIR || b2 == Blocks.AIR) return false;
|
||||
|
||||
for(HashSet<Item> set : replacements) {
|
||||
if(set.contains(b1.asItem()) && set.contains(b2.asItem())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for(HashSet<Item> set : replacements) {
|
||||
if(set.contains(b1.asItem()) && set.contains(b2.asItem())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,75 +1,233 @@
|
|||
package thetadev.constructionwand.basics;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.BlockSnapshot;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
import thetadev.constructionwand.containers.ContainerManager;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
import thetadev.constructionwand.wand.WandItemUseContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class WandUtil
|
||||
{
|
||||
public static ResourceLocation TAG_TROWELS = new ResourceLocation(ConstructionWand.MODID, "trowels");
|
||||
public static boolean stackEquals(ItemStack stackA, ItemStack stackB) {
|
||||
return ItemStack.isSameItemSameTags(stackA, stackB);
|
||||
}
|
||||
|
||||
public static boolean stackEquals(ItemStack stackA, ItemStack stackB) {
|
||||
return ItemStack.areItemsEqual(stackA, stackB) && ItemStack.areItemStackTagsEqual(stackA, stackB);
|
||||
}
|
||||
public static boolean stackEquals(ItemStack stackA, Item item) {
|
||||
ItemStack stackB = new ItemStack(item);
|
||||
return stackEquals(stackA, stackB);
|
||||
}
|
||||
|
||||
public static boolean stackEquals(ItemStack stackA, Item item) {
|
||||
ItemStack stackB = new ItemStack(item);
|
||||
return stackEquals(stackA, stackB);
|
||||
}
|
||||
public static ItemStack holdingWand(Player player) {
|
||||
if(player.getItemInHand(InteractionHand.MAIN_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.MAIN_HAND).getItem() instanceof ItemWand) {
|
||||
return player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
}
|
||||
else if(player.getItemInHand(InteractionHand.OFF_HAND) != ItemStack.EMPTY && player.getItemInHand(InteractionHand.OFF_HAND).getItem() instanceof ItemWand) {
|
||||
return player.getItemInHand(InteractionHand.OFF_HAND);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ItemStack holdingWand(PlayerEntity player) {
|
||||
if(player.getHeldItem(Hand.MAIN_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.MAIN_HAND).getItem() instanceof ItemWand) {
|
||||
return player.getHeldItem(Hand.MAIN_HAND);
|
||||
}
|
||||
else if(player.getHeldItem(Hand.OFF_HAND) != ItemStack.EMPTY && player.getHeldItem(Hand.OFF_HAND).getItem() instanceof ItemWand) {
|
||||
return player.getHeldItem(Hand.OFF_HAND);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static BlockPos playerPos(Player player) {
|
||||
return new BlockPos(player.position());
|
||||
}
|
||||
|
||||
public static BlockPos playerPos(PlayerEntity player) {
|
||||
return new BlockPos(player.getPositionVec());
|
||||
}
|
||||
|
||||
public static Vector3d entityPositionVec(Entity entity) {
|
||||
return new Vector3d(entity.getPosX(), entity.getPosY() - entity.getYOffset() + entity.getHeight()/2, entity.getPosZ());
|
||||
}
|
||||
public static Vec3 entityPositionVec(Entity entity) {
|
||||
return new Vec3(entity.getX(), entity.getY() - entity.getMyRidingOffset() + entity.getBbHeight() / 2, entity.getZ());
|
||||
}
|
||||
|
||||
public static Vector3d blockPosVec(BlockPos pos) {
|
||||
return new Vector3d(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
public static Vec3 blockPosVec(BlockPos pos) {
|
||||
return new Vec3(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
public static List<ItemStack> getHotbar(PlayerEntity player) {
|
||||
return player.inventory.mainInventory.subList(0, 9);
|
||||
}
|
||||
public static List<ItemStack> getHotbar(Player player) {
|
||||
return player.getInventory().items.subList(0, 9);
|
||||
}
|
||||
|
||||
public static List<ItemStack> getHotbarWithOffhand(PlayerEntity player) {
|
||||
ArrayList<ItemStack> inventory = new ArrayList<>(player.inventory.offHandInventory);
|
||||
inventory.addAll(player.inventory.mainInventory.subList(0, 9));
|
||||
return inventory;
|
||||
}
|
||||
public static List<ItemStack> getHotbarWithOffhand(Player player) {
|
||||
ArrayList<ItemStack> inventory = new ArrayList<>(player.getInventory().items.subList(0, 9));
|
||||
inventory.addAll(player.getInventory().offhand);
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public static List<ItemStack> getMainInv(PlayerEntity player) {
|
||||
return player.inventory.mainInventory.subList(9, player.inventory.mainInventory.size());
|
||||
}
|
||||
public static List<ItemStack> getMainInv(Player player) {
|
||||
return player.getInventory().items.subList(9, player.getInventory().items.size());
|
||||
}
|
||||
|
||||
public static List<ItemStack> getFullInv(PlayerEntity player) {
|
||||
ArrayList<ItemStack> inventory = new ArrayList<>(player.inventory.offHandInventory);
|
||||
inventory.addAll(player.inventory.mainInventory);
|
||||
return inventory;
|
||||
}
|
||||
public static List<ItemStack> getFullInv(Player player) {
|
||||
ArrayList<ItemStack> inventory = new ArrayList<>(player.getInventory().offhand);
|
||||
inventory.addAll(player.getInventory().items);
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public static int maxRange(BlockPos p1, BlockPos p2) {
|
||||
return Math.max(Math.abs(p1.getX() - p2.getX()), Math.abs(p1.getZ() - p2.getZ()));
|
||||
}
|
||||
public static int blockDistance(BlockPos p1, BlockPos p2) {
|
||||
return Math.max(Math.abs(p1.getX() - p2.getX()), Math.abs(p1.getZ() - p2.getZ()));
|
||||
}
|
||||
|
||||
public static boolean isTEAllowed(BlockState state) {
|
||||
if(!state.hasBlockEntity()) return true;
|
||||
|
||||
ResourceLocation name = state.getBlock().getRegistryName();
|
||||
if(name == null) return false;
|
||||
|
||||
String fullId = name.toString();
|
||||
String modId = name.getNamespace();
|
||||
|
||||
boolean inList = ConfigServer.TE_LIST.get().contains(fullId) || ConfigServer.TE_LIST.get().contains(modId);
|
||||
boolean isWhitelist = ConfigServer.TE_WHITELIST.get();
|
||||
|
||||
return isWhitelist == inList;
|
||||
}
|
||||
|
||||
public static boolean placeBlock(Level world, Player player, BlockState block, BlockPos pos, @Nullable BlockItem item) {
|
||||
if(!world.setBlockAndUpdate(pos, block)) {
|
||||
ConstructionWand.LOGGER.info("Block could not be placed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove block if placeEvent is canceled
|
||||
BlockSnapshot snapshot = BlockSnapshot.create(world.dimension(), world, pos);
|
||||
BlockEvent.EntityPlaceEvent placeEvent = new BlockEvent.EntityPlaceEvent(snapshot, block, player);
|
||||
MinecraftForge.EVENT_BUS.post(placeEvent);
|
||||
if(placeEvent.isCanceled()) {
|
||||
world.removeBlock(pos, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack stack;
|
||||
if(item == null) stack = new ItemStack(block.getBlock().asItem());
|
||||
else {
|
||||
stack = new ItemStack(item);
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
}
|
||||
|
||||
// Call OnBlockPlaced method
|
||||
block.getBlock().setPlacedBy(world, pos, block, player, stack);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean removeBlock(Level world, Player player, @Nullable BlockState block, BlockPos pos) {
|
||||
BlockState currentBlock = world.getBlockState(pos);
|
||||
|
||||
if(!world.mayInteract(player, pos)) return false;
|
||||
|
||||
if(!player.isCreative()) {
|
||||
if(currentBlock.getDestroySpeed(world, pos) <= -1 || world.getBlockEntity(pos) != null) return false;
|
||||
|
||||
if(block != null)
|
||||
if(!ReplacementRegistry.matchBlocks(currentBlock.getBlock(), block.getBlock())) return false;
|
||||
}
|
||||
|
||||
BlockEvent.BreakEvent breakEvent = new BlockEvent.BreakEvent(world, pos, currentBlock, player);
|
||||
MinecraftForge.EVENT_BUS.post(breakEvent);
|
||||
if(breakEvent.isCanceled()) return false;
|
||||
|
||||
world.removeBlock(pos, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int countItem(Player player, Item item) {
|
||||
if(player.getInventory().items == null) return 0;
|
||||
if(player.isCreative()) return Integer.MAX_VALUE;
|
||||
|
||||
int total = 0;
|
||||
ContainerManager containerManager = ConstructionWand.instance.containerManager;
|
||||
List<ItemStack> inventory = WandUtil.getFullInv(player);
|
||||
|
||||
for(ItemStack stack : inventory) {
|
||||
if(stack == null || stack.isEmpty()) continue;
|
||||
|
||||
if(WandUtil.stackEquals(stack, item)) {
|
||||
total += stack.getCount();
|
||||
}
|
||||
else {
|
||||
int amount = containerManager.countItems(player, new ItemStack(item), stack);
|
||||
if(amount == Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
||||
total += amount;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private static boolean isPositionModifiable(Level world, Player player, BlockPos pos) {
|
||||
// Is position out of world?
|
||||
if(!world.isInWorldBounds(pos)) return false;
|
||||
|
||||
// Is block modifiable?
|
||||
if(!world.mayInteract(player, pos)) return false;
|
||||
|
||||
// Limit range
|
||||
if(ConfigServer.MAX_RANGE.get() > 0 &&
|
||||
WandUtil.blockDistance(player.blockPosition(), pos) > ConfigServer.MAX_RANGE.get()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a wand can place a block at a certain position.
|
||||
* This check is independent of the used block.
|
||||
*/
|
||||
public static boolean isPositionPlaceable(Level world, Player player, BlockPos pos, boolean replace) {
|
||||
if(!isPositionModifiable(world, player, pos)) return false;
|
||||
|
||||
// If replace mode is off, target has to be air
|
||||
if(world.isEmptyBlock(pos)) return true;
|
||||
|
||||
// Otherwise, check if the block can be replaced by a generic block
|
||||
return replace && world.getBlockState(pos).canBeReplaced(
|
||||
new WandItemUseContext(world, player,
|
||||
new BlockHitResult(new Vec3(0, 0, 0), Direction.DOWN, pos, false),
|
||||
pos, (BlockItem) Items.STONE));
|
||||
}
|
||||
|
||||
public static boolean isBlockRemovable(Level world, Player player, BlockPos pos) {
|
||||
if(!isPositionModifiable(world, player, pos)) return false;
|
||||
|
||||
if(!player.isCreative()) {
|
||||
return !(world.getBlockState(pos).getDestroySpeed(world, pos) <= -1) && world.getBlockEntity(pos) == null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isBlockPermeable(Level world, BlockPos pos) {
|
||||
return world.isEmptyBlock(pos) || world.getBlockState(pos).getCollisionShape(world, pos).isEmpty();
|
||||
}
|
||||
|
||||
public static boolean entitiesCollidingWithBlock(Level world, BlockState blockState, BlockPos pos) {
|
||||
VoxelShape shape = blockState.getCollisionShape(world, pos);
|
||||
if(!shape.isEmpty()) {
|
||||
AABB blockBB = shape.bounds().move(pos);
|
||||
return !world.getEntitiesOfClass(LivingEntity.class, blockBB, Predicate.not(Entity::isSpectator)).isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Direction fromVector(Vec3 vector) {
|
||||
return Direction.getNearest(vector.x, vector.y, vector.z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,25 +4,33 @@ import thetadev.constructionwand.ConstructionWand;
|
|||
|
||||
public interface IOption<T>
|
||||
{
|
||||
String getKey();
|
||||
String getValueString();
|
||||
void setValueString(String val);
|
||||
String getKey();
|
||||
|
||||
default String getKeyTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey();
|
||||
}
|
||||
default String getValueTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString();
|
||||
}
|
||||
default String getDescTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString() + ".desc";
|
||||
}
|
||||
String getValueString();
|
||||
|
||||
boolean isEnabled();
|
||||
void set(T val);
|
||||
T get();
|
||||
T next(boolean dir);
|
||||
default T next() {
|
||||
return next(true);
|
||||
}
|
||||
void setValueString(String val);
|
||||
|
||||
default String getKeyTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey();
|
||||
}
|
||||
|
||||
default String getValueTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString();
|
||||
}
|
||||
|
||||
default String getDescTranslation() {
|
||||
return ConstructionWand.MODID + ".option." + getKey() + "." + getValueString() + ".desc";
|
||||
}
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
void set(T val);
|
||||
|
||||
T get();
|
||||
|
||||
T next(boolean dir);
|
||||
|
||||
default T next() {
|
||||
return next(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,62 @@
|
|||
package thetadev.constructionwand.basics.option;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public class OptionBoolean implements IOption<Boolean>
|
||||
{
|
||||
private final CompoundNBT tag;
|
||||
private final String key;
|
||||
private final boolean enabled;
|
||||
private boolean value;
|
||||
private final CompoundTag tag;
|
||||
private final String key;
|
||||
private final boolean enabled;
|
||||
private boolean value;
|
||||
|
||||
public OptionBoolean(CompoundNBT tag, String key, boolean dval, boolean enabled) {
|
||||
this.tag = tag;
|
||||
this.key = key;
|
||||
this.enabled = enabled;
|
||||
public OptionBoolean(CompoundTag tag, String key, boolean dval, boolean enabled) {
|
||||
this.tag = tag;
|
||||
this.key = key;
|
||||
this.enabled = enabled;
|
||||
|
||||
if(tag.contains(key)) value = tag.getBoolean(key);
|
||||
else value = dval;
|
||||
}
|
||||
if(tag.contains(key)) value = tag.getBoolean(key);
|
||||
else value = dval;
|
||||
}
|
||||
|
||||
public OptionBoolean(CompoundNBT tag, String key, boolean dval) {
|
||||
this(tag, key, dval, true);
|
||||
}
|
||||
public OptionBoolean(CompoundTag tag, String key, boolean dval) {
|
||||
this(tag, key, dval, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString() {
|
||||
return value ? "yes" : "no";
|
||||
}
|
||||
@Override
|
||||
public String getValueString() {
|
||||
return value ? "yes" : "no";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueString(String val) {
|
||||
set(val.equals("yes"));
|
||||
}
|
||||
@Override
|
||||
public void setValueString(String val) {
|
||||
set(val.equals("yes"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Boolean val) {
|
||||
if(!enabled) return;
|
||||
value = val;
|
||||
tag.putBoolean(key, value);
|
||||
}
|
||||
@Override
|
||||
public void set(Boolean val) {
|
||||
if(!enabled) return;
|
||||
value = val;
|
||||
tag.putBoolean(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get() {
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public Boolean get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean next(boolean dir) {
|
||||
set(!value);
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public Boolean next(boolean dir) {
|
||||
set(!value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
package thetadev.constructionwand.basics.option;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public class OptionEnum<E extends Enum<E>> implements IOption<E>
|
||||
{
|
||||
private final CompoundNBT tag;
|
||||
private final String key;
|
||||
private final Class<E> enumClass;
|
||||
private final boolean enabled;
|
||||
private final E dval;
|
||||
private E value;
|
||||
private final CompoundTag tag;
|
||||
private final String key;
|
||||
private final Class<E> enumClass;
|
||||
private final boolean enabled;
|
||||
private final E dval;
|
||||
private E value;
|
||||
|
||||
public OptionEnum(CompoundNBT tag, String key, Class<E> enumClass, E dval, boolean enabled) {
|
||||
this.tag = tag;
|
||||
this.key = key;
|
||||
this.enumClass = enumClass;
|
||||
this.enabled = enabled;
|
||||
this.dval = dval;
|
||||
public OptionEnum(CompoundTag tag, String key, Class<E> enumClass, E dval, boolean enabled) {
|
||||
this.tag = tag;
|
||||
this.key = key;
|
||||
this.enumClass = enumClass;
|
||||
this.enabled = enabled;
|
||||
this.dval = dval;
|
||||
|
||||
value = Enums.getIfPresent(enumClass, tag.getString(key).toUpperCase()).or(dval);
|
||||
}
|
||||
value = Enums.getIfPresent(enumClass, tag.getString(key).toUpperCase()).or(dval);
|
||||
}
|
||||
|
||||
public OptionEnum(CompoundNBT tag, String key, Class<E> enumClass, E dval) {
|
||||
this(tag, key, enumClass, dval, true);
|
||||
}
|
||||
public OptionEnum(CompoundTag tag, String key, Class<E> enumClass, E dval) {
|
||||
this(tag, key, enumClass, dval, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString() {
|
||||
return value.name().toLowerCase();
|
||||
}
|
||||
@Override
|
||||
public String getValueString() {
|
||||
return value.name().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueString(String val) {
|
||||
set(Enums.getIfPresent(enumClass, val.toUpperCase()).or(dval));
|
||||
}
|
||||
@Override
|
||||
public void setValueString(String val) {
|
||||
set(Enums.getIfPresent(enumClass, val.toUpperCase()).or(dval));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(E val) {
|
||||
if(!enabled) return;
|
||||
value = val;
|
||||
tag.putString(key, getValueString());
|
||||
}
|
||||
@Override
|
||||
public void set(E val) {
|
||||
if(!enabled) return;
|
||||
value = val;
|
||||
tag.putString(key, getValueString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get() {
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public E get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next(boolean dir) {
|
||||
E[] enumValues = enumClass.getEnumConstants();
|
||||
int i = value.ordinal() + (dir ? 1:-1);
|
||||
if(i < 0) i += enumValues.length;
|
||||
set(enumValues[i % enumValues.length]);
|
||||
return value;
|
||||
}
|
||||
@Override
|
||||
public E next(boolean dir) {
|
||||
E[] enumValues = enumClass.getEnumConstants();
|
||||
int i = value.ordinal() + (dir ? 1 : -1);
|
||||
if(i < 0) i += enumValues.length;
|
||||
set(enumValues[i % enumValues.length]);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +1,100 @@
|
|||
package thetadev.constructionwand.basics.option;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import thetadev.constructionwand.api.IWandCore;
|
||||
import thetadev.constructionwand.api.IWandUpgrade;
|
||||
import thetadev.constructionwand.basics.ReplacementRegistry;
|
||||
import thetadev.constructionwand.items.core.CoreDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class WandOptions
|
||||
{
|
||||
public final CompoundNBT tag;
|
||||
public final CompoundTag tag;
|
||||
|
||||
private static final String TAG_ROOT = "wand_options";
|
||||
private static final String TAG_ROOT = "wand_options";
|
||||
|
||||
public enum MODE
|
||||
{
|
||||
DEFAULT,
|
||||
ANGEL;
|
||||
}
|
||||
public enum LOCK
|
||||
{
|
||||
HORIZONTAL,
|
||||
VERTICAL,
|
||||
NORTHSOUTH,
|
||||
EASTWEST,
|
||||
NOLOCK;
|
||||
}
|
||||
public enum DIRECTION
|
||||
{
|
||||
TARGET,
|
||||
PLAYER;
|
||||
}
|
||||
public enum MATCH
|
||||
{
|
||||
EXACT,
|
||||
SIMILAR,
|
||||
ANY;
|
||||
}
|
||||
public enum LOCK
|
||||
{
|
||||
HORIZONTAL,
|
||||
VERTICAL,
|
||||
NORTHSOUTH,
|
||||
EASTWEST,
|
||||
NOLOCK
|
||||
}
|
||||
|
||||
public final OptionEnum<MODE> mode;
|
||||
public final OptionEnum<LOCK> lock;
|
||||
public final OptionEnum<DIRECTION> direction;
|
||||
public final OptionBoolean replace;
|
||||
public final OptionEnum<MATCH> match;
|
||||
public final OptionBoolean random;
|
||||
public enum DIRECTION
|
||||
{
|
||||
TARGET,
|
||||
PLAYER
|
||||
}
|
||||
|
||||
public final IOption<?>[] allOptions;
|
||||
public enum MATCH
|
||||
{
|
||||
EXACT,
|
||||
SIMILAR,
|
||||
ANY
|
||||
}
|
||||
|
||||
public WandOptions(ItemStack wandStack) {
|
||||
ItemWand wand = (ItemWand) wandStack.getItem();
|
||||
tag = wandStack.getOrCreateChildTag(TAG_ROOT);
|
||||
public final WandUpgradesSelectable<IWandCore> cores;
|
||||
|
||||
mode = new OptionEnum<>(tag, "mode", MODE.class, MODE.DEFAULT, ConfigServer.getWandProperties(wand).getAngel() > 0);
|
||||
lock = new OptionEnum<>(tag, "lock", LOCK.class, LOCK.NOLOCK);
|
||||
direction = new OptionEnum<>(tag, "direction", DIRECTION.class, DIRECTION.TARGET);
|
||||
replace = new OptionBoolean(tag, "replace", true);
|
||||
match = new OptionEnum<>(tag, "match", MATCH.class, MATCH.SIMILAR);
|
||||
random = new OptionBoolean(tag, "random", false);
|
||||
public final OptionEnum<LOCK> lock;
|
||||
public final OptionEnum<DIRECTION> direction;
|
||||
public final OptionBoolean replace;
|
||||
public final OptionEnum<MATCH> match;
|
||||
public final OptionBoolean random;
|
||||
|
||||
allOptions = new IOption[]{mode, lock, direction, replace, match, random};
|
||||
}
|
||||
public final IOption<?>[] allOptions;
|
||||
|
||||
@Nullable
|
||||
public IOption<?> get(String key){
|
||||
for(IOption<?> option : allOptions) {
|
||||
if(option.getKey().equals(key)) return option;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public WandOptions(ItemStack wandStack) {
|
||||
tag = wandStack.getOrCreateTagElement(TAG_ROOT);
|
||||
|
||||
public boolean testLock(LOCK l) {
|
||||
if(lock.get() == LOCK.NOLOCK) return true;
|
||||
return lock.get() == l;
|
||||
}
|
||||
cores = new WandUpgradesSelectable<>(tag, "cores", new CoreDefault());
|
||||
|
||||
lock = new OptionEnum<>(tag, "lock", LOCK.class, LOCK.NOLOCK);
|
||||
direction = new OptionEnum<>(tag, "direction", DIRECTION.class, DIRECTION.TARGET);
|
||||
replace = new OptionBoolean(tag, "replace", true);
|
||||
match = new OptionEnum<>(tag, "match", MATCH.class, MATCH.SIMILAR);
|
||||
random = new OptionBoolean(tag, "random", false);
|
||||
|
||||
allOptions = new IOption[]{cores, lock, direction, replace, match, random};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IOption<?> get(String key) {
|
||||
for(IOption<?> option : allOptions) {
|
||||
if(option.getKey().equals(key)) return option;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean testLock(LOCK l) {
|
||||
if(lock.get() == LOCK.NOLOCK) return true;
|
||||
return lock.get() == l;
|
||||
}
|
||||
|
||||
public boolean matchBlocks(Block b1, Block b2) {
|
||||
switch(match.get()) {
|
||||
case EXACT:
|
||||
return b1 == b2;
|
||||
case SIMILAR:
|
||||
return ReplacementRegistry.matchBlocks(b1, b2);
|
||||
case ANY:
|
||||
return b1 != Blocks.AIR && b2 != Blocks.AIR;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasUpgrade(IWandUpgrade upgrade) {
|
||||
if(upgrade instanceof IWandCore) return cores.hasUpgrade((IWandCore) upgrade);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addUpgrade(IWandUpgrade upgrade) {
|
||||
if(upgrade instanceof IWandCore) return cores.addUpgrade((IWandCore) upgrade);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package thetadev.constructionwand.basics.option;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.api.IWandUpgrade;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class WandUpgrades<T extends IWandUpgrade>
|
||||
{
|
||||
protected final CompoundTag tag;
|
||||
protected final String key;
|
||||
protected final ArrayList<T> upgrades;
|
||||
protected final T dval;
|
||||
|
||||
public WandUpgrades(CompoundTag tag, String key, T dval) {
|
||||
this.tag = tag;
|
||||
this.key = key;
|
||||
this.dval = dval;
|
||||
|
||||
upgrades = new ArrayList<>();
|
||||
if(dval != null) upgrades.add(0, dval);
|
||||
|
||||
deserialize();
|
||||
}
|
||||
|
||||
protected void deserialize() {
|
||||
ListTag listnbt = tag.getList(key, Tag.TAG_STRING);
|
||||
boolean require_fix = false;
|
||||
|
||||
for(int i = 0; i < listnbt.size(); i++) {
|
||||
String str = listnbt.getString(i);
|
||||
Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(str));
|
||||
|
||||
T data;
|
||||
try {
|
||||
//noinspection unchecked
|
||||
data = (T) item;
|
||||
upgrades.add(data);
|
||||
} catch(ClassCastException e) {
|
||||
ConstructionWand.LOGGER.warn("Invalid wand upgrade: " + str);
|
||||
require_fix = true;
|
||||
}
|
||||
}
|
||||
if(require_fix) serialize();
|
||||
}
|
||||
|
||||
protected void serialize() {
|
||||
ListTag listnbt = new ListTag();
|
||||
|
||||
for(T item : upgrades) {
|
||||
if(item == dval) continue;
|
||||
listnbt.add(StringTag.valueOf(item.getRegistryName().toString()));
|
||||
}
|
||||
tag.put(key, listnbt);
|
||||
}
|
||||
|
||||
public boolean addUpgrade(T upgrade) {
|
||||
if(hasUpgrade(upgrade)) return false;
|
||||
|
||||
upgrades.add(upgrade);
|
||||
serialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasUpgrade(T upgrade) {
|
||||
return upgrades.contains(upgrade);
|
||||
}
|
||||
|
||||
public ArrayList<T> getUpgrades() {
|
||||
return upgrades;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package thetadev.constructionwand.basics.option;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import thetadev.constructionwand.api.IWandUpgrade;
|
||||
|
||||
public class WandUpgradesSelectable<T extends IWandUpgrade> extends WandUpgrades<T> implements IOption<T>
|
||||
{
|
||||
private byte selector;
|
||||
|
||||
public WandUpgradesSelectable(CompoundTag tag, String key, T dval) {
|
||||
super(tag, key, dval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueString() {
|
||||
return get().getRegistryName().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueString(String val) {
|
||||
for(byte i = 0; i < upgrades.size(); i++) {
|
||||
if(upgrades.get(i).getRegistryName().toString().equals(val)) {
|
||||
selector = i;
|
||||
serializeSelector();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return upgrades.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T val) {
|
||||
selector = (byte) upgrades.indexOf(val);
|
||||
fixSelector();
|
||||
serializeSelector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
fixSelector();
|
||||
return upgrades.get(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next(boolean dir) {
|
||||
selector++;
|
||||
fixSelector();
|
||||
serializeSelector();
|
||||
return get();
|
||||
}
|
||||
|
||||
private void fixSelector() {
|
||||
if(selector < 0 || selector >= upgrades.size()) selector = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deserialize() {
|
||||
super.deserialize();
|
||||
|
||||
selector = tag.getByte(key + "_sel");
|
||||
fixSelector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void serialize() {
|
||||
super.serialize();
|
||||
|
||||
serializeSelector();
|
||||
}
|
||||
|
||||
private void serializeSelector() {
|
||||
tag.putByte(key + "_sel", selector);
|
||||
}
|
||||
}
|
|
@ -4,8 +4,12 @@ import javax.annotation.Nullable;
|
|||
|
||||
public interface IPool<T>
|
||||
{
|
||||
void add(T element);
|
||||
@Nullable
|
||||
T draw();
|
||||
void reset();
|
||||
void add(T element);
|
||||
|
||||
void remove(T element);
|
||||
|
||||
@Nullable
|
||||
T draw();
|
||||
|
||||
void reset();
|
||||
}
|
||||
|
|
|
@ -5,30 +5,35 @@ import java.util.ArrayList;
|
|||
|
||||
public class OrderedPool<T> implements IPool<T>
|
||||
{
|
||||
private final ArrayList<T> elements;
|
||||
private int index;
|
||||
private final ArrayList<T> elements;
|
||||
private int index;
|
||||
|
||||
public OrderedPool() {
|
||||
elements = new ArrayList<>();
|
||||
reset();
|
||||
}
|
||||
public OrderedPool() {
|
||||
elements = new ArrayList<>();
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T element) {
|
||||
elements.add(element);
|
||||
}
|
||||
@Override
|
||||
public void add(T element) {
|
||||
elements.add(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T draw() {
|
||||
if(index >= elements.size()) return null;
|
||||
T e = elements.get(index);
|
||||
index++;
|
||||
return e;
|
||||
}
|
||||
@Override
|
||||
public void remove(T element) {
|
||||
elements.remove(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
index = 0;
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public T draw() {
|
||||
if(index >= elements.size()) return null;
|
||||
T e = elements.get(index);
|
||||
index++;
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,48 +7,54 @@ import java.util.Random;
|
|||
|
||||
public class RandomPool<T> implements IPool<T>
|
||||
{
|
||||
private final Random rng;
|
||||
private final HashMap<T, Integer> elements;
|
||||
private HashSet<T> pool;
|
||||
private final Random rng;
|
||||
private final HashMap<T, Integer> elements;
|
||||
private HashSet<T> pool;
|
||||
|
||||
public RandomPool(Random rng) {
|
||||
this.rng = rng;
|
||||
elements = new HashMap<>();
|
||||
reset();
|
||||
}
|
||||
public RandomPool(Random rng) {
|
||||
this.rng = rng;
|
||||
elements = new HashMap<>();
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T element) {
|
||||
addWithWeight(element, 1);
|
||||
}
|
||||
@Override
|
||||
public void add(T element) {
|
||||
addWithWeight(element, 1);
|
||||
}
|
||||
|
||||
public void addWithWeight(T element, int weight) {
|
||||
if(weight < 1) return;
|
||||
elements.merge(element, weight, Integer::sum);
|
||||
pool.add(element);
|
||||
}
|
||||
@Override
|
||||
public void remove(T element) {
|
||||
elements.remove(element);
|
||||
pool.remove(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T draw() {
|
||||
int allWeights = pool.stream().reduce(0, (partialRes, e) -> partialRes + elements.get(e), Integer::sum);
|
||||
if(allWeights < 1) return null;
|
||||
public void addWithWeight(T element, int weight) {
|
||||
if(weight < 1) return;
|
||||
elements.merge(element, weight, Integer::sum);
|
||||
pool.add(element);
|
||||
}
|
||||
|
||||
int random = rng.nextInt(allWeights);
|
||||
int accWeight = 0;
|
||||
@Nullable
|
||||
@Override
|
||||
public T draw() {
|
||||
int allWeights = pool.stream().reduce(0, (partialRes, e) -> partialRes + elements.get(e), Integer::sum);
|
||||
if(allWeights < 1) return null;
|
||||
|
||||
for(T e : pool) {
|
||||
accWeight += elements.get(e);
|
||||
if(random < accWeight) {
|
||||
pool.remove(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
int random = rng.nextInt(allWeights);
|
||||
int accWeight = 0;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
pool = new HashSet<>(elements.keySet());
|
||||
}
|
||||
for(T e : pool) {
|
||||
accWeight += elements.get(e);
|
||||
if(random < accWeight) {
|
||||
pool.remove(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
pool = new HashSet<>(elements.keySet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,84 +1,105 @@
|
|||
package thetadev.constructionwand.client;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.*;
|
||||
import thetadev.constructionwand.basics.ConfigClient;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
import thetadev.constructionwand.network.PacketQueryUndo;
|
||||
import thetadev.constructionwand.network.PacketWandOption;
|
||||
|
||||
@Mod.EventBusSubscriber(value = Dist.CLIENT)
|
||||
public class ClientEvents
|
||||
{
|
||||
private static boolean ctrlPressed = false;
|
||||
private boolean optPressed;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void KeyEvent(InputEvent.KeyInputEvent event) {
|
||||
PlayerEntity player = Minecraft.getInstance().player;
|
||||
if(player == null) return;
|
||||
if(WandUtil.holdingWand(player) == null) return;
|
||||
public ClientEvents() {
|
||||
optPressed = false;
|
||||
}
|
||||
|
||||
boolean ctrlState = Screen.hasControlDown();
|
||||
if(ctrlPressed != ctrlState) {
|
||||
ctrlPressed = ctrlState;
|
||||
PacketQueryUndo packet = new PacketQueryUndo(ctrlPressed);
|
||||
ConstructionWand.instance.HANDLER.sendToServer(packet);
|
||||
//ConstructionWand.LOGGER.debug("CTRL key update: "+ctrlPressed);
|
||||
}
|
||||
}
|
||||
// Send state of OPT key to server
|
||||
@SubscribeEvent
|
||||
public void KeyEvent(InputEvent.KeyInputEvent event) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if(player == null) return;
|
||||
if(WandUtil.holdingWand(player) == null) return;
|
||||
|
||||
// SHIFT+(CTRL)+Scroll to change direction lock
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void MouseScrollEvent(InputEvent.MouseScrollEvent event) {
|
||||
PlayerEntity player = Minecraft.getInstance().player;
|
||||
double scroll = event.getScrollDelta();
|
||||
boolean optState = isOptKeyDown();
|
||||
if(optPressed != optState) {
|
||||
optPressed = optState;
|
||||
PacketQueryUndo packet = new PacketQueryUndo(optPressed);
|
||||
ConstructionWand.instance.HANDLER.sendToServer(packet);
|
||||
//ConstructionWand.LOGGER.debug("OPT key update: " + optPressed);
|
||||
}
|
||||
}
|
||||
|
||||
if(player == null || !player.isSneaking() || (!Screen.hasControlDown() && ConfigClient.SHIFTCTRL_MODE.get()) || scroll == 0) return;
|
||||
// Sneak+(OPT)+Scroll to change direction lock
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void MouseScrollEvent(InputEvent.MouseScrollEvent event) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
double scroll = event.getScrollDelta();
|
||||
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
if(player == null || !modeKeyCombDown(player) || scroll == 0) return;
|
||||
|
||||
WandOptions wandOptions = new WandOptions(wand);
|
||||
wandOptions.lock.next(scroll<0);
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.lock, true));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
|
||||
// SHIFT+(CTRL)+Left click wand to change mode
|
||||
@SubscribeEvent
|
||||
public static void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) {
|
||||
PlayerEntity player = event.getPlayer();
|
||||
WandOptions wandOptions = new WandOptions(wand);
|
||||
wandOptions.lock.next(scroll < 0);
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.lock, true));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
if(player == null || !player.isSneaking() || (!Screen.hasControlDown() && ConfigClient.SHIFTCTRL_MODE.get())) return;
|
||||
// Sneak+(OPT)+Left click wand to change core
|
||||
@SubscribeEvent
|
||||
public void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
ItemStack wand = event.getItemStack();
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
if(player == null || !modeKeyCombDown(player)) return;
|
||||
|
||||
WandOptions wandOptions = new WandOptions(wand);
|
||||
wandOptions.mode.next();
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.mode, true));
|
||||
}
|
||||
ItemStack wand = event.getItemStack();
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
|
||||
// SHIFT+Right click wand to open GUI
|
||||
@SubscribeEvent
|
||||
public static void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
|
||||
PlayerEntity player = event.getPlayer();
|
||||
if(player == null || !player.isSneaking() || (!Screen.hasControlDown() && ConfigClient.SHIFTCTRL_GUI.get())) return;
|
||||
WandOptions wandOptions = new WandOptions(wand);
|
||||
wandOptions.cores.next();
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(wandOptions.cores, true));
|
||||
}
|
||||
|
||||
ItemStack wand = event.getItemStack();
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
// Sneak+(OPT)+Right click wand to open GUI
|
||||
@SubscribeEvent
|
||||
public void onRightClickItem(PlayerInteractEvent.RightClickItem event) {
|
||||
if(event.getSide().isServer()) return;
|
||||
|
||||
Minecraft.getInstance().displayGuiScreen(new ScreenWand(wand));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
Player player = event.getPlayer();
|
||||
if(player == null || !guiKeyCombDown(player)) return;
|
||||
|
||||
ItemStack wand = event.getItemStack();
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
|
||||
Minecraft.getInstance().setScreen(new ScreenWand(wand));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
private static boolean isKeyDown(int id) {
|
||||
return InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), id);
|
||||
}
|
||||
|
||||
public static boolean isOptKeyDown() {
|
||||
return isKeyDown(ConfigClient.OPT_KEY.get());
|
||||
}
|
||||
|
||||
public static boolean modeKeyCombDown(Player player) {
|
||||
return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_MODE.get());
|
||||
}
|
||||
|
||||
public static boolean guiKeyCombDown(Player player) {
|
||||
return player.isCrouching() && (isOptKeyDown() || !ConfigClient.SHIFTOPT_GUI.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +1,78 @@
|
|||
package thetadev.constructionwand.client;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraftforge.client.event.DrawHighlightEvent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraftforge.client.event.DrawSelectionEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.job.WandJob;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
import thetadev.constructionwand.wand.WandJob;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class RenderBlockPreview
|
||||
{
|
||||
public WandJob wandJob;
|
||||
public Set<BlockPos> undoBlocks;
|
||||
private WandJob wandJob;
|
||||
public Set<BlockPos> undoBlocks;
|
||||
|
||||
@SubscribeEvent
|
||||
public void renderBlockHighlight(DrawHighlightEvent event)
|
||||
{
|
||||
if(event.getTarget().getType() != RayTraceResult.Type.BLOCK) return;
|
||||
@SubscribeEvent
|
||||
public void renderBlockHighlight(DrawSelectionEvent.HighlightBlock event) {
|
||||
if(event.getTarget().getType() != HitResult.Type.BLOCK) return;
|
||||
|
||||
BlockRayTraceResult rtr = (BlockRayTraceResult) event.getTarget();
|
||||
Entity entity = event.getInfo().getRenderViewEntity();
|
||||
if(!(entity instanceof PlayerEntity)) return;
|
||||
PlayerEntity player = (PlayerEntity) entity;
|
||||
Set<BlockPos> blocks;
|
||||
float colorR=0, colorG=0, colorB=0;
|
||||
BlockHitResult rtr = event.getTarget();
|
||||
Entity entity = event.getCamera().getEntity();
|
||||
if(!(entity instanceof Player player)) return;
|
||||
Set<BlockPos> blocks;
|
||||
float colorR = 0, colorG = 0, colorB = 0;
|
||||
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
|
||||
if(!(player.isSneaking() && Screen.hasControlDown())) {
|
||||
if(wandJob == null || !(wandJob.getRayTraceResult().equals(rtr)) || !(wandJob.getWand().equals(wand))) {
|
||||
wandJob = WandJob.getJob(player, player.getEntityWorld(), rtr, wand);
|
||||
}
|
||||
if(!(player.isCrouching() && ClientEvents.isOptKeyDown())) {
|
||||
// Use cached wandJob for previews of the same target pos/dir
|
||||
// Exception: always update if blockCount < 2 to prevent 1-block previews when block updates
|
||||
// from the last placement are lagging
|
||||
if(wandJob == null || !compareRTR(wandJob.rayTraceResult, rtr) || !(wandJob.wand.equals(wand))
|
||||
|| wandJob.blockCount() < 2) {
|
||||
wandJob = ItemWand.getWandJob(player, player.level, rtr, wand);
|
||||
}
|
||||
blocks = wandJob.getBlockPositions();
|
||||
}
|
||||
else {
|
||||
blocks = undoBlocks;
|
||||
colorG = 1;
|
||||
}
|
||||
|
||||
blocks = wandJob.getBlockPositions();
|
||||
}
|
||||
else {
|
||||
blocks = undoBlocks;
|
||||
colorG = 1;
|
||||
}
|
||||
if(blocks == null || blocks.isEmpty()) return;
|
||||
|
||||
if(blocks == null || blocks.isEmpty()) return;
|
||||
PoseStack ms = event.getPoseStack();
|
||||
MultiBufferSource buffer = event.getMultiBufferSource();
|
||||
VertexConsumer lineBuilder = buffer.getBuffer(RenderType.LINES);
|
||||
|
||||
renderBlockList(blocks, event.getMatrix(), event.getBuffers(), colorR, colorG, colorB);
|
||||
double partialTicks = event.getPartialTicks();
|
||||
double d0 = player.xOld + (player.getX() - player.xOld) * partialTicks;
|
||||
double d1 = player.yOld + player.getEyeHeight() + (player.getY() - player.yOld) * partialTicks;
|
||||
double d2 = player.zOld + (player.getZ() - player.zOld) * partialTicks;
|
||||
|
||||
event.setCanceled(true);
|
||||
}
|
||||
for(BlockPos block : blocks) {
|
||||
AABB aabb = new AABB(block).move(-d0, -d1, -d2);
|
||||
LevelRenderer.renderLineBox(ms, lineBuilder, aabb, colorR, colorG, colorB, 0.4F);
|
||||
}
|
||||
|
||||
private void renderBlockList(Set<BlockPos> blocks, MatrixStack ms, IRenderTypeBuffer buffer, float red, float green, float blue) {
|
||||
double renderPosX = Minecraft.getInstance().getRenderManager().info.getProjectedView().getX();
|
||||
double renderPosY = Minecraft.getInstance().getRenderManager().info.getProjectedView().getY();
|
||||
double renderPosZ = Minecraft.getInstance().getRenderManager().info.getProjectedView().getZ();
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
ms.push();
|
||||
ms.translate(-renderPosX, -renderPosY, -renderPosZ);
|
||||
|
||||
for(BlockPos block : blocks) {
|
||||
AxisAlignedBB aabb = new AxisAlignedBB(block);
|
||||
IVertexBuilder lineBuilder = buffer.getBuffer(RenderTypes.TRANSLUCENT_LINES);
|
||||
WorldRenderer.drawBoundingBox(ms, lineBuilder, aabb, red, green, blue, 0.4F);
|
||||
}
|
||||
ms.pop();
|
||||
}
|
||||
private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) {
|
||||
return rtr1.getBlockPos().equals(rtr2.getBlockPos()) && rtr1.getDirection().equals(rtr2.getDirection());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package thetadev.constructionwand.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.renderer.RenderState;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
|
||||
import java.util.OptionalDouble;
|
||||
|
||||
public class RenderTypes
|
||||
{
|
||||
public static final RenderType TRANSLUCENT_LINES;
|
||||
|
||||
protected static final RenderState.TransparencyState TRANSLUCENT_TRANSPARENCY = new RenderState.TransparencyState("translucent_transparency", () -> {
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
}, RenderSystem::disableBlend);
|
||||
protected static final RenderState.DepthTestState DEPTH_ALWAYS = new RenderState.DepthTestState("always", GL11.GL_ALWAYS);
|
||||
|
||||
static {
|
||||
RenderType.State translucentNoDepthState = RenderType.State.getBuilder().transparency(TRANSLUCENT_TRANSPARENCY)
|
||||
.line(new RenderState.LineState(OptionalDouble.of(2)))
|
||||
.texture(new RenderState.TextureState())
|
||||
.depthTest(DEPTH_ALWAYS)
|
||||
.build(false);
|
||||
|
||||
TRANSLUCENT_LINES = RenderType.makeType(
|
||||
ConstructionWand.MODID+":translucent_lines",
|
||||
DefaultVertexFormats.POSITION_COLOR,
|
||||
GL11.GL_LINES,
|
||||
256,
|
||||
translucentNoDepthState
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,91 +1,95 @@
|
|||
package thetadev.constructionwand.client;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.button.Button;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.option.IOption;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.network.PacketWandOption;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ScreenWand extends Screen
|
||||
{
|
||||
private final ItemStack wand;
|
||||
private final WandOptions wandOptions;
|
||||
private final ItemStack wand;
|
||||
private final WandOptions wandOptions;
|
||||
|
||||
private static final int BUTTON_WIDTH = 160;
|
||||
private static final int BUTTON_HEIGHT = 20;
|
||||
private static final int SPACING_WIDTH = 50;
|
||||
private static final int SPACING_HEIGHT = 30;
|
||||
private static final int N_COLS = 2;
|
||||
private static final int N_ROWS = 3;
|
||||
private static final int BUTTON_WIDTH = 160;
|
||||
private static final int BUTTON_HEIGHT = 20;
|
||||
private static final int SPACING_WIDTH = 50;
|
||||
private static final int SPACING_HEIGHT = 30;
|
||||
private static final int N_COLS = 2;
|
||||
private static final int N_ROWS = 3;
|
||||
|
||||
private static final int FIELD_WIDTH = N_COLS * (BUTTON_WIDTH+SPACING_WIDTH) - SPACING_WIDTH;
|
||||
private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT+SPACING_HEIGHT) - SPACING_HEIGHT;
|
||||
private static final int FIELD_WIDTH = N_COLS * (BUTTON_WIDTH + SPACING_WIDTH) - SPACING_WIDTH;
|
||||
private static final int FIELD_HEIGHT = N_ROWS * (BUTTON_HEIGHT + SPACING_HEIGHT) - SPACING_HEIGHT;
|
||||
|
||||
public ScreenWand(ItemStack wand) {
|
||||
super(new StringTextComponent("ScreenWand"));
|
||||
this.wand = wand;
|
||||
wandOptions = new WandOptions(wand);
|
||||
}
|
||||
public ScreenWand(ItemStack wand) {
|
||||
super(new TextComponent("ScreenWand"));
|
||||
this.wand = wand;
|
||||
wandOptions = new WandOptions(wand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Minecraft minecraft, int width, int height) {
|
||||
super.init(minecraft, width, height);
|
||||
@Override
|
||||
protected void init() {
|
||||
createButton(0, 0, wandOptions.cores);
|
||||
createButton(0, 1, wandOptions.lock);
|
||||
createButton(0, 2, wandOptions.direction);
|
||||
createButton(1, 0, wandOptions.replace);
|
||||
createButton(1, 1, wandOptions.match);
|
||||
createButton(1, 2, wandOptions.random);
|
||||
}
|
||||
|
||||
createButton(0, 0, wandOptions.mode);
|
||||
createButton(0, 1, wandOptions.lock);
|
||||
createButton(0, 2, wandOptions.direction);
|
||||
createButton(1, 0, wandOptions.replace);
|
||||
createButton(1, 1, wandOptions.match);
|
||||
createButton(1, 2, wandOptions.random);
|
||||
}
|
||||
@Override
|
||||
public void render(@Nonnull PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(matrixStack);
|
||||
drawCenteredString(matrixStack, font, wand.getDisplayName(), width / 2, height / 2 - FIELD_HEIGHT / 2 - SPACING_HEIGHT, 16777215);
|
||||
super.render(matrixStack, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrixStack, int mouseX, int mouseY, float partialTicks) {
|
||||
renderBackground(matrixStack);
|
||||
super.render(matrixStack, mouseX, mouseY, partialTicks);
|
||||
drawCenteredString(matrixStack, font, wand.getDisplayName(), width/2, height/2 - FIELD_HEIGHT/2 - SPACING_HEIGHT, 16777215);
|
||||
}
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (Minecraft.getInstance().options.keyInventory.matches(keyCode, scanCode)) {
|
||||
this.onClose();
|
||||
return true;
|
||||
} else {
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean charTyped(char character, int code) {
|
||||
if(character == 'e') closeScreen();
|
||||
return super.charTyped(character, code);
|
||||
}
|
||||
private void createButton(int cx, int cy, IOption<?> option) {
|
||||
Button button = new Button(getX(cx), getY(cy), BUTTON_WIDTH, BUTTON_HEIGHT, getButtonLabel(option), bt -> clickButton(bt, option), (bt, ms, x, y) -> drawTooltip(ms, x, y, option));
|
||||
button.active = option.isEnabled();
|
||||
addRenderableWidget(button);
|
||||
}
|
||||
|
||||
private void createButton(int cx, int cy, IOption<?> option) {
|
||||
Button button = new Button(getX(cx), getY(cy), BUTTON_WIDTH, BUTTON_HEIGHT, getButtonLabel(option), bt -> clickButton(bt, option), (bt, ms, x, y) -> drawTooltip(ms, x, y, option));
|
||||
button.active = option.isEnabled();
|
||||
addButton(button);
|
||||
}
|
||||
private void clickButton(Button button, IOption<?> option) {
|
||||
option.next();
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(option, false));
|
||||
button.setMessage(getButtonLabel(option));
|
||||
}
|
||||
|
||||
private void clickButton(Button button, IOption<?> option) {
|
||||
option.next();
|
||||
ConstructionWand.instance.HANDLER.sendToServer(new PacketWandOption(option, false));
|
||||
button.setMessage(getButtonLabel(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 void drawTooltip(MatrixStack matrixStack, int mouseX, int mouseY, IOption<?> option) {
|
||||
if(isMouseOver(mouseX, mouseY)) {
|
||||
renderTooltip(matrixStack, new TranslationTextComponent(option.getDescTranslation()), mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
private int getX(int n) {
|
||||
return width / 2 - FIELD_WIDTH / 2 + n * (BUTTON_WIDTH + SPACING_WIDTH);
|
||||
}
|
||||
|
||||
private int getX(int n) {
|
||||
return width/2 - FIELD_WIDTH/2 + n*(BUTTON_WIDTH+SPACING_WIDTH);
|
||||
}
|
||||
private int getY(int n) {
|
||||
return height / 2 - FIELD_HEIGHT / 2 + n * (BUTTON_HEIGHT + SPACING_HEIGHT);
|
||||
}
|
||||
|
||||
private int getY(int n) {
|
||||
return height/2 - FIELD_HEIGHT/2 + n*(BUTTON_HEIGHT+SPACING_HEIGHT);
|
||||
}
|
||||
|
||||
private ITextComponent getButtonLabel(IOption<?> option) {
|
||||
return new TranslationTextComponent(option.getKeyTranslation()).append(new TranslationTextComponent(option.getValueTranslation()));
|
||||
}
|
||||
private Component getButtonLabel(IOption<?> option) {
|
||||
return new TranslatableComponent(option.getKeyTranslation()).append(new TranslatableComponent(option.getValueTranslation()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,38 @@
|
|||
package thetadev.constructionwand.containers;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import thetadev.constructionwand.api.IContainerHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ContainerManager {
|
||||
private ArrayList<IContainerHandler> handlers;
|
||||
public class ContainerManager
|
||||
{
|
||||
private final ArrayList<IContainerHandler> handlers;
|
||||
|
||||
public ContainerManager() {
|
||||
handlers = new ArrayList<IContainerHandler>();
|
||||
}
|
||||
public ContainerManager() {
|
||||
handlers = new ArrayList<IContainerHandler>();
|
||||
}
|
||||
|
||||
public boolean register(IContainerHandler handler) {
|
||||
return handlers.add(handler);
|
||||
}
|
||||
public boolean register(IContainerHandler handler) {
|
||||
return handlers.add(handler);
|
||||
}
|
||||
|
||||
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
for(IContainerHandler handler : handlers) {
|
||||
if(handler.matches(player, itemStack, inventoryStack)) {
|
||||
return handler.countItems(player,itemStack, inventoryStack);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
for(IContainerHandler handler : handlers) {
|
||||
if(handler.matches(player, itemStack, inventoryStack)) {
|
||||
return handler.countItems(player, itemStack, inventoryStack);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
for(IContainerHandler handler : handlers) {
|
||||
if(handler.matches(player, itemStack, inventoryStack)) {
|
||||
return handler.useItems(player, itemStack, inventoryStack, count);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
for(IContainerHandler handler : handlers) {
|
||||
if(handler.matches(player, itemStack, inventoryStack)) {
|
||||
return handler.useItems(player, itemStack, inventoryStack, count);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
|
@ -2,17 +2,21 @@ package thetadev.constructionwand.containers;
|
|||
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.containers.handlers.*;
|
||||
import thetadev.constructionwand.containers.handlers.HandlerBotania;
|
||||
import thetadev.constructionwand.containers.handlers.HandlerBundle;
|
||||
import thetadev.constructionwand.containers.handlers.HandlerCapability;
|
||||
import thetadev.constructionwand.containers.handlers.HandlerShulkerbox;
|
||||
|
||||
public class ContainerRegistrar
|
||||
{
|
||||
public static void register() {
|
||||
ConstructionWand.instance.containerManager.register(new HandlerCapability());
|
||||
ConstructionWand.instance.containerManager.register(new HandlerShulkerbox());
|
||||
public static void register() {
|
||||
ConstructionWand.instance.containerManager.register(new HandlerCapability());
|
||||
ConstructionWand.instance.containerManager.register(new HandlerShulkerbox());
|
||||
ConstructionWand.instance.containerManager.register(new HandlerBundle());
|
||||
|
||||
if(ModList.get().isLoaded("botania")) {
|
||||
ConstructionWand.instance.containerManager.register(new HandlerBotania());
|
||||
ConstructionWand.LOGGER.info("Botania integration added");
|
||||
}
|
||||
}
|
||||
if(ModList.get().isLoaded("botania")) {
|
||||
ConstructionWand.instance.containerManager.register(new HandlerBotania());
|
||||
ConstructionWand.LOGGER.info("Botania integration added");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,41 @@
|
|||
package thetadev.constructionwand.containers.handlers;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import thetadev.constructionwand.api.IContainerHandler;
|
||||
import vazkii.botania.api.BotaniaForgeCapabilities;
|
||||
import vazkii.botania.api.item.IBlockProvider;
|
||||
|
||||
/**
|
||||
* Created by james on 28/12/16.
|
||||
*/
|
||||
import java.util.Optional;
|
||||
|
||||
public class HandlerBotania implements IContainerHandler
|
||||
{
|
||||
@Override
|
||||
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() instanceof IBlockProvider;
|
||||
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return inventoryStack != null && inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem();
|
||||
int provCount = prov.getBlockCount(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem()));
|
||||
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
Optional<IBlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
|
||||
if(provOptional.isEmpty()) return 0;
|
||||
|
||||
IBlockProvider prov = provOptional.get();
|
||||
int provCount = prov.getBlockCount(player, inventoryStack, Block.byItem(itemStack.getItem()));
|
||||
if(provCount == -1)
|
||||
return Integer.MAX_VALUE;
|
||||
return provCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem();
|
||||
if(prov.provideBlock(player, itemStack, inventoryStack, Block.getBlockFromItem(itemStack.getItem()), true))
|
||||
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
Optional<IBlockProvider> provOptional = inventoryStack.getCapability(BotaniaForgeCapabilities.BLOCK_PROVIDER).resolve();
|
||||
if(provOptional.isEmpty()) return 0;
|
||||
|
||||
IBlockProvider prov = provOptional.get();
|
||||
if(prov.provideBlock(player, inventoryStack, Block.byItem(itemStack.getItem()), true))
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package thetadev.constructionwand.containers.handlers;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import thetadev.constructionwand.api.IContainerHandler;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class HandlerBundle implements IContainerHandler
|
||||
{
|
||||
@Override
|
||||
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return inventoryStack != null && inventoryStack.getCount() == 1 && inventoryStack.getItem() == Items.BUNDLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return getContents(inventoryStack).filter((stack) -> WandUtil.stackEquals(stack, itemStack))
|
||||
.map(ItemStack::getCount).reduce(0, Integer::sum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
AtomicInteger newCount = new AtomicInteger(count);
|
||||
|
||||
List<ItemStack> itemStacks = getContents(inventoryStack).filter((stack -> {
|
||||
if(WandUtil.stackEquals(stack, itemStack)) {
|
||||
int toTake = Math.min(newCount.get(), stack.getCount());
|
||||
stack.shrink(toTake);
|
||||
newCount.set(newCount.get() - toTake);
|
||||
}
|
||||
return !stack.isEmpty();
|
||||
})).toList();
|
||||
|
||||
setItemList(inventoryStack, itemStacks);
|
||||
|
||||
return newCount.get();
|
||||
}
|
||||
|
||||
private Stream<ItemStack> getContents(ItemStack bundleStack) {
|
||||
CompoundTag compoundtag = bundleStack.getTag();
|
||||
if(compoundtag == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
else {
|
||||
ListTag listtag = compoundtag.getList("Items", 10);
|
||||
return listtag.stream().map(CompoundTag.class::cast).map(ItemStack::of);
|
||||
}
|
||||
}
|
||||
|
||||
private void setItemList(ItemStack itemStack, List<ItemStack> itemStacks) {
|
||||
CompoundTag rootTag = itemStack.getOrCreateTag();
|
||||
ListTag listTag = new ListTag();
|
||||
rootTag.put("Items", listTag);
|
||||
|
||||
for(ItemStack stack : itemStacks) {
|
||||
CompoundTag itemTag = new CompoundTag();
|
||||
stack.save(itemTag);
|
||||
listTag.add(itemTag);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +1,52 @@
|
|||
package thetadev.constructionwand.containers.handlers;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import thetadev.constructionwand.api.IContainerHandler;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Created by james on 28/12/16.
|
||||
*/
|
||||
public class HandlerCapability implements IContainerHandler
|
||||
{
|
||||
@Override
|
||||
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return inventoryStack != null && inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
LazyOptional<IItemHandler> itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
|
||||
if(!itemHandlerLazyOptional.isPresent()) return 0;
|
||||
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve();
|
||||
if(itemHandlerOptional.isEmpty()) return 0;
|
||||
|
||||
int total = 0;
|
||||
|
||||
IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance());
|
||||
IItemHandler itemHandler = itemHandlerOptional.get();
|
||||
|
||||
for (int i = 0; i < itemHandler.getSlots(); i++) {
|
||||
for(int i = 0; i < itemHandler.getSlots(); i++) {
|
||||
ItemStack containerStack = itemHandler.getStackInSlot(i);
|
||||
if (containerStack != null && itemStack.isItemEqual(containerStack)) {
|
||||
if(WandUtil.stackEquals(itemStack, containerStack)) {
|
||||
total += Math.max(0, containerStack.getCount());
|
||||
}
|
||||
|
||||
// Already in a container. Don't inception this thing.
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
int toUse = itemStack.getCount();
|
||||
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
Optional<IItemHandler> itemHandlerOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve();
|
||||
if(itemHandlerOptional.isEmpty()) return 0;
|
||||
|
||||
LazyOptional<IItemHandler> itemHandlerLazyOptional = inventoryStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
|
||||
if(!itemHandlerLazyOptional.isPresent()) return 0;
|
||||
IItemHandler itemHandler = itemHandlerOptional.get();
|
||||
|
||||
IItemHandler itemHandler = itemHandlerLazyOptional.orElse(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.getDefaultInstance());
|
||||
|
||||
for (int i = 0; i < itemHandler.getSlots(); i++) {
|
||||
for(int i = 0; i < itemHandler.getSlots(); i++) {
|
||||
ItemStack handlerStack = itemHandler.getStackInSlot(i);
|
||||
if(handlerStack != null && handlerStack.isItemEqual(itemStack)) {
|
||||
if(WandUtil.stackEquals(itemStack, handlerStack)) {
|
||||
ItemStack extracted = itemHandler.extractItem(i, count, false);
|
||||
if(extracted != null) {
|
||||
count -= extracted.getCount();
|
||||
}
|
||||
if(count <= 0) {
|
||||
break;
|
||||
}
|
||||
count -= extracted.getCount();
|
||||
if(count <= 0) break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
|
@ -1,78 +1,75 @@
|
|||
package thetadev.constructionwand.containers.handlers;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ShulkerBoxBlock;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.ItemStackHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
import thetadev.constructionwand.api.IContainerHandler;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
|
||||
public class HandlerShulkerbox implements IContainerHandler
|
||||
{
|
||||
private final int SLOTS = 27;
|
||||
private final int SLOTS = 27;
|
||||
|
||||
@Override
|
||||
public boolean matches(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack)
|
||||
{
|
||||
return inventoryStack != null && inventoryStack.getCount() == 1 && Block.getBlockFromItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock;
|
||||
}
|
||||
@Override
|
||||
public boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
return inventoryStack != null && inventoryStack.getCount() == 1 && Block.byItem(inventoryStack.getItem()) instanceof ShulkerBoxBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack)
|
||||
{
|
||||
int count = 0;
|
||||
@Override
|
||||
public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) {
|
||||
int count = 0;
|
||||
|
||||
for(ItemStack stack : getItemList(inventoryStack)) {
|
||||
if(WandUtil.stackEquals(stack, itemStack)) count += stack.getCount();
|
||||
}
|
||||
for(ItemStack stack : getItemList(inventoryStack)) {
|
||||
if(WandUtil.stackEquals(stack, itemStack)) count += stack.getCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int useItems(PlayerEntity player, ItemStack itemStack, ItemStack inventoryStack, int count)
|
||||
{
|
||||
NonNullList<ItemStack> itemList = getItemList(inventoryStack);
|
||||
boolean changed = false;
|
||||
@Override
|
||||
public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) {
|
||||
NonNullList<ItemStack> itemList = getItemList(inventoryStack);
|
||||
boolean changed = false;
|
||||
|
||||
for(ItemStack stack : itemList) {
|
||||
if(WandUtil.stackEquals(stack, itemStack)) {
|
||||
int toTake = Math.min(count, stack.getCount());
|
||||
stack.shrink(toTake);
|
||||
count -= toTake;
|
||||
changed = true;
|
||||
if(count == 0) break;
|
||||
}
|
||||
}
|
||||
if(changed) {
|
||||
setItemList(inventoryStack, itemList);
|
||||
player.inventory.markDirty();
|
||||
}
|
||||
for(ItemStack stack : itemList) {
|
||||
if(WandUtil.stackEquals(stack, itemStack)) {
|
||||
int toTake = Math.min(count, stack.getCount());
|
||||
stack.shrink(toTake);
|
||||
count -= toTake;
|
||||
changed = true;
|
||||
if(count == 0) break;
|
||||
}
|
||||
}
|
||||
if(changed) {
|
||||
setItemList(inventoryStack, itemList);
|
||||
player.getInventory().setChanged();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private NonNullList<ItemStack> getItemList(ItemStack itemStack) {
|
||||
NonNullList<ItemStack> itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY);
|
||||
CompoundNBT rootTag = itemStack.getTag();
|
||||
if (rootTag != null && rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) {
|
||||
CompoundNBT entityTag = rootTag.getCompound("BlockEntityTag");
|
||||
if (entityTag.contains("Items", Constants.NBT.TAG_LIST)) {
|
||||
ItemStackHelper.loadAllItems(entityTag, itemStacks);
|
||||
}
|
||||
}
|
||||
return itemStacks;
|
||||
}
|
||||
private NonNullList<ItemStack> getItemList(ItemStack itemStack) {
|
||||
NonNullList<ItemStack> itemStacks = NonNullList.withSize(SLOTS, ItemStack.EMPTY);
|
||||
CompoundTag rootTag = itemStack.getTag();
|
||||
if(rootTag != null && rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) {
|
||||
CompoundTag entityTag = rootTag.getCompound("BlockEntityTag");
|
||||
if(entityTag.contains("Items", Tag.TAG_LIST)) {
|
||||
ContainerHelper.loadAllItems(entityTag, itemStacks);
|
||||
}
|
||||
}
|
||||
return itemStacks;
|
||||
}
|
||||
|
||||
private void setItemList(ItemStack itemStack, NonNullList<ItemStack> itemStacks) {
|
||||
CompoundNBT rootTag = itemStack.getOrCreateTag();
|
||||
if (!rootTag.contains("BlockEntityTag", Constants.NBT.TAG_COMPOUND)) {
|
||||
rootTag.put("BlockEntityTag", new CompoundNBT());
|
||||
}
|
||||
ItemStackHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks);
|
||||
}
|
||||
private void setItemList(ItemStack itemStack, NonNullList<ItemStack> itemStacks) {
|
||||
CompoundTag rootTag = itemStack.getOrCreateTag();
|
||||
if(!rootTag.contains("BlockEntityTag", Tag.TAG_COMPOUND)) {
|
||||
rootTag.put("BlockEntityTag", new CompoundTag());
|
||||
}
|
||||
ContainerHelper.saveAllItems(rootTag.getCompound("BlockEntityTag"), itemStacks);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package thetadev.constructionwand.crafting;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.inventory.CraftingContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.CustomRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import thetadev.constructionwand.api.IWandUpgrade;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class RecipeWandUpgrade extends CustomRecipe
|
||||
{
|
||||
public static final SimpleRecipeSerializer<RecipeWandUpgrade> SERIALIZER = new SimpleRecipeSerializer<>(RecipeWandUpgrade::new);
|
||||
|
||||
public RecipeWandUpgrade(ResourceLocation resourceLocation) {
|
||||
super(resourceLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(@Nonnull CraftingContainer inv, @Nonnull Level worldIn) {
|
||||
ItemStack wand = null;
|
||||
IWandUpgrade upgrade = null;
|
||||
|
||||
for(int i = 0; i < inv.getContainerSize(); i++) {
|
||||
ItemStack stack = inv.getItem(i);
|
||||
if(!stack.isEmpty()) {
|
||||
if(wand == null && stack.getItem() instanceof ItemWand) wand = stack;
|
||||
else if(upgrade == null && stack.getItem() instanceof IWandUpgrade)
|
||||
upgrade = (IWandUpgrade) stack.getItem();
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(wand == null || upgrade == null) return false;
|
||||
return !new WandOptions(wand).hasUpgrade(upgrade) && ConfigServer.getWandProperties(wand.getItem()).isUpgradeable();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ItemStack assemble(@Nonnull CraftingContainer inv) {
|
||||
ItemStack wand = null;
|
||||
IWandUpgrade upgrade = null;
|
||||
|
||||
for(int i = 0; i < inv.getContainerSize(); i++) {
|
||||
ItemStack stack = inv.getItem(i);
|
||||
if(!stack.isEmpty()) {
|
||||
if(stack.getItem() instanceof ItemWand) wand = stack;
|
||||
else if(stack.getItem() instanceof IWandUpgrade) upgrade = (IWandUpgrade) stack.getItem();
|
||||
}
|
||||
}
|
||||
|
||||
if(wand == null || upgrade == null) return ItemStack.EMPTY;
|
||||
|
||||
ItemStack newWand = wand.copy();
|
||||
new WandOptions(newWand).addUpgrade(upgrade);
|
||||
return newWand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCraftInDimensions(int width, int height) {
|
||||
return width * height >= 2;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RecipeSerializer<?> getSerializer() {
|
||||
return SERIALIZER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
public interface ICustomItemModel
|
||||
{
|
||||
void generateCustomItemModel(ItemModelGenerator generator, String name);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
public interface INoItemBlock
|
||||
{
|
||||
}
|
|
@ -1,27 +1,28 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
import net.minecraft.advancements.criterion.ItemPredicate;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.tags.ITag;
|
||||
import net.minecraft.util.IItemProvider;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.tags.Tag;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
|
||||
public class Inp
|
||||
{
|
||||
public final String name;
|
||||
public final Ingredient ingredient;
|
||||
public final ItemPredicate predicate;
|
||||
public final String name;
|
||||
public final Ingredient ingredient;
|
||||
public final ItemPredicate predicate;
|
||||
|
||||
public Inp(String name, Ingredient ingredient, ItemPredicate predicate) {
|
||||
this.name = name;
|
||||
this.ingredient = ingredient;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
public Inp(String name, Ingredient ingredient, ItemPredicate predicate) {
|
||||
this.name = name;
|
||||
this.ingredient = ingredient;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
public static Inp fromItem(IItemProvider in) {
|
||||
return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.fromItems(in), ItemPredicate.Builder.create().item(in).build());
|
||||
}
|
||||
public static Inp fromTag(ITag.INamedTag<Item> in) {
|
||||
return new Inp(in.getName().getPath(), Ingredient.fromTag(in), ItemPredicate.Builder.create().tag(in).build());
|
||||
}
|
||||
public static Inp fromItem(ItemLike in) {
|
||||
return new Inp(in.asItem().getRegistryName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
|
||||
}
|
||||
|
||||
public static Inp fromTag(Tag.Named<Item> in) {
|
||||
return new Inp(in.getName().getPath(), Ingredient.of(in), ItemPredicate.Builder.item().of(in).build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.client.model.generators.ItemModelProvider;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ItemModelGenerator extends ItemModelProvider
|
||||
{
|
||||
public ItemModelGenerator(DataGenerator generator, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, ConstructionWand.MODID, existingFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerModels() {
|
||||
for(RegistryObject<Item> itemObject : ModItems.ITEMS.getEntries()) {
|
||||
Item item = itemObject.get();
|
||||
String name = item.getRegistryName().getPath();
|
||||
|
||||
if(item instanceof ICustomItemModel)
|
||||
((ICustomItemModel) item).generateCustomItemModel(this, name);
|
||||
else if(item instanceof BlockItem)
|
||||
withExistingParent(name, modLoc("block/" + name));
|
||||
else withExistingParent(name, "item/generated").texture("layer0", "item/" + name);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return ConstructionWand.MODNAME + " item models";
|
||||
}
|
||||
}
|
|
@ -1,19 +1,25 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
||||
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
|
||||
|
||||
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public class ModData
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void gatherData(GatherDataEvent event) {
|
||||
DataGenerator generator = event.getGenerator();
|
||||
@SubscribeEvent
|
||||
public static void gatherData(GatherDataEvent event) {
|
||||
DataGenerator generator = event.getGenerator();
|
||||
ExistingFileHelper fileHelper = event.getExistingFileHelper();
|
||||
|
||||
if(event.includeServer()) {
|
||||
generator.addProvider(new RecipeGenerator(generator));
|
||||
}
|
||||
}
|
||||
if(event.includeServer()) {
|
||||
generator.addProvider(new RecipeGenerator(generator));
|
||||
}
|
||||
|
||||
if(event.includeClient()) {
|
||||
generator.addProvider(new ItemModelGenerator(generator, fileHelper));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,74 @@
|
|||
package thetadev.constructionwand.data;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.IFinishedRecipe;
|
||||
import net.minecraft.data.RecipeProvider;
|
||||
import net.minecraft.data.ShapedRecipeBuilder;
|
||||
import net.minecraft.data.recipes.FinishedRecipe;
|
||||
import net.minecraft.data.recipes.RecipeProvider;
|
||||
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
||||
import net.minecraft.data.recipes.SpecialRecipeBuilder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.util.IItemProvider;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraftforge.common.Tags;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.crafting.RecipeWandUpgrade;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RecipeGenerator extends RecipeProvider
|
||||
{
|
||||
public RecipeGenerator(DataGenerator generatorIn) {
|
||||
super(generatorIn);
|
||||
}
|
||||
public RecipeGenerator(DataGenerator generatorIn) {
|
||||
super(generatorIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerRecipes(Consumer<IFinishedRecipe> consumer) {
|
||||
wandRecipe(consumer, ModItems.WAND_STONE, Inp.fromTag(ItemTags.field_232909_aa_)); //stone_tool_materials
|
||||
wandRecipe(consumer, ModItems.WAND_IRON, Inp.fromTag(Tags.Items.INGOTS_IRON));
|
||||
wandRecipe(consumer, ModItems.WAND_DIAMOND, Inp.fromTag(Tags.Items.GEMS_DIAMOND));
|
||||
wandRecipe(consumer, ModItems.WAND_INFINITY, Inp.fromTag(Tags.Items.NETHER_STARS));
|
||||
}
|
||||
@Override
|
||||
protected void buildCraftingRecipes(@Nonnull Consumer<FinishedRecipe> consumer) {
|
||||
wandRecipe(consumer, ModItems.WAND_STONE.get(), Inp.fromTag(ItemTags.STONE_TOOL_MATERIALS));
|
||||
wandRecipe(consumer, ModItems.WAND_IRON.get(), Inp.fromTag(Tags.Items.INGOTS_IRON));
|
||||
wandRecipe(consumer, ModItems.WAND_DIAMOND.get(), Inp.fromTag(Tags.Items.GEMS_DIAMOND));
|
||||
wandRecipe(consumer, ModItems.WAND_INFINITY.get(), Inp.fromTag(Tags.Items.NETHER_STARS));
|
||||
|
||||
private void wandRecipe(Consumer<IFinishedRecipe> consumer, IItemProvider wand, Inp material) {
|
||||
ShapedRecipeBuilder.shapedRecipe(wand)
|
||||
.key('X', material.ingredient)
|
||||
.key('#', Tags.Items.RODS_WOODEN)
|
||||
.patternLine(" X")
|
||||
.patternLine(" # ")
|
||||
.patternLine("# ")
|
||||
.addCriterion("has_item", hasItem(material.predicate))
|
||||
.build(consumer);
|
||||
}
|
||||
coreRecipe(consumer, ModItems.CORE_ANGEL.get(), Inp.fromTag(Tags.Items.FEATHERS), Inp.fromTag(Tags.Items.INGOTS_GOLD));
|
||||
coreRecipe(consumer, ModItems.CORE_DESTRUCTION.get(), Inp.fromTag(Tags.Items.STORAGE_BLOCKS_DIAMOND), Inp.fromItem(Items.DIAMOND_PICKAXE));
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return ConstructionWand.MODID + " crafting recipes";
|
||||
}
|
||||
specialRecipe(consumer, RecipeWandUpgrade.SERIALIZER);
|
||||
}
|
||||
|
||||
private void wandRecipe(Consumer<FinishedRecipe> consumer, ItemLike wand, Inp material) {
|
||||
ShapedRecipeBuilder.shaped(wand)
|
||||
.define('X', material.ingredient)
|
||||
.define('#', Tags.Items.RODS_WOODEN)
|
||||
.pattern(" X")
|
||||
.pattern(" # ")
|
||||
.pattern("# ")
|
||||
.unlockedBy("has_item", inventoryTrigger(material.predicate))
|
||||
.save(consumer);
|
||||
}
|
||||
|
||||
private void coreRecipe(Consumer<FinishedRecipe> consumer, ItemLike core, Inp item1, Inp item2) {
|
||||
ShapedRecipeBuilder.shaped(core)
|
||||
.define('O', item1.ingredient)
|
||||
.define('X', item2.ingredient)
|
||||
.define('#', Tags.Items.GLASS_PANES)
|
||||
.pattern(" #X")
|
||||
.pattern("#O#")
|
||||
.pattern("X# ")
|
||||
.unlockedBy("has_item", inventoryTrigger(item1.predicate))
|
||||
.save(consumer);
|
||||
}
|
||||
|
||||
private void specialRecipe(Consumer<FinishedRecipe> consumer, SimpleRecipeSerializer<?> serializer) {
|
||||
ResourceLocation name = ForgeRegistries.RECIPE_SERIALIZERS.getKey(serializer);
|
||||
SpecialRecipeBuilder.special(serializer).save(consumer, ConstructionWand.loc("dynamic/" + name.getPath()).toString());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return ConstructionWand.MODID + " crafting recipes";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package thetadev.constructionwand.integrations.jei;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import mezz.jei.api.IModPlugin;
|
||||
import mezz.jei.api.JeiPlugin;
|
||||
import mezz.jei.api.constants.VanillaTypes;
|
||||
import mezz.jei.api.registration.IRecipeRegistration;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.ConfigClient;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@JeiPlugin
|
||||
public class ConstructionWandJeiPlugin implements IModPlugin
|
||||
{
|
||||
private static final ResourceLocation pluginId = new ResourceLocation(ConstructionWand.MODID, ConstructionWand.MODID);
|
||||
private static final String baseKey = ConstructionWand.MODID + ".description.";
|
||||
private static final String baseKeyItem = "item." + ConstructionWand.MODID + ".";
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ResourceLocation getPluginUid() {
|
||||
return pluginId;
|
||||
}
|
||||
|
||||
private Component keyComboComponent(boolean shiftOpt, Component optkeyComponent) {
|
||||
String key = shiftOpt ? "sneak_opt" : "sneak";
|
||||
return new TranslatableComponent(baseKey + "key." + key, optkeyComponent).withStyle(ChatFormatting.BLUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRecipes(IRecipeRegistration registration) {
|
||||
Component optkeyComponent = new TranslatableComponent(InputConstants.getKey(ConfigClient.OPT_KEY.get(), -1).getName())
|
||||
.withStyle(ChatFormatting.BLUE);
|
||||
Component wandModeComponent = keyComboComponent(ConfigClient.SHIFTOPT_MODE.get(), optkeyComponent);
|
||||
Component wandGuiComponent = keyComboComponent(ConfigClient.SHIFTOPT_GUI.get(), optkeyComponent);
|
||||
|
||||
for(RegistryObject<Item> wandSupplier : ModItems.WANDS) {
|
||||
Item wand = wandSupplier.get();
|
||||
ConfigServer.WandProperties wandProperties = ConfigServer.getWandProperties(wand);
|
||||
|
||||
String durabilityKey = wand == ModItems.WAND_INFINITY.get() ? "unlimited" : "limited";
|
||||
Component durabilityComponent = new TranslatableComponent(baseKey + "durability." + durabilityKey, wandProperties.getDurability());
|
||||
|
||||
registration.addIngredientInfo(new ItemStack(wand), VanillaTypes.ITEM,
|
||||
new TranslatableComponent(baseKey + "wand",
|
||||
new TranslatableComponent(baseKeyItem + wand.getRegistryName().getPath()),
|
||||
wandProperties.getLimit(), durabilityComponent,
|
||||
optkeyComponent, wandModeComponent, wandGuiComponent)
|
||||
);
|
||||
}
|
||||
|
||||
for(RegistryObject<Item> coreSupplier : ModItems.CORES) {
|
||||
Item core = coreSupplier.get();
|
||||
registration.addIngredientInfo(new ItemStack(core), VanillaTypes.ITEM,
|
||||
new TranslatableComponent(baseKey + core.getRegistryName().getPath()),
|
||||
new TranslatableComponent(baseKey + "core", wandModeComponent)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
package thetadev.constructionwand.items;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUseContext;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.option.IOption;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.job.AngelJob;
|
||||
import thetadev.constructionwand.job.WandJob;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ItemWand extends Item
|
||||
{
|
||||
public ItemWand(String name, Item.Properties properties) {
|
||||
super(properties.group(ItemGroup.TOOLS));
|
||||
setRegistryName(ConstructionWand.loc(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType onItemUse(ItemUseContext context)
|
||||
{
|
||||
PlayerEntity player = context.getPlayer();
|
||||
Hand hand = context.getHand();
|
||||
World world = context.getWorld();
|
||||
|
||||
if(world.isRemote || player == null) return ActionResultType.FAIL;
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(player.isSneaking() && ConstructionWand.instance.undoHistory.isUndoActive(player)) {
|
||||
return ConstructionWand.instance.undoHistory.undo(player, world, context.getPos()) ? ActionResultType.SUCCESS : ActionResultType.FAIL;
|
||||
}
|
||||
else {
|
||||
WandJob job = WandJob.getJob(player, world, new BlockRayTraceResult(context.getHitVec(), context.getFace(), context.getPos(), false), stack);
|
||||
return job.doIt() ? ActionResultType.SUCCESS : ActionResultType.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(World world, PlayerEntity player, Hand hand) {
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!player.isSneaking()) {
|
||||
if(world.isRemote) return ActionResult.resultFail(stack);
|
||||
|
||||
// Right click: Place angel block
|
||||
//ConstructionWand.LOGGER.debug("Place angel block");
|
||||
WandJob job = new AngelJob(player, world, stack);
|
||||
return job.doIt() ? ActionResult.resultSuccess(stack) : ActionResult.resultFail(stack);
|
||||
}
|
||||
return ActionResult.resultFail(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHarvestBlock(BlockState blockIn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsRepairable(ItemStack toRepair, ItemStack repair) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLimit(PlayerEntity player, ItemStack stack) {
|
||||
return getLimit();
|
||||
}
|
||||
|
||||
protected int getLimit() {
|
||||
return ConfigServer.getWandProperties(this).getLimit();
|
||||
}
|
||||
|
||||
public static int getWandMode(ItemStack stack) {
|
||||
WandOptions options = new WandOptions(stack);
|
||||
return options.mode.get().ordinal();
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void addInformation(ItemStack itemstack, World worldIn, List<ITextComponent> lines, ITooltipFlag extraInfo) {
|
||||
ItemWand wand = (ItemWand) itemstack.getItem();
|
||||
WandOptions options = new WandOptions(itemstack);
|
||||
|
||||
String langTooltip = ConstructionWand.MODID + ".tooltip.";
|
||||
|
||||
if(Screen.hasShiftDown()) {
|
||||
for(int i=1; i<options.allOptions.length; i++) {
|
||||
IOption<?> opt = options.allOptions[i];
|
||||
lines.add(new TranslationTextComponent(opt.getKeyTranslation()).mergeStyle(TextFormatting.AQUA)
|
||||
.append(new TranslationTextComponent(opt.getValueTranslation()).mergeStyle(TextFormatting.GRAY))
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
IOption<?> opt = options.allOptions[0];
|
||||
lines.add(new TranslationTextComponent(langTooltip + "blocks", getLimit()).mergeStyle(TextFormatting.GRAY));
|
||||
lines.add(new TranslationTextComponent(opt.getKeyTranslation()).mergeStyle(TextFormatting.AQUA)
|
||||
.append(new TranslationTextComponent(opt.getValueTranslation()).mergeStyle(TextFormatting.WHITE)));
|
||||
lines.add(new TranslationTextComponent(langTooltip + "shift").mergeStyle(TextFormatting.AQUA));
|
||||
}
|
||||
}
|
||||
|
||||
public static void optionMessage(PlayerEntity player, IOption<?> option) {
|
||||
player.sendStatusMessage(
|
||||
new TranslationTextComponent(option.getKeyTranslation()).mergeStyle(TextFormatting.AQUA)
|
||||
.append(new TranslationTextComponent(option.getValueTranslation()).mergeStyle(TextFormatting.WHITE))
|
||||
.append(new StringTextComponent(" - ").mergeStyle(TextFormatting.GRAY))
|
||||
.append(new TranslationTextComponent(option.getDescTranslation()).mergeStyle(TextFormatting.WHITE))
|
||||
, true);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package thetadev.constructionwand.items;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.IItemTier;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.Item.Properties;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
|
||||
public class ItemWandBasic extends ItemWand
|
||||
{
|
||||
private final IItemTier tier;
|
||||
|
||||
public ItemWandBasic(String name, IItemTier tier) {
|
||||
super(name, new Properties().maxDamage(tier.getMaxUses()));
|
||||
this.tier = tier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack) {
|
||||
return ConfigServer.getWandProperties(this).getDurability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLimit(PlayerEntity player, ItemStack stack) {
|
||||
return Math.min(stack.getMaxDamage() - stack.getDamage(), getLimit());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsRepairable(ItemStack toRepair, ItemStack repair) {
|
||||
return this.tier.getRepairMaterial().test(repair);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package thetadev.constructionwand.items;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
|
||||
public class ItemWandInfinity extends ItemWand
|
||||
{
|
||||
public ItemWandInfinity(String name)
|
||||
{
|
||||
super(name, new Properties().maxStackSize(1).isBurnable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLimit(PlayerEntity player, ItemStack stack) {
|
||||
return player.isCreative() ? ConfigServer.LIMIT_CREATIVE.get() : getLimit();
|
||||
}
|
||||
}
|
|
@ -1,38 +1,84 @@
|
|||
package thetadev.constructionwand.items;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemModelsProperties;
|
||||
import net.minecraft.item.ItemTier;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Tiers;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.registries.IForgeRegistryEntry;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import net.minecraftforge.registries.*;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.crafting.RecipeWandUpgrade;
|
||||
import thetadev.constructionwand.items.core.ItemCoreAngel;
|
||||
import thetadev.constructionwand.items.core.ItemCoreDestruction;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
import thetadev.constructionwand.items.wand.ItemWandBasic;
|
||||
import thetadev.constructionwand.items.wand.ItemWandInfinity;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = ConstructionWand.MODID, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public class ModItems
|
||||
{
|
||||
public static final Item WAND_STONE = new ItemWandBasic("stone_wand", ItemTier.STONE);
|
||||
public static final Item WAND_IRON = new ItemWandBasic("iron_wand", ItemTier.IRON);
|
||||
public static final Item WAND_DIAMOND = new ItemWandBasic("diamond_wand", ItemTier.DIAMOND);
|
||||
public static final Item WAND_INFINITY = new ItemWandInfinity("infinity_wand");
|
||||
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ConstructionWand.MODID);
|
||||
|
||||
public static final Item[] WANDS = {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY};
|
||||
// Wands
|
||||
public static final RegistryObject<Item> WAND_STONE = ITEMS.register("stone_wand", () -> new ItemWandBasic(propWand(), Tiers.STONE));
|
||||
public static final RegistryObject<Item> WAND_IRON = ITEMS.register("iron_wand", () -> new ItemWandBasic(propWand(), Tiers.IRON));
|
||||
public static final RegistryObject<Item> WAND_DIAMOND = ITEMS.register("diamond_wand", () -> new ItemWandBasic(propWand(), Tiers.DIAMOND));
|
||||
public static final RegistryObject<Item> WAND_INFINITY = ITEMS.register("infinity_wand", () -> new ItemWandInfinity(propWand()));
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRegisterItems(RegistryEvent.Register<Item> event)
|
||||
{
|
||||
event.getRegistry().registerAll(WANDS);
|
||||
}
|
||||
// Cores
|
||||
public static final RegistryObject<Item> CORE_ANGEL = ITEMS.register("core_angel", () -> new ItemCoreAngel(propUpgrade()));
|
||||
public static final RegistryObject<Item> CORE_DESTRUCTION = ITEMS.register("core_destruction", () -> new ItemCoreDestruction(propUpgrade()));
|
||||
|
||||
public static void registerModelProperties() {
|
||||
for(Item item : WANDS) {
|
||||
ItemModelsProperties.func_239418_a_(
|
||||
item, new ResourceLocation(ConstructionWand.MODID, "wand_mode"),
|
||||
(stack, world, entity) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 : ItemWand.getWandMode(stack)
|
||||
);
|
||||
}
|
||||
}
|
||||
// Collections
|
||||
public static final RegistryObject<Item>[] WANDS = new RegistryObject[] {WAND_STONE, WAND_IRON, WAND_DIAMOND, WAND_INFINITY};
|
||||
public static final RegistryObject<Item>[] CORES = new RegistryObject[] {CORE_ANGEL, CORE_DESTRUCTION};
|
||||
|
||||
public static Item.Properties propWand() {
|
||||
return new Item.Properties().tab(CreativeModeTab.TAB_TOOLS);
|
||||
}
|
||||
|
||||
private static Item.Properties propUpgrade() {
|
||||
return new Item.Properties().tab(CreativeModeTab.TAB_MISC).stacksTo(1);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerRecipeSerializers(RegistryEvent.Register<RecipeSerializer<?>> event) {
|
||||
IForgeRegistry<RecipeSerializer<?>> r = event.getRegistry();
|
||||
register(r, "wand_upgrade", RecipeWandUpgrade.SERIALIZER);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void registerModelProperties() {
|
||||
for(RegistryObject<Item> itemSupplier : WANDS) {
|
||||
Item item = itemSupplier.get();
|
||||
ItemProperties.register(
|
||||
item, ConstructionWand.loc("using_core"),
|
||||
(stack, world, entity, n) -> entity == null || !(stack.getItem() instanceof ItemWand) ? 0 :
|
||||
new WandOptions(stack).cores.get().getColor() > -1 ? 1 : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void registerItemColors() {
|
||||
ItemColors colors = Minecraft.getInstance().getItemColors();
|
||||
|
||||
for(RegistryObject<Item> itemSupplier : WANDS) {
|
||||
Item item = itemSupplier.get();
|
||||
colors.register((stack, layer) -> (layer == 1 && stack.getItem() instanceof ItemWand) ?
|
||||
new WandOptions(stack).cores.get().getColor() : -1, item);
|
||||
}
|
||||
}
|
||||
|
||||
private static <V extends IForgeRegistryEntry<V>> void register(IForgeRegistry<V> reg, String name, IForgeRegistryEntry<V> thing) {
|
||||
reg.register(thing.setRegistryName(ConstructionWand.loc(name)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package thetadev.constructionwand.items.core;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.api.IWandCore;
|
||||
import thetadev.constructionwand.wand.action.ActionConstruction;
|
||||
|
||||
public class CoreDefault implements IWandCore
|
||||
{
|
||||
@Override
|
||||
public int getColor() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWandAction getWandAction() {
|
||||
return new ActionConstruction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getRegistryName() {
|
||||
return ConstructionWand.loc("default");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package thetadev.constructionwand.items.core;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.api.IWandCore;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ItemCore extends Item implements IWandCore
|
||||
{
|
||||
public ItemCore(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
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")
|
||||
.withStyle(ChatFormatting.GRAY));
|
||||
lines.add(new TranslatableComponent(ConstructionWand.MODID + ".tooltip.core_tip")
|
||||
.withStyle(ChatFormatting.AQUA));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package thetadev.constructionwand.items.core;
|
||||
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.wand.action.ActionAngel;
|
||||
|
||||
public class ItemCoreAngel extends ItemCore
|
||||
{
|
||||
public ItemCoreAngel(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return 0xE9B115;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWandAction getWandAction() {
|
||||
return new ActionAngel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package thetadev.constructionwand.items.core;
|
||||
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.wand.action.ActionDestruction;
|
||||
|
||||
public class ItemCoreDestruction extends ItemCore
|
||||
{
|
||||
public ItemCoreDestruction(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return 0xFF0000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWandAction getWandAction() {
|
||||
return new ActionDestruction();
|
||||
}
|
||||
}
|
155
src/main/java/thetadev/constructionwand/items/wand/ItemWand.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
package thetadev.constructionwand.items.wand;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.model.generators.ModelFile;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.api.IWandCore;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.IOption;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.data.ICustomItemModel;
|
||||
import thetadev.constructionwand.data.ItemModelGenerator;
|
||||
import thetadev.constructionwand.wand.WandJob;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ItemWand extends Item implements ICustomItemModel
|
||||
{
|
||||
public ItemWand(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
Player player = context.getPlayer();
|
||||
InteractionHand hand = context.getHand();
|
||||
Level world = context.getLevel();
|
||||
|
||||
if(world.isClientSide || player == null) return InteractionResult.FAIL;
|
||||
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
if(player.isCrouching() && ConstructionWand.instance.undoHistory.isUndoActive(player)) {
|
||||
return ConstructionWand.instance.undoHistory.undo(player, world, context.getClickedPos()) ? InteractionResult.SUCCESS : InteractionResult.FAIL;
|
||||
}
|
||||
else {
|
||||
WandJob job = getWandJob(player, world, new BlockHitResult(context.getClickLocation(), context.getClickedFace(), context.getClickedPos(), false), stack);
|
||||
return job.doIt() ? InteractionResult.SUCCESS : InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(@Nonnull Level world, Player player, @Nonnull InteractionHand hand) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
if(!player.isCrouching()) {
|
||||
if(world.isClientSide) return InteractionResultHolder.fail(stack);
|
||||
|
||||
// Right click: Place angel block
|
||||
WandJob job = getWandJob(player, world, BlockHitResult.miss(player.getLookAngle(),
|
||||
WandUtil.fromVector(player.getLookAngle()), WandUtil.playerPos(player)), stack);
|
||||
return job.doIt() ? InteractionResultHolder.success(stack) : InteractionResultHolder.fail(stack);
|
||||
}
|
||||
return InteractionResultHolder.fail(stack);
|
||||
}
|
||||
|
||||
public static WandJob getWandJob(Player player, Level world, @Nullable BlockHitResult rayTraceResult, ItemStack wand) {
|
||||
WandJob wandJob = new WandJob(player, world, rayTraceResult, wand);
|
||||
wandJob.getSnapshots();
|
||||
|
||||
return wandJob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCorrectToolForDrops(@Nonnull BlockState blockIn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidRepairItem(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int remainingDurability(ItemStack stack) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void appendHoverText(@Nonnull ItemStack itemstack, Level worldIn, @Nonnull List<Component> lines, @Nonnull TooltipFlag extraInfo) {
|
||||
WandOptions options = new WandOptions(itemstack);
|
||||
int limit = options.cores.get().getWandAction().getLimit(itemstack);
|
||||
|
||||
String langTooltip = ConstructionWand.MODID + ".tooltip.";
|
||||
|
||||
// +SHIFT tooltip: show all options + installed cores
|
||||
if(Screen.hasShiftDown()) {
|
||||
for(int i = 1; i < options.allOptions.length; i++) {
|
||||
IOption<?> opt = options.allOptions[i];
|
||||
lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
|
||||
.append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.GRAY))
|
||||
);
|
||||
}
|
||||
if(!options.cores.getUpgrades().isEmpty()) {
|
||||
lines.add(new TextComponent(""));
|
||||
lines.add(new TranslatableComponent(langTooltip + "cores").withStyle(ChatFormatting.GRAY));
|
||||
|
||||
for(IWandCore core : options.cores.getUpgrades()) {
|
||||
lines.add(new TranslatableComponent(options.cores.getKeyTranslation() + "." + core.getRegistryName().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Default tooltip: show block limit + active wand core
|
||||
else {
|
||||
IOption<?> opt = options.allOptions[0];
|
||||
lines.add(new TranslatableComponent(langTooltip + "blocks", limit).withStyle(ChatFormatting.GRAY));
|
||||
lines.add(new TranslatableComponent(opt.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
|
||||
.append(new TranslatableComponent(opt.getValueTranslation()).withStyle(ChatFormatting.WHITE)));
|
||||
lines.add(new TranslatableComponent(langTooltip + "shift").withStyle(ChatFormatting.AQUA));
|
||||
}
|
||||
}
|
||||
|
||||
public static void optionMessage(Player player, IOption<?> option) {
|
||||
player.displayClientMessage(
|
||||
new TranslatableComponent(option.getKeyTranslation()).withStyle(ChatFormatting.AQUA)
|
||||
.append(new TranslatableComponent(option.getValueTranslation()).withStyle(ChatFormatting.WHITE))
|
||||
.append(new TextComponent(" - ").withStyle(ChatFormatting.GRAY))
|
||||
.append(new TranslatableComponent(option.getDescTranslation()).withStyle(ChatFormatting.WHITE))
|
||||
, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCustomItemModel(ItemModelGenerator generator, String name) {
|
||||
ModelFile wandWithCore = generator.withExistingParent(name + "_core", "item/handheld")
|
||||
.texture("layer0", generator.modLoc("item/" + name))
|
||||
.texture("layer1", generator.modLoc("item/overlay_core"));
|
||||
|
||||
generator.withExistingParent(name, "item/handheld")
|
||||
.texture("layer0", generator.modLoc("item/" + name))
|
||||
.override()
|
||||
.predicate(generator.modLoc("using_core"), 1)
|
||||
.model(wandWithCore).end();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package thetadev.constructionwand.items.wand;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Tier;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ItemWandBasic extends ItemWand
|
||||
{
|
||||
private final Tier tier;
|
||||
|
||||
public ItemWandBasic(Properties properties, Tier tier) {
|
||||
super(properties.durability(tier.getUses()));
|
||||
this.tier = tier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack) {
|
||||
return ConfigServer.getWandProperties(this).getDurability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remainingDurability(ItemStack stack) {
|
||||
return stack.getMaxDamage() - stack.getDamageValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidRepairItem(@Nonnull ItemStack toRepair, @Nonnull ItemStack repair) {
|
||||
return this.tier.getRepairIngredient().test(repair);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package thetadev.constructionwand.items.wand;
|
||||
|
||||
|
||||
public class ItemWandInfinity extends ItemWand
|
||||
{
|
||||
public ItemWandInfinity(Properties properties) {
|
||||
super(properties.stacksTo(1).fireResistant());
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
|
||||
public class AngelJob extends WandJob
|
||||
{
|
||||
public AngelJob(PlayerEntity player, World world, ItemStack wand) {
|
||||
super(player, world, new BlockRayTraceResult(player.getLookVec(), fromVector(player.getLookVec()), WandUtil.playerPos(player), false), wand);
|
||||
}
|
||||
|
||||
private static Direction fromVector(Vector3d vector) {
|
||||
return Direction.getFacingFromVector(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getBlockPositionList() {
|
||||
if(options.mode.get() != WandOptions.MODE.ANGEL || ConfigServer.getWandProperties(wandItem).getAngel() == 0) return;
|
||||
|
||||
if(!player.isCreative() && !ConfigServer.ANGEL_FALLING.get() && player.fallDistance > 10) return;
|
||||
|
||||
Vector3d playerVec = WandUtil.entityPositionVec(player);
|
||||
Vector3d lookVec = player.getLookVec().mul(2, 2, 2);
|
||||
Vector3d placeVec = playerVec.add(lookVec);
|
||||
|
||||
BlockPos currentPos = new BlockPos(placeVec);
|
||||
|
||||
PlaceSnapshot snapshot = getPlaceSnapshot(currentPos, Blocks.AIR.getDefaultState());
|
||||
if(snapshot != null) {
|
||||
placeSnapshots.add(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ConstructionJob extends WandJob
|
||||
{
|
||||
public ConstructionJob(PlayerEntity player, World world, BlockRayTraceResult rayTraceResult, ItemStack itemStack) {
|
||||
super(player, world, rayTraceResult, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getBlockPositionList() {
|
||||
LinkedList<BlockPos> candidates = new LinkedList<>();
|
||||
HashSet<BlockPos> allCandidates = new HashSet<>();
|
||||
|
||||
Direction placeDirection = rayTraceResult.getFace();
|
||||
BlockState targetBlock = world.getBlockState(rayTraceResult.getPos());
|
||||
BlockPos startingPoint = rayTraceResult.getPos().offset(placeDirection);
|
||||
|
||||
// Is place direction allowed by lock?
|
||||
if(placeDirection == Direction.UP || placeDirection == Direction.DOWN) {
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) || options.testLock(WandOptions.LOCK.EASTWEST)) candidates.add(startingPoint);
|
||||
}
|
||||
else if(options.testLock(WandOptions.LOCK.HORIZONTAL) || options.testLock(WandOptions.LOCK.VERTICAL)) candidates.add(startingPoint);
|
||||
|
||||
while(!candidates.isEmpty() && placeSnapshots.size() < maxBlocks)
|
||||
{
|
||||
BlockPos currentCandidate = candidates.removeFirst();
|
||||
try {
|
||||
BlockPos supportingPoint = currentCandidate.offset(placeDirection.getOpposite());
|
||||
BlockState candidateSupportingBlock = world.getBlockState(supportingPoint);
|
||||
|
||||
if(matchBlocks(targetBlock.getBlock(), candidateSupportingBlock.getBlock()) && allCandidates.add(currentCandidate)) {
|
||||
PlaceSnapshot snapshot = getPlaceSnapshot(currentCandidate, candidateSupportingBlock);
|
||||
if(snapshot == null) continue;
|
||||
placeSnapshots.add(snapshot);
|
||||
|
||||
switch(placeDirection) {
|
||||
case DOWN:
|
||||
case UP:
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.EASTWEST)) {
|
||||
candidates.add(currentCandidate.offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.WEST));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) && options.testLock(WandOptions.LOCK.EASTWEST)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH).offset(Direction.WEST));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH).offset(Direction.WEST));
|
||||
}
|
||||
break;
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.WEST));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.WEST));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.EAST));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.WEST));
|
||||
}
|
||||
break;
|
||||
case EAST:
|
||||
case WEST:
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.NORTH));
|
||||
candidates.add(currentCandidate.offset(Direction.UP).offset(Direction.SOUTH));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.NORTH));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN).offset(Direction.SOUTH));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
// Can't do anything, could be anything.
|
||||
// Skip if anything goes wrong.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class PlaceSnapshot
|
||||
{
|
||||
public BlockState block;
|
||||
public final BlockState supportingBlock;
|
||||
public final BlockPos pos;
|
||||
public final BlockItem item;
|
||||
|
||||
public PlaceSnapshot(BlockPos pos, BlockState supportingBlock, BlockItem item)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.supportingBlock = supportingBlock;
|
||||
this.item = item;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.world.World;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
|
||||
public class TransductionJob extends WandJob
|
||||
{
|
||||
public TransductionJob(PlayerEntity player, World world, BlockRayTraceResult rayTraceResult, ItemStack wand) {
|
||||
super(player, world, rayTraceResult, wand);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getBlockPositionList() {
|
||||
Direction placeDirection = rayTraceResult.getFace();
|
||||
BlockPos currentPos = rayTraceResult.getPos();
|
||||
BlockState supportingBlock = world.getBlockState(currentPos);
|
||||
|
||||
for(int i = 0; i< ConfigServer.getWandProperties(wandItem).getAngel(); i++) {
|
||||
currentPos = currentPos.offset(placeDirection.getOpposite());
|
||||
|
||||
PlaceSnapshot snapshot = getPlaceSnapshot(currentPos, supportingBlock);
|
||||
if(snapshot != null) {
|
||||
placeSnapshots.add(snapshot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.network.PacketDistributor;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.ReplacementRegistry;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.network.PacketUndoBlocks;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class UndoHistory
|
||||
{
|
||||
private final HashMap<UUID, PlayerEntry> history;
|
||||
|
||||
public UndoHistory() {
|
||||
history = new HashMap<>();
|
||||
}
|
||||
|
||||
private PlayerEntry getEntryFromPlayer(PlayerEntity player) {
|
||||
return history.computeIfAbsent(player.getUniqueID(), k -> new PlayerEntry());
|
||||
}
|
||||
|
||||
public void add(PlayerEntity player, World world, LinkedList<PlaceSnapshot> placeSnapshots) {
|
||||
LinkedList<HistoryEntry> list = getEntryFromPlayer(player).entries;
|
||||
list.add(new HistoryEntry(placeSnapshots, world));
|
||||
while(list.size() > ConfigServer.UNDO_HISTORY.get()) list.removeFirst();
|
||||
}
|
||||
|
||||
public void removePlayer(PlayerEntity player) {
|
||||
history.remove(player.getUniqueID());
|
||||
}
|
||||
|
||||
public void updateClient(PlayerEntity player, boolean ctrlDown) {
|
||||
World world = player.getEntityWorld();
|
||||
if(world.isRemote) return;
|
||||
|
||||
// Set state of CTRL key
|
||||
PlayerEntry playerEntry = getEntryFromPlayer(player);
|
||||
playerEntry.undoActive = ctrlDown;
|
||||
|
||||
LinkedList<HistoryEntry> historyEntries = playerEntry.entries;
|
||||
Set<BlockPos> positions;
|
||||
|
||||
// Send block positions of most recent entry to client
|
||||
if(historyEntries.isEmpty()) positions = Collections.emptySet();
|
||||
else {
|
||||
HistoryEntry entry = historyEntries.getLast();
|
||||
|
||||
if(entry == null || !entry.world.equals(world)) positions = Collections.emptySet();
|
||||
else positions = entry.getBlockPositions();
|
||||
}
|
||||
|
||||
PacketUndoBlocks packet = new PacketUndoBlocks(positions);
|
||||
ConstructionWand.instance.HANDLER.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), packet);
|
||||
}
|
||||
|
||||
public boolean isUndoActive(PlayerEntity player) {
|
||||
return getEntryFromPlayer(player).undoActive;
|
||||
}
|
||||
|
||||
public boolean undo(PlayerEntity player, World world, BlockPos pos) {
|
||||
// If CTRL key is not pressed, return
|
||||
PlayerEntry playerEntry = getEntryFromPlayer(player);
|
||||
if(!playerEntry.undoActive) return false;
|
||||
|
||||
// Get the most recent entry for undo
|
||||
LinkedList<HistoryEntry> historyEntries = playerEntry.entries;
|
||||
if(historyEntries.isEmpty()) return false;
|
||||
HistoryEntry entry = historyEntries.getLast();
|
||||
|
||||
if(entry.world.equals(world) && entry.getBlockPositions().contains(pos)) {
|
||||
// Remove history entry, sent update to client and undo it
|
||||
historyEntries.remove(entry);
|
||||
updateClient(player, true);
|
||||
return entry.undo(player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class PlayerEntry {
|
||||
public final LinkedList<HistoryEntry> entries;
|
||||
public boolean undoActive;
|
||||
|
||||
public PlayerEntry() {
|
||||
entries = new LinkedList<>();
|
||||
undoActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HistoryEntry {
|
||||
public final LinkedList<PlaceSnapshot> placeSnapshots;
|
||||
public final World world;
|
||||
|
||||
public HistoryEntry(LinkedList<PlaceSnapshot> placeSnapshots, World world) {
|
||||
this.placeSnapshots = placeSnapshots;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public Set<BlockPos> getBlockPositions() {
|
||||
return placeSnapshots.stream().map(snapshot -> snapshot.pos).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public boolean undo(PlayerEntity player) {
|
||||
for(PlaceSnapshot snapshot : placeSnapshots) {
|
||||
BlockState currentBlock = world.getBlockState(snapshot.pos);
|
||||
|
||||
// If placed block is still present and can be broken, break it and return item
|
||||
if(world.isBlockModifiable(player, snapshot.pos) &&
|
||||
(player.isCreative() ||
|
||||
(currentBlock.getBlockHardness(world, snapshot.pos) > -1 && world.getTileEntity(snapshot.pos) == null && ReplacementRegistry.matchBlocks(currentBlock.getBlock(), snapshot.block.getBlock()))))
|
||||
{
|
||||
BlockEvent.BreakEvent breakEvent = new BlockEvent.BreakEvent(world, snapshot.pos, currentBlock, player);
|
||||
MinecraftForge.EVENT_BUS.post(breakEvent);
|
||||
if(breakEvent.isCanceled()) continue;
|
||||
|
||||
world.removeBlock(snapshot.pos, false);
|
||||
|
||||
if(!player.isCreative()) {
|
||||
ItemStack stack = new ItemStack(snapshot.item);
|
||||
if(!player.inventory.addItemStackToInventory(stack)) {
|
||||
player.dropItem(stack, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
player.inventory.markDirty();
|
||||
|
||||
// Play teleport sound
|
||||
SoundEvent sound = SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT;
|
||||
world.playSound(null, WandUtil.playerPos(player), sound, SoundCategory.PLAYERS, 1.0F, 1.0F);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.BlockItemUseContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
|
||||
public class WandItemUseContext extends BlockItemUseContext
|
||||
{
|
||||
public WandItemUseContext(WandJob job, BlockPos pos, BlockItem item) {
|
||||
super(job.world, job.player, Hand.MAIN_HAND, new ItemStack(item), new BlockRayTraceResult(getBlockHitVec(job, pos), job.rayTraceResult.getFace(), pos, false));
|
||||
}
|
||||
|
||||
private static Vector3d getBlockHitVec(WandJob job, BlockPos pos) {
|
||||
Vector3d hitVec = job.rayTraceResult.getHitVec(); // Absolute coords of hit target
|
||||
|
||||
Vector3d blockDelta = WandUtil.blockPosVec(job.rayTraceResult.getPos()).subtract(WandUtil.blockPosVec(pos)); // Vector between start and current block
|
||||
|
||||
return blockDelta.add(hitVec); // Absolute coords of current block hit target
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace() {
|
||||
return replaceClicked;
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
package thetadev.constructionwand.job;
|
||||
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.state.Property;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.state.properties.SlabType;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.BlockRayTraceResult;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.BlockSnapshot;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.basics.*;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.basics.pool.*;
|
||||
import thetadev.constructionwand.containers.ContainerManager;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class WandJob
|
||||
{
|
||||
protected final PlayerEntity player;
|
||||
protected final World world;
|
||||
protected final BlockRayTraceResult rayTraceResult;
|
||||
protected ItemStack wand;
|
||||
protected ItemWand wandItem;
|
||||
|
||||
// Wand options
|
||||
protected WandOptions options;
|
||||
protected int maxBlocks;
|
||||
|
||||
protected HashMap<BlockItem, Integer> itemCounts;
|
||||
protected IPool<BlockItem> itemPool;
|
||||
|
||||
protected LinkedList<PlaceSnapshot> placeSnapshots;
|
||||
|
||||
|
||||
protected WandJob(PlayerEntity player, World world, BlockRayTraceResult rayTraceResult, ItemStack wand)
|
||||
{
|
||||
this.player = player;
|
||||
this.world = world;
|
||||
this.rayTraceResult = rayTraceResult;
|
||||
placeSnapshots = new LinkedList<>();
|
||||
|
||||
// Get wand
|
||||
if(wand == null || wand == ItemStack.EMPTY || !(wand.getItem() instanceof ItemWand)) return;
|
||||
this.wand = wand;
|
||||
|
||||
wandItem = (ItemWand) wand.getItem();
|
||||
|
||||
// Get options
|
||||
options = new WandOptions(wand);
|
||||
|
||||
// Get place item
|
||||
addBlockItems();
|
||||
if(itemCounts.isEmpty()) return;
|
||||
|
||||
// Get inventory supply
|
||||
for(int v : itemCounts.values()) {
|
||||
try {
|
||||
maxBlocks = Math.addExact(maxBlocks, v);
|
||||
}
|
||||
catch(ArithmeticException e) {
|
||||
maxBlocks = Integer.MAX_VALUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
maxBlocks = Math.min(maxBlocks, wandItem.getLimit(player, wand));
|
||||
if(maxBlocks == 0) return;
|
||||
|
||||
getBlockPositionList();
|
||||
}
|
||||
|
||||
public static WandJob getJob(PlayerEntity player, World world, BlockRayTraceResult rayTraceResult, ItemStack itemStack) {
|
||||
WandOptions options = new WandOptions(itemStack);
|
||||
|
||||
if(options.mode.get() == WandOptions.MODE.ANGEL) return new TransductionJob(player, world, rayTraceResult, itemStack);
|
||||
return new ConstructionJob(player, world, rayTraceResult, itemStack);
|
||||
}
|
||||
|
||||
public Set<BlockPos> getBlockPositions() {
|
||||
return placeSnapshots.stream().map(snapshot -> snapshot.pos).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public BlockRayTraceResult getRayTraceResult() { return rayTraceResult; }
|
||||
|
||||
public ItemStack getWand() { return wand; }
|
||||
|
||||
private void addBlockItem(BlockItem item) {
|
||||
int count = countItem(item);
|
||||
if(count > 0) {
|
||||
itemCounts.put(item, count);
|
||||
itemPool.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void addBlockItems() {
|
||||
itemCounts = new LinkedHashMap<>();
|
||||
|
||||
BlockPos targetPos = rayTraceResult.getPos();
|
||||
BlockState targetState = world.getBlockState(targetPos);
|
||||
Block targetBlock = targetState.getBlock();
|
||||
ItemStack offhandStack = player.getHeldItem(Hand.OFF_HAND);
|
||||
|
||||
// Random mode -> add all items from hotbar
|
||||
if(options.random.get()) {
|
||||
itemPool = new RandomPool<>(player.getRNG());
|
||||
|
||||
for(ItemStack stack : WandUtil.getHotbarWithOffhand(player)) {
|
||||
if(stack.getItem() instanceof BlockItem) addBlockItem((BlockItem) stack.getItem());
|
||||
}
|
||||
}
|
||||
else {
|
||||
itemPool = new OrderedPool<>();
|
||||
|
||||
// Block in offhand -> override
|
||||
if(!offhandStack.isEmpty() && offhandStack.getItem() instanceof BlockItem) {
|
||||
addBlockItem((BlockItem) offhandStack.getItem());
|
||||
}
|
||||
// Otherwise use target block
|
||||
else {
|
||||
Item item = targetBlock.asItem();
|
||||
if(item instanceof BlockItem) {
|
||||
addBlockItem((BlockItem) item);
|
||||
|
||||
// Add replacement items
|
||||
if(options.match.get() != WandOptions.MATCH.EXACT) {
|
||||
for(Item it : ReplacementRegistry.getMatchingSet(item)) {
|
||||
if(it instanceof BlockItem) addBlockItem((BlockItem) it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int countItem(Item item) {
|
||||
if(player.inventory == null || player.inventory.mainInventory == null) return 0;
|
||||
if(player.isCreative()) return Integer.MAX_VALUE;
|
||||
|
||||
int total = 0;
|
||||
ContainerManager containerManager = ConstructionWand.instance.containerManager;
|
||||
List<ItemStack> inventory = WandUtil.getFullInv(player);
|
||||
|
||||
for(ItemStack stack : inventory) {
|
||||
if(stack == null) continue;
|
||||
|
||||
if(WandUtil.stackEquals(stack, item)) {
|
||||
total += stack.getCount();
|
||||
}
|
||||
else {
|
||||
int amount = containerManager.countItems(player, new ItemStack(item), stack);
|
||||
if(amount == Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
||||
total += amount;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Attempts to take specified number of items, returns number of missing items
|
||||
private int takeItems(Item item, int count)
|
||||
{
|
||||
if(player.inventory == null || player.inventory.mainInventory == null) return count;
|
||||
if(player.isCreative()) return 0;
|
||||
|
||||
List<ItemStack> hotbar = WandUtil.getHotbarWithOffhand(player);
|
||||
List<ItemStack> mainInv = WandUtil.getMainInv(player);
|
||||
|
||||
// Take items from main inv, loose items first
|
||||
count = takeItemsInvList(count, item, mainInv, false);
|
||||
count = takeItemsInvList(count, item, mainInv, true);
|
||||
|
||||
// Take items from hotbar, containers first
|
||||
count = takeItemsInvList(count, item, hotbar, true);
|
||||
count = takeItemsInvList(count, item, hotbar, false);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private int takeItemsInvList(int count, Item item, List<ItemStack> inv, boolean container) {
|
||||
ContainerManager containerManager = ConstructionWand.instance.containerManager;
|
||||
|
||||
for(ItemStack stack : inv) {
|
||||
if(count == 0) break;
|
||||
|
||||
if(container) {
|
||||
int nCount = containerManager.useItems(player, new ItemStack(item), stack, count);
|
||||
count = nCount;
|
||||
}
|
||||
|
||||
if(!container && WandUtil.stackEquals(stack, item)) {
|
||||
int toTake = Math.min(count, stack.getCount());
|
||||
stack.shrink(toTake);
|
||||
count -= toTake;
|
||||
player.inventory.markDirty();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
protected abstract void getBlockPositionList();
|
||||
|
||||
// Get PlaceSnapshot, or null if no block can be placed
|
||||
@Nullable
|
||||
protected PlaceSnapshot getPlaceSnapshot(BlockPos pos, BlockState supportingBlock) {
|
||||
// Is position out of world?
|
||||
if(!world.isBlockPresent(pos)) return null;
|
||||
|
||||
// Is block modifiable?
|
||||
if(!world.isBlockModifiable(player, pos)) return null;
|
||||
|
||||
// If replace mode is off, target has to be air
|
||||
if(!options.replace.get() && !world.isAirBlock(pos)) return null;
|
||||
|
||||
// Limit placement range
|
||||
if(ConfigServer.MAX_RANGE.get() > 0 && WandUtil.maxRange(rayTraceResult.getPos(), pos) > ConfigServer.MAX_RANGE.get()) return null;
|
||||
|
||||
itemPool.reset();
|
||||
|
||||
while(true) {
|
||||
// Draw item from pool (returns null if none are left)
|
||||
BlockItem item = itemPool.draw();
|
||||
if(item == null) return null;
|
||||
|
||||
int count = itemCounts.get(item);
|
||||
if(count == 0) continue;
|
||||
|
||||
// Is block at pos replaceable?
|
||||
BlockItemUseContext ctx = new WandItemUseContext(this, pos, item);
|
||||
if(!ctx.canPlace()) continue;
|
||||
|
||||
// Can block be placed?
|
||||
BlockState blockState = item.getBlock().getStateForPlacement(ctx);
|
||||
if(blockState == null) continue;
|
||||
blockState = Block.getValidBlockForPosition(blockState, world, pos);
|
||||
if(blockState.getBlock() == Blocks.AIR || !blockState.isValidPosition(world, pos)) continue;
|
||||
|
||||
// No entities colliding?
|
||||
VoxelShape shape = blockState.getCollisionShape(world, pos);
|
||||
if(!shape.isEmpty()) {
|
||||
AxisAlignedBB blockBB = shape.getBoundingBox().offset(pos);
|
||||
if(!world.getEntitiesWithinAABB(LivingEntity.class, blockBB, EntityPredicates.NOT_SPECTATING).isEmpty()) continue;
|
||||
}
|
||||
|
||||
// Reduce item count
|
||||
if(count < Integer.MAX_VALUE) itemCounts.merge(item, -1, Integer::sum);
|
||||
return new PlaceSnapshot(pos, supportingBlock, item);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean placeBlock(PlaceSnapshot placeSnapshot) {
|
||||
BlockPos blockPos = placeSnapshot.pos;
|
||||
|
||||
BlockItemUseContext ctx = new WandItemUseContext(this, blockPos, placeSnapshot.item);
|
||||
if(!ctx.canPlace()) return false;
|
||||
|
||||
BlockState placeBlock = Block.getBlockFromItem(placeSnapshot.item).getStateForPlacement(ctx);
|
||||
if(placeBlock == null) return false;
|
||||
|
||||
BlockState supportingBlock = placeSnapshot.supportingBlock;
|
||||
|
||||
if(options.direction.get() == WandOptions.DIRECTION.TARGET) {
|
||||
// Block properties to be copied (alignment/rotation properties)
|
||||
for(Property property : new Property[] {
|
||||
BlockStateProperties.HORIZONTAL_FACING, BlockStateProperties.FACING, BlockStateProperties.FACING_EXCEPT_UP,
|
||||
BlockStateProperties.ROTATION_0_15, BlockStateProperties.AXIS, BlockStateProperties.HALF, BlockStateProperties.STAIRS_SHAPE})
|
||||
{
|
||||
if(supportingBlock.hasProperty(property) && placeBlock.hasProperty(property)) {
|
||||
placeBlock = placeBlock.with(property, supportingBlock.get(property));
|
||||
}
|
||||
}
|
||||
|
||||
// Dont dupe double slabs
|
||||
if(supportingBlock.hasProperty(BlockStateProperties.SLAB_TYPE) && placeBlock.hasProperty(BlockStateProperties.SLAB_TYPE)) {
|
||||
SlabType slabType = supportingBlock.get(BlockStateProperties.SLAB_TYPE);
|
||||
if(slabType != SlabType.DOUBLE) placeBlock = placeBlock.with(BlockStateProperties.SLAB_TYPE, slabType);
|
||||
}
|
||||
}
|
||||
// Place the block
|
||||
if(!world.setBlockState(blockPos, placeBlock)) {
|
||||
ConstructionWand.LOGGER.info("Block could not be placed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove block if placeEvent is canceled
|
||||
BlockSnapshot snapshot = BlockSnapshot.create(world.func_234923_W_(), world, blockPos);
|
||||
BlockEvent.EntityPlaceEvent placeEvent = new BlockEvent.EntityPlaceEvent(snapshot, placeBlock, player);
|
||||
MinecraftForge.EVENT_BUS.post(placeEvent);
|
||||
if(placeEvent.isCanceled()) {
|
||||
world.removeBlock(blockPos, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update stats
|
||||
player.addStat(Stats.ITEM_USED.get(placeSnapshot.item));
|
||||
player.addStat(ModStats.USE_WAND);
|
||||
|
||||
placeSnapshot.block = placeBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean matchBlocks(Block b1, Block b2) {
|
||||
switch(options.match.get()) {
|
||||
case EXACT: return b1 == b2;
|
||||
case SIMILAR: return ReplacementRegistry.matchBlocks(b1, b2);
|
||||
case ANY: return b1 != Blocks.AIR && b2 != Blocks.AIR;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean doIt() {
|
||||
LinkedList<PlaceSnapshot> placed = new LinkedList<>();
|
||||
|
||||
for(PlaceSnapshot snapshot : placeSnapshots) {
|
||||
if(wand.isEmpty() || wandItem.getLimit(player, wand) == 0) continue;
|
||||
|
||||
BlockPos pos = snapshot.pos;
|
||||
BlockItem placeItem = snapshot.item;
|
||||
|
||||
if(placeBlock(snapshot)) {
|
||||
wand.damageItem(1, player, (e) -> e.sendBreakAnimation(player.swingingHand));
|
||||
|
||||
// If the item cant be taken, undo the placement
|
||||
if(takeItems(placeItem, 1) == 0) placed.add(snapshot);
|
||||
else {
|
||||
ConstructionWand.LOGGER.info("Item could not be taken. Remove block: "+placeItem.toString());
|
||||
world.removeBlock(pos, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
placeSnapshots = placed;
|
||||
|
||||
// Play place sound
|
||||
if(!placeSnapshots.isEmpty()) {
|
||||
SoundType sound = placeSnapshots.getFirst().block.getSoundType();
|
||||
world.playSound(null, WandUtil.playerPos(player), sound.getPlaceSound(), SoundCategory.BLOCKS, sound.volume, sound.pitch);
|
||||
}
|
||||
|
||||
// Add to job history for undo
|
||||
if(placeSnapshots.size() > 1) ConstructionWand.instance.undoHistory.add(player, world, placeSnapshots);
|
||||
|
||||
return !placeSnapshots.isEmpty();
|
||||
}
|
||||
}
|
|
@ -1,39 +1,39 @@
|
|||
package thetadev.constructionwand.network;
|
||||
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PacketQueryUndo
|
||||
{
|
||||
public boolean undoPressed;
|
||||
public boolean undoPressed;
|
||||
|
||||
public PacketQueryUndo(boolean undoPressed) {
|
||||
this.undoPressed = undoPressed;
|
||||
}
|
||||
public PacketQueryUndo(boolean undoPressed) {
|
||||
this.undoPressed = undoPressed;
|
||||
}
|
||||
|
||||
public static void encode(PacketQueryUndo msg, PacketBuffer buffer) {
|
||||
buffer.writeBoolean(msg.undoPressed);
|
||||
}
|
||||
public static void encode(PacketQueryUndo msg, FriendlyByteBuf buffer) {
|
||||
buffer.writeBoolean(msg.undoPressed);
|
||||
}
|
||||
|
||||
public static PacketQueryUndo decode(PacketBuffer buffer) {
|
||||
return new PacketQueryUndo(buffer.readBoolean());
|
||||
}
|
||||
public static PacketQueryUndo decode(FriendlyByteBuf buffer) {
|
||||
return new PacketQueryUndo(buffer.readBoolean());
|
||||
}
|
||||
|
||||
public static class Handler
|
||||
{
|
||||
public static void handle(final PacketQueryUndo msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isServer()) return;
|
||||
public static class Handler
|
||||
{
|
||||
public static void handle(final PacketQueryUndo msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isServer()) return;
|
||||
|
||||
ServerPlayerEntity player = ctx.get().getSender();
|
||||
if(player == null) return;
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if(player == null) return;
|
||||
|
||||
ConstructionWand.instance.undoHistory.updateClient(player, msg.undoPressed);
|
||||
ConstructionWand.instance.undoHistory.updateClient(player, msg.undoPressed);
|
||||
|
||||
//ConstructionWand.LOGGER.debug("Undo queried");
|
||||
}
|
||||
}
|
||||
//ConstructionWand.LOGGER.debug("Undo queried");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +1,50 @@
|
|||
package thetadev.constructionwand.network;
|
||||
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PacketUndoBlocks
|
||||
{
|
||||
public HashSet<BlockPos> undoBlocks;
|
||||
public HashSet<BlockPos> undoBlocks;
|
||||
|
||||
public PacketUndoBlocks(Set<BlockPos> undoBlocks) {
|
||||
this.undoBlocks = new HashSet<>(undoBlocks);
|
||||
}
|
||||
private PacketUndoBlocks(HashSet<BlockPos> undoBlocks) {
|
||||
this.undoBlocks = undoBlocks;
|
||||
}
|
||||
public PacketUndoBlocks(Set<BlockPos> undoBlocks) {
|
||||
this.undoBlocks = new HashSet<>(undoBlocks);
|
||||
}
|
||||
|
||||
public static void encode(PacketUndoBlocks msg, PacketBuffer buffer) {
|
||||
for(BlockPos pos : msg.undoBlocks) {
|
||||
buffer.writeBlockPos(pos);
|
||||
}
|
||||
}
|
||||
private PacketUndoBlocks(HashSet<BlockPos> undoBlocks) {
|
||||
this.undoBlocks = undoBlocks;
|
||||
}
|
||||
|
||||
public static PacketUndoBlocks decode(PacketBuffer buffer) {
|
||||
HashSet<BlockPos> undoBlocks = new HashSet<>();
|
||||
public static void encode(PacketUndoBlocks msg, FriendlyByteBuf buffer) {
|
||||
for(BlockPos pos : msg.undoBlocks) {
|
||||
buffer.writeBlockPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
while(buffer.isReadable()) {
|
||||
undoBlocks.add(buffer.readBlockPos());
|
||||
}
|
||||
return new PacketUndoBlocks(undoBlocks);
|
||||
}
|
||||
public static PacketUndoBlocks decode(FriendlyByteBuf buffer) {
|
||||
HashSet<BlockPos> undoBlocks = new HashSet<>();
|
||||
|
||||
public static class Handler {
|
||||
public static void handle(final PacketUndoBlocks msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isClient()) return;
|
||||
while(buffer.isReadable()) {
|
||||
undoBlocks.add(buffer.readBlockPos());
|
||||
}
|
||||
return new PacketUndoBlocks(undoBlocks);
|
||||
}
|
||||
|
||||
//ConstructionWand.LOGGER.debug("PacketUndoBlocks received, Blocks: " + msg.undoBlocks.size());
|
||||
ConstructionWand.instance.renderBlockPreview.undoBlocks = msg.undoBlocks;
|
||||
public static class Handler
|
||||
{
|
||||
public static void handle(final PacketUndoBlocks msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isClient()) return;
|
||||
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
//ConstructionWand.LOGGER.debug("PacketUndoBlocks received, Blocks: " + msg.undoBlocks.size());
|
||||
ConstructionWand.instance.renderBlockPreview.undoBlocks = msg.undoBlocks;
|
||||
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
package thetadev.constructionwand.network;
|
||||
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.IOption;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.items.ItemWand;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PacketWandOption
|
||||
{
|
||||
public final String key;
|
||||
public final String value;
|
||||
public final boolean notify;
|
||||
public final String key;
|
||||
public final String value;
|
||||
public final boolean notify;
|
||||
|
||||
public PacketWandOption(IOption<?> option, boolean notify) {
|
||||
this(option.getKey(), option.getValueString(), notify);
|
||||
}
|
||||
public PacketWandOption(IOption<?> option, boolean notify) {
|
||||
this(option.getKey(), option.getValueString(), notify);
|
||||
}
|
||||
|
||||
private PacketWandOption(String key, String value, boolean notify) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.notify = notify;
|
||||
}
|
||||
private PacketWandOption(String key, String value, boolean notify) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.notify = notify;
|
||||
}
|
||||
|
||||
public static void encode(PacketWandOption msg, PacketBuffer buffer) {
|
||||
buffer.writeString(msg.key);
|
||||
buffer.writeString(msg.value);
|
||||
buffer.writeBoolean(msg.notify);
|
||||
}
|
||||
public static void encode(PacketWandOption msg, FriendlyByteBuf buffer) {
|
||||
buffer.writeUtf(msg.key);
|
||||
buffer.writeUtf(msg.value);
|
||||
buffer.writeBoolean(msg.notify);
|
||||
}
|
||||
|
||||
public static PacketWandOption decode(PacketBuffer buffer) {
|
||||
return new PacketWandOption(buffer.readString(100), buffer.readString(100), buffer.readBoolean());
|
||||
}
|
||||
public static PacketWandOption decode(FriendlyByteBuf buffer) {
|
||||
return new PacketWandOption(buffer.readUtf(100), buffer.readUtf(100), buffer.readBoolean());
|
||||
}
|
||||
|
||||
public static class Handler
|
||||
{
|
||||
public static void handle(final PacketWandOption msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isServer()) return;
|
||||
public static class Handler
|
||||
{
|
||||
public static void handle(final PacketWandOption msg, final Supplier<NetworkEvent.Context> ctx) {
|
||||
if(!ctx.get().getDirection().getReceptionSide().isServer()) return;
|
||||
|
||||
ServerPlayerEntity player = ctx.get().getSender();
|
||||
if(player == null) return;
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if(player == null) return;
|
||||
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
WandOptions options = new WandOptions(wand);
|
||||
ItemStack wand = WandUtil.holdingWand(player);
|
||||
if(wand == null) return;
|
||||
WandOptions options = new WandOptions(wand);
|
||||
|
||||
IOption<?> option = options.get(msg.key);
|
||||
if(option == null) return;
|
||||
option.setValueString(msg.value);
|
||||
IOption<?> option = options.get(msg.key);
|
||||
if(option == null) return;
|
||||
option.setValueString(msg.value);
|
||||
|
||||
if(msg.notify) ItemWand.optionMessage(player, option);
|
||||
player.inventory.markDirty();
|
||||
}
|
||||
}
|
||||
if(msg.notify) ItemWand.optionMessage(player, option);
|
||||
player.getInventory().setChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package thetadev.constructionwand.wand;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
|
||||
public class WandItemUseContext extends BlockPlaceContext
|
||||
{
|
||||
public WandItemUseContext(Level world, Player player, BlockHitResult rayTraceResult, BlockPos pos, BlockItem item) {
|
||||
super(world, player, InteractionHand.MAIN_HAND, new ItemStack(item),
|
||||
new BlockHitResult(getBlockHitVec(rayTraceResult, pos), rayTraceResult.getDirection(), pos, false));
|
||||
}
|
||||
|
||||
private static Vec3 getBlockHitVec(BlockHitResult rayTraceResult, BlockPos pos) {
|
||||
Vec3 hitVec = rayTraceResult.getLocation(); // Absolute coords of hit target
|
||||
|
||||
Vec3 blockDelta = WandUtil.blockPosVec(rayTraceResult.getBlockPos()).subtract(WandUtil.blockPosVec(pos)); // Vector between start and current block
|
||||
|
||||
return blockDelta.add(hitVec); // Absolute coords of current block hit target
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace() {
|
||||
return replaceClicked;
|
||||
}
|
||||
}
|
130
src/main/java/thetadev/constructionwand/wand/WandJob.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
package thetadev.constructionwand.wand;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import thetadev.constructionwand.ConstructionWand;
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.api.IWandSupplier;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.ModStats;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.items.ModItems;
|
||||
import thetadev.constructionwand.items.wand.ItemWand;
|
||||
import thetadev.constructionwand.wand.supplier.SupplierInventory;
|
||||
import thetadev.constructionwand.wand.supplier.SupplierRandom;
|
||||
import thetadev.constructionwand.wand.undo.ISnapshot;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WandJob
|
||||
{
|
||||
public final Player player;
|
||||
public final Level world;
|
||||
public final BlockHitResult rayTraceResult;
|
||||
public final WandOptions options;
|
||||
public final ItemStack wand;
|
||||
public final ItemWand wandItem;
|
||||
|
||||
private final IWandAction wandAction;
|
||||
private final IWandSupplier wandSupplier;
|
||||
|
||||
private List<ISnapshot> placeSnapshots;
|
||||
|
||||
public WandJob(Player player, Level world, BlockHitResult rayTraceResult, ItemStack wand) {
|
||||
this.player = player;
|
||||
this.world = world;
|
||||
this.rayTraceResult = rayTraceResult;
|
||||
this.placeSnapshots = new ArrayList<>();
|
||||
|
||||
// Get wand
|
||||
this.wand = wand;
|
||||
this.wandItem = (ItemWand) wand.getItem();
|
||||
options = new WandOptions(wand);
|
||||
|
||||
// Select wand action and supplier based on options
|
||||
wandSupplier = options.random.get() ?
|
||||
new SupplierRandom(player, options) : new SupplierInventory(player, options);
|
||||
wandAction = options.cores.get().getWandAction();
|
||||
|
||||
wandSupplier.getSupply(getTargetItem(world, rayTraceResult));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BlockItem getTargetItem(Level world, BlockHitResult rayTraceResult) {
|
||||
// Get target item
|
||||
Item tgitem = world.getBlockState(rayTraceResult.getBlockPos()).getBlock().asItem();
|
||||
if(!(tgitem instanceof BlockItem)) return null;
|
||||
return (BlockItem) tgitem;
|
||||
}
|
||||
|
||||
public void getSnapshots() {
|
||||
int limit;
|
||||
// Infinity wand gets enhanced limit in creative mode
|
||||
if(player.isCreative() && wandItem == ModItems.WAND_INFINITY.get()) limit = ConfigServer.LIMIT_CREATIVE.get();
|
||||
else limit = Math.min(wandItem.remainingDurability(wand), wandAction.getLimit(wand));
|
||||
|
||||
if(rayTraceResult.getType() == HitResult.Type.BLOCK)
|
||||
placeSnapshots = wandAction.getSnapshots(world, player, rayTraceResult, wand, options, wandSupplier, limit);
|
||||
else
|
||||
placeSnapshots = wandAction.getSnapshotsFromAir(world, player, rayTraceResult, wand, options, wandSupplier, limit);
|
||||
}
|
||||
|
||||
public Set<BlockPos> getBlockPositions() {
|
||||
return placeSnapshots.stream().map(ISnapshot::getPos).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public int blockCount() {
|
||||
return placeSnapshots.size();
|
||||
}
|
||||
|
||||
public boolean doIt() {
|
||||
ArrayList<ISnapshot> executed = new ArrayList<>();
|
||||
|
||||
for(ISnapshot snapshot : placeSnapshots) {
|
||||
if(wand.isEmpty() || wandItem.remainingDurability(wand) == 0) break;
|
||||
|
||||
if(snapshot.execute(world, player, rayTraceResult)) {
|
||||
if(player.isCreative()) executed.add(snapshot);
|
||||
else {
|
||||
// If the item cant be taken, undo the placement
|
||||
if(wandSupplier.takeItemStack(snapshot.getRequiredItems()) == 0) {
|
||||
executed.add(snapshot);
|
||||
wand.hurtAndBreak(1, player, e -> e.broadcastBreakEvent(InteractionHand.MAIN_HAND));
|
||||
}
|
||||
else {
|
||||
ConstructionWand.LOGGER.info("Item could not be taken. Remove block: " +
|
||||
snapshot.getBlockState().getBlock().toString());
|
||||
snapshot.forceRestore(world);
|
||||
}
|
||||
}
|
||||
player.awardStat(ModStats.USE_WAND);
|
||||
}
|
||||
}
|
||||
placeSnapshots = executed;
|
||||
|
||||
// Play place sound
|
||||
if(!placeSnapshots.isEmpty()) {
|
||||
SoundType sound = placeSnapshots.get(0).getBlockState().getSoundType();
|
||||
world.playSound(null, WandUtil.playerPos(player), sound.getPlaceSound(), SoundSource.BLOCKS, sound.volume, sound.pitch);
|
||||
|
||||
// Add to job history for undo
|
||||
ConstructionWand.instance.undoHistory.add(player, world, placeSnapshots);
|
||||
}
|
||||
|
||||
return !placeSnapshots.isEmpty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package thetadev.constructionwand.wand.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.api.IWandSupplier;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.WandUtil;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.wand.undo.ISnapshot;
|
||||
import thetadev.constructionwand.wand.undo.PlaceSnapshot;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ActionAngel implements IWandAction
|
||||
{
|
||||
@Override
|
||||
public int getLimit(ItemStack wand) {
|
||||
return ConfigServer.getWandProperties(wand.getItem()).getAngel();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ISnapshot> getSnapshots(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
|
||||
LinkedList<ISnapshot> placeSnapshots = new LinkedList<>();
|
||||
|
||||
Direction placeDirection = rayTraceResult.getDirection();
|
||||
BlockPos currentPos = rayTraceResult.getBlockPos();
|
||||
BlockState supportingBlock = world.getBlockState(currentPos);
|
||||
|
||||
for(int i = 0; i < limit; i++) {
|
||||
currentPos = currentPos.offset(placeDirection.getOpposite().getNormal());
|
||||
|
||||
PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentPos, rayTraceResult, supportingBlock);
|
||||
if(snapshot != null) {
|
||||
placeSnapshots.add(snapshot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return placeSnapshots;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ISnapshot> getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
|
||||
LinkedList<ISnapshot> placeSnapshots = new LinkedList<>();
|
||||
|
||||
if(!player.isCreative() && !ConfigServer.ANGEL_FALLING.get() && player.fallDistance > 10) return placeSnapshots;
|
||||
|
||||
Vec3 playerVec = WandUtil.entityPositionVec(player);
|
||||
Vec3 lookVec = player.getLookAngle().multiply(2, 2, 2);
|
||||
Vec3 placeVec = playerVec.add(lookVec);
|
||||
|
||||
BlockPos currentPos = new BlockPos(placeVec);
|
||||
|
||||
PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentPos, rayTraceResult, null);
|
||||
if(snapshot != null) placeSnapshots.add(snapshot);
|
||||
|
||||
return placeSnapshots;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package thetadev.constructionwand.wand.action;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import thetadev.constructionwand.api.IWandAction;
|
||||
import thetadev.constructionwand.api.IWandSupplier;
|
||||
import thetadev.constructionwand.basics.ConfigServer;
|
||||
import thetadev.constructionwand.basics.option.WandOptions;
|
||||
import thetadev.constructionwand.wand.undo.ISnapshot;
|
||||
import thetadev.constructionwand.wand.undo.PlaceSnapshot;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default WandAction. Extends your building on the side you're facing.
|
||||
*/
|
||||
public class ActionConstruction implements IWandAction
|
||||
{
|
||||
@Override
|
||||
public int getLimit(ItemStack wand) {
|
||||
return ConfigServer.getWandProperties(wand.getItem()).getLimit();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ISnapshot> getSnapshots(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
|
||||
LinkedList<ISnapshot> placeSnapshots = new LinkedList<>();
|
||||
LinkedList<BlockPos> candidates = new LinkedList<>();
|
||||
HashSet<BlockPos> allCandidates = new HashSet<>();
|
||||
|
||||
Direction placeDirection = rayTraceResult.getDirection();
|
||||
BlockState targetBlock = world.getBlockState(rayTraceResult.getBlockPos());
|
||||
BlockPos startingPoint = rayTraceResult.getBlockPos().offset(placeDirection.getNormal());
|
||||
|
||||
// Is place direction allowed by lock?
|
||||
if(placeDirection == Direction.UP || placeDirection == Direction.DOWN) {
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) || options.testLock(WandOptions.LOCK.EASTWEST))
|
||||
candidates.add(startingPoint);
|
||||
}
|
||||
else if(options.testLock(WandOptions.LOCK.HORIZONTAL) || options.testLock(WandOptions.LOCK.VERTICAL))
|
||||
candidates.add(startingPoint);
|
||||
|
||||
while(!candidates.isEmpty() && placeSnapshots.size() < limit) {
|
||||
BlockPos currentCandidate = candidates.removeFirst();
|
||||
try {
|
||||
BlockPos supportingPoint = currentCandidate.offset(placeDirection.getOpposite().getNormal());
|
||||
BlockState candidateSupportingBlock = world.getBlockState(supportingPoint);
|
||||
|
||||
if(options.matchBlocks(targetBlock.getBlock(), candidateSupportingBlock.getBlock()) &&
|
||||
allCandidates.add(currentCandidate)) {
|
||||
PlaceSnapshot snapshot = supplier.getPlaceSnapshot(world, currentCandidate, rayTraceResult, candidateSupportingBlock);
|
||||
if(snapshot == null) continue;
|
||||
placeSnapshots.add(snapshot);
|
||||
|
||||
switch(placeDirection) {
|
||||
case DOWN:
|
||||
case UP:
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.EASTWEST)) {
|
||||
candidates.add(currentCandidate.offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.WEST.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.NORTHSOUTH) && options.testLock(WandOptions.LOCK.EASTWEST)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()).offset(Direction.WEST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()).offset(Direction.WEST.getNormal()));
|
||||
}
|
||||
break;
|
||||
case NORTH:
|
||||
case SOUTH:
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.WEST.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.WEST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.EAST.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.WEST.getNormal()));
|
||||
}
|
||||
break;
|
||||
case EAST:
|
||||
case WEST:
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.NORTH.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.SOUTH.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()));
|
||||
}
|
||||
if(options.testLock(WandOptions.LOCK.HORIZONTAL) && options.testLock(WandOptions.LOCK.VERTICAL)) {
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.NORTH.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.UP.getNormal()).offset(Direction.SOUTH.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.NORTH.getNormal()));
|
||||
candidates.add(currentCandidate.offset(Direction.DOWN.getNormal()).offset(Direction.SOUTH.getNormal()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
// Can't do anything, could be anything.
|
||||
// Skip if anything goes wrong.
|
||||
}
|
||||
}
|
||||
return placeSnapshots;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ISnapshot> getSnapshotsFromAir(Level world, Player player, BlockHitResult rayTraceResult,
|
||||
ItemStack wand, WandOptions options, IWandSupplier supplier, int limit) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|