Initial 1.17 port.

This commit is contained in:
stfwi 2021-08-08 09:10:14 +02:00
parent 011edac67f
commit 3621b15989
242 changed files with 21371 additions and 22684 deletions

View file

@ -1,20 +1,18 @@
// @file build.gradle // @file build.gradle
// Engineer's decor mod gradle build. // Engineer's decor mod gradle build.
import net.minecraftforge.gradle.common.task.SignJar
buildscript { buildscript {
repositories { repositories {
maven { url = 'https://files.minecraftforge.net/maven' } maven { url = 'https://maven.minecraftforge.net' }
jcenter() mavenCentral()
mavenCentral()
} }
dependencies { 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' apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'eclipse' apply plugin: 'eclipse'
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' java.toolchain.languageVersion = JavaLanguageVersion.of(16)
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
version = "${version_engineersdecor}" version = "${version_engineersdecor}"
group = "wile.engineersdecor" group = "wile.engineersdecor"
@ -23,51 +21,40 @@ archivesBaseName = "engineersdecor-${version_minecraft}"
repositories { repositories {
maven { name = "Progwml6 maven"; url = "https://dvs1.progwml6.com/files/maven/" } // JEI files maven { name = "Progwml6 maven"; url = "https://dvs1.progwml6.com/files/maven/" } // JEI files
maven { name = "ModMaven"; url = "https://modmaven.k-4u.nl" } // JEI files, fallback maven { name = "ModMaven"; url = "https://modmaven.k-4u.nl" } // JEI files, fallback
flatDir { dir 'libs' } // Mod jar dependencies in ./libs
} }
minecraft { minecraft {
// mappings channel: 'snapshot', version: "${version_fml_mappings}" // mappings channel: 'snapshot', version: "${version_fml_mappings}"
mappings channel: "official", version: "1.16.5" mappings channel: "official", version: "1.17.1"
// accessTransformer = file('build/resources/main/META-INF/accesstransformer.cfg') // accessTransformer = file('build/resources/main/META-INF/accesstransformer.cfg')
runs { runs {
client { client {
workingDirectory project.file('run') workingDirectory project.file('run')
property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP
property 'forge.logging.console.level', 'debug' property 'forge.logging.console.level', 'info'
mods { mods { engineersdecor { source sourceSets.main } }
engineersdecor {
source sourceSets.main
}
}
} }
server { server {
workingDirectory project.file('run') workingDirectory project.file('run')
property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP
property 'forge.logging.console.level', 'debug' property 'forge.logging.console.level', 'info'
mods { mods { engineersdecor { source sourceSets.main } }
engineersdecor {
source sourceSets.main
}
}
}
data {
workingDirectory project.file('run')
property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP
property 'forge.logging.console.level', 'debug'
args '--mod', 'engineersdecor', '--all', '--output', file('src/generated/resources/')
mods {
engineersdecor {
source sourceSets.main
}
}
} }
// data {
// workingDirectory project.file('run')
// property 'forge.logging.markers', '' // SCAN,REGISTRIES,REGISTRYDUMP
// property 'forge.logging.console.level', 'info'
// args '--mod', 'engineersdecor', '--all', '--output', file('src/generated/resources/')
// mods { engineersdecor { source sourceSets.main } }
// }
} }
} }
dependencies { dependencies {
minecraft "net.minecraftforge:forge:${version_forge_minecraft}" minecraft "net.minecraftforge:forge:${version_forge_minecraft}"
compileOnly fg.deobf("mezz.jei:jei-${version_jei}:api") //compileOnly fg.deobf("mezz.jei:jei-${version_jei}:api")
runtimeOnly fg.deobf("mezz.jei:jei-${version_jei}") //runtimeOnly fg.deobf("mezz.jei:jei-${version_jei}")
} }
processResources { processResources {
@ -89,25 +76,29 @@ jar {
} }
} }
def reobfFile = file("$buildDir/reobfJar/output.jar") sourceSets.main.resources { srcDir 'src/generated/resources' } // Include datagen resources
def reobfArtifact = artifacts.add('default', reobfFile) { type 'jar'; builtBy 'reobfJar' } jar.finalizedBy('reobfJar')
task signJar(type: SignJar, dependsOn: jar) { // import net.minecraftforge.gradle.common.task.SignJar
onlyIf { project.hasProperty("keystore_file") } // def reobfFile = file("$buildDir/reobfJar/output.jar")
if(project.hasProperty("keystore_file")) { // def reobfArtifact = artifacts.add('default', reobfFile) { type 'jar'; builtBy 'reobfJar' }
keyStore = project.getProperty("keystore_file")
alias = project.getProperty("keystore_alias") // task signJar(type: SignJar, dependsOn: jar) {
storePass = project.getProperty("keystore_pass") // onlyIf { project.hasProperty("keystore_file") }
keyPass = project.getProperty("keystore_keypass") // if(project.hasProperty("keystore_file")) {
inputFile = jar.archivePath // keyStore = project.getProperty("keystore_file")
outputFile = jar.archivePath // alias = project.getProperty("keystore_alias")
} else { // storePass = project.getProperty("keystore_pass")
logger.warn("[WARNING] Signing skipped.") // keyPass = project.getProperty("keystore_keypass")
} // inputFile = jar.archivePath
} // outputFile = jar.archivePath
build.dependsOn signJar // } else {
// logger.warn("[WARNING] Signing skipped.")
// }
// }
// build.dependsOn signJar
publishing { publishing {
publications { mavenJava(MavenPublication) { artifact reobfArtifact } } publications { mavenJava(MavenPublication) { artifact jar } }
repositories { maven { url "file:///${project.projectDir}/mcmodsrepo" } } repositories { maven { url "file:///${project.projectDir}/mcmodsrepo" } }
} }

View file

@ -1,8 +1,7 @@
# @file gradle.properties # @file gradle.properties
org.gradle.daemon=false org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G org.gradle.jvmargs=-Xmx8G
version_minecraft=1.16.4 version_minecraft=1.17.1
version_forge_minecraft=1.16.4-35.1.10 version_forge_minecraft=1.17.1-37.0.17
version_fml_mappings=20201028-1.16.3
version_jei=1.16.4:7.6.1.63 version_jei=1.16.4:7.6.1.63
version_engineersdecor=1.1.15-b1 version_engineersdecor=1.1.17-b1

Binary file not shown.

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-7.2-20210702220150+0000-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip

55
gradlew vendored
View file

@ -1,5 +1,21 @@
#!/usr/bin/env sh #!/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 ## Gradle start up script for UN*X
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` 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. # 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. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@ -56,7 +72,7 @@ case "`uname`" in
Darwin* ) Darwin* )
darwin=true darwin=true
;; ;;
MINGW* ) MSYS* | MINGW* )
msys=true msys=true
;; ;;
NONSTOP* ) NONSTOP* )
@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath
@ -138,19 +156,19 @@ if $cygwin ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # 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" 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" "$@" exec "$JAVACMD" "$@"

43
gradlew.bat vendored
View file

@ -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 @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% 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. @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 @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 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_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail 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 :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @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 :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View file

@ -1,6 +1,6 @@
{ {
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/", "homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.16.4": { "1.17.1": {
"1.1.14": "[R] Release build v1.1.14.", "1.1.14": "[R] Release build v1.1.14.",
"1.1.14-b3": "[A] Spanish language support added (PR#180, thx FrannDzs).", "1.1.14-b3": "[A] Spanish language support added (PR#180, thx FrannDzs).",
"1.1.14-b2": "[F] Block Placer: Attempt circumventing external placement prevention.", "1.1.14-b2": "[F] Block Placer: Attempt circumventing external placement prevention.",
@ -39,7 +39,7 @@
"1.1.2-b1": "[U] Ported to MC1.16.2." "1.1.2-b1": "[U] Ported to MC1.16.2."
}, },
"promos": { "promos": {
"1.16.4-recommended": "1.1.14", "1.17.1-recommended": "1.1.14",
"1.16.4-latest": "1.1.14" "1.17.1-latest": "1.1.14"
} }
} }

View file

@ -11,7 +11,11 @@ Mod sources for Minecraft version 1.16.x.
## Version history ## Version history
~ v1.1.15-b1 [F] ~ v1.1.17-b1 [U] Initial MC 1.17.1 port.
[M] Clinker Brick textures adapted to contemporary MC textures.
[D] Gas Concrete dropped (Rebar Concrete suffices for texture variation).
[D] Treated Wood Side Table dropped.
[M] Updated zh_cn (PR#183, Lyaiya).
- v1.1.14 [R] Release build v1.1.14. - v1.1.14 [R] Release build v1.1.14.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,136 +1,131 @@
package wile.engineersdecor; package wile.engineersdecor;
import wile.engineersdecor.blocks.*; import net.minecraft.world.entity.EntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.entity.player.Player;
import wile.engineersdecor.libmc.detail.OptionalRecipeCondition; import net.minecraft.world.inventory.MenuType;
import net.minecraft.entity.EntityType; import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.item.Item;
import net.minecraft.inventory.container.ContainerType; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.ItemGroup; import net.minecraft.world.level.block.Block;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.block.Block; import net.minecraftforge.api.distmarker.Dist;
import net.minecraft.item.Item; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraft.item.ItemStack; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.*; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.api.distmarker.Dist; import org.apache.logging.log4j.LogManager;
import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import wile.engineersdecor.blocks.EdLadderBlock;
import org.apache.logging.log4j.Logger; import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.OptionalRecipeCondition;
@Mod("engineersdecor")
public class ModEngineersDecor @Mod("engineersdecor")
{ public class ModEngineersDecor
public static final String MODID = "engineersdecor"; {
public static final String MODNAME = "Engineer's Decor"; public static final String MODID = "engineersdecor";
public static final int VERSION_DATAFIXER = 0; public static final String MODNAME = "Engineer's Decor";
private static final Logger LOGGER = LogManager.getLogger(); public static final int VERSION_DATAFIXER = 0;
private static final Logger LOGGER = LogManager.getLogger();
public ModEngineersDecor()
{ public ModEngineersDecor()
Auxiliaries.init(MODID, LOGGER, ModConfig::getServerConfig); {
Auxiliaries.logGitVersion(MODNAME); Auxiliaries.init(MODID, LOGGER, ModConfig::getServerConfig);
OptionalRecipeCondition.init(MODID, LOGGER); Auxiliaries.logGitVersion(MODNAME);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup); OptionalRecipeCondition.init(MODID, LOGGER);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.SERVER, ModConfig.SERVER_CONFIG_SPEC);
FMLJavaModLoadingContext.get().getModEventBus().addListener(ForgeEvents::onConfigLoad); ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.COMMON, ModConfig.COMMON_CONFIG_SPEC);
FMLJavaModLoadingContext.get().getModEventBus().addListener(ForgeEvents::onConfigReload); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup);
ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.SERVER, ModConfig.SERVER_CONFIG_SPEC); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup);
ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.COMMON, ModConfig.COMMON_CONFIG_SPEC); // FMLJavaModLoadingContext.get().getModEventBus().addListener(ForgeEvents::onConfigLoad);
ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.CLIENT, ModConfig.CLIENT_CONFIG_SPEC); // FMLJavaModLoadingContext.get().getModEventBus().addListener(ForgeEvents::onConfigReload);
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
} }
public static final Logger logger() { return LOGGER; } public static Logger logger() { return LOGGER; }
// //
// Events // Events
// //
private void onSetup(final FMLCommonSetupEvent event) private void onSetup(final FMLCommonSetupEvent event)
{ {
LOGGER.info("Registering recipe condition processor ..."); LOGGER.info("Registering recipe condition processor ...");
CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE); CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE);
wile.engineersdecor.libmc.detail.Networking.init(MODID); wile.engineersdecor.libmc.detail.Networking.init(MODID);
} }
private void onClientSetup(final FMLClientSetupEvent event) private void onClientSetup(final FMLClientSetupEvent event)
{ {
ModContent.registerContainerGuis(event); ModContent.registerContainerGuis(event);
ModContent.registerTileEntityRenderers(event); ModContent.registerTileEntityRenderers(event);
ModContent.processContentClientSide(event); ModContent.processContentClientSide(event);
wile.engineersdecor.libmc.detail.Overlay.register(); wile.engineersdecor.libmc.detail.Overlay.register();
} }
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class ForgeEvents public static class ForgeEvents
{ {
@SubscribeEvent @SubscribeEvent
public static void onBlocksRegistry(final RegistryEvent.Register<Block> event) public static void onBlocksRegistry(final RegistryEvent.Register<Block> event)
{ ModContent.registerBlocks(event); } { ModContent.registerBlocks(event); }
@SubscribeEvent @SubscribeEvent
public static void onItemRegistry(final RegistryEvent.Register<Item> event) public static void onItemRegistry(final RegistryEvent.Register<Item> event)
{ ModContent.registerItems(event); ModContent.registerBlockItems(event); } { ModContent.registerItems(event); ModContent.registerBlockItems(event); }
@SubscribeEvent @SubscribeEvent
public static void onTileEntityRegistry(final RegistryEvent.Register<TileEntityType<?>> event) public static void onTileEntityRegistry(final RegistryEvent.Register<BlockEntityType<?>> event)
{ ModContent.registerTileEntities(event); } { ModContent.registerTileEntities(event); }
@SubscribeEvent @SubscribeEvent
public static void onRegisterEntityTypes(final RegistryEvent.Register<EntityType<?>> event) public static void onRegisterEntityTypes(final RegistryEvent.Register<EntityType<?>> event)
{ ModContent.registerEntities(event); } { ModContent.registerEntities(event); }
@SubscribeEvent @SubscribeEvent
public static void onRegisterContainerTypes(final RegistryEvent.Register<ContainerType<?>> event) public static void onRegisterContainerTypes(final RegistryEvent.Register<MenuType<?>> event)
{ ModContent.registerContainers(event); } { ModContent.registerContainers(event); }
public static void onConfigLoad(net.minecraftforge.fml.config.ModConfig.Loading configEvent) /*
{ ModConfig.apply(); } public static void onConfigLoad(net.minecraftforge.fml.config.ModConfig.Loading configEvent)
{ ModConfig.apply(); }
public static void onConfigReload(net.minecraftforge.fml.config.ModConfig.Reloading configEvent)
{ public static void onConfigReload(net.minecraftforge.fml.config.ModConfig.Reloading configEvent)
try { {
ModEngineersDecor.logger().info("Config file changed {}", configEvent.getConfig().getFileName()); try {
ModConfig.apply(); ModEngineersDecor.logger().info("Config file changed {}", configEvent.getConfig().getFileName());
} catch(Throwable e) { ModConfig.apply();
ModEngineersDecor.logger().error("Failed to load changed config: " + e.getMessage()); } catch(Throwable e) {
} ModEngineersDecor.logger().error("Failed to load changed config: " + e.getMessage());
} }
}
@SubscribeEvent */
public static void onDataGeneration(GatherDataEvent event) }
{
event.getGenerator().addProvider(new wile.engineersdecor.libmc.datagen.LootTableGen(event.getGenerator(), ModContent::allBlocks)); //
} // Item group / creative tab
} //
public static final CreativeModeTab ITEMGROUP = (new CreativeModeTab("tab" + MODID) {
// @OnlyIn(Dist.CLIENT)
// Item group / creative tab public ItemStack makeIcon()
// { return new ItemStack(ModContent.SIGN_MODLOGO); }
public static final ItemGroup ITEMGROUP = (new ItemGroup("tab" + MODID) { });
@OnlyIn(Dist.CLIENT)
public ItemStack makeIcon() //
{ return new ItemStack(ModContent.SIGN_MODLOGO); } // Player update event
}); //
@SubscribeEvent
// public void onPlayerEvent(final LivingEvent.LivingUpdateEvent event)
// Player update event {
// if((event.getEntity().level == null) || (!(event.getEntity() instanceof final Player player))) return;
@SubscribeEvent if(player.onClimbable()) EdLadderBlock.onPlayerUpdateEvent(player);
public void onPlayerEvent(final LivingEvent.LivingUpdateEvent event) }
{
if((event.getEntity().level == null) || (!(event.getEntity() instanceof PlayerEntity))) return; }
final PlayerEntity player = (PlayerEntity)event.getEntity();
if(player.onClimbable()) EdLadderBlock.onPlayerUpdateEvent(player);
}
}

View file

@ -1,135 +1,34 @@
/* /*
* @file DecorBlock.java * @file DecorBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Common functionality class for decor blocks. * Common functionality class for decor blocks.
* Mainly needed for: * Mainly needed for:
* - MC block defaults. * - MC block defaults.
* - Tooltip functionality * - Tooltip functionality
* - Model initialisation * - Model initialisation
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks.IStandardBlock; public class DecorBlock
import wile.engineersdecor.libmc.detail.Auxiliaries; {
import net.minecraft.block.IWaterLoggable; public static final long CFG_DEFAULT = StandardBlocks.CFG_DEFAULT;
import net.minecraft.util.math.AxisAlignedBB; public static final long CFG_CUTOUT = StandardBlocks.CFG_CUTOUT;
import net.minecraft.util.math.shapes.VoxelShape; public static final long CFG_MIPPED = StandardBlocks.CFG_MIPPED;
import java.util.ArrayList; public static final long CFG_TRANSLUCENT = StandardBlocks.CFG_TRANSLUCENT;
import java.util.function.Supplier; public static final long CFG_WATERLOGGABLE = StandardBlocks.CFG_WATERLOGGABLE;
public static final long CFG_HORIZIONTAL = StandardBlocks.CFG_HORIZIONTAL;
public static final long CFG_LOOK_PLACEMENT = StandardBlocks.CFG_LOOK_PLACEMENT;
public static final long CFG_FACING_PLACEMENT = StandardBlocks.CFG_FACING_PLACEMENT;
public class DecorBlock public static final long CFG_OPPOSITE_PLACEMENT = StandardBlocks.CFG_OPPOSITE_PLACEMENT;
{ public static final long CFG_FLIP_PLACEMENT_IF_SAME = StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME;
public static final long CFG_DEFAULT = StandardBlocks.CFG_DEFAULT; public static final long CFG_FLIP_PLACEMENT_SHIFTCLICK = StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK;
public static final long CFG_CUTOUT = StandardBlocks.CFG_CUTOUT; public static final long CFG_STRICT_CONNECTIONS = StandardBlocks.CFG_STRICT_CONNECTIONS;
public static final long CFG_MIPPED = StandardBlocks.CFG_MIPPED; public static final long CFG_AI_PASSABLE = StandardBlocks.CFG_AI_PASSABLE;
public static final long CFG_TRANSLUCENT = StandardBlocks.CFG_TRANSLUCENT; public static final long CFG_HARD_IE_DEPENDENT = 0x8000000000000000L;
public static final long CFG_WATERLOGGABLE = StandardBlocks.CFG_WATERLOGGABLE; @Deprecated public static final long CFG_EXPERIMENTAL = 0x4000000000000000L;
public static final long CFG_HORIZIONTAL = StandardBlocks.CFG_HORIZIONTAL; }
public static final long CFG_LOOK_PLACEMENT = StandardBlocks.CFG_LOOK_PLACEMENT;
public static final long CFG_FACING_PLACEMENT = StandardBlocks.CFG_FACING_PLACEMENT;
public static final long CFG_OPPOSITE_PLACEMENT = StandardBlocks.CFG_OPPOSITE_PLACEMENT;
public static final long CFG_FLIP_PLACEMENT_IF_SAME = StandardBlocks.CFG_FLIP_PLACEMENT_IF_SAME;
public static final long CFG_FLIP_PLACEMENT_SHIFTCLICK = StandardBlocks.CFG_FLIP_PLACEMENT_SHIFTCLICK;
public static final long CFG_STRICT_CONNECTIONS = StandardBlocks.CFG_STRICT_CONNECTIONS;
public static final long CFG_AI_PASSABLE = StandardBlocks.CFG_AI_PASSABLE;
public static final long CFG_HARD_IE_DEPENDENT = 0x8000000000000000L;
@Deprecated public static final long CFG_EXPERIMENTAL = 0x4000000000000000L;
public static class Normal extends StandardBlocks.BaseBlock implements IDecorBlock
{
public Normal(long conf, AbstractBlock.Properties properties)
{ super(conf, properties); }
}
public static class Cutout extends StandardBlocks.Cutout implements IDecorBlock
{
public Cutout(long conf, AbstractBlock.Properties properties)
{ super(conf, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16,16 )); }
public Cutout(long conf, AbstractBlock.Properties properties, AxisAlignedBB aabb)
{ super(conf, properties, aabb);}
public Cutout(long conf, AbstractBlock.Properties properties, VoxelShape voxel_shape)
{ super(conf, properties, voxel_shape); }
public Cutout(long conf, AbstractBlock.Properties properties, AxisAlignedBB[] aabbs)
{ super(conf, properties, aabbs); }
}
public static class WaterLoggable extends StandardBlocks.WaterLoggable implements IStandardBlock, IWaterLoggable
{
public WaterLoggable(long config, AbstractBlock.Properties properties)
{ super(config, properties); }
public WaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public WaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public WaterLoggable(long config, AbstractBlock.Properties properties, VoxelShape voxel_shape)
{ super(config, properties, voxel_shape); }
}
public static class Directed extends StandardBlocks.Directed implements IDecorBlock
{
public Directed(long config, AbstractBlock.Properties properties, final AxisAlignedBB unrotatedAABB)
{ super(config, properties, unrotatedAABB); }
public Directed(long config, AbstractBlock.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, properties, unrotatedAABBs); }
public Directed(long config, AbstractBlock.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config, properties, shape_supplier); }
}
public static class DirectedWaterLoggable extends StandardBlocks.DirectedWaterLoggable implements IDecorBlock,IWaterLoggable
{
public DirectedWaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public DirectedWaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public DirectedWaterLoggable(long config, AbstractBlock.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config, properties, shape_supplier); }
}
public static class Horizontal extends StandardBlocks.Horizontal implements IDecorBlock
{
public Horizontal(long config, AbstractBlock.Properties properties, final AxisAlignedBB unrotatedAABB)
{ super(config, properties, unrotatedAABB); }
public Horizontal(long config, AbstractBlock.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, properties, unrotatedAABBs); }
public Horizontal(long config, AbstractBlock.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config, properties, shape_supplier); }
}
public static class HorizontalWaterLoggable extends StandardBlocks.HorizontalWaterLoggable implements IWaterLoggable
{
public HorizontalWaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public HorizontalWaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public HorizontalWaterLoggable(long config, AbstractBlock.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config, properties, shape_supplier); }
}
public static class HorizontalFourWayWaterLoggable extends StandardBlocks.HorizontalFourWayWaterLoggable implements IWaterLoggable
{
public HorizontalFourWayWaterLoggable(long config, AbstractBlock.Properties properties, AxisAlignedBB base_aabb, AxisAlignedBB side_aabb, int railing_height_extension)
{ super(config, properties, base_aabb, side_aabb, railing_height_extension); }
}
}

View file

@ -1,356 +1,349 @@
/* /*
* @file EdBreaker.java * @file EdBreaker.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Small Block Breaker * Small Block Breaker
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import wile.engineersdecor.ModConfig; import net.minecraft.core.BlockPos;
import wile.engineersdecor.ModContent; import net.minecraft.core.Direction;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.core.particles.ParticleTypes;
import wile.engineersdecor.libmc.detail.Inventories; import net.minecraft.nbt.CompoundTag;
import wile.engineersdecor.libmc.detail.Overlay; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.World; import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.IBlockReader; import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.GameRules; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.server.ServerWorld; import net.minecraft.util.Mth;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.InteractionHand;
import net.minecraft.state.StateContainer; import net.minecraft.world.InteractionResult;
import net.minecraft.block.AbstractBlock; import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.block.Blocks; import net.minecraft.world.entity.player.Player;
import net.minecraft.block.SoundType; import net.minecraft.world.item.ItemStack;
import net.minecraft.block.Block; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.block.BlockState; import net.minecraft.world.level.BlockGetter;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.level.GameRules;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.Level;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.block.Blocks;
import net.minecraft.entity.item.ItemEntity; import net.minecraft.world.level.block.SoundType;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.particles.ParticleTypes; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.*; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.phys.AABB;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.util.math.MathHelper; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.energy.IEnergyStorage; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.libmc.detail.RfEnergy; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import java.util.HashSet; import wile.engineersdecor.libmc.detail.Auxiliaries;
import java.util.List; import wile.engineersdecor.libmc.detail.Inventories;
import java.util.Random; import wile.engineersdecor.libmc.detail.Overlay;
import wile.engineersdecor.libmc.detail.RfEnergy;
import javax.annotation.Nullable;
public class EdBreaker import java.util.HashSet;
{ import java.util.List;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required) import java.util.Random;
{ BreakerTileEntity.on_config(boost_energy_per_tick, breaking_time_per_hardness, min_breaking_time_ticks, power_required); }
//-------------------------------------------------------------------------------------------------------------------- public class EdBreaker
// Block {
//-------------------------------------------------------------------------------------------------------------------- public static final int BOOST_FACTOR = 8;
public static final int DEFAULT_BOOST_ENERGY = 64;
public static class BreakerBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock public static final int DEFAULT_BREAKING_RELUCTANCE = 17;
{ public static final int DEFAULT_MIN_BREAKING_TIME = 15;
public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); public static final int MAX_BREAKING_TIME = 800;
public BreakerBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABBs) private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
{ super(config, builder, unrotatedAABBs); } private static int energy_max = 32000;
private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE;
@Override private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME;
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) private static boolean requires_power = false;
{ super.createBlockStateDefinition(builder); builder.add(ACTIVE); }
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required)
@Override {
@Nullable final int interval = BreakerTileEntity.TICK_INTERVAL;
public BlockState getStateForPlacement(BlockItemUseContext context) boost_energy_consumption = interval * Mth.clamp(boost_energy_per_tick, 4, 4096);
{ return super.getStateForPlacement(context).setValue(ACTIVE, false); } energy_max = Math.max(boost_energy_consumption * 10, 100000);
breaking_reluctance = Mth.clamp(breaking_time_per_hardness, 5, 50);
@Override min_breaking_time = Mth.clamp(min_breaking_time_ticks, 10, 100);
public boolean hasTileEntity(BlockState state) requires_power = power_required;
{ return true; } ModConfig.log("Config block breaker: Boost energy consumption:" + (boost_energy_consumption/interval) + "rf/t, reluctance=" + breaking_reluctance + "t/hrdn, break time offset=" + min_breaking_time + "t.");
}
@Override
@Nullable //--------------------------------------------------------------------------------------------------------------------
public TileEntity createTileEntity(BlockState state, IBlockReader world) // Block
{ return new BreakerTileEntity(); } //--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT) public static class BreakerBlock extends StandardBlocks.HorizontalWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock<BreakerTileEntity>
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd) {
{ public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return;
final double rv = rnd.nextDouble(); public BreakerBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs)
if(rv > 0.8) return; { super(config, builder, unrotatedAABBs); }
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2); @Nullable
switch(state.getValue(HORIZONTAL_FACING)) { @Override
case WEST: world.addParticle(ParticleTypes.SMOKE, x-xc, yr, z+xr, 0.0, 0.0, 0.0); break; public BlockEntityType<BreakerTileEntity> getBlockEntityType()
case EAST: world.addParticle(ParticleTypes.SMOKE, x+xc, yr, z+xr, 0.0, 0.0, 0.0); break; { return ModContent.TET_SMALL_BLOCK_BREAKER; }
case NORTH: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z-xc, 0.0, 0.0, 0.0); break;
default: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z+xc, 0.0, 0.0, 0.0); break; @Override
} protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
} { super.createBlockStateDefinition(builder); builder.add(ACTIVE); }
@Override @Override
@SuppressWarnings("deprecation") @Nullable
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) public BlockState getStateForPlacement(BlockPlaceContext context)
{ { return super.getStateForPlacement(context).setValue(ACTIVE, false); }
if(!(world instanceof World) || (((World) world).isClientSide)) return;
TileEntity te = world.getBlockEntity(pos); @OnlyIn(Dist.CLIENT)
if(!(te instanceof BreakerTileEntity)) return; @SuppressWarnings("deprecation")
((BreakerTileEntity)te).block_updated(); public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd)
} {
if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return;
@Override // Sound
@SuppressWarnings("deprecation") if(true || (world.getGameTime() & 0x1) == 0) {
public boolean isSignalSource(BlockState state) SoundEvent sound = SoundEvents.WOOD_HIT;
{ return true; } BlockState target_state = world.getBlockState(pos.relative(state.getValue(BreakerBlock.HORIZONTAL_FACING)));
SoundType stype = target_state.getBlock().getSoundType(target_state);
@Override if((stype == SoundType.WOOL) || (stype == SoundType.GRASS) || (stype == SoundType.SNOW)) {
@SuppressWarnings("deprecation") sound = SoundEvents.WOOL_HIT;
public int getSignal(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side) } else if((stype == SoundType.GRAVEL) || (stype == SoundType.SAND)) {
{ return 0; } sound = SoundEvents.GRAVEL_HIT;
}
@Override world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundSource.BLOCKS, 0.1f, 1.2f, false);
@SuppressWarnings("deprecation") }
public int getDirectSignal(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side) // Particles
{ return 0; } {
final double rv = rnd.nextDouble();
@Override if(rv < 0.8) {
@SuppressWarnings("deprecation") final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2);
{ switch (state.getValue(HORIZONTAL_FACING)) {
if(world.isClientSide()) return ActionResultType.SUCCESS; case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0);
TileEntity te = world.getBlockEntity(pos); case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0);
if(te instanceof BreakerTileEntity) ((BreakerTileEntity)te).state_message(player); case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0);
return ActionResultType.CONSUME; default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0);
} }
} }
}
//-------------------------------------------------------------------------------------------------------------------- }
// Tile entity
//-------------------------------------------------------------------------------------------------------------------- @Override
@SuppressWarnings("deprecation")
public static class BreakerTileEntity extends TileEntity implements ITickableTileEntity public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{ {
public static final int IDLE_TICK_INTERVAL = 40; if(!(world instanceof Level) || (world.isClientSide)) return;
public static final int TICK_INTERVAL = 5; BlockEntity te = world.getBlockEntity(pos);
public static final int BOOST_FACTOR = 8; if(!(te instanceof BreakerTileEntity)) return;
public static final int DEFAULT_BOOST_ENERGY = 64; ((BreakerTileEntity)te).block_updated();
public static final int DEFAULT_BREAKING_RELUCTANCE = 17; }
public static final int DEFAULT_MIN_BREAKING_TIME = 15;
public static final int MAX_BREAKING_TIME = 800; @Override
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY; @SuppressWarnings("deprecation")
private static int energy_max = 32000; public boolean isSignalSource(BlockState state)
private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE; { return true; }
private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME;
private static boolean requires_power = false; @Override
private int tick_timer_; @SuppressWarnings("deprecation")
private int active_timer_; public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side)
private int proc_time_elapsed_; { return 0; }
private int time_needed_;
private final RfEnergy.Battery battery_; @Override
private final LazyOptional<IEnergyStorage> energy_handler_; @SuppressWarnings("deprecation")
public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side)
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required) { return 0; }
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096); @Override
energy_max = Math.max(boost_energy_consumption * 10, 100000); @SuppressWarnings("deprecation")
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100); {
requires_power = power_required; if(world.isClientSide()) return InteractionResult.SUCCESS;
ModConfig.log("Config block breaker: Boost energy consumption:" + (boost_energy_consumption/TICK_INTERVAL) + "rf/t, reluctance=" + breaking_reluctance + "t/hrdn, break time offset=" + min_breaking_time + "t."); BlockEntity te = world.getBlockEntity(pos);
} if(te instanceof BreakerTileEntity) ((BreakerTileEntity)te).state_message(player);
return InteractionResult.CONSUME;
public BreakerTileEntity() }
{ this(ModContent.TET_SMALL_BLOCK_BREAKER); } }
public BreakerTileEntity(TileEntityType<?> te_type) //--------------------------------------------------------------------------------------------------------------------
{ // Tile entity
super(te_type); //--------------------------------------------------------------------------------------------------------------------
battery_ = new RfEnergy.Battery(energy_max, boost_energy_consumption, 0);
energy_handler_ = battery_.createEnergyHandler(); public static class BreakerTileEntity extends StandardEntityBlocks.StandardBlockEntity
} {
public static final int IDLE_TICK_INTERVAL = 40;
public void block_updated() public static final int TICK_INTERVAL = 5;
{ if(tick_timer_ > 2) tick_timer_ = 2; } private int tick_timer_;
private int active_timer_;
public void readnbt(CompoundNBT nbt) private int proc_time_elapsed_;
{ battery_.load(nbt); } private int time_needed_;
private final RfEnergy.Battery battery_ = new RfEnergy.Battery(energy_max, boost_energy_consumption, 0);
private void writenbt(CompoundNBT nbt) private final LazyOptional<IEnergyStorage> energy_handler_ = battery_.createEnergyHandler();
{ battery_.save(nbt); }
public BreakerTileEntity(BlockPos pos, BlockState state)
public void state_message(PlayerEntity player) { super(ModContent.TET_SMALL_BLOCK_BREAKER, pos, state); }
{
String progress = "0"; public void block_updated()
if((proc_time_elapsed_ > 0) && (time_needed_ > 0)) { { if(tick_timer_ > 2) tick_timer_ = 2; }
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100));
} public void readnbt(CompoundTag nbt)
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", new Object[]{battery_.getSOC(), energy_max, progress })); { battery_.load(nbt); }
}
private void writenbt(CompoundTag nbt)
// TileEntity ------------------------------------------------------------------------------ { battery_.save(nbt); }
@Override public void state_message(Player player)
public void load(BlockState state, CompoundNBT nbt) {
{ super.load(state, nbt); readnbt(nbt); } String progress = "0";
if((proc_time_elapsed_ > 0) && (time_needed_ > 0)) {
@Override progress = Integer.toString((int)Mth.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100));
public CompoundNBT save(CompoundNBT nbt) }
{ super.save(nbt); writenbt(nbt); return nbt; } Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", battery_.getSOC(), energy_max, progress));
}
@Override
public void setRemoved() // BlockEntity ------------------------------------------------------------------------------
{
super.setRemoved(); @Override
energy_handler_.invalidate(); public void load(CompoundTag nbt)
} { super.load(nbt); readnbt(nbt); }
// Capability export ---------------------------------------------------------------------------- @Override
public CompoundTag save(CompoundTag nbt)
@Override { super.save(nbt); writenbt(nbt); return nbt; }
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{ @Override
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast(); public void setRemoved()
return super.getCapability(capability, facing); {
} super.setRemoved();
energy_handler_.invalidate();
// ITickable ------------------------------------------------------------------------------------ }
private static final HashSet<Block> blacklist = new HashSet<>(); // Capability export ----------------------------------------------------------------------------
static {
blacklist.add(Blocks.AIR); @Override
blacklist.add(Blocks.BEDROCK); public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
blacklist.add(Blocks.FIRE); {
blacklist.add(Blocks.END_PORTAL); if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast();
blacklist.add(Blocks.END_GATEWAY); return super.getCapability(capability, facing);
blacklist.add(Blocks.END_PORTAL_FRAME); }
blacklist.add(Blocks.NETHER_PORTAL);
blacklist.add(Blocks.BARRIER); // ITickable ------------------------------------------------------------------------------------
}
private static final HashSet<Block> blacklist = new HashSet<>();
private static boolean isBreakable(BlockState state, BlockPos pos, World world) static {
{ blacklist.add(Blocks.AIR);
final Block block = state.getBlock(); blacklist.add(Blocks.BEDROCK);
if(blacklist.contains(block)) return false; blacklist.add(Blocks.FIRE);
if(state.getMaterial().isLiquid()) return false; blacklist.add(Blocks.END_PORTAL);
if(block.isAir(state, world, pos)) return false; blacklist.add(Blocks.END_GATEWAY);
float bh = state.getDestroySpeed(world, pos); blacklist.add(Blocks.END_PORTAL_FRAME);
if((bh<0) || (bh>55)) return false; blacklist.add(Blocks.NETHER_PORTAL);
return true; blacklist.add(Blocks.BARRIER);
} }
private static void spawnBlockAsEntity(World world, BlockPos pos, ItemStack stack) { private static boolean isBreakable(BlockState state, BlockPos pos, Level world)
if(world.isClientSide || stack.isEmpty() || (!world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) || world.restoringBlockSnapshots) return; {
ItemEntity e = new ItemEntity(world, final Block block = state.getBlock();
((world.random.nextFloat()*0.1)+0.5) + pos.getX(), if(blacklist.contains(block)) return false;
((world.random.nextFloat()*0.1)+0.5) + pos.getY(), if(state.isAir()) return false;
((world.random.nextFloat()*0.1)+0.5) + pos.getZ(), if(state.getMaterial().isLiquid()) return false;
stack float bh = state.getDestroySpeed(world, pos);
); return !((bh<0) || (bh>55));
e.setDefaultPickUpDelay(); }
e.setDeltaMovement((world.random.nextFloat()*0.1-0.05), (world.random.nextFloat()*0.1-0.03), (world.random.nextFloat()*0.1-0.05));
world.addFreshEntity(e); private static void spawnBlockAsEntity(Level world, BlockPos pos, ItemStack stack) {
} if(world.isClientSide || stack.isEmpty() || (!world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) || world.restoringBlockSnapshots) return;
ItemEntity e = new ItemEntity(world,
private static boolean canInsertInto(World world, BlockPos pos) ((world.random.nextFloat()*0.1)+0.5) + pos.getX(),
{ ((world.random.nextFloat()*0.1)+0.5) + pos.getY(),
// Maybe make a tag for that. The question is if it is actually worth it, or if that would be only ((world.random.nextFloat()*0.1)+0.5) + pos.getZ(),
// tag spamming the game. So for now only FH and VH. stack
final BlockState state = world.getBlockState(pos); );
return (state.getBlock() == ModContent.FACTORY_HOPPER) || (state.getBlock() == Blocks.HOPPER); e.setDefaultPickUpDelay();
} e.setDeltaMovement((world.random.nextFloat()*0.1-0.05), (world.random.nextFloat()*0.1-0.03), (world.random.nextFloat()*0.1-0.05));
world.addFreshEntity(e);
private boolean breakBlock(BlockState state, BlockPos pos, World world) }
{
if(world.isClientSide || (!(world instanceof ServerWorld)) || world.restoringBlockSnapshots) return false; // retry next cycle private static boolean canInsertInto(Level world, BlockPos pos)
List<ItemStack> drops; {
final Block block = state.getBlock(); // Maybe make a tag for that. The question is if it is actually worth it, or if that would be only
final boolean insert = canInsertInto(world, getBlockPos().below()); // tag spamming the game. So for now only FH and VH.
drops = Block.getDrops(state, (ServerWorld)world, pos, world.getBlockEntity(pos)); final BlockState state = world.getBlockState(pos);
world.removeBlock(pos, false); return (state.getBlock() == ModContent.FACTORY_HOPPER) || (state.getBlock() == Blocks.HOPPER);
for(ItemStack drop:drops) { }
if(!insert) {
spawnBlockAsEntity(world, pos, drop); private boolean breakBlock(BlockState state, BlockPos pos, Level world)
} else { {
final ItemStack remaining = Inventories.insert(world, getBlockPos().below(), Direction.UP, drop, false); if(world.isClientSide || (!(world instanceof ServerLevel)) || world.restoringBlockSnapshots) return false; // retry next cycle
if(!remaining.isEmpty()) spawnBlockAsEntity(world, pos, remaining); List<ItemStack> drops;
} final Block block = state.getBlock();
} final boolean insert = canInsertInto(world, getBlockPos().below());
SoundType stype = state.getBlock().getSoundType(state, world, pos, null); drops = Block.getDrops(state, (ServerLevel)world, pos, world.getBlockEntity(pos));
if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch()); world.removeBlock(pos, false);
return true; for(ItemStack drop:drops) {
} if(!insert) {
spawnBlockAsEntity(world, pos, drop);
@Override } else {
@SuppressWarnings("deprecation") final ItemStack remaining = Inventories.insert(world, getBlockPos().below(), Direction.UP, drop, false);
public void tick() if(!remaining.isEmpty()) spawnBlockAsEntity(world, pos, remaining);
{ }
if(--tick_timer_ > 0) return; }
final BlockState device_state = level.getBlockState(worldPosition); SoundType stype = state.getBlock().getSoundType(state, world, pos, null);
if(!(device_state.getBlock() instanceof BreakerBlock)) return; if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundSource.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
if(level.isClientSide) { return true;
if(!device_state.getValue(BreakerBlock.ACTIVE)) { }
tick_timer_ = TICK_INTERVAL;
} else { @Override
tick_timer_ = 1; @SuppressWarnings("deprecation")
// not sure if is so cool to do this each tick ... may be simplified/removed again. public void tick()
SoundEvent sound = SoundEvents.WOOD_HIT; {
BlockState target_state = level.getBlockState(worldPosition.relative(device_state.getValue(BreakerBlock.HORIZONTAL_FACING))); if(--tick_timer_ > 0) return;
SoundType stype = target_state.getBlock().getSoundType(target_state); tick_timer_ = TICK_INTERVAL;
if((stype == SoundType.WOOL) || (stype == SoundType.GRASS) || (stype == SoundType.SNOW)) { final BlockState device_state = level.getBlockState(worldPosition);
sound = SoundEvents.WOOL_HIT; if(!(device_state.getBlock() instanceof BreakerBlock)) return;
} else if((stype == SoundType.GRAVEL) || (stype == SoundType.SAND)) { final BlockPos target_pos = worldPosition.relative(device_state.getValue(BreakerBlock.HORIZONTAL_FACING));
sound = SoundEvents.GRAVEL_HIT; final BlockState target_state = level.getBlockState(target_pos);
} if((level.hasNeighborSignal(worldPosition)) || (!isBreakable(target_state, target_pos, level))) {
level.playLocalSound(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), sound, SoundCategory.BLOCKS, 0.1f, 1.2f, false); if(device_state.getValue(BreakerBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, false), 1|2);
} proc_time_elapsed_ = 0;
} else { tick_timer_ = IDLE_TICK_INTERVAL;
tick_timer_ = TICK_INTERVAL; return;
final BlockPos target_pos = worldPosition.relative(device_state.getValue(BreakerBlock.HORIZONTAL_FACING)); }
final BlockState target_state = level.getBlockState(target_pos); time_needed_ = Mth.clamp((int)(target_state.getDestroySpeed(level, worldPosition) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
if((level.hasNeighborSignal(worldPosition)) || (!isBreakable(target_state, target_pos, level))) { if(battery_.draw(boost_energy_consumption)) {
if(device_state.getValue(BreakerBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, false), 1|2); proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR);
proc_time_elapsed_ = 0; time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5);
tick_timer_ = IDLE_TICK_INTERVAL; active_timer_ = 2;
return; } else if(!requires_power) {
} proc_time_elapsed_ += TICK_INTERVAL;
time_needed_ = MathHelper.clamp((int)(target_state.getDestroySpeed(level, worldPosition) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME); active_timer_ = 1024;
if(battery_.draw(boost_energy_consumption)) { } else if(active_timer_ > 0) {
proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR); --active_timer_;
time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5); }
active_timer_ = 2; boolean active = (active_timer_ > 0);
} else if(!requires_power) { if(requires_power && !active) {
proc_time_elapsed_ += TICK_INTERVAL; proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
active_timer_ = 1024; }
} else if(active_timer_ > 0) { if(proc_time_elapsed_ >= time_needed_) {
--active_timer_; proc_time_elapsed_ = 0;
} breakBlock(target_state, target_pos, level);
boolean active = (active_timer_ > 0); active = false;
if(requires_power && !active) { }
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL); if(device_state.getValue(BreakerBlock.ACTIVE) != active) {
} level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, active), 1|2);
if(proc_time_elapsed_ >= time_needed_) { }
proc_time_elapsed_ = 0; }
breakBlock(target_state, target_pos, level);
active = false; }
} }
if(device_state.getValue(BreakerBlock.ACTIVE) != active) {
level.setBlock(worldPosition, device_state.setValue(BreakerBlock.ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -1,131 +1,138 @@
/* /*
* @file EdCatwalkBlock.java * @file EdCatwalkBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Bottom aligned platforms with railings. * Bottom aligned platforms with railings.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.block.BlockState; import net.minecraft.sounds.SoundEvents;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.sounds.SoundSource;
import net.minecraft.fluid.Fluids; import net.minecraft.world.InteractionHand;
import net.minecraft.item.*; import net.minecraft.world.InteractionResult;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.entity.player.Player;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.item.BlockItem;
import net.minecraft.util.*; import net.minecraft.world.item.Item;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.block.Block;
import wile.engineersdecor.ModContent; import net.minecraft.world.level.block.entity.BlockEntity;
import wile.engineersdecor.libmc.detail.Inventories; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import java.util.ArrayList; import net.minecraft.world.level.material.Fluids;
import java.util.Arrays; import net.minecraft.world.phys.AABB;
import java.util.Collections; import net.minecraft.world.phys.BlockHitResult;
import java.util.List; import net.minecraft.world.phys.Vec3;
import java.util.stream.Collectors; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Inventories;
public class EdCatwalkBlock extends DecorBlock.HorizontalFourWayWaterLoggable implements IDecorBlock
{ import javax.annotation.Nullable;
final Block railing_block; import java.util.ArrayList;
final AxisAlignedBB base_aabb; import java.util.Arrays;
import java.util.Collections;
public EdCatwalkBlock(long config, AbstractBlock.Properties properties, final AxisAlignedBB base_aabb, final AxisAlignedBB railing_aabb, final Block railing_block) import java.util.List;
{ super(config, properties, base_aabb, railing_aabb, 0); this.railing_block = railing_block; this.base_aabb=base_aabb; } import java.util.stream.Collectors;
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) public class EdCatwalkBlock extends StandardBlocks.HorizontalFourWayWaterLoggable
{ return true; } {
final Block railing_block;
@Override final AABB base_aabb;
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context) public EdCatwalkBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb, final Block railing_block)
{ return super.getStateForPlacement(context).setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false); } { super(config, properties, base_aabb, railing_aabb, 0); this.railing_block = railing_block; this.base_aabb=base_aabb; }
public static boolean place_consume(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, int shrink) @Override
{ public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
if(!world.setBlock(pos, state, 1|2)) return false; { return true; }
world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundCategory.BLOCKS, 1f, 1f);
if((!player.isCreative()) && (!world.isClientSide())) { @Override
ItemStack stack = player.getItemInHand(hand); @Nullable
if(shrink >= 0) { public BlockState getStateForPlacement(BlockPlaceContext context)
stack.shrink(shrink); { return super.getStateForPlacement(context).setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false); }
} else if(stack.getCount() < stack.getMaxStackSize()) {
stack.grow(Math.abs(shrink)); public static boolean place_consume(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, int shrink)
} else { {
Inventories.give(player, new ItemStack(stack.getItem(), Math.abs(shrink))); if(!world.setBlock(pos, state, 1|2)) return false;
} world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f);
Inventories.setItemInPlayerHand(player, hand, stack); if((!player.isCreative()) && (!world.isClientSide())) {
} ItemStack stack = player.getItemInHand(hand);
return true; if(shrink >= 0) {
} stack.shrink(shrink);
} else if(stack.getCount() < stack.getMaxStackSize()) {
@Override stack.grow(Math.abs(shrink));
@SuppressWarnings("deprecation") } else {
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) Inventories.give(player, new ItemStack(stack.getItem(), Math.abs(shrink)));
{ }
final Item item = player.getItemInHand(hand).getItem(); Inventories.setItemInPlayerHand(player, hand, stack);
if((!(item instanceof BlockItem))) return ActionResultType.PASS; }
final Block block = ((BlockItem)item).getBlock(); return true;
if(block == this) { }
if(hit.getDirection().getAxis().isHorizontal()) return ActionResultType.PASS; // place new block on the clicked side.
BlockPos adjacent_pos = pos.relative(player.getDirection()); @Override
BlockState adjacent_state = world.getBlockState(adjacent_pos); @SuppressWarnings("deprecation")
if(adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
BlockState place_state = defaultBlockState(); {
place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); final Item item = player.getItemInHand(hand).getItem();
place_consume(place_state, world, adjacent_pos, player, hand, 1); if((!(item instanceof BlockItem))) return InteractionResult.PASS;
} final Block block = ((BlockItem)item).getBlock();
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; if(block == this) {
} if(hit.getDirection().getAxis().isHorizontal()) return InteractionResult.PASS; // place new block on the clicked side.
if(block == railing_block) { BlockPos adjacent_pos = pos.relative(player.getDirection());
Direction face = hit.getDirection(); BlockState adjacent_state = world.getBlockState(adjacent_pos);
final Vector3d rhv = hit.getLocation().subtract(Vector3d.atCenterOf(hit.getBlockPos())); if(adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) {
if(face.getAxis().isHorizontal()) { BlockState place_state = defaultBlockState();
// Side or railing clicked place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER);
if(rhv.multiply(Vector3d.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side. place_consume(place_state, world, adjacent_pos, player, hand, 1);
} else if(player.distanceToSqr(Vector3d.atCenterOf(pos)) < 3) { }
// near accurate placement return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
face = Direction.getNearest(rhv.x, 0, rhv.z); }
} else { if(block == railing_block) {
// far automatic placement Direction face = hit.getDirection();
face = Direction.getNearest(player.getLookAngle().x, 0, player.getLookAngle().z); final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos()));
List<Direction> free_sides = Arrays.stream(Direction.values()).filter(d->d.getAxis().isHorizontal() && (world.getBlockState(pos.relative(d)).getBlock()!=this)).collect(Collectors.toList()); if(face.getAxis().isHorizontal()) {
if(free_sides.isEmpty()) return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; // Side or railing clicked
if(!free_sides.contains(face)) face = free_sides.get(0); if(rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side.
} } else if(player.distanceToSqr(Vec3.atCenterOf(pos)) < 3) {
BooleanProperty railing = getDirectionProperty(face); // near accurate placement
boolean add = (!state.getValue(railing)); face = Direction.getNearest(rhv.x, 0, rhv.z);
place_consume(state.setValue(railing, add), world, pos, player, hand, add ? 1 : -1); } else {
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; // far automatic placement
} face = Direction.getNearest(player.getLookAngle().x, 0, player.getLookAngle().z);
return ActionResultType.PASS; List<Direction> free_sides = Arrays.stream(Direction.values()).filter(d->d.getAxis().isHorizontal() && (world.getBlockState(pos.relative(d)).getBlock()!=this)).collect(Collectors.toList());
} if(free_sides.isEmpty()) return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
if(!free_sides.contains(face)) face = free_sides.get(0);
// -- IDecorBlock }
BooleanProperty railing = getDirectionProperty(face);
@Override boolean add = (!state.getValue(railing));
public boolean hasDynamicDropList() place_consume(state.setValue(railing, add), world, pos, player, hand, add ? 1 : -1);
{ return true; } return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
}
@Override return InteractionResult.PASS;
public List<ItemStack> dropList(BlockState state, World world, @Nullable TileEntity te, boolean explosion) }
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); @Override
List<ItemStack> drops = new ArrayList<>(); public boolean hasDynamicDropList()
drops.add(new ItemStack(state.getBlock().asItem())); { return true; }
int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0);
if(n > 0) drops.add(new ItemStack(ModContent.STEEL_RAILING, n)); @Override
return drops; public List<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
} {
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
} List<ItemStack> drops = new ArrayList<>();
drops.add(new ItemStack(state.getBlock().asItem()));
int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0);
if(n > 0) drops.add(new ItemStack(ModContent.STEEL_RAILING, n));
return drops;
}
}

View file

@ -1,183 +1,186 @@
/* /*
* @file EdCatwalkStairsBlock.java * @file EdCatwalkStairsBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Stair version of the catwalk block, optional left/right railings. * Stair version of the catwalk block, optional left/right railings.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.block.BlockState; import net.minecraft.world.InteractionHand;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionResult;
import net.minecraft.fluid.Fluids; import net.minecraft.world.entity.player.Player;
import net.minecraft.item.*; import net.minecraft.world.item.BlockItem;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.item.Item;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.ItemStack;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.*; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.Direction.Axis; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.IBlockReader; import net.minecraft.world.phys.AABB;
import net.minecraft.world.World; import net.minecraft.world.phys.BlockHitResult;
import wile.engineersdecor.ModContent; import net.minecraft.world.phys.Vec3;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.CollisionContext;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.Shapes;
import java.util.*; import net.minecraft.world.phys.shapes.VoxelShape;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
public class EdCatwalkStairsBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock
{ import javax.annotation.Nullable;
public static final BooleanProperty RIGHT_RAILING = BooleanProperty.create("right_railing"); import java.util.*;
public static final BooleanProperty LEFT_RAILING = BooleanProperty.create("left_railing");
protected final Map<BlockState, VoxelShape> shapes;
protected final Map<BlockState, VoxelShape> collision_shapes; public class EdCatwalkStairsBlock extends StandardBlocks.HorizontalWaterLoggable
protected final Map<Direction, Integer> y_rotations; {
public static final BooleanProperty RIGHT_RAILING = BooleanProperty.create("right_railing");
public EdCatwalkStairsBlock(long config, AbstractBlock.Properties properties, final AxisAlignedBB[] base_aabb, final AxisAlignedBB[] railing_aabbs) public static final BooleanProperty LEFT_RAILING = BooleanProperty.create("left_railing");
{ protected final Map<BlockState, VoxelShape> shapes;
super(config, properties, base_aabb); protected final Map<BlockState, VoxelShape> collision_shapes;
Map<BlockState, VoxelShape> sh = new HashMap<>(); protected final Map<Direction, Integer> y_rotations;
Map<BlockState, VoxelShape> csh = new HashMap<>();
getStateDefinition().getPossibleStates().forEach(state->{ public EdCatwalkStairsBlock(long config, BlockBehaviour.Properties properties, final AABB[] base_aabb, final AABB[] railing_aabbs)
Direction facing = state.getValue(HORIZONTAL_FACING); {
VoxelShape base_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(base_aabb, facing, true)); super(config, properties, base_aabb);
if(state.getValue(RIGHT_RAILING)) { Map<BlockState, VoxelShape> sh = new HashMap<>();
VoxelShape right_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(Auxiliaries.getMirroredAABB(railing_aabbs, Axis.X), facing, true)); Map<BlockState, VoxelShape> csh = new HashMap<>();
base_shape = VoxelShapes.joinUnoptimized(base_shape, right_shape, IBooleanFunction.OR); getStateDefinition().getPossibleStates().forEach(state->{
} Direction facing = state.getValue(HORIZONTAL_FACING);
if(state.getValue(LEFT_RAILING)) { VoxelShape base_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(base_aabb, facing, true));
VoxelShape left_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(railing_aabbs, facing, true)); if(state.getValue(RIGHT_RAILING)) {
base_shape = VoxelShapes.joinUnoptimized(base_shape, left_shape, IBooleanFunction.OR); VoxelShape right_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(Auxiliaries.getMirroredAABB(railing_aabbs, Direction.Axis.X), facing, true));
} base_shape = Shapes.joinUnoptimized(base_shape, right_shape, BooleanOp.OR);
sh.put(state, base_shape); }
csh.put(state, base_shape); if(state.getValue(LEFT_RAILING)) {
}); VoxelShape left_shape = Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(railing_aabbs, facing, true));
shapes = sh; base_shape = Shapes.joinUnoptimized(base_shape, left_shape, BooleanOp.OR);
collision_shapes = csh; }
y_rotations = new HashMap<>(); sh.put(state, base_shape);
y_rotations.put(Direction.NORTH, 0); csh.put(state, base_shape);
y_rotations.put(Direction.EAST, 1); });
y_rotations.put(Direction.SOUTH, 2); shapes = sh;
y_rotations.put(Direction.WEST, 3); collision_shapes = csh;
y_rotations.put(Direction.UP, 0); y_rotations = new HashMap<>();
y_rotations.put(Direction.DOWN, 0); y_rotations.put(Direction.NORTH, 0);
registerDefaultState(super.defaultBlockState().setValue(LEFT_RAILING, false).setValue(RIGHT_RAILING, false)); y_rotations.put(Direction.EAST, 1);
} y_rotations.put(Direction.SOUTH, 2);
y_rotations.put(Direction.WEST, 3);
@Override y_rotations.put(Direction.UP, 0);
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) y_rotations.put(Direction.DOWN, 0);
{ return shapes.getOrDefault(state, VoxelShapes.block()); } registerDefaultState(super.defaultBlockState().setValue(LEFT_RAILING, false).setValue(RIGHT_RAILING, false));
}
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) @Override
{ return collision_shapes.getOrDefault(state, VoxelShapes.block()); } public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context)
{ return shapes.getOrDefault(state, Shapes.block()); }
@Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) @Override
{ super.createBlockStateDefinition(builder); builder.add(RIGHT_RAILING, LEFT_RAILING); } public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context)
{ return collision_shapes.getOrDefault(state, Shapes.block()); }
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) @Override
{ return true; } protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); builder.add(RIGHT_RAILING, LEFT_RAILING); }
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
{ return super.getStateForPlacement(context); } { return true; }
@Override @Override
@SuppressWarnings("deprecation") @Nullable
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) public BlockState getStateForPlacement(BlockPlaceContext context)
{ { return super.getStateForPlacement(context); }
final Item item = player.getItemInHand(hand).getItem();
if((!(item instanceof BlockItem))) return ActionResultType.PASS; @Override
final Block block = ((BlockItem)item).getBlock(); @SuppressWarnings("deprecation")
final Direction facing = state.getValue(HORIZONTAL_FACING); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
if(block == this) { {
final Direction hlv = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis().isHorizontal()).findFirst().orElse(Direction.NORTH); final Item item = player.getItemInHand(hand).getItem();
BlockPos adjacent_pos; if((!(item instanceof BlockItem))) return InteractionResult.PASS;
if(hlv == facing) { final Block block = ((BlockItem)item).getBlock();
adjacent_pos = pos.above().relative(hlv); final Direction facing = state.getValue(HORIZONTAL_FACING);
} else if(hlv == facing.getOpposite()) { if(block == this) {
adjacent_pos = pos.below().relative(hlv); final Direction hlv = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis().isHorizontal()).findFirst().orElse(Direction.NORTH);
} else { BlockPos adjacent_pos;
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; if(hlv == facing) {
} adjacent_pos = pos.above().relative(hlv);
final BlockState adjacent_state = world.getBlockState(adjacent_pos); } else if(hlv == facing.getOpposite()) {
if(adjacent_state == null) return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; adjacent_pos = pos.below().relative(hlv);
if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return ActionResultType.CONSUME; } else {
BlockState place_state = defaultBlockState().setValue(HORIZONTAL_FACING, facing); return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); }
EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); final BlockState adjacent_state = world.getBlockState(adjacent_pos);
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; if(adjacent_state == null) return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
} else if((block == ModContent.STEEL_CATWALK) || (block == ModContent.STEEL_CATWALK_TOP_ALIGNED)) { if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return InteractionResult.CONSUME;
BlockPos adjacent_pos; BlockState place_state = defaultBlockState().setValue(HORIZONTAL_FACING, facing);
adjacent_pos = pos.relative(facing); place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER);
final BlockState adjacent_state = world.getBlockState(adjacent_pos); EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1);
if(adjacent_state == null) return ActionResultType.CONSUME; return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return ActionResultType.CONSUME; } else if((block == ModContent.STEEL_CATWALK) || (block == ModContent.STEEL_CATWALK_TOP_ALIGNED)) {
BlockState place_state = ModContent.STEEL_CATWALK_TOP_ALIGNED.defaultBlockState(); BlockPos adjacent_pos;
place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); adjacent_pos = pos.relative(facing);
EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1); final BlockState adjacent_state = world.getBlockState(adjacent_pos);
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; if(adjacent_state == null) return InteractionResult.CONSUME;
} else if(block == ModContent.STEEL_RAILING) { if(!adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) return InteractionResult.CONSUME;
Direction face = hit.getDirection(); BlockState place_state = ModContent.STEEL_CATWALK_TOP_ALIGNED.defaultBlockState();
int shrink = 0; place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER);
BlockState place_state = state; EdCatwalkBlock.place_consume(place_state, world, adjacent_pos, player, hand, 1);
if(face == Direction.UP) { return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
Vector3d rhv = hit.getLocation().subtract(Vector3d.atCenterOf(hit.getBlockPos())).multiply(new Vector3d(1,0,1)).cross(Vector3d.atLowerCornerOf(facing.getNormal())); } else if(block == ModContent.STEEL_RAILING) {
face = (rhv.y > 0) ? (facing.getClockWise()) : (facing.getCounterClockWise()); Direction face = hit.getDirection();
} int shrink = 0;
if(face == facing.getClockWise()) { BlockState place_state = state;
if(state.getValue(RIGHT_RAILING)) { if(face == Direction.UP) {
place_state = state.setValue(RIGHT_RAILING, false); Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos())).multiply(new Vec3(1,0,1)).cross(Vec3.atLowerCornerOf(facing.getNormal()));
shrink = -1; face = (rhv.y > 0) ? (facing.getClockWise()) : (facing.getCounterClockWise());
} else { }
place_state = state.setValue(RIGHT_RAILING, true); if(face == facing.getClockWise()) {
shrink = 1; if(state.getValue(RIGHT_RAILING)) {
} place_state = state.setValue(RIGHT_RAILING, false);
} else if(face == facing.getCounterClockWise()) { shrink = -1;
if(state.getValue(LEFT_RAILING)) { } else {
place_state = state.setValue(LEFT_RAILING, false); place_state = state.setValue(RIGHT_RAILING, true);
shrink = -1; shrink = 1;
} else { }
place_state = state.setValue(LEFT_RAILING, true); } else if(face == facing.getCounterClockWise()) {
shrink = 1; if(state.getValue(LEFT_RAILING)) {
} place_state = state.setValue(LEFT_RAILING, false);
} shrink = -1;
if(shrink != 0) EdCatwalkBlock.place_consume(place_state, world, pos, player, hand, shrink); } else {
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; place_state = state.setValue(LEFT_RAILING, true);
} shrink = 1;
return ActionResultType.PASS; }
} }
if(shrink != 0) EdCatwalkBlock.place_consume(place_state, world, pos, player, hand, shrink);
// -- IDecorBlock return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
}
@Override return InteractionResult.PASS;
public boolean hasDynamicDropList() }
{ return true; }
@Override
@Override public boolean hasDynamicDropList()
public List<ItemStack> dropList(BlockState state, World world, @Nullable TileEntity te, boolean explosion) { return true; }
{
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY); @Override
List<ItemStack> drops = new ArrayList<>(); public List<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
drops.add(new ItemStack(state.getBlock().asItem())); {
int n = (state.getValue(LEFT_RAILING)?1:0)+(state.getValue(RIGHT_RAILING)?1:0); if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
if(n > 0) drops.add(new ItemStack(ModContent.STEEL_RAILING, n)); List<ItemStack> drops = new ArrayList<>();
return drops; drops.add(new ItemStack(state.getBlock().asItem()));
} int n = (state.getValue(LEFT_RAILING)?1:0)+(state.getValue(RIGHT_RAILING)?1:0);
if(n > 0) drops.add(new ItemStack(ModContent.STEEL_RAILING, n));
} return drops;
}
}

View file

@ -1,106 +1,111 @@
/* /*
* @file EdCatwalkTopAlignedBlock.java * @file EdCatwalkTopAlignedBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Top aligned platforms, down-connection to poles. * Top aligned platforms, down-connection to poles.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.block.BlockState; import net.minecraft.world.InteractionHand;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionResult;
import net.minecraft.fluid.Fluids; import net.minecraft.world.entity.player.Player;
import net.minecraft.item.*; import net.minecraft.world.item.Item;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.*; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.IWorld; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.World; import net.minecraft.world.level.material.Fluids;
import wile.engineersdecor.ModContent; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.Shapes;
import java.util.*; import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.stream.Collectors; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
public class EdCatwalkTopAlignedBlock extends DecorBlock.WaterLoggable implements IDecorBlock import javax.annotation.Nullable;
{ import java.util.List;
public static final IntegerProperty VARIANT = IntegerProperty.create("variant", 0, 3); import java.util.stream.Collectors;
protected final List<VoxelShape> variant_shapes;
public EdCatwalkTopAlignedBlock(long config, AbstractBlock.Properties properties, final VoxelShape[] variant_shapes) public class EdCatwalkTopAlignedBlock extends StandardBlocks.WaterLoggable
{ {
super(config, properties, variant_shapes[0]); public static final IntegerProperty VARIANT = IntegerProperty.create("variant", 0, 3);
registerDefaultState(super.defaultBlockState().setValue(VARIANT, 0)); protected final List<VoxelShape> variant_shapes;
this.variant_shapes = VARIANT.getPossibleValues().stream().map(i->(i<variant_shapes.length) ? (variant_shapes[i]) : (VoxelShapes.block())).collect(Collectors.toList());
} public EdCatwalkTopAlignedBlock(long config, BlockBehaviour.Properties properties, final VoxelShape[] variant_shapes)
{
@Override super(config, properties, variant_shapes[0]);
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) registerDefaultState(super.defaultBlockState().setValue(VARIANT, 0));
{ return true; } this.variant_shapes = VARIANT.getPossibleValues().stream().map(i->(i<variant_shapes.length) ? (variant_shapes[i]) : (Shapes.block())).collect(Collectors.toList());
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) @Override
{ return variant_shapes.get(state.getValue(VARIANT)); } public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
{ return true; }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) @Override
{ return getShape(state, world, pos, selectionContext); } public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
{ return variant_shapes.get(state.getValue(VARIANT)); }
@Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) @Override
{ super.createBlockStateDefinition(builder); builder.add(VARIANT); } public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ { super.createBlockStateDefinition(builder); builder.add(VARIANT); }
BlockState state = adapted_state(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos());
if(context.getClickedFace() != Direction.UP) return state; @Override
BlockState below = context.getLevel().getBlockState(context.getClickedPos().below()); @Nullable
if((state.getValue(VARIANT)==0) && (below.isFaceSturdy(context.getLevel(), context.getClickedPos().below(), Direction.UP))) return state.setValue(VARIANT, 3); public BlockState getStateForPlacement(BlockPlaceContext context)
return state; {
} BlockState state = adapted_state(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos());
if(context.getClickedFace() != Direction.UP) return state;
@Override BlockState below = context.getLevel().getBlockState(context.getClickedPos().below());
@SuppressWarnings("deprecation") if((state.getValue(VARIANT)==0) && (below.isFaceSturdy(context.getLevel(), context.getClickedPos().below(), Direction.UP))) return state.setValue(VARIANT, 3);
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) return state;
{ }
final Item item = player.getItemInHand(hand).getItem();
if(item != this.asItem()) return ActionResultType.PASS; @Override
if(hit.getDirection().getAxis().isHorizontal()) return ActionResultType.PASS; @SuppressWarnings("deprecation")
BlockPos adjacent_pos = pos.relative(player.getDirection()); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
BlockState adjacent_state = world.getBlockState(adjacent_pos); {
if(adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) { final Item item = player.getItemInHand(hand).getItem();
BlockState place_state = defaultBlockState(); if(item != this.asItem()) return InteractionResult.PASS;
place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER); if(hit.getDirection().getAxis().isHorizontal()) return InteractionResult.PASS;
EdCatwalkBlock.place_consume(adapted_state(place_state, world, adjacent_pos), world, adjacent_pos, player, hand, 1); BlockPos adjacent_pos = pos.relative(player.getDirection());
} BlockState adjacent_state = world.getBlockState(adjacent_pos);
return world.isClientSide() ? ActionResultType.SUCCESS : ActionResultType.CONSUME; if(adjacent_state.canBeReplaced(new DirectionalPlaceContext(world, adjacent_pos, hit.getDirection().getOpposite(), player.getItemInHand(hand), hit.getDirection()))) {
} BlockState place_state = defaultBlockState();
place_state = place_state.setValue(WATERLOGGED,adjacent_state.getFluidState().getType()==Fluids.WATER);
@Override EdCatwalkBlock.place_consume(adapted_state(place_state, world, adjacent_pos), world, adjacent_pos, player, hand, 1);
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos) }
{ return adapted_state(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); } return world.isClientSide() ? InteractionResult.SUCCESS : InteractionResult.CONSUME;
}
// ---
@Override
private BlockState adapted_state(BlockState state, IWorld world, BlockPos pos) public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
{ { return adapted_state(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); }
BlockState below = world.getBlockState(pos.below());
if((below == null) || (state == null)) return state; // ---
if((below.getBlock() == ModContent.THICK_STEEL_POLE) || (below.getBlock() == ModContent.THICK_STEEL_POLE_HEAD)) return state.setValue(VARIANT, 1);
if((below.getBlock() == ModContent.THIN_STEEL_POLE) || (below.getBlock() == ModContent.THIN_STEEL_POLE_HEAD)) return state.setValue(VARIANT, 2); private BlockState adapted_state(BlockState state, LevelAccessor world, BlockPos pos)
return state; {
} BlockState below = world.getBlockState(pos.below());
if((below == null) || (state == null)) return state;
} if((below.getBlock() == ModContent.THICK_STEEL_POLE) || (below.getBlock() == ModContent.THICK_STEEL_POLE_HEAD)) return state.setValue(VARIANT, 1);
if((below.getBlock() == ModContent.THIN_STEEL_POLE) || (below.getBlock() == ModContent.THIN_STEEL_POLE_HEAD)) return state.setValue(VARIANT, 2);
return state;
}
}

View file

@ -1,195 +1,204 @@
/* /*
* @file EdChair.java * @file EdChair.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Full block characteristics class. * Full block characteristics class.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.entity.monster.piglin.PiglinEntity; import net.minecraft.core.BlockPos;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.nbt.CompoundTag;
import net.minecraft.block.AbstractBlock; import net.minecraft.network.protocol.Packet;
import net.minecraft.world.server.ServerWorld; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.World; import net.minecraft.util.Mth;
import net.minecraft.block.BlockState; import net.minecraft.world.InteractionHand;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionResult;
import net.minecraft.entity.*; import net.minecraft.world.entity.Entity;
import net.minecraft.entity.monster.*; import net.minecraft.world.entity.EntityType;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.network.IPacket; import net.minecraft.world.entity.Mob;
import net.minecraft.util.math.*; import net.minecraft.world.entity.monster.*;
import net.minecraft.util.*; import net.minecraft.world.entity.monster.piglin.Piglin;
import net.minecraftforge.fml.network.FMLPlayMessages; import net.minecraft.world.entity.player.Player;
import net.minecraftforge.fml.network.NetworkHooks; import net.minecraft.world.level.Level;
import wile.engineersdecor.ModConfig; import net.minecraft.world.level.block.state.BlockBehaviour;
import wile.engineersdecor.ModContent; import net.minecraft.world.level.block.state.BlockState;
import java.util.List; import net.minecraft.world.phys.AABB;
import java.util.Random; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fmllegacy.network.FMLPlayMessages;
import net.minecraftforge.fmllegacy.network.NetworkHooks;
public class EdChair import wile.engineersdecor.ModConfig;
{ import wile.engineersdecor.ModContent;
private static boolean sitting_enabled = true; import wile.engineersdecor.libmc.blocks.StandardBlocks;
private static double sitting_probability = 0.1;
private static double standup_probability = 0.01; import java.util.List;
import java.util.Random;
public static void on_config(boolean without_sitting, boolean without_mob_sitting, double sitting_probability_percent, double standup_probability_percent)
{
sitting_enabled = (!without_sitting);
sitting_probability = (without_sitting||without_mob_sitting) ? 0.0 : MathHelper.clamp(sitting_probability_percent/100, 0, 0.9); public class EdChair
standup_probability = (without_sitting||without_mob_sitting) ? 1.0 : MathHelper.clamp(standup_probability_percent/100, 1e-6, 1e-2); {
ModConfig.log("Config chairs: sit:" + sitting_enabled + ", mob-sit: " + (sitting_probability*100) + "%, standup: " + (standup_probability) + "%."); private static boolean sitting_enabled = true;
} private static double sitting_probability = 0.1;
private static double standup_probability = 0.01;
//--------------------------------------------------------------------------------------------------------------------
// Block public static void on_config(boolean without_sitting, boolean without_mob_sitting, double sitting_probability_percent, double standup_probability_percent)
//-------------------------------------------------------------------------------------------------------------------- {
sitting_enabled = (!without_sitting);
public static class ChairBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock sitting_probability = (without_sitting||without_mob_sitting) ? 0.0 : Mth.clamp(sitting_probability_percent/100, 0, 0.9);
{ standup_probability = (without_sitting||without_mob_sitting) ? 1.0 : Mth.clamp(standup_probability_percent/100, 1e-6, 1e-2);
public ChairBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABBs) ModConfig.log("Config chairs: sit:" + sitting_enabled + ", mob-sit: " + (sitting_probability*100) + "%, standup: " + (standup_probability) + "%.");
{ super(config, builder.randomTicks(), unrotatedAABBs); } }
@Override //--------------------------------------------------------------------------------------------------------------------
@SuppressWarnings("deprecation") // Block
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) //--------------------------------------------------------------------------------------------------------------------
{
if(!sitting_enabled) return ActionResultType.PASS; public static class ChairBlock extends StandardBlocks.HorizontalWaterLoggable
if(world.isClientSide()) return ActionResultType.SUCCESS; {
EntityChair.sit(world, player, pos); public ChairBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBs)
return ActionResultType.CONSUME; { super(config, builder.randomTicks(), unrotatedAABBs); }
}
@Override
@Override @SuppressWarnings("deprecation")
@SuppressWarnings("deprecation") public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
public void entityInside(BlockState state, World world, BlockPos pos, Entity entity) {
{ if(!sitting_enabled) return InteractionResult.PASS;
if(sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof MobEntity)) EntityChair.sit(world, (LivingEntity)entity, pos); if(world.isClientSide()) return InteractionResult.SUCCESS;
} EntityChair.sit(world, player, pos);
return InteractionResult.CONSUME;
@Override }
@SuppressWarnings("deprecation")
public void tick(BlockState state, ServerWorld world, BlockPos pos, Random rnd) @Override
{ @SuppressWarnings("deprecation")
if((!sitting_enabled) || (sitting_probability < 1e-6)) return; public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity)
final List<LivingEntity> entities = world.getEntitiesOfClass(MobEntity.class, new AxisAlignedBB(pos).inflate(2,1,2).expandTowards(0,1,0), e->true); {
if(entities.isEmpty()) return; if(sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof Mob)) EntityChair.sit(world, (LivingEntity)entity, pos);
int index = rnd.nextInt(entities.size()); }
if((index < 0) || (index >= entities.size())) return;
EntityChair.sit(world, entities.get(index), pos); @Override
} @SuppressWarnings("deprecation")
} public void tick(BlockState state, ServerLevel world, BlockPos pos, Random rnd)
{
//-------------------------------------------------------------------------------------------------------------------- if((!sitting_enabled) || (sitting_probability < 1e-6)) return;
// Entity final List<Mob> entities = world.getEntitiesOfClass(Mob.class, new AABB(pos).inflate(2,1,2).expandTowards(0,1,0), e->true);
//-------------------------------------------------------------------------------------------------------------------- if(entities.isEmpty()) return;
int index = rnd.nextInt(entities.size());
public static class EntityChair extends Entity if((index < 0) || (index >= entities.size())) return;
{ EntityChair.sit(world, entities.get(index), pos);
public static final double x_offset = 0.5d; }
public static final double y_offset = 0.4d; }
public static final double z_offset = 0.5d;
private int t_sit = 0; //--------------------------------------------------------------------------------------------------------------------
public BlockPos chair_pos = new BlockPos(0,0,0); // Entity
//--------------------------------------------------------------------------------------------------------------------
public EntityChair(EntityType<? extends Entity> entityType, World world)
{ public static class EntityChair extends Entity
super(entityType, world); {
blocksBuilding=true; public static final double x_offset = 0.5d;
setDeltaMovement(Vector3d.ZERO); public static final double y_offset = 0.4d;
canUpdate(true); public static final double z_offset = 0.5d;
noPhysics=true; private int t_sit = 0;
} public BlockPos chair_pos = new BlockPos(0,0,0);
public EntityChair(World world) public EntityChair(EntityType<? extends Entity> entityType, Level world)
{ this(ModContent.ET_CHAIR, world); } {
super(entityType, world);
public static EntityChair customClientFactory(FMLPlayMessages.SpawnEntity spkt, World world) blocksBuilding=true;
{ return new EntityChair(world); } setDeltaMovement(Vec3.ZERO);
canUpdate(true);
public IPacket<?> getAddEntityPacket() noPhysics=true;
{ return NetworkHooks.getEntitySpawningPacket(this); } }
public static boolean accepts_mob(LivingEntity entity) public EntityChair(Level world)
{ { this(ModContent.ET_CHAIR, world); }
if(!(entity instanceof net.minecraft.entity.monster.MonsterEntity)) return false;
if((entity.getType().getDimensions().height > 2.5) || (entity.getType().getDimensions().height > 2.0)) return false; public static EntityChair customClientFactory(FMLPlayMessages.SpawnEntity spkt, Level world)
if(entity instanceof ZombieEntity) return true; { return new EntityChair(world); }
if(entity instanceof ZombieVillagerEntity) return true;
if(entity instanceof ZombifiedPiglinEntity) return true; public Packet<?> getAddEntityPacket()
if(entity instanceof PiglinEntity) return true; { return NetworkHooks.getEntitySpawningPacket(this); }
if(entity instanceof HuskEntity) return true;
if(entity instanceof StrayEntity) return true; public static boolean accepts_mob(LivingEntity entity)
if(entity instanceof SkeletonEntity) return true; {
if(entity instanceof WitherSkeletonEntity) return true; if(!(entity instanceof Monster)) return false;
return false; if((entity.getType().getDimensions().height > 2.5) || (entity.getType().getDimensions().height > 2.0)) return false;
} if(entity instanceof Zombie) return true;
if(entity instanceof ZombieVillager) return true;
public static void sit(World world, LivingEntity sitter, BlockPos pos) if(entity instanceof ZombifiedPiglin) return true;
{ if(entity instanceof Piglin) return true;
if(!sitting_enabled) return; if(entity instanceof Husk) return true;
if((world==null) || (world.isClientSide) || (sitter==null) || (pos==null)) return; if(entity instanceof Stray) return true;
if((!(sitter instanceof PlayerEntity)) && (!accepts_mob(sitter))) return; if(entity instanceof Skeleton) return true;
if(!world.getEntitiesOfClass(EntityChair.class, new AxisAlignedBB(pos)).isEmpty()) return; if(entity instanceof WitherSkeleton) return true;
if(sitter.isVehicle() || (!sitter.isAlive()) || (sitter.isPassenger()) ) return; return false;
if((!world.isEmptyBlock(pos.above())) || (!world.isEmptyBlock(pos.above(2)))) return; }
boolean on_top_of_block_position = true;
boolean use_next_negative_y_position = false; public static void sit(Level world, LivingEntity sitter, BlockPos pos)
EntityChair chair = new EntityChair(world); {
BlockPos chair_pos = chair.blockPosition(); if(!sitting_enabled) return;
chair.chair_pos = pos; if((world==null) || (world.isClientSide) || (sitter==null) || (pos==null)) return;
chair.t_sit = 5; if((!(sitter instanceof Player)) && (!accepts_mob(sitter))) return;
chair.xo = chair_pos.getX(); if(!world.getEntitiesOfClass(EntityChair.class, new AABB(pos)).isEmpty()) return;
chair.yo = chair_pos.getY(); if(sitter.isVehicle() || (!sitter.isAlive()) || (sitter.isPassenger()) ) return;
chair.zo = chair_pos.getZ(); if((!world.isEmptyBlock(pos.above())) || (!world.isEmptyBlock(pos.above(2)))) return;
chair.setPos(pos.getX()+x_offset,pos.getY()+y_offset,pos.getZ()+z_offset); boolean on_top_of_block_position = true;
world.addFreshEntity(chair); boolean use_next_negative_y_position = false;
sitter.startRiding(chair, true); EntityChair chair = new EntityChair(world);
} BlockPos chair_pos = chair.blockPosition();
chair.chair_pos = pos;
@Override chair.t_sit = 5;
protected void defineSynchedData() {} chair.xo = chair_pos.getX();
chair.yo = chair_pos.getY();
@Override chair.zo = chair_pos.getZ();
protected void readAdditionalSaveData(CompoundNBT compound) {} chair.setPos(pos.getX()+x_offset,pos.getY()+y_offset,pos.getZ()+z_offset);
world.addFreshEntity(chair);
@Override sitter.startRiding(chair, true);
protected void addAdditionalSaveData(CompoundNBT compound) {} }
@Override @Override
public boolean isPushable() protected void defineSynchedData() {}
{ return false; }
@Override
@Override protected void readAdditionalSaveData(CompoundTag compound) {}
public double getPassengersRidingOffset()
{ return 0.0; } @Override
protected void addAdditionalSaveData(CompoundTag compound) {}
@Override
public void tick() @Override
{ public boolean isPushable()
if(level.isClientSide) return; { return false; }
super.tick();
if(--t_sit > 0) return; @Override
Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0); public double getPassengersRidingOffset()
if((sitter==null) || (!sitter.isAlive())) { { return 0.0; }
this.remove();
return; @Override
} public void tick()
boolean abort = (!sitting_enabled); {
final BlockState state = level.getBlockState(chair_pos); if(level.isClientSide) return;
if((state==null) || (!(state.getBlock() instanceof ChairBlock))) abort = true; super.tick();
if(!level.isEmptyBlock(chair_pos.above())) abort = true; if(--t_sit > 0) return;
if((!(sitter instanceof PlayerEntity)) && (Math.random() < standup_probability)) abort = true; Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0);
if(abort) { if((sitter==null) || (!sitter.isAlive())) {
for(Entity e:getPassengers()) { this.remove(RemovalReason.DISCARDED);
if(e.isAlive()) e.stopRiding(); return;
} }
this.remove(); boolean abort = (!sitting_enabled);
} final BlockState state = level.getBlockState(chair_pos);
} if((state==null) || (!(state.getBlock() instanceof ChairBlock))) abort = true;
} if(!level.isEmptyBlock(chair_pos.above())) abort = true;
if((!(sitter instanceof Player)) && (Math.random() < standup_probability)) abort = true;
} if(abort) {
for(Entity e:getPassengers()) {
if(e.isAlive()) e.stopRiding();
}
this.remove(RemovalReason.DISCARDED);
}
}
}
}

View file

@ -1,98 +1,101 @@
/* /*
* @file EdChimneyBlock.java * @file EdChimneyBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Block type for smoking chimneys. * Block type for smoking chimneys.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.*; import net.minecraft.core.BlockPos;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.core.Direction;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.particles.ParticleTypes; import net.minecraft.world.InteractionHand;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.InteractionResult;
import net.minecraft.state.StateContainer; import net.minecraft.world.entity.player.Player;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.ActionResultType; import net.minecraft.world.level.Level;
import net.minecraft.util.Direction; import net.minecraft.world.level.LevelReader;
import net.minecraft.util.Hand; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.IWorldReader; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.api.distmarker.Dist;
import javax.annotation.Nullable; import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.Random; import wile.engineersdecor.libmc.blocks.StandardBlocks;
public class EdChimneyBlock extends DecorBlock.Cutout implements IDecorBlock import javax.annotation.Nullable;
{ import java.util.Random;
public static final IntegerProperty POWER = BlockStateProperties.POWER;
public class EdChimneyBlock extends StandardBlocks.Cutout
public EdChimneyBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB aabb) {
{ super(config, properties, aabb); } public static final IntegerProperty POWER = BlockStateProperties.POWER;
public EdChimneyBlock(long config, AbstractBlock.Properties builder) public EdChimneyBlock(long config, BlockBehaviour.Properties properties, AABB aabb)
{ { super(config, properties, aabb); }
this(config, builder, new AxisAlignedBB(0,0,0,1,1,1));
registerDefaultState(super.defaultBlockState().setValue(POWER, 0)); // no smoke in JEI public EdChimneyBlock(long config, BlockBehaviour.Properties builder)
} {
this(config, builder, new AABB(0,0,0,1,1,1));
@Override registerDefaultState(super.defaultBlockState().setValue(POWER, 0)); // no smoke in JEI
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) }
{ super.createBlockStateDefinition(builder); builder.add(POWER); }
@Override
@Override protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
@Nullable { super.createBlockStateDefinition(builder); builder.add(POWER); }
public BlockState getStateForPlacement(BlockItemUseContext context)
{ @Override
BlockState state = super.getStateForPlacement(context); @Nullable
if(state==null) return state; public BlockState getStateForPlacement(BlockPlaceContext context)
int p = context.getLevel().getBestNeighborSignal(context.getClickedPos()); {
return state.setValue(POWER, p==0 ? 5 : p); BlockState state = super.getStateForPlacement(context);
} if(state==null) return state;
int p = context.getLevel().getBestNeighborSignal(context.getClickedPos());
@Override return state.setValue(POWER, p==0 ? 5 : p);
@SuppressWarnings("deprecation") }
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{ world.setBlock(pos, state.setValue(POWER, (state.getValue(POWER)+1) & 0xf), 1|2); return ActionResultType.sidedSuccess(world.isClientSide()); } @Override
@SuppressWarnings("deprecation")
@Override public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
@SuppressWarnings("deprecation") { world.setBlock(pos, state.setValue(POWER, (state.getValue(POWER)+1) & 0xf), 1|2); return InteractionResult.sidedSuccess(world.isClientSide()); }
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{ @Override
int p = world.getBestNeighborSignal(pos); @SuppressWarnings("deprecation")
if(p != state.getValue(POWER)) world.setBlock(pos, state.setValue(POWER, p), 2); public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
} {
int p = world.getBestNeighborSignal(pos);
@Override if(p != state.getValue(POWER)) world.setBlock(pos, state.setValue(POWER, p), 2);
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) }
{ return false; }
@Override
@Override public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
@OnlyIn(Dist.CLIENT) { return false; }
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{ @Override
if(state.getBlock() != this) return; @OnlyIn(Dist.CLIENT)
final int p = state.getValue(POWER); public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd)
if(p==0) return; {
int end = 1+rnd.nextInt(10) * p / 15; if(state.getBlock() != this) return;
for(int i=0; i<end; ++i) { final int p = state.getValue(POWER);
double rv = rnd.nextDouble() * p / 5; if(p==0) return;
world.addParticle( int end = 1+rnd.nextInt(10) * p / 15;
(rv > 0.7 ? ParticleTypes.LARGE_SMOKE : (rv>0.4 ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE)), for(int i=0; i<end; ++i) {
0.5+pos.getX()+(rnd.nextDouble()*0.2), double rv = rnd.nextDouble() * p / 5;
0.9+pos.getY()+(rnd.nextDouble()*0.1), world.addParticle(
0.5+pos.getZ()+(rnd.nextDouble()*0.2), (rv > 0.7 ? ParticleTypes.LARGE_SMOKE : (rv>0.4 ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE)),
-0.02 + rnd.nextDouble()*0.04, 0.5+pos.getX()+(rnd.nextDouble()*0.2),
+0.05 + rnd.nextDouble()*0.1, 0.9+pos.getY()+(rnd.nextDouble()*0.1),
-0.02 + rnd.nextDouble()*0.04 0.5+pos.getZ()+(rnd.nextDouble()*0.2),
); -0.02 + rnd.nextDouble()*0.04,
} +0.05 + rnd.nextDouble()*0.1,
} -0.02 + rnd.nextDouble()*0.04
);
} }
}
}

View file

@ -1,36 +1,37 @@
/* /*
* @file EdChimneyTrunkBlock.java * @file EdChimneyTrunkBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Roof block with chimney trunk, only straight. * Roof block with chimney trunk, only straight.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.*; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.state.properties.Half; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.state.properties.StairsShape; import net.minecraft.world.level.block.state.properties.Half;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.properties.StairsShape;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import javax.annotation.Nullable;
import javax.annotation.Nullable;
public class EdChimneyTrunkBlock extends EdRoofBlock implements IDecorBlock
{ public class EdChimneyTrunkBlock extends EdRoofBlock
public EdChimneyTrunkBlock(long config, AbstractBlock.Properties properties) {
{ super(config, properties.dynamicShape(), VoxelShapes.empty(), VoxelShapes.empty()); } public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties)
{ super(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); }
public EdChimneyTrunkBlock(long config, AbstractBlock.Properties properties, VoxelShape add, VoxelShape cut)
{ super(config, properties, add, cut); } public EdChimneyTrunkBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut)
{ super(config, properties, add, cut); }
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) @Nullable
{ public BlockState getStateForPlacement(BlockPlaceContext context)
BlockState state = super.getStateForPlacement(context); {
return (state==null) ? (state) : (state.setValue(EdRoofBlock.SHAPE, StairsShape.STRAIGHT).setValue(EdRoofBlock.HALF, Half.BOTTOM)); BlockState state = super.getStateForPlacement(context);
} return (state==null) ? (state) : (state.setValue(EdRoofBlock.SHAPE, StairsShape.STRAIGHT).setValue(EdRoofBlock.HALF, Half.BOTTOM));
} }
}

View file

@ -1,71 +1,72 @@
/* /*
* @file EdCornerOrnamentedBlock.java * @file EdCornerOrnamentedBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Block for corner/quoin ornamentation. * Block for corner/quoin ornamentation.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.block.BlockState; import net.minecraft.util.Mth;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.Direction; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.vector.Vector2f; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.World; import net.minecraft.world.phys.Vec2;
import wile.engineersdecor.libmc.detail.Auxiliaries; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import javax.annotation.Nullable;
import java.util.*; import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashSet;
public class EdCornerOrnamentedBlock extends DecorBlock.Directed
{ public class EdCornerOrnamentedBlock extends StandardBlocks.Directed
protected final HashSet<Block> compatible_blocks; {
protected final HashSet<Block> compatible_blocks;
public EdCornerOrnamentedBlock(long config, AbstractBlock.Properties properties, Block[] assigned_wall_blocks)
{ public EdCornerOrnamentedBlock(long config, BlockBehaviour.Properties properties, Block[] assigned_wall_blocks)
super(config, properties, Auxiliaries.getPixeledAABB(0,0,0,16,16,16)); {
compatible_blocks = new HashSet<Block>(Arrays.asList(assigned_wall_blocks)); super(config, properties, Auxiliaries.getPixeledAABB(0,0,0,16,16,16));
} compatible_blocks = new HashSet<>(Arrays.asList(assigned_wall_blocks));
}
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) @Nullable
{ public BlockState getStateForPlacement(BlockPlaceContext context)
final World world = context.getLevel(); {
final BlockPos pos = context.getClickedPos(); final Level world = context.getLevel();
// 1. Placement as below/above for corners, or placement adjacent horizontally if up/down facing. final BlockPos pos = context.getClickedPos();
for(Direction adj: Direction.values()) { // 1. Placement as below/above for corners, or placement adjacent horizontally if up/down facing.
BlockState state = world.getBlockState(pos.relative(adj)); for(Direction adj: Direction.values()) {
if(state.getBlock() != this) continue; BlockState state = world.getBlockState(pos.relative(adj));
Direction facing = state.getValue(FACING); if(state.getBlock() != this) continue;
if(facing.getAxis().isHorizontal() == (adj.getAxis().isVertical())) { Direction facing = state.getValue(FACING);
return super.getStateForPlacement(context).setValue(FACING, state.getValue(FACING)); if(facing.getAxis().isHorizontal() == (adj.getAxis().isVertical())) {
} return super.getStateForPlacement(context).setValue(FACING, state.getValue(FACING));
} }
// 2. By Player look angles with minimum horizontal diagonal deviation. }
{ // 2. By Player look angles with minimum horizontal diagonal deviation.
Direction facing = Direction.WEST; {
final Vector2f look = context.getPlayer().getRotationVector(); Direction facing = Direction.WEST;
final Direction hit_face = context.getClickedFace(); final Vec2 look = context.getPlayer().getRotationVector();
if((context.getClickedFace()==Direction.DOWN) && (look.x <= -60)) { final Direction hit_face = context.getClickedFace();
facing = Direction.DOWN; if((context.getClickedFace()==Direction.DOWN) && (look.x <= -60)) {
} else if((context.getClickedFace()==Direction.UP) && (look.x >= 60)) { facing = Direction.DOWN;
facing = Direction.UP; } else if((context.getClickedFace()==Direction.UP) && (look.x >= 60)) {
} else if(MathHelper.degreesDifferenceAbs(look.y, 45) <= 45) { facing = Direction.UP;
facing = Direction.NORTH; } else if(Mth.degreesDifferenceAbs(look.y, 45) <= 45) {
} else if(MathHelper.degreesDifferenceAbs(look.y, 45+90) <= 45) { facing = Direction.NORTH;
facing = Direction.EAST; } else if(Mth.degreesDifferenceAbs(look.y, 45+90) <= 45) {
} else if(MathHelper.degreesDifferenceAbs(look.y, 45+180) <= 45) { facing = Direction.EAST;
facing = Direction.SOUTH; } else if(Mth.degreesDifferenceAbs(look.y, 45+180) <= 45) {
} facing = Direction.SOUTH;
return super.getStateForPlacement(context).setValue(FACING, facing); }
} return super.getStateForPlacement(context).setValue(FACING, facing);
} }
} }
}

File diff suppressed because it is too large Load diff

View file

@ -1,31 +0,0 @@
/*
* @file EdDoorBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Blocks representing centered doors opening by sliding
* to the sides.
*/
package wile.engineersdecor.blocks;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.block.*;
import wile.engineersdecor.libmc.blocks.StandardDoorBlock;
public class EdDoorBlock extends StandardDoorBlock implements IDecorBlock
{
public EdDoorBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB[] open_aabbs_top, AxisAlignedBB[] open_aabbs_bottom, AxisAlignedBB[] closed_aabbs_top, AxisAlignedBB[] closed_aabbs_bottom, SoundEvent open_sound, SoundEvent close_sound)
{ super(config, properties, open_aabbs_top, open_aabbs_bottom, closed_aabbs_top, closed_aabbs_bottom, open_sound, close_sound); }
public EdDoorBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB open_aabb, AxisAlignedBB closed_aabb, SoundEvent open_sound, SoundEvent close_sound)
{ super(config, properties, open_aabb, closed_aabb, open_sound, close_sound); }
public EdDoorBlock(long config, AbstractBlock.Properties properties, SoundEvent open_sound, SoundEvent close_sound)
{ super(config, properties, open_sound, close_sound); }
public EdDoorBlock(long config, AbstractBlock.Properties properties)
{ super(config, properties); }
}

View file

@ -1,156 +1,165 @@
/* /*
* @file EdDoubleGateBlock.java * @file EdDoubleGateBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Gate blocks that can be one or two segments high. * Gate blocks that can be one or two segments high.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.core.BlockPos;
import net.minecraft.world.IBlockReader; import net.minecraft.core.Direction;
import net.minecraft.world.IWorld; import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.World; import net.minecraft.sounds.SoundSource;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.InteractionHand;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.InteractionResult;
import net.minecraft.state.StateContainer; import net.minecraft.world.entity.player.Player;
import net.minecraft.block.*; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.BlockGetter;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.Level;
import net.minecraft.util.*; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.pathfinding.PathType; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import javax.annotation.Nullable; import net.minecraft.world.level.pathfinder.PathComputationType;
import java.util.ArrayList; import net.minecraft.world.phys.AABB;
import java.util.Arrays; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
public class EdDoubleGateBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock import net.minecraft.world.phys.shapes.VoxelShape;
{ import wile.engineersdecor.libmc.blocks.StandardBlocks;
public static final IntegerProperty SEGMENT = IntegerProperty.create("segment", 0, 1); import wile.engineersdecor.libmc.detail.Auxiliaries;
public static final BooleanProperty OPEN = FenceGateBlock.OPEN;
public static final int SEGMENT_LOWER = 0; import javax.annotation.Nullable;
public static final int SEGMENT_UPPER = 1; import java.util.ArrayList;
protected final ArrayList<VoxelShape> collision_shapes_; import java.util.Arrays;
public EdDoubleGateBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB aabb)
{ this(config, properties, new AxisAlignedBB[]{aabb}); } public class EdDoubleGateBlock extends StandardBlocks.HorizontalWaterLoggable
{
public EdDoubleGateBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB[] aabbs) public static final IntegerProperty SEGMENT = IntegerProperty.create("segment", 0, 1);
{ public static final BooleanProperty OPEN = FenceGateBlock.OPEN;
super(config, properties, aabbs); public static final int SEGMENT_LOWER = 0;
AxisAlignedBB[] caabbs = new AxisAlignedBB[aabbs.length]; public static final int SEGMENT_UPPER = 1;
for(int i=0; i<caabbs.length; ++i) caabbs[i] = aabbs[i].expandTowards(0, 0.5, 0); protected final ArrayList<VoxelShape> collision_shapes_;
collision_shapes_ = new ArrayList<VoxelShape>(Arrays.asList(
VoxelShapes.block(), public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB aabb)
VoxelShapes.block(), { this(config, properties, new AABB[]{aabb}); }
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.NORTH, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.SOUTH, true)), public EdDoubleGateBlock(long config, BlockBehaviour.Properties properties, AABB[] aabbs)
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.WEST, true)), {
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.EAST, true)), super(config, properties, aabbs);
VoxelShapes.block(), AABB[] caabbs = new AABB[aabbs.length];
VoxelShapes.block() for(int i=0; i<caabbs.length; ++i) caabbs[i] = aabbs[i].expandTowards(0, 0.5, 0);
)); collision_shapes_ = new ArrayList<>(Arrays.asList(
} Shapes.block(),
Shapes.block(),
@Override Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.NORTH, true)),
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.SOUTH, true)),
{ return state.getValue(OPEN) ? VoxelShapes.empty() : collision_shapes_.get(state.getValue(HORIZONTAL_FACING).get3DDataValue() & 0x7); } Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.WEST, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.EAST, true)),
@Override Shapes.block(),
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) Shapes.block()
{ super.createBlockStateDefinition(builder); builder.add(SEGMENT).add(OPEN); } ));
}
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
{ return getInitialState(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); } { return state.getValue(OPEN) ? Shapes.empty() : collision_shapes_.get(state.getValue(HORIZONTAL_FACING).get3DDataValue() & 0x7); }
@Override @Override
@SuppressWarnings("deprecation") protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos) { super.createBlockStateDefinition(builder); builder.add(SEGMENT).add(OPEN); }
{ return getInitialState(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); }
@Override
@Override @Nullable
@SuppressWarnings("deprecation") public BlockState getStateForPlacement(BlockPlaceContext context)
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) { return getInitialState(super.getStateForPlacement(context), context.getLevel(), context.getClickedPos()); }
{
if((rayTraceResult.getDirection()==Direction.UP) || (rayTraceResult.getDirection()==Direction.DOWN) && (player.getItemInHand(hand).getItem()==this.asItem())) return ActionResultType.PASS; @Override
if(world.isClientSide()) return ActionResultType.SUCCESS; @SuppressWarnings("deprecation")
final boolean open = !state.getValue(OPEN); public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
world.setBlock(pos, state.setValue(OPEN, open),2|8|16); { return getInitialState(super.updateShape(state, facing, facingState, world, pos, facingPos), world, pos); }
if(state.getValue(SEGMENT) == SEGMENT_UPPER) {
final BlockState adjacent = world.getBlockState(pos.below()); @Override
if(adjacent.getBlock()==this) world.setBlock(pos.below(), adjacent.setValue(OPEN, open), 2|8|16); @SuppressWarnings("deprecation")
} else { public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
final BlockState adjacent = world.getBlockState(pos.above()); {
if(adjacent.getBlock()==this) world.setBlock(pos.above(), adjacent.setValue(OPEN, open), 2|8|16); if((rayTraceResult.getDirection()==Direction.UP) || (rayTraceResult.getDirection()==Direction.DOWN) && (player.getItemInHand(hand).getItem()==this.asItem())) return InteractionResult.PASS;
} if(world.isClientSide()) return InteractionResult.SUCCESS;
world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f); final boolean open = !state.getValue(OPEN);
return ActionResultType.CONSUME; world.setBlock(pos, state.setValue(OPEN, open),2|8|16);
} if(state.getValue(SEGMENT) == SEGMENT_UPPER) {
final BlockState adjacent = world.getBlockState(pos.below());
@Override if(adjacent.getBlock()==this) world.setBlock(pos.below(), adjacent.setValue(OPEN, open), 2|8|16);
@SuppressWarnings("deprecation") } else {
public boolean isPathfindable(BlockState state, IBlockReader world, BlockPos pos, PathType type) final BlockState adjacent = world.getBlockState(pos.above());
{ return state.getValue(OPEN); } if(adjacent.getBlock()==this) world.setBlock(pos.above(), adjacent.setValue(OPEN, open), 2|8|16);
}
@Override world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f);
@SuppressWarnings("deprecation") return InteractionResult.CONSUME;
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) }
{
if(world.isClientSide) return; @Override
boolean powered = false; @SuppressWarnings("deprecation")
BlockState adjacent; public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type)
BlockPos adjacent_pos; { return state.getValue(OPEN); }
if(state.getValue(SEGMENT) == SEGMENT_UPPER) {
adjacent_pos = pos.below(); @Override
adjacent = world.getBlockState(adjacent_pos); @SuppressWarnings("deprecation")
if(adjacent.getBlock()!=this) adjacent = null; public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
if(world.getSignal(pos.above(), Direction.UP) > 0) { {
powered = true; if(world.isClientSide) return;
} else if((adjacent!=null) && (world.hasNeighborSignal(pos.below(2)))) { boolean powered = false;
powered = true; BlockState adjacent;
} BlockPos adjacent_pos;
} else { if(state.getValue(SEGMENT) == SEGMENT_UPPER) {
adjacent_pos = pos.above(); adjacent_pos = pos.below();
adjacent = world.getBlockState(adjacent_pos); adjacent = world.getBlockState(adjacent_pos);
if(adjacent.getBlock()!=this) adjacent = null; if(adjacent.getBlock()!=this) adjacent = null;
if(world.hasNeighborSignal(pos)) { if(world.getSignal(pos.above(), Direction.UP) > 0) {
powered = true; powered = true;
} else if((adjacent!=null) && (world.getSignal(pos.above(2), Direction.UP) > 0)) { } else if((adjacent!=null) && (world.hasNeighborSignal(pos.below(2)))) {
powered = true; powered = true;
} }
} } else {
boolean sound = false; adjacent_pos = pos.above();
if(powered != state.getValue(OPEN)) { adjacent = world.getBlockState(adjacent_pos);
world.setBlock(pos, state.setValue(OPEN, powered), 2|8|16); if(adjacent.getBlock()!=this) adjacent = null;
sound = true; if(world.hasNeighborSignal(pos)) {
} powered = true;
if((adjacent != null) && (powered != adjacent.getValue(OPEN))) { } else if((adjacent!=null) && (world.getSignal(pos.above(2), Direction.UP) > 0)) {
world.setBlock(adjacent_pos, adjacent.setValue(OPEN, powered), 2|8|16); powered = true;
sound = true; }
} }
if(sound) { boolean sound = false;
world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f); if(powered != state.getValue(OPEN)) {
} world.setBlock(pos, state.setValue(OPEN, powered), 2|8|16);
} sound = true;
}
// ------------------------------------------------------------------------------------------------------------------- if((adjacent != null) && (powered != adjacent.getValue(OPEN))) {
world.setBlock(adjacent_pos, adjacent.setValue(OPEN, powered), 2|8|16);
private BlockState getInitialState(BlockState state, IWorld world, BlockPos pos) sound = true;
{ }
final BlockState down = world.getBlockState(pos.below()); if(sound) {
if(down.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_UPPER).setValue(OPEN, down.getValue(OPEN)).setValue(HORIZONTAL_FACING, down.getValue(HORIZONTAL_FACING)); world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f);
final BlockState up = world.getBlockState(pos.above()); }
if(up.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, up.getValue(OPEN)).setValue(HORIZONTAL_FACING, up.getValue(HORIZONTAL_FACING)); }
return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, false);
} // -------------------------------------------------------------------------------------------------------------------
} private BlockState getInitialState(BlockState state, LevelAccessor world, BlockPos pos)
{
final BlockState down = world.getBlockState(pos.below());
if(down.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_UPPER).setValue(OPEN, down.getValue(OPEN)).setValue(HORIZONTAL_FACING, down.getValue(HORIZONTAL_FACING));
final BlockState up = world.getBlockState(pos.above());
if(up.getBlock() == this) return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, up.getValue(OPEN)).setValue(HORIZONTAL_FACING, up.getValue(HORIZONTAL_FACING));
return state.setValue(SEGMENT, SEGMENT_LOWER).setValue(OPEN, false);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,30 @@
/* /*
* @file EdFenceBlock.java * @file EdFenceBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Wall blocks. * Wall blocks.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardFenceBlock; import net.minecraft.core.BlockPos;
import net.minecraft.util.Direction; import net.minecraft.core.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.IWorldReader; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.block.*; import net.minecraft.world.level.block.state.BlockState;
import wile.engineersdecor.libmc.blocks.StandardFenceBlock;
public class EdFenceBlock extends StandardFenceBlock implements IDecorBlock
{ public class EdFenceBlock extends StandardFenceBlock
public EdFenceBlock(long config, AbstractBlock.Properties properties) {
{ super(config, properties); } public EdFenceBlock(long config, BlockBehaviour.Properties properties)
{ super(config, properties); }
public EdFenceBlock(long config, AbstractBlock.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y)
{ super(config, properties, pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y); } public EdFenceBlock(long config, BlockBehaviour.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y)
{ super(config, properties, pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y); }
@Override
protected boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side) @Override
{ return ((facingState.getBlock()) instanceof EdDoubleGateBlock) || super.attachesTo(facingState, world, facingPos, side); } protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side)
} { return ((facingState.getBlock()) instanceof EdDoubleGateBlock) || super.attachesTo(facingState, world, facingPos, side); }
}

View file

@ -1,72 +1,73 @@
/* /*
* @file EdFloorGratingBlock.java * @file EdFloorGratingBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Floor gratings. * Floor gratings.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.BlockState; import net.minecraft.util.Mth;
import net.minecraft.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.world.entity.EntityType;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.entity.item.ItemEntity; import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.Level;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.World; import net.minecraft.world.phys.AABB;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import javax.annotation.Nullable;
import javax.annotation.Nullable;
public class EdFloorGratingBlock extends DecorBlock.WaterLoggable implements IDecorBlock
{ public class EdFloorGratingBlock extends StandardBlocks.WaterLoggable
public EdFloorGratingBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABB) {
{ super(config, builder, unrotatedAABB); } public EdFloorGratingBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public RenderTypeHint getRenderTypeHint() @Override
{ return RenderTypeHint.CUTOUT; } public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) @Override
{ return true; } public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
{ return true; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) @Override
{ return false; } public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
@SuppressWarnings("deprecation") @Override
public void entityInside(BlockState state, World world, BlockPos pos, Entity entity) @SuppressWarnings("deprecation")
{ public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity)
if(!(entity instanceof ItemEntity)) return; {
final boolean colliding = ((entity.position().y-pos.getY()) > 0.7); if(!(entity instanceof ItemEntity)) return;
if(colliding || (entity.getDeltaMovement().y() > 0)) { final boolean colliding = ((entity.position().y-pos.getY()) > 0.7);
double x = pos.getX() + 0.5; if(colliding || (entity.getDeltaMovement().y() > 0)) {
double y = MathHelper.clamp(entity.position().y-0.3, pos.getY(), pos.getY()+0.6); double x = pos.getX() + 0.5;
double z = pos.getZ() + 0.5; double y = Mth.clamp(entity.position().y-0.3, pos.getY(), pos.getY()+0.6);
double vx = entity.getDeltaMovement().x(); double z = pos.getZ() + 0.5;
double vy = entity.getDeltaMovement().y(); double vx = entity.getDeltaMovement().x();
double vz = entity.getDeltaMovement().z(); double vy = entity.getDeltaMovement().y();
if(colliding) { double vz = entity.getDeltaMovement().z();
vx = 0; if(colliding) {
vy = -0.3; vx = 0;
vz = 0; vy = -0.3;
if((entity.position().y-pos.getY()) > 0.8) y = pos.getY() + 0.6; vz = 0;
entity.xo = x+0.1; if((entity.position().y-pos.getY()) > 0.8) y = pos.getY() + 0.6;
entity.yo = y+0.1; entity.xo = x+0.1;
entity.zo = z+0.1; entity.yo = y+0.1;
} entity.zo = z+0.1;
vy = MathHelper.clamp(vy, -0.3, 0); }
entity.setDeltaMovement(vx, vy, vz); vy = Mth.clamp(vy, -0.3, 0);
entity.fallDistance = 0; entity.setDeltaMovement(vx, vy, vz);
entity.teleportTo(x,y,z); entity.fallDistance = 0;
} entity.teleportTo(x,y,z);
} }
}
}
}

View file

@ -1,428 +1,399 @@
/* /*
* @file EdFluidBarrel.java * @file EdFluidBarrel.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Simple fluid tank with a built-in gauge. * Simple fluid tank with a built-in gauge.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.fluid.Fluid; import net.minecraft.core.BlockPos;
import net.minecraft.fluid.Fluids; import net.minecraft.core.Direction;
import net.minecraft.util.*; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.IWorldReader; import net.minecraft.network.chat.Component;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.entity.LivingEntity; import net.minecraft.sounds.SoundEvent;
import net.minecraft.item.BlockItem; import net.minecraft.sounds.SoundEvents;
import net.minecraft.item.Item; import net.minecraft.sounds.SoundSource;
import net.minecraft.state.IntegerProperty; import net.minecraft.util.Mth;
import net.minecraft.state.StateContainer; import net.minecraft.util.Tuple;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.InteractionHand;
import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.InteractionResult;
import net.minecraft.world.World; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.IBlockReader; import net.minecraft.world.entity.player.Player;
import net.minecraft.block.AbstractBlock; import net.minecraft.world.item.BlockItem;
import net.minecraft.block.Block; import net.minecraft.world.item.Item;
import net.minecraft.block.BlockState; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.ItemStack; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.level.BlockGetter;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.Level;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.LevelReader;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.util.Constants; import net.minecraft.world.level.material.Fluids;
import net.minecraftforge.common.util.LazyOptional; import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.common.util.LazyOptional;
import wile.engineersdecor.ModConfig; import net.minecraftforge.fluids.FluidStack;
import wile.engineersdecor.ModContent; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import wile.engineersdecor.libmc.blocks.StandardBlocks; import net.minecraftforge.fluids.capability.IFluidHandler;
import wile.engineersdecor.libmc.detail.Auxiliaries; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.libmc.detail.Fluidics; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.detail.Overlay; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.detail.Auxiliaries;
import java.util.ArrayList; import wile.engineersdecor.libmc.detail.Fluidics;
import java.util.List; import wile.engineersdecor.libmc.detail.Overlay;
import javax.annotation.Nullable;
import java.util.ArrayList;
public class EdFluidBarrel import java.util.List;
{
//--------------------------------------------------------------------------------------------------------------------
// Config
//-------------------------------------------------------------------------------------------------------------------- public class EdFluidBarrel
{
private static int capacity_ = 12000; //--------------------------------------------------------------------------------------------------------------------
private static int item_fluid_handler_transfer_rate_ = 1000; // Config
private static int tile_fluid_handler_transfer_rate_ = 1000; //--------------------------------------------------------------------------------------------------------------------
public static void on_config(int tank_capacity, int transfer_rate) private static int capacity_ = 12000;
{ private static int item_fluid_handler_transfer_rate_ = 1000;
capacity_ = MathHelper.clamp(tank_capacity, 2000, 64000); private static int tile_fluid_handler_transfer_rate_ = 1000;
tile_fluid_handler_transfer_rate_ = MathHelper.clamp(tank_capacity, 50, 4096);
item_fluid_handler_transfer_rate_ = tile_fluid_handler_transfer_rate_; public static void on_config(int tank_capacity, int transfer_rate)
ModConfig.log("Config fluid barrel: capacity:" + capacity_ + "mb, transfer-rate:" + tile_fluid_handler_transfer_rate_ + "mb/t."); {
} capacity_ = Mth.clamp(tank_capacity, 2000, 64000);
tile_fluid_handler_transfer_rate_ = Mth.clamp(tank_capacity, 50, 4096);
//-------------------------------------------------------------------------------------------------------------------- item_fluid_handler_transfer_rate_ = tile_fluid_handler_transfer_rate_;
// Block ModConfig.log("Config fluid barrel: capacity:" + capacity_ + "mb, transfer-rate:" + tile_fluid_handler_transfer_rate_ + "mb/t.");
//-------------------------------------------------------------------------------------------------------------------- }
public static class FluidBarrelBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock, StandardBlocks.IBlockItemFactory //--------------------------------------------------------------------------------------------------------------------
{ // Block
public static final int FILL_LEVEL_MAX = 4; //--------------------------------------------------------------------------------------------------------------------
public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
public static class FluidBarrelBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock<FluidBarrelTileEntity>, StandardBlocks.IBlockItemFactory
public FluidBarrelBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABB) {
{ public static final int FILL_LEVEL_MAX = 4;
super(config, builder, unrotatedAABB); public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
registerDefaultState(super.defaultBlockState().setValue(FACING, Direction.UP).setValue(FILL_LEVEL, 0));
} public FluidBarrelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB)
{
// IBlockItemFactory ---------------------------------------------------------------------------- super(config, builder, unrotatedAABB);
registerDefaultState(super.defaultBlockState().setValue(FACING, Direction.UP).setValue(FILL_LEVEL, 0));
@Override }
public BlockItem getBlockItem(Block block, Item.Properties builder)
{ return new FluidBarrelItem(block, builder); } @Nullable
@Override
// IStandardBlock -------------------------------------------------------------------------------- public BlockEntityType<EdFluidBarrel.FluidBarrelTileEntity> getBlockEntityType()
{ return ModContent.TET_FLUID_BARREL; }
@Override
public boolean hasDynamicDropList() @Override
{ return true; } public BlockItem getBlockItem(Block block, Item.Properties builder)
{ return new FluidBarrelItem(block, builder); }
@Override
public List<ItemStack> dropList(BlockState state, World world, final TileEntity te, boolean explosion) @Override
{ public boolean hasDynamicDropList()
final List<ItemStack> stacks = new ArrayList<ItemStack>(); { return true; }
if(world.isClientSide) return stacks;
if(!(te instanceof FluidBarrelTileEntity)) return stacks; @Override
ItemStack stack = new ItemStack(this, 1); public List<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion)
CompoundNBT te_nbt = ((FluidBarrelTileEntity) te).clear_getnbt(); {
if(!te_nbt.isEmpty()) { final List<ItemStack> stacks = new ArrayList<>();
CompoundNBT nbt = new CompoundNBT(); if(world.isClientSide) return stacks;
nbt.put("tedata", te_nbt); if(!(te instanceof FluidBarrelTileEntity)) return stacks;
stack.setTag(nbt); ItemStack stack = new ItemStack(this, 1);
} CompoundTag te_nbt = ((FluidBarrelTileEntity) te).clear_getnbt();
stacks.add(stack); if(!te_nbt.isEmpty()) {
return stacks; CompoundTag nbt = new CompoundTag();
} nbt.put("tedata", te_nbt);
stack.setTag(nbt);
// Block/IForgeBlock ----------------------------------------------------------------------------- }
stacks.add(stack);
@Override return stacks;
@OnlyIn(Dist.CLIENT) }
public void appendHoverText(final ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ @Override
if( @OnlyIn(Dist.CLIENT)
(!(stack.getItem() instanceof FluidBarrelItem)) || public void appendHoverText(final ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
(Auxiliaries.Tooltip.helpCondition()) {
) { if((!(stack.getItem() instanceof FluidBarrelItem)) || (Auxiliaries.Tooltip.helpCondition())) {
super.appendHoverText(stack, world, tooltip, flag); return; super.appendHoverText(stack, world, tooltip, flag); return;
} }
FluidStack fs = FluidBarrelItem.getFluid(stack); FluidStack fs = FluidBarrelItem.getFluid(stack);
if(!fs.isEmpty()) { if(!fs.isEmpty()) {
tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip", new Object[] { tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip", Integer.toString(fs.getAmount()), Integer.toString(capacity_), new TranslatableComponent(fs.getTranslationKey())));
Integer.toString(fs.getAmount()), } else {
Integer.toString(capacity_), tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip.empty", "0", Integer.toString(capacity_)));
new TranslationTextComponent(fs.getTranslationKey()) }
})); if(!Auxiliaries.Tooltip.extendedTipCondition()) {
} else { super.appendHoverText(stack, world, tooltip, flag);
tooltip.add(Auxiliaries.localizable(getDescriptionId()+".status.tip.empty", new Object[] { }
"0", }
Integer.toString(capacity_),
})); @Override
} protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
if(!Auxiliaries.Tooltip.extendedTipCondition()) { { super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); }
super.appendHoverText(stack, world, tooltip, flag);
} @Override
} @Nullable
public BlockState getStateForPlacement(BlockPlaceContext context)
@Override {
public boolean hasTileEntity(BlockState state) BlockState state = super.getStateForPlacement(context);
{ return true; } if(!context.getPlayer().isShiftKeyDown()) state = state.setValue(FACING, Direction.UP);
return state;
@Override }
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world) @Override
{ return new EdFluidBarrel.FluidBarrelTileEntity(); } public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
@Override if(world.isClientSide) return;
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
{ super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); } CompoundTag te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
@Override final BlockEntity te = world.getBlockEntity(pos);
@Nullable if(!(te instanceof FluidBarrelTileEntity)) return;
public BlockState getStateForPlacement(BlockItemUseContext context) ((FluidBarrelTileEntity)te).readnbt(te_nbt);
{ te.setChanged();
BlockState state = super.getStateForPlacement(context); world.getBlockTicks().scheduleTick(pos, this, 4);
if(!context.getPlayer().isShiftKeyDown()) state = state.setValue(FACING, Direction.UP); }
return state;
} @Override
@SuppressWarnings("deprecation")
@Override public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
public void setPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
{ if(player.getItemInHand(hand).getItem() == asItem()) return InteractionResult.PASS; // Pass that to block placement.
if(world.isClientSide) return; if(world.isClientSide()) return InteractionResult.SUCCESS;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; if(!(world.getBlockEntity(pos) instanceof final FluidBarrelTileEntity te)) return InteractionResult.FAIL;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata"); if(!te.handlePlayerInteraction(state, world, pos, player, hand)) return InteractionResult.PASS;
if(te_nbt.isEmpty()) return; world.getBlockTicks().scheduleTick(pos, this, 4);
final TileEntity te = world.getBlockEntity(pos); return InteractionResult.CONSUME;
if(!(te instanceof FluidBarrelTileEntity)) return; }
((FluidBarrelTileEntity)te).readnbt(te_nbt);
((FluidBarrelTileEntity)te).setChanged(); @Override
world.getBlockTicks().scheduleTick(pos, this, 4); @SuppressWarnings("deprecation")
} public boolean hasAnalogOutputSignal(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation") @Override
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) @SuppressWarnings("deprecation")
{ public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos)
if(player.getItemInHand(hand).getItem() == asItem()) return ActionResultType.PASS; // Pass that to block placement. {
if(world.isClientSide()) return ActionResultType.SUCCESS; BlockEntity te = world.getBlockEntity(pos);
TileEntity te = world.getBlockEntity(pos); if(!(te instanceof FluidBarrelTileEntity)) return 0;
if(!(te instanceof FluidBarrelTileEntity)) return ActionResultType.FAIL; return (int)Mth.clamp(((FluidBarrelTileEntity)te).getNormalizedFillLevel() * 15, 0, 15);
if(!((FluidBarrelTileEntity)te).handlePlayerInteraction(state, world, pos, player, hand)) return ActionResultType.PASS; }
world.getBlockTicks().scheduleTick(pos, this, 4);
return ActionResultType.CONSUME; @Override
} public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
{ return false; }
@Override
@SuppressWarnings("deprecation") }
public boolean hasAnalogOutputSignal(BlockState state)
{ return true; } //--------------------------------------------------------------------------------------------------------------------
// Tile entity
@Override //--------------------------------------------------------------------------------------------------------------------
@SuppressWarnings("deprecation")
public int getAnalogOutputSignal(BlockState state, World world, BlockPos pos) public static class FluidBarrelTileEntity extends StandardEntityBlocks.StandardBlockEntity implements ICapabilityProvider
{ {
TileEntity te = world.getBlockEntity(pos); private final int TICK_INTERVAL = 10;
if(!(te instanceof FluidBarrelTileEntity)) return 0; private int tick_timer_ = 0;
return (int)MathHelper.clamp(((FluidBarrelTileEntity)te).getNormalizedFillLevel() * 15, 0, 15); private final Fluidics.Tank tank_ = (new Fluidics.Tank(capacity_)).setInteractionNotifier((t,d)->on_tank_changed());
} private final LazyOptional<IFluidHandler> fluid_handler_ = tank_.createFluidHandler();
@Override public FluidBarrelTileEntity(BlockPos pos, BlockState state)
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) { super(ModContent.TET_FLUID_BARREL, pos, state); }
{ return false; }
public void readnbt(CompoundTag nbt)
} { tank_.load(nbt); }
//-------------------------------------------------------------------------------------------------------------------- public CompoundTag writenbt(CompoundTag nbt)
// Tile entity { tank_.save(nbt); return nbt; }
//--------------------------------------------------------------------------------------------------------------------
public boolean handlePlayerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand)
public static class FluidBarrelTileEntity extends TileEntity implements ICapabilityProvider, ITickableTileEntity {
{ if(world.isClientSide()) return false;
private final int TICK_INTERVAL = 10; {
private int tick_timer_ = 0; Tuple<Fluid,Integer> transferred = Fluidics.manualTrackedFluidHandlerInteraction(world, pos, null, player, hand);
private final Fluidics.Tank tank_; if(transferred==null) {
private final LazyOptional<IFluidHandler> fluid_handler_; world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f);
} else if(transferred.getB() > 0) {
public FluidBarrelTileEntity() SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA: SoundEvents.BUCKET_EMPTY;
{ this(ModContent.TET_FLUID_BARREL); } world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f);
} else {
public FluidBarrelTileEntity(TileEntityType<?> te_type) SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_FILL_LAVA : SoundEvents.BUCKET_FILL;
{ world.playSound(null, pos, se, SoundSource.BLOCKS, 1f, 1f);
super(te_type); }
tank_ = new Fluidics.Tank(capacity_); }
tank_.setInteractionNotifier((t,d)->on_tank_changed()); {
fluid_handler_ = tank_.createFluidHandler(); int vol = tank_.getFluidAmount();
} int cap = tank_.getCapacity();
String name = (new TranslatableComponent(tank_.getFluid().getTranslationKey())).getString();
public void readnbt(CompoundNBT nbt) if((vol>0) && (cap>0)) {
{ tank_.load(nbt); } Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status", Integer.toString(vol), Integer.toString(cap), name));
} else {
public CompoundNBT writenbt(CompoundNBT nbt) Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status.empty", Integer.toString(vol), Integer.toString(cap)));
{ tank_.save(nbt); return nbt; } }
}
public boolean handlePlayerInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand) return true;
{ }
if(world.isClientSide()) return false;
{ public double getNormalizedFillLevel()
Tuple<Fluid,Integer> transferred = Fluidics.manualTrackedFluidHandlerInteraction(world, pos, null, player, hand); { return (tank_.isEmpty()) ? (0) : ((double)tank_.getFluidAmount()/(double)tank_.getCapacity()); }
if(transferred==null) {
world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundCategory.BLOCKS, 0.2f, 0.02f); protected void on_tank_changed()
} else if(transferred.getB() > 0) { { if(tick_timer_ > 2) tick_timer_ = 2; }
SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA: SoundEvents.BUCKET_EMPTY;
world.playSound(null, pos, se, SoundCategory.BLOCKS, 1f, 1f); // BlockEntity ------------------------------------------------------------------------------
} else {
SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.BUCKET_FILL_LAVA : SoundEvents.BUCKET_FILL; @Override
world.playSound(null, pos, se, SoundCategory.BLOCKS, 1f, 1f); public void load(CompoundTag nbt)
} { super.load(nbt); readnbt(nbt); }
}
{ @Override
int vol = tank_.getFluidAmount(); public CompoundTag save(CompoundTag nbt)
int cap = tank_.getCapacity(); { super.save(nbt); return writenbt(nbt); }
String name = (new TranslationTextComponent(tank_.getFluid().getTranslationKey())).getString();
if((vol>0) && (cap>0)) { @Override
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status", new Object[]{ public void setRemoved()
Integer.toString(vol), Integer.toString(cap), name { super.setRemoved(); fluid_handler_.invalidate(); }
}));
} else { public CompoundTag clear_getnbt()
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status.empty", new Object[]{ { return tank_.save(new CompoundTag()); }
Integer.toString(vol), Integer.toString(cap)
})); // ICapabilityProvider --------------------------------------------------------------------
}
} @Override
return true; public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
} {
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
public double getNormalizedFillLevel() return super.getCapability(capability, facing);
{ return (tank_.isEmpty()) ? (0) : ((double)tank_.getFluidAmount()/(double)tank_.getCapacity()); } }
protected void on_tank_changed() // Tick --------------------------------------------------------------------
{ if(tick_timer_ > 2) tick_timer_ = 2; }
private boolean transfer_down()
// TileEntity ------------------------------------------------------------------------------ {
if(tank_.isEmpty()) return false;
@Override final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP);
public void load(BlockState state, CompoundNBT nbt) if(fh==null) return false;
{ super.load(state, nbt); readnbt(nbt); } final FluidStack fs = tank_.getFluid().copy();
if(fs.getAmount() > tile_fluid_handler_transfer_rate_) fs.setAmount(tile_fluid_handler_transfer_rate_);
@Override final int nfilled = fh.fill(fs, IFluidHandler.FluidAction.EXECUTE);
public CompoundNBT save(CompoundNBT nbt) if(nfilled <= 0) return false;
{ super.save(nbt); return writenbt(nbt); } tank_.drain(nfilled, IFluidHandler.FluidAction.EXECUTE);
return true;
@Override }
public void setRemoved()
{ super.setRemoved(); fluid_handler_.invalidate(); } public void tick()
{
public CompoundNBT clear_getnbt() if((level.isClientSide()) || (--tick_timer_>=0)) return;
{ return tank_.save(new CompoundNBT()); } tick_timer_ = TICK_INTERVAL;
final BlockState state = getBlockState();
// ICapabilityProvider -------------------------------------------------------------------- final Block block = state.getBlock();
if(!(block instanceof FluidBarrelBlock)) return;
@Override if(state.getValue(FluidBarrelBlock.FACING).getAxis().isVertical()) transfer_down(); // tick_timer_ ==> 1 if something was transferred, otherwise no need to waste CPU
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) double norm_level = getNormalizedFillLevel();
{ int fill_level = (norm_level <= 0) ? 0 : ((int)Mth.clamp((norm_level * FluidBarrelBlock.FILL_LEVEL_MAX)+0.5, 1, FluidBarrelBlock.FILL_LEVEL_MAX));
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast(); if(fill_level != state.getValue(FluidBarrelBlock.FILL_LEVEL)) {
return super.getCapability(capability, facing); level.setBlock(worldPosition, state.setValue(FluidBarrelBlock.FILL_LEVEL, fill_level), 2);
} level.updateNeighborsAt(worldPosition, block);
}
// ITickableTileEntity -------------------------------------------------------------------- }
private boolean transfer_down() }
{
if(tank_.isEmpty()) return false; //--------------------------------------------------------------------------------------------------------------------
final IFluidHandler fh = FluidUtil.getFluidHandler(level, worldPosition.below(), Direction.UP).orElse(null); // Block item
if(fh==null) return false; //--------------------------------------------------------------------------------------------------------------------
final FluidStack fs = tank_.getFluid().copy();
if(fs.getAmount() > tile_fluid_handler_transfer_rate_) fs.setAmount(tile_fluid_handler_transfer_rate_); public static class FluidBarrelItem extends BlockItem
final int nfilled = fh.fill(fs, FluidAction.EXECUTE); {
if(nfilled <= 0) return false; public FluidBarrelItem(Block block, Item.Properties builder)
tank_.drain(nfilled, FluidAction.EXECUTE); { super(block, builder); }
return true;
} private static CompoundTag read_fluid_nbt(ItemStack stack)
{
public void tick() if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return new CompoundTag();
{ final CompoundTag nbt = stack.getTag().getCompound("tedata");
if((level.isClientSide()) || (--tick_timer_>=0)) return; if(!nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) return new CompoundTag();
tick_timer_ = TICK_INTERVAL; return nbt.getCompound("tank");
final BlockState state = getBlockState(); }
final Block block = state.getBlock();
if(!(block instanceof FluidBarrelBlock)) return; private static void write_fluid_nbt(ItemStack stack, CompoundTag fluid_nbt)
if(state.getValue(FluidBarrelBlock.FACING).getAxis().isVertical()) transfer_down(); // tick_timer_ ==> 1 if something was transferred, otherwise no need to waste CPU {
double norm_level = getNormalizedFillLevel(); if((fluid_nbt==null) || (fluid_nbt.isEmpty())) {
int fill_level = (norm_level <= 0) ? 0 : ((int)MathHelper.clamp((norm_level * FluidBarrelBlock.FILL_LEVEL_MAX)+0.5, 1, FluidBarrelBlock.FILL_LEVEL_MAX)); if((!stack.hasTag()) || (!stack.getTag().contains("tedata", Constants.NBT.TAG_COMPOUND))) return;
if(fill_level != state.getValue(FluidBarrelBlock.FILL_LEVEL)) { final CompoundTag tag = stack.getTag();
level.setBlock(worldPosition, state.setValue(FluidBarrelBlock.FILL_LEVEL, fill_level), 2); final CompoundTag tedata = tag.getCompound("tedata");
level.updateNeighborsAt(worldPosition, block); if(tedata.contains("tank")) tedata.remove("tank");
} if(tedata.isEmpty()) tag.remove("tedata");
} stack.setTag(tag.isEmpty() ? null : tag);
} else {
} CompoundTag tag = stack.getTag();
if(tag==null) tag = new CompoundTag();
//-------------------------------------------------------------------------------------------------------------------- CompoundTag tedata = tag.getCompound("tedata");
// Block item if(tedata==null) tedata = new CompoundTag();
//-------------------------------------------------------------------------------------------------------------------- tedata.put("tank", fluid_nbt);
tag.put("tedata", tedata);
public static class FluidBarrelItem extends BlockItem stack.setTag(tag);
{ }
public FluidBarrelItem(Block block, Item.Properties builder) }
{ super(block, builder); }
public static FluidStack getFluid(ItemStack stack)
private static CompoundNBT read_fluid_nbt(ItemStack stack) {
{ final CompoundTag nbt = read_fluid_nbt(stack);
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return new CompoundNBT(); return (nbt.isEmpty()) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt));
final CompoundNBT nbt = stack.getTag().getCompound("tedata"); }
if(!nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) return new CompoundNBT();
return nbt.getCompound("tank"); public static ItemStack setFluid(ItemStack stack, FluidStack fs)
} { write_fluid_nbt(stack, fs.writeToNBT(new CompoundTag())); return stack; }
private static void write_fluid_nbt(ItemStack stack, CompoundNBT fluid_nbt) @Override
{ public int getItemStackLimit(ItemStack stack)
if((fluid_nbt==null) || (fluid_nbt.isEmpty())) { { return (!getFluid(stack).isEmpty()) ? 1 : super.getItemStackLimit(stack); }
if((!stack.hasTag()) || (!stack.getTag().contains("tedata", Constants.NBT.TAG_COMPOUND))) return;
final CompoundNBT tag = stack.getTag(); @Override
final CompoundNBT tedata = tag.getCompound("tedata"); public boolean showDurabilityBar(ItemStack stack)
if(tedata.contains("tank")) tedata.remove("tank"); { return (!getFluid(stack).isEmpty()); }
if(tedata.isEmpty()) tag.remove("tedata");
stack.setTag(tag.isEmpty() ? null : tag); @Override
} else { public double getDurabilityForDisplay(ItemStack stack)
CompoundNBT tag = stack.getTag(); { return 1.0 - Mth.clamp(((double)(getFluid(stack).getAmount()))/((double)capacity_), 0.0, 1.0); }
if(tag==null) tag = new CompoundNBT();
CompoundNBT tedata = tag.getCompound("tedata"); @Override
if(tedata==null) tedata = new CompoundNBT(); public int getRGBDurabilityForDisplay(ItemStack stack)
tedata.put("tank", fluid_nbt); { return 0x336633; }
tag.put("tedata", tedata);
stack.setTag(tag); @Override
} public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt)
} { return new Fluidics.FluidContainerItemCapabilityWrapper(stack, capacity_, item_fluid_handler_transfer_rate_, FluidBarrelItem::read_fluid_nbt, FluidBarrelItem::write_fluid_nbt, e->true); }
public static FluidStack getFluid(ItemStack stack) @Override
{ public boolean hasContainerItem(ItemStack stack)
final CompoundNBT nbt = read_fluid_nbt(stack); { return (stack.getCount()==1) && (!getFluid(stack).isEmpty()); }
return (nbt.isEmpty()) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt));
} @Override
public ItemStack getContainerItem(ItemStack stack)
public static ItemStack setFluid(ItemStack stack, FluidStack fs) {
{ write_fluid_nbt(stack, fs.writeToNBT(new CompoundNBT())); return stack; } if(stack.getCount()!=1) return ItemStack.EMPTY;
FluidStack fs = getFluid(stack);
@Override if(fs.getAmount() > 1000) fs.shrink(1000); else fs = FluidStack.EMPTY;
public int getItemStackLimit(ItemStack stack) return setFluid(stack, fs);
{ return (!getFluid(stack).isEmpty()) ? 1 : super.getItemStackLimit(stack); } }
}
@Override
public boolean showDurabilityBar(ItemStack stack) }
{ return (!getFluid(stack).isEmpty()); }
@Override
public double getDurabilityForDisplay(ItemStack stack)
{ return 1.0 - MathHelper.clamp(((double)(getFluid(stack).getAmount()))/((double)capacity_), 0.0, 1.0); }
@Override
public int getRGBDurabilityForDisplay(ItemStack stack)
{ return 0x336633; }
@Override
public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundNBT nbt)
{ return new Fluidics.FluidContainerItemCapabilityWrapper(stack, capacity_, item_fluid_handler_transfer_rate_, (s)->read_fluid_nbt(s), (s,n)->write_fluid_nbt(s,n), e->true); }
@Override
public boolean hasContainerItem(ItemStack stack)
{ return (stack.getCount()==1) && (!getFluid(stack).isEmpty()); }
@Override
public ItemStack getContainerItem(ItemStack stack)
{
if(stack.getCount()!=1) return ItemStack.EMPTY;
FluidStack fs = getFluid(stack);
if(fs.getAmount() > 1000) fs.shrink(1000); else fs = FluidStack.EMPTY;
return setFluid(stack, fs);
}
}
}

View file

@ -1,416 +1,400 @@
/* /*
* @file EdFluidFunnel.java * @file EdFluidFunnel.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* A device that collects and stores fluid blocks above it. * A device that collects and stores fluid blocks above it.
* Tracks flowing fluid to their source blocks. Compatible * Tracks flowing fluid to their source blocks. Compatible
* with vanilla infinite water source. * with vanilla infinite water source.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.core.BlockPos;
import net.minecraft.world.IWorldReader; import net.minecraft.core.Direction;
import net.minecraft.block.*; import net.minecraft.core.Vec3i;
import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.CompoundTag;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.Mth;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.InteractionHand;
import net.minecraft.item.ItemStack; import net.minecraft.world.InteractionResult;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.entity.player.Player;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.ItemStack;
import net.minecraft.fluid.Fluid; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.fluid.Fluids; import net.minecraft.world.level.Level;
import net.minecraft.fluid.FluidState; import net.minecraft.world.level.LevelReader;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.block.Blocks;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.ActionResultType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.Direction; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.Hand; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.*; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.vector.*; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.World; import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraft.world.level.material.FluidState;
import net.minecraftforge.common.util.LazyOptional; import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.*; import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import wile.engineersdecor.ModConfig; import net.minecraftforge.fluids.capability.IFluidHandler;
import wile.engineersdecor.ModContent; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.libmc.detail.Fluidics; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import java.util.*; import wile.engineersdecor.libmc.detail.Fluidics;
import javax.annotation.Nullable;
public class EdFluidFunnel import java.util.*;
{
private static boolean with_device_fluid_handler_collection = false; public class EdFluidFunnel
{
public static void on_config(boolean with_tank_fluid_collection)
{ private static boolean with_device_fluid_handler_collection = false;
with_device_fluid_handler_collection = with_tank_fluid_collection;
ModConfig.log("Config fluid funnel: tank-fluid-collection:" + with_device_fluid_handler_collection + "."); public static void on_config(boolean with_tank_fluid_collection)
} {
with_device_fluid_handler_collection = with_tank_fluid_collection;
//-------------------------------------------------------------------------------------------------------------------- ModConfig.log("Config fluid funnel: tank-fluid-collection:" + with_device_fluid_handler_collection + ".");
// Block }
//--------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------
public static class FluidFunnelBlock extends DecorBlock.Cutout implements IDecorBlock // Block
{ //--------------------------------------------------------------------------------------------------------------------
public static final int FILL_LEVEL_MAX = 3;
public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX); public static class FluidFunnelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock<FluidFunnelTileEntity>
{
public FluidFunnelBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABB) public static final int FILL_LEVEL_MAX = 3;
{ super(config, builder, unrotatedAABB); } public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
@Override public FluidFunnelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB)
public RenderTypeHint getRenderTypeHint() { super(config, builder, unrotatedAABB); }
{ return RenderTypeHint.CUTOUT; }
@Nullable
@Override @Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) public BlockEntityType<EdFluidFunnel.FluidFunnelTileEntity> getBlockEntityType()
{ super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); } { return ModContent.TET_SMALL_FLUID_FUNNEL; }
@Override @Override
@Nullable public RenderTypeHint getRenderTypeHint()
public BlockState getStateForPlacement(BlockItemUseContext context) { return RenderTypeHint.CUTOUT; }
{ return super.getStateForPlacement(context).setValue(FILL_LEVEL, 0); }
@Override
@Override protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
public boolean hasTileEntity(BlockState state) { super.createBlockStateDefinition(builder); builder.add(FILL_LEVEL); }
{ return true; }
@Override
@Override @Nullable
@Nullable public BlockState getStateForPlacement(BlockPlaceContext context)
public TileEntity createTileEntity(BlockState state, IBlockReader world) { return super.getStateForPlacement(context).setValue(FILL_LEVEL, 0); }
{ return new FluidFunnelTileEntity(); }
@Override
@Override @SuppressWarnings("deprecation")
@SuppressWarnings("deprecation") public boolean hasAnalogOutputSignal(BlockState state)
public boolean hasAnalogOutputSignal(BlockState state) { return true; }
{ return true; }
@Override
@Override @SuppressWarnings("deprecation")
@SuppressWarnings("deprecation") public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos)
public int getAnalogOutputSignal(BlockState state, World world, BlockPos pos) { return Mth.clamp((state.getValue(FILL_LEVEL)*5), 0, 15); }
{ return MathHelper.clamp((state.getValue(FILL_LEVEL)*5), 0, 15); }
@Override
@Override public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
public void setPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
{ if(world.isClientSide) return;
if(world.isClientSide) return; if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return; CompoundTag te_nbt = stack.getTag().getCompound("tedata");
CompoundNBT te_nbt = stack.getTag().getCompound("tedata"); if(te_nbt.isEmpty()) return;
if(te_nbt.isEmpty()) return; final BlockEntity te = world.getBlockEntity(pos);
final TileEntity te = world.getBlockEntity(pos); if(!(te instanceof FluidFunnelTileEntity)) return;
if(!(te instanceof FluidFunnelTileEntity)) return; ((FluidFunnelTileEntity)te).readnbt(te_nbt);
((FluidFunnelTileEntity)te).readnbt(te_nbt); te.setChanged();
((FluidFunnelTileEntity)te).setChanged(); world.setBlockAndUpdate(pos, state.setValue(FILL_LEVEL, 0));
world.setBlockAndUpdate(pos, state.setValue(FILL_LEVEL, 0)); }
}
@Override
@Override public boolean hasDynamicDropList()
public boolean hasDynamicDropList() { return true; }
{ return true; }
@Override
@Override public List<ItemStack> dropList(BlockState state, Level world, final BlockEntity te, boolean explosion)
public List<ItemStack> dropList(BlockState state, World world, final TileEntity te, boolean explosion) {
{ final List<ItemStack> stacks = new ArrayList<>();
final List<ItemStack> stacks = new ArrayList<ItemStack>(); if(world.isClientSide) return stacks;
if(world.isClientSide) return stacks; if(!(te instanceof FluidFunnelTileEntity)) return stacks;
if(!(te instanceof FluidFunnelTileEntity)) return stacks; if(!explosion) {
if(!explosion) { ItemStack stack = new ItemStack(this, 1);
ItemStack stack = new ItemStack(this, 1); CompoundTag te_nbt = new CompoundTag();
CompoundNBT te_nbt = new CompoundNBT(); ((FluidFunnelTileEntity)te).writenbt(te_nbt);
((FluidFunnelTileEntity)te).writenbt(te_nbt); if(!te_nbt.isEmpty()) {
if(!te_nbt.isEmpty()) { CompoundTag nbt = new CompoundTag();
CompoundNBT nbt = new CompoundNBT(); nbt.put("tedata", te_nbt);
nbt.put("tedata", te_nbt); stack.setTag(nbt);
stack.setTag(nbt); }
} stacks.add(stack);
stacks.add(stack); } else {
} else { stacks.add(new ItemStack(this, 1));
stacks.add(new ItemStack(this, 1)); }
} return stacks;
return stacks; }
}
@Override
@Override @SuppressWarnings("deprecation")
@SuppressWarnings("deprecation") public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) {
{ if(world.isClientSide) return InteractionResult.SUCCESS;
if(world.isClientSide) return ActionResultType.SUCCESS; return (world.getBlockEntity(pos) instanceof FluidFunnelTileEntity) && Fluidics.manualFluidHandlerInteraction(player, hand, world, pos, rayTraceResult.getDirection()) ? InteractionResult.CONSUME : InteractionResult.FAIL;
TileEntity te = world.getBlockEntity(pos); }
if(!(te instanceof FluidFunnelTileEntity)) return ActionResultType.FAIL;
return FluidUtil.interactWithFluidHandler(player, hand, world, pos, rayTraceResult.getDirection()) ? ActionResultType.CONSUME : ActionResultType.FAIL; @Override
} @SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
@Override { BlockEntity te = world.getBlockEntity(pos); if(te instanceof FluidFunnelTileEntity) ((FluidFunnelTileEntity)te).block_changed(); }
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused) @Override
{ TileEntity te = world.getBlockEntity(pos); if(te instanceof FluidFunnelTileEntity) ((FluidFunnelTileEntity)te).block_changed(); } public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
{ return false; }
@Override }
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side)
{ return false; } //--------------------------------------------------------------------------------------------------------------------
} // Tile entity
//--------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------
// Tile entity public static class FluidFunnelTileEntity extends StandardEntityBlocks.StandardBlockEntity
//-------------------------------------------------------------------------------------------------------------------- {
public static final int TANK_CAPACITY = 3000;
public static class FluidFunnelTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider public static final int TICK_INTERVAL = 10; // ca 500ms
{ public static final int COLLECTION_INTERVAL = 40; // ca 2000ms, simulates suction delay and saves CPU when not drained.
public static final int TANK_CAPACITY = 3000; public static final int MAX_TRACK_RADIUS = 16;
public static final int TICK_INTERVAL = 10; // ca 500ms public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72;
public static final int COLLECTION_INTERVAL = 40; // ca 2000ms, simulates suction delay and saves CPU when not drained. public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024;
public static final int MAX_TRACK_RADIUS = 16; public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS*MAX_TRACK_RADIUS;
public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72; public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16;
public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024; private int tick_timer_ = 0;
public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS*MAX_TRACK_RADIUS; private int collection_timer_ = 0;
public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16; private int no_fluid_found_counter_ = 0;
private int tick_timer_ = 0; private int intensive_search_counter_ = 0;
private int collection_timer_ = 0; private int total_pick_counter_ = 0;
private int no_fluid_found_counter_ = 0; private BlockPos last_pick_pos_ = BlockPos.ZERO;
private int intensive_search_counter_ = 0; private ArrayList<Vec3i> search_offsets_ = null;
private int total_pick_counter_ = 0; private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, TANK_CAPACITY);
private BlockPos last_pick_pos_ = BlockPos.ZERO; private final LazyOptional<IFluidHandler> fluid_handler_ = tank_.createOutputFluidHandler();
private ArrayList<Vector3i> search_offsets_ = null;
private final Fluidics.Tank tank_; public FluidFunnelTileEntity(BlockPos pos, BlockState state)
private final LazyOptional<IFluidHandler> fluid_handler_; { super(ModContent.TET_SMALL_FLUID_FUNNEL, pos, state); }
public FluidFunnelTileEntity() public void readnbt(CompoundTag nbt)
{ this(ModContent.TET_SMALL_FLUID_FUNNEL); } {
tank_.load(nbt);
public FluidFunnelTileEntity(TileEntityType<?> te_type) }
{
super(te_type); public void writenbt(CompoundTag nbt)
tank_ = new Fluidics.Tank(TANK_CAPACITY, 0, TANK_CAPACITY); {
fluid_handler_ = tank_.createOutputFluidHandler(); tank_.save(nbt);
} }
public void readnbt(CompoundNBT nbt) public void block_changed()
{ { tick_timer_ = TICK_INTERVAL; }
tank_.load(nbt);
} // BlockEntity -----------------------------------------------------------------------------------------
public void writenbt(CompoundNBT nbt) @Override
{ public void load(CompoundTag nbt)
tank_.save(nbt); { super.load(nbt); readnbt(nbt); }
}
@Override
public void block_changed() public CompoundTag save(CompoundTag nbt)
{ tick_timer_ = TICK_INTERVAL; } { super.save(nbt); writenbt(nbt); return nbt; }
// TileEntity ----------------------------------------------------------------------------------------- @Override
public void setRemoved()
@Override {
public void load(BlockState state, CompoundNBT nbt) super.setRemoved();
{ super.load(state, nbt); readnbt(nbt); } fluid_handler_.invalidate();
}
@Override
public CompoundNBT save(CompoundNBT nbt) // ICapabilityProvider / Output flow handler ----------------------------------------------------------
{ super.save(nbt); writenbt(nbt); return nbt; }
@Override
@Override public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
public void setRemoved() {
{ if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
super.setRemoved(); return super.getCapability(capability, facing);
fluid_handler_.invalidate(); }
}
// -----------------------------------------------------------------------------------------------------------------
// ICapabilityProvider / Output flow handler ----------------------------------------------------------
private FluidState get_fluidstate(BlockPos pos)
@Override {
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) return level.getFluidState(pos);
{ }
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
return super.getCapability(capability, facing); private boolean try_pick(BlockPos pos, FluidState fluidstate)
} {
if(!fluidstate.isSource()) return false;
// ITickableTileEntity -------------------------------------------------------------------------------- IFluidHandler hnd = Fluidics.handler(level, pos, null);
FluidStack fs;
private FluidState get_fluidstate(BlockPos pos) if(hnd != null) {
{ fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.EXECUTE); // IFluidBlock
final Block collection_block = level.getBlockState(pos).getBlock(); } else {
if((!(collection_block instanceof IFluidBlock)) && (!(collection_block instanceof FlowingFluidBlock)) && (!(collection_block instanceof IWaterLoggable))) { fs = new FluidStack(fluidstate.getType(), 1000);
return Fluids.EMPTY.defaultFluidState(); BlockState state = level.getBlockState(pos);
} if(state.hasProperty(BlockStateProperties.WATERLOGGED)) {
return level.getFluidState(pos); level.setBlock(pos, state.setValue(BlockStateProperties.WATERLOGGED, false), 1|2);
} } else {
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 1|2); // ok we can't leave the block, that would be an infinite source of an unknown fluid.
private boolean try_pick(BlockPos pos, FluidState fluidstate) }
{ }
if(!fluidstate.isSource()) return false; if((fs==null) || (fs.isEmpty())) return false; // it's marked nonnull but I don't trust every modder - including meself ...
IFluidHandler hnd = FluidUtil.getFluidHandler(level, pos, null).orElse(null); if(tank_.isEmpty()) {
FluidStack fs; tank_.setFluid(fs.copy());
if(hnd != null) { } else if(tank_.isFluidEqual(fs)) {
fs = hnd.drain(TANK_CAPACITY, FluidAction.EXECUTE); // IFluidBlock tank_.fill(fs, IFluidHandler.FluidAction.EXECUTE);
} else { } else {
fs = new FluidStack(fluidstate.getType(), 1000); return false;
BlockState state = level.getBlockState(pos); }
if(state instanceof IBucketPickupHandler) { return true;
((IBucketPickupHandler)state).takeLiquid(level, pos, state); }
} else if((state.getBlock() instanceof IWaterLoggable) && (state.hasProperty(BlockStateProperties.WATERLOGGED))) {
level.setBlock(pos, state.setValue(BlockStateProperties.WATERLOGGED, false), 1|2); private boolean can_pick(BlockPos pos, FluidState fluidstate)
} else { {
level.setBlock(pos, Blocks.AIR.defaultBlockState(), 1|2); // ok we can't leave the block, that would be an infinite source of an unknown fluid. if(fluidstate.isSource()) return true;
} final IFluidHandler hnd = Fluidics.handler(level, pos, null);
} if(hnd == null) return false;
if((fs==null) || (fs.isEmpty())) return false; // it's marked nonnull but I don't trust every modder - including meself ... final FluidStack fs = hnd.drain(TANK_CAPACITY, IFluidHandler.FluidAction.SIMULATE); // don't trust that everyone returns nonnull
if(tank_.isEmpty()) { return ((fs!=null) && (!fs.isEmpty())) && (fluidstate.getType().isSame(fs.getFluid()));
tank_.setFluid(fs.copy()); }
} else if(tank_.isFluidEqual(fs)) {
tank_.fill(fs, FluidAction.EXECUTE); private void rebuild_search_offsets(boolean intensive)
} else { {
return false; search_offsets_ = new ArrayList<>(9);
} search_offsets_.add(new Vec3i(0, 1, 0)); // up first
return true; {
} ArrayList<Vec3i> ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 0, 0), new Vec3i( 1, 0, 0), new Vec3i( 0, 0,-1), new Vec3i( 0, 0, 1)));
if(intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs);
private boolean can_pick(BlockPos pos, FluidState fluidstate) search_offsets_.addAll(ofs);
{ }
if(fluidstate.isSource()) return true; if(intensive) {
IFluidHandler hnd = FluidUtil.getFluidHandler(level, pos, null).orElse(null); ArrayList<Vec3i> ofs = new ArrayList<>(Arrays.asList(new Vec3i(-1, 1, 0), new Vec3i( 1, 1, 0), new Vec3i( 0, 1,-1), new Vec3i( 0, 1, 1)));
if(hnd == null) return false; Collections.shuffle(ofs);
FluidStack fs = hnd.drain(TANK_CAPACITY, FluidAction.SIMULATE); // don't trust that everyone returns nonnull search_offsets_.addAll(ofs);
return ((fs!=null) && (!fs.isEmpty())) && (fluidstate.getType().isSame(fs.getFluid())); }
} }
private void rebuild_search_offsets(boolean intensive) private boolean try_collect(final BlockPos collection_pos)
{ {
search_offsets_ = new ArrayList<>(9); FluidState collection_fluidstate = get_fluidstate(collection_pos);
search_offsets_.add(new Vector3i(0, 1, 0)); // up first if(collection_fluidstate.isEmpty()) return false;
{ Fluid fluid_to_collect = collection_fluidstate.getType();
ArrayList<Vector3i> ofs = new ArrayList<Vector3i>(Arrays.asList(new Vector3i(-1, 0, 0), new Vector3i( 1, 0, 0), new Vector3i( 0, 0,-1), new Vector3i( 0, 0, 1))); if((!tank_.isEmpty()) && (!tank_.getFluid().getFluid().isSame(fluid_to_collect))) return false;
if(intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs); if(try_pick(collection_pos, collection_fluidstate)) { last_pick_pos_ = collection_pos; return true; } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks.
search_offsets_.addAll(ofs); if((last_pick_pos_==null) || (last_pick_pos_.distSqr(collection_pos) > MAX_TRACK_RADIUS_SQ)) { last_pick_pos_ = collection_pos; search_offsets_ = null; }
} BlockPos pos = last_pick_pos_;
if(intensive) { HashSet<BlockPos> checked = new HashSet<>();
ArrayList<Vector3i> ofs = new ArrayList<Vector3i>(Arrays.asList(new Vector3i(-1, 1, 0), new Vector3i( 1, 1, 0), new Vector3i( 0, 1,-1), new Vector3i( 0, 1, 1))); Stack<BlockPos> trail = new Stack<>();
Collections.shuffle(ofs); trail.add(pos);
search_offsets_.addAll(ofs); checked.add(pos);
} int steps=0;
} boolean intensive = (no_fluid_found_counter_ >= INTENSIVE_SEARCH_TRIGGER_THRESHOLD);
if(intensive) { no_fluid_found_counter_ = 0; ++intensive_search_counter_; }
private boolean try_collect(final BlockPos collection_pos) if(search_offsets_ == null) rebuild_search_offsets(intensive);
{ int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE;
FluidState collection_fluidstate = get_fluidstate(collection_pos); while(++steps <= max) {
if(collection_fluidstate.isEmpty()) return false; int num_adjacent = 0;
Fluid fluid_to_collect = collection_fluidstate.getType(); for(int i=0; i<search_offsets_.size(); ++i) {
if((!tank_.isEmpty()) && (!tank_.getFluid().getFluid().isSame(fluid_to_collect))) return false; BlockPos p = pos.offset(search_offsets_.get(i));
if(try_pick(collection_pos, collection_fluidstate)) { last_pick_pos_ = collection_pos; return true; } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks. if(checked.contains(p)) continue;
if((last_pick_pos_==null) || (last_pick_pos_.distSqr(collection_pos) > MAX_TRACK_RADIUS_SQ)) { last_pick_pos_ = collection_pos; search_offsets_ = null; } checked.add(p);
BlockPos pos = last_pick_pos_; ++steps;
HashSet<BlockPos> checked = new HashSet<>(); FluidState fluidstate = get_fluidstate(p);
Stack<BlockPos> trail = new Stack<BlockPos>(); if(fluidstate.getType().isSame(fluid_to_collect)) {
trail.add(pos); ++num_adjacent;
checked.add(pos); pos = p;
int steps=0; trail.push(pos);
boolean intensive = (no_fluid_found_counter_ >= INTENSIVE_SEARCH_TRIGGER_THRESHOLD); if(steps < MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2) {
if(intensive) { no_fluid_found_counter_ = 0; ++intensive_search_counter_; } // check for same fluid above (only source blocks)
if(search_offsets_ == null) rebuild_search_offsets(intensive); final int max_surface_search = (MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2)-steps;
int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE; for(int k=0; k<max_surface_search; ++k) {
while(++steps <= max) { FluidState fs = get_fluidstate(pos.above());
int num_adjacent = 0; if(!can_pick(pos.above(), fs)) break;
for(int i=0; i<search_offsets_.size(); ++i) { fluidstate = fs;
BlockPos p = pos.offset(search_offsets_.get(i)); pos = pos.above();
if(checked.contains(p)) continue; trail.push(pos);
checked.add(p); }
++steps; }
FluidState fluidstate = get_fluidstate(p); if(try_pick(pos, fluidstate)) {
if(fluidstate.getType().isSame(fluid_to_collect)) { last_pick_pos_ = pos;
++num_adjacent; no_fluid_found_counter_ = 0;
pos = p; search_offsets_ = null;
trail.push(pos); // probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes.
if(steps < MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2) { if((++total_pick_counter_ > 50) && level.random.nextInt(10)==0) last_pick_pos_ = collection_pos;
// check for same fluid above (only source blocks) //println("PASS " + steps + " - " + (pos.subtract(collection_pos)));
final int max_surface_search = (MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2)-steps; return true;
for(int k=0; k<max_surface_search; ++k) { }
FluidState fs = get_fluidstate(pos.above()); }
if(!can_pick(pos.above(), fs)) break; }
fluidstate = fs; if(trail.isEmpty()) break; // reset search
pos = pos.above(); if(num_adjacent==0) pos = trail.pop();
trail.push(pos); }
} //println("FAIL=" + steps + " - " + (pos.subtract(collection_pos)));
} //String s = new String(); for(BlockPos p:checked) s += "\n" + p; println(s);
if(try_pick(pos, fluidstate)) { if(intensive_search_counter_ > 2) level.removeBlock(pos, false);
last_pick_pos_ = pos; last_pick_pos_ = collection_pos;
no_fluid_found_counter_ = 0; search_offsets_ = null; // try other search order
search_offsets_ = null; ++no_fluid_found_counter_;
// probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes. return false;
if((++total_pick_counter_ > 50) && level.random.nextInt(10)==0) last_pick_pos_ = collection_pos; }
//println("PASS " + steps + " - " + (pos.subtract(collection_pos)));
return true; public void tick()
} {
} if((level.isClientSide) || (--tick_timer_ > 0)) return;
} tick_timer_ = TICK_INTERVAL;
if(trail.isEmpty()) break; // reset search collection_timer_ += TICK_INTERVAL;
if(num_adjacent==0) pos = trail.pop(); final BlockState funnel_state = level.getBlockState(worldPosition);
} if(!(funnel_state.getBlock() instanceof FluidFunnelBlock)) return;
//println("FAIL=" + steps + " - " + (pos.subtract(collection_pos))); boolean dirty = false;
//String s = new String(); for(BlockPos p:checked) s += "\n" + p; println(s); // Collection
if(intensive_search_counter_ > 2) level.removeBlock(pos, false); if((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_==null) || (tank_.getFluidAmount() <= (TANK_CAPACITY-1000)))) {
last_pick_pos_ = collection_pos; collection_timer_ = 0;
search_offsets_ = null; // try other search order if(!level.hasNeighborSignal(worldPosition)) { // redstone disable feature
++no_fluid_found_counter_; if(last_pick_pos_==null) last_pick_pos_ = worldPosition.above();
return false; BlockEntity te = with_device_fluid_handler_collection ? (level.getBlockEntity(worldPosition.above())) : (null);
} if(te != null) {
IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.DOWN).orElse(null);
public void tick() if(fh == null) {
{ te = null;
if((level.isClientSide) || (--tick_timer_ > 0)) return; } else if(tank_.isEmpty()) {
tick_timer_ = TICK_INTERVAL; FluidStack fs = fh.drain(1000, IFluidHandler.FluidAction.EXECUTE);
collection_timer_ += TICK_INTERVAL; if((fs!=null) && (!fs.isEmpty())) tank_.setFluid(fs.copy());
final BlockState funnel_state = level.getBlockState(worldPosition); dirty = true;
if(!(funnel_state.getBlock() instanceof FluidFunnelBlock)) return; } else if (!tank_.isFull()) {
boolean dirty = false; FluidStack todrain = new FluidStack(tank_.getFluid(), Math.min(tank_.getCapacity()-tank_.getFluidAmount(), 1000));
// Collection tank_.fill(fh.drain(todrain, IFluidHandler.FluidAction.EXECUTE), IFluidHandler.FluidAction.EXECUTE);
if((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_==null) || (tank_.getFluidAmount() <= (TANK_CAPACITY-1000)))) { dirty = true;
collection_timer_ = 0; }
if(!level.hasNeighborSignal(worldPosition)) { // redstone disable feature }
if(last_pick_pos_==null) last_pick_pos_ = worldPosition.above(); if(te==null) {
TileEntity te = with_device_fluid_handler_collection ? (level.getBlockEntity(worldPosition.above())) : (null); if(try_collect(worldPosition.above())) dirty = true;
if(te != null) { }
IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.DOWN).orElse(null); }
if(fh == null) { }
te = null; // Gravity fluid transfer
} else if(tank_.isEmpty()) { if((tank_.getFluidAmount() >= 1000)) {
FluidStack fs = fh.drain(1000, FluidAction.EXECUTE); final IFluidHandler fh = Fluidics.handler(level, worldPosition.below(), Direction.UP);
if((fs!=null) && (!fs.isEmpty())) tank_.setFluid(fs.copy()); if(fh != null) {
dirty = true; FluidStack fs = new FluidStack(tank_.getFluid().getFluid(), 1000);
} else if (!tank_.isFull()) { int nfilled = Mth.clamp(fh.fill(fs, IFluidHandler.FluidAction.EXECUTE), 0, 1000);
FluidStack todrain = new FluidStack(tank_.getFluid(), Math.min(tank_.getCapacity()-tank_.getFluidAmount(), 1000)); tank_.drain(nfilled);
tank_.fill(fh.drain(todrain, FluidAction.EXECUTE), FluidAction.EXECUTE); dirty = true;
dirty = true; }
} }
} // Block state
if(te==null) { int fill_level = (tank_==null) ? 0 : (Mth.clamp(tank_.getFluidAmount()/1000,0, FluidFunnelBlock.FILL_LEVEL_MAX));
if(try_collect(worldPosition.above())) dirty = true; if(funnel_state.getValue(FluidFunnelBlock.FILL_LEVEL) != fill_level) level.setBlock(worldPosition, funnel_state.setValue(FluidFunnelBlock.FILL_LEVEL, fill_level), 2|16);
} if(dirty) setChanged();
} }
} }
// Gravity fluid transfer }
if((tank_.getFluidAmount() >= 1000)) {
IFluidHandler fh = FluidUtil.getFluidHandler(level, worldPosition.below(), Direction.UP).orElse(null);
if(fh != null) {
FluidStack fs = new FluidStack(tank_.getFluid().getFluid(), 1000);
int nfilled = MathHelper.clamp(fh.fill(fs, FluidAction.EXECUTE), 0, 1000);
tank_.drain(nfilled);
dirty = true;
}
}
// Block state
int fill_level = (tank_==null) ? 0 : (MathHelper.clamp(tank_.getFluidAmount()/1000,0, FluidFunnelBlock.FILL_LEVEL_MAX));
if(funnel_state.getValue(FluidFunnelBlock.FILL_LEVEL) != fill_level) level.setBlock(worldPosition, funnel_state.setValue(FluidFunnelBlock.FILL_LEVEL, fill_level), 2|16);
if(dirty) setChanged();
}
}
}

View file

@ -1,394 +1,397 @@
/* /*
* @file EdFreezer.java * @file EdFreezer.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Small highly insulated stone liquification furnace * Small highly insulated stone liquification furnace
* (magmatic phase). * (magmatic phase).
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.world.IWorldReader; import net.minecraft.core.BlockPos;
import net.minecraft.world.IBlockReader; import net.minecraft.core.Direction;
import net.minecraft.world.World; import net.minecraft.nbt.CompoundTag;
import net.minecraft.block.*; import net.minecraft.sounds.SoundEvents;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.sounds.SoundSource;
import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Mth;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.InteractionHand;
import net.minecraft.item.Items; import net.minecraft.world.InteractionResult;
import net.minecraft.item.ItemStack; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.entity.player.Player;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.item.ItemStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.world.item.Items;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.*; import net.minecraft.world.level.Level;
import net.minecraft.util.math.*; import net.minecraft.world.level.LevelReader;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.level.block.Block;
import net.minecraft.state.StateContainer; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.fluid.Fluids; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.common.util.LazyOptional; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraftforge.energy.IEnergyStorage; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraft.world.level.material.Fluids;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraft.world.phys.AABB;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.LazyOptional;
import wile.engineersdecor.ModConfig; import net.minecraftforge.energy.CapabilityEnergy;
import wile.engineersdecor.libmc.detail.Fluidics; import net.minecraftforge.energy.IEnergyStorage;
import wile.engineersdecor.ModContent; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import javax.annotation.Nonnull; import net.minecraftforge.items.CapabilityItemHandler;
import javax.annotation.Nullable; import net.minecraftforge.items.IItemHandler;
import java.util.*; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.ModContent;
public class EdFreezer import wile.engineersdecor.libmc.blocks.StandardBlocks;
{ import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
public static void on_config(int consumption, int cooldown_per_second) import wile.engineersdecor.libmc.detail.Fluidics;
{ FreezerTileEntity.on_config(consumption, cooldown_per_second); }
import javax.annotation.Nonnull;
//-------------------------------------------------------------------------------------------------------------------- import javax.annotation.Nullable;
// Block import java.util.ArrayList;
//-------------------------------------------------------------------------------------------------------------------- import java.util.List;
import java.util.Random;
public static class FreezerBlock extends DecorBlock.Horizontal implements IDecorBlock
{ public class EdFreezer
public static final int PHASE_MAX = 4; {
public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX); public static void on_config(int consumption, int cooldown_per_second)
{ FreezerTileEntity.on_config(consumption, cooldown_per_second); }
public FreezerBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); } //--------------------------------------------------------------------------------------------------------------------
// Block
@Override //--------------------------------------------------------------------------------------------------------------------
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); builder.add(PHASE); } public static class FreezerBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock<FreezerTileEntity>
{
@Override public static final int PHASE_MAX = 4;
@Nullable public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX);
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).setValue(PHASE, 0); } public FreezerBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
@SuppressWarnings("deprecation") @Nullable
public boolean hasAnalogOutputSignal(BlockState state) @Override
{ return true; } public BlockEntityType<EdFreezer.FreezerTileEntity> getBlockEntityType()
{ return ModContent.TET_FREEZER; }
@Override
@SuppressWarnings("deprecation") @Override
public int getAnalogOutputSignal(BlockState state, World world, BlockPos pos) protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ return MathHelper.clamp((state.getValue(PHASE)*4), 0, 15); } { super.createBlockStateDefinition(builder); builder.add(PHASE); }
@Override @Override
public boolean hasTileEntity(BlockState state) @Nullable
{ return true; } public BlockState getStateForPlacement(BlockPlaceContext context)
{ return super.getStateForPlacement(context).setValue(PHASE, 0); }
@Override
@Nullable @Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) @SuppressWarnings("deprecation")
{ return new EdFreezer.FreezerTileEntity(); } public boolean hasAnalogOutputSignal(BlockState state)
{ return true; }
@Override
public void setPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) @Override
{} @SuppressWarnings("deprecation")
public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos)
@Override { return Mth.clamp((state.getValue(PHASE)*4), 0, 15); }
public boolean hasDynamicDropList()
{ return true; } @Override
public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
@Override {}
public List<ItemStack> dropList(BlockState state, World world, TileEntity te, boolean explosion)
{ @Override
final List<ItemStack> stacks = new ArrayList<ItemStack>(); public boolean hasDynamicDropList()
if(world.isClientSide) return stacks; { return true; }
if(!(te instanceof FreezerTileEntity)) return stacks;
((FreezerTileEntity)te).reset_process(); @Override
stacks.add(new ItemStack(this, 1)); public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
return stacks; {
} final List<ItemStack> stacks = new ArrayList<>();
if(world.isClientSide) return stacks;
@Override if(!(te instanceof FreezerTileEntity)) return stacks;
@SuppressWarnings("deprecation") ((FreezerTileEntity)te).reset_process();
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) stacks.add(new ItemStack(this, 1));
{ return stacks;
if(player.isShiftKeyDown()) return ActionResultType.PASS; }
if(world.isClientSide()) return ActionResultType.SUCCESS;
FreezerTileEntity te = getTe(world, pos); @Override
if(te==null) return ActionResultType.FAIL; @SuppressWarnings("deprecation")
final ItemStack stack = player.getItemInHand(hand); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
boolean dirty = false; {
if(Fluidics.manualFluidHandlerInteraction(world, pos, null, player, hand)) { if(player.isShiftKeyDown()) return InteractionResult.PASS;
world.playSound(null, pos, SoundEvents.BUCKET_EMPTY, SoundCategory.BLOCKS, 0.5f, 1.4f); if(world.isClientSide()) return InteractionResult.SUCCESS;
return ActionResultType.CONSUME; FreezerTileEntity te = getTe(world, pos);
} if(te==null) return InteractionResult.FAIL;
if(stack.getItem()==Items.WATER_BUCKET) { final ItemStack stack = player.getItemInHand(hand);
return ActionResultType.CONSUME; // would be already handled boolean dirty = false;
} else if(stack.isEmpty()) { if(Fluidics.manualFluidHandlerInteraction(world, pos, null, player, hand)) {
ItemStack ice = te.getIceItem(true); world.playSound(null, pos, SoundEvents.BUCKET_EMPTY, SoundSource.BLOCKS, 0.5f, 1.4f);
if(!ice.isEmpty()) { return InteractionResult.CONSUME;
player.addItem(ice); }
world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundCategory.BLOCKS, 0.3f, 1.1f); if(stack.getItem()==Items.WATER_BUCKET) {
} else { return InteractionResult.CONSUME; // would be already handled
world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundCategory.BLOCKS, 0.2f, 0.02f); } else if(stack.isEmpty()) {
} ItemStack ice = te.getIceItem(true);
return ActionResultType.CONSUME; if(!ice.isEmpty()) {
} else { player.addItem(ice);
return ActionResultType.PASS; world.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.BLOCKS, 0.3f, 1.1f);
} } else {
} world.playSound(null, pos, SoundEvents.IRON_TRAPDOOR_OPEN, SoundSource.BLOCKS, 0.2f, 0.02f);
}
@Override return InteractionResult.CONSUME;
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) } else {
{ return false; } return InteractionResult.PASS;
}
@Override }
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd) @Override
{} public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
{ return false; }
@Nullable
private FreezerTileEntity getTe(World world, BlockPos pos) @Override
{ final TileEntity te=world.getBlockEntity(pos); return (!(te instanceof FreezerTileEntity)) ? (null) : ((FreezerTileEntity)te); } @OnlyIn(Dist.CLIENT)
} public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd)
{}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity @Nullable
//-------------------------------------------------------------------------------------------------------------------- private FreezerTileEntity getTe(Level world, BlockPos pos)
{ final BlockEntity te=world.getBlockEntity(pos); return (!(te instanceof FreezerTileEntity)) ? (null) : ((FreezerTileEntity)te); }
public static class FreezerTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage, ICapabilityProvider }
{
public static final int TICK_INTERVAL = 20; //--------------------------------------------------------------------------------------------------------------------
public static final int MAX_FLUID_LEVEL = 2000; // Tile entity
public static final int MAX_ENERGY_BUFFER = 32000; //--------------------------------------------------------------------------------------------------------------------
public static final int MAX_ENERGY_TRANSFER = 8192;
public static final int TANK_CAPACITY = 2000; public static class FreezerTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage
public static final int DEFAULT_ENERGY_CONSUMPTION = 92; {
public static final int DEFAULT_COOLDOWN_RATE = 2; public static final int TICK_INTERVAL = 20;
public static final int PHASE_EMPTY = 0; public static final int MAX_FLUID_LEVEL = 2000;
public static final int PHASE_WATER = 1; public static final int MAX_ENERGY_BUFFER = 32000;
public static final int PHASE_ICE = 2; public static final int MAX_ENERGY_TRANSFER = 8192;
public static final int PHASE_PACKEDICE = 3; public static final int TANK_CAPACITY = 2000;
public static final int PHASE_BLUEICE = 4; public static final int DEFAULT_ENERGY_CONSUMPTION = 92;
public static final int DEFAULT_COOLDOWN_RATE = 2;
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION; public static final int PHASE_EMPTY = 0;
private static int cooldown_rate = DEFAULT_COOLDOWN_RATE; public static final int PHASE_WATER = 1;
private static int reheat_rate = 1; public static final int PHASE_ICE = 2;
private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, TANK_CAPACITY, TANK_CAPACITY, fs->fs.getFluid()==Fluids.WATER); public static final int PHASE_PACKEDICE = 3;
private int tick_timer_; public static final int PHASE_BLUEICE = 4;
private int energy_stored_;
private int progress_; private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
private boolean force_block_update_; private static int cooldown_rate = DEFAULT_COOLDOWN_RATE;
private static int reheat_rate = 1;
public static void on_config(int consumption, int cooldown_per_second) private final Fluidics.Tank tank_ = new Fluidics.Tank(TANK_CAPACITY, TANK_CAPACITY, TANK_CAPACITY, fs->fs.getFluid()==Fluids.WATER);
{ private int tick_timer_;
energy_consumption = MathHelper.clamp(consumption, 8, 4096); private int energy_stored_;
cooldown_rate = MathHelper.clamp(cooldown_per_second, 1, 5); private int progress_;
reheat_rate = MathHelper.clamp(cooldown_per_second/2, 1, 5); private boolean force_block_update_;
ModConfig.log("Config freezer energy consumption:" + energy_consumption + "rf/t, cooldown-rate: " + cooldown_rate + "%/s.");
} public static void on_config(int consumption, int cooldown_per_second)
{
public FreezerTileEntity() energy_consumption = Mth.clamp(consumption, 8, 4096);
{ this(ModContent.TET_FREEZER); } cooldown_rate = Mth.clamp(cooldown_per_second, 1, 5);
reheat_rate = Mth.clamp(cooldown_per_second/2, 1, 5);
public FreezerTileEntity(TileEntityType<?> te_type) ModConfig.log("Config freezer energy consumption:" + energy_consumption + "rf/t, cooldown-rate: " + cooldown_rate + "%/s.");
{ super(te_type); } }
public int progress() public FreezerTileEntity(BlockPos pos, BlockState state)
{ return progress_; } { super(ModContent.TET_FREEZER, pos, state); }
public int phase() public int progress()
{ { return progress_; }
if(tank_.getFluidAmount() < 1000) return PHASE_EMPTY;
if(progress_ >= 100) return PHASE_BLUEICE; public int phase()
if(progress_ >= 70) return PHASE_PACKEDICE; {
if(progress_ >= 30) return PHASE_ICE; if(tank_.getFluidAmount() < 1000) return PHASE_EMPTY;
return PHASE_WATER; if(progress_ >= 100) return PHASE_BLUEICE;
} if(progress_ >= 70) return PHASE_PACKEDICE;
if(progress_ >= 30) return PHASE_ICE;
public ItemStack getIceItem(boolean extract) return PHASE_WATER;
{ }
ItemStack stack;
switch(phase()) { public ItemStack getIceItem(boolean extract)
case PHASE_ICE: stack = new ItemStack(Items.ICE); break; {
case PHASE_PACKEDICE: stack = new ItemStack(Items.PACKED_ICE); break; ItemStack stack;
case PHASE_BLUEICE: stack = new ItemStack(Items.BLUE_ICE); break; switch(phase()) {
default: return ItemStack.EMPTY; case PHASE_ICE: stack = new ItemStack(Items.ICE); break;
} case PHASE_PACKEDICE: stack = new ItemStack(Items.PACKED_ICE); break;
if(extract) reset_process(); case PHASE_BLUEICE: stack = new ItemStack(Items.BLUE_ICE); break;
return stack; default: return ItemStack.EMPTY;
} }
if(extract) reset_process();
public int comparator_signal() return stack;
{ return phase() * 4; } }
protected void reset_process() public int comparator_signal()
{ { return phase() * 4; }
force_block_update_ = true;
tank_.drain(1000); protected void reset_process()
tick_timer_ = 0; {
progress_ = 0; force_block_update_ = true;
} tank_.drain(1000);
tick_timer_ = 0;
public void readnbt(CompoundNBT nbt) progress_ = 0;
{ }
energy_stored_ = nbt.getInt("energy");
progress_ = nbt.getInt("progress"); public void readnbt(CompoundTag nbt)
tank_.load(nbt); {
} energy_stored_ = nbt.getInt("energy");
progress_ = nbt.getInt("progress");
protected void writenbt(CompoundNBT nbt) tank_.load(nbt);
{ }
nbt.putInt("energy", MathHelper.clamp(energy_stored_,0 , MAX_ENERGY_BUFFER));
nbt.putInt("progress", MathHelper.clamp(progress_,0 , 100)); protected void writenbt(CompoundTag nbt)
tank_.save(nbt); {
} nbt.putInt("energy", Mth.clamp(energy_stored_,0 , MAX_ENERGY_BUFFER));
nbt.putInt("progress", Mth.clamp(progress_,0 , 100));
// TileEntity ------------------------------------------------------------------------------ tank_.save(nbt);
}
@Override
public void load(BlockState state, CompoundNBT nbt) // BlockEntity ------------------------------------------------------------------------------
{ super.load(state, nbt); readnbt(nbt); }
@Override
@Override public void load(CompoundTag nbt)
public CompoundNBT save(CompoundNBT nbt) { super.load(nbt); readnbt(nbt); }
{ super.save(nbt); writenbt(nbt); return nbt; }
@Override
@Override public CompoundTag save(CompoundTag nbt)
public void setRemoved() { super.save(nbt); writenbt(nbt); return nbt; }
{
super.setRemoved(); @Override
energy_handler_.invalidate(); public void setRemoved()
fluid_handler_.invalidate(); {
item_handler_.invalidate(); super.setRemoved();
} energy_handler_.invalidate();
fluid_handler_.invalidate();
// IItemHandler -------------------------------------------------------------------------------- item_handler_.invalidate();
}
private final LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> (IItemHandler)new FreezerItemHandler(this));
// IItemHandler --------------------------------------------------------------------------------
protected static class FreezerItemHandler implements IItemHandler
{ private final LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> new FreezerItemHandler(this));
private final FreezerTileEntity te;
protected static class FreezerItemHandler implements IItemHandler
FreezerItemHandler(FreezerTileEntity te) {
{ this.te = te; } private final FreezerTileEntity te;
@Override FreezerItemHandler(FreezerTileEntity te)
public int getSlots() { this.te = te; }
{ return 1; }
@Override
@Override public int getSlots()
public int getSlotLimit(int index) { return 1; }
{ return 1; }
@Override
@Override public int getSlotLimit(int index)
public boolean isItemValid(int slot, @Nonnull ItemStack stack) { return 1; }
{ return false; }
@Override
@Override public boolean isItemValid(int slot, @Nonnull ItemStack stack)
@Nonnull { return false; }
public ItemStack getStackInSlot(int index)
{ return (index!=0) ? ItemStack.EMPTY : te.getIceItem(false); } @Override
@Nonnull
@Override public ItemStack getStackInSlot(int index)
@Nonnull { return (index!=0) ? ItemStack.EMPTY : te.getIceItem(false); }
public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate)
{ return ItemStack.EMPTY; } @Override
@Nonnull
@Override public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate)
@Nonnull { return ItemStack.EMPTY; }
public ItemStack extractItem(int index, int amount, boolean simulate)
{ return te.getIceItem(!simulate); } @Override
} @Nonnull
public ItemStack extractItem(int index, int amount, boolean simulate)
// IFluidHandler -------------------------------------------------------------------------------- { return te.getIceItem(!simulate); }
}
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_));
// IFluidHandler --------------------------------------------------------------------------------
// IEnergyStorage ----------------------------------------------------------------------------
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_));
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
// IEnergyStorage ----------------------------------------------------------------------------
@Override
public boolean canExtract() protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> this);
{ return false; }
@Override
@Override public boolean canExtract()
public boolean canReceive() { return false; }
{ return true; }
@Override
@Override public boolean canReceive()
public int getMaxEnergyStored() { return true; }
{ return MAX_ENERGY_BUFFER; }
@Override
@Override public int getMaxEnergyStored()
public int getEnergyStored() { return MAX_ENERGY_BUFFER; }
{ return energy_stored_; }
@Override
@Override public int getEnergyStored()
public int extractEnergy(int maxExtract, boolean simulate) { return energy_stored_; }
{ return 0; }
@Override
@Override public int extractEnergy(int maxExtract, boolean simulate)
public int receiveEnergy(int maxReceive, boolean simulate) { return 0; }
{
if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0; @Override
int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_)); public int receiveEnergy(int maxReceive, boolean simulate)
if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER; {
if(!simulate) {energy_stored_ += n; setChanged(); } if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0;
return n; int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_));
} if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER;
if(!simulate) {energy_stored_ += n; setChanged(); }
// Capability export ---------------------------------------------------------------------------- return n;
}
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) // Capability export ----------------------------------------------------------------------------
{
if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast(); @Override
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast(); public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast(); {
return super.getCapability(capability, facing); if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
} if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast();
// ITickable ------------------------------------------------------------------------------------ return super.getCapability(capability, facing);
}
@Override
public void tick() // ITickable ------------------------------------------------------------------------------------
{
if(level.isClientSide) return; @Override
if(--tick_timer_ > 0) return; public void tick()
tick_timer_ = TICK_INTERVAL; {
BlockState state = level.getBlockState(worldPosition); if(level.isClientSide) return;
if(!(state.getBlock() instanceof FreezerBlock)) return; if(--tick_timer_ > 0) return;
boolean dirty = false; tick_timer_ = TICK_INTERVAL;
final int last_phase = phase(); BlockState state = level.getBlockState(worldPosition);
if(tank_.getFluidAmount() < 1000) { if(!(state.getBlock() instanceof FreezerBlock)) return;
progress_ = 0; boolean dirty = false;
} else if((energy_stored_ <= 0) || (level.hasNeighborSignal(worldPosition))) { final int last_phase = phase();
progress_ = MathHelper.clamp(progress_-reheat_rate, 0,100); if(tank_.getFluidAmount() < 1000) {
} else if(progress_ >= 100) { progress_ = 0;
progress_ = 100; } else if((energy_stored_ <= 0) || (level.hasNeighborSignal(worldPosition))) {
energy_stored_ = MathHelper.clamp(energy_stored_-((energy_consumption*TICK_INTERVAL)/20), 0, MAX_ENERGY_BUFFER); progress_ = Mth.clamp(progress_-reheat_rate, 0,100);
} else { } else if(progress_ >= 100) {
energy_stored_ = MathHelper.clamp(energy_stored_-(energy_consumption*TICK_INTERVAL), 0, MAX_ENERGY_BUFFER); progress_ = 100;
progress_ = MathHelper.clamp(progress_+cooldown_rate, 0, 100); energy_stored_ = Mth.clamp(energy_stored_-((energy_consumption*TICK_INTERVAL)/20), 0, MAX_ENERGY_BUFFER);
} } else {
int new_phase = phase(); energy_stored_ = Mth.clamp(energy_stored_-(energy_consumption*TICK_INTERVAL), 0, MAX_ENERGY_BUFFER);
if(new_phase > last_phase) { progress_ = Mth.clamp(progress_+cooldown_rate, 0, 100);
level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundCategory.BLOCKS, 0.2f, 0.7f); }
} else if(new_phase < last_phase) { int new_phase = phase();
level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundCategory.BLOCKS, 0.2f, 0.7f); if(new_phase > last_phase) {
} level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f);
// Block state } else if(new_phase < last_phase) {
if((force_block_update_ || (state.getValue(FreezerBlock.PHASE) != new_phase))) { level.playSound(null, worldPosition, SoundEvents.SAND_FALL, SoundSource.BLOCKS, 0.2f, 0.7f);
state = state.setValue(FreezerBlock.PHASE, new_phase); }
level.setBlock(worldPosition, state,3|16); // Block state
level.updateNeighborsAt(getBlockPos(), state.getBlock()); if((force_block_update_ || (state.getValue(FreezerBlock.PHASE) != new_phase))) {
force_block_update_ = false; state = state.setValue(FreezerBlock.PHASE, new_phase);
} level.setBlock(worldPosition, state,3|16);
if(dirty) setChanged(); level.updateNeighborsAt(getBlockPos(), state.getBlock());
} force_block_update_ = false;
} }
} if(dirty) setChanged();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,53 +1,54 @@
/* /*
* @file EdGlassBlock.java * @file EdGlassBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Full block characteristics class. Explicitly overrides some * Full block characteristics class. Explicitly overrides some
* `Block` methods to return faster due to exclusive block properties. * `Block` methods to return faster due to exclusive block properties.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.world.IBlockReader; import net.minecraft.core.Direction;
import net.minecraft.block.AbstractBlock; import net.minecraft.network.chat.Component;
import net.minecraft.block.BlockState; import net.minecraft.world.item.DyeColor;
import net.minecraft.block.StainedGlassBlock; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.DyeColor; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.Direction; import net.minecraft.world.level.block.StainedGlassBlock;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import javax.annotation.Nullable;
import java.util.List; import javax.annotation.Nullable;
import java.util.List;
public class EdGlassBlock extends StainedGlassBlock implements IDecorBlock
{ public class EdGlassBlock extends StainedGlassBlock implements StandardBlocks.IStandardBlock
public EdGlassBlock(long config, AbstractBlock.Properties properties) {
{ super(DyeColor.BLACK, properties); } public EdGlassBlock(long config, BlockBehaviour.Properties properties)
{ super(DyeColor.BLACK, properties); }
@Override
@OnlyIn(Dist.CLIENT) @Override
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) @OnlyIn(Dist.CLIENT)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
@Override
public RenderTypeHint getRenderTypeHint() @Override
{ return RenderTypeHint.TRANSLUCENT; } public StandardBlocks.IStandardBlock.RenderTypeHint getRenderTypeHint()
{ return StandardBlocks.IStandardBlock.RenderTypeHint.TRANSLUCENT; }
@Override
@OnlyIn(Dist.CLIENT) @Override
@SuppressWarnings("deprecation") @OnlyIn(Dist.CLIENT)
public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) @SuppressWarnings("deprecation")
{ return (adjacentBlockState.getBlock()==this) ? true : super.skipRendering(state, adjacentBlockState, side); } public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side)
{ return (adjacentBlockState.getBlock()==this) || (super.skipRendering(state, adjacentBlockState, side)); }
@Override
public boolean isPossibleToRespawnInThis() @Override
{ return false; } public boolean isPossibleToRespawnInThis()
{ return false; }
}
}

View file

@ -1,18 +0,0 @@
/*
* @file EdGroundBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Block type for soils, floors, etc. Drawn out into a class
* to enable functionality block overrides.
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.*;
public class EdGroundBlock extends DecorBlock.Normal implements IDecorBlock
{
public EdGroundBlock(long config, AbstractBlock.Properties builder)
{ super(config, builder); }
}

View file

@ -1,134 +1,140 @@
/* /*
* @file EdFloorGratingBlock.java * @file EdFloorGratingBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Floor gratings. * Floor gratings.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.block.BlockState; import net.minecraft.sounds.SoundEvents;
import net.minecraft.entity.*; import net.minecraft.sounds.SoundSource;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionHand;
import net.minecraft.pathfinding.PathType; import net.minecraft.world.InteractionResult;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.entity.Entity;
import net.minecraft.state.StateContainer; import net.minecraft.world.entity.EntityType;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.*; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.Level;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.LevelReader;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.IWorldReader; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import javax.annotation.Nullable; import net.minecraft.world.level.pathfinder.PathComputationType;
import java.util.ArrayList; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
public class EdHatchBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock import net.minecraft.world.phys.shapes.VoxelShape;
{ import wile.engineersdecor.libmc.blocks.StandardBlocks;
public static final BooleanProperty OPEN = BlockStateProperties.OPEN;
public static final BooleanProperty POWERED = BlockStateProperties.POWERED; import javax.annotation.Nullable;
protected final ArrayList<VoxelShape> vshapes_open; import java.util.ArrayList;
public EdHatchBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABBClosed, final AxisAlignedBB unrotatedAABBOpen)
{ public class EdHatchBlock extends StandardBlocks.HorizontalWaterLoggable
super(config, builder, unrotatedAABBClosed); vshapes_open = makeHorizontalShapeLookup(new AxisAlignedBB[]{unrotatedAABBOpen}); {
registerDefaultState(super.defaultBlockState().setValue(OPEN, false).setValue(POWERED, false)); public static final BooleanProperty OPEN = BlockStateProperties.OPEN;
} public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
protected final ArrayList<VoxelShape> vshapes_open;
public EdHatchBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABBsClosed, final AxisAlignedBB[] unrotatedAABBsOpen)
{ super(config, builder, unrotatedAABBsClosed); vshapes_open = makeHorizontalShapeLookup(unrotatedAABBsOpen); } public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABBClosed, final AABB unrotatedAABBOpen)
{
@Override super(config, builder, unrotatedAABBClosed); vshapes_open = makeHorizontalShapeLookup(new AABB[]{unrotatedAABBOpen});
public RenderTypeHint getRenderTypeHint() registerDefaultState(super.defaultBlockState().setValue(OPEN, false).setValue(POWERED, false));
{ return RenderTypeHint.CUTOUT; } }
@Override public EdHatchBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABBsClosed, final AABB[] unrotatedAABBsOpen)
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext) { super(config, builder, unrotatedAABBsClosed); vshapes_open = makeHorizontalShapeLookup(unrotatedAABBsOpen); }
{ return state.getValue(OPEN) ? vshapes_open.get((state.getValue(HORIZONTAL_FACING)).get3DDataValue() & 0x7) : super.getShape(state, source, pos, selectionContext); }
@Override
@Override public RenderTypeHint getRenderTypeHint()
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) { return RenderTypeHint.CUTOUT; }
{ return state.getValue(OPEN); }
@Override
@Override public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext)
@SuppressWarnings("deprecation") { return state.getValue(OPEN) ? vshapes_open.get((state.getValue(HORIZONTAL_FACING)).get3DDataValue() & 0x7) : super.getShape(state, source, pos, selectionContext); }
public boolean isPathfindable(BlockState state, IBlockReader world, BlockPos pos, PathType type)
{ return !state.getValue(OPEN); } @Override
public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
@Override { return state.getValue(OPEN); }
public boolean isLadder(BlockState state, IWorldReader world, BlockPos pos, LivingEntity entity)
{ @Override
if(!state.getValue(OPEN)) return false; @SuppressWarnings("deprecation")
{ public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type)
final BlockState up_state = world.getBlockState(pos.above()); { return !state.getValue(OPEN); }
if(up_state.is(this) && (up_state.getValue(OPEN))) return true;
if(up_state.isLadder(world, pos.above(), entity)) return true; @Override
} public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity)
{ {
final BlockState down_state = world.getBlockState(pos.below()); if(!state.getValue(OPEN)) return false;
if(down_state.is(this) && (down_state.getValue(OPEN))) return true; {
if(down_state.isLadder(world, pos.below(), entity)) return true; final BlockState up_state = world.getBlockState(pos.above());
} if(up_state.is(this) && (up_state.getValue(OPEN))) return true;
return false; if(up_state.isLadder(world, pos.above(), entity)) return true;
} }
{
@Override final BlockState down_state = world.getBlockState(pos.below());
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) if(down_state.is(this) && (down_state.getValue(OPEN))) return true;
{ return false; } if(down_state.isLadder(world, pos.below(), entity)) return true;
}
@Override return false;
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) }
{ super.createBlockStateDefinition(builder); builder.add(OPEN, POWERED); }
@Override
@Override public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
@SuppressWarnings("deprecation") { return false; }
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{ @Override
if(world.isClientSide()) return ActionResultType.SUCCESS; protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
boolean open = !state.getValue(OPEN); { super.createBlockStateDefinition(builder); builder.add(OPEN, POWERED); }
world.setBlock(pos, state.setValue(OPEN, open), 1|2);
world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f); @Override
return ActionResultType.CONSUME; @SuppressWarnings("deprecation")
} public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult rayTraceResult)
{
@Override if(world.isClientSide()) return InteractionResult.SUCCESS;
@SuppressWarnings("deprecation") boolean open = !state.getValue(OPEN);
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) world.setBlock(pos, state.setValue(OPEN, open), 1|2);
{ world.playSound(null, pos, open?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f);
if((world.isClientSide) || (!(state.getBlock() instanceof EdHatchBlock))) return; return InteractionResult.CONSUME;
boolean powered = world.hasNeighborSignal(pos); }
if(powered == state.getValue(POWERED)) return;
if(powered != state.getValue(OPEN)) world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f); @Override
world.setBlock(pos, state.setValue(OPEN, powered).setValue(POWERED, powered), 1|2); @SuppressWarnings("deprecation")
} public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
{
@Override if((world.isClientSide) || (!(state.getBlock() instanceof EdHatchBlock))) return;
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) boolean powered = world.hasNeighborSignal(pos);
{ return false; } if(powered == state.getValue(POWERED)) return;
if(powered != state.getValue(OPEN)) world.playSound(null, pos, powered?SoundEvents.IRON_DOOR_OPEN:SoundEvents.IRON_DOOR_CLOSE, SoundSource.BLOCKS, 0.7f, 1.4f);
@Override world.setBlock(pos, state.setValue(OPEN, powered).setValue(POWERED, powered), 1|2);
@SuppressWarnings("deprecation") }
public void entityInside(BlockState state, World world, BlockPos pos, Entity entity)
{ @Override
if((!state.getValue(OPEN)) || (!(entity instanceof PlayerEntity))) return; public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
final PlayerEntity player = (PlayerEntity)entity; { return false; }
if(entity.getLookAngle().y() > -0.75) return;
if(player.getDirection() != state.getValue(HORIZONTAL_FACING)) return; @Override
Vector3d ppos = player.position(); @SuppressWarnings("deprecation")
Vector3d centre = Vector3d.atBottomCenterOf(pos); public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity)
Vector3d v = centre.subtract(ppos); {
if(ppos.y() < (centre.y()-0.1) || (v.lengthSqr() > 0.3)) return; if((!state.getValue(OPEN)) || (!(entity instanceof final Player player))) return;
v = v.scale(0.3); if(entity.getLookAngle().y() > -0.75) return;
player.push(v.x, 0, v.z); if(player.getDirection() != state.getValue(HORIZONTAL_FACING)) return;
} Vec3 ppos = player.position();
} Vec3 centre = Vec3.atBottomCenterOf(pos);
Vec3 v = centre.subtract(ppos);
if(ppos.y() < (centre.y()-0.1) || (v.lengthSqr() > 0.3)) return;
v = v.scale(0.3);
player.push(v.x, 0, v.z);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,166 +1,172 @@
/* /*
* @file EdHorizontalSupportBlock.java * @file EdHorizontalSupportBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Horizontal ceiling support. Symmetric x axis, fixed in * Horizontal ceiling support. Symmetric x axis, fixed in
* xz plane, therefore boolean placement state. * xz plane, therefore boolean placement state.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.Block; import net.minecraft.core.Direction;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.sounds.SoundEvents;
import net.minecraft.entity.EntityType; import net.minecraft.sounds.SoundSource;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionHand;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.InteractionResult;
import net.minecraft.item.DirectionalPlaceContext; import net.minecraft.world.entity.EntityType;
import net.minecraft.item.ItemStack; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.entity.player.Player;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.item.ItemStack;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.*; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.Level;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.IWorld; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.block.BlockState; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import wile.engineersdecor.ModContent; import net.minecraft.world.phys.AABB;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.phys.BlockHitResult;
import wile.engineersdecor.libmc.detail.Inventories; import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.CollisionContext;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.Shapes;
import java.util.HashMap; import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.Map; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Inventories;
public class EdHorizontalSupportBlock extends DecorBlock.WaterLoggable implements IDecorBlock
{ import javax.annotation.Nullable;
public static final BooleanProperty EASTWEST = BooleanProperty.create("eastwest"); import java.util.HashMap;
public static final BooleanProperty LEFTBEAM = BooleanProperty.create("leftbeam"); import java.util.Map;
public static final BooleanProperty RIGHTBEAM = BooleanProperty.create("rightbeam");
public static final IntegerProperty DOWNCONNECT = IntegerProperty.create("downconnect", 0, 2);
protected final Map<BlockState, VoxelShape> AABBs;
public class EdHorizontalSupportBlock extends StandardBlocks.WaterLoggable
public EdHorizontalSupportBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB mainBeamAABB, final AxisAlignedBB eastBeamAABB, final AxisAlignedBB thinDownBeamAABB, final AxisAlignedBB thickDownBeamAABB) {
{ public static final BooleanProperty EASTWEST = BooleanProperty.create("eastwest");
super(config|DecorBlock.CFG_HORIZIONTAL, builder); public static final BooleanProperty LEFTBEAM = BooleanProperty.create("leftbeam");
Map<BlockState, VoxelShape> aabbs = new HashMap<>(); public static final BooleanProperty RIGHTBEAM = BooleanProperty.create("rightbeam");
for(boolean eastwest:EASTWEST.getPossibleValues()) { public static final IntegerProperty DOWNCONNECT = IntegerProperty.create("downconnect", 0, 2);
for(boolean leftbeam:LEFTBEAM.getPossibleValues()) { protected final Map<BlockState, VoxelShape> AABBs;
for(boolean rightbeam:RIGHTBEAM.getPossibleValues()) {
for(int downconnect:DOWNCONNECT.getPossibleValues()) { public EdHorizontalSupportBlock(long config, BlockBehaviour.Properties builder, final AABB mainBeamAABB, final AABB eastBeamAABB, final AABB thinDownBeamAABB, final AABB thickDownBeamAABB)
final BlockState state = defaultBlockState().setValue(EASTWEST, eastwest).setValue(LEFTBEAM, leftbeam).setValue(RIGHTBEAM, rightbeam).setValue(DOWNCONNECT, downconnect); {
VoxelShape shape = VoxelShapes.create(Auxiliaries.getRotatedAABB(mainBeamAABB, eastwest?Direction.EAST:Direction.NORTH, true)); super(config|DecorBlock.CFG_HORIZIONTAL, builder);
if(rightbeam) shape = VoxelShapes.joinUnoptimized(shape, VoxelShapes.create(Auxiliaries.getRotatedAABB(eastBeamAABB, eastwest?Direction.EAST:Direction.NORTH, true)), IBooleanFunction.OR); Map<BlockState, VoxelShape> aabbs = new HashMap<>();
if(leftbeam) shape = VoxelShapes.joinUnoptimized(shape, VoxelShapes.create(Auxiliaries.getRotatedAABB(eastBeamAABB, eastwest?Direction.WEST:Direction.SOUTH, true)), IBooleanFunction.OR); for(boolean eastwest:EASTWEST.getPossibleValues()) {
if(downconnect==1) shape = VoxelShapes.joinUnoptimized(shape, VoxelShapes.create(thinDownBeamAABB), IBooleanFunction.OR); for(boolean leftbeam:LEFTBEAM.getPossibleValues()) {
if(downconnect==2) shape = VoxelShapes.joinUnoptimized(shape, VoxelShapes.create(thickDownBeamAABB), IBooleanFunction.OR); for(boolean rightbeam:RIGHTBEAM.getPossibleValues()) {
aabbs.put(state.setValue(WATERLOGGED, false), shape); for(int downconnect:DOWNCONNECT.getPossibleValues()) {
aabbs.put(state.setValue(WATERLOGGED, true), shape); final BlockState state = defaultBlockState().setValue(EASTWEST, eastwest).setValue(LEFTBEAM, leftbeam).setValue(RIGHTBEAM, rightbeam).setValue(DOWNCONNECT, downconnect);
} VoxelShape shape = Shapes.create(Auxiliaries.getRotatedAABB(mainBeamAABB, eastwest?Direction.EAST:Direction.NORTH, true));
} if(rightbeam) shape = Shapes.joinUnoptimized(shape, Shapes.create(Auxiliaries.getRotatedAABB(eastBeamAABB, eastwest?Direction.EAST:Direction.NORTH, true)), BooleanOp.OR);
} if(leftbeam) shape = Shapes.joinUnoptimized(shape, Shapes.create(Auxiliaries.getRotatedAABB(eastBeamAABB, eastwest?Direction.WEST:Direction.SOUTH, true)), BooleanOp.OR);
} if(downconnect==1) shape = Shapes.joinUnoptimized(shape, Shapes.create(thinDownBeamAABB), BooleanOp.OR);
AABBs = aabbs; if(downconnect==2) shape = Shapes.joinUnoptimized(shape, Shapes.create(thickDownBeamAABB), BooleanOp.OR);
} aabbs.put(state.setValue(WATERLOGGED, false), shape);
aabbs.put(state.setValue(WATERLOGGED, true), shape);
@Override }
public RenderTypeHint getRenderTypeHint() }
{ return RenderTypeHint.CUTOUT; } }
}
@Override AABBs = aabbs;
public boolean isPossibleToRespawnInThis() }
{ return false; }
@Override
@Override public RenderTypeHint getRenderTypeHint()
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) { return RenderTypeHint.CUTOUT; }
{ return false; }
@Override
@Override public boolean isPossibleToRespawnInThis()
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext) { return false; }
{ return AABBs.get(state); }
@Override
@Override public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) { return false; }
{ return getShape(state, world, pos, selectionContext); }
@Override
@Override public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext)
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) { return AABBs.get(state); }
{ super.createBlockStateDefinition(builder); builder.add(EASTWEST, RIGHTBEAM, LEFTBEAM, DOWNCONNECT); }
@Override
@Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
@Nullable { return getShape(state, world, pos, selectionContext); }
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return temp_block_update_until_better(super.getStateForPlacement(context).setValue(EASTWEST, context.getHorizontalDirection().getAxis()==Direction.Axis.X), context.getLevel(), context.getClickedPos()); } @Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
private BlockState temp_block_update_until_better(BlockState state, IWorld world, BlockPos pos) { super.createBlockStateDefinition(builder); builder.add(EASTWEST, RIGHTBEAM, LEFTBEAM, DOWNCONNECT); }
{
boolean ew = state.getValue(EASTWEST); @Override
final BlockState rstate = world.getBlockState((!ew) ? (pos.east()) : (pos.south()) ); @Nullable
final BlockState lstate = world.getBlockState((!ew) ? (pos.west()) : (pos.north()) ); public BlockState getStateForPlacement(BlockPlaceContext context)
final BlockState dstate = world.getBlockState(pos.below()); { return temp_block_update_until_better(super.getStateForPlacement(context).setValue(EASTWEST, context.getHorizontalDirection().getAxis()==Direction.Axis.X), context.getLevel(), context.getClickedPos()); }
int down_connector = 0;
if((dstate.getBlock() instanceof EdStraightPoleBlock)) { private BlockState temp_block_update_until_better(BlockState state, LevelAccessor world, BlockPos pos)
final Direction dfacing = dstate.getValue(EdStraightPoleBlock.FACING); {
final EdStraightPoleBlock pole = (EdStraightPoleBlock)dstate.getBlock(); boolean ew = state.getValue(EASTWEST);
if((dfacing.getAxis() == Direction.Axis.Y)) { final BlockState rstate = world.getBlockState((!ew) ? (pos.east()) : (pos.south()) );
if((pole== ModContent.THICK_STEEL_POLE) || ((pole==ModContent.THICK_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) { final BlockState lstate = world.getBlockState((!ew) ? (pos.west()) : (pos.north()) );
down_connector = 2; final BlockState dstate = world.getBlockState(pos.below());
} else if((pole==ModContent.THIN_STEEL_POLE) || ((pole==ModContent.THIN_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) { int down_connector = 0;
down_connector = 1; if((dstate.getBlock() instanceof final EdStraightPoleBlock pole)) {
} final Direction dfacing = dstate.getValue(EdStraightPoleBlock.FACING);
} if((dfacing.getAxis() == Direction.Axis.Y)) {
} if((pole== ModContent.THICK_STEEL_POLE) || ((pole==ModContent.THICK_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) {
return state.setValue(RIGHTBEAM, (rstate.getBlock()==this) && (rstate.getValue(EASTWEST) != ew)) down_connector = 2;
.setValue(LEFTBEAM , (lstate.getBlock()==this) && (lstate.getValue(EASTWEST) != ew)) } else if((pole==ModContent.THIN_STEEL_POLE) || ((pole==ModContent.THIN_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) {
.setValue(DOWNCONNECT , down_connector); down_connector = 1;
} }
}
@Override }
@SuppressWarnings("deprecation") return state.setValue(RIGHTBEAM, (rstate.getBlock()==this) && (rstate.getValue(EASTWEST) != ew))
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos) .setValue(LEFTBEAM , (lstate.getBlock()==this) && (lstate.getValue(EASTWEST) != ew))
{ return temp_block_update_until_better(state, world, pos); } .setValue(DOWNCONNECT , down_connector);
}
@Override
@SuppressWarnings("deprecation") @Override
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) @SuppressWarnings("deprecation")
{ public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
ItemStack held_stack = player.getItemInHand(hand); { return temp_block_update_until_better(state, world, pos); }
if((held_stack.isEmpty()) || (held_stack.getItem() != this.asItem())) return ActionResultType.PASS;
if(!(hit.getDirection().getAxis().isVertical())) return ActionResultType.PASS; @Override
final Direction placement_direction = player.getDirection(); @SuppressWarnings("deprecation")
final BlockPos adjacent_pos = pos.relative(placement_direction); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
final BlockState adjacent = world.getBlockState(adjacent_pos); {
final BlockItemUseContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite()); ItemStack held_stack = player.getItemInHand(hand);
if(!adjacent.canBeReplaced(ctx)) return ActionResultType.sidedSuccess(world.isClientSide()); if((held_stack.isEmpty()) || (held_stack.getItem() != this.asItem())) return InteractionResult.PASS;
final BlockState new_state = getStateForPlacement(ctx); if(!(hit.getDirection().getAxis().isVertical())) return InteractionResult.PASS;
if(new_state == null) return ActionResultType.FAIL; final Direction placement_direction = player.getDirection();
if(!world.setBlock(adjacent_pos, new_state, 1|2)) return ActionResultType.FAIL; final BlockPos adjacent_pos = pos.relative(placement_direction);
world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundCategory.BLOCKS, 1f, 1f); final BlockState adjacent = world.getBlockState(adjacent_pos);
if(!player.isCreative()) { final BlockPlaceContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite());
held_stack.shrink(1); if(!adjacent.canBeReplaced(ctx)) return InteractionResult.sidedSuccess(world.isClientSide());
Inventories.setItemInPlayerHand(player, hand, held_stack); final BlockState new_state = getStateForPlacement(ctx);
} if(new_state == null) return InteractionResult.FAIL;
return ActionResultType.sidedSuccess(world.isClientSide()); if(!world.setBlock(adjacent_pos, new_state, 1|2)) return InteractionResult.FAIL;
} world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f);
if(!player.isCreative()) {
@Override held_stack.shrink(1);
@SuppressWarnings("deprecation") Inventories.setItemInPlayerHand(player, hand, held_stack);
public BlockState rotate(BlockState state, Rotation rot) }
{ return (rot==Rotation.CLOCKWISE_180) ? state : state.setValue(EASTWEST, !state.getValue(EASTWEST)); } return InteractionResult.sidedSuccess(world.isClientSide());
}
@Override
@SuppressWarnings("deprecation") @Override
public BlockState mirror(BlockState state, Mirror mirrorIn) @SuppressWarnings("deprecation")
{ return state; } public BlockState rotate(BlockState state, Rotation rot)
{ return (rot==Rotation.CLOCKWISE_180) ? state : state.setValue(EASTWEST, !state.getValue(EASTWEST)); }
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state; }
}

File diff suppressed because it is too large Load diff

View file

@ -1,118 +1,121 @@
/* /*
* @file EdLadderBlock.java * @file EdLadderBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Ladder block. The implementation is based on the vanilla * Ladder block. The implementation is based on the vanilla
* net.minecraft.block.BlockLadder. Minor changes to enable * net.minecraft.block.BlockLadder. Minor changes to enable
* later configuration (for block list based construction * later configuration (for block list based construction
* time configuration), does not drop when the block behind * time configuration), does not drop when the block behind
* is broken, etc. * is broken, etc.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import net.minecraft.entity.LivingEntity; import net.minecraft.core.Direction;
import net.minecraft.world.IWorldReader; import net.minecraft.network.chat.Component;
import wile.engineersdecor.ModConfig; import net.minecraft.world.entity.EntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.util.math.vector.*; import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.item.ItemStack;
import net.minecraft.block.BlockState; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.block.*; import net.minecraft.world.level.BlockGetter;
import net.minecraft.block.material.PushReaction; import net.minecraft.world.level.LevelReader;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.LadderBlock;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.material.PushReaction;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.phys.AABB;
import net.minecraft.world.IBlockReader; import net.minecraft.world.phys.Vec3;
import net.minecraft.util.*; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.util.math.BlockPos; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.api.distmarker.OnlyIn; import wile.engineersdecor.ModConfig;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import java.util.List; import wile.engineersdecor.libmc.detail.Auxiliaries;
import javax.annotation.Nullable;
public class EdLadderBlock extends LadderBlock implements IDecorBlock import java.util.List;
{
protected static final AxisAlignedBB EDLADDER_UNROTATED_AABB = Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 3);
protected static final VoxelShape EDLADDER_SOUTH_AABB = VoxelShapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.SOUTH, false)); public class EdLadderBlock extends LadderBlock implements StandardBlocks.IStandardBlock
protected static final VoxelShape EDLADDER_EAST_AABB = VoxelShapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.EAST, false)); {
protected static final VoxelShape EDLADDER_WEST_AABB = VoxelShapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.WEST, false)); protected static final AABB EDLADDER_UNROTATED_AABB = Auxiliaries.getPixeledAABB(3, 0, 0, 13, 16, 3);
protected static final VoxelShape EDLADDER_NORTH_AABB = VoxelShapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.NORTH, false)); protected static final VoxelShape EDLADDER_SOUTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.SOUTH, false));
private static boolean without_speed_boost_ = false; protected static final VoxelShape EDLADDER_EAST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.EAST, false));
protected static final VoxelShape EDLADDER_WEST_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.WEST, false));
public static void on_config(boolean without_speed_boost) protected static final VoxelShape EDLADDER_NORTH_AABB = Shapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.NORTH, false));
{ private static boolean without_speed_boost_ = false;
without_speed_boost_ = without_speed_boost;
ModConfig.log("Config ladder: without-speed-boost:" + without_speed_boost_); public static void on_config(boolean without_speed_boost)
} {
without_speed_boost_ = without_speed_boost;
public EdLadderBlock(long config, AbstractBlock.Properties builder) ModConfig.log("Config ladder: without-speed-boost:" + without_speed_boost_);
{ super(builder); } }
@Override public EdLadderBlock(long config, BlockBehaviour.Properties builder)
public RenderTypeHint getRenderTypeHint() { super(builder); }
{ return RenderTypeHint.CUTOUT; }
@Override
@Override public RenderTypeHint getRenderTypeHint()
@OnlyIn(Dist.CLIENT) { return RenderTypeHint.CUTOUT; }
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } @Override
@OnlyIn(Dist.CLIENT)
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos) public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
{ { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
switch ((Direction)state.getValue(FACING)) {
case NORTH: return EDLADDER_NORTH_AABB; public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos)
case SOUTH: return EDLADDER_SOUTH_AABB; {
case WEST: return EDLADDER_WEST_AABB; return switch(state.getValue(FACING)) {
default: return EDLADDER_EAST_AABB; case NORTH -> EDLADDER_NORTH_AABB;
} case SOUTH -> EDLADDER_SOUTH_AABB;
} case WEST -> EDLADDER_WEST_AABB;
default -> EDLADDER_EAST_AABB;
@Override };
public boolean isPossibleToRespawnInThis() }
{ return false; }
@Override
@Override public boolean isPossibleToRespawnInThis()
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) { return false; }
{ return false; }
@Override
@Override public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
@SuppressWarnings("deprecation") { return false; }
public PushReaction getPistonPushReaction(BlockState state)
{ return PushReaction.NORMAL; } @Override
@SuppressWarnings("deprecation")
@Override public PushReaction getPistonPushReaction(BlockState state)
public boolean isLadder(BlockState state, IWorldReader world, BlockPos pos, LivingEntity entity) { return PushReaction.NORMAL; }
{ return true; }
@Override
// Player update event, forwarded from the main mod instance. public boolean isLadder(BlockState state, LevelReader world, BlockPos pos, LivingEntity entity)
public static void onPlayerUpdateEvent(final PlayerEntity player) { return true; }
{
if((without_speed_boost_) || (player.isOnGround()) || (!player.onClimbable()) || (player.isSteppingCarefully()) || (player.isSpectator())) return; // Player update event, forwarded from the main mod instance.
double lvy = player.getLookAngle().y; public static void onPlayerUpdateEvent(final Player player)
if(Math.abs(lvy) < 0.92) return; {
final BlockPos pos = player.blockPosition(); if((without_speed_boost_) || (player.isOnGround()) || (!player.onClimbable()) || (player.isSteppingCarefully()) || (player.isSpectator())) return;
final BlockState state = player.level.getBlockState(pos); double lvy = player.getLookAngle().y;
if(!(state.getBlock() instanceof EdLadderBlock)) return; if(Math.abs(lvy) < 0.92) return;
player.fallDistance = 0; final BlockPos pos = player.blockPosition();
if((player.getDeltaMovement().y() < 0) == (player.getLookAngle().y < 0)) { final BlockState state = player.level.getBlockState(pos);
player.makeStuckInBlock(state, new Vector3d(0.2, (lvy>0)?(3):(6), 0.2)); if(!(state.getBlock() instanceof EdLadderBlock)) return;
if(Math.abs(player.getDeltaMovement().y()) > 0.1) { player.fallDistance = 0;
Vector3d vdiff = Vector3d.atBottomCenterOf(pos).subtract(player.position()).scale(1); if((player.getDeltaMovement().y() < 0) == (player.getLookAngle().y < 0)) {
vdiff.add(Vector3d.atBottomCenterOf(state.getValue(FACING).getNormal()).scale(0.5)); player.makeStuckInBlock(state, new Vec3(0.2, (lvy>0)?(3):(6), 0.2));
vdiff = new Vector3d(vdiff.x, player.getDeltaMovement().y, vdiff.z); if(Math.abs(player.getDeltaMovement().y()) > 0.1) {
player.setDeltaMovement(vdiff); Vec3 vdiff = Vec3.atBottomCenterOf(pos).subtract(player.position()).scale(1);
} vdiff.add(Vec3.atBottomCenterOf(state.getValue(FACING).getNormal()).scale(0.5));
} else if(player.getLookAngle().y > 0) { vdiff = new Vec3(vdiff.x, player.getDeltaMovement().y, vdiff.z);
player.makeStuckInBlock(state, new Vector3d(1, 0.05, 1)); player.setDeltaMovement(vdiff);
} }
} } else if(player.getLookAngle().y > 0) {
player.makeStuckInBlock(state, new Vec3(1, 0.05, 1));
} }
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,278 +1,273 @@
/* /*
* @file EdPipeValve.java * @file EdPipeValve.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Basically a piece of pipe that does not connect to * Basically a piece of pipe that does not connect to
* pipes on the side, and conducts fluids only in one way. * pipes on the side, and conducts fluids only in one way.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.world.IWorldReader; import net.minecraft.core.BlockPos;
import net.minecraft.world.IWorld; import net.minecraft.core.Direction;
import net.minecraft.world.World; import net.minecraft.util.Mth;
import net.minecraft.world.IBlockReader; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.item.ItemStack;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.block.AbstractBlock; import net.minecraft.world.level.BlockGetter;
import net.minecraft.block.Block; import net.minecraft.world.level.Level;
import net.minecraft.block.BlockState; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.LevelReader;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.block.Rotation;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.Direction; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.Rotation; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.phys.AABB;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.ModConfig; import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModContent; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import javax.annotation.Nonnull; import wile.engineersdecor.libmc.detail.RsSignals;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class EdPipeValve
{
public static final int CFG_CHECK_VALVE = 0x1; public class EdPipeValve
public static final int CFG_ANALOG_VALVE = 0x2; {
public static final int CFG_REDSTONE_CONTROLLED_VALVE = 0x4; public static final int CFG_CHECK_VALVE = 0x1;
public static final int CFG_ANALOG_VALVE = 0x2;
public static void on_config(int container_size_decl, int redstone_slope) public static final int CFG_REDSTONE_CONTROLLED_VALVE = 0x4;
{
PipeValveTileEntity.fluid_maxflow_mb = MathHelper.clamp(container_size_decl, 1, 10000); public static void on_config(int container_size_decl, int redstone_slope)
PipeValveTileEntity.redstone_flow_slope_mb = MathHelper.clamp(redstone_slope, 1, 10000); {
ModConfig.log("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig."); PipeValveTileEntity.fluid_maxflow_mb = Mth.clamp(container_size_decl, 1, 10000);
} PipeValveTileEntity.redstone_flow_slope_mb = Mth.clamp(redstone_slope, 1, 10000);
ModConfig.log("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig.");
//-------------------------------------------------------------------------------------------------------------------- }
// Block
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
// Block
public static class PipeValveBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock //--------------------------------------------------------------------------------------------------------------------
{
public static final BooleanProperty RS_CN_N = BooleanProperty.create("rs_n"); public static class PipeValveBlock extends StandardBlocks.DirectedWaterLoggable implements StandardEntityBlocks.IStandardEntityBlock<PipeValveTileEntity>
public static final BooleanProperty RS_CN_S = BooleanProperty.create("rs_s"); {
public static final BooleanProperty RS_CN_E = BooleanProperty.create("rs_e"); public static final BooleanProperty RS_CN_N = BooleanProperty.create("rs_n");
public static final BooleanProperty RS_CN_W = BooleanProperty.create("rs_w"); public static final BooleanProperty RS_CN_S = BooleanProperty.create("rs_s");
public static final BooleanProperty RS_CN_U = BooleanProperty.create("rs_u"); public static final BooleanProperty RS_CN_E = BooleanProperty.create("rs_e");
public static final BooleanProperty RS_CN_D = BooleanProperty.create("rs_d"); public static final BooleanProperty RS_CN_W = BooleanProperty.create("rs_w");
public static final BooleanProperty RS_CN_U = BooleanProperty.create("rs_u");
public final int valve_config; public static final BooleanProperty RS_CN_D = BooleanProperty.create("rs_d");
public final int valve_config;
public PipeValveBlock(long config, int valve_config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); this.valve_config = valve_config; } public PipeValveBlock(long config, int valve_config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); this.valve_config = valve_config; }
private BlockState get_rsconnector_state(BlockState state, IWorld world, BlockPos pos, @Nullable BlockPos fromPos)
{ @Override
if((valve_config & (CFG_REDSTONE_CONTROLLED_VALVE))==0) return state; @Nullable
Direction.Axis bfa = state.getValue(FACING).getAxis(); public BlockEntityType<EdPipeValve.PipeValveTileEntity> getBlockEntityType()
for(Direction f:Direction.values()) { { return ModContent.TET_STRAIGHT_PIPE_VALVE; }
boolean cn = (f.getAxis() != bfa);
if(cn) { @Override
BlockPos nbp = pos.relative(f); public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
if((fromPos != null) && (!nbp.equals(fromPos))) continue; // do not change connectors except form the frompos. { return Shapes.block(); }
BlockState nbs = world.getBlockState(nbp);
if((nbs.getBlock() instanceof PipeValveBlock) || ((!nbs.isSignalSource()) && (!nbs.canConnectRedstone(world, nbp, f.getOpposite())))) cn = false; @Override
} protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
switch(f) { { super.createBlockStateDefinition(builder); builder.add(RS_CN_N, RS_CN_S, RS_CN_E, RS_CN_W, RS_CN_U, RS_CN_D); }
case NORTH: state = state.setValue(RS_CN_N, cn); break;
case SOUTH: state = state.setValue(RS_CN_S, cn); break; @Override
case EAST: state = state.setValue(RS_CN_E, cn); break; @Nullable
case WEST: state = state.setValue(RS_CN_W, cn); break; public BlockState getStateForPlacement(BlockPlaceContext context)
case UP: state = state.setValue(RS_CN_U, cn); break; {
case DOWN: state = state.setValue(RS_CN_D, cn); break; return super.getStateForPlacement(context).setValue(RS_CN_N, false).setValue(RS_CN_S, false).setValue(RS_CN_E, false)
} .setValue(RS_CN_W, false).setValue(RS_CN_U, false).setValue(RS_CN_D, false);
} }
return state;
} @Override
@SuppressWarnings("deprecation")
@Override public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) { return get_rsconnector_state(state, world, pos, null); }
{ return VoxelShapes.block(); }
@Override
@Override public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) { world.updateNeighborsAt(pos,this); }
{ super.createBlockStateDefinition(builder); builder.add(RS_CN_N, RS_CN_S, RS_CN_E, RS_CN_W, RS_CN_U, RS_CN_D); }
@Override
@Override public BlockState rotate(BlockState state, LevelAccessor world, BlockPos pos, Rotation direction)
@Nullable { return get_rsconnector_state(state, world, pos, null); } // don't rotate at all
public BlockState getStateForPlacement(BlockItemUseContext context)
{ @Override
return super.getStateForPlacement(context).setValue(RS_CN_N, false).setValue(RS_CN_S, false).setValue(RS_CN_E, false) public boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side)
.setValue(RS_CN_W, false).setValue(RS_CN_U, false).setValue(RS_CN_D, false); { return (side!=null) && (side!=state.getValue(FACING)) && (side!=state.getValue(FACING).getOpposite()); }
}
@Override
@Override public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
@SuppressWarnings("deprecation") { return false; }
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{ return get_rsconnector_state(state, world, pos, null); } @Override
@SuppressWarnings("deprecation") // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; }
@Override public boolean isSignalSource(BlockState p_60571_)
public void setPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { return true; }
{ world.updateNeighborsAt(pos,this); }
@Override
@Override @SuppressWarnings("deprecation")
public boolean hasTileEntity(BlockState state) public int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side)
{ return true; } { return 0; }
@Override @Override
@Nullable @SuppressWarnings("deprecation")
public TileEntity createTileEntity(BlockState state, IBlockReader world) public int getDirectSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side)
{ return new PipeValveTileEntity(); } { return 0; }
@Override private BlockState get_rsconnector_state(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockPos fromPos)
public BlockState rotate(BlockState state, IWorld world, BlockPos pos, Rotation direction) {
{ return get_rsconnector_state(state, world, pos, null); } // don't rotate at all if((valve_config & (CFG_REDSTONE_CONTROLLED_VALVE))==0) return state;
Direction.Axis bfa = state.getValue(FACING).getAxis();
@Override for(Direction f:Direction.values()) {
public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, @Nullable Direction side) boolean cn = (f.getAxis() != bfa);
{ return (side!=null) && (side!=state.getValue(FACING)) && (side!=state.getValue(FACING).getOpposite()); } if(cn) {
BlockPos nbp = pos.relative(f);
@Override if((fromPos != null) && (!nbp.equals(fromPos))) continue; // do not change connectors except form the frompos.
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) BlockState nbs = world.getBlockState(nbp);
{ return false; } if((nbs.getBlock() instanceof PipeValveBlock) || (!nbs.isSignalSource()) && (RsSignals.hasSignalConnector(nbs, world, nbp, f.getOpposite()))) cn = false;
}
@Override switch (f) {
@SuppressWarnings("deprecation") case NORTH -> state = state.setValue(RS_CN_N, cn);
public boolean isSignalSource(BlockState state) case SOUTH -> state = state.setValue(RS_CN_S, cn);
{ return true; } case EAST -> state = state.setValue(RS_CN_E, cn);
case WEST -> state = state.setValue(RS_CN_W, cn);
@Override case UP -> state = state.setValue(RS_CN_U, cn);
@SuppressWarnings("deprecation") case DOWN -> state = state.setValue(RS_CN_D, cn);
public int getSignal(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side) }
{ return 0; } }
return state;
@Override }
@SuppressWarnings("deprecation")
public int getDirectSignal(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side) }
{ return 0; }
} //--------------------------------------------------------------------------------------------------------------------
// Tile entity
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
// Tile entity
//-------------------------------------------------------------------------------------------------------------------- public static class PipeValveTileEntity extends StandardEntityBlocks.StandardBlockEntity
{
public static class PipeValveTileEntity extends TileEntity implements ICapabilityProvider //, IFluidPipe protected static int fluid_maxflow_mb = 1000;
{ protected static int redstone_flow_slope_mb = 1000/15;
protected static int fluid_maxflow_mb = 1000; private final Direction block_facing_ = null;
protected static int redstone_flow_slope_mb = 1000/15; private boolean filling_ = false;
private Direction block_facing_ = null; private int valve_config_;
private boolean filling_ = false;
private int valve_config_; public PipeValveTileEntity(BlockPos pos, BlockState state)
{ super(ModContent.TET_STRAIGHT_PIPE_VALVE, pos, state); }
public PipeValveTileEntity()
{ this(ModContent.TET_STRAIGHT_PIPE_VALVE); } private Direction block_facing()
{
public PipeValveTileEntity(TileEntityType<?> te_type) BlockState st = getLevel().getBlockState(getBlockPos());
{ super(te_type); } return (st.getBlock() instanceof PipeValveBlock) ? st.getValue(PipeValveBlock.FACING) : Direction.NORTH;
}
private Direction block_facing()
{ private long valve_config()
BlockState st = getLevel().getBlockState(getBlockPos()); {
return (st.getBlock() instanceof PipeValveBlock) ? st.getValue(PipeValveBlock.FACING) : Direction.NORTH; if(valve_config_ <= 0) {
} final Block block = getLevel().getBlockState(getBlockPos()).getBlock();
if(block instanceof PipeValveBlock) valve_config_ = ((PipeValveBlock)block).valve_config;
private long valve_config() }
{ return valve_config_;
if(valve_config_ <= 0) { }
final Block block = getLevel().getBlockState(getBlockPos()).getBlock();
if(block instanceof PipeValveBlock) valve_config_ = ((PipeValveBlock)block).valve_config; // BlockEntity -----------------------------------------------------------------------------
}
return valve_config_; @Override
} public void setRemoved()
{
// TileEntity ----------------------------------------------------------------------------- super.setRemoved();
back_flow_handler_.invalidate();
@Override fluid_handler_.invalidate();
public void setRemoved() }
{
super.setRemoved(); // ICapabilityProvider --------------------------------------------------------------------
back_flow_handler_.invalidate();
fluid_handler_.invalidate(); private final LazyOptional<IFluidHandler> back_flow_handler_ = LazyOptional.of(BackFlowHandler::new);
} private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new MainFlowHandler(this));
// ICapabilityProvider -------------------------------------------------------------------- @Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
private final LazyOptional<IFluidHandler> back_flow_handler_ = LazyOptional.of(() -> (IFluidHandler)new BackFlowHandler()); {
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new MainFlowHandler(this)); if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
Direction bf = block_facing();
@Override if(facing == bf) return back_flow_handler_.cast();
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) if(facing == bf.getOpposite()) return fluid_handler_.cast();
{ return LazyOptional.empty();
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { }
Direction bf = block_facing(); return super.getCapability(capability, facing);
if(facing == bf) return back_flow_handler_.cast(); }
if(facing == bf.getOpposite()) return fluid_handler_.cast();
return LazyOptional.empty(); // IFluidHandlers
}
return super.getCapability(capability, facing); @Nullable
} private IFluidHandler forward_fluid_handler()
{
// IFluidHandlers final BlockEntity te = level.getBlockEntity(worldPosition.relative(block_facing()));
if(te == null) return null;
@Nullable return te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing().getOpposite()).orElse(null);
private IFluidHandler forward_fluid_handler() }
{
final TileEntity te = level.getBlockEntity(worldPosition.relative(block_facing())); // Forward flow handler --
if(te == null) return null;
return te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing().getOpposite()).orElse(null); private static class MainFlowHandler implements IFluidHandler
} {
private final PipeValveTileEntity te;
// Forward flow handler -- public MainFlowHandler(PipeValveTileEntity te) { this.te = te; }
@Override public int getTanks() { return 1; }
private static class MainFlowHandler implements IFluidHandler @Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
{ @Override public int getTankCapacity(int tank) { return fluid_maxflow_mb; }
private final PipeValveTileEntity te; @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
public MainFlowHandler(PipeValveTileEntity te) { this.te = te; } @Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; }
@Override public int getTanks() { return 1; } @Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return fluid_maxflow_mb; } @Override public int fill(FluidStack resource, FluidAction action)
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; } {
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; } if(te.filling_) return 0;
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; } final IFluidHandler fh = te.forward_fluid_handler();
if(fh==null) return 0;
@Override public int fill(FluidStack resource, FluidAction action) FluidStack res = resource.copy();
{ if((te.valve_config() & CFG_REDSTONE_CONTROLLED_VALVE) != 0) {
if(te.filling_) return 0; int rs = te.level.getBestNeighborSignal(te.worldPosition);
final IFluidHandler fh = te.forward_fluid_handler(); if(rs <= 0) return 0;
if(fh==null) return 0; if(((te.valve_config() & CFG_ANALOG_VALVE) != 0) && (rs < 15)) res.setAmount(Mth.clamp(rs * redstone_flow_slope_mb, 1, res.getAmount()));
FluidStack res = resource.copy(); }
if((te.valve_config() & CFG_REDSTONE_CONTROLLED_VALVE) != 0) { if(res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb);
int rs = te.level.getBestNeighborSignal(te.worldPosition); te.filling_ = true;
if(rs <= 0) return 0; int n_filled = fh.fill(res, action);
if(((te.valve_config() & CFG_ANALOG_VALVE) != 0) && (rs < 15)) res.setAmount(MathHelper.clamp(rs * redstone_flow_slope_mb, 1, res.getAmount())); te.filling_ = false;
} return n_filled;
if(res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb); }
te.filling_ = true; }
int n_filled = fh.fill(res, action);
te.filling_ = false; // Back flow prevention handler --
return n_filled;
} private static class BackFlowHandler implements IFluidHandler
} {
@Override public int getTanks() { return 1; }
// Back flow prevention handler -- @Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return 0; }
private static class BackFlowHandler implements IFluidHandler @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return false; }
{ @Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public int getTanks() { return 1; } @Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; } @Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return 0; } }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return false; } }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; } }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; }
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,96 +1,100 @@
/* /*
* @file EdCatwalkBlock.java * @file EdCatwalkBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Bottom aligned platforms with railings. * Bottom aligned platforms with railings.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.BlockState; import net.minecraft.core.Direction;
import net.minecraft.block.Blocks; import net.minecraft.world.InteractionHand;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.InteractionResult;
import net.minecraft.fluid.Fluids; import net.minecraft.world.entity.player.Player;
import net.minecraft.item.*; import net.minecraft.world.item.ItemStack;
import net.minecraft.state.BooleanProperty; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.*; import net.minecraft.world.level.Level;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.Blocks;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.World; import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.AABB;
import javax.annotation.Nullable; import net.minecraft.world.phys.BlockHitResult;
import java.util.ArrayList; import net.minecraft.world.phys.Vec3;
import java.util.Collections; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import java.util.List;
import javax.annotation.Nullable;
import java.util.ArrayList;
public class EdRailingBlock extends DecorBlock.HorizontalFourWayWaterLoggable implements IDecorBlock import java.util.Collections;
{ import java.util.List;
public EdRailingBlock(long config, AbstractBlock.Properties properties, final AxisAlignedBB base_aabb, final AxisAlignedBB railing_aabb)
{ super(config, properties, base_aabb, railing_aabb, 0); }
public class EdRailingBlock extends StandardBlocks.HorizontalFourWayWaterLoggable
@Override {
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) public EdRailingBlock(long config, BlockBehaviour.Properties properties, final AABB base_aabb, final AABB railing_aabb)
{ return true; } { super(config, properties, base_aabb, railing_aabb, 0); }
@Override @Override
@SuppressWarnings("deprecation") public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
public boolean canBeReplaced(BlockState state, BlockItemUseContext useContext) { return true; }
{ return (useContext.getItemInHand().getItem() == asItem()) ? true : super.canBeReplaced(state, useContext); }
@Override
@Override @SuppressWarnings("deprecation")
@Nullable public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext)
public BlockState getStateForPlacement(BlockItemUseContext context) { return (useContext.getItemInHand().getItem() == asItem()) || super.canBeReplaced(state, useContext); }
{
if(context.getClickedFace() != Direction.UP) return null; @Override
BlockState state = context.getLevel().getBlockState(context.getClickedPos()); @Nullable
if(state.getBlock() != this) state = super.getStateForPlacement(context); public BlockState getStateForPlacement(BlockPlaceContext context)
final Vector3d rhv = context.getClickLocation().subtract(Vector3d.atCenterOf(context.getClickedPos())); {
BooleanProperty side = getDirectionProperty(Direction.getNearest(rhv.x, 0, rhv.z)); if(context.getClickedFace() != Direction.UP) return null;
return state.setValue(side, true); BlockState state = context.getLevel().getBlockState(context.getClickedPos());
} if(state.getBlock() != this) state = super.getStateForPlacement(context);
final Vec3 rhv = context.getClickLocation().subtract(Vec3.atCenterOf(context.getClickedPos()));
@Override BooleanProperty side = getDirectionProperty(Direction.getNearest(rhv.x, 0, rhv.z));
@SuppressWarnings("deprecation") return state.setValue(side, true);
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) }
{
if(player.getItemInHand(hand).getItem() != asItem()) return ActionResultType.PASS; @Override
Direction face = hit.getDirection(); @SuppressWarnings("deprecation")
if(!face.getAxis().isHorizontal()) return ActionResultType.sidedSuccess(world.isClientSide()); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
final Vector3d rhv = hit.getLocation().subtract(Vector3d.atCenterOf(hit.getBlockPos())); {
if(rhv.multiply(Vector3d.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side. if(player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS;
BooleanProperty railing = getDirectionProperty(face); Direction face = hit.getDirection();
boolean add = (!state.getValue(railing)); if(!face.getAxis().isHorizontal()) return InteractionResult.sidedSuccess(world.isClientSide());
state = state.setValue(railing, add); final Vec3 rhv = hit.getLocation().subtract(Vec3.atCenterOf(hit.getBlockPos()));
if((!state.getValue(NORTH)) && (!state.getValue(EAST)) && (!state.getValue(SOUTH)) && (!state.getValue(WEST))) { if(rhv.multiply(Vec3.atLowerCornerOf(face.getNormal())).scale(2).lengthSqr() < 0.99) face = face.getOpposite(); // click on railing, not the outer side.
state = (world.getFluidState(pos).getType() == Fluids.WATER) ? Blocks.WATER.defaultBlockState() : (Blocks.AIR.defaultBlockState()); BooleanProperty railing = getDirectionProperty(face);
EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); boolean add = (!state.getValue(railing));
} else { state = state.setValue(railing, add);
EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1); if((!state.getValue(NORTH)) && (!state.getValue(EAST)) && (!state.getValue(SOUTH)) && (!state.getValue(WEST))) {
} state = (world.getFluidState(pos).getType() == Fluids.WATER) ? Blocks.WATER.defaultBlockState() : (Blocks.AIR.defaultBlockState());
return ActionResultType.sidedSuccess(world.isClientSide()); EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1);
} } else {
EdCatwalkBlock.place_consume(state, world, pos, player, hand, add ? 1 : -1);
// -- IDecorBlock }
return InteractionResult.sidedSuccess(world.isClientSide());
@Override }
public boolean hasDynamicDropList()
{ return true; } // -- IDecorBlock
@Override @Override
public List<ItemStack> dropList(BlockState state, World world, @Nullable TileEntity te, boolean explosion) public boolean hasDynamicDropList()
{ { return true; }
if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
List<ItemStack> drops = new ArrayList<>(); @Override
int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0); public List<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion)
drops.add(new ItemStack(state.getBlock().asItem(), Math.max(n, 1))); {
return drops; if(world.isClientSide()) return Collections.singletonList(ItemStack.EMPTY);
} List<ItemStack> drops = new ArrayList<>();
int n = (state.getValue(NORTH)?1:0)+(state.getValue(EAST)?1:0)+(state.getValue(SOUTH)?1:0)+(state.getValue(WEST)?1:0);
} drops.add(new ItemStack(state.getBlock().asItem(), Math.max(n, 1)));
return drops;
}
}

View file

@ -1,231 +1,226 @@
/* /*
* @file EdRoofBlock.java * @file EdRoofBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Roof blocks. * Roof blocks.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.fluid.FluidState; import net.minecraft.core.BlockPos;
import net.minecraft.fluid.Fluids; import net.minecraft.core.Direction;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.pathfinding.PathType; import net.minecraft.world.level.BlockGetter;
import net.minecraft.state.EnumProperty; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.state.StateContainer; import net.minecraft.world.level.block.Block;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.world.level.block.Mirror;
import net.minecraft.state.properties.Half; import net.minecraft.world.level.block.Rotation;
import net.minecraft.state.properties.StairsShape; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.*; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.Direction.Axis; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.world.level.block.state.properties.Half;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.state.properties.StairsShape;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.material.FluidState;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.IWorld; import net.minecraft.world.phys.AABB;
import net.minecraft.block.*; import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.block.BlockState; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.phys.shapes.VoxelShape;
import wile.engineersdecor.libmc.blocks.StandardBlocks; import net.minecraftforge.api.distmarker.Dist;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
public class EdRoofBlock extends StandardBlocks.HorizontalWaterLoggable implements IDecorBlock
{
public static final EnumProperty<StairsShape> SHAPE = BlockStateProperties.STAIRS_SHAPE; public class EdRoofBlock extends StandardBlocks.HorizontalWaterLoggable
public static final EnumProperty<Half> HALF = BlockStateProperties.HALF; {
private final VoxelShape[][][] shape_cache_; public static final EnumProperty<StairsShape> SHAPE = BlockStateProperties.STAIRS_SHAPE;
public static final EnumProperty<Half> HALF = BlockStateProperties.HALF;
public EdRoofBlock(long config, AbstractBlock.Properties properties) private final VoxelShape[][][] shape_cache_;
{ this(config, properties.dynamicShape(), VoxelShapes.empty(), VoxelShapes.empty()); }
public EdRoofBlock(long config, BlockBehaviour.Properties properties)
public EdRoofBlock(long config, AbstractBlock.Properties properties, VoxelShape add, VoxelShape cut) { this(config, properties.dynamicShape(), Shapes.empty(), Shapes.empty()); }
{
super(config, properties, Auxiliaries.getPixeledAABB(0, 0,0,16, 8, 16)); public EdRoofBlock(long config, BlockBehaviour.Properties properties, VoxelShape add, VoxelShape cut)
registerDefaultState(super.defaultBlockState().setValue(HORIZONTAL_FACING, Direction.NORTH).setValue(SHAPE, StairsShape.STRAIGHT)); {
shape_cache_ = makeShapes(add, cut); super(config, properties, Auxiliaries.getPixeledAABB(0, 0,0,16, 8, 16));
} registerDefaultState(super.defaultBlockState().setValue(HORIZONTAL_FACING, Direction.NORTH).setValue(SHAPE, StairsShape.STRAIGHT));
shape_cache_ = makeShapes(add, cut);
@Override }
@SuppressWarnings("deprecation")
public boolean useShapeForLightOcclusion(BlockState state) @Override
{ return false; } @SuppressWarnings("deprecation")
public boolean useShapeForLightOcclusion(BlockState state)
@OnlyIn(Dist.CLIENT) { return false; }
@SuppressWarnings("deprecation")
public float getShadeBrightness(BlockState state, IBlockReader world, BlockPos pos) @OnlyIn(Dist.CLIENT)
{ return 0.98f; } @SuppressWarnings("deprecation")
public float getShadeBrightness(BlockState state, BlockGetter world, BlockPos pos)
@Override { return 0.98f; }
@SuppressWarnings("deprecation")
public int getLightBlock(BlockState state, IBlockReader world, BlockPos pos) @Override
{ return 1; } @SuppressWarnings("deprecation")
public int getLightBlock(BlockState state, BlockGetter world, BlockPos pos)
@Override { return 1; }
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext context)
{ return shape_cache_[state.getValue(HALF).ordinal()][state.getValue(HORIZONTAL_FACING).get3DDataValue()][state.getValue(SHAPE).ordinal()]; } @Override
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context)
@Override { return shape_cache_[state.getValue(HALF).ordinal()][state.getValue(HORIZONTAL_FACING).get3DDataValue()][state.getValue(SHAPE).ordinal()]; }
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); builder.add(SHAPE, HALF); } @Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
@Override { super.createBlockStateDefinition(builder); builder.add(SHAPE, HALF); }
public FluidState getFluidState(BlockState state)
{ return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); } @Override
public FluidState getFluidState(BlockState state)
@Override { return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); }
public boolean isPathfindable(BlockState state, IBlockReader world, BlockPos pos, PathType type)
{ return false; } @Override
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type)
@Override { return false; }
public BlockState getStateForPlacement(BlockItemUseContext context)
{ @Override
BlockPos pos = context.getClickedPos(); public BlockState getStateForPlacement(BlockPlaceContext context)
Direction face = context.getClickedFace(); {
BlockState state = defaultBlockState() BlockPos pos = context.getClickedPos();
.setValue(HORIZONTAL_FACING, context.getHorizontalDirection()) Direction face = context.getClickedFace();
.setValue(HALF, (face == Direction.DOWN) ? Half.TOP : Half.BOTTOM) BlockState state = defaultBlockState()
.setValue(WATERLOGGED, context.getLevel().getFluidState(pos).getType()==Fluids.WATER); .setValue(HORIZONTAL_FACING, context.getHorizontalDirection())
return state.setValue(SHAPE, getStairsShapeProperty(state, context.getLevel(), pos)); .setValue(HALF, (face == Direction.DOWN) ? Half.TOP : Half.BOTTOM)
} .setValue(WATERLOGGED, context.getLevel().getFluidState(pos).getType()==Fluids.WATER);
return state.setValue(SHAPE, getStairsShapeProperty(state, context.getLevel(), pos));
@Override }
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{ @Override
if(state.getValue(WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
return (facing.getAxis().isHorizontal()) ? (state.setValue(SHAPE, getStairsShapeProperty(state, world, pos))) : (super.updateShape(state, facing, facingState, world, pos, facingPos)); {
} if(state.getValue(WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
return (facing.getAxis().isHorizontal()) ? (state.setValue(SHAPE, getStairsShapeProperty(state, world, pos))) : (super.updateShape(state, facing, facingState, world, pos, facingPos));
@Override }
public BlockState rotate(BlockState state, Rotation rot)
{ return state.setValue(HORIZONTAL_FACING, rot.rotate(state.getValue(HORIZONTAL_FACING))); } @Override
public BlockState rotate(BlockState state, Rotation rot)
@Override { return state.setValue(HORIZONTAL_FACING, rot.rotate(state.getValue(HORIZONTAL_FACING))); }
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror where) @Override
{ @SuppressWarnings("deprecation")
if((where==Mirror.LEFT_RIGHT) && (state.getValue(HORIZONTAL_FACING).getAxis()==Direction.Axis.Z)) { public BlockState mirror(BlockState state, Mirror where)
switch(state.getValue(SHAPE)) { {
case INNER_LEFT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); if((where==Mirror.LEFT_RIGHT) && (state.getValue(HORIZONTAL_FACING).getAxis()==Direction.Axis.Z)) {
case INNER_RIGHT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); return switch (state.getValue(SHAPE)) {
case OUTER_LEFT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT);
case OUTER_RIGHT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT);
default: return state.rotate(Rotation.CLOCKWISE_180); case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT);
} case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT);
} else if((where==Mirror.FRONT_BACK) && (state.getValue(HORIZONTAL_FACING).getAxis() == Direction.Axis.X)) { default -> state.rotate(Rotation.CLOCKWISE_180);
switch(state.getValue(SHAPE)) { };
case INNER_LEFT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT); } else if((where==Mirror.FRONT_BACK) && (state.getValue(HORIZONTAL_FACING).getAxis() == Direction.Axis.X)) {
case INNER_RIGHT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT); return switch (state.getValue(SHAPE)) {
case OUTER_LEFT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT); case INNER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_LEFT);
case OUTER_RIGHT: return state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT); case INNER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.INNER_RIGHT);
case STRAIGHT: return state.rotate(Rotation.CLOCKWISE_180); case OUTER_LEFT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_RIGHT);
} case OUTER_RIGHT -> state.rotate(Rotation.CLOCKWISE_180).setValue(SHAPE, StairsShape.OUTER_LEFT);
} case STRAIGHT -> state.rotate(Rotation.CLOCKWISE_180);
return super.mirror(state, where); };
} }
return super.mirror(state, where);
private static boolean isRoofBlock(BlockState state) }
{ return (state.getBlock() instanceof EdRoofBlock); }
private static boolean isRoofBlock(BlockState state)
private static boolean isOtherRoofState(BlockState state, IBlockReader world, BlockPos pos, Direction facing) { return (state.getBlock() instanceof EdRoofBlock); }
{
BlockState st = world.getBlockState(pos.relative(facing)); private static boolean isOtherRoofState(BlockState state, BlockGetter world, BlockPos pos, Direction facing)
return (!isRoofBlock(st)) || (st.getValue(HORIZONTAL_FACING) != state.getValue(HORIZONTAL_FACING)); {
} BlockState st = world.getBlockState(pos.relative(facing));
return (!isRoofBlock(st)) || (st.getValue(HORIZONTAL_FACING) != state.getValue(HORIZONTAL_FACING));
private static VoxelShape[][][] makeShapes(VoxelShape add, VoxelShape cut) }
{
VoxelShape[][][] shapes = new VoxelShape[2][6][5]; private static VoxelShape[][][] makeShapes(VoxelShape add, VoxelShape cut)
for(int half_index=0; half_index<Half.values().length; ++half_index) { {
for(int direction_index=0; direction_index<Direction.values().length; ++direction_index) { VoxelShape[][][] shapes = new VoxelShape[2][6][5];
for(int stairs_shape_index=0; stairs_shape_index<StairsShape.values().length; ++stairs_shape_index) { for(int half_index=0; half_index<Half.values().length; ++half_index) {
VoxelShape shape = makeShape(half_index, direction_index, stairs_shape_index); for(int direction_index=0; direction_index<Direction.values().length; ++direction_index) {
try { for(int stairs_shape_index=0; stairs_shape_index<StairsShape.values().length; ++stairs_shape_index) {
// Only in case something changes and this fails, log but do not prevent the game from starting. VoxelShape shape = makeShape(half_index, direction_index, stairs_shape_index);
// Roof shapes are not the most important thing in the world. try {
if(!add.isEmpty()) shape = VoxelShapes.joinUnoptimized(shape, add, IBooleanFunction.OR); // Only in case something changes and this fails, log but do not prevent the game from starting.
if(!cut.isEmpty()) shape = VoxelShapes.joinUnoptimized(shape, cut, IBooleanFunction.ONLY_FIRST); // Roof shapes are not the most important thing in the world.
} catch(Throwable ex) { if(!add.isEmpty()) shape = Shapes.joinUnoptimized(shape, add, BooleanOp.OR);
Auxiliaries.logError("Failed to cut shape using Boolean function. This is bug."); if(!cut.isEmpty()) shape = Shapes.joinUnoptimized(shape, cut, BooleanOp.ONLY_FIRST);
} } catch(Throwable ex) {
shapes[half_index][direction_index][stairs_shape_index] = shape; Auxiliaries.logError("Failed to cut shape using Boolean function. This is bug.");
} }
} shapes[half_index][direction_index][stairs_shape_index] = shape;
} }
return shapes; }
} }
return shapes;
private static VoxelShape makeShape(int half_index, int direction_index, int stairs_shape_index) }
{
AxisAlignedBB[] straight = new AxisAlignedBB[]{ private static VoxelShape makeShape(int half_index, int direction_index, int stairs_shape_index)
Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16), {
Auxiliaries.getPixeledAABB( 4, 4, 0, 16, 8, 16), AABB[] straight = new AABB[]{
Auxiliaries.getPixeledAABB( 8, 8, 0, 16, 12, 16), Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB(12, 12, 0, 16, 16, 16) Auxiliaries.getPixeledAABB( 4, 4, 0, 16, 8, 16),
}; Auxiliaries.getPixeledAABB( 8, 8, 0, 16, 12, 16),
AxisAlignedBB[] pyramid = new AxisAlignedBB[]{ Auxiliaries.getPixeledAABB(12, 12, 0, 16, 16, 16)
Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16), };
Auxiliaries.getPixeledAABB( 4, 4, 4, 16, 8, 16), AABB[] pyramid = new AABB[]{
Auxiliaries.getPixeledAABB( 8, 8, 8, 16, 12, 16), Auxiliaries.getPixeledAABB( 0, 0, 0, 16, 4, 16),
Auxiliaries.getPixeledAABB(12, 12, 12, 16, 16, 16) Auxiliaries.getPixeledAABB( 4, 4, 4, 16, 8, 16),
}; Auxiliaries.getPixeledAABB( 8, 8, 8, 16, 12, 16),
final Half half = Half.values()[half_index]; Auxiliaries.getPixeledAABB(12, 12, 12, 16, 16, 16)
if(half==Half.TOP) { };
straight = Auxiliaries.getMirroredAABB(straight, Axis.Y); final Half half = Half.values()[half_index];
pyramid = Auxiliaries.getMirroredAABB(pyramid, Axis.Y); if(half==Half.TOP) {
} straight = Auxiliaries.getMirroredAABB(straight, Direction.Axis.Y);
Direction direction = Direction.from3DDataValue(direction_index); pyramid = Auxiliaries.getMirroredAABB(pyramid, Direction.Axis.Y);
if((direction==Direction.UP) || (direction==Direction.DOWN)) return VoxelShapes.block(); }
direction_index = (direction.get2DDataValue()+1) & 0x03; // ref NORTH -> EAST for stairs compliancy. Direction direction = Direction.from3DDataValue(direction_index);
final StairsShape stairs = StairsShape.values()[stairs_shape_index]; if((direction==Direction.UP) || (direction==Direction.DOWN)) return Shapes.block();
switch(stairs) { direction_index = (direction.get2DDataValue()+1) & 0x03; // ref NORTH -> EAST for stairs compliancy.
case STRAIGHT: final StairsShape stairs = StairsShape.values()[stairs_shape_index];
return Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(straight, direction_index)); return switch (stairs) {
case OUTER_LEFT: case STRAIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(straight, direction_index));
return Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index-1)); case OUTER_LEFT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index - 1));
case OUTER_RIGHT: case OUTER_RIGHT -> Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index));
return Auxiliaries.getUnionShape(Auxiliaries.getYRotatedAABB(pyramid, direction_index)); case INNER_LEFT -> Auxiliaries.getUnionShape(
case INNER_LEFT: Auxiliaries.getYRotatedAABB(straight, direction_index),
return Auxiliaries.getUnionShape( Auxiliaries.getYRotatedAABB(straight, direction_index - 1)
Auxiliaries.getYRotatedAABB(straight, direction_index), );
Auxiliaries.getYRotatedAABB(straight, direction_index-1) case INNER_RIGHT -> Auxiliaries.getUnionShape(
); Auxiliaries.getYRotatedAABB(straight, direction_index),
case INNER_RIGHT: Auxiliaries.getYRotatedAABB(straight, direction_index + 1)
return Auxiliaries.getUnionShape( );
Auxiliaries.getYRotatedAABB(straight, direction_index), };
Auxiliaries.getYRotatedAABB(straight, direction_index+1) }
);
default: private static StairsShape getStairsShapeProperty(BlockState state, BlockGetter world, BlockPos pos)
return VoxelShapes.block(); {
} Direction direction = state.getValue(HORIZONTAL_FACING);
} {
BlockState ns = world.getBlockState(pos.relative(direction));
private static StairsShape getStairsShapeProperty(BlockState state, IBlockReader world, BlockPos pos) if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) {
{ Direction nf = ns.getValue(HORIZONTAL_FACING);
Direction direction = state.getValue(HORIZONTAL_FACING); if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf.getOpposite())) {
{ return (nf == direction.getCounterClockWise()) ? StairsShape.OUTER_LEFT : StairsShape.OUTER_RIGHT;
BlockState ns = world.getBlockState(pos.relative(direction)); }
if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { }
Direction nf = ns.getValue(HORIZONTAL_FACING); }
if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf.getOpposite())) { {
return (nf == direction.getCounterClockWise()) ? StairsShape.OUTER_LEFT : StairsShape.OUTER_RIGHT; BlockState ns = world.getBlockState(pos.relative(direction.getOpposite()));
} if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) {
} Direction nf = ns.getValue(HORIZONTAL_FACING);
} if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf)) {
{ return (nf == direction.getCounterClockWise()) ? StairsShape.INNER_LEFT : StairsShape.INNER_RIGHT;
BlockState ns = world.getBlockState(pos.relative(direction.getOpposite())); }
if(isRoofBlock(ns) && (state.getValue(HALF) == ns.getValue(HALF))) { }
Direction nf = ns.getValue(HORIZONTAL_FACING); }
if(nf.getAxis() != state.getValue(HORIZONTAL_FACING).getAxis() && isOtherRoofState(state, world, pos, nf)) { return StairsShape.STRAIGHT;
return (nf == direction.getCounterClockWise()) ? StairsShape.INNER_LEFT : StairsShape.INNER_RIGHT; }
} }
}
}
return StairsShape.STRAIGHT;
}
}

View file

@ -1,18 +0,0 @@
/*
* @file EdSlabBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Standard half block horizontal slab characteristics class.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.VariantSlabBlock;
import net.minecraft.block.*;
public class EdSlabBlock extends VariantSlabBlock implements IDecorBlock
{
public EdSlabBlock(long config, AbstractBlock.Properties builder)
{ super(config, builder); }
}

View file

@ -1,19 +0,0 @@
/*
* @file EdSlabSliceBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Half slab ("slab slices") characteristics class. Actually
* it's now a quater slab, but who cares.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.SlabSliceBlock;
import net.minecraft.block.AbstractBlock;
public class EdSlabSliceBlock extends SlabSliceBlock implements IDecorBlock
{
public EdSlabSliceBlock(long config, AbstractBlock.Properties builder)
{ super(config, builder); }
}

View file

@ -1,266 +1,229 @@
/* /*
* @file EdSolarPanel.java * @file EdSolarPanel.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Smaller (cutout) block with a defined facing. * Smaller (cutout) block with a defined facing.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.world.IWorldReader; import net.minecraft.core.BlockPos;
import net.minecraft.world.World; import net.minecraft.core.Direction;
import net.minecraft.world.IBlockReader; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.LightType; import net.minecraft.util.Mth;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.InteractionHand;
import net.minecraft.state.StateContainer; import net.minecraft.world.InteractionResult;
import net.minecraft.block.Block; import net.minecraft.world.entity.player.Player;
import net.minecraft.block.BlockState; import net.minecraft.world.level.Level;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.level.LevelReader;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.LightLayer;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.ActionResultType; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.Hand; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.phys.AABB;
import net.minecraft.util.Direction; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.util.math.MathHelper; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import wile.engineersdecor.ModConfig;
import net.minecraftforge.common.util.LazyOptional; import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModConfig; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.ModContent; import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries; import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay; import wile.engineersdecor.libmc.detail.Overlay;
import wile.engineersdecor.libmc.detail.RfEnergy;
import javax.annotation.Nullable;
import java.util.ArrayList; import javax.annotation.Nullable;
import java.util.List; import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.AbstractBlock; public class EdSolarPanel
{
public class EdSolarPanel public static final int DEFAULT_PEAK_POWER = 40;
{ private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER;
public static void on_config(int peak_power_per_tick) private static int max_power_storage_ = 64000;
{ SolarPanelTileEntity.on_config(peak_power_per_tick); } private static int max_feed_power = 4096;
private static int feeding_threshold = max_power_storage_/5;
//-------------------------------------------------------------------------------------------------------------------- private static int balancing_threshold = max_power_storage_/10;
// Block
//-------------------------------------------------------------------------------------------------------------------- public static void on_config(int peak_power_per_tick, int battery_capacity, int max_feed_in_power)
{
public static class SolarPanelBlock extends DecorBlock.Cutout implements IDecorBlock final int t = SolarPanelTileEntity.TICK_INTERVAL;
{ peak_power_per_tick_ = Mth.clamp(peak_power_per_tick, 12, 8192);
public static final IntegerProperty EXPOSITION = IntegerProperty.create("exposition", 0, 4); feeding_threshold = Math.max(max_power_storage_/5, 1000);
balancing_threshold = Math.max(max_power_storage_/10, 1000);
public SolarPanelBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABB) max_power_storage_ = battery_capacity;
{ max_feed_power = max_feed_in_power * t;
super(config, builder, unrotatedAABB); ModConfig.log("Config small solar panel: Peak production:" + peak_power_per_tick_ + "/t, capacity:" + max_power_storage_ + "rf, max-feed:" + (max_feed_power/t) + "rf/t");
registerDefaultState(super.defaultBlockState().setValue(EXPOSITION, 1)); }
}
//--------------------------------------------------------------------------------------------------------------------
@Override // Block
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) //--------------------------------------------------------------------------------------------------------------------
{ super.createBlockStateDefinition(builder); builder.add(EXPOSITION); }
public static class SolarPanelBlock extends StandardBlocks.Cutout implements StandardEntityBlocks.IStandardEntityBlock<SolarPanelTileEntity>
@Override {
public boolean hasTileEntity(BlockState state) public static final IntegerProperty EXPOSITION = IntegerProperty.create("exposition", 0, 4);
{ return true; }
public SolarPanelBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB)
@Override {
@Nullable super(config, builder, unrotatedAABB);
public TileEntity createTileEntity(BlockState state, IBlockReader world) registerDefaultState(super.defaultBlockState().setValue(EXPOSITION, 1));
{ return new EdSolarPanel.SolarPanelTileEntity(); } }
@Override @Override
@SuppressWarnings("deprecation") @Nullable
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) public BlockEntityType<EdSolarPanel.SolarPanelTileEntity> getBlockEntityType()
{ { return ModContent.TET_SMALL_SOLAR_PANEL; }
if(world.isClientSide()) return ActionResultType.SUCCESS;
TileEntity te = world.getBlockEntity(pos); @Override
if(te instanceof SolarPanelTileEntity) ((SolarPanelTileEntity)te).state_message(player); protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
return ActionResultType.CONSUME; { super.createBlockStateDefinition(builder); builder.add(EXPOSITION); }
}
@Override
@Override @SuppressWarnings("deprecation")
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
{ return false; } {
} if(world.isClientSide()) return InteractionResult.SUCCESS;
BlockEntity te = world.getBlockEntity(pos);
//-------------------------------------------------------------------------------------------------------------------- if(te instanceof SolarPanelTileEntity) ((SolarPanelTileEntity)te).state_message(player);
// Tile entity return InteractionResult.CONSUME;
//-------------------------------------------------------------------------------------------------------------------- }
public static class SolarPanelTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider, IEnergyStorage @Override
{ public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
public static final int DEFAULT_PEAK_POWER = 40; { return false; }
public static final int TICK_INTERVAL = 4; }
public static final int ACCUMULATION_INTERVAL = 8;
private static final Direction transfer_directions_[] = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH }; //--------------------------------------------------------------------------------------------------------------------
private static int peak_power_per_tick_ = DEFAULT_PEAK_POWER; // Tile entity
private static final int max_power_storage_ = 64000; //--------------------------------------------------------------------------------------------------------------------
private static final int max_feed_power = 8192;
private static int feeding_threshold = max_power_storage_/5; public static class SolarPanelTileEntity extends StandardEntityBlocks.StandardBlockEntity
private static int balancing_threshold = max_power_storage_/10; {
private int tick_timer_ = 0; public static final int TICK_INTERVAL = 4;
private int recalc_timer_ = 0; public static final int ACCUMULATION_INTERVAL = 8;
private int accumulated_power_ = 0; private static final Direction[] transfer_directions_ = {Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH };
private int current_production_ = 0;
private int current_feedin_ = 0; private int tick_timer_ = 0;
private boolean output_enabled_ = false; private int recalc_timer_ = 0;
private int current_production_ = 0;
public static void on_config(int peak_power_per_tick) private int current_feedin_ = 0;
{ private boolean output_enabled_ = false;
peak_power_per_tick_ = MathHelper.clamp(peak_power_per_tick, 2, 8192);
feeding_threshold = Math.max(max_power_storage_/5, 1000); private final RfEnergy.Battery battery_ = new RfEnergy.Battery(max_power_storage_, 0, 1024);
balancing_threshold = Math.max(max_power_storage_/10, 1000); private final LazyOptional<IEnergyStorage> energy_handler_ = battery_.createEnergyHandler();
ModConfig.log("Config small solar panel: Peak production:" + peak_power_per_tick_ + "/t.");
} //------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------ public SolarPanelTileEntity(BlockPos pos, BlockState state)
{ super(ModContent.TET_SMALL_SOLAR_PANEL, pos, state); }
public SolarPanelTileEntity()
{ this(ModContent.TET_SMALL_SOLAR_PANEL); } public void readnbt(CompoundTag nbt, boolean update_packet)
{ battery_.load(nbt); }
public SolarPanelTileEntity(TileEntityType<?> te_type)
{ super(te_type); } protected void writenbt(CompoundTag nbt, boolean update_packet)
{ battery_.save(nbt); }
public void readnbt(CompoundNBT nbt, boolean update_packet)
{ accumulated_power_ = nbt.getInt("energy"); } public void state_message(Player player)
{
protected void writenbt(CompoundNBT nbt, boolean update_packet) String soc = Integer.toString(Mth.clamp((battery_.getEnergyStored()*100/max_power_storage_),0,100));
{ nbt.putInt("energy", accumulated_power_); } Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", soc, max_power_storage_, current_production_, current_feedin_));
}
public void state_message(PlayerEntity player)
{ // ICapabilityProvider ---------------------------------------------------------------------
String soc = Integer.toString(MathHelper.clamp((accumulated_power_*100/max_power_storage_),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_solar_panel.status", new Object[]{soc, max_power_storage_, current_production_, current_feedin_ })); @Override
} public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
// IEnergyStorage -------------------------------------------------------------------------- if(capability== CapabilityEnergy.ENERGY) return energy_handler_.cast();
return super.getCapability(capability, facing);
@Override }
public boolean canExtract()
{ return true; } // BlockEntity ------------------------------------------------------------------------------
@Override @Override
public boolean canReceive() public void load(CompoundTag nbt)
{ return false; } { super.load(nbt); readnbt(nbt, false); }
@Override @Override
public int getMaxEnergyStored() public CompoundTag save(CompoundTag nbt)
{ return max_power_storage_; } { super.save(nbt); writenbt(nbt, false); return nbt; }
@Override @Override
public int getEnergyStored() public void setRemoved()
{ return accumulated_power_; } {
super.setRemoved();
@Override energy_handler_.invalidate();
public int extractEnergy(int maxExtract, boolean simulate) }
{
int p = Math.min(accumulated_power_, maxExtract); @Override
if(!simulate) accumulated_power_ -= p; public void tick()
return p; {
} if((level.isClientSide) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
@Override BlockState state = level.getBlockState(worldPosition);
public int receiveEnergy(int maxReceive, boolean simulate) if(!(state.getBlock() instanceof SolarPanelBlock)) return;
{ return 0; } current_feedin_ = 0;
final List<SolarPanelTileEntity> adjacent_panels = new ArrayList<>();
// ICapabilityProvider --------------------------------------------------------------------- if(output_enabled_) {
for(int i=0; (i<transfer_directions_.length) && (!battery_.isEmpty()); ++i) {
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this); final Direction f = transfer_directions_[i];
BlockEntity te = level.getBlockEntity(worldPosition.relative(f));
@Override if(te==null) continue;
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite()).orElse(null);
{ if(es==null) continue;
if(capability== CapabilityEnergy.ENERGY) return energy_handler_.cast(); if(!es.canReceive()) {
return super.getCapability(capability, facing); if(!(te instanceof SolarPanelTileEntity)) continue;
} adjacent_panels.add((SolarPanelTileEntity)te);
continue;
// TileEntity ------------------------------------------------------------------------------ }
final int feed_power = (battery_.getEnergyStored() > (max_power_storage_/10)) ? max_feed_power : Math.max(current_production_*2, (peak_power_per_tick_/4));
@Override final int fed = es.receiveEnergy(Math.min(battery_.getEnergyStored(), feed_power * TICK_INTERVAL), false);
public void load(BlockState state, CompoundNBT nbt) battery_.draw(fed);
{ super.load(state, nbt); readnbt(nbt, false); } current_feedin_ += fed;
}
@Override }
public CompoundNBT save(CompoundNBT nbt) current_feedin_ /= TICK_INTERVAL;
{ super.save(nbt); writenbt(nbt, false); return nbt; } if((current_feedin_ <= 0) && ((battery_.getEnergyStored() >= balancing_threshold) || (current_production_ <= 0))) {
for(SolarPanelTileEntity panel: adjacent_panels) {
@Override if(panel.battery_.getEnergyStored() >= (battery_.getEnergyStored()-balancing_threshold)) continue;
public void setRemoved() panel.battery_.setEnergyStored(panel.battery_.getEnergyStored() + balancing_threshold);
{ battery_.setEnergyStored(battery_.getEnergyStored() - balancing_threshold);
super.setRemoved(); if(battery_.getEnergyStored() < balancing_threshold) break;
energy_handler_.invalidate(); }
} }
if(!level.canSeeSkyFromBelowWater(worldPosition)) {
@Override tick_timer_ = TICK_INTERVAL * 10;
public void tick() current_production_ = 0;
{ if((!battery_.isEmpty())) output_enabled_ = true;
if((level.isClientSide) || (--tick_timer_ > 0)) return; if(state.getValue((SolarPanelBlock.EXPOSITION))!=2) level.setBlockAndUpdate(worldPosition, state.setValue(SolarPanelBlock.EXPOSITION, 2));
tick_timer_ = TICK_INTERVAL; return;
BlockState state = level.getBlockState(worldPosition); }
if(!(state.getBlock() instanceof SolarPanelBlock)) return; if(battery_.isEmpty()) output_enabled_ = false;
current_feedin_ = 0; if(--recalc_timer_ > 0) return;
final List<SolarPanelTileEntity> adjacent_panels = new ArrayList<>(); recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
if(output_enabled_) { int theta = ((((int)(level.getSunAngle(1f) * (180.0/Math.PI)))+90) % 360);
for(int i=0; (i<transfer_directions_.length) && (accumulated_power_>0); ++i) { int e = 2;
final Direction f = transfer_directions_[i]; if(theta > 340) e = 2;
TileEntity te = level.getBlockEntity(worldPosition.relative(f)); else if(theta < 45) e = 0;
if(te==null) continue; else if(theta < 80) e = 1;
IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite()).orElse(null); else if(theta < 100) e = 2;
if(es==null) continue; else if(theta < 135) e = 3;
if(!es.canReceive()) { else if(theta < 190) e = 4;
if(!(te instanceof SolarPanelTileEntity)) continue; BlockState nstate = state.setValue(SolarPanelBlock.EXPOSITION, e);
adjacent_panels.add((SolarPanelTileEntity)te); if(nstate != state) level.setBlock(worldPosition, nstate, 1|2);
continue; final double eff = (1.0-((level.getRainLevel(1f)*0.6)+(level.getThunderLevel(1f)*0.3)));
} final double ll = ((double)(level.getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(getBlockPos())))/15;
final int feed_power = (accumulated_power_ > (max_power_storage_/10)) ? max_feed_power : Math.max(current_production_*2, (peak_power_per_tick_/4)); final double rf = Math.sin((Math.PI/2) * Math.sqrt(((double)(((theta<0)||(theta>180))?(0):((theta>90)?(180-theta):(theta))))/90));
int fed = es.receiveEnergy(Math.min(accumulated_power_, feed_power * TICK_INTERVAL), false); current_production_ = (int)(Math.min(rf*rf*eff*ll, 1) * peak_power_per_tick_);
accumulated_power_ = MathHelper.clamp(accumulated_power_-fed,0, accumulated_power_); battery_.setEnergyStored(Math.min(battery_.getEnergyStored() + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_));
current_feedin_ += fed; if(battery_.getEnergyStored() >= (feeding_threshold)) output_enabled_ = true;
} }
} }
current_feedin_ /= TICK_INTERVAL; }
if((current_feedin_ <= 0) && ((accumulated_power_ >= balancing_threshold) || (current_production_ <= 0))) {
for(SolarPanelTileEntity panel: adjacent_panels) {
if(panel.accumulated_power_ >= (accumulated_power_-balancing_threshold)) continue;
panel.accumulated_power_ += balancing_threshold;
accumulated_power_ -= balancing_threshold;
if(accumulated_power_ < balancing_threshold) break;
}
}
if(!level.canSeeSkyFromBelowWater(worldPosition)) {
tick_timer_ = TICK_INTERVAL * 10;
current_production_ = 0;
if((accumulated_power_ > 0)) output_enabled_ = true;
if(state.getValue((SolarPanelBlock.EXPOSITION))!=2) level.setBlockAndUpdate(worldPosition, state.setValue(SolarPanelBlock.EXPOSITION, 2));
return;
}
if(accumulated_power_ <= 0) output_enabled_ = false;
if(--recalc_timer_ > 0) return;
recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
int theta = ((((int)(level.getSunAngle(1f) * (180.0/Math.PI)))+90) % 360);
int e = 2;
if(theta > 340) e = 2;
else if(theta < 45) e = 0;
else if(theta < 80) e = 1;
else if(theta < 100) e = 2;
else if(theta < 135) e = 3;
else if(theta < 190) e = 4;
BlockState nstate = state.setValue(SolarPanelBlock.EXPOSITION, e);
if(nstate != state) level.setBlock(worldPosition, nstate, 1|2);
final double eff = (1.0-((level.getRainLevel(1f)*0.6)+(level.getThunderLevel(1f)*0.3)));
final double ll = ((double)(level.getLightEngine().getLayerListener(LightType.SKY).getLightValue(getBlockPos())))/15;
final double rf = Math.sin((Math.PI/2) * Math.sqrt(((double)(((theta<0)||(theta>180))?(0):((theta>90)?(180-theta):(theta))))/90));
current_production_ = (int)(Math.min(rf*rf*eff*ll, 1) * peak_power_per_tick_);
accumulated_power_ = Math.min(accumulated_power_ + (current_production_*(TICK_INTERVAL*ACCUMULATION_INTERVAL)), max_power_storage_);
if(accumulated_power_ >= (feeding_threshold)) output_enabled_ = true;
}
}
}

View file

@ -1,22 +0,0 @@
/*
* @file EdStairsBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Stairs and roof blocks, almost entirely based on vanilla stairs.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardStairsBlock;
import net.minecraft.block.*;
import net.minecraft.block.BlockState;
public class EdStairsBlock extends StandardStairsBlock implements IDecorBlock
{
public EdStairsBlock(long config, BlockState state, AbstractBlock.Properties properties)
{ super(config, state, properties); }
public EdStairsBlock(long config, java.util.function.Supplier<BlockState> state, AbstractBlock.Properties properties)
{ super(config, state, properties); }
}

View file

@ -1,84 +1,88 @@
/* /*
* @file EdStraightPoleBlock.java * @file EdStraightPoleBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Smaller (cutout) block with a defined facing. * Smaller (cutout) block with a defined facing.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.Block; import net.minecraft.core.BlockPos;
import net.minecraft.block.BlockState; import net.minecraft.core.Direction;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.sounds.SoundEvents;
import net.minecraft.item.BlockItem; import net.minecraft.sounds.SoundSource;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.InteractionHand;
import net.minecraft.item.DirectionalPlaceContext; import net.minecraft.world.InteractionResult;
import net.minecraft.item.ItemStack; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.*; import net.minecraft.world.item.BlockItem;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.item.ItemStack;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.World; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.Block;
import wile.engineersdecor.libmc.detail.Inventories; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable; import net.minecraft.world.phys.AABB;
import java.util.Arrays; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import net.minecraft.block.AbstractBlock; import wile.engineersdecor.libmc.detail.Inventories;
public class EdStraightPoleBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock import javax.annotation.Nullable;
{ import java.util.Arrays;
private final EdStraightPoleBlock default_pole;
public EdStraightPoleBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABB, @Nullable EdStraightPoleBlock defaultPole) public class EdStraightPoleBlock extends StandardBlocks.DirectedWaterLoggable
{ super(config, builder, unrotatedAABB); default_pole=(defaultPole==null) ? (this) : (defaultPole); } {
private final EdStraightPoleBlock default_pole;
@Override
@Nullable public EdStraightPoleBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB, @Nullable EdStraightPoleBlock defaultPole)
public BlockState getStateForPlacement(BlockItemUseContext context) { super(config, builder, unrotatedAABB); default_pole=(defaultPole==null) ? (this) : (defaultPole); }
{
Direction facing = context.getClickedFace(); @Override
BlockState state = super.getStateForPlacement(context).setValue(FACING, facing); @Nullable
if((config & DecorBlock.CFG_FLIP_PLACEMENT_IF_SAME) != 0) { public BlockState getStateForPlacement(BlockPlaceContext context)
World world = context.getLevel(); {
BlockPos pos = context.getClickedPos(); Direction facing = context.getClickedFace();
if(world.getBlockState(pos.relative(facing.getOpposite())).getBlock() instanceof EdStraightPoleBlock) { BlockState state = super.getStateForPlacement(context).setValue(FACING, facing);
state = state.setValue(FACING, state.getValue(FACING).getOpposite()); if((config & DecorBlock.CFG_FLIP_PLACEMENT_IF_SAME) != 0) {
} Level world = context.getLevel();
} BlockPos pos = context.getClickedPos();
return state; if(world.getBlockState(pos.relative(facing.getOpposite())).getBlock() instanceof EdStraightPoleBlock) {
} state = state.setValue(FACING, state.getValue(FACING).getOpposite());
}
@Override }
@SuppressWarnings("deprecation") return state;
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) }
{
if((hit.getDirection().getAxis() == state.getValue(FACING).getAxis())) return ActionResultType.PASS; @Override
final ItemStack held_stack = player.getItemInHand(hand); @SuppressWarnings("deprecation")
if((held_stack.isEmpty()) || (!(held_stack.getItem() instanceof BlockItem))) return ActionResultType.PASS; public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
if(!(((BlockItem)(held_stack.getItem())).getBlock() instanceof EdStraightPoleBlock)) return ActionResultType.PASS; {
if(held_stack.getItem() != default_pole.asItem()) return ActionResultType.sidedSuccess(world.isClientSide()); if((hit.getDirection().getAxis() == state.getValue(FACING).getAxis())) return InteractionResult.PASS;
final Block held_block = ((BlockItem)(held_stack.getItem())).getBlock(); final ItemStack held_stack = player.getItemInHand(hand);
final Direction block_direction = state.getValue(FACING); if((held_stack.isEmpty()) || (!(held_stack.getItem() instanceof BlockItem))) return InteractionResult.PASS;
final Vector3d block_vec = Vector3d.atLowerCornerOf(state.getValue(FACING).getNormal()); if(!(((BlockItem)(held_stack.getItem())).getBlock() instanceof EdStraightPoleBlock)) return InteractionResult.PASS;
final double colinearity = 1.0-block_vec.cross(player.getLookAngle()).length(); if(held_stack.getItem() != default_pole.asItem()) return InteractionResult.sidedSuccess(world.isClientSide());
final Direction placement_direction = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis()==block_direction.getAxis()).findFirst().orElse(Direction.NORTH); final Block held_block = ((BlockItem)(held_stack.getItem())).getBlock();
final BlockPos adjacent_pos = pos.relative(placement_direction); final Direction block_direction = state.getValue(FACING);
final BlockState adjacent = world.getBlockState(adjacent_pos); final Vec3 block_vec = Vec3.atLowerCornerOf(state.getValue(FACING).getNormal());
final BlockItemUseContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite()); final double colinearity = 1.0-block_vec.cross(player.getLookAngle()).length();
if(!adjacent.canBeReplaced(ctx)) return ActionResultType.sidedSuccess(world.isClientSide()); final Direction placement_direction = Arrays.stream(Direction.orderedByNearest(player)).filter(d->d.getAxis()==block_direction.getAxis()).findFirst().orElse(Direction.NORTH);
final BlockState new_state = held_block.getStateForPlacement(ctx); final BlockPos adjacent_pos = pos.relative(placement_direction);
if(new_state == null) return ActionResultType.FAIL; final BlockState adjacent = world.getBlockState(adjacent_pos);
if(!world.setBlock(adjacent_pos, new_state, 1|2)) return ActionResultType.FAIL; final BlockPlaceContext ctx = new DirectionalPlaceContext(world, adjacent_pos, placement_direction, player.getItemInHand(hand), placement_direction.getOpposite());
world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundCategory.BLOCKS, 1f, 1f); if(!adjacent.canBeReplaced(ctx)) return InteractionResult.sidedSuccess(world.isClientSide());
if(!player.isCreative()) { final BlockState new_state = held_block.getStateForPlacement(ctx);
held_stack.shrink(1); if(new_state == null) return InteractionResult.FAIL;
Inventories.setItemInPlayerHand(player, hand, held_stack); if(!world.setBlock(adjacent_pos, new_state, 1|2)) return InteractionResult.FAIL;
} world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f);
return ActionResultType.sidedSuccess(world.isClientSide()); if(!player.isCreative()) {
} held_stack.shrink(1);
Inventories.setItemInPlayerHand(player, hand, held_stack);
} }
return InteractionResult.sidedSuccess(world.isClientSide());
}
}

View file

@ -1,339 +1,335 @@
/* /*
* @file EdTestBlock.java * @file EdTestBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Creative mod testing block * Creative mod testing block
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.Blocks; import net.minecraft.core.BlockPos;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.core.Direction;
import net.minecraft.fluid.Fluids; import net.minecraft.nbt.CompoundTag;
import net.minecraft.item.Items; import net.minecraft.network.chat.TextComponent;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.InteractionHand;
import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.InteractionResult;
import net.minecraft.world.IBlockReader; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.IWorldReader; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.World; import net.minecraft.world.item.Items;
import net.minecraft.block.AbstractBlock; import net.minecraft.world.level.BlockGetter;
import net.minecraft.block.BlockState; import net.minecraft.world.level.Level;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.level.LevelReader;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.block.Blocks;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.ActionResultType; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.Hand; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.Direction; import net.minecraft.world.level.material.Fluids;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.phys.AABB;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistries; import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModContent; import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.*; import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import wile.engineersdecor.libmc.detail.*;
import javax.annotation.Nullable;
import java.util.ArrayList; import javax.annotation.Nullable;
import java.util.Collections; import java.util.ArrayList;
import java.util.List; import java.util.Collections;
import java.util.List;
public class EdTestBlock
{ public class EdTestBlock
//-------------------------------------------------------------------------------------------------------------------- {
// Block //--------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------- // Block
//--------------------------------------------------------------------------------------------------------------------
public static class TestBlock extends DecorBlock.Directed implements Auxiliaries.IExperimentalFeature, IDecorBlock
{ public static class TestBlock extends StandardBlocks.Directed implements StandardEntityBlocks.IStandardEntityBlock<TestTileEntity>, Auxiliaries.IExperimentalFeature
public TestBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABB) {
{ super(config, builder, unrotatedAABB); } public TestBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) @Override
{ return VoxelShapes.block(); } @Nullable
public BlockEntityType<EdTestBlock.TestTileEntity> getBlockEntityType()
@Override { return ModContent.TET_TEST_BLOCK; }
public boolean hasTileEntity(BlockState state)
{ return true; } @Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
@Override { return Shapes.block(); }
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world) @Override
{ return new TestTileEntity(); } @SuppressWarnings("deprecation") // public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) { return true; }
public boolean isSignalSource(BlockState p_60571_)
@Override { return true; }
public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, @Nullable Direction side)
{ return true; } @Override
public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
@Override { return false; }
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side)
{ return false; } @Override
public boolean hasDynamicDropList()
@Override { return true; }
public boolean hasDynamicDropList()
{ return true; } @Override
public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
@Override { return Collections.singletonList(new ItemStack(this)); }
public List<ItemStack> dropList(BlockState state, World world, TileEntity te, boolean explosion)
{ return Collections.singletonList(new ItemStack(this)); } @Override
@SuppressWarnings("deprecation")
@Override public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
@SuppressWarnings("deprecation") {
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) if(world.isClientSide()) return InteractionResult.SUCCESS;
{ BlockEntity te = world.getBlockEntity(pos);
if(world.isClientSide()) return ActionResultType.SUCCESS; if(!(te instanceof TestTileEntity)) return InteractionResult.FAIL;
TileEntity te = world.getBlockEntity(pos); return ((TestTileEntity)te).activated(player, hand, hit) ? InteractionResult.CONSUME : InteractionResult.PASS;
if(!(te instanceof TestTileEntity)) return ActionResultType.FAIL; }
return ((TestTileEntity)te).activated(player, hand, hit) ? ActionResultType.CONSUME : ActionResultType.PASS; }
}
} //--------------------------------------------------------------------------------------------------------------------
// Tile entity
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
// Tile entity
//-------------------------------------------------------------------------------------------------------------------- public static class TestTileEntity extends StandardEntityBlocks.StandardBlockEntity
{
public static class TestTileEntity extends TileEntity implements ITickableTileEntity private final RfEnergy.Battery battery_;
{ private final LazyOptional<IEnergyStorage> energy_handler_;
private final RfEnergy.Battery battery_; private final Fluidics.Tank tank_;
private final LazyOptional<IEnergyStorage> energy_handler_; private final LazyOptional<IFluidHandler> fluid_handler_;
private final Fluidics.Tank tank_; private final Inventories.StorageInventory inventory_;
private final LazyOptional<IFluidHandler> fluid_handler_; private final LazyOptional<IItemHandler> item_handler_;
private final Inventories.StorageInventory inventory_; private int tick_timer = 0;
private final LazyOptional<IItemHandler> item_handler_; private int rf_fed_avg = 0;
private int tick_timer = 0; private int rf_fed_total = 0;
private int rf_fed_avg = 0; private int rf_fed_acc = 0;
private int rf_fed_total = 0; private int rf_received_avg = 0;
private int rf_fed_acc = 0; private int rf_received_total = 0;
private int rf_received_avg = 0; private int liq_filled_avg = 0;
private int rf_received_total = 0; private int liq_filled_total = 0;
private int liq_filled_avg = 0; private int liq_filled_acc = 0;
private int liq_filled_total = 0; private int liq_received_avg = 0;
private int liq_filled_acc = 0; private int liq_received_total = 0;
private int liq_received_avg = 0; private int items_inserted_total = 0;
private int liq_received_total = 0; private int items_received_total = 0;
private int items_inserted_total = 0; private int rf_feed_setting = 4096;
private int items_received_total = 0; private FluidStack liq_fill_stack = new FluidStack(Fluids.WATER, 128);
private int rf_feed_setting = 4096; private ItemStack insertion_item = ItemStack.EMPTY;
private FluidStack liq_fill_stack = new FluidStack(Fluids.WATER, 128); private Direction block_facing = Direction.NORTH;
private ItemStack insertion_item = ItemStack.EMPTY; private boolean paused = false;
private Direction block_facing = Direction.NORTH;
private boolean paused = false;
public TestTileEntity(BlockPos pos, BlockState state)
public TestTileEntity() {
{ this(ModContent.TET_TEST_BLOCK); } super(ModContent.TET_TEST_BLOCK, pos, state);
battery_ = new RfEnergy.Battery((int)1e9, (int)1e9, 0, 0);
public TestTileEntity(TileEntityType<?> te_type) energy_handler_ = battery_.createEnergyHandler();
{ tank_ = new Fluidics.Tank((int)1e9);
super(te_type); fluid_handler_ = tank_.createFluidHandler();
battery_ = new RfEnergy.Battery((int)1e9, (int)1e9, 0, 0); inventory_ = new Inventories.StorageInventory(this, 1);
energy_handler_ = battery_.createEnergyHandler(); item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(inventory_);
tank_ = new Fluidics.Tank((int)1e9); }
fluid_handler_ = tank_.createFluidHandler();
inventory_ = new Inventories.StorageInventory(this, 1); @Override
item_handler_ = Inventories.MappedItemHandler.createInsertionHandler(inventory_); public void load(CompoundTag nbt)
} {
super.load(nbt);
@Override tank_.load(nbt);
public void load(BlockState state, CompoundNBT nbt) battery_.load(nbt);
{ rf_fed_avg = nbt.getInt("rf_fed_avg");
super.load(state, nbt); rf_fed_total = nbt.getInt("rf_fed_total");
tank_.load(nbt); rf_fed_acc = nbt.getInt("rf_fed_acc");
battery_.load(nbt); rf_received_avg = nbt.getInt("rf_received_avg");
rf_fed_avg = nbt.getInt("rf_fed_avg"); rf_received_total = nbt.getInt("rf_received_total");
rf_fed_total = nbt.getInt("rf_fed_total"); liq_filled_avg = nbt.getInt("liq_filled_avg");
rf_fed_acc = nbt.getInt("rf_fed_acc"); liq_filled_total = nbt.getInt("liq_filled_total");
rf_received_avg = nbt.getInt("rf_received_avg"); liq_filled_acc = nbt.getInt("liq_filled_acc");
rf_received_total = nbt.getInt("rf_received_total"); liq_received_avg = nbt.getInt("liq_received_avg");
liq_filled_avg = nbt.getInt("liq_filled_avg"); liq_received_total = nbt.getInt("liq_received_total");
liq_filled_total = nbt.getInt("liq_filled_total"); rf_feed_setting = nbt.getInt("rf_feed_setting");
liq_filled_acc = nbt.getInt("liq_filled_acc"); items_received_total = nbt.getInt("items_received_total");
liq_received_avg = nbt.getInt("liq_received_avg"); items_inserted_total = nbt.getInt("items_inserted_total");
liq_received_total = nbt.getInt("liq_received_total"); if(nbt.contains("liq_fill_stack")) liq_fill_stack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("liq_fill_stack"));
rf_feed_setting = nbt.getInt("rf_feed_setting"); if(nbt.contains("insertion_item")) insertion_item = ItemStack.of(nbt.getCompound("insertion_item"));
items_received_total = nbt.getInt("items_received_total"); }
items_inserted_total = nbt.getInt("items_inserted_total");
if(nbt.contains("liq_fill_stack")) liq_fill_stack = FluidStack.loadFluidStackFromNBT(nbt.getCompound("liq_fill_stack")); @Override
if(nbt.contains("insertion_item")) insertion_item = ItemStack.of(nbt.getCompound("insertion_item")); public CompoundTag save(CompoundTag nbt)
} {
super.save(nbt);
@Override tank_.save(nbt);
public CompoundNBT save(CompoundNBT nbt) battery_.save(nbt);
{ nbt.putInt("rf_fed_avg", rf_fed_avg);
super.save(nbt); nbt.putInt("rf_fed_total", rf_fed_total);
tank_.save(nbt); nbt.putInt("rf_fed_acc", rf_fed_acc);
battery_.save(nbt); nbt.putInt("rf_received_avg", rf_received_avg);
nbt.putInt("rf_fed_avg", rf_fed_avg); nbt.putInt("rf_received_total", rf_received_total);
nbt.putInt("rf_fed_total", rf_fed_total); nbt.putInt("liq_filled_avg", liq_filled_avg);
nbt.putInt("rf_fed_acc", rf_fed_acc); nbt.putInt("liq_filled_total", liq_filled_total);
nbt.putInt("rf_received_avg", rf_received_avg); nbt.putInt("liq_filled_acc", liq_filled_acc);
nbt.putInt("rf_received_total", rf_received_total); nbt.putInt("liq_received_avg", liq_received_avg);
nbt.putInt("liq_filled_avg", liq_filled_avg); nbt.putInt("liq_received_total", liq_received_total);
nbt.putInt("liq_filled_total", liq_filled_total); nbt.putInt("rf_feed_setting", rf_feed_setting);
nbt.putInt("liq_filled_acc", liq_filled_acc); nbt.putInt("items_received_total", items_received_total);
nbt.putInt("liq_received_avg", liq_received_avg); nbt.putInt("items_inserted_total", items_inserted_total);
nbt.putInt("liq_received_total", liq_received_total); if(!liq_fill_stack.isEmpty()) nbt.put("liq_fill_stack", liq_fill_stack.writeToNBT(new CompoundTag()));
nbt.putInt("rf_feed_setting", rf_feed_setting); if(!insertion_item.isEmpty()) nbt.put("insertion_item", insertion_item.save(new CompoundTag()));
nbt.putInt("items_received_total", items_received_total); return nbt;
nbt.putInt("items_inserted_total", items_inserted_total); }
if(!liq_fill_stack.isEmpty()) nbt.put("liq_fill_stack", liq_fill_stack.writeToNBT(new CompoundNBT()));
if(!insertion_item.isEmpty()) nbt.put("insertion_item", insertion_item.save(new CompoundNBT())); private FluidStack getFillFluid(ItemStack stack)
return nbt; {
} // intentionally not item fluid handler, only specific items.
if(stack.getItem() == Items.WATER_BUCKET) return new FluidStack(Fluids.WATER, 1000);
private FluidStack getFillFluid(ItemStack stack) if(stack.getItem() == Items.LAVA_BUCKET) return new FluidStack(Fluids.LAVA, 1000);
{ return FluidStack.EMPTY;
// intentionally not item fluid handler, only specific items. }
if(stack.getItem() == Items.WATER_BUCKET) return new FluidStack(Fluids.WATER, 1000);
if(stack.getItem() == Items.LAVA_BUCKET) return new FluidStack(Fluids.LAVA, 1000); private ItemStack getRandomItemstack()
return FluidStack.EMPTY; {
} final int n = (int)Math.floor(Math.random() * ForgeRegistries.ITEMS.getValues().size());
ItemStack stack = new ItemStack(ForgeRegistries.ITEMS.getValues().stream().skip(n).findAny().orElse(Items.COBBLESTONE));
private ItemStack getRandomItemstack() stack.setCount((int)Math.floor(Math.random() * stack.getMaxStackSize()));
{ return stack;
final int n = (int)Math.floor(Math.random() * ForgeRegistries.ITEMS.getValues().size()); }
ItemStack stack = new ItemStack(ForgeRegistries.ITEMS.getValues().stream().skip(n).findAny().orElse(Items.COBBLESTONE));
stack.setCount((int)Math.floor(Math.random() * stack.getMaxStackSize())); public boolean activated(Player player, InteractionHand hand, BlockHitResult hit)
return stack; {
} final ItemStack held = player.getItemInHand(hand);
if(held.isEmpty()) {
public boolean activated(PlayerEntity player, Hand hand, BlockRayTraceResult hit) ArrayList<String> msgs = new ArrayList<>();
{ if(rf_fed_avg > 0) msgs.add("-" + rf_fed_avg + "rf/t");
final ItemStack held = player.getItemInHand(hand); if(rf_fed_total > 0) msgs.add("-" + rf_fed_total + "rf");
if(held.isEmpty()) { if(rf_received_avg > 0) msgs.add("+" + rf_received_avg + "rf/t");
ArrayList<String> msgs = new ArrayList<>(); if(rf_received_total > 0) msgs.add("+" + rf_received_total + "rf");
if(rf_fed_avg > 0) msgs.add("-" + rf_fed_avg + "rf/t"); if(liq_filled_avg > 0) msgs.add("-" + liq_filled_avg + "mb/t");
if(rf_fed_total > 0) msgs.add("-" + rf_fed_total + "rf"); if(liq_filled_total > 0) msgs.add("-" + liq_filled_total + "mb");
if(rf_received_avg > 0) msgs.add("+" + rf_received_avg + "rf/t"); if(liq_received_avg > 0) msgs.add("+" + liq_received_avg + "mb/t");
if(rf_received_total > 0) msgs.add("+" + rf_received_total + "rf"); if(liq_received_total > 0) msgs.add("+" + liq_received_total + "mb");
if(liq_filled_avg > 0) msgs.add("-" + liq_filled_avg + "mb/t"); if(items_received_total > 0) msgs.add("+" + items_received_total + "items");
if(liq_filled_total > 0) msgs.add("-" + liq_filled_total + "mb"); if(items_inserted_total > 0) msgs.add("-" + items_inserted_total + "items");
if(liq_received_avg > 0) msgs.add("+" + liq_received_avg + "mb/t"); if(msgs.isEmpty()) msgs.add("Nothing transferred yet.");
if(liq_received_total > 0) msgs.add("+" + liq_received_total + "mb"); Overlay.show(player, new TextComponent(String.join(" | ", msgs)), 1000);
if(items_received_total > 0) msgs.add("+" + items_received_total + "items"); return true;
if(items_inserted_total > 0) msgs.add("-" + items_inserted_total + "items"); } else if(paused) {
if(msgs.isEmpty()) msgs.add("Nothing transferred yet."); if(!getFillFluid(held).isEmpty()) {
Overlay.show(player, new StringTextComponent(String.join(" | ", msgs)), 1000); FluidStack fs = getFillFluid(held);
return true; if(liq_fill_stack.isEmpty() || !liq_fill_stack.isFluidEqual(fs)) {
} else if(paused) { fs.setAmount(128);
if(!getFillFluid(held).isEmpty()) { liq_fill_stack = fs;
FluidStack fs = getFillFluid(held); } else {
if(liq_fill_stack.isEmpty() || !liq_fill_stack.isFluidEqual(fs)) { int amount = liq_fill_stack.getAmount() * 2;
fs.setAmount(128); if(amount > 4096) amount = 16;
liq_fill_stack = fs; liq_fill_stack.setAmount(amount);
} else { }
int amount = liq_fill_stack.getAmount() * 2; if(liq_fill_stack.isEmpty()) {
if(amount > 4096) amount = 16; Overlay.show(player, new TextComponent("Fluid fill: none"), 1000);
liq_fill_stack.setAmount(amount); } else {
} Overlay.show(player, new TextComponent("Fluid fill: " + liq_fill_stack.getAmount() + "mb/t of " + liq_fill_stack.getFluid().getRegistryName()), 1000);
if(liq_fill_stack.isEmpty()) { }
Overlay.show(player, new StringTextComponent("Fluid fill: none"), 1000); } else if(held.getItem() == Items.REDSTONE) {
} else { rf_feed_setting = (rf_feed_setting<<1) & 0x00fffff0;
Overlay.show(player, new StringTextComponent("Fluid fill: " + liq_fill_stack.getAmount() + "mb/t of " + liq_fill_stack.getFluid().getRegistryName()), 1000); if(rf_feed_setting == 0) rf_feed_setting = 0x10;
} Overlay.show(player, new TextComponent("RF feed rate: " + rf_feed_setting + "rf/t"), 1000);
} else if(held.getItem() == Items.REDSTONE) { } else {
rf_feed_setting = (rf_feed_setting<<1) & 0x00fffff0; BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing));
if(rf_feed_setting == 0) rf_feed_setting = 0x10; if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.FACTORY_HOPPER) {
Overlay.show(player, new StringTextComponent("RF feed rate: " + rf_feed_setting + "rf/t"), 1000); insertion_item = held.copy();
} else { Overlay.show(player, new TextComponent("Insertion item: " + (insertion_item.getItem()==Items.LEVER ? "random" : insertion_item.toString()) + "/s"), 1000);
BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); }
if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.FACTORY_HOPPER) { }
insertion_item = held.copy(); return true;
Overlay.show(player, new StringTextComponent("Insertion item: " + (insertion_item.getItem()==Items.LEVER ? "random" : insertion_item.toString()) + "/s"), 1000); } else {
} return false;
} }
return true; }
} else {
return false; @Override
} public void setRemoved()
} {
super.setRemoved();
@Override energy_handler_.invalidate();
public void setRemoved() fluid_handler_.invalidate();
{ item_handler_.invalidate();
super.setRemoved(); }
energy_handler_.invalidate();
fluid_handler_.invalidate(); @Override
item_handler_.invalidate(); public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
} {
if((!paused) && (facing != block_facing)) {
@Override if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast();
{ if(capability ==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
if((!paused) && (facing != block_facing)) { }
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast(); return super.getCapability(capability, facing);
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast(); }
if(capability ==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
} @Override
return super.getCapability(capability, facing); public void tick()
} {
if(level.isClientSide()) return;
@Override block_facing = getBlockState().getValue(TestBlock.FACING);
public void tick() paused = level.hasNeighborSignal(getBlockPos());
{ if(!paused) {
if(level.isClientSide()) return; boolean dirty = false;
block_facing = getBlockState().getValue(TestBlock.FACING); {
paused = level.hasNeighborSignal(getBlockPos()); int p = RfEnergy.feed(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), rf_feed_setting);
if(!paused) { rf_fed_acc += p;
boolean dirty = false; dirty |= p>0;
{ }
int p = RfEnergy.feed(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), rf_feed_setting); if(!liq_fill_stack.isEmpty()) {
rf_fed_acc += p; int f = Fluidics.fill(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), liq_fill_stack);
dirty |= p>0; liq_filled_acc += f;
} dirty |= f>0;
if(!liq_fill_stack.isEmpty()) { }
int f = Fluidics.fill(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), liq_fill_stack); if(!inventory_.isEmpty()) {
liq_filled_acc += f; int i = inventory_.getItem(0).getCount();
dirty |= f>0; items_received_total += i;
} inventory_.clearContent();
if(!inventory_.isEmpty()) { dirty |= i>0;
int i = inventory_.getItem(0).getCount(); }
items_received_total += i; if((tick_timer == 1) && (!insertion_item.isEmpty())) {
inventory_.clearContent(); BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing));
dirty |= i>0; ItemStack stack = (insertion_item.getItem()==Items.LEVER) ? getRandomItemstack() : insertion_item.copy();
} if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.FACTORY_HOPPER) {
if((tick_timer == 1) && (!insertion_item.isEmpty())) { ItemStack remaining = Inventories.insert(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), stack, false);
BlockState adjacent_state = level.getBlockState(worldPosition.relative(block_facing)); int n = stack.getCount() - remaining.getCount();
ItemStack stack = (insertion_item.getItem()==Items.LEVER) ? getRandomItemstack() : insertion_item.copy(); items_inserted_total += n;
if(adjacent_state.getBlock()==Blocks.HOPPER || adjacent_state.getBlock()==ModContent.FACTORY_HOPPER) { dirty |= n>0;
ItemStack remaining = Inventories.insert(getLevel(), getBlockPos().relative(block_facing), block_facing.getOpposite(), stack, false); }
int n = stack.getCount() - remaining.getCount(); }
items_inserted_total += n; if(dirty) {
dirty |= n>0; setChanged();
} }
} }
if(dirty) { if(--tick_timer <= 0) {
setChanged(); tick_timer = 20;
} rf_fed_avg = rf_fed_acc/20;
} rf_fed_total += rf_fed_acc;
if(--tick_timer <= 0) { rf_fed_acc = 0;
tick_timer = 20; rf_received_avg = battery_.getEnergyStored()/20;
rf_fed_avg = rf_fed_acc/20; rf_received_total += battery_.getEnergyStored();
rf_fed_total += rf_fed_acc; battery_.clear();
rf_fed_acc = 0; liq_received_avg = tank_.getFluidAmount();
rf_received_avg = battery_.getEnergyStored()/20; liq_received_total += tank_.getFluidAmount();
rf_received_total += battery_.getEnergyStored(); tank_.clear();
battery_.clear(); liq_filled_avg = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_acc/20);
liq_received_avg = tank_.getFluidAmount(); liq_filled_total = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_total+liq_filled_acc);
liq_received_total += tank_.getFluidAmount(); liq_filled_acc = 0;
tank_.clear(); }
liq_filled_avg = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_acc/20); }
liq_filled_total = (liq_fill_stack.isEmpty()) ? 0 : (liq_filled_total+liq_filled_acc);
liq_filled_acc = 0; }
} }
}
}
}

View file

@ -1,274 +1,270 @@
/* /*
* @file EdTreeCutter.java * @file EdTreeCutter.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Small Tree Cutter * Small Tree Cutter
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.world.IWorldReader; import net.minecraft.core.BlockPos;
import net.minecraft.world.IBlockReader; import net.minecraft.core.Direction;
import net.minecraft.world.World; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.block.AbstractBlock; import net.minecraft.nbt.CompoundTag;
import net.minecraft.block.Block; import net.minecraft.sounds.SoundEvents;
import net.minecraft.block.BlockState; import net.minecraft.sounds.SoundSource;
import net.minecraft.state.BooleanProperty; import net.minecraft.util.Mth;
import net.minecraft.state.StateContainer; import net.minecraft.world.InteractionHand;
import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.world.InteractionResult;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.world.entity.player.Player;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.Level;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.LevelReader;
import net.minecraft.particles.ParticleTypes; import net.minecraft.world.level.block.Block;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.*; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.common.util.LazyOptional;
import wile.engineersdecor.ModConfig; import net.minecraftforge.energy.CapabilityEnergy;
import wile.engineersdecor.ModContent; import net.minecraftforge.energy.IEnergyStorage;
import wile.engineersdecor.detail.TreeCutting; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.libmc.detail.Auxiliaries; import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.detail.Overlay; import wile.engineersdecor.detail.TreeCutting;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.blocks.StandardEntityBlocks;
import java.util.Random; import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import javax.annotation.Nullable;
public class EdTreeCutter import java.util.Random;
{
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
{ TreeCutterTileEntity.on_config(boost_energy_per_tick, cutting_time_seconds,power_required); }
public class EdTreeCutter
//-------------------------------------------------------------------------------------------------------------------- {
// Block public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
//-------------------------------------------------------------------------------------------------------------------- { TreeCutterTileEntity.on_config(boost_energy_per_tick, cutting_time_seconds,power_required); }
public static class TreeCutterBlock extends DecorBlock.Horizontal implements IDecorBlock //--------------------------------------------------------------------------------------------------------------------
{ // Block
public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); //--------------------------------------------------------------------------------------------------------------------
public TreeCutterBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB[] unrotatedAABB) public static class TreeCutterBlock extends StandardBlocks.Horizontal implements StandardEntityBlocks.IStandardEntityBlock<TreeCutterTileEntity>
{ super(config, builder, unrotatedAABB); } {
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
@Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) public TreeCutterBlock(long config, BlockBehaviour.Properties builder, final AABB[] unrotatedAABB)
{ super.createBlockStateDefinition(builder); builder.add(ACTIVE); } { super(config, builder, unrotatedAABB); }
@Override @Override
@Nullable @Nullable
public BlockState getStateForPlacement(BlockItemUseContext context) public BlockEntityType<EdTreeCutter.TreeCutterTileEntity> getBlockEntityType()
{ return super.getStateForPlacement(context).setValue(ACTIVE, false); } { return ModContent.TET_SMALL_TREE_CUTTER; }
@Override @Override
public boolean hasTileEntity(BlockState state) protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ return true; } { super.createBlockStateDefinition(builder); builder.add(ACTIVE); }
@Override @Override
@Nullable @Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world) public BlockState getStateForPlacement(BlockPlaceContext context)
{ return new TreeCutterTileEntity(); } { return super.getStateForPlacement(context).setValue(ACTIVE, false); }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd) public void animateTick(BlockState state, Level world, BlockPos pos, Random rnd)
{ {
if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return; if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return;
final double rv = rnd.nextDouble(); // Sound
if(rv > 0.8) return; if(true || (world.getGameTime() & 0x1) == 0) {
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ(); world.playLocalSound(pos.getX(), pos.getY(), pos.getZ(), SoundEvents.WOOD_HIT, SoundSource.BLOCKS, 0.1f, 1.0f, false);
final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2); }
switch(state.getValue(HORIZONTAL_FACING)) { // Particles
case WEST: world.addParticle(ParticleTypes.SMOKE, x-xc, yr, z+xr, 0.0, 0.0, 0.0); break; {
case EAST: world.addParticle(ParticleTypes.SMOKE, x+xc, yr, z+xr, 0.0, 0.0, 0.0); break; final double rv = rnd.nextDouble();
case NORTH: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z-xc, 0.0, 0.0, 0.0); break; if(rv < 0.8) {
default: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z+xc, 0.0, 0.0, 0.0); break; final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
} final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2);
} switch(state.getValue(HORIZONTAL_FACING)) {
case WEST -> world.addParticle(ParticleTypes.SMOKE, x - xc, yr, z + xr, 0.0, 0.0, 0.0);
@Override case EAST -> world.addParticle(ParticleTypes.SMOKE, x + xc, yr, z + xr, 0.0, 0.0, 0.0);
@SuppressWarnings("deprecation") case NORTH -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z - xc, 0.0, 0.0, 0.0);
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) default -> world.addParticle(ParticleTypes.SMOKE, x + xr, yr, z + xc, 0.0, 0.0, 0.0);
{ }
if(world.isClientSide()) return ActionResultType.SUCCESS; }
TileEntity te = world.getBlockEntity(pos); }
if(te instanceof TreeCutterTileEntity) ((TreeCutterTileEntity)te).state_message(player); }
return ActionResultType.CONSUME;
} @Override
@SuppressWarnings("deprecation")
@Override public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
public boolean shouldCheckWeakPower(BlockState state, IWorldReader world, BlockPos pos, Direction side) {
{ return false; } if(world.isClientSide()) return InteractionResult.SUCCESS;
} BlockEntity te = world.getBlockEntity(pos);
if(te instanceof TreeCutterTileEntity) ((TreeCutterTileEntity)te).state_message(player);
//-------------------------------------------------------------------------------------------------------------------- return InteractionResult.CONSUME;
// Tile entity }
//--------------------------------------------------------------------------------------------------------------------
@Override
public static class TreeCutterTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage public boolean shouldCheckWeakPower(BlockState state, LevelReader world, BlockPos pos, Direction side)
{ { return false; }
public static final int IDLE_TICK_INTERVAL = 40; }
public static final int TICK_INTERVAL = 5;
public static final int BOOST_FACTOR = 6; //--------------------------------------------------------------------------------------------------------------------
public static final int DEFAULT_BOOST_ENERGY = 64; // Tile entity
public static final int DEFAULT_CUTTING_TIME_NEEDED = 60; // 60 secs, so that people don't come to the bright idea to carry one with them. //--------------------------------------------------------------------------------------------------------------------
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int energy_max = DEFAULT_BOOST_ENERGY * 20; public static class TreeCutterTileEntity extends StandardEntityBlocks.StandardBlockEntity implements IEnergyStorage
private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED; {
private static boolean requires_power = false; public static final int IDLE_TICK_INTERVAL = 40;
public static final int TICK_INTERVAL = 5;
private int tick_timer_; public static final int BOOST_FACTOR = 6;
private int active_timer_; public static final int DEFAULT_BOOST_ENERGY = 64;
private int proc_time_elapsed_; public static final int DEFAULT_CUTTING_TIME_NEEDED = 60; // 60 secs, so that people don't come to the bright idea to carry one with them.
private int energy_; private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int energy_max = DEFAULT_BOOST_ENERGY * 20;
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required) private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED;
{ private static boolean requires_power = false;
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000); private int tick_timer_;
cutting_time_needed = 20 * MathHelper.clamp(cutting_time_seconds, 10, 240); private int active_timer_;
requires_power = power_required; private int proc_time_elapsed_;
ModConfig.log("Config tree cutter: energy consumption:" + (boost_energy_consumption/TICK_INTERVAL) + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time:" + cutting_time_needed + "t." ); private int energy_;
}
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
public TreeCutterTileEntity() {
{ super(ModContent.TET_SMALL_TREE_CUTTER); } boost_energy_consumption = TICK_INTERVAL * Mth.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
public TreeCutterTileEntity(TileEntityType<?> te_type) cutting_time_needed = 20 * Mth.clamp(cutting_time_seconds, 10, 240);
{ super(te_type); } requires_power = power_required;
ModConfig.log("Config tree cutter: energy consumption:" + (boost_energy_consumption/TICK_INTERVAL) + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time:" + cutting_time_needed + "t." );
public void readnbt(CompoundNBT nbt) }
{ energy_ = nbt.getInt("energy"); }
public TreeCutterTileEntity(BlockPos pos, BlockState state)
private void writenbt(CompoundNBT nbt) { super(ModContent.TET_SMALL_TREE_CUTTER, pos, state); }
{ nbt.putInt("energy", energy_); }
public void readnbt(CompoundTag nbt)
public void state_message(PlayerEntity player) { energy_ = nbt.getInt("energy"); }
{
String progress = "0"; private void writenbt(CompoundTag nbt)
if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) { { nbt.putInt("energy", energy_); }
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100));
} public void state_message(Player player)
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100)); {
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", new Object[]{soc, energy_max, progress, (cutting_time_needed/20) })); String progress = "0";
} if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) {
progress = Integer.toString((int)Mth.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100));
// TileEntity ------------------------------------------------------------------------------ }
String soc = Integer.toString(Mth.clamp((energy_*100/energy_max),0,100));
@Override Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", soc, energy_max, progress, (cutting_time_needed/20)));
public void load(BlockState state, CompoundNBT nbt) }
{ super.load(state, nbt); readnbt(nbt); }
// BlockEntity ------------------------------------------------------------------------------
@Override
public CompoundNBT save(CompoundNBT nbt) @Override
{ super.save(nbt); writenbt(nbt); return nbt; } public void load(CompoundTag nbt)
{ super.load(nbt); readnbt(nbt); }
@Override
public void setRemoved() @Override
{ public CompoundTag save(CompoundTag nbt)
super.setRemoved(); { super.save(nbt); writenbt(nbt); return nbt; }
energy_handler_.invalidate();
} @Override
public void setRemoved()
// IEnergyStorage ---------------------------------------------------------------------------- {
super.setRemoved();
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this); energy_handler_.invalidate();
}
@Override
public boolean canExtract() // IEnergyStorage ----------------------------------------------------------------------------
{ return false; }
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> this);
@Override
public boolean canReceive() @Override
{ return true; } public boolean canExtract()
{ return false; }
@Override
public int getMaxEnergyStored() @Override
{ return boost_energy_consumption*2; } public boolean canReceive()
{ return true; }
@Override
public int getEnergyStored() @Override
{ return energy_; } public int getMaxEnergyStored()
{ return boost_energy_consumption*2; }
@Override
public int extractEnergy(int maxExtract, boolean simulate) @Override
{ return 0; } public int getEnergyStored()
{ return energy_; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate) @Override
{ public int extractEnergy(int maxExtract, boolean simulate)
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0)); { return 0; }
if(!simulate) energy_ += maxReceive;
return maxReceive; @Override
} public int receiveEnergy(int maxReceive, boolean simulate)
{
// Capability export ---------------------------------------------------------------------------- maxReceive = Mth.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0));
if(!simulate) energy_ += maxReceive;
@Override return maxReceive;
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing) }
{
if(capability== CapabilityEnergy.ENERGY) return energy_handler_.cast(); // Capability export ----------------------------------------------------------------------------
return super.getCapability(capability, facing);
} @Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
// ITickable ------------------------------------------------------------------------------------ {
if(capability== CapabilityEnergy.ENERGY) return energy_handler_.cast();
@Override return super.getCapability(capability, facing);
public void tick() }
{
if(--tick_timer_ > 0) return; // ITickable ------------------------------------------------------------------------------------
final BlockState device_state = level.getBlockState(worldPosition);
if(!(device_state.getBlock() instanceof TreeCutterBlock)) { tick_timer_ = TICK_INTERVAL; return; } @Override
if(level.isClientSide) { public void tick()
if(!device_state.getValue(TreeCutterBlock.ACTIVE)) { {
tick_timer_ = TICK_INTERVAL; if(--tick_timer_ > 0) return;
} else { tick_timer_ = TICK_INTERVAL;
tick_timer_ = 1; final BlockState device_state = level.getBlockState(worldPosition);
level.playLocalSound(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), SoundEvents.WOOD_HIT, SoundCategory.BLOCKS, 0.1f, 1.0f, false); if(!(device_state.getBlock() instanceof TreeCutterBlock)) return;
} final BlockPos tree_pos = worldPosition.relative(device_state.getValue(TreeCutterBlock.HORIZONTAL_FACING));
} else { final BlockState tree_state = level.getBlockState(tree_pos);
tick_timer_ = TICK_INTERVAL; if(!TreeCutting.canChop(tree_state) || (level.hasNeighborSignal(worldPosition))) {
final BlockPos tree_pos = worldPosition.relative(device_state.getValue(TreeCutterBlock.HORIZONTAL_FACING)); if(device_state.getValue(TreeCutterBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, false), 1|2);
final BlockState tree_state = level.getBlockState(tree_pos); proc_time_elapsed_ = 0;
if(!TreeCutting.canChop(tree_state) || (level.hasNeighborSignal(worldPosition))) { active_timer_ = 0;
if(device_state.getValue(TreeCutterBlock.ACTIVE)) level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, false), 1|2); tick_timer_ = IDLE_TICK_INTERVAL;
proc_time_elapsed_ = 0; return;
active_timer_ = 0; }
tick_timer_ = IDLE_TICK_INTERVAL; proc_time_elapsed_ += TICK_INTERVAL;
return; if(energy_ >= boost_energy_consumption) {
} energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL; proc_time_elapsed_ += TICK_INTERVAL*BOOST_FACTOR;
if(energy_ >= boost_energy_consumption) { active_timer_ = 2;
energy_ -= boost_energy_consumption; } else if(!requires_power) {
proc_time_elapsed_ += TICK_INTERVAL*BOOST_FACTOR; active_timer_ = 1024;
active_timer_ = 2; } else if(active_timer_ > 0) {
} else if(!requires_power) { --active_timer_;
active_timer_ = 1024; }
} else if(active_timer_ > 0) { boolean active = (active_timer_ > 0);
--active_timer_; if(requires_power && !active) {
} proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
boolean active = (active_timer_ > 0); }
if(requires_power && !active) { if(proc_time_elapsed_ >= cutting_time_needed) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL); proc_time_elapsed_ = 0;
} TreeCutting.chopTree(level, tree_state, tree_pos, 512, false);
if(proc_time_elapsed_ >= cutting_time_needed) { level.playSound(null, worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), SoundEvents.WOOD_BREAK, SoundSource.BLOCKS, 1.0f, 1.0f);
proc_time_elapsed_ = 0; active = false;
TreeCutting.chopTree(level, tree_state, tree_pos, 512, false); }
level.playSound(null, worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), SoundEvents.WOOD_BREAK, SoundCategory.BLOCKS, 1.0f, 1.0f); if(device_state.getValue(TreeCutterBlock.ACTIVE) != active) {
active = false; level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, active), 1|2);
} }
if(device_state.getValue(TreeCutterBlock.ACTIVE) != active) { }
level.setBlock(worldPosition, device_state.setValue(TreeCutterBlock.ACTIVE, active), 1|2); }
} }
}
}
}
}

View file

@ -1,31 +1,35 @@
/* /*
* @file EdWallBlock.java * @file EdWallBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Wall blocks. * Wall blocks.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.*; import net.minecraft.core.BlockPos;
import net.minecraft.util.Direction; import net.minecraft.core.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.IWorldReader; import net.minecraft.world.level.block.IronBarsBlock;
import wile.engineersdecor.libmc.blocks.VariantWallBlock; import net.minecraft.world.level.block.StainedGlassPaneBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
public class EdWallBlock extends VariantWallBlock implements IDecorBlock import wile.engineersdecor.libmc.blocks.VariantWallBlock;
{
public EdWallBlock(long config, AbstractBlock.Properties builder)
{ super(config, builder); } public class EdWallBlock extends VariantWallBlock
{
protected boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side) public EdWallBlock(long config, BlockBehaviour.Properties builder)
{ { super(config, builder); }
if(facingState==null) return false;
if(super.attachesTo(facingState, world, facingPos, side)) return true; protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side)
if(facingState.getBlock() instanceof EdWindowBlock) return true; {
if(facingState.getBlock() instanceof PaneBlock) return true; if(facingState==null) return false;
return false; if(super.attachesTo(facingState, world, facingPos, side)) return true;
} if(facingState.getBlock() instanceof EdWindowBlock) return true;
} if(facingState.getBlock() instanceof IronBarsBlock) return true;
if(facingState.getBlock() instanceof StainedGlassPaneBlock) return true;
return false;
}
}

View file

@ -1,88 +1,93 @@
/* /*
* @file EdWindowBlock.java * @file EdWindowBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Mod windows. * Mod windows.
*/ */
package wile.engineersdecor.blocks; package wile.engineersdecor.blocks;
import net.minecraft.block.AbstractBlock; import net.minecraft.core.BlockPos;
import net.minecraft.block.BlockState; import net.minecraft.core.Direction;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.sounds.SoundEvents;
import net.minecraft.fluid.Fluids; import net.minecraft.sounds.SoundSource;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.InteractionHand;
import net.minecraft.item.DirectionalPlaceContext; import net.minecraft.world.InteractionResult;
import net.minecraft.util.*; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable; import net.minecraft.world.level.material.Fluids;
import java.util.Arrays; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
public class EdWindowBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock import javax.annotation.Nullable;
{ import java.util.Arrays;
public EdWindowBlock(long config, AbstractBlock.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override public class EdWindowBlock extends StandardBlocks.DirectedWaterLoggable
public RenderTypeHint getRenderTypeHint() {
{ return RenderTypeHint.TRANSLUCENT; } public EdWindowBlock(long config, BlockBehaviour.Properties builder, final AABB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) public RenderTypeHint getRenderTypeHint()
{ { return RenderTypeHint.TRANSLUCENT; }
Direction facing = context.getHorizontalDirection();
if(Math.abs(context.getPlayer().getLookAngle().y) > 0.9) { @Override
facing = context.getNearestLookingDirection(); @Nullable
} else { public BlockState getStateForPlacement(BlockPlaceContext context)
for(Direction f: Direction.values()) { {
BlockState st = context.getLevel().getBlockState(context.getClickedPos().relative(f)); Direction facing = context.getHorizontalDirection();
if(st.getBlock() == this) { if(Math.abs(context.getPlayer().getLookAngle().y) > 0.9) {
facing = st.getValue(FACING); facing = context.getNearestLookingDirection();
break; } else {
} for(Direction f: Direction.values()) {
} BlockState st = context.getLevel().getBlockState(context.getClickedPos().relative(f));
} if(st.getBlock() == this) {
return super.getStateForPlacement(context).setValue(FACING, facing); facing = st.getValue(FACING);
} break;
}
@Override }
@SuppressWarnings("deprecation") }
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit) return super.getStateForPlacement(context).setValue(FACING, facing);
{ }
if(player.getItemInHand(hand).getItem() != asItem()) return ActionResultType.PASS;
final Direction facing = state.getValue(FACING); @Override
if(facing.getAxis() != hit.getDirection().getAxis()) return ActionResultType.PASS; @SuppressWarnings("deprecation")
Arrays.stream(Direction.orderedByNearest(player)) public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
.filter(d->d.getAxis() != facing.getAxis()) {
.filter(d->world.getBlockState(pos.relative(d)).canBeReplaced((new DirectionalPlaceContext(world, pos.relative(d), facing.getOpposite(), player.getItemInHand(hand), facing)))) if(player.getItemInHand(hand).getItem() != asItem()) return InteractionResult.PASS;
.findFirst().ifPresent((d)->{ final Direction facing = state.getValue(FACING);
BlockState st = defaultBlockState() if(facing.getAxis() != hit.getDirection().getAxis()) return InteractionResult.PASS;
.setValue(FACING, facing) Arrays.stream(Direction.orderedByNearest(player))
.setValue(WATERLOGGED,world.getBlockState(pos.relative(d)).getFluidState().getType()==Fluids.WATER); .filter(d->d.getAxis() != facing.getAxis())
world.setBlock(pos.relative(d), st, 1|2); .filter(d->world.getBlockState(pos.relative(d)).canBeReplaced((new DirectionalPlaceContext(world, pos.relative(d), facing.getOpposite(), player.getItemInHand(hand), facing))))
world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundCategory.BLOCKS, 1f, 1f); .findFirst().ifPresent((d)->{
player.getItemInHand(hand).shrink(1); BlockState st = defaultBlockState()
} .setValue(FACING, facing)
); .setValue(WATERLOGGED,world.getBlockState(pos.relative(d)).getFluidState().getType()==Fluids.WATER);
return ActionResultType.sidedSuccess(world.isClientSide()); world.setBlock(pos.relative(d), st, 1|2);
} world.playSound(player, pos, SoundEvents.METAL_PLACE, SoundSource.BLOCKS, 1f, 1f);
player.getItemInHand(hand).shrink(1);
@Override }
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos) );
{ return true; } return InteractionResult.sidedSuccess(world.isClientSide());
}
@Override
@SuppressWarnings("deprecation") @Override
public boolean useShapeForLightOcclusion(BlockState state) public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos)
{ return true; } { return true; }
} @Override
@SuppressWarnings("deprecation")
public boolean useShapeForLightOcclusion(BlockState state)
{ return true; }
}

View file

@ -1,15 +0,0 @@
/*
* @file IDecorBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Interface for tagging and common default behaviour.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
public interface IDecorBlock extends StandardBlocks.IStandardBlock
{
}

View file

@ -1,22 +0,0 @@
/*
* @file ExtItems.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Object holder based item references.
*/
package wile.engineersdecor.detail;
import net.minecraft.item.Item;
import net.minecraftforge.registries.ObjectHolder;
public class ExternalObjects
{
@ObjectHolder("bottledmilk:milk_bottle_drinkable")
public static final Item BOTTLED_MILK_BOTTLE_DRINKLABLE = null;
public static final void onPostInit()
{}
}

View file

@ -1,165 +1,169 @@
/* /*
* @file ModTesrs.java * @file ModTesrs.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Yet unstructured initial experiments with TESRs. * Yet unstructured initial experiments with TESRs.
* May be structured after I know what I am doing there. * May be structured after I know what I am doing there.
*/ */
package wile.engineersdecor.detail; package wile.engineersdecor.detail;
import net.minecraft.util.text.ITextComponent; import com.mojang.blaze3d.vertex.PoseStack;
import wile.engineersdecor.ModEngineersDecor; import com.mojang.math.Vector3f;
import wile.engineersdecor.blocks.EdCraftingTable; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.entity.Entity; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.ResourceLocation; import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.util.math.vector.*; import net.minecraft.network.chat.Component;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer; import net.minecraft.resources.ResourceLocation;
import net.minecraft.client.Minecraft; import net.minecraft.util.Mth;
import net.minecraft.item.ItemStack; import net.minecraft.world.entity.Entity;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.Dist;
import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.blocks.EdCraftingTable.CraftingTableBlock; import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.blocks.EdLabeledCrate; import wile.engineersdecor.blocks.EdCraftingTable;
import wile.engineersdecor.blocks.EdCraftingTable.CraftingTableBlock;
import wile.engineersdecor.blocks.EdLabeledCrate;
public class ModRenderers
{
//-------------------------------------------------------------------------------------------------------------------- public class ModRenderers
// InvisibleEntityRenderer {
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
// InvisibleEntityRenderer
@OnlyIn(Dist.CLIENT) //--------------------------------------------------------------------------------------------------------------------
public static class InvisibleEntityRenderer<T extends Entity> extends EntityRenderer<T>
{ @OnlyIn(Dist.CLIENT)
private final Minecraft mc = Minecraft.getInstance(); public static class InvisibleEntityRenderer<T extends Entity> extends EntityRenderer<T>
{
public InvisibleEntityRenderer(EntityRendererManager renderManagerIn) private final Minecraft mc = Minecraft.getInstance();
{ super(renderManagerIn); }
public InvisibleEntityRenderer(EntityRendererProvider.Context context)
public void render(T entity, float entityYaw, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight) { super(context); }
{}
public void render(T entity, float entityYaw, float partialTicks, PoseStack matrixStack, MultiBufferSource buffer, int packedLight)
public Vector3d getRenderOffset(T entity, float partialTicks) {}
{ return Vector3d.ZERO; }
public Vec3 getRenderOffset(T entity, float partialTicks)
@SuppressWarnings("deprecation") { return Vec3.ZERO; }
public ResourceLocation getTextureLocation(T entity)
{ return AtlasTexture.LOCATION_BLOCKS; } @SuppressWarnings("deprecation")
public ResourceLocation getTextureLocation(T entity)
protected boolean shouldShowName(T entity) { return TextureAtlas.LOCATION_BLOCKS; }
{ return false; }
protected boolean shouldShowName(T entity)
protected void renderNameTag(T entity, ITextComponent displayName, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight) { return false; }
{}
} protected void renderNameTag(T entity, Component displayName, PoseStack matrixStack, MultiBufferSource buffer, int packedLight)
{}
//-------------------------------------------------------------------------------------------------------------------- }
// Crafting table
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
// Crafting table
@OnlyIn(Dist.CLIENT) //--------------------------------------------------------------------------------------------------------------------
public static class CraftingTableTer extends TileEntityRenderer<EdCraftingTable.CraftingTableTileEntity>
{ @OnlyIn(Dist.CLIENT)
private static int tesr_error_counter = 4; public static class CraftingTableTer implements BlockEntityRenderer<EdCraftingTable.CraftingTableTileEntity>
private static float scaler = 0.1f; {
private static float gap = 0.19f; private static int tesr_error_counter = 4;
private static float yrotations[] = {0, 90, 180, 270}; // [hdirection] S-W-N-E private static final float scaler = 0.1f;
private static float offsets[][][] = { // [hdirection][slotindex][xz] private static final float gap = 0.19f;
{ {-1,-1},{+0,-1},{+1,-1}, {-1,+0},{+0,+0},{+1,+0}, {-1,+1},{+0,+1},{+1,+1} }, // S private static final float[] yrotations = {0, 90, 180, 270}; // [hdirection] S-W-N-E
{ {+1,-1},{+1,+0},{+1,+1}, {+0,-1},{+0,+0},{+0,+1}, {-1,-1},{-1,+0},{-1,+1} }, // W private static final float[][][] offsets = { // [hdirection][slotindex][xz]
{ {+1,+1},{+0,+1},{-1,+1}, {+1,+0},{+0,+0},{-1,+0}, {+1,-1},{+0,-1},{-1,-1} }, // N { {-1,-1},{+0,-1},{+1,-1}, {-1,+0},{+0,+0},{+1,+0}, {-1,+1},{+0,+1},{+1,+1} }, // S
{ {-1,+1},{-1,+0},{-1,-1}, {+0,+1},{+0,+0},{+0,-1}, {+1,+1},{+1,+0},{+1,-1} }, // E { {+1,-1},{+1,+0},{+1,+1}, {+0,-1},{+0,+0},{+0,+1}, {-1,-1},{-1,+0},{-1,+1} }, // W
}; { {+1,+1},{+0,+1},{-1,+1}, {+1,+0},{+0,+0},{-1,+0}, {+1,-1},{+0,-1},{-1,-1} }, // N
{ {-1,+1},{-1,+0},{-1,-1}, {+0,+1},{+0,+0},{+0,-1}, {+1,+1},{+1,+0},{+1,-1} }, // E
public CraftingTableTer(TileEntityRendererDispatcher dispatcher) };
{ super(dispatcher); } private final BlockEntityRendererProvider.Context renderer_;
@Override public CraftingTableTer(BlockEntityRendererProvider.Context renderer)
@SuppressWarnings("deprecation") { this.renderer_ = renderer; }
public void render(final EdCraftingTable.CraftingTableTileEntity te, float unused1, MatrixStack mxs, IRenderTypeBuffer buf, int i5, int i6)
{ @Override
if(tesr_error_counter <= 0) return; @SuppressWarnings("deprecation")
try { public void render(final EdCraftingTable.CraftingTableTileEntity te, float unused1, PoseStack mxs, MultiBufferSource buf, int i5, int overlayTexture)
final int di = MathHelper.clamp(te.getLevel().getBlockState(te.getBlockPos()).getValue(CraftingTableBlock.HORIZONTAL_FACING).get2DDataValue(), 0, 3); {
long posrnd = te.getBlockPos().asLong(); if(tesr_error_counter <= 0) return;
posrnd = (posrnd>>16)^(posrnd<<1); try {
for(int i=0; i<9; ++i) { final int di = Mth.clamp(te.getLevel().getBlockState(te.getBlockPos()).getValue(CraftingTableBlock.HORIZONTAL_FACING).get2DDataValue(), 0, 3);
final ItemStack stack = te.mainInventory().getItem(i); long posrnd = te.getBlockPos().asLong();
if(stack.isEmpty()) continue; posrnd = (posrnd>>16)^(posrnd<<1);
float prnd = ((float)(((Integer.rotateRight(stack.getItem().hashCode()^(int)posrnd,(stack.getCount()+i)&31)))&1023))/1024f; for(int i=0; i<9; ++i) {
float rndo = gap * ((prnd*0.1f)-0.05f); final ItemStack stack = te.mainInventory().getItem(i);
float ox = gap * offsets[di][i][0], oz = gap * offsets[di][i][1]; if(stack.isEmpty()) continue;
float oy = 0.5f; float prnd = ((float)(((Integer.rotateRight(stack.getItem().hashCode()^(int)posrnd,(stack.getCount()+i)&31)))&1023))/1024f;
float ry = ((yrotations[di]+180) + ((prnd*60)-30)) % 360; float rndo = gap * ((prnd*0.1f)-0.05f);
if(stack.isEmpty()) return; float ox = gap * offsets[di][i][0], oz = gap * offsets[di][i][1];
mxs.pushPose(); float oy = 0.5f;
mxs.translate(0.5+ox, 0.5+oy, 0.5+oz); float ry = ((yrotations[di]+180) + ((prnd*60)-30)) % 360;
mxs.mulPose(Vector3f.XP.rotationDegrees(90.0f)); if(stack.isEmpty()) return;
mxs.mulPose(Vector3f.ZP.rotationDegrees(ry)); mxs.pushPose();
mxs.translate(rndo, rndo, 0); mxs.translate(0.5+ox, 0.5+oy, 0.5+oz);
mxs.scale(scaler, scaler, scaler); mxs.mulPose(Vector3f.XP.rotationDegrees(90.0f));
Minecraft.getInstance().getItemRenderer().renderStatic(stack, net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.FIXED, i5, i6, mxs, buf); mxs.mulPose(Vector3f.ZP.rotationDegrees(ry));
mxs.popPose(); mxs.translate(rndo, rndo, 0);
} mxs.scale(scaler, scaler, scaler);
} catch(Throwable e) { Minecraft.getInstance().getItemRenderer().renderStatic(stack, ItemTransforms.TransformType.FIXED, i5, overlayTexture, mxs, buf, 0);
if(--tesr_error_counter<=0) { mxs.popPose();
ModEngineersDecor.logger().error("TER was disabled because broken, exception was: " + e.getMessage()); }
ModEngineersDecor.logger().error(e.getStackTrace()); } catch(Throwable e) {
} if(--tesr_error_counter<=0) {
} ModEngineersDecor.logger().error("TER was disabled because broken, exception was: " + e.getMessage());
} ModEngineersDecor.logger().error(e.getStackTrace());
} }
}
//-------------------------------------------------------------------------------------------------------------------- }
// Labeled Crate }
//--------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT) // Labeled Crate
public static class DecorLabeledCrateTer extends TileEntityRenderer<EdLabeledCrate.LabeledCrateTileEntity> //--------------------------------------------------------------------------------------------------------------------
{
private static int tesr_error_counter = 4; @OnlyIn(Dist.CLIENT)
private static float scaler = 0.35f; public static class DecorLabeledCrateTer implements BlockEntityRenderer<EdLabeledCrate.LabeledCrateTileEntity>
double tr[][]= { // [hdirection=S-W-N-E][param] {
{ +8.0/32, -8.0/32, +15.5/32, 180.0 }, // N private static int tesr_error_counter = 4;
{ -15.5/32, -8.0/32, +8.0/32, 90.0 }, // E private static final float scaler = 0.35f;
{ -8.0/32, -8.0/32, -15.5/32, 0.0 }, // S param=tx,ty,tz,ry private static final double[][] tr = { // [hdirection=S-W-N-E][param]
{ +15.5/32, -8.0/32, -8.0/32, 270.0 }, // W { +8.0/32, -8.0/32, +15.5/32, 180.0 }, // N
}; { -15.5/32, -8.0/32, +8.0/32, 90.0 }, // E
{ -8.0/32, -8.0/32, -15.5/32, 0.0 }, // S param=tx,ty,tz,ry
public DecorLabeledCrateTer(TileEntityRendererDispatcher dispatcher) { +15.5/32, -8.0/32, -8.0/32, 270.0 }, // W
{ super(dispatcher); } };
private final BlockEntityRendererProvider.Context renderer_;
@Override
@SuppressWarnings("deprecation") public DecorLabeledCrateTer(BlockEntityRendererProvider.Context renderer)
public void render(final EdLabeledCrate.LabeledCrateTileEntity te, float unused1, MatrixStack mxs, IRenderTypeBuffer buf, int i5, int i6) { this.renderer_ = renderer; }
{
if(tesr_error_counter<=0) return; @Override
try { @SuppressWarnings("deprecation")
final ItemStack stack = te.getItemFrameStack(); public void render(final EdLabeledCrate.LabeledCrateTileEntity te, float unused1, PoseStack mxs, MultiBufferSource buf, int i5, int overlayTexture)
if(stack.isEmpty()) return; {
final int di = MathHelper.clamp(te.getLevel().getBlockState(te.getBlockPos()).getValue(EdLabeledCrate.LabeledCrateBlock.HORIZONTAL_FACING).get2DDataValue(), 0, 3); if(tesr_error_counter<=0) return;
double ox = tr[di][0], oy = tr[di][1], oz = tr[di][2]; try {
float ry = (float)tr[di][3]; final ItemStack stack = te.getItemFrameStack();
mxs.pushPose(); if(stack.isEmpty()) return;
mxs.translate(0.5+ox, 0.5+oy, 0.5+oz); final int di = Mth.clamp(te.getLevel().getBlockState(te.getBlockPos()).getValue(EdLabeledCrate.LabeledCrateBlock.HORIZONTAL_FACING).get2DDataValue(), 0, 3);
mxs.mulPose(Vector3f.YP.rotationDegrees(ry)); double ox = tr[di][0], oy = tr[di][1], oz = tr[di][2];
mxs.scale(scaler, scaler, scaler); float ry = (float)tr[di][3];
Minecraft.getInstance().getItemRenderer().renderStatic(stack, net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.FIXED, i5, i6, mxs, buf); mxs.pushPose();
mxs.popPose(); mxs.translate(0.5+ox, 0.5+oy, 0.5+oz);
} catch(Throwable e) { mxs.mulPose(Vector3f.YP.rotationDegrees(ry));
if(--tesr_error_counter<=0) { mxs.scale(scaler, scaler, scaler);
ModEngineersDecor.logger().error("TER was disabled (because broken), exception was: " + e.getMessage()); Minecraft.getInstance().getItemRenderer().renderStatic(stack, ItemTransforms.TransformType.FIXED, i5, overlayTexture, mxs, buf, 0);
} mxs.popPose();
} } catch(Throwable e) {
} if(--tesr_error_counter<=0) {
} ModEngineersDecor.logger().error("TER was disabled (because broken), exception was: " + e.getMessage());
} }
}
}
}
}

View file

@ -1,178 +1,182 @@
/* /*
* @file TreeCutting.java * @file TreeCutting.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Simple tree cutting algorithm. * Simple tree cutting algorithm.
*/ */
package wile.engineersdecor.detail; package wile.engineersdecor.detail;
import net.minecraft.block.*; import com.google.common.collect.ImmutableList;
import net.minecraft.tags.BlockTags; import net.minecraft.core.BlockPos;
import net.minecraft.util.ResourceLocation; import net.minecraft.core.Vec3i;
import wile.engineersdecor.ModEngineersDecor; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.math.BlockPos; import net.minecraft.tags.BlockTags;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.Mth;
import net.minecraft.util.math.vector.Vector3i; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.block.Block;
import com.google.common.collect.ImmutableList; import net.minecraft.world.level.block.LeavesBlock;
import java.util.*; import net.minecraft.world.level.block.VineBlock;
import net.minecraft.world.level.block.state.BlockState;
import wile.engineersdecor.ModEngineersDecor;
public class TreeCutting
{ import java.util.*;
private static org.apache.logging.log4j.Logger LOGGER = ModEngineersDecor.logger();
public static boolean canChop(BlockState state) public class TreeCutting
{ return isLog(state); } {
private static final org.apache.logging.log4j.Logger LOGGER = ModEngineersDecor.logger();
// -------------------------------------------------------------------------------------------------------------------
public static boolean canChop(BlockState state)
private static final List<Vector3i> hoffsets = ImmutableList.of( { return isLog(state); }
new Vector3i( 1,0, 0), new Vector3i( 1,0, 1), new Vector3i( 0,0, 1),
new Vector3i(-1,0, 1), new Vector3i(-1,0, 0), new Vector3i(-1,0,-1), // -------------------------------------------------------------------------------------------------------------------
new Vector3i( 0,0,-1), new Vector3i( 1,0,-1)
); private static final List<Vec3i> hoffsets = ImmutableList.of(
new Vec3i( 1,0, 0), new Vec3i( 1,0, 1), new Vec3i( 0,0, 1),
private static boolean isLog(BlockState state) new Vec3i(-1,0, 1), new Vec3i(-1,0, 0), new Vec3i(-1,0,-1),
{ return (state.getBlock().is(BlockTags.LOGS)) || (state.getBlock().getTags().contains(new ResourceLocation("minecraft","logs"))); } new Vec3i( 0,0,-1), new Vec3i( 1,0,-1)
);
private static boolean isSameLog(BlockState a, BlockState b)
{ return (a.getBlock()==b.getBlock()); } private static boolean isLog(BlockState state)
{ return (state.is(BlockTags.LOGS)) || (state.getBlock().getTags().contains(new ResourceLocation("minecraft","logs"))); }
private static boolean isLeaves(BlockState state)
{ private static boolean isSameLog(BlockState a, BlockState b)
if(state.getBlock() instanceof LeavesBlock) return true; { return (a.getBlock()==b.getBlock()); }
if(state.getBlock().getTags().contains(new ResourceLocation("minecraft","leaves"))) return true;
return false; private static boolean isLeaves(BlockState state)
} {
if(state.getBlock() instanceof LeavesBlock) return true;
private static List<BlockPos> findBlocksAround(final World world, final BlockPos centerPos, final BlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left) if(state.getBlock().getTags().contains(new ResourceLocation("minecraft","leaves"))) return true;
{ return false;
ArrayList<BlockPos> to_decay = new ArrayList<BlockPos>(); }
for(int y=-1; y<=1; ++y) {
final BlockPos layer = centerPos.offset(0,y,0); private static List<BlockPos> findBlocksAround(final Level world, final BlockPos centerPos, final BlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left)
for(Vector3i v:hoffsets) { {
BlockPos pos = layer.offset(v); ArrayList<BlockPos> to_decay = new ArrayList<>();
if((!checked.contains(pos)) && (world.getBlockState(pos).getBlock()==leaf_type_state.getBlock())) { for(int y=-1; y<=1; ++y) {
checked.add(pos); final BlockPos layer = centerPos.offset(0,y,0);
to_decay.add(pos); for(Vec3i v:hoffsets) {
if(recursion_left > 0) { BlockPos pos = layer.offset(v);
to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, recursion_left-1)); if((!checked.contains(pos)) && (world.getBlockState(pos).getBlock()==leaf_type_state.getBlock())) {
} checked.add(pos);
} to_decay.add(pos);
} if(recursion_left > 0) {
} to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, recursion_left-1));
return to_decay; }
} }
}
private static void breakBlock(World world, BlockPos pos) }
{ return to_decay;
Block.dropResources(world.getBlockState(pos), world, pos); }
world.setBlock(pos, world.getFluidState(pos).createLegacyBlock(), 1|2|8);
} private static void breakBlock(Level world, BlockPos pos)
{
public static int chopTree(World world, BlockState broken_state, BlockPos startPos, int max_blocks_to_break, boolean without_target_block) Block.dropResources(world.getBlockState(pos), world, pos);
{ world.setBlock(pos, world.getFluidState(pos).createLegacyBlock(), 1|2|8);
if(world.isClientSide || !isLog(broken_state)) return 0; }
final long ymin = startPos.getY();
final long max_leaf_distance = 8; public static int chopTree(Level world, BlockState broken_state, BlockPos startPos, int max_blocks_to_break, boolean without_target_block)
Set<BlockPos> checked = new HashSet<BlockPos>(); {
ArrayList<BlockPos> to_break = new ArrayList<BlockPos>(); if(world.isClientSide || !isLog(broken_state)) return 0;
ArrayList<BlockPos> to_decay = new ArrayList<BlockPos>(); final long ymin = startPos.getY();
checked.add(startPos); final long max_leaf_distance = 8;
// Initial simple layer-up search of same logs. This forms the base corpus, and only leaves and Set<BlockPos> checked = new HashSet<>();
// leaf-enclosed logs attached to this corpus may be broken/decayed. ArrayList<BlockPos> to_break = new ArrayList<>();
{ ArrayList<BlockPos> to_decay = new ArrayList<>();
LinkedList<BlockPos> queue = new LinkedList<BlockPos>(); checked.add(startPos);
LinkedList<BlockPos> upqueue = new LinkedList<BlockPos>(); // Initial simple layer-up search of same logs. This forms the base corpus, and only leaves and
queue.add(startPos); // leaf-enclosed logs attached to this corpus may be broken/decayed.
int cutlevel = 0; {
int steps_left = 128; LinkedList<BlockPos> queue = new LinkedList<>();
while(!queue.isEmpty() && (--steps_left >= 0)) { LinkedList<BlockPos> upqueue = new LinkedList<>();
final BlockPos pos = queue.removeFirst(); queue.add(startPos);
// Vertical search int cutlevel = 0;
final BlockPos uppos = pos.above(); int steps_left = 128;
final BlockState upstate = world.getBlockState(uppos); while(!queue.isEmpty() && (--steps_left >= 0)) {
if(!checked.contains(uppos)) { final BlockPos pos = queue.removeFirst();
checked.add(uppos); // Vertical search
if(isSameLog(upstate, broken_state)) { final BlockPos uppos = pos.above();
// Up is log final BlockState upstate = world.getBlockState(uppos);
upqueue.add(uppos); if(!checked.contains(uppos)) {
to_break.add(uppos); checked.add(uppos);
steps_left = 128; if(isSameLog(upstate, broken_state)) {
} else { // Up is log
boolean isleaf = isLeaves(upstate); upqueue.add(uppos);
if(isleaf || world.isEmptyBlock(uppos) || (upstate.getBlock() instanceof VineBlock)) { to_break.add(uppos);
if(isleaf) to_decay.add(uppos); steps_left = 128;
// Up is air, check adjacent for diagonal up (e.g. Accacia) } else {
for(Vector3i v:hoffsets) { boolean isleaf = isLeaves(upstate);
final BlockPos p = uppos.offset(v); if(isleaf || world.isEmptyBlock(uppos) || (upstate.getBlock() instanceof VineBlock)) {
if(checked.contains(p)) continue; if(isleaf) to_decay.add(uppos);
checked.add(p); // Up is air, check adjacent for diagonal up (e.g. Accacia)
final BlockState st = world.getBlockState(p); for(Vec3i v:hoffsets) {
final Block bl = st.getBlock(); final BlockPos p = uppos.offset(v);
if(isSameLog(st, broken_state)) { if(checked.contains(p)) continue;
queue.add(p); checked.add(p);
to_break.add(p); final BlockState st = world.getBlockState(p);
} else if(isLeaves(st)) { final Block bl = st.getBlock();
to_decay.add(p); if(isSameLog(st, broken_state)) {
} queue.add(p);
} to_break.add(p);
} } else if(isLeaves(st)) {
} to_decay.add(p);
} }
// Lateral search }
for(Vector3i v:hoffsets) { }
final BlockPos p = pos.offset(v); }
if(checked.contains(p)) continue; }
checked.add(p); // Lateral search
if(p.distSqr(new BlockPos(startPos.getX(), p.getY(), startPos.getZ())) > (cutlevel > 2 ? 256 : 9)) continue; for(Vec3i v:hoffsets) {
final BlockState st = world.getBlockState(p); final BlockPos p = pos.offset(v);
final Block bl = st.getBlock(); if(checked.contains(p)) continue;
if(isSameLog(st, broken_state)) { checked.add(p);
queue.add(p); if(p.distSqr(new BlockPos(startPos.getX(), p.getY(), startPos.getZ())) > (cutlevel > 2 ? 256 : 9)) continue;
to_break.add(p); final BlockState st = world.getBlockState(p);
} else if(isLeaves(st)) { final Block bl = st.getBlock();
queue.add(p); if(isSameLog(st, broken_state)) {
to_decay.add(p); queue.add(p);
} to_break.add(p);
} } else if(isLeaves(st)) {
if(queue.isEmpty() && (!upqueue.isEmpty())) { queue.add(p);
queue = upqueue; to_decay.add(p);
upqueue = new LinkedList<BlockPos>(); }
++cutlevel; }
} if(queue.isEmpty() && (!upqueue.isEmpty())) {
} queue = upqueue;
} upqueue = new LinkedList<>();
{ ++cutlevel;
// Determine lose logs between the leafs }
for(BlockPos pos:to_decay) { }
int dist = 1; }
to_break.addAll(findBlocksAround(world, pos, broken_state, checked, dist)); {
} // Determine lose logs between the leafs
} for(BlockPos pos:to_decay) {
if(!to_decay.isEmpty()) { int dist = 1;
final BlockState leaf_type_state = world.getBlockState(to_decay.get(0)); to_break.addAll(findBlocksAround(world, pos, broken_state, checked, dist));
final ArrayList<BlockPos> leafs = to_decay; }
to_decay = new ArrayList<BlockPos>(); }
for(BlockPos pos:leafs) { if(!to_decay.isEmpty()) {
int dist = 3; final BlockState leaf_type_state = world.getBlockState(to_decay.get(0));
to_decay.add(pos); final ArrayList<BlockPos> leafs = to_decay;
to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, dist)); to_decay = new ArrayList<>();
} for(BlockPos pos:leafs) {
} int dist = 3;
if(without_target_block) { to_decay.add(pos);
checked.remove(startPos); to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, dist));
} else { }
to_break.add(startPos); }
} if(without_target_block) {
for(BlockPos pos:to_break) breakBlock(world, pos); checked.remove(startPos);
for(BlockPos pos:to_decay) breakBlock(world, pos); } else {
{ to_break.add(startPos);
// And now the bill. }
return MathHelper.clamp(((to_break.size()*6/5)+(to_decay.size()/10)-1), 1, 65535); for(BlockPos pos:to_break) breakBlock(world, pos);
} for(BlockPos pos:to_decay) breakBlock(world, pos);
} {
} // And now the bill.
return Mth.clamp(((to_break.size()*6/5)+(to_decay.size()/10)-1), 1, 65535);
}
}
}

View file

@ -1,93 +1,95 @@
/* /*
* @file JEIPlugin.java * @file JEIPlugin.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* JEI plugin (see https://github.com/mezz/JustEnoughItems/wiki/Creating-Plugins) * JEI plugin (see https://github.com/mezz/JustEnoughItems/wiki/Creating-Plugins)
*/ */
package wile.engineersdecor.eapi.jei; package wile.engineersdecor.eapi.jei;
//public class JEIPlugin {} public class JEIPlugin {}
import mezz.jei.api.registration.IRecipeCatalystRegistration; /*
import wile.engineersdecor.ModEngineersDecor; import mezz.jei.api.registration.IRecipeCatalystRegistration;
import wile.engineersdecor.ModConfig; import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.ModContent; import wile.engineersdecor.ModConfig;
import wile.engineersdecor.blocks.EdCraftingTable; import wile.engineersdecor.ModContent;
import mezz.jei.api.constants.VanillaRecipeCategoryUid; import wile.engineersdecor.blocks.EdCraftingTable;
import mezz.jei.api.registration.IRecipeTransferRegistration; import mezz.jei.api.constants.VanillaRecipeCategoryUid;
import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.runtime.IJeiRuntime; import mezz.jei.api.constants.VanillaTypes;
import net.minecraft.block.Block; import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.item.BlockItem; import net.minecraft.block.Block;
import net.minecraft.item.Item; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack; import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation; import net.minecraft.item.ItemStack;
import wile.engineersdecor.blocks.EdCraftingTable.CraftingTableTileEntity; import net.minecraft.util.ResourceLocation;
import wile.engineersdecor.blocks.EdCraftingTable.CraftingTableTileEntity;
import java.util.HashSet;
import java.util.List; import java.util.HashSet;
import java.util.stream.Collectors; import java.util.List;
import java.util.stream.Collectors;
@mezz.jei.api.JeiPlugin
public class JEIPlugin implements mezz.jei.api.IModPlugin @mezz.jei.api.JeiPlugin
{ public class JEIPlugin implements mezz.jei.api.IModPlugin
@Override {
public ResourceLocation getPluginUid() @Override
{ return new ResourceLocation(ModEngineersDecor.MODID, "jei_plugin_uid"); } public ResourceLocation getPluginUid()
{ return new ResourceLocation(Auxiliaries.modid(), "jei_plugin_uid"); }
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) @Override
{ public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration)
if(!ModConfig.isOptedOut(ModContent.CRAFTING_TABLE)) { {
try { if(!ModConfig.isOptedOut(ModContent.CRAFTING_TABLE)) {
registration.addRecipeTransferHandler( try {
EdCraftingTable.CraftingTableUiContainer.class, registration.addRecipeTransferHandler(
VanillaRecipeCategoryUid.CRAFTING, EdCraftingTable.CraftingTableUiContainer.class,
1, 9, 10, 36+CraftingTableTileEntity.NUM_OF_STORAGE_SLOTS VanillaRecipeCategoryUid.CRAFTING,
); 1, 9, 10, 36+CraftingTableTileEntity.NUM_OF_STORAGE_SLOTS
} catch(Throwable e) { );
ModEngineersDecor.logger().warn("Exception in JEI crafting table handler registration: '" + e.getMessage() + "'."); } catch(Throwable e) {
} ModEngineersDecor.logger().warn("Exception in JEI crafting table handler registration: '" + e.getMessage() + "'.");
} }
} }
}
@Override
public void onRuntimeAvailable(IJeiRuntime jeiRuntime) @Override
{ public void onRuntimeAvailable(IJeiRuntime jeiRuntime)
HashSet<Item> blacklisted = new HashSet<>(); {
for(Block e: ModContent.getRegisteredBlocks()) { HashSet<Item> blacklisted = new HashSet<>();
if(ModConfig.isOptedOut(e) && (e.asItem().getRegistryName().getPath()).equals((e.getRegistryName().getPath()))) { for(Block e: ModContent.getRegisteredBlocks()) {
blacklisted.add(e.asItem()); if(ModConfig.isOptedOut(e) && (e.asItem().getRegistryName().getPath()).equals((e.getRegistryName().getPath()))) {
} blacklisted.add(e.asItem());
} }
for(Item e: ModContent.getRegisteredItems()) { }
if(ModConfig.isOptedOut(e) && (!(e instanceof BlockItem))) { for(Item e: ModContent.getRegisteredItems()) {
blacklisted.add(e); if(ModConfig.isOptedOut(e) && (!(e instanceof BlockItem))) {
} blacklisted.add(e);
} }
if(!blacklisted.isEmpty()) { }
List<ItemStack> blacklist = blacklisted.stream().map(ItemStack::new).collect(Collectors.toList()); if(!blacklisted.isEmpty()) {
try { List<ItemStack> blacklist = blacklisted.stream().map(ItemStack::new).collect(Collectors.toList());
jeiRuntime.getIngredientManager().removeIngredientsAtRuntime(VanillaTypes.ITEM, blacklist); try {
} catch(Exception e) { jeiRuntime.getIngredientManager().removeIngredientsAtRuntime(VanillaTypes.ITEM, blacklist);
ModEngineersDecor.logger().warn("Exception in JEI opt-out processing: '" + e.getMessage() + "', skipping further JEI optout processing."); } catch(Exception e) {
} ModEngineersDecor.logger().warn("Exception in JEI opt-out processing: '" + e.getMessage() + "', skipping further JEI optout processing.");
} }
} }
}
@Override
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) @Override
{ public void registerRecipeCatalysts(IRecipeCatalystRegistration registration)
if(!ModConfig.isOptedOut(ModContent.CRAFTING_TABLE)) { {
registration.addRecipeCatalyst(new ItemStack(ModContent.CRAFTING_TABLE), VanillaRecipeCategoryUid.CRAFTING); if(!ModConfig.isOptedOut(ModContent.CRAFTING_TABLE)) {
} registration.addRecipeCatalyst(new ItemStack(ModContent.CRAFTING_TABLE), VanillaRecipeCategoryUid.CRAFTING);
if(!ModConfig.isOptedOut(ModContent.SMALL_LAB_FURNACE)) { }
registration.addRecipeCatalyst(new ItemStack(ModContent.SMALL_LAB_FURNACE), VanillaRecipeCategoryUid.FURNACE); if(!ModConfig.isOptedOut(ModContent.SMALL_LAB_FURNACE)) {
} registration.addRecipeCatalyst(new ItemStack(ModContent.SMALL_LAB_FURNACE), VanillaRecipeCategoryUid.FURNACE);
if(!ModConfig.isOptedOut(ModContent.SMALL_ELECTRICAL_FURNACE)) { }
registration.addRecipeCatalyst(new ItemStack(ModContent.SMALL_ELECTRICAL_FURNACE), VanillaRecipeCategoryUid.FURNACE); if(!ModConfig.isOptedOut(ModContent.SMALL_ELECTRICAL_FURNACE)) {
} registration.addRecipeCatalyst(new ItemStack(ModContent.SMALL_ELECTRICAL_FURNACE), VanillaRecipeCategoryUid.FURNACE);
} }
} }
}
*/

View file

@ -1,46 +1,46 @@
/* /*
* @file EdItem.java * @file EdItem.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Basic item functionality for mod items. * Basic item functionality for mod items.
*/ */
package wile.engineersdecor.items; package wile.engineersdecor.items;
import wile.engineersdecor.ModEngineersDecor; import net.minecraft.network.chat.Component;
import wile.engineersdecor.ModConfig; import net.minecraft.world.item.CreativeModeTab;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.item.Item;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.item.ItemStack;
import net.minecraft.item.Item; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.item.ItemGroup; import net.minecraft.world.level.Level;
import net.minecraft.item.ItemStack; import net.minecraftforge.api.distmarker.Dist;
import net.minecraft.util.text.ITextComponent; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraft.world.World; import wile.engineersdecor.ModEngineersDecor;
import net.minecraftforge.api.distmarker.Dist; import wile.engineersdecor.ModConfig;
import net.minecraftforge.api.distmarker.OnlyIn; import wile.engineersdecor.libmc.detail.Auxiliaries;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public class EdItem extends Item public class EdItem extends Item
{ {
public static final Collection<ItemGroup> ENABLED_TABS = Collections.singletonList(ModEngineersDecor.ITEMGROUP); public static final Collection<CreativeModeTab> ENABLED_TABS = Collections.singletonList(ModEngineersDecor.ITEMGROUP);
public static final Collection<ItemGroup> DISABLED_TABS = new ArrayList<ItemGroup>(); public static final Collection<CreativeModeTab> DISABLED_TABS = new ArrayList<CreativeModeTab>();
public EdItem(Item.Properties properties) public EdItem(Item.Properties properties)
{ super(properties.tab(ModEngineersDecor.ITEMGROUP)); } { super(properties.tab(ModEngineersDecor.ITEMGROUP)); }
@Override @Override
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable World world, List<ITextComponent> tooltip, ITooltipFlag flag) public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
@Override @Override
public Collection<ItemGroup> getCreativeTabs() public Collection<CreativeModeTab> getCreativeTabs()
{ return ModConfig.isOptedOut(this) ? (DISABLED_TABS) : (ENABLED_TABS); } { return ModConfig.isOptedOut(this) ? (DISABLED_TABS) : (ENABLED_TABS); }
} }

View file

@ -1,214 +1,218 @@
/* /*
* @file BlockDecorHalfSlab.java * @file BlockDecorHalfSlab.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Half slab ("slab slices") characteristics class. Actually * Half slab ("slab slices") characteristics class. Actually
* it's now a quater slab, but who cares. * it's now a quater slab, but who cares.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import net.minecraft.world.World; import net.minecraft.core.Direction;
import net.minecraft.world.IWorld; import net.minecraft.network.chat.Component;
import net.minecraft.world.IBlockReader; import net.minecraft.sounds.SoundSource;
import net.minecraft.block.*; import net.minecraft.world.entity.EntityType;
import net.minecraft.block.BlockState; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.EntityType; import net.minecraft.world.item.ItemStack;
import net.minecraft.state.StateContainer; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.fluid.Fluid; import net.minecraft.world.level.BlockGetter;
import net.minecraft.fluid.FluidState; import net.minecraft.world.level.Level;
import net.minecraft.item.*; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.block.Block;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.Mirror;
import net.minecraft.util.*; import net.minecraft.world.level.block.Rotation;
import net.minecraft.util.math.*; import net.minecraft.world.level.block.SoundType;
import net.minecraft.util.math.vector.*; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.level.material.FluidState;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.CollisionContext;
import java.util.ArrayList; import net.minecraft.world.phys.shapes.Shapes;
import java.util.Collections; import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.List; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.blocks.StandardBlocks.IStandardBlock.RenderTypeHint;
import javax.annotation.Nullable;
public class SlabSliceBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock import java.util.ArrayList;
{ import java.util.Collections;
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14); import java.util.List;
protected static final VoxelShape AABBs[] = { public class SlabSliceBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 2./16, 1)), {
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 4./16, 1)), public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14);
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 6./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 8./16, 1)), protected static final VoxelShape[] AABBs = {
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 10./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 2./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 12./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 4./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 14./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 6./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 8./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 2./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 10./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 4./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 12./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 6./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 14./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 8./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 0./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 10./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 2./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 12./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 4./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 14./16, 0, 1, 16./16, 1)), Shapes.create(new AABB(0, 6./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0,0,0,1,1,1)) // <- with 4bit fill Shapes.create(new AABB(0, 8./16, 0, 1, 16./16, 1)),
}; Shapes.create(new AABB(0, 10./16, 0, 1, 16./16, 1)),
Shapes.create(new AABB(0, 12./16, 0, 1, 16./16, 1)),
protected static final int num_slabs_contained_in_parts_[] = { 1,2,3,4,5,6,7,8,7,6,5,4,3,2,1 ,0x1 }; // <- with 4bit fill Shapes.create(new AABB(0, 14./16, 0, 1, 16./16, 1)),
private static boolean with_pickup = false; Shapes.create(new AABB(0,0,0,1,1,1)) // <- with 4bit fill
};
public static void on_config(boolean direct_slab_pickup)
{ with_pickup = direct_slab_pickup; } protected static final int[] num_slabs_contained_in_parts_ = { 1,2,3,4,5,6,7,8,7,6,5,4,3,2,1 ,0x1 }; // <- with 4bit fill
private static boolean with_pickup = false;
public SlabSliceBlock(long config, AbstractBlock.Properties builder)
{ super(config, builder); } public static void on_config(boolean direct_slab_pickup)
{ with_pickup = direct_slab_pickup; }
protected boolean is_cube(BlockState state)
{ return state.getValue(PARTS) == 0x07; } public SlabSliceBlock(long config, BlockBehaviour.Properties builder)
{ super(config, builder); }
@Override
@OnlyIn(Dist.CLIENT) protected boolean is_cube(BlockState state)
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) { return state.getValue(PARTS) == 0x07; }
{
if(!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return; @Override
if(with_pickup) Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", tooltip); @OnlyIn(Dist.CLIENT)
} public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
{
@Override if(!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return;
public RenderTypeHint getRenderTypeHint() if(with_pickup) Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", tooltip);
{ return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); } }
@Override @Override
public boolean isPossibleToRespawnInThis() public RenderTypeHint getRenderTypeHint()
{ return false; } { return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); }
@Override @Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) public boolean isPossibleToRespawnInThis()
{ return false; } { return false; }
@Override @Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext) public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
{ return AABBs[state.getValue(PARTS) & 0xf]; } { return false; }
@Override @Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); } { return AABBs[state.getValue(PARTS) & 0xf]; }
@Override @Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
{ super.createBlockStateDefinition(builder); builder.add(PARTS); } { return getShape(state, world, pos, selectionContext); }
@Override @Override
@Nullable protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
public BlockState getStateForPlacement(BlockItemUseContext context) { super.createBlockStateDefinition(builder); builder.add(PARTS); }
{
final BlockPos pos = context.getClickedPos(); @Override
BlockState state = context.getLevel().getBlockState(pos); @Nullable
if(state.getBlock() == this) { public BlockState getStateForPlacement(BlockPlaceContext context)
int parts = state.getValue(PARTS); {
if(parts == 7) return null; // -> is already a full block. final BlockPos pos = context.getClickedPos();
parts += (parts < 7) ? 1 : -1; BlockState state = context.getLevel().getBlockState(pos);
if(parts==7) state = state.setValue(WATERLOGGED, false); if(state.getBlock() == this) {
return state.setValue(PARTS, parts); int parts = state.getValue(PARTS);
} else { if(parts == 7) return null; // -> is already a full block.
final Direction face = context.getClickedFace(); parts += (parts < 7) ? 1 : -1;
final BlockState placement_state = super.getStateForPlacement(context); // fluid state if(parts==7) state = state.setValue(WATERLOGGED, false);
if(face == Direction.UP) return placement_state.setValue(PARTS, 0); return state.setValue(PARTS, parts);
if(face == Direction.DOWN) return placement_state.setValue(PARTS, 14); } else {
if(!face.getAxis().isHorizontal()) return placement_state; final Direction face = context.getClickedFace();
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5); final BlockState placement_state = super.getStateForPlacement(context); // fluid state
return placement_state.setValue(PARTS, isupper ? 14 : 0); if(face == Direction.UP) return placement_state.setValue(PARTS, 0);
} if(face == Direction.DOWN) return placement_state.setValue(PARTS, 14);
} if(!face.getAxis().isHorizontal()) return placement_state;
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
@Override return placement_state.setValue(PARTS, isupper ? 14 : 0);
@SuppressWarnings("deprecation") }
public boolean canBeReplaced(BlockState state, BlockItemUseContext context) }
{
if(context.getItemInHand().getItem() != this.asItem()) return false; @Override
if(!context.replacingClickedOnBlock()) return true; @SuppressWarnings("deprecation")
final Direction face = context.getClickedFace(); public boolean canBeReplaced(BlockState state, BlockPlaceContext context)
final int parts = state.getValue(PARTS); {
if(parts == 7) return false; if(context.getItemInHand().getItem() != this.asItem()) return false;
if((face == Direction.UP) && (parts < 7)) return true; if(!context.replacingClickedOnBlock()) return true;
if((face == Direction.DOWN) && (parts > 7)) return true; final Direction face = context.getClickedFace();
if(!face.getAxis().isHorizontal()) return false; final int parts = state.getValue(PARTS);
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5); if(parts == 7) return false;
return isupper ? (parts==0) : (parts==1); if((face == Direction.UP) && (parts < 7)) return true;
} if((face == Direction.DOWN) && (parts > 7)) return true;
if(!face.getAxis().isHorizontal()) return false;
@Override final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
@SuppressWarnings("deprecation") return isupper ? (parts==0) : (parts==1);
public BlockState rotate(BlockState state, Rotation rot) }
{ return state; }
@Override
@Override @SuppressWarnings("deprecation")
@SuppressWarnings("deprecation") public BlockState rotate(BlockState state, Rotation rot)
public BlockState mirror(BlockState state, Mirror mirrorIn) { return state; }
{ return state; }
@Override
@Override @SuppressWarnings("deprecation")
public boolean hasDynamicDropList() public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return true; } { return state; }
@Override @Override
public List<ItemStack> dropList(BlockState state, World world, TileEntity te, boolean explosion) public boolean hasDynamicDropList()
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(PARTS) & 0xf]))); } { return true; }
@Override @Override
@SuppressWarnings("deprecation") public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
public void attack(BlockState state, World world, BlockPos pos, PlayerEntity player) { return new ArrayList<>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(PARTS) & 0xf]))); }
{
if((world.isClientSide) || (!with_pickup)) return; @Override
final ItemStack stack = player.getMainHandItem(); @SuppressWarnings("deprecation")
if(stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return; public void attack(BlockState state, Level world, BlockPos pos, Player player)
if(stack.getCount() >= stack.getMaxStackSize()) return; {
Vector3d lv = player.getLookAngle(); if((world.isClientSide) || (!with_pickup)) return;
Direction facing = Direction.getNearest((float)lv.x, (float)lv.y, (float)lv.z); final ItemStack stack = player.getMainHandItem();
if((facing != Direction.UP) && (facing != Direction.DOWN)) return; if(stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return;
if(state.getBlock() != this) return; if(stack.getCount() >= stack.getMaxStackSize()) return;
int parts = state.getValue(PARTS); Vec3 lv = player.getLookAngle();
if((facing == Direction.DOWN) && (parts <= 7)) { Direction facing = Direction.getNearest((float)lv.x, (float)lv.y, (float)lv.z);
if(parts > 0) { if((facing != Direction.UP) && (facing != Direction.DOWN)) return;
world.setBlock(pos, state.setValue(PARTS, parts-1), 3); if(state.getBlock() != this) return;
} else { int parts = state.getValue(PARTS);
world.removeBlock(pos, false); if((facing == Direction.DOWN) && (parts <= 7)) {
} if(parts > 0) {
} else if((facing == Direction.UP) && (parts >= 7)) { world.setBlock(pos, state.setValue(PARTS, parts-1), 3);
if(parts < 14) { } else {
world.setBlock(pos, state.setValue(PARTS, parts + 1), 3); world.removeBlock(pos, false);
} else { }
world.removeBlock(pos, false); } else if((facing == Direction.UP) && (parts >= 7)) {
} if(parts < 14) {
} else { world.setBlock(pos, state.setValue(PARTS, parts + 1), 3);
return; } else {
} world.removeBlock(pos, false);
if(!player.isCreative()) { }
stack.grow(1); } else {
if(player.inventory != null) player.inventory.setChanged(); // @todo: check if inventory can actually be null return;
} }
SoundType st = this.getSoundType(state, world, pos, null); if(!player.isCreative()) {
world.playSound(player, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch()); stack.grow(1);
} if(player.getInventory() != null) player.getInventory().setChanged();
}
@Override SoundType st = this.getSoundType(state, world, pos, null);
public boolean placeLiquid(IWorld world, BlockPos pos, BlockState state, FluidState fluidState) world.playSound(player, pos, st.getPlaceSound(), SoundSource.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
{ return (state.getValue(PARTS)==14) ? false : super.placeLiquid(world, pos, state, fluidState); } }
@Override @Override
public boolean canPlaceLiquid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid) public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState)
{ return (state.getValue(PARTS)==14) ? false : super.canPlaceLiquid(world, pos, state, fluid); } { return (state.getValue(PARTS) != 14) && (super.placeLiquid(world, pos, state, fluidState)); }
} @Override
public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.getValue(PARTS) != 14) && (super.canPlaceLiquid(world, pos, state, fluid)); }
}

View file

@ -1,162 +1,168 @@
/* /*
* @file StandardDoorBlock.java * @file StandardDoorBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Door blocks, almost entirely based on vanilla. * Door blocks, almost entirely based on vanilla.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import net.minecraft.entity.EntityType; import net.minecraft.core.Direction;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.chat.Component;
import net.minecraft.state.properties.DoorHingeSide; import net.minecraft.sounds.SoundEvent;
import net.minecraft.state.properties.DoubleBlockHalf; import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.*; import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Direction.Axis; import net.minecraft.world.InteractionHand;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.InteractionResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.entity.Entity;
import net.minecraft.block.*; import net.minecraft.world.entity.EntityType;
import net.minecraft.block.BlockState; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.entity.player.Player;
import net.minecraft.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.Level;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.Block;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.World; import net.minecraft.world.level.block.state.properties.DoorHingeSide;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.phys.AABB;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.BooleanOp;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.CollisionContext;
import java.util.List; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
public class StandardDoorBlock extends DoorBlock implements StandardBlocks.IStandardBlock import net.minecraftforge.api.distmarker.OnlyIn;
{ import wile.engineersdecor.libmc.detail.Auxiliaries;
private final long config_;
protected final VoxelShape shapes_[][][][]; import javax.annotation.Nullable;
protected final SoundEvent open_sound_; import java.util.List;
protected final SoundEvent close_sound_;
public StandardDoorBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB[] open_aabbs_top, AxisAlignedBB[] open_aabbs_bottom, AxisAlignedBB[] closed_aabbs_top, AxisAlignedBB[] closed_aabbs_bottom, SoundEvent open_sound, SoundEvent close_sound) public class StandardDoorBlock extends DoorBlock implements StandardBlocks.IStandardBlock
{ {
super(properties); private final long config_;
VoxelShape shapes[][][][] = new VoxelShape[Direction.values().length][2][2][2]; protected final VoxelShape[][][][] shapes_;
for(Direction facing: Direction.values()) { protected final SoundEvent open_sound_;
for(boolean open: new boolean[]{false,true}) { protected final SoundEvent close_sound_;
for(DoubleBlockHalf half: new DoubleBlockHalf[]{DoubleBlockHalf.UPPER,DoubleBlockHalf.LOWER}) {
for(boolean hinge_right: new boolean[]{false,true}) { public StandardDoorBlock(long config, BlockBehaviour.Properties properties, AABB[] open_aabbs_top, AABB[] open_aabbs_bottom, AABB[] closed_aabbs_top, AABB[] closed_aabbs_bottom, SoundEvent open_sound, SoundEvent close_sound)
VoxelShape shape = VoxelShapes.empty(); {
if(facing.getAxis() == Axis.Y) { super(properties);
shape = VoxelShapes.block(); VoxelShape[][][][] shapes = new VoxelShape[Direction.values().length][2][2][2];
} else { for(Direction facing: Direction.values()) {
final AxisAlignedBB[] aabbs = (open)?((half==DoubleBlockHalf.UPPER) ? open_aabbs_top : open_aabbs_bottom) : ((half==DoubleBlockHalf.UPPER) ? closed_aabbs_top : closed_aabbs_bottom); for(boolean open: new boolean[]{false,true}) {
for(AxisAlignedBB e:aabbs) { for(DoubleBlockHalf half: new DoubleBlockHalf[]{DoubleBlockHalf.UPPER,DoubleBlockHalf.LOWER}) {
AxisAlignedBB aabb = Auxiliaries.getRotatedAABB(e, facing, true); for(boolean hinge_right: new boolean[]{false,true}) {
if(!hinge_right) aabb = Auxiliaries.getMirroredAABB(aabb, facing.getClockWise().getAxis()); VoxelShape shape = Shapes.empty();
shape = VoxelShapes.join(shape, VoxelShapes.create(aabb), IBooleanFunction.OR); if(facing.getAxis() == Direction.Axis.Y) {
} shape = Shapes.block();
} } else {
shapes[facing.ordinal()][open?1:0][hinge_right?1:0][half==DoubleBlockHalf.UPPER?0:1] = shape; final AABB[] aabbs = (open)?((half==DoubleBlockHalf.UPPER) ? open_aabbs_top : open_aabbs_bottom) : ((half==DoubleBlockHalf.UPPER) ? closed_aabbs_top : closed_aabbs_bottom);
} for(AABB e:aabbs) {
} AABB aabb = Auxiliaries.getRotatedAABB(e, facing, true);
} if(!hinge_right) aabb = Auxiliaries.getMirroredAABB(aabb, facing.getClockWise().getAxis());
} shape = Shapes.join(shape, Shapes.create(aabb), BooleanOp.OR);
config_ = config; }
shapes_ = shapes; }
open_sound_ = open_sound; shapes[facing.ordinal()][open?1:0][hinge_right?1:0][half==DoubleBlockHalf.UPPER?0:1] = shape;
close_sound_ = close_sound; }
} }
}
public StandardDoorBlock(long config, AbstractBlock.Properties properties, AxisAlignedBB open_aabb, AxisAlignedBB closed_aabb, SoundEvent open_sound, SoundEvent close_sound) }
{ this(config, properties, new AxisAlignedBB[]{open_aabb}, new AxisAlignedBB[]{open_aabb}, new AxisAlignedBB[]{closed_aabb}, new AxisAlignedBB[]{closed_aabb}, open_sound, close_sound); } config_ = config;
shapes_ = shapes;
public StandardDoorBlock(long config, AbstractBlock.Properties properties, SoundEvent open_sound, SoundEvent close_sound) open_sound_ = open_sound;
{ close_sound_ = close_sound;
this( }
config, properties,
Auxiliaries.getPixeledAABB(13,0, 0, 16,16,16), public StandardDoorBlock(long config, BlockBehaviour.Properties properties, AABB open_aabb, AABB closed_aabb, SoundEvent open_sound, SoundEvent close_sound)
Auxiliaries.getPixeledAABB( 0,0,13, 16,16,16), { this(config, properties, new AABB[]{open_aabb}, new AABB[]{open_aabb}, new AABB[]{closed_aabb}, new AABB[]{closed_aabb}, open_sound, close_sound); }
open_sound,
close_sound public StandardDoorBlock(long config, BlockBehaviour.Properties properties, SoundEvent open_sound, SoundEvent close_sound)
); {
} this(
config, properties,
public StandardDoorBlock(long config, AbstractBlock.Properties properties) Auxiliaries.getPixeledAABB(13,0, 0, 16,16,16),
{ Auxiliaries.getPixeledAABB( 0,0,13, 16,16,16),
this( open_sound,
config, properties, close_sound
Auxiliaries.getPixeledAABB(13,0, 0, 16,16,16), );
Auxiliaries.getPixeledAABB( 0,0,13, 16,16,16), }
SoundEvents.WOODEN_DOOR_OPEN,
SoundEvents.WOODEN_DOOR_CLOSE public StandardDoorBlock(long config, BlockBehaviour.Properties properties)
); {
} this(
config, properties,
@Override Auxiliaries.getPixeledAABB(13,0, 0, 16,16,16),
public long config() Auxiliaries.getPixeledAABB( 0,0,13, 16,16,16),
{ return config_; } SoundEvents.WOODEN_DOOR_OPEN,
SoundEvents.WOODEN_DOOR_CLOSE
protected void sound(IBlockReader world, BlockPos pos, boolean open) );
{ if(world instanceof World) ((World)world).playSound(null, pos, open ? open_sound_ : close_sound_, SoundCategory.BLOCKS, 0.7f, 1f); } }
protected void actuate_adjacent_wing(BlockState state, IBlockReader world_ro, BlockPos pos, boolean open) @Override
{ public long config()
if(!(world_ro instanceof World)) return; { return config_; }
final World world = (World)world_ro;
final BlockPos adjecent_pos = pos.relative( (state.getValue(HINGE)==DoorHingeSide.LEFT) ? (state.getValue(FACING).getClockWise()) : (state.getValue(FACING).getCounterClockWise())); protected void sound(BlockGetter world, BlockPos pos, boolean open)
if(!world.isLoaded(adjecent_pos)) return; { if(world instanceof Level) ((Level)world).playSound(null, pos, open ? open_sound_ : close_sound_, SoundSource.BLOCKS, 0.7f, 1f); }
BlockState adjacent_state = world.getBlockState(adjecent_pos);
if(adjacent_state.getBlock()!=this) return; protected void actuate_adjacent_wing(BlockState state, BlockGetter world_ro, BlockPos pos, boolean open)
if(adjacent_state.getValue(OPEN)==open) return; {
world.setBlock(adjecent_pos, adjacent_state.setValue(OPEN, open), 2|10); if(!(world_ro instanceof final Level world)) return;
} final BlockPos adjecent_pos = pos.relative( (state.getValue(HINGE)==DoorHingeSide.LEFT) ? (state.getValue(FACING).getClockWise()) : (state.getValue(FACING).getCounterClockWise()));
if(!world.isLoaded(adjecent_pos)) return;
@Override BlockState adjacent_state = world.getBlockState(adjecent_pos);
@OnlyIn(Dist.CLIENT) if(adjacent_state.getBlock()!=this) return;
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) if(adjacent_state.getValue(OPEN)==open) return;
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } world.setBlock(adjecent_pos, adjacent_state.setValue(OPEN, open), 2|10);
}
@Override
public boolean isPossibleToRespawnInThis() @Override
{ return false; } @OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
@Override { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; } @Override
public boolean isPossibleToRespawnInThis()
@Override { return false; }
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext context)
{ return shapes_[state.getValue(FACING).ordinal()][state.getValue(OPEN)?1:0][state.getValue(HINGE)==DoorHingeSide.RIGHT?1:0][state.getValue(HALF)==DoubleBlockHalf.UPPER?0:1]; } @Override
public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
@Override { return false; }
public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{ setOpen(world, state, pos, !state.getValue(OPEN)); return ActionResultType.sidedSuccess(world.isClientSide()); } @Override
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context)
@Override { return shapes_[state.getValue(FACING).ordinal()][state.getValue(OPEN)?1:0][state.getValue(HINGE)==DoorHingeSide.RIGHT?1:0][state.getValue(HALF)==DoubleBlockHalf.UPPER?0:1]; }
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
{ @Override
boolean powered = world.hasNeighborSignal(pos) || world.hasNeighborSignal(pos.relative(state.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN)); public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit)
if((block == this) || (powered == state.getValue(POWERED))) return; { setOpen(player, world, state, pos, !state.getValue(OPEN)); return InteractionResult.sidedSuccess(world.isClientSide()); }
world.setBlock(pos, state.setValue(POWERED, powered).setValue(OPEN, powered), 2);
actuate_adjacent_wing(state, world, pos, powered); @Override
if(powered != state.getValue(OPEN)) sound(world, pos, powered); public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
} {
boolean powered = world.hasNeighborSignal(pos) || world.hasNeighborSignal(pos.relative(state.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
@Override if((block == this) || (powered == state.getValue(POWERED))) return;
public void setOpen(World world, BlockState state, BlockPos pos, boolean open) world.setBlock(pos, state.setValue(POWERED, powered).setValue(OPEN, powered), 2);
{ actuate_adjacent_wing(state, world, pos, powered);
if(!state.is(this) || (state.getValue(OPEN) == open)) return; if(powered != state.getValue(OPEN)) sound(world, pos, powered);
state = state.setValue(OPEN, open); }
world.setBlock(pos, state, 2|8);
sound(world, pos, open); @Override
actuate_adjacent_wing(state, world, pos, open); public void setOpen(@Nullable Entity entity, Level world, BlockState state, BlockPos pos, boolean open)
} {
if(!state.is(this) || (state.getValue(OPEN) == open)) return;
} state = state.setValue(OPEN, open);
world.setBlock(pos, state, 2|8);
sound(world, pos, open);
actuate_adjacent_wing(state, world, pos, open);
}
}

View file

@ -0,0 +1,68 @@
/*
* @file StandardEntityBlocks.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Common functionality class for blocks with block entities.
*/
package wile.engineersdecor.libmc.blocks;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEventListener;
import net.minecraftforge.common.util.FakePlayer;
import javax.annotation.Nullable;
public class StandardEntityBlocks
{
public interface IStandardEntityBlock<ET extends StandardBlockEntity> extends EntityBlock
{
@Nullable
BlockEntityType<ET> getBlockEntityType();
@Override
@Nullable
default BlockEntity newBlockEntity(BlockPos pos, BlockState state)
{ return (getBlockEntityType()==null) ? null : getBlockEntityType().create(pos, state); }
@Override
@Nullable
default <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level world, BlockState state, BlockEntityType<T> te_type)
{ return (world.isClientSide) ? (null) : ((Level w, BlockPos p, BlockState s, T te) -> ((StandardBlockEntity)te).tick()); } // To be evaluated if
@Override
@Nullable
default <T extends BlockEntity> GameEventListener getListener(Level world, T te)
{ return null; }
default InteractionResult useOpenGui(BlockState state, Level world, BlockPos pos, Player player)
{
if(world.isClientSide()) return InteractionResult.SUCCESS;
final BlockEntity te = world.getBlockEntity(pos);
if(!(te instanceof MenuProvider) || ((player instanceof FakePlayer))) return InteractionResult.FAIL;
player.openMenu((MenuProvider)te);
return InteractionResult.CONSUME;
}
}
public static abstract class StandardBlockEntity extends BlockEntity
{
public StandardBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state)
{ super(type, pos, state); }
public void tick()
{}
}
}

View file

@ -1,194 +1,200 @@
/* /*
* @file StandardFenceBlock.java * @file StandardFenceBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Wall blocks. * Wall blocks.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import com.google.common.collect.ImmutableMap;
import net.minecraft.pathfinding.PathType; import net.minecraft.core.BlockPos;
import net.minecraft.state.BooleanProperty; import net.minecraft.core.Direction;
import net.minecraft.state.EnumProperty; import net.minecraft.network.chat.Component;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.world.entity.EntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.world.*; import net.minecraft.world.item.ItemStack;
import net.minecraft.fluid.FluidState; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.entity.EntityType; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.BlockGetter;
import net.minecraft.state.StateContainer; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.block.*; import net.minecraft.world.level.LevelReader;
import net.minecraft.block.material.PushReaction; import net.minecraft.world.level.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.WallBlock;
import net.minecraft.fluid.Fluids; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.Direction; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.properties.WallSide;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.material.FluidState;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.level.material.Fluids;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.level.material.PushReaction;
import com.google.common.collect.ImmutableMap; import net.minecraft.world.phys.shapes.CollisionContext;
import com.google.common.collect.ImmutableMap.Builder; import net.minecraft.world.phys.shapes.Shapes;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.*; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries;
public class StandardFenceBlock extends WallBlock implements StandardBlocks.IStandardBlock
{ import javax.annotation.Nullable;
public static final BooleanProperty UP = BlockStateProperties.UP; import java.util.List;
public static final EnumProperty<WallHeight> WALL_EAST = BlockStateProperties.EAST_WALL; import java.util.Map;
public static final EnumProperty<WallHeight> WALL_NORTH = BlockStateProperties.NORTH_WALL;
public static final EnumProperty<WallHeight> WALL_SOUTH = BlockStateProperties.SOUTH_WALL;
public static final EnumProperty<WallHeight> WALL_WEST = BlockStateProperties.WEST_WALL; public class StandardFenceBlock extends WallBlock implements StandardBlocks.IStandardBlock
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; {
private final Map<BlockState, VoxelShape> shape_voxels; public static final BooleanProperty UP = BlockStateProperties.UP;
private final Map<BlockState, VoxelShape> collision_shape_voxels; public static final EnumProperty<WallSide> WALL_EAST = BlockStateProperties.EAST_WALL;
private final long config; public static final EnumProperty<WallSide> WALL_NORTH = BlockStateProperties.NORTH_WALL;
public static final EnumProperty<WallSide> WALL_SOUTH = BlockStateProperties.SOUTH_WALL;
public StandardFenceBlock(long config, AbstractBlock.Properties properties) public static final EnumProperty<WallSide> WALL_WEST = BlockStateProperties.WEST_WALL;
{ this(config, properties, 1.5,16, 1.5, 0, 14, 16); } public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private final Map<BlockState, VoxelShape> shape_voxels;
public StandardFenceBlock(long config, AbstractBlock.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) private final Map<BlockState, VoxelShape> collision_shape_voxels;
{ private final long config;
super(properties);
shape_voxels = buildShapes(pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y); public StandardFenceBlock(long config, BlockBehaviour.Properties properties)
collision_shape_voxels = buildShapes(pole_width,24, pole_width, 0, 24, 24); { this(config, properties, 1.5,16, 1.5, 0, 14, 16); }
this.config = config;
} public StandardFenceBlock(long config, BlockBehaviour.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y)
{
@Override super(properties);
public long config() shape_voxels = buildShapes(pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y);
{ return config; } collision_shape_voxels = buildShapes(pole_width,24, pole_width, 0, 24, 24);
this.config = config;
@Override }
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) @Override
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } public long config()
{ return config; }
private static VoxelShape combinedShape(VoxelShape pole, WallHeight height, VoxelShape low, VoxelShape high)
{ @Override
if(height == WallHeight.TALL) return VoxelShapes.or(pole, high); @OnlyIn(Dist.CLIENT)
if(height == WallHeight.LOW) return VoxelShapes.or(pole, low); public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
return pole; { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
}
private static VoxelShape combinedShape(VoxelShape pole, WallSide height, VoxelShape low, VoxelShape high)
protected Map<BlockState, VoxelShape> buildShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) {
{ if(height == WallSide.TALL) return Shapes.or(pole, high);
final double px0=8.0-pole_width, px1=8.0+pole_width, sx0=8.0-side_width, sx1=8.0+side_width; if(height == WallSide.LOW) return Shapes.or(pole, low);
VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1); return pole;
VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1); }
VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1); protected Map<BlockState, VoxelShape> buildShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y)
VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1); {
VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1); final double px0=8.0-pole_width, px1=8.0+pole_width, sx0=8.0-side_width, sx1=8.0+side_width;
VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16); VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1);
VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1); VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1); VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
Builder<BlockState, VoxelShape> builder = ImmutableMap.builder(); VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
for(Boolean up : UP.getPossibleValues()) { VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
for(WallHeight wh_east : WALL_EAST.getPossibleValues()) { VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
for(WallHeight wh_north : WALL_NORTH.getPossibleValues()) { VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
for(WallHeight wh_west : WALL_WEST.getPossibleValues()) { VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
for(WallHeight wh_south : WALL_SOUTH.getPossibleValues()) { VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
VoxelShape shape = VoxelShapes.empty(); ImmutableMap.Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
shape = combinedShape(shape, wh_east, vs4, vs8); for(Boolean up : UP.getPossibleValues()) {
shape = combinedShape(shape, wh_west, vs3, vs7); for(WallSide wh_east : WALL_EAST.getPossibleValues()) {
shape = combinedShape(shape, wh_north, vs1, vs5); for(WallSide wh_north : WALL_NORTH.getPossibleValues()) {
shape = combinedShape(shape, wh_south, vs2, vs6); for(WallSide wh_west : WALL_WEST.getPossibleValues()) {
if(up) shape = VoxelShapes.or(shape, vp); for(WallSide wh_south : WALL_SOUTH.getPossibleValues()) {
BlockState bs = defaultBlockState().setValue(UP, up) VoxelShape shape = Shapes.empty();
.setValue(WALL_EAST, wh_east) shape = combinedShape(shape, wh_east, vs4, vs8);
.setValue(WALL_NORTH, wh_north) shape = combinedShape(shape, wh_west, vs3, vs7);
.setValue(WALL_WEST, wh_west) shape = combinedShape(shape, wh_north, vs1, vs5);
.setValue(WALL_SOUTH, wh_south); shape = combinedShape(shape, wh_south, vs2, vs6);
builder.put(bs.setValue(WATERLOGGED, false), shape); if(up) shape = Shapes.or(shape, vp);
builder.put(bs.setValue(WATERLOGGED, true), shape); BlockState bs = defaultBlockState().setValue(UP, up)
} .setValue(WALL_EAST, wh_east)
} .setValue(WALL_NORTH, wh_north)
} .setValue(WALL_WEST, wh_west)
} .setValue(WALL_SOUTH, wh_south);
} builder.put(bs.setValue(WATERLOGGED, false), shape);
return builder.build(); builder.put(bs.setValue(WATERLOGGED, true), shape);
} }
}
@Override }
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) }
{ return shape_voxels.getOrDefault(state, VoxelShapes.block()); } }
return builder.build();
@Override }
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return collision_shape_voxels.getOrDefault(state, VoxelShapes.block()); } @Override
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
@Override { return shape_voxels.getOrDefault(state, Shapes.block()); }
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder)
{ super.createBlockStateDefinition(builder); } @Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
protected boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side) { return collision_shape_voxels.getOrDefault(state, Shapes.block()); }
{
final Block block = facingState.getBlock(); @Override
if((block instanceof FenceGateBlock) || (block instanceof StandardFenceBlock) || (block instanceof VariantWallBlock)) return true; protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2)); { super.createBlockStateDefinition(builder); }
if(!(oppositeState.getBlock() instanceof StandardFenceBlock)) return false;
return facingState.isRedstoneConductor(world, facingPos) && canSupportCenter(world, facingPos, side); protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side)
} {
final Block block = facingState.getBlock();
protected WallHeight selectWallHeight(IWorldReader world, BlockPos pos, Direction direction) if((block instanceof FenceGateBlock) || (block instanceof StandardFenceBlock) || (block instanceof VariantWallBlock)) return true;
{ final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2));
return WallHeight.LOW; // @todo: implement if(!(oppositeState.getBlock() instanceof StandardFenceBlock)) return false;
} return facingState.isRedstoneConductor(world, facingPos) && canSupportCenter(world, facingPos, side);
}
public BlockState getStateForPlacement(BlockItemUseContext context)
{ protected WallSide selectWallHeight(LevelReader world, BlockPos pos, Direction direction)
IWorldReader world = context.getLevel(); {
BlockPos pos = context.getClickedPos(); return WallSide.LOW; // @todo: implement
FluidState fs = context.getLevel().getFluidState(context.getClickedPos()); }
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST); public BlockState getStateForPlacement(BlockPlaceContext context)
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH); {
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST); LevelReader world = context.getLevel();
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w); BlockPos pos = context.getClickedPos();
return defaultBlockState() FluidState fs = context.getLevel().getFluidState(context.getClickedPos());
.setValue(UP, not_straight) boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE) boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
.setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE) boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE) boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
.setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE) boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
.setValue(WATERLOGGED, fs.getType() == Fluids.WATER); return defaultBlockState()
} .setValue(UP, not_straight)
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
@Override .setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
public BlockState updateShape(BlockState state, Direction side, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos) .setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
{ .setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
if(state.getValue(BlockStateProperties.WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); .setValue(WATERLOGGED, fs.getType() == Fluids.WATER);
if(side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos); }
boolean n = (side==Direction.NORTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_NORTH)!=WallHeight.NONE);
boolean e = (side==Direction.EAST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_EAST) !=WallHeight.NONE); @Override
boolean s = (side==Direction.SOUTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_SOUTH)!=WallHeight.NONE); public BlockState updateShape(BlockState state, Direction side, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
boolean w = (side==Direction.WEST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_WEST) !=WallHeight.NONE); {
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w); if(state.getValue(BlockStateProperties.WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
return state.setValue(UP, not_straight) if(side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos);
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE) boolean n = (side==Direction.NORTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_NORTH)!=WallSide.NONE);
.setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE) boolean e = (side==Direction.EAST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_EAST) !=WallSide.NONE);
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE) boolean s = (side==Direction.SOUTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_SOUTH)!=WallSide.NONE);
.setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE); boolean w = (side==Direction.WEST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_WEST) !=WallSide.NONE);
} boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
return state.setValue(UP, not_straight)
@Override .setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) .setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
{ return false; } .setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
.setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE);
@Override }
public boolean isPossibleToRespawnInThis()
{ return false; } @Override
public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
@Override { return false; }
@SuppressWarnings("deprecation")
public PushReaction getPistonPushReaction(BlockState state) @Override
{ return PushReaction.NORMAL; } public boolean isPossibleToRespawnInThis()
} { return false; }
@Override
@SuppressWarnings("deprecation")
public PushReaction getPistonPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
}

View file

@ -1,55 +1,57 @@
/* /*
* @file StandardStairsBlock.java * @file StandardStairsBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Stairs and roof blocks, almost entirely based on vanilla stairs. * Stairs and roof blocks, almost entirely based on vanilla stairs.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.network.chat.Component;
import net.minecraft.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.block.*; import net.minecraft.world.item.ItemStack;
import net.minecraft.block.material.PushReaction; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.block.BlockState; import net.minecraft.world.level.BlockGetter;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.StairBlock;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.IBlockReader; import net.minecraft.world.level.material.PushReaction;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable; import wile.engineersdecor.libmc.detail.Auxiliaries;
import java.util.List;
import javax.annotation.Nullable;
import java.util.List;
public class StandardStairsBlock extends StairsBlock implements StandardBlocks.IStandardBlock
{
private final long config; public class StandardStairsBlock extends StairBlock implements StandardBlocks.IStandardBlock
{
public StandardStairsBlock(long config, BlockState state, AbstractBlock.Properties properties) private final long config;
{ super(()->state, properties); this.config = config; }
public StandardStairsBlock(long config, BlockState state, BlockBehaviour.Properties properties)
public StandardStairsBlock(long config, java.util.function.Supplier<BlockState> state, AbstractBlock.Properties properties) { super(()->state, properties); this.config = config; }
{ super(state, properties); this.config = config; }
public StandardStairsBlock(long config, java.util.function.Supplier<BlockState> state, BlockBehaviour.Properties properties)
@Override { super(state, properties); this.config = config; }
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) @Override
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } @OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
@Override { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
public boolean isPossibleToRespawnInThis()
{ return false; } @Override
public boolean isPossibleToRespawnInThis()
@Override { return false; }
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; } @Override
public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
@Override { return false; }
@SuppressWarnings("deprecation")
public PushReaction getPistonPushReaction(BlockState state) @Override
{ return PushReaction.NORMAL; } @SuppressWarnings("deprecation")
} public PushReaction getPistonPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
}

View file

@ -1,201 +1,206 @@
/* /*
* @file VariantSlabBlock.java * @file VariantSlabBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Standard half block horizontal slab characteristics class. * Standard half block horizontal slab characteristics class.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.world.IWorld; import net.minecraft.sounds.SoundSource;
import net.minecraft.world.World; import net.minecraft.util.Mth;
import net.minecraft.world.IBlockReader; import net.minecraft.world.entity.EntityType;
import net.minecraft.state.StateContainer; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.block.*; import net.minecraft.world.entity.player.Player;
import net.minecraft.block.BlockState; import net.minecraft.world.item.ItemStack;
import net.minecraft.fluid.Fluid; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.fluid.FluidState; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.level.BlockGetter;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.Level;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.level.block.Block;
import net.minecraft.state.EnumProperty; import net.minecraft.world.level.block.Mirror;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.world.level.block.Rotation;
import net.minecraft.state.properties.SlabType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.util.*; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.util.math.*; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.util.math.vector.*; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.entity.EntityType; import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.level.material.Fluid;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.material.FluidState;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.AABB;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable; import net.minecraft.world.phys.shapes.CollisionContext;
import java.util.ArrayList; import net.minecraft.world.phys.shapes.Shapes;
import java.util.Collections; import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.List; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.blocks.StandardBlocks.IStandardBlock.RenderTypeHint;
import javax.annotation.Nullable;
public class VariantSlabBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock import java.util.ArrayList;
{ import java.util.Collections;
public static final EnumProperty<SlabType> TYPE = BlockStateProperties.SLAB_TYPE; import java.util.List;
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
protected static final VoxelShape AABBs[] = { public class VariantSlabBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock
VoxelShapes.create(new AxisAlignedBB(0, 8./16, 0, 1, 16./16, 1)), // top slab {
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 8./16, 1)), // bottom slab public static final EnumProperty<SlabType> TYPE = BlockStateProperties.SLAB_TYPE;
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)), // both slabs public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)) // << 2bit fill
}; protected static final VoxelShape[] AABBs = {
protected static final int num_slabs_contained_in_parts_[] = {1,1,2,2}; Shapes.create(new AABB(0, 8./16, 0, 1, 16./16, 1)), // top slab
private static boolean with_pickup = false; Shapes.create(new AABB(0, 0./16, 0, 1, 8./16, 1)), // bottom slab
Shapes.create(new AABB(0, 0./16, 0, 1, 16./16, 1)), // both slabs
public static void on_config(boolean direct_slab_pickup) Shapes.create(new AABB(0, 0./16, 0, 1, 16./16, 1)) // << 2bit fill
{ with_pickup = direct_slab_pickup; } };
protected static final int[] num_slabs_contained_in_parts_ = {1,1,2,2};
protected boolean is_cube(BlockState state) private static boolean with_pickup = false;
{ return state.getValue(TYPE) == SlabType.DOUBLE; }
public static void on_config(boolean direct_slab_pickup)
public VariantSlabBlock(long config, AbstractBlock.Properties builder) { with_pickup = direct_slab_pickup; }
{ super(config, builder); registerDefaultState(defaultBlockState().setValue(TYPE, SlabType.BOTTOM)); }
protected boolean is_cube(BlockState state)
@Override { return state.getValue(TYPE) == SlabType.DOUBLE; }
public RenderTypeHint getRenderTypeHint()
{ return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); } public VariantSlabBlock(long config, BlockBehaviour.Properties builder)
{ super(config, builder); registerDefaultState(defaultBlockState().setValue(TYPE, SlabType.BOTTOM)); }
@Override
@OnlyIn(Dist.CLIENT) @Override
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag) public RenderTypeHint getRenderTypeHint()
{ { return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); }
if(!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return;
if(with_pickup) Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", "engineersdecor.tooltip.slabpickup", tooltip, flag, true); @Override
} @OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
@Override {
@OnlyIn(Dist.CLIENT) if(!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return;
@SuppressWarnings("deprecation") if(with_pickup) Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", "engineersdecor.tooltip.slabpickup", tooltip, flag, true);
public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) }
{ return (adjacentBlockState==state) ? true : super.skipRendering(state, adjacentBlockState, side); }
@Override
@Override @OnlyIn(Dist.CLIENT)
public boolean isPossibleToRespawnInThis() @SuppressWarnings("deprecation")
{ return false; } public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side)
{ return (adjacentBlockState==state) || (super.skipRendering(state, adjacentBlockState, side)); }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) @Override
{ return false; } public boolean isPossibleToRespawnInThis()
{ return false; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext) @Override
{ return AABBs[state.getValue(TYPE).ordinal() & 0x3]; } public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) @Override
{ return getShape(state, world, pos, selectionContext); } public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext)
{ return AABBs[state.getValue(TYPE).ordinal() & 0x3]; }
@Override
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) @Override
{ super.createBlockStateDefinition(builder); builder.add(TYPE, TEXTURE_VARIANT); } public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
@Nullable @Override
public BlockState getStateForPlacement(BlockItemUseContext context) protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ { super.createBlockStateDefinition(builder); builder.add(TYPE, TEXTURE_VARIANT); }
BlockPos pos = context.getClickedPos();
if(context.getLevel().getBlockState(pos).getBlock() == this) return context.getLevel().getBlockState(pos).setValue(TYPE, SlabType.DOUBLE).setValue(WATERLOGGED, false); @Override
final int rnd = MathHelper.clamp((int)(MathHelper.getSeed(context.getClickedPos()) & 0x3), 0, 3); @Nullable
final Direction face = context.getClickedFace(); public BlockState getStateForPlacement(BlockPlaceContext context)
final BlockState placement_state = super.getStateForPlacement(context).setValue(TEXTURE_VARIANT, rnd); // fluid state {
if(face == Direction.UP) return placement_state.setValue(TYPE, SlabType.BOTTOM); BlockPos pos = context.getClickedPos();
if(face == Direction.DOWN) return placement_state.setValue(TYPE, SlabType.TOP); if(context.getLevel().getBlockState(pos).getBlock() == this) return context.getLevel().getBlockState(pos).setValue(TYPE, SlabType.DOUBLE).setValue(WATERLOGGED, false);
if(!face.getAxis().isHorizontal()) return placement_state; final int rnd = Mth.clamp((int)(Mth.getSeed(context.getClickedPos()) & 0x3), 0, 3);
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5); final Direction face = context.getClickedFace();
return placement_state.setValue(TYPE, isupper ? SlabType.TOP : SlabType.BOTTOM); final BlockState placement_state = super.getStateForPlacement(context).setValue(TEXTURE_VARIANT, rnd); // fluid state
} if(face == Direction.UP) return placement_state.setValue(TYPE, SlabType.BOTTOM);
if(face == Direction.DOWN) return placement_state.setValue(TYPE, SlabType.TOP);
@Override if(!face.getAxis().isHorizontal()) return placement_state;
@SuppressWarnings("deprecation") final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
public boolean canBeReplaced(BlockState state, BlockItemUseContext context) return placement_state.setValue(TYPE, isupper ? SlabType.TOP : SlabType.BOTTOM);
{ }
if(context.getItemInHand().getItem() != this.asItem()) return false;
if(!context.replacingClickedOnBlock()) return true; @Override
final Direction face = context.getClickedFace(); @SuppressWarnings("deprecation")
final SlabType type = state.getValue(TYPE); public boolean canBeReplaced(BlockState state, BlockPlaceContext context)
if((face == Direction.UP) && (type==SlabType.BOTTOM)) return true; {
if((face == Direction.DOWN) && (type==SlabType.TOP)) return true; if(context.getItemInHand().getItem() != this.asItem()) return false;
if(!face.getAxis().isHorizontal()) return false; if(!context.replacingClickedOnBlock()) return true;
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5); final Direction face = context.getClickedFace();
return isupper ? (type==SlabType.BOTTOM) : (type==SlabType.TOP); final SlabType type = state.getValue(TYPE);
} if((face == Direction.UP) && (type==SlabType.BOTTOM)) return true;
if((face == Direction.DOWN) && (type==SlabType.TOP)) return true;
@Override if(!face.getAxis().isHorizontal()) return false;
@SuppressWarnings("deprecation") final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
public BlockState rotate(BlockState state, Rotation rot) return isupper ? (type==SlabType.BOTTOM) : (type==SlabType.TOP);
{ return state; } }
@Override @Override
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn) public BlockState rotate(BlockState state, Rotation rot)
{ return state; } { return state; }
@Override @Override
public boolean hasDynamicDropList() @SuppressWarnings("deprecation")
{ return true; } public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state; }
@Override
public List<ItemStack> dropList(BlockState state, World world, TileEntity te, boolean explosion) @Override
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(TYPE).ordinal() & 0x3]))); } public boolean hasDynamicDropList()
{ return true; }
@Override
@SuppressWarnings("deprecation") @Override
public void attack(BlockState state, World world, BlockPos pos, PlayerEntity player) public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion)
{ { return new ArrayList<>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(TYPE).ordinal() & 0x3]))); }
if((world.isClientSide) || (!with_pickup)) return;
final ItemStack stack = player.getMainHandItem(); @Override
if(stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return; @SuppressWarnings("deprecation")
if(stack.getCount() >= stack.getMaxStackSize()) return; public void attack(BlockState state, Level world, BlockPos pos, Player player)
Vector3d lv = player.getLookAngle(); {
Direction facing = Direction.getNearest((float)lv.x, (float)lv.y, (float)lv.z); if((world.isClientSide) || (!with_pickup)) return;
if((facing != Direction.UP) && (facing != Direction.DOWN)) return; final ItemStack stack = player.getMainHandItem();
if(state.getBlock() != this) return; if(stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return;
SlabType type = state.getValue(TYPE); if(stack.getCount() >= stack.getMaxStackSize()) return;
if(facing == Direction.DOWN) { Vec3 lv = player.getLookAngle();
if(type == SlabType.DOUBLE) { Direction facing = Direction.getNearest((float)lv.x, (float)lv.y, (float)lv.z);
world.setBlock(pos, state.setValue(TYPE, SlabType.BOTTOM), 3); if((facing != Direction.UP) && (facing != Direction.DOWN)) return;
} else { if(state.getBlock() != this) return;
world.removeBlock(pos, false); SlabType type = state.getValue(TYPE);
} if(facing == Direction.DOWN) {
} else if(facing == Direction.UP) { if(type == SlabType.DOUBLE) {
if(type == SlabType.DOUBLE) { world.setBlock(pos, state.setValue(TYPE, SlabType.BOTTOM), 3);
world.setBlock(pos, state.setValue(TYPE, SlabType.TOP), 3); } else {
} else { world.removeBlock(pos, false);
world.removeBlock(pos, false); }
} } else if(facing == Direction.UP) {
} if(type == SlabType.DOUBLE) {
if(!player.isCreative()) { world.setBlock(pos, state.setValue(TYPE, SlabType.TOP), 3);
stack.grow(1); } else {
if(player.inventory != null) player.inventory.setChanged(); world.removeBlock(pos, false);
} }
SoundType st = this.getSoundType(state, world, pos, null); }
world.playSound(player, pos, st.getPlaceSound(), SoundCategory.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch()); if(!player.isCreative()) {
} stack.grow(1);
if(player.getInventory() != null) player.getInventory().setChanged();
@Override }
public boolean placeLiquid(IWorld world, BlockPos pos, BlockState state, FluidState fluidState) SoundType st = this.getSoundType(state, world, pos, null);
{ return (state.getValue(TYPE)==SlabType.DOUBLE) ? false : super.placeLiquid(world, pos, state, fluidState); } world.playSound(player, pos, st.getPlaceSound(), SoundSource.BLOCKS, (st.getVolume()+1f)/2.5f, 0.9f*st.getPitch());
}
@Override
public boolean canPlaceLiquid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid) @Override
{ return (state.getValue(TYPE)==SlabType.DOUBLE) ? false : super.canPlaceLiquid(world, pos, state, fluid); } public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState)
{ return (state.getValue(TYPE) != SlabType.DOUBLE) && super.placeLiquid(world, pos, state, fluidState); }
}
@Override
public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.getValue(TYPE) != SlabType.DOUBLE) && super.canPlaceLiquid(world, pos, state, fluid); }
}

View file

@ -1,197 +1,198 @@
/* /*
* @file VariantWallBlock.java * @file VariantWallBlock.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Wall blocks. * Wall blocks.
*/ */
package wile.engineersdecor.libmc.blocks; package wile.engineersdecor.libmc.blocks;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableMap.Builder;
import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.core.BlockPos;
import net.minecraft.state.BooleanProperty; import net.minecraft.core.Direction;
import net.minecraft.state.EnumProperty; import net.minecraft.network.chat.Component;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.util.Mth;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.entity.EntityType;
import wile.engineersdecor.libmc.detail.Auxiliaries; import net.minecraft.world.entity.SpawnPlacements;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.*; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.fluid.FluidState; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.entity.EntityType; import net.minecraft.world.level.BlockGetter;
import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.state.StateContainer; import net.minecraft.world.level.LevelReader;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.block.Block;
import net.minecraft.block.*; import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.block.material.PushReaction; import net.minecraft.world.level.block.WallBlock;
import net.minecraft.block.BlockState; import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.fluid.Fluids; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.block.state.properties.*;
import net.minecraft.state.IntegerProperty; import net.minecraft.world.level.material.FluidState;
import net.minecraft.util.Direction; import net.minecraft.world.level.material.Fluids;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.material.PushReaction;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.Dist;
import javax.annotation.Nullable; import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.List; import wile.engineersdecor.libmc.detail.Auxiliaries;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.List;
public class VariantWallBlock extends WallBlock implements StandardBlocks.IStandardBlock import java.util.Map;
{
public static final BooleanProperty UP = BlockStateProperties.UP;
public static final EnumProperty<WallHeight> WALL_EAST = BlockStateProperties.EAST_WALL; public class VariantWallBlock extends WallBlock implements StandardBlocks.IStandardBlock
public static final EnumProperty<WallHeight> WALL_NORTH = BlockStateProperties.NORTH_WALL; {
public static final EnumProperty<WallHeight> WALL_SOUTH = BlockStateProperties.SOUTH_WALL; public static final BooleanProperty UP = BlockStateProperties.UP;
public static final EnumProperty<WallHeight> WALL_WEST = BlockStateProperties.WEST_WALL; public static final EnumProperty<WallSide> WALL_EAST = BlockStateProperties.EAST_WALL;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; public static final EnumProperty<WallSide> WALL_NORTH = BlockStateProperties.NORTH_WALL;
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 7); public static final EnumProperty<WallSide> WALL_SOUTH = BlockStateProperties.SOUTH_WALL;
private final Map<BlockState, VoxelShape> shape_voxels; public static final EnumProperty<WallSide> WALL_WEST = BlockStateProperties.WEST_WALL;
private final Map<BlockState, VoxelShape> collision_shape_voxels; public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private final long config; public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 7);
private final Map<BlockState, VoxelShape> shape_voxels;
public VariantWallBlock(long config, AbstractBlock.Properties builder) private final Map<BlockState, VoxelShape> collision_shape_voxels;
{ private final long config;
super(builder);
shape_voxels = buildWallShapes(4, 16, 4, 0, 16, 16); public VariantWallBlock(long config, BlockBehaviour.Properties builder)
collision_shape_voxels = buildWallShapes(6, 16, 5, 0, 24, 24); {
this.config = config; super(builder);
} shape_voxels = buildWallShapes(4, 16, 4, 0, 16, 16);
collision_shape_voxels = buildWallShapes(6, 16, 5, 0, 24, 24);
@Override this.config = config;
public long config() }
{ return config; }
@Override
@Override public long config()
@OnlyIn(Dist.CLIENT) { return config; }
public void appendHoverText(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } @Override
@OnlyIn(Dist.CLIENT)
private static VoxelShape combinedShape(VoxelShape pole, WallHeight height, VoxelShape low, VoxelShape high) public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag)
{ { Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
if(height == WallHeight.TALL) return VoxelShapes.or(pole, high);
if(height == WallHeight.LOW) return VoxelShapes.or(pole, low); private static VoxelShape combinedShape(VoxelShape pole, WallSide height, VoxelShape low, VoxelShape high)
return pole; {
} if(height == WallSide.TALL) return Shapes.or(pole, high);
if(height == WallSide.LOW) return Shapes.or(pole, low);
protected Map<BlockState, VoxelShape> buildWallShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) return pole;
{ }
final double px0=8.0-pole_width, px1=8.0+pole_width, sx0=8.0-side_width, sx1=8.0+side_width;
VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1); protected Map<BlockState, VoxelShape> buildWallShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y)
VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1); {
VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16); final double px0=8.0-pole_width, px1=8.0+pole_width, sx0=8.0-side_width, sx1=8.0+side_width;
VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1); VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1);
VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1); VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1); VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16); VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1); VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1); VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
Builder<BlockState, VoxelShape> builder = ImmutableMap.builder(); VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
for(Boolean up : UP.getPossibleValues()) { VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
for(WallHeight wh_east : WALL_EAST.getPossibleValues()) { VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
for(WallHeight wh_north : WALL_NORTH.getPossibleValues()) { Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
for(WallHeight wh_west : WALL_WEST.getPossibleValues()) { for(Boolean up : UP.getPossibleValues()) {
for(WallHeight wh_south : WALL_SOUTH.getPossibleValues()) { for(WallSide wh_east : WALL_EAST.getPossibleValues()) {
VoxelShape shape = VoxelShapes.empty(); for(WallSide wh_north : WALL_NORTH.getPossibleValues()) {
shape = combinedShape(shape, wh_east, vs4, vs8); for(WallSide wh_west : WALL_WEST.getPossibleValues()) {
shape = combinedShape(shape, wh_west, vs3, vs7); for(WallSide wh_south : WALL_SOUTH.getPossibleValues()) {
shape = combinedShape(shape, wh_north, vs1, vs5); VoxelShape shape = Shapes.empty();
shape = combinedShape(shape, wh_south, vs2, vs6); shape = combinedShape(shape, wh_east, vs4, vs8);
if(up) shape = VoxelShapes.or(shape, vp); shape = combinedShape(shape, wh_west, vs3, vs7);
BlockState bs = defaultBlockState().setValue(UP, up) shape = combinedShape(shape, wh_north, vs1, vs5);
.setValue(WALL_EAST, wh_east) shape = combinedShape(shape, wh_south, vs2, vs6);
.setValue(WALL_NORTH, wh_north) if(up) shape = Shapes.or(shape, vp);
.setValue(WALL_WEST, wh_west) BlockState bs = defaultBlockState().setValue(UP, up)
.setValue(WALL_SOUTH, wh_south); .setValue(WALL_EAST, wh_east)
final VoxelShape tvs = shape; .setValue(WALL_NORTH, wh_north)
TEXTURE_VARIANT.getPossibleValues().forEach((tv)->{ .setValue(WALL_WEST, wh_west)
builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, false), tvs); .setValue(WALL_SOUTH, wh_south);
builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, true), tvs); final VoxelShape tvs = shape;
}); TEXTURE_VARIANT.getPossibleValues().forEach((tv)->{
} builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, false), tvs);
} builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, true), tvs);
} });
} }
} }
return builder.build(); }
} }
}
@Override return builder.build();
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) }
{ return shape_voxels.getOrDefault(state, VoxelShapes.block()); }
@Override
@Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) { return shape_voxels.getOrDefault(state, Shapes.block()); }
{ return collision_shape_voxels.getOrDefault(state, VoxelShapes.block()); }
@Override
@Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext)
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> builder) { return collision_shape_voxels.getOrDefault(state, Shapes.block()); }
{ super.createBlockStateDefinition(builder); builder.add(TEXTURE_VARIANT); }
@Override
protected boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side) protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder)
{ { super.createBlockStateDefinition(builder); builder.add(TEXTURE_VARIANT); }
final Block block = facingState.getBlock();
if((block instanceof FenceGateBlock) || (block instanceof WallBlock)) return true; protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side)
final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2)); {
if(!(oppositeState.getBlock() instanceof VariantWallBlock)) return false; final Block block = facingState.getBlock();
return facingState.isRedstoneConductor(world, facingPos) && Block.canSupportCenter(world, facingPos, side); if((block instanceof FenceGateBlock) || (block instanceof WallBlock)) return true;
} final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2));
if(!(oppositeState.getBlock() instanceof VariantWallBlock)) return false;
protected WallHeight selectWallHeight(IWorldReader world, BlockPos pos, Direction direction) return facingState.isRedstoneConductor(world, facingPos) && Block.canSupportCenter(world, facingPos, side);
{ }
return WallHeight.LOW; // @todo: implement
} protected WallSide selectWallHeight(LevelReader world, BlockPos pos, Direction direction)
{ return WallSide.LOW; }
public BlockState getStateForPlacement(BlockItemUseContext context)
{ public BlockState getStateForPlacement(BlockPlaceContext context)
IWorldReader world = context.getLevel(); {
BlockPos pos = context.getClickedPos(); LevelReader world = context.getLevel();
FluidState fs = context.getLevel().getFluidState(context.getClickedPos()); BlockPos pos = context.getClickedPos();
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH); FluidState fs = context.getLevel().getFluidState(context.getClickedPos());
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST); boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH); boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST); boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w); boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
return defaultBlockState().setValue(UP, not_straight) boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE) return defaultBlockState().setValue(UP, not_straight)
.setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE) .setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE) .setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
.setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE) .setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
.setValue(WATERLOGGED, fs.getType()==Fluids.WATER); .setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
} .setValue(WATERLOGGED, fs.getType()==Fluids.WATER);
}
@Override
public BlockState updateShape(BlockState state, Direction side, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos) @Override
{ public BlockState updateShape(BlockState state, Direction side, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos)
if(state.getValue(WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); {
if(side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos); if(state.getValue(WATERLOGGED)) world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
boolean n = (side==Direction.NORTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_NORTH)!=WallHeight.NONE; if(side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos);
boolean e = (side==Direction.EAST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_EAST)!=WallHeight.NONE; boolean n = (side==Direction.NORTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_NORTH)!=WallSide.NONE;
boolean s = (side==Direction.SOUTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_SOUTH)!=WallHeight.NONE; boolean e = (side==Direction.EAST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_EAST)!=WallSide.NONE;
boolean w = (side==Direction.WEST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_WEST)!=WallHeight.NONE; boolean s = (side==Direction.SOUTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_SOUTH)!=WallSide.NONE;
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w); boolean w = (side==Direction.WEST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_WEST)!=WallSide.NONE;
return state.setValue(UP, not_straight) boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE) return state.setValue(UP, not_straight)
.setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE) .setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE) .setValue(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
.setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE) .setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
.setValue(TEXTURE_VARIANT, ((int)MathHelper.getSeed(pos)) & 0x7); .setValue(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
} .setValue(TEXTURE_VARIANT, ((int)Mth.getSeed(pos)) & 0x7);
}
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType) @Override
{ return false; } public boolean canCreatureSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public boolean isPossibleToRespawnInThis() @Override
{ return false; } public boolean isPossibleToRespawnInThis()
{ return false; }
@Override
@SuppressWarnings("deprecation") @Override
public PushReaction getPistonPushReaction(BlockState state) @SuppressWarnings("deprecation")
{ return PushReaction.NORMAL; } public PushReaction getPistonPushReaction(BlockState state)
} { return PushReaction.NORMAL; }
}

View file

@ -1,42 +0,0 @@
package wile.engineersdecor.libmc.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.gui.IHasContainer;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public abstract class ContainerGui<T extends Container> extends ContainerScreen<T> implements IHasContainer<T>
{
public ContainerGui(T screenContainer, PlayerInventory inv, ITextComponent titleIn)
{ super(screenContainer, inv, titleIn); }
protected boolean canHaveDisturbingButtonsFromOtherMods()
{ return false; }
public void init(Minecraft minecraft, int width, int height)
{
this.minecraft = minecraft;
this.itemRenderer = minecraft.getItemRenderer();
this.font = minecraft.font;
this.width = width;
this.height = height;
java.util.function.Consumer<Widget> remove = (b) -> { buttons.remove(b); children.remove(b); };
if((!canHaveDisturbingButtonsFromOtherMods()) || (!net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent.Pre(this, this.buttons, this::addButton, remove)))) {
this.buttons.clear();
this.children.clear();
this.setFocused((IGuiEventListener)null);
this.init();
}
if(canHaveDisturbingButtonsFromOtherMods()) {
net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent.Post(this, this.buttons, this::addButton, remove));
}
}
}

View file

@ -1,48 +0,0 @@
/*
* @file BlockStateDataGen.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Blockstate data generator.
*/
package wile.engineersdecor.libmc.datagen;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraft.data.*;
public class AssetsDataGen
{
public static class BlockStates extends BlockStateProvider
{
public BlockStates(DataGenerator gen, ExistingFileHelper efh)
{ super(gen, Auxiliaries.modid(), efh); }
@Override
public String getName()
{ return Auxiliaries.modid() + " Block states"; }
@Override
protected void registerStatesAndModels()
{
}
}
public static class ItemModels extends ItemModelProvider
{
public ItemModels(DataGenerator generator, ExistingFileHelper efh)
{ super(generator, Auxiliaries.modid(), efh); }
@Override
public String getName()
{ return Auxiliaries.modid() + "Item models"; }
@Override
protected void registerModels()
{
}
}
}

View file

@ -1,96 +0,0 @@
/*
* @file LootTableGen.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Loot table generator.
*/
package wile.engineersdecor.libmc.datagen;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.block.Block;
import net.minecraft.data.*;
import net.minecraft.util.ResourceLocation;
import net.minecraft.loot.*;
import net.minecraft.loot.functions.CopyName;
import net.minecraft.loot.functions.CopyName.Source;
import net.minecraft.loot.functions.CopyNbt;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class LootTableGen extends LootTableProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create();
private final DataGenerator generator;
private final Supplier<List<Block>> block_listing;
//--------------------------------------------------------------------------------------------------------------------
public LootTableGen(DataGenerator gen, Supplier<List<Block>> block_list_supplier)
{ super(gen); generator=gen; block_listing = block_list_supplier; }
//-- LootTableProvider -----------------------------------------------------------------------------------------------
@Override
public String getName()
{ return Auxiliaries.modid() + " Loot Tables"; }
@Override
public void run(DirectoryCache cache)
{ save(cache, generate()); }
//--------------------------------------------------------------------------------------------------------------------
private Map<ResourceLocation, LootTable> generate()
{
final HashMap<ResourceLocation, LootTable> tables = new HashMap<ResourceLocation, LootTable>();
final List<Block> blocks = block_listing.get();
blocks.forEach((block)->{
LOGGER.info("Generating loot table for " + block.getRegistryName());
if((!(block instanceof StandardBlocks.IStandardBlock)) || (!(((StandardBlocks.IStandardBlock)block).hasDynamicDropList()))) {
tables.put(
block.getLootTable(),
defaultBlockDrops(block.getRegistryName().getPath() + "_dlt", block)
.setParamSet(LootParameterSets.BLOCK).build());
} else {
LOGGER.info("Dynamic drop list, skipping loot table for " + block.getRegistryName());
}
});
return tables;
}
private void save(DirectoryCache cache, Map<ResourceLocation, LootTable> tables)
{
final Path root = generator.getOutputFolder();
tables.forEach((rl,tab)->{
Path fp = root.resolve("data/" + rl.getNamespace() + "/loot_tables/" + rl.getPath() + ".json");
try {
IDataProvider.save(GSON, cache, LootTableManager.serialize(tab), fp);
} catch(Exception e) {
LOGGER.error("Failed to save loottable '"+fp+"', exception: " + e);
}
});
}
private LootTable.Builder defaultBlockDrops(String rl_path, Block block)
{
StandaloneLootEntry.Builder iltb = ItemLootEntry.lootTableItem(block);
iltb.apply(CopyName.copyName(Source.BLOCK_ENTITY));
if(block.hasTileEntity(block.defaultBlockState())) {
iltb.apply(CopyNbt.copyData(CopyNbt.Source.BLOCK_ENTITY));
}
return LootTable.lootTable().withPool(LootPool.lootPool().name(rl_path).setRolls(ConstantRange.exactly(1)).add(iltb));
}
}

View file

@ -1,391 +1,505 @@
/* /*
* @file Auxiliaries.java * @file Auxiliaries.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* General commonly used functionality. * General commonly used functionality.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.client.util.InputMappings; import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.SharedConstants;
import net.minecraft.util.Direction; import net.minecraft.core.BlockPos;
import net.minecraft.util.Direction.Axis; import net.minecraft.core.Direction;
import net.minecraft.util.SharedConstants; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.shapes.IBooleanFunction; import net.minecraft.network.chat.Component;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.IBlockReader; import net.minecraft.world.item.TooltipFlag;
import net.minecraft.item.ItemStack; import net.minecraft.world.level.BlockGetter;
import net.minecraft.client.util.ITooltipFlag; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.util.text.TextFormatting; import net.minecraft.world.phys.AABB;
import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraftforge.fml.ModList; import net.minecraft.world.phys.shapes.Shapes;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.ModList;
import org.apache.logging.log4j.Logger; import net.minecraftforge.api.distmarker.Dist;
import org.lwjgl.glfw.GLFW; import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.ModConfig; import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nullable; import wile.engineersdecor.ModConfig;
import java.io.BufferedReader;
import java.io.InputStream; import javax.annotation.Nullable;
import java.io.InputStreamReader; import java.io.BufferedReader;
import java.nio.charset.StandardCharsets; import java.io.InputStream;
import java.util.List; import java.io.InputStreamReader;
import java.util.UUID; import java.nio.charset.StandardCharsets;
import java.util.function.Supplier; import java.util.Iterator;
import java.util.regex.Matcher; import java.util.List;
import java.util.regex.Pattern; import java.util.NoSuchElementException;
import java.util.stream.Collectors; import java.util.UUID;
import java.util.function.Supplier;
import java.util.regex.Matcher;
public class Auxiliaries import java.util.regex.Pattern;
{ import java.util.stream.Collectors;
private static String modid; import java.util.stream.Stream;
private static Logger logger;
private static Supplier<CompoundNBT> server_config_supplier = ()->new CompoundNBT();
public class Auxiliaries
public static void init(String modid, Logger logger, Supplier<CompoundNBT> server_config_supplier) {
{ private static String modid;
Auxiliaries.modid = modid; private static Logger logger;
Auxiliaries.logger = logger; private static Supplier<CompoundTag> server_config_supplier = CompoundTag::new;
Auxiliaries.server_config_supplier = server_config_supplier;
} public static void init(String modid, Logger logger, Supplier<CompoundTag> server_config_supplier)
{
// ------------------------------------------------------------------------------------------------------------------- Auxiliaries.modid = modid;
// Mod specific exports Auxiliaries.logger = logger;
// ------------------------------------------------------------------------------------------------------------------- Auxiliaries.server_config_supplier = server_config_supplier;
}
public static String modid()
{ return modid; } // -------------------------------------------------------------------------------------------------------------------
// Mod specific exports
public static Logger logger() // -------------------------------------------------------------------------------------------------------------------
{ return logger; }
public static String modid()
// ------------------------------------------------------------------------------------------------------------------- { return modid; }
// Sideness, system/environment, tagging interfaces
// ------------------------------------------------------------------------------------------------------------------- public static Logger logger()
{ return logger; }
public interface IExperimentalFeature {}
// -------------------------------------------------------------------------------------------------------------------
public static final boolean isModLoaded(final String registry_name) // Sideness, system/environment, tagging interfaces
{ return ModList.get().isLoaded(registry_name); } // -------------------------------------------------------------------------------------------------------------------
public static final boolean isDevelopmentMode() public interface IExperimentalFeature {}
{ return SharedConstants.IS_RUNNING_IN_IDE; }
public static boolean isModLoaded(final String registry_name)
@OnlyIn(Dist.CLIENT) { return ModList.get().isLoaded(registry_name); }
public static final boolean isShiftDown()
{ public static boolean isDevelopmentMode()
return (InputMappings.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT) || { return SharedConstants.IS_RUNNING_IN_IDE; }
InputMappings.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_SHIFT));
} @OnlyIn(Dist.CLIENT)
public static boolean isShiftDown()
@OnlyIn(Dist.CLIENT) {
public static final boolean isCtrlDown() return (InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT) ||
{ InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_SHIFT));
return (InputMappings.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_CONTROL) || }
InputMappings.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_CONTROL));
} @OnlyIn(Dist.CLIENT)
public static boolean isCtrlDown()
// ------------------------------------------------------------------------------------------------------------------- {
// Logging return (InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_CONTROL) ||
// ------------------------------------------------------------------------------------------------------------------- InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_CONTROL));
}
public static final void logInfo(final String msg)
{ logger.info(msg); } // -------------------------------------------------------------------------------------------------------------------
// Logging
public static final void logWarn(final String msg) // -------------------------------------------------------------------------------------------------------------------
{ logger.warn(msg); }
public static void logInfo(final String msg)
public static final void logError(final String msg) { logger.info(msg); }
{ logger.error(msg); }
public static void logWarn(final String msg)
public static final void logDebug(final String msg) { logger.warn(msg); }
{ if(ModConfig.withDebug()) logger.warn(msg); }
public static void logError(final String msg)
// ------------------------------------------------------------------------------------------------------------------- { logger.error(msg); }
// Localization, text formatting
// ------------------------------------------------------------------------------------------------------------------- public static void logDebug(final String msg)
{ if(ModConfig.withDebugLogging()) logger.info(msg); }
/**
* Text localisation wrapper, implicitly prepends `MODID` to the // -------------------------------------------------------------------------------------------------------------------
* translation keys. Forces formatting argument, nullable if no special formatting shall be applied.. // Localization, text formatting
*/ // -------------------------------------------------------------------------------------------------------------------
public static TranslationTextComponent localizable(String modtrkey, Object... args)
{ /**
return new TranslationTextComponent((modtrkey.startsWith("block.") || (modtrkey.startsWith("item."))) ? (modtrkey) : (modid+"."+modtrkey), args); * Text localisation wrapper, implicitly prepends `MODID` to the
} * translation keys. Forces formatting argument, nullable if no special formatting shall be applied..
*/
public static TranslationTextComponent localizable(String modtrkey) public static TranslatableComponent localizable(String modtrkey, Object... args)
{ return localizable(modtrkey, new Object[]{}); } {
return new TranslatableComponent((modtrkey.startsWith("block.") || (modtrkey.startsWith("item."))) ? (modtrkey) : (modid+"."+modtrkey), args);
public static TranslationTextComponent localizable_block_key(String blocksubkey) }
{ return new TranslationTextComponent("block."+modid+"."+blocksubkey); }
public static TranslatableComponent localizable(String modtrkey)
@OnlyIn(Dist.CLIENT) { return localizable(modtrkey, new Object[]{}); }
public static String localize(String translationKey, Object... args)
{ public static TranslatableComponent localizable_block_key(String blocksubkey)
TranslationTextComponent tr = new TranslationTextComponent(translationKey, args); { return new TranslatableComponent("block."+modid+"."+blocksubkey); }
tr.withStyle(TextFormatting.RESET);
final String ft = tr.getString(); @OnlyIn(Dist.CLIENT)
if(ft.contains("${")) { public static String localize(String translationKey, Object... args)
// Non-recursive, non-argument lang file entry cross referencing. {
Pattern pt = Pattern.compile("\\$\\{([^}]+)\\}"); TranslatableComponent tr = new TranslatableComponent(translationKey, args);
Matcher mt = pt.matcher(ft); tr.withStyle(ChatFormatting.RESET);
StringBuffer sb = new StringBuffer(); final String ft = tr.getString();
while(mt.find()) { if(ft.contains("${")) {
String m = mt.group(1); // Non-recursive, non-argument lang file entry cross referencing.
if(m.contains("?")) { Pattern pt = Pattern.compile("\\$\\{([^}]+)\\}");
String[] kv = m.split("\\?", 2); Matcher mt = pt.matcher(ft);
String key = kv[0].trim(); StringBuffer sb = new StringBuffer();
boolean not = key.startsWith("!"); while(mt.find()) {
if(not) key = key.replaceFirst("!", ""); String m = mt.group(1);
m = kv[1].trim(); if(m.contains("?")) {
if(!server_config_supplier.get().contains(key)) { String[] kv = m.split("\\?", 2);
m = ""; String key = kv[0].trim();
} else { boolean not = key.startsWith("!");
boolean r = server_config_supplier.get().getBoolean(key); if(not) key = key.replaceFirst("!", "");
if(not) r = !r; m = kv[1].trim();
if(!r) m = ""; if(!server_config_supplier.get().contains(key)) {
} m = "";
} } else {
mt.appendReplacement(sb, Matcher.quoteReplacement((new TranslationTextComponent(m)).getString().trim())); boolean r = server_config_supplier.get().getBoolean(key);
} if(not) r = !r;
mt.appendTail(sb); if(!r) m = "";
return sb.toString(); }
} else { }
return ft; mt.appendReplacement(sb, Matcher.quoteReplacement((new TranslatableComponent(m)).getString().trim()));
} }
} mt.appendTail(sb);
return sb.toString();
/** } else {
* Returns true if a given key is translated for the current language. return ft;
*/ }
@OnlyIn(Dist.CLIENT) }
public static boolean hasTranslation(String key)
{ return net.minecraft.client.resources.I18n.exists(key); } /**
* Returns true if a given key is translated for the current language.
public static final class Tooltip */
{ @OnlyIn(Dist.CLIENT)
@OnlyIn(Dist.CLIENT) public static boolean hasTranslation(String key)
public static boolean extendedTipCondition() { return net.minecraft.client.resources.language.I18n.exists(key); }
{ return isShiftDown(); }
public static final class Tooltip
@OnlyIn(Dist.CLIENT) {
public static boolean helpCondition() @OnlyIn(Dist.CLIENT)
{ return isShiftDown() && isCtrlDown(); } public static boolean extendedTipCondition()
{ return isShiftDown(); }
/**
* Adds an extended tooltip or help tooltip depending on the key states of CTRL and SHIFT. @OnlyIn(Dist.CLIENT)
* Returns true if the localisable help/tip was added, false if not (either not CTL/SHIFT or public static boolean helpCondition()
* no translation found). { return isShiftDown() && isCtrlDown(); }
*/
@OnlyIn(Dist.CLIENT) /**
public static boolean addInformation(@Nullable String advancedTooltipTranslationKey, @Nullable String helpTranslationKey, List<ITextComponent> tooltip, ITooltipFlag flag, boolean addAdvancedTooltipHints) * Adds an extended tooltip or help tooltip depending on the key states of CTRL and SHIFT.
{ * Returns true if the localisable help/tip was added, false if not (either not CTL/SHIFT or
// Note: intentionally not using keybinding here, this must be `control` or `shift`. * no translation found).
final boolean help_available = (helpTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".help"); */
final boolean tip_available = (advancedTooltipTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".tip"); @OnlyIn(Dist.CLIENT)
if((!help_available) && (!tip_available)) return false; public static boolean addInformation(@Nullable String advancedTooltipTranslationKey, @Nullable String helpTranslationKey, List<Component> tooltip, TooltipFlag flag, boolean addAdvancedTooltipHints)
String tip_text = ""; {
if(helpCondition()) { // Note: intentionally not using keybinding here, this must be `control` or `shift`.
if(help_available) tip_text = localize(helpTranslationKey + ".help"); final boolean help_available = (helpTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".help");
} else if(extendedTipCondition()) { final boolean tip_available = (advancedTooltipTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".tip");
if(tip_available) tip_text = localize(advancedTooltipTranslationKey + ".tip"); if((!help_available) && (!tip_available)) return false;
} else if(addAdvancedTooltipHints) { String tip_text = "";
if(tip_available) tip_text += localize(modid + ".tooltip.hint.extended") + (help_available ? " " : ""); if(helpCondition()) {
if(help_available) tip_text += localize(modid + ".tooltip.hint.help"); if(help_available) tip_text = localize(helpTranslationKey + ".help");
} } else if(extendedTipCondition()) {
if(tip_text.isEmpty()) return false; if(tip_available) tip_text = localize(advancedTooltipTranslationKey + ".tip");
String[] tip_list = tip_text.split("\\r?\\n"); } else if(addAdvancedTooltipHints) {
for(String tip:tip_list) { if(tip_available) tip_text += localize(modid + ".tooltip.hint.extended") + (help_available ? " " : "");
tooltip.add(new StringTextComponent(tip.replaceAll("\\s+$","").replaceAll("^\\s+", "")).withStyle(TextFormatting.GRAY)); if(help_available) tip_text += localize(modid + ".tooltip.hint.help");
} }
return true; if(tip_text.isEmpty()) return false;
} String[] tip_list = tip_text.split("\\r?\\n");
for(String tip:tip_list) {
/** tooltip.add(new TextComponent(tip.replaceAll("\\s+$","").replaceAll("^\\s+", "")).withStyle(ChatFormatting.GRAY));
* Adds an extended tooltip or help tooltip for a given stack depending on the key states of CTRL and SHIFT. }
* Format in the lang file is (e.g. for items): "item.MODID.REGISTRYNAME.tip" and "item.MODID.REGISTRYNAME.help". return true;
* Return value see method pattern above. }
*/
@OnlyIn(Dist.CLIENT) /**
public static boolean addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag, boolean addAdvancedTooltipHints) * Adds an extended tooltip or help tooltip for a given stack depending on the key states of CTRL and SHIFT.
{ return addInformation(stack.getDescriptionId(), stack.getDescriptionId(), tooltip, flag, addAdvancedTooltipHints); } * Format in the lang file is (e.g. for items): "item.MODID.REGISTRYNAME.tip" and "item.MODID.REGISTRYNAME.help".
* Return value see method pattern above.
@OnlyIn(Dist.CLIENT) */
public static boolean addInformation(String translation_key, List<ITextComponent> tooltip) @OnlyIn(Dist.CLIENT)
{ public static boolean addInformation(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag, boolean addAdvancedTooltipHints)
if(!Auxiliaries.hasTranslation(translation_key)) return false; { return addInformation(stack.getDescriptionId(), stack.getDescriptionId(), tooltip, flag, addAdvancedTooltipHints); }
tooltip.add(new StringTextComponent(localize(translation_key).replaceAll("\\s+$","").replaceAll("^\\s+", "")).withStyle(TextFormatting.GRAY));
return true; @OnlyIn(Dist.CLIENT)
} public static boolean addInformation(String translation_key, List<Component> tooltip)
{
} if(!Auxiliaries.hasTranslation(translation_key)) return false;
tooltip.add(new TextComponent(localize(translation_key).replaceAll("\\s+$","").replaceAll("^\\s+", "")).withStyle(ChatFormatting.GRAY));
@SuppressWarnings("unused") return true;
public static void playerChatMessage(final PlayerEntity player, final String message) }
{
String s = message.trim(); }
if(!s.isEmpty()) player.sendMessage(new TranslationTextComponent(s), new UUID(0,0));
} @SuppressWarnings("unused")
public static void playerChatMessage(final Player player, final String message)
public static @Nullable ITextComponent unserializeTextComponent(String serialized) {
{ return ITextComponent.Serializer.fromJson(serialized); } String s = message.trim();
if(!s.isEmpty()) player.sendMessage(new TranslatableComponent(s), new UUID(0,0));
public static String serializeTextComponent(ITextComponent tc) }
{ return (tc==null) ? ("") : (ITextComponent.Serializer.toJson(tc)); }
public static @Nullable Component unserializeTextComponent(String serialized)
// ------------------------------------------------------------------------------------------------------------------- { return Component.Serializer.fromJson(serialized); }
// Item NBT data
// ------------------------------------------------------------------------------------------------------------------- public static String serializeTextComponent(Component tc)
{ return (tc==null) ? ("") : (Component.Serializer.toJson(tc)); }
/**
* Equivalent to getDisplayName(), returns null if no custom name is set. // -------------------------------------------------------------------------------------------------------------------
*/ // Item NBT data
public static @Nullable ITextComponent getItemLabel(ItemStack stack) // -------------------------------------------------------------------------------------------------------------------
{
CompoundNBT nbt = stack.getTagElement("display"); /**
if(nbt != null && nbt.contains("Name", 8)) { * Equivalent to getDisplayName(), returns null if no custom name is set.
try { */
ITextComponent tc = unserializeTextComponent(nbt.getString("Name")); public static @Nullable Component getItemLabel(ItemStack stack)
if(tc != null) return tc; {
nbt.remove("Name"); CompoundTag nbt = stack.getTagElement("display");
} catch(Exception e) { if(nbt != null && nbt.contains("Name", 8)) {
nbt.remove("Name"); try {
} Component tc = unserializeTextComponent(nbt.getString("Name"));
} if(tc != null) return tc;
return null; nbt.remove("Name");
} } catch(Exception e) {
nbt.remove("Name");
public static ItemStack setItemLabel(ItemStack stack, @Nullable ITextComponent name) }
{ }
if(name != null) { return null;
CompoundNBT nbt = stack.getOrCreateTagElement("display"); }
nbt.putString("Name", serializeTextComponent(name));
} else { public static ItemStack setItemLabel(ItemStack stack, @Nullable Component name)
if(stack.hasTag()) stack.removeTagKey("display"); {
} if(name != null) {
return stack; CompoundTag nbt = stack.getOrCreateTagElement("display");
} nbt.putString("Name", serializeTextComponent(name));
} else {
// ------------------------------------------------------------------------------------------------------------------- if(stack.hasTag()) stack.removeTagKey("display");
// Block handling }
// ------------------------------------------------------------------------------------------------------------------- return stack;
}
public static final AxisAlignedBB getPixeledAABB(double x0, double y0, double z0, double x1, double y1, double z1)
{ return new AxisAlignedBB(x0/16.0, y0/16.0, z0/16.0, x1/16.0, y1/16.0, z1/16.0); } // -------------------------------------------------------------------------------------------------------------------
// Block handling
public static final AxisAlignedBB getRotatedAABB(AxisAlignedBB bb, Direction new_facing, boolean horizontal_rotation) // -------------------------------------------------------------------------------------------------------------------
{
if(!horizontal_rotation) { public static boolean isWaterLogged(BlockState state)
switch(new_facing.get3DDataValue()) { { return state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(BlockStateProperties.WATERLOGGED); }
case 0: return new AxisAlignedBB(1-bb.maxX, bb.minZ, bb.minY, 1-bb.minX, bb.maxZ, bb.maxY); // D
case 1: return new AxisAlignedBB(1-bb.maxX, 1-bb.maxZ, 1-bb.maxY, 1-bb.minX, 1-bb.minZ, 1-bb.minY); // U public static AABB getPixeledAABB(double x0, double y0, double z0, double x1, double y1, double z1)
case 2: return new AxisAlignedBB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb { return new AABB(x0/16.0, y0/16.0, z0/16.0, x1/16.0, y1/16.0, z1/16.0); }
case 3: return new AxisAlignedBB(1-bb.maxX, bb.minY, 1-bb.maxZ, 1-bb.minX, bb.maxY, 1-bb.minZ); // S
case 4: return new AxisAlignedBB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W public static AABB getRotatedAABB(AABB bb, Direction new_facing)
case 5: return new AxisAlignedBB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E { return getRotatedAABB(bb, new_facing, false); }
}
} else { public static AABB[] getRotatedAABB(AABB[] bb, Direction new_facing)
switch(new_facing.get3DDataValue()) { { return getRotatedAABB(bb, new_facing, false); }
case 0: return new AxisAlignedBB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // D --> bb
case 1: return new AxisAlignedBB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // U --> bb public static AABB getRotatedAABB(AABB bb, Direction new_facing, boolean horizontal_rotation)
case 2: return new AxisAlignedBB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb {
case 3: return new AxisAlignedBB(1-bb.maxX, bb.minY, 1-bb.maxZ, 1-bb.minX, bb.maxY, 1-bb.minZ); // S if(!horizontal_rotation) {
case 4: return new AxisAlignedBB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W switch(new_facing.get3DDataValue()) {
case 5: return new AxisAlignedBB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E case 0: return new AABB(1-bb.maxX, bb.minZ, bb.minY, 1-bb.minX, bb.maxZ, bb.maxY); // D
} case 1: return new AABB(1-bb.maxX, 1-bb.maxZ, 1-bb.maxY, 1-bb.minX, 1-bb.minZ, 1-bb.minY); // U
} case 2: return new AABB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb
return bb; case 3: return new AABB(1-bb.maxX, bb.minY, 1-bb.maxZ, 1-bb.minX, bb.maxY, 1-bb.minZ); // S
} case 4: return new AABB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W
case 5: return new AABB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E
public static final AxisAlignedBB[] getRotatedAABB(AxisAlignedBB[] bbs, Direction new_facing, boolean horizontal_rotation) }
{ } else {
final AxisAlignedBB[] transformed = new AxisAlignedBB[bbs.length]; switch(new_facing.get3DDataValue()) {
for(int i=0; i<bbs.length; ++i) transformed[i] = getRotatedAABB(bbs[i], new_facing, horizontal_rotation); case 0: return new AABB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // D --> bb
return transformed; case 1: return new AABB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // U --> bb
} case 2: return new AABB( bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb
case 3: return new AABB(1-bb.maxX, bb.minY, 1-bb.maxZ, 1-bb.minX, bb.maxY, 1-bb.minZ); // S
public static final AxisAlignedBB getYRotatedAABB(AxisAlignedBB bb, int clockwise_90deg_steps) case 4: return new AABB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W
{ case 5: return new AABB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E
final Direction direction_map[] = new Direction[]{Direction.NORTH,Direction.EAST,Direction.SOUTH,Direction.WEST}; }
return getRotatedAABB(bb, direction_map[(clockwise_90deg_steps+4096) & 0x03], true); }
} return bb;
}
public static final AxisAlignedBB[] getYRotatedAABB(AxisAlignedBB[] bbs, int clockwise_90deg_steps)
{ public static AABB[] getRotatedAABB(AABB[] bbs, Direction new_facing, boolean horizontal_rotation)
final AxisAlignedBB[] transformed = new AxisAlignedBB[bbs.length]; {
for(int i=0; i<bbs.length; ++i) transformed[i] = getYRotatedAABB(bbs[i], clockwise_90deg_steps); final AABB[] transformed = new AABB[bbs.length];
return transformed; for(int i=0; i<bbs.length; ++i) transformed[i] = getRotatedAABB(bbs[i], new_facing, horizontal_rotation);
} return transformed;
}
public static final AxisAlignedBB getMirroredAABB(AxisAlignedBB bb, Axis axis)
{ public static AABB getYRotatedAABB(AABB bb, int clockwise_90deg_steps)
switch(axis) { {
case X: return new AxisAlignedBB(1-bb.maxX, bb.minY, bb.minZ, 1-bb.minX, bb.maxY, bb.maxZ); final Direction[] direction_map = new Direction[]{Direction.NORTH,Direction.EAST,Direction.SOUTH,Direction.WEST};
case Y: return new AxisAlignedBB(bb.minX, 1-bb.maxY, bb.minZ, bb.maxX, 1-bb.minY, bb.maxZ); return getRotatedAABB(bb, direction_map[(clockwise_90deg_steps+4096) & 0x03], true);
case Z: return new AxisAlignedBB(bb.minX, bb.minY, 1-bb.maxZ, bb.maxX, bb.maxY, 1-bb.minZ); }
default: return bb;
} public static AABB[] getYRotatedAABB(AABB[] bbs, int clockwise_90deg_steps)
} {
final AABB[] transformed = new AABB[bbs.length];
public static final AxisAlignedBB[] getMirroredAABB(AxisAlignedBB[] bbs, Axis axis) for(int i=0; i<bbs.length; ++i) transformed[i] = getYRotatedAABB(bbs[i], clockwise_90deg_steps);
{ return transformed;
final AxisAlignedBB[] transformed = new AxisAlignedBB[bbs.length]; }
for(int i=0; i<bbs.length; ++i) transformed[i] = getMirroredAABB(bbs[i], axis);
return transformed; public static AABB getMirroredAABB(AABB bb, Direction.Axis axis)
} {
return switch (axis) {
public static final VoxelShape getUnionShape(AxisAlignedBB ... aabbs) case X -> new AABB(1 - bb.maxX, bb.minY, bb.minZ, 1 - bb.minX, bb.maxY, bb.maxZ);
{ case Y -> new AABB(bb.minX, 1 - bb.maxY, bb.minZ, bb.maxX, 1 - bb.minY, bb.maxZ);
VoxelShape shape = VoxelShapes.empty(); case Z -> new AABB(bb.minX, bb.minY, 1 - bb.maxZ, bb.maxX, bb.maxY, 1 - bb.minZ);
for(AxisAlignedBB aabb: aabbs) shape = VoxelShapes.join(shape, VoxelShapes.create(aabb), IBooleanFunction.OR); };
return shape; }
}
public static AABB[] getMirroredAABB(AABB[] bbs, Direction.Axis axis)
public static final VoxelShape getUnionShape(AxisAlignedBB[] ... aabb_list) {
{ final AABB[] transformed = new AABB[bbs.length];
VoxelShape shape = VoxelShapes.empty(); for(int i=0; i<bbs.length; ++i) transformed[i] = getMirroredAABB(bbs[i], axis);
for(AxisAlignedBB[] aabbs:aabb_list) { return transformed;
for(AxisAlignedBB aabb: aabbs) shape = VoxelShapes.joinUnoptimized(shape, VoxelShapes.create(aabb), IBooleanFunction.OR); }
}
return shape; public static VoxelShape getUnionShape(AABB ... aabbs)
} {
VoxelShape shape = Shapes.empty();
// ------------------------------------------------------------------------------------------------------------------- for(AABB aabb: aabbs) shape = Shapes.joinUnoptimized(shape, Shapes.create(aabb), BooleanOp.OR);
// JAR resource related return shape;
// ------------------------------------------------------------------------------------------------------------------- }
public static String loadResourceText(InputStream is) public static VoxelShape getUnionShape(AABB[] ... aabb_list)
{ {
try { VoxelShape shape = Shapes.empty();
if(is==null) return ""; for(AABB[] aabbs:aabb_list) {
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); for(AABB aabb: aabbs) shape = Shapes.joinUnoptimized(shape, Shapes.create(aabb), BooleanOp.OR);
return br.lines().collect(Collectors.joining("\n")); }
} catch(Throwable e) { return shape;
return ""; }
}
} public static final class BlockPosRange implements Iterable<BlockPos>
{
public static String loadResourceText(String path) private final int x0, x1, y0, y1, z0, z1;
{ return loadResourceText(Auxiliaries.class.getResourceAsStream(path)); }
public BlockPosRange(int x0, int y0, int z0, int x1, int y1, int z1)
public static void logGitVersion(String mod_name) {
{ this.x0 = Math.min(x0,x1); this.x1 = Math.max(x0,x1);
try { this.y0 = Math.min(y0,y1); this.y1 = Math.max(y0,y1);
// Done during construction to have an exact version in case of a crash while registering. this.z0 = Math.min(z0,z1); this.z1 = Math.max(z0,z1);
String version = Auxiliaries.loadResourceText("/.gitversion-" + modid).trim(); }
logInfo(mod_name+((version.isEmpty())?(" (dev build)"):(" GIT id #"+version)) + ".");
} catch(Throwable e) { public static BlockPosRange of(AABB range)
// (void)e; well, then not. Priority is not to get unneeded crashes because of version logging. {
} return new BlockPosRange(
} (int)Math.floor(range.minX),
} (int)Math.floor(range.minY),
(int)Math.floor(range.minZ),
(int)Math.floor(range.maxX-.0625),
(int)Math.floor(range.maxY-.0625),
(int)Math.floor(range.maxZ-.0625)
);
}
public int getXSize()
{ return x1-x0+1; }
public int getYSize()
{ return y1-y0+1; }
public int getZSize()
{ return z1-z0+1; }
public int getArea()
{ return getXSize() * getZSize(); }
public int getHeight()
{ return getYSize(); }
public int getVolume()
{ return getXSize() * getYSize() * getZSize(); }
public BlockPos byXZYIndex(int xyz_index)
{
final int xsz=getXSize(), ysz=getYSize(), zsz=getZSize();
xyz_index = xyz_index % (xsz*ysz*zsz);
final int y = xyz_index / (xsz*zsz);
xyz_index -= y * (xsz*zsz);
final int z = xyz_index / xsz;
xyz_index -= z * xsz;
final int x = xyz_index;
return new BlockPos(x0+x, y0+y, z0+z);
}
public BlockPos byXZIndex(int xz_index, int y_offset)
{
final int xsz=getXSize(), zsz=getZSize();
xz_index = xz_index % (xsz*zsz);
final int z = xz_index / xsz;
xz_index -= z * xsz;
final int x = xz_index;
return new BlockPos(x0+x, y0+y_offset, z0+z);
}
public static final class BlockRangeIterator implements Iterator<BlockPos>
{
private final BlockPosRange range_;
private int x,y,z;
public BlockRangeIterator(BlockPosRange range)
{ range_ = range; x=range.x0; y=range.y0; z=range.z0; }
@Override
public boolean hasNext()
{ return (z <= range_.z1); }
@Override
public BlockPos next()
{
if(!hasNext()) throw new NoSuchElementException();
final BlockPos pos = new BlockPos(x,y,z);
++x;
if(x > range_.x1) {
x = range_.x0;
++y;
if(y > range_.y1) {
y = range_.y0;
++z;
}
}
return pos;
}
}
@Override
public BlockRangeIterator iterator()
{ return new BlockRangeIterator(this); }
public Stream<BlockPos> stream()
{ return java.util.stream.StreamSupport.stream(spliterator(), false); }
}
// -------------------------------------------------------------------------------------------------------------------
// JAR resource related
// -------------------------------------------------------------------------------------------------------------------
public static String loadResourceText(InputStream is)
{
try {
if(is==null) return "";
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
return br.lines().collect(Collectors.joining("\n"));
} catch(Throwable e) {
return "";
}
}
public static String loadResourceText(String path)
{ return loadResourceText(Auxiliaries.class.getResourceAsStream(path)); }
public static void logGitVersion(String mod_name)
{
try {
// Done during construction to have an exact version in case of a crash while registering.
String version = Auxiliaries.loadResourceText("/.gitversion-" + modid).trim();
logInfo(mod_name+((version.isEmpty())?(" (dev build)"):(" GIT id #"+version)) + ".");
} catch(Throwable e) {
// (void)e; well, then not. Priority is not to get unneeded crashes because of version logging.
}
}
}

View file

@ -0,0 +1,364 @@
/*
* @file Recipes.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Recipe utility functionality.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.util.Tuple;
import net.minecraft.world.Container;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.EnchantmentInstance;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.ComposterBlock;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.brewing.BrewingRecipeRegistry;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.BiPredicate;
public class Crafting
{
// -------------------------------------------------------------------------------------------------------------------
public static final class CraftingGrid extends CraftingContainer
{
protected static final CraftingGrid instance3x3 = new CraftingGrid(3,3);
protected CraftingGrid(int width, int height)
{ super(new AbstractContainerMenu(null,0) { public boolean stillValid(Player player) { return false; } }, width, height); }
protected void fill(Container grid)
{ for(int i=0; i<getContainerSize(); ++i) setItem(i, i>=grid.getContainerSize() ? ItemStack.EMPTY : grid.getItem(i)); }
public List<CraftingRecipe> getRecipes(Level world, Container grid)
{ fill(grid); return world.getRecipeManager().getRecipesFor(RecipeType.CRAFTING, this, world); }
public List<ItemStack> getRemainingItems(Level world, Container grid, CraftingRecipe recipe)
{ fill(grid); return recipe.getRemainingItems(this); }
public ItemStack getCraftingResult(Level world, Container grid, CraftingRecipe recipe)
{ fill(grid); return recipe.assemble(this); }
}
/**
* Returns a Crafting recipe by registry name.
*/
public static Optional<CraftingRecipe> getCraftingRecipe(Level world, ResourceLocation recipe_id)
{
Recipe<?> recipe = world.getRecipeManager().byKey(recipe_id).orElse(null);
return (recipe instanceof CraftingRecipe) ? Optional.of((CraftingRecipe)recipe) : Optional.empty();
}
/**
* Returns a list of matching recipes by the first N slots (crafting grid slots) of the given inventory.
*/
public static List<CraftingRecipe> get3x3CraftingRecipes(Level world, Container crafting_grid_slots)
{ return CraftingGrid.instance3x3.getRecipes(world, crafting_grid_slots); }
/**
* Returns a recipe by the first N slots (crafting grid slots).
*/
public static Optional<CraftingRecipe> get3x3CraftingRecipe(Level world, Container crafting_grid_slots)
{ return get3x3CraftingRecipes(world, crafting_grid_slots).stream().findFirst(); }
/**
* Returns the result item of the recipe with the given grid layout.
*/
public static ItemStack get3x3CraftingResult(Level world, Container grid, CraftingRecipe recipe)
{ return CraftingGrid.instance3x3.getCraftingResult(world, grid, recipe); }
/**
* Returns the items remaining in the grid after crafting 3x3.
*/
public static List<ItemStack> get3x3RemainingItems(Level world, Container grid, CraftingRecipe recipe)
{ return CraftingGrid.instance3x3.getRemainingItems(world, grid, recipe); }
public static List<ItemStack> get3x3Placement(Level world, CraftingRecipe recipe, Container item_inventory, @Nullable Container crafting_grid)
{
final int width = 3;
final int height = 3;
if(!recipe.canCraftInDimensions(width,height)) return Collections.emptyList();
List<ItemStack> used = new ArrayList<>(); //NonNullList.withSize(width*height);
for(int i=width*height; i>0; --i) used.add(ItemStack.EMPTY);
Container check_inventory = Inventories.copyOf(item_inventory);
Inventories.InventoryRange source = new Inventories.InventoryRange(check_inventory);
final List<Ingredient> ingredients = recipe.getIngredients();
final List<ItemStack> preferred = new ArrayList<>(width*height);
if(crafting_grid != null) {
for(int i=0; i<crafting_grid.getContainerSize(); ++i) {
ItemStack stack = crafting_grid.getItem(i);
if(stack.isEmpty()) continue;
stack = stack.copy();
stack.setCount(1);
if(!source.extract(stack).isEmpty()) preferred.add(stack);
}
}
for(int i=0; i<ingredients.size(); ++i) {
final Ingredient ingredient = ingredients.get(i);
if(ingredient == Ingredient.EMPTY) continue;
ItemStack stack = preferred.stream().filter(ingredient).findFirst().orElse(ItemStack.EMPTY);
if(!stack.isEmpty()) {
preferred.remove(stack);
} else {
stack = source.stream().filter(ingredient).findFirst().orElse(ItemStack.EMPTY);
if(stack.isEmpty()) return Collections.emptyList();
stack = stack.copy();
stack.setCount(1);
if(source.extract(stack).isEmpty()) return Collections.emptyList();
}
used.set(i, stack);
}
if(recipe instanceof ShapedRecipe shaped) {
List<ItemStack> placement = NonNullList.withSize(width*height, ItemStack.EMPTY);
for(int row=0; row<shaped.getRecipeHeight(); ++row) {
for(int col=0; col<shaped.getRecipeWidth(); ++col) {
placement.set(width*row+col, used.get(row*shaped.getRecipeWidth()+col));
}
}
return placement;
} else {
return used;
}
}
// -------------------------------------------------------------------------------------------------------------------
/**
* Returns the recipe for a given input stack to smelt, null if there is no recipe
* for the given type (SMELTING,BLASTING,SMOKING, etc).
*/
public static <T extends Recipe<?>> Optional<AbstractCookingRecipe> getFurnaceRecipe(RecipeType<T> recipe_type, Level world, ItemStack input_stack)
{
if(input_stack.isEmpty()) {
return Optional.empty();
} else if(recipe_type == RecipeType.SMELTING) {
SimpleContainer inventory = new SimpleContainer(3);
inventory.setItem(0, input_stack);
SmeltingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.SMELTING, inventory, world).orElse(null);
return (recipe==null) ? Optional.empty() : Optional.of(recipe);
} else if(recipe_type == RecipeType.BLASTING) {
SimpleContainer inventory = new SimpleContainer(3);
inventory.setItem(0, input_stack);
BlastingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.BLASTING, inventory, world).orElse(null);
return (recipe==null) ? Optional.empty() : Optional.of(recipe);
} else if(recipe_type == RecipeType.SMOKING) {
SimpleContainer inventory = new SimpleContainer(3);
inventory.setItem(0, input_stack);
SmokingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.SMOKING, inventory, world).orElse(null);
return (recipe==null) ? Optional.empty() : Optional.of(recipe);
} else {
return Optional.empty();
}
}
public static <T extends Recipe<?>> int getSmeltingTimeNeeded(RecipeType<T> recipe_type, Level world, ItemStack stack)
{
if(stack.isEmpty()) return 0;
final int t = getFurnaceRecipe(recipe_type, world, stack).map((AbstractCookingRecipe::getCookingTime)).orElse(0);
return (t<=0) ? 200 : t;
}
/**
* Returns the burn time of an item when used as fuel, 0 if it is no fuel.
*/
public static int getFuelBurntime(Level world, ItemStack stack)
{
if(stack.isEmpty()) return 0;
int t = ForgeHooks.getBurnTime(stack, null);
return Math.max(t, 0);
}
/**
* Returns true if an item can be used as fuel.
*/
public static boolean isFuel(Level world, ItemStack stack)
{ return (getFuelBurntime(world, stack) > 0) || (stack.getItem()==Items.LAVA_BUCKET); }
/**
* Returns burntime and remaining stack then the item shall be used as fuel.
*/
public static Tuple<Integer,ItemStack> consumeFuel(Level world, ItemStack stack)
{
if(stack.isEmpty()) return new Tuple<>(0,stack);
int burnime = getFuelBurntime(world, stack);
if((stack.getItem()==Items.LAVA_BUCKET)) {
if(burnime <= 0) burnime = 1000*20;
return new Tuple<>(burnime,new ItemStack(Items.BUCKET));
} else if(burnime <= 0) {
return new Tuple<>(0,stack);
} else {
ItemStack left_over = stack.copy();
left_over.shrink(1);
return new Tuple<>(burnime,left_over);
}
}
// -------------------------------------------------------------------------------------------------------------------
/**
* Returns true if the item can be used as brewing fuel.
*/
public static boolean isBrewingFuel(Level world, ItemStack stack)
{ return (stack.getItem() == Items.BLAZE_POWDER) || (stack.getItem() == Items.BLAZE_ROD); }
/**
* Returns true if the item can be used as brewing ingredient.
*/
public static boolean isBrewingIngredient(Level world, ItemStack stack)
{ return BrewingRecipeRegistry.isValidIngredient(stack); }
/**
* Returns true if the item can be used as brewing bottle.
*/
public static boolean isBrewingInput(Level world, ItemStack stack)
{ return BrewingRecipeRegistry.isValidInput(stack); }
/**
* Returns the burn time for brewing of the given stack.
*/
public static int getBrewingFuelBurntime(Level world, ItemStack stack)
{
if(stack.isEmpty()) return 0;
if(stack.getItem() == Items.BLAZE_POWDER) return (400*20);
if(stack.getItem() == Items.BLAZE_ROD) return (400*40);
return 0;
}
/**
* Returns brewing burn time and remaining stack if the item shall be used as fuel.
*/
public static Tuple<Integer,ItemStack> consumeBrewingFuel(Level world, ItemStack stack)
{
int burntime = getBrewingFuelBurntime(world, stack);
if(burntime <= 0) return new Tuple<>(0, stack.copy());
stack = stack.copy();
stack.shrink(1);
return new Tuple<>(burntime, stack.isEmpty() ? ItemStack.EMPTY : stack);
}
public static final class BrewingOutput
{
public static final int DEFAULT_BREWING_TIME = 400;
public static final BrewingOutput EMPTY = new BrewingOutput(ItemStack.EMPTY, new SimpleContainer(1), new SimpleContainer(1), 0,0, DEFAULT_BREWING_TIME);
public final ItemStack item;
public final Container potionInventory;
public final Container ingredientInventory;
public final int potionSlot;
public final int ingredientSlot;
public final int brewTime;
public BrewingOutput(ItemStack output_potion, Container potion_inventory, Container ingredient_inventory, int potion_slot, int ingredient_slot, int time_needed)
{
item = output_potion;
potionInventory = potion_inventory;
ingredientInventory = ingredient_inventory;
potionSlot = potion_slot;
ingredientSlot = ingredient_slot;
brewTime = time_needed;
}
public static BrewingOutput find(Level world, Container potion_inventory, Container ingredient_inventory)
{
for(int potion_slot = 0; potion_slot<potion_inventory.getContainerSize(); ++potion_slot) {
final ItemStack pstack = potion_inventory.getItem(potion_slot);
if(!isBrewingInput(world, pstack)) continue;
for(int ingredient_slot = 0; ingredient_slot<ingredient_inventory.getContainerSize(); ++ingredient_slot) {
final ItemStack istack = ingredient_inventory.getItem(ingredient_slot);
if((!isBrewingIngredient(world, istack)) || (ingredient_slot == potion_slot) || (isBrewingFuel(world, istack))) continue;
final ItemStack result = BrewingRecipeRegistry.getOutput(pstack, istack);
if(result.isEmpty()) continue;
return new BrewingOutput(result, potion_inventory, ingredient_inventory, potion_slot, ingredient_slot, DEFAULT_BREWING_TIME);
}
}
return BrewingOutput.EMPTY;
}
}
// -------------------------------------------------------------------------------------------------------------------
public static double getCompostingChance(ItemStack stack)
{ return ComposterBlock.COMPOSTABLES.getOrDefault(stack.getItem(),0); }
// -------------------------------------------------------------------------------------------------------------------
/**
* Returns the enchtments bound to the given stack.
*/
public static Map<Enchantment, Integer> getEnchantmentsOnItem(Level world, ItemStack stack)
{ return (stack.isEmpty() || (stack.getTag()==null)) ? Collections.emptyMap() : EnchantmentHelper.getEnchantments(stack); }
/**
* Returns an enchanted book with the given enchantment, emtpy stack if not applicable.
*/
public static ItemStack getEnchantmentBook(Level world, Enchantment enchantment, int level)
{ return ((!enchantment.isAllowedOnBooks()) || (level <= 0)) ? ItemStack.EMPTY : EnchantedBookItem.createForEnchantment(new EnchantmentInstance(enchantment, level)); }
/**
* Returns the accumulated repair cost for the given enchantments.
*/
public static int getEnchantmentRepairCost(Level world, Map<Enchantment, Integer> enchantments)
{
int repair_cost = 0;
for(Map.Entry<Enchantment, Integer> e:enchantments.entrySet()) repair_cost = repair_cost * 2 + 1; // @see: RepairContainer.getNewRepairCost()
return repair_cost;
}
/**
* Trys to add an enchtment to the given stack, returns boolean success.
*/
public static boolean addEnchantmentOnItem(Level world, ItemStack stack, Enchantment enchantment, int level)
{
if(stack.isEmpty() || (level <= 0) || (!stack.isEnchantable()) || (level >= enchantment.getMaxLevel())) return false;
final Map<Enchantment, Integer> on_item = getEnchantmentsOnItem(world, stack);
if(on_item.keySet().stream().anyMatch(ench-> ench.isCompatibleWith(enchantment))) return false;
final ItemStack book = EnchantedBookItem.createForEnchantment(new EnchantmentInstance(enchantment, level));
if((!(stack.isBookEnchantable(book) && enchantment.isAllowedOnBooks())) && (!stack.canApplyAtEnchantingTable(enchantment)) && (!enchantment.canEnchant(stack))) return false;
final int existing_level = on_item.getOrDefault(enchantment, 0);
if(existing_level > 0) level = Mth.clamp(level+existing_level, 1, enchantment.getMaxLevel());
on_item.put(enchantment, level);
EnchantmentHelper.setEnchantments(on_item, stack);
stack.setRepairCost(getEnchantmentRepairCost(world, on_item));
return true;
}
/**
* Removes enchantments from a stack, returns the removed enchantments.
*/
public static Map<Enchantment, Integer> removeEnchantmentsOnItem(Level world, ItemStack stack, BiPredicate<Enchantment,Integer> filter)
{
if(stack.isEmpty()) return Collections.emptyMap();
final Map<Enchantment, Integer> on_item = getEnchantmentsOnItem(world, stack);
final Map<Enchantment, Integer> removed = new HashMap<>();
for(Map.Entry<Enchantment, Integer> e:on_item.entrySet()) {
if(filter.test(e.getKey(), e.getValue())) {
removed.put(e.getKey(), e.getValue());
}
}
for(Enchantment e:removed.keySet()) {
on_item.remove(e);
}
EnchantmentHelper.setEnchantments(on_item, stack);
stack.setRepairCost(getEnchantmentRepairCost(world, on_item));
return removed;
}
}

View file

@ -1,66 +0,0 @@
/*
* @file DataFixing.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Data fixing and mapping correction functionality encapsulation.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.RegistryEvent.MissingMappings.Mapping;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class DataFixing
{
private static String modid = "";
private static Map<String, String> item_registry_renaming = new HashMap<>();
private static Map<String, String> block_registry_renaming = new HashMap<>();
public static void init(String mod_id, @Nullable Map<String, String> item_renaming, @Nullable Map<String, String> block_renaming)
{
modid = mod_id;
block_registry_renaming = new HashMap<>();
item_registry_renaming = new HashMap<>();
if(item_renaming!=null) item_registry_renaming.putAll(item_renaming);
if(block_renaming!=null) { block_registry_renaming.putAll(block_renaming); item_registry_renaming.putAll(block_renaming); }
}
public static void onDataFixMissingItemMapping(net.minecraftforge.event.RegistryEvent.MissingMappings<Item> event)
{
// Handler registered in main mod event subscription.
for(Mapping<Item> mapping: event.getMappings()) {
if(mapping.key.getNamespace() != modid) continue;
final String rm = item_registry_renaming.getOrDefault(mapping.key.getPath(), "");
if(rm.isEmpty()) continue;
final Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(modid, rm));
if((item==null) || (item==Items.AIR)) continue;
mapping.remap(item);
}
}
public static void onDataFixMissingBlockMapping(net.minecraftforge.event.RegistryEvent.MissingMappings<Block> event)
{
// Handler registered in main mod event subscription.
for(Mapping<Block> mapping: event.getMappings()) {
if(mapping.key.getNamespace() != modid) continue;
final String rm = block_registry_renaming.getOrDefault(mapping.key.getPath(), "");
if(rm.isEmpty()) continue;
final Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(modid, rm));
if((block==null) || (block==Blocks.AIR)) continue;
mapping.remap(block);
}
}
// @todo: Find a way to register blockstate data fixing.
}

View file

@ -1,385 +1,390 @@
/* /*
* @file Fluidics.java * @file Fluidics.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* General fluid handling functionality. * General fluid handling functionality.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.core.BlockPos;
import net.minecraft.fluid.Fluid; import net.minecraft.core.Direction;
import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.Mth;
import net.minecraft.util.Direction; import net.minecraft.util.Tuple;
import net.minecraft.util.Hand; import net.minecraft.world.InteractionHand;
import net.minecraft.util.Tuple; import net.minecraft.world.entity.player.Player;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.item.ItemStack;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidActionResult; import net.minecraftforge.fluids.FluidActionResult;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.IFluidHandlerItem; import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
public class Fluidics public class Fluidics
{ {
/** public static class SingleTankFluidHandler implements IFluidHandler
* Dedicated fluid handler for a single tank. {
*/ private final IFluidTank tank_;
public static class SingleTankFluidHandler implements IFluidHandler public SingleTankFluidHandler(IFluidTank tank) { tank_ = tank; }
{ @Override public int getTanks() { return 1; }
private final IFluidTank tank_; @Override public FluidStack getFluidInTank(int tank) { return tank_.getFluid(); }
public SingleTankFluidHandler(IFluidTank tank) { tank_ = tank; } @Override public int getTankCapacity(int tank) { return tank_.getCapacity(); }
@Override public int getTanks() { return 1; } @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return tank_.isFluidValid(stack); }
@Override public FluidStack getFluidInTank(int tank) { return tank_.getFluid(); } @Override public int fill(FluidStack resource, FluidAction action) { return tank_.fill(resource, action); }
@Override public int getTankCapacity(int tank) { return tank_.getCapacity(); } @Override public FluidStack drain(FluidStack resource, FluidAction action) { return tank_.drain(resource, action); }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return tank_.isFluidValid(stack); } @Override public FluidStack drain(int maxDrain, FluidAction action) { return tank_.drain(maxDrain, action); }
@Override public int fill(FluidStack resource, FluidAction action) { return tank_.fill(resource, action); } }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return tank_.drain(resource, action); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return tank_.drain(maxDrain, action); } private static class SingleTankOutputFluidHandler implements IFluidHandler
} {
private final IFluidTank tank_;
private static class SingleTankOutputFluidHandler implements IFluidHandler public SingleTankOutputFluidHandler(IFluidTank tank) { tank_ = tank; }
{ @Override public int getTanks() { return 1; }
private final IFluidTank tank_; @Override public FluidStack getFluidInTank(int tank) { return tank_.getFluid().copy(); }
public SingleTankOutputFluidHandler(IFluidTank tank) { tank_ = tank; } @Override public int getTankCapacity(int tank) { return tank_.getCapacity(); }
@Override public int getTanks() { return 1; } @Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public FluidStack getFluidInTank(int tank) { return tank_.getFluid().copy(); } @Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public int getTankCapacity(int tank) { return tank_.getCapacity(); } @Override public FluidStack drain(FluidStack resource, FluidAction action) { return tank_.drain(resource, action); }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; } @Override public FluidStack drain(int maxDrain, FluidAction action) { return tank_.drain(maxDrain, action); }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; } }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return tank_.drain(resource, action); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return tank_.drain(maxDrain, action); } public static class Tank implements IFluidTank
} {
private Predicate<FluidStack> validator_ = ((e)->true);
/** private BiConsumer<Tank,Integer> interaction_notifier_ = ((tank,diff)->{});
* Simple fluid tank, validator concept according to reference implementation by KingLemming. private FluidStack fluid_ = FluidStack.EMPTY;
*/ private int capacity_;
public static class Tank implements IFluidTank private int fill_rate_;
{ private int drain_rate_;
private Predicate<FluidStack> validator_ = ((e)->true);
private BiConsumer<Tank,Integer> interaction_notifier_ = ((tank,diff)->{}); public Tank(int capacity)
private FluidStack fluid_ = FluidStack.EMPTY; { this(capacity, capacity, capacity); }
private int capacity_;
private int fill_rate_; public Tank(int capacity, int fill_rate, int drain_rate)
private int drain_rate_; { this(capacity, fill_rate, drain_rate, e->true); }
public Tank(int capacity) public Tank(int capacity, int fill_rate, int drain_rate, Predicate<FluidStack> validator)
{ this(capacity, capacity, capacity); } {
capacity_ = capacity;
public Tank(int capacity, int fill_rate, int drain_rate) setMaxFillRate(fill_rate);
{ this(capacity, fill_rate, drain_rate, e->true); } setMaxDrainRate(drain_rate);
setValidator(validator);
public Tank(int capacity, int fill_rate, int drain_rate, Predicate<FluidStack> validator) }
{
capacity_ = capacity; public Tank load(CompoundTag nbt)
setMaxFillRate(fill_rate); {
setMaxDrainRate(drain_rate); if(nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) {
setValidator(validator); setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
} } else {
clear();
public Tank load(CompoundNBT nbt) }
{ return this;
if(nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) { }
setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
} else { public CompoundTag save(CompoundTag nbt)
clear(); { if(!isEmpty()) { nbt.put("tank", fluid_.writeToNBT(new CompoundTag())); } return nbt; }
}
return this; public void reset()
} { clear(); }
public CompoundNBT save(CompoundNBT nbt) public Tank clear()
{ if(!isEmpty()) { nbt.put("tank", fluid_.writeToNBT(new CompoundNBT())); } return nbt; } { setFluid(null); return this; }
public void reset() public int getCapacity()
{ clear(); } { return capacity_; }
public Tank clear() public Tank setCapacity(int capacity)
{ setFluid(null); return this; } { capacity_ = capacity; return this; }
public int getCapacity() public int getMaxDrainRate()
{ return capacity_; } { return drain_rate_; }
public Tank setCapacity(int capacity) public Tank setMaxDrainRate(int rate)
{ capacity_ = capacity; return this; } { drain_rate_ = Mth.clamp(rate, 0, capacity_); return this; }
public int getMaxDrainRate() public int getMaxFillRate()
{ return drain_rate_; } { return fill_rate_; }
public Tank setMaxDrainRate(int rate) public Tank setMaxFillRate(int rate)
{ drain_rate_ = MathHelper.clamp(rate, 0, capacity_); return this; } { fill_rate_ = Mth.clamp(rate, 0, capacity_); return this; }
public int getMaxFillRate() public Tank setValidator(Predicate<FluidStack> validator)
{ return fill_rate_; } { validator_ = (validator!=null) ? validator : ((e)->true); return this; }
public Tank setMaxFillRate(int rate) public Tank setInteractionNotifier(BiConsumer<Tank,Integer> notifier)
{ fill_rate_ = MathHelper.clamp(rate, 0, capacity_); return this; } { interaction_notifier_ = (notifier!=null) ? notifier : ((tank,diff)->{}); return this; }
public Tank setValidator(Predicate<FluidStack> validator) public LazyOptional<IFluidHandler> createFluidHandler()
{ validator_ = (validator!=null) ? validator : ((e)->true); return this; } { return LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(this)); }
public Tank setInteractionNotifier(BiConsumer<Tank,Integer> notifier) public LazyOptional<IFluidHandler> createOutputFluidHandler()
{ interaction_notifier_ = (notifier!=null) ? notifier : ((tank,diff)->{}); return this; } { return LazyOptional.of(() -> new Fluidics.SingleTankOutputFluidHandler(this)); }
public LazyOptional<IFluidHandler> createFluidHandler() // IFluidTank ------------------------------------------------------------------------------------
{ return LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(this)); }
@Nonnull
public LazyOptional<IFluidHandler> createOutputFluidHandler() public FluidStack getFluid()
{ return LazyOptional.of(() -> new Fluidics.SingleTankOutputFluidHandler(this)); } { return fluid_; }
// IFluidTank ------------------------------------------------------------------------------------ public void setFluid(@Nullable FluidStack stack)
{ fluid_ = (stack==null) ? FluidStack.EMPTY : stack; }
@Nonnull
public FluidStack getFluid() public int getFluidAmount()
{ return fluid_; } { return fluid_.getAmount(); }
public void setFluid(@Nullable FluidStack stack) public boolean isEmpty()
{ fluid_ = (stack==null) ? FluidStack.EMPTY : stack; } { return fluid_.isEmpty(); }
public int getFluidAmount() public boolean isFull()
{ return fluid_.getAmount(); } { return getFluidAmount() >= getCapacity(); }
public boolean isEmpty() public boolean isFluidValid(FluidStack stack)
{ return fluid_.isEmpty(); } { return validator_.test(stack); }
public boolean isFull() public boolean isFluidEqual(FluidStack stack)
{ return getFluidAmount() >= getCapacity(); } { return (stack==null) ? (fluid_.isEmpty()) : fluid_.isFluidEqual(stack); }
public boolean isFluidValid(FluidStack stack) @Override
{ return validator_.test(stack); } public int fill(FluidStack fs, FluidAction action)
{
public boolean isFluidEqual(FluidStack stack) if((fs==null) || fs.isEmpty() || (!isFluidValid(fs))) {
{ return (stack==null) ? (fluid_.isEmpty()) : fluid_.isFluidEqual(stack); } return 0;
} else if(action.simulate()) {
@Override if(fluid_.isEmpty()) return Math.min(capacity_, fs.getAmount());
public int fill(FluidStack fs, FluidAction action) if(!fluid_.isFluidEqual(fs)) return 0;
{ return Math.min(capacity_-fluid_.getAmount(), fs.getAmount());
if((fs==null) || fs.isEmpty() || (!isFluidValid(fs))) { } else if(fluid_.isEmpty()) {
return 0; fluid_ = new FluidStack(fs, Math.min(capacity_, fs.getAmount()));
} else if(action.simulate()) { return fluid_.getAmount();
if(fluid_.isEmpty()) return Math.min(capacity_, fs.getAmount()); } else if(!fluid_.isFluidEqual(fs)) {
if(!fluid_.isFluidEqual(fs)) return 0; return 0;
return Math.min(capacity_-fluid_.getAmount(), fs.getAmount()); } else {
} else if(fluid_.isEmpty()) { int amount = capacity_ - fluid_.getAmount();
fluid_ = new FluidStack(fs, Math.min(capacity_, fs.getAmount())); if(fs.getAmount() < amount) {
return fluid_.getAmount(); fluid_.grow(fs.getAmount());
} else if(!fluid_.isFluidEqual(fs)) { amount = fs.getAmount();
return 0; } else {
} else { fluid_.setAmount(capacity_);
int amount = capacity_ - fluid_.getAmount(); }
if(fs.getAmount() < amount) { if(amount != 0) interaction_notifier_.accept(this, amount);
fluid_.grow(fs.getAmount()); return amount;
amount = fs.getAmount(); }
} else { }
fluid_.setAmount(capacity_);
} @Nonnull
if(amount != 0) interaction_notifier_.accept(this, amount); public FluidStack drain(int maxDrain)
return amount; { return drain(maxDrain, FluidAction.EXECUTE); }
}
} @Nonnull
@Override
@Nonnull public FluidStack drain(FluidStack fs, FluidAction action)
public FluidStack drain(int maxDrain) { return ((fs.isEmpty()) || (!fs.isFluidEqual(fluid_))) ? FluidStack.EMPTY : drain(fs.getAmount(), action); }
{ return drain(maxDrain, FluidAction.EXECUTE); }
@Nonnull
@Nonnull @Override
@Override public FluidStack drain(int maxDrain, FluidAction action)
public FluidStack drain(FluidStack fs, FluidAction action) {
{ return ((fs.isEmpty()) || (!fs.isFluidEqual(fluid_))) ? FluidStack.EMPTY : drain(fs.getAmount(), action); } final int amount = Math.min(fluid_.getAmount(), maxDrain);
final FluidStack stack = new FluidStack(fluid_, amount);
@Nonnull if((amount > 0) && action.execute()) {
@Override fluid_.shrink(amount);
public FluidStack drain(int maxDrain, FluidAction action) if(fluid_.isEmpty()) fluid_ = FluidStack.EMPTY;
{ if(amount != 0) interaction_notifier_.accept(this, -amount);
final int amount = Math.min(fluid_.getAmount(), maxDrain); }
final FluidStack stack = new FluidStack(fluid_, amount); return stack;
if((amount > 0) && action.execute()) { }
fluid_.shrink(amount); }
if(fluid_.isEmpty()) fluid_ = FluidStack.EMPTY;
if(amount != 0) interaction_notifier_.accept(this, -amount); // -------------------------------------------------------------------------------------------------------------------
}
return stack; public static @Nullable IFluidHandler handler(Level world, BlockPos pos, @Nullable Direction side)
} { return FluidUtil.getFluidHandler(world, pos, side).orElse(null); }
}
/**
/** * Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
* Fills or drains items with fluid handlers from or into tile blocks with fluid handlers. */
*/ public static boolean manualFluidHandlerInteraction(Level world, BlockPos pos, @Nullable Direction side, Player player, InteractionHand hand)
public static boolean manualFluidHandlerInteraction(World world, BlockPos pos, @Nullable Direction side, PlayerEntity player, Hand hand) { return manualTrackedFluidHandlerInteraction(world, pos, side, player, hand) != null; }
{ return manualTrackedFluidHandlerInteraction(world, pos, side, player, hand) != null; }
public static boolean manualFluidHandlerInteraction(Player player, InteractionHand hand, IFluidHandler handler)
/** { return FluidUtil.interactWithFluidHandler(player, hand, handler); }
* Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
* Returns the fluid and (possibly negative) amount that transferred from the item into the block. /**
*/ * Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
public static @Nullable Tuple<Fluid, Integer> manualTrackedFluidHandlerInteraction(World world, BlockPos pos, @Nullable Direction side, PlayerEntity player, Hand hand) * Returns the fluid and (possibly negative) amount that transferred from the item into the block.
{ */
if(world.isClientSide()) return null; public static @Nullable Tuple<Fluid, Integer> manualTrackedFluidHandlerInteraction(Level world, BlockPos pos, @Nullable Direction side, Player player, InteractionHand hand)
final ItemStack held = player.getItemInHand(hand); {
if(held.isEmpty()) return null; if(world.isClientSide()) return null;
final IFluidHandler fh = FluidUtil.getFluidHandler(world, pos, side).orElse(null); final ItemStack held = player.getItemInHand(hand);
if(fh==null) return null; if(held.isEmpty()) return null;
final IItemHandler ih = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null); final IFluidHandler fh = handler(world, pos, side);
if(ih==null) return null; if(fh==null) return null;
FluidActionResult far = FluidUtil.tryFillContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true); final IItemHandler ih = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null);
if(!far.isSuccess()) far = FluidUtil.tryEmptyContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true); if(ih==null) return null;
if(!far.isSuccess()) return null; FluidActionResult far = FluidUtil.tryFillContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
final ItemStack rstack = far.getResult().copy(); if(!far.isSuccess()) far = FluidUtil.tryEmptyContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
player.setItemInHand(hand, far.getResult()); if(!far.isSuccess()) return null;
final IFluidHandler fh_before = FluidUtil.getFluidHandler(held).orElse(null); final ItemStack rstack = far.getResult().copy();
final IFluidHandler fh_after = FluidUtil.getFluidHandler(rstack).orElse(null); player.setItemInHand(hand, far.getResult());
if((fh_before==null) || (fh_after==null) || (fh_after.getTanks()!=fh_before.getTanks())) return null; // should not be, but y'never know. final IFluidHandler fh_before = FluidUtil.getFluidHandler(held).orElse(null);
for(int i=0; i<fh_before.getTanks(); ++i) { final IFluidHandler fh_after = FluidUtil.getFluidHandler(rstack).orElse(null);
final int vol_before = fh_before.getFluidInTank(i).getAmount(); if((fh_before==null) || (fh_after==null) || (fh_after.getTanks()!=fh_before.getTanks())) return null; // should not be, but y'never know.
final int vol_after = fh_after.getFluidInTank(i).getAmount(); for(int i=0; i<fh_before.getTanks(); ++i) {
if(vol_before != vol_after) { final int vol_before = fh_before.getFluidInTank(i).getAmount();
return new Tuple<>( final int vol_after = fh_after.getFluidInTank(i).getAmount();
(vol_before>0) ? (fh_before.getFluidInTank(i).getFluid()) : (fh_after.getFluidInTank(i).getFluid()), if(vol_before != vol_after) {
(vol_before - vol_after) return new Tuple<>(
); (vol_before>0) ? (fh_before.getFluidInTank(i).getFluid()) : (fh_after.getFluidInTank(i).getFluid()),
} (vol_before - vol_after)
} );
return null; }
} }
return null;
public static int fill(World world, BlockPos pos, Direction side, FluidStack fs, FluidAction action) }
{
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos, side).orElse(null); public static boolean manualFluidHandlerInteraction(Player player, InteractionHand hand, Level world, BlockPos pos, @Nullable Direction side)
return (fh==null) ? (0) : (fh.fill(fs, action)); { return FluidUtil.interactWithFluidHandler(player, hand, world, pos, side); }
}
public static int fill(Level world, BlockPos pos, Direction side, FluidStack fs, FluidAction action)
public static int fill(World world, BlockPos pos, Direction side, FluidStack fs) {
{ return fill(world, pos, side, fs, FluidAction.EXECUTE); } IFluidHandler fh = FluidUtil.getFluidHandler(world, pos, side).orElse(null);
return (fh==null) ? (0) : (fh.fill(fs, action));
/** }
* Fluid tank access when itemized.
*/ public static int fill(Level world, BlockPos pos, Direction side, FluidStack fs)
public static class FluidContainerItemCapabilityWrapper implements IFluidHandlerItem, ICapabilityProvider { return fill(world, pos, side, fs, FluidAction.EXECUTE); }
{
private final LazyOptional<IFluidHandlerItem> handler_ = LazyOptional.of(()->this); /**
private final Function<ItemStack, CompoundNBT> nbt_getter_; * Fluid tank access when itemized.
private final BiConsumer<ItemStack, CompoundNBT> nbt_setter_; */
private final Predicate<FluidStack> validator_; public static class FluidContainerItemCapabilityWrapper implements IFluidHandlerItem, ICapabilityProvider
private final ItemStack container_; {
private final int capacity_; private final LazyOptional<IFluidHandlerItem> handler_ = LazyOptional.of(()->this);
private final int transfer_rate_; private final Function<ItemStack, CompoundTag> nbt_getter_;
private final BiConsumer<ItemStack, CompoundTag> nbt_setter_;
public FluidContainerItemCapabilityWrapper(ItemStack container, int capacity, int transfer_rate, private final Predicate<FluidStack> validator_;
Function<ItemStack, CompoundNBT> nbt_getter, private final ItemStack container_;
BiConsumer<ItemStack, CompoundNBT> nbt_setter, private final int capacity_;
Predicate<FluidStack> validator) private final int transfer_rate_;
{
container_ = container; public FluidContainerItemCapabilityWrapper(ItemStack container, int capacity, int transfer_rate,
capacity_ = capacity; Function<ItemStack, CompoundTag> nbt_getter,
transfer_rate_ = transfer_rate; BiConsumer<ItemStack, CompoundTag> nbt_setter,
nbt_getter_ = nbt_getter; Predicate<FluidStack> validator)
nbt_setter_ = nbt_setter; {
validator_ = (validator!=null) ? validator : (e->true); container_ = container;
} capacity_ = capacity;
transfer_rate_ = transfer_rate;
@Override nbt_getter_ = nbt_getter;
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction side) nbt_setter_ = nbt_setter;
{ return (capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY) ? handler_.cast() : LazyOptional.empty(); } validator_ = (validator!=null) ? validator : (e->true);
}
protected FluidStack readnbt()
{ @Override
final CompoundNBT nbt = nbt_getter_.apply(container_); public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction side)
return ((nbt==null) || (nbt.isEmpty())) ? FluidStack.EMPTY : FluidStack.loadFluidStackFromNBT(nbt); { return (capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY) ? handler_.cast() : LazyOptional.empty(); }
}
protected FluidStack readnbt()
protected void writenbt(FluidStack fs) {
{ final CompoundTag nbt = nbt_getter_.apply(container_);
CompoundNBT nbt = new CompoundNBT(); return ((nbt==null) || (nbt.isEmpty())) ? FluidStack.EMPTY : FluidStack.loadFluidStackFromNBT(nbt);
if(!fs.isEmpty()) fs.writeToNBT(nbt); }
nbt_setter_.accept(container_, nbt);
} protected void writenbt(FluidStack fs)
{
@Override CompoundTag nbt = new CompoundTag();
public ItemStack getContainer() if(!fs.isEmpty()) fs.writeToNBT(nbt);
{ return container_; } nbt_setter_.accept(container_, nbt);
}
@Override
public int getTanks() @Override
{ return 1; } public ItemStack getContainer()
{ return container_; }
@Override
public FluidStack getFluidInTank(int tank) @Override
{ return readnbt(); } public int getTanks()
{ return 1; }
@Override
public int getTankCapacity(int tank) @Override
{ return capacity_; } public FluidStack getFluidInTank(int tank)
{ return readnbt(); }
@Override
public boolean isFluidValid(int tank, FluidStack fs) @Override
{ return isFluidValid(fs); } public int getTankCapacity(int tank)
{ return capacity_; }
public boolean isFluidValid(FluidStack fs)
{ return validator_.test(fs); } @Override
public boolean isFluidValid(int tank, FluidStack fs)
@Override { return isFluidValid(fs); }
public int fill(FluidStack fs, FluidAction action)
{ public boolean isFluidValid(FluidStack fs)
if((fs.isEmpty()) || (!isFluidValid(fs) || (container_.getCount()!=1))) return 0; { return validator_.test(fs); }
FluidStack tank = readnbt();
final int amount = Math.min(Math.min(fs.getAmount(),transfer_rate_), capacity_-tank.getAmount()); @Override
if(amount <= 0) return 0; public int fill(FluidStack fs, FluidAction action)
if(tank.isEmpty()) { {
if(action.execute()) { if((fs.isEmpty()) || (!isFluidValid(fs) || (container_.getCount()!=1))) return 0;
tank = new FluidStack(fs.getFluid(), amount, fs.getTag()); FluidStack tank = readnbt();
writenbt(tank); final int amount = Math.min(Math.min(fs.getAmount(),transfer_rate_), capacity_-tank.getAmount());
} if(amount <= 0) return 0;
} else { if(tank.isEmpty()) {
if(!tank.isFluidEqual(fs)) { if(action.execute()) {
return 0; tank = new FluidStack(fs.getFluid(), amount, fs.getTag());
} else if(action.execute()) { writenbt(tank);
tank.grow(amount); }
writenbt(tank); } else {
} if(!tank.isFluidEqual(fs)) {
} return 0;
return amount; } else if(action.execute()) {
} tank.grow(amount);
writenbt(tank);
@Override }
public FluidStack drain(FluidStack fs, FluidAction action) }
{ return amount;
if((fs.isEmpty()) || (container_.getCount()!=1)) return FluidStack.EMPTY; }
final FluidStack tank = readnbt();
if((!tank.isEmpty()) && (!tank.isFluidEqual(fs))) return FluidStack.EMPTY; @Override
return drain(fs.getAmount(), action); public FluidStack drain(FluidStack fs, FluidAction action)
} {
if((fs.isEmpty()) || (container_.getCount()!=1)) return FluidStack.EMPTY;
@Override final FluidStack tank = readnbt();
public FluidStack drain(int max, FluidAction action) if((!tank.isEmpty()) && (!tank.isFluidEqual(fs))) return FluidStack.EMPTY;
{ return drain(fs.getAmount(), action);
if((max<=0) || (container_.getCount()!=1)) return FluidStack.EMPTY; }
FluidStack tank = readnbt();
if(tank.isEmpty()) return FluidStack.EMPTY; @Override
final int amount = Math.min(Math.min(tank.getAmount(), max), transfer_rate_); public FluidStack drain(int max, FluidAction action)
final FluidStack fs = tank.copy(); {
fs.setAmount(amount); if((max<=0) || (container_.getCount()!=1)) return FluidStack.EMPTY;
if(action.execute()) { tank.shrink(amount); writenbt(tank); } FluidStack tank = readnbt();
return fs; if(tank.isEmpty()) return FluidStack.EMPTY;
} final int amount = Math.min(Math.min(tank.getAmount(), max), transfer_rate_);
} final FluidStack fs = tank.copy();
fs.setAmount(amount);
} if(action.execute()) { tank.shrink(amount); writenbt(tank); }
return fs;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,309 +1,309 @@
/* /*
* @file Networking.java * @file Networking.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Main client/server message handling. * Main client/server message handling.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.util.ResourceLocation; import net.minecraft.core.BlockPos;
import net.minecraft.util.text.ITextComponent; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.common.util.FakePlayer; import net.minecraft.network.chat.Component;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tileentity.TileEntity; import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.world.level.Level;
import net.minecraft.util.math.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.inventory.container.Container; import net.minecraftforge.common.util.FakePlayer;
import net.minecraft.network.PacketBuffer; import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fmllegacy.network.NetworkDirection;
import net.minecraftforge.fml.network.simple.SimpleChannel; import net.minecraftforge.fmllegacy.network.NetworkRegistry;
import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fmllegacy.network.simple.SimpleChannel;
import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class Networking
{ public class Networking
private static final String PROTOCOL = "1"; {
private static SimpleChannel DEFAULT_CHANNEL; private static final String PROTOCOL = "1";
private static SimpleChannel DEFAULT_CHANNEL;
public static void init(String modid)
{ public static void init(String modid)
DEFAULT_CHANNEL = NetworkRegistry.ChannelBuilder {
.named(new ResourceLocation(modid, "default_ch")) DEFAULT_CHANNEL = NetworkRegistry.ChannelBuilder
.clientAcceptedVersions(PROTOCOL::equals).serverAcceptedVersions(PROTOCOL::equals).networkProtocolVersion(() -> PROTOCOL) .named(new ResourceLocation(modid, "default_ch"))
.simpleChannel(); .clientAcceptedVersions(PROTOCOL::equals).serverAcceptedVersions(PROTOCOL::equals).networkProtocolVersion(() -> PROTOCOL)
int discr = -1; .simpleChannel();
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyClientToServer.class, PacketTileNotifyClientToServer::compose, PacketTileNotifyClientToServer::parse, PacketTileNotifyClientToServer.Handler::handle); int discr = -1;
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle); DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyClientToServer.class, PacketTileNotifyClientToServer::compose, PacketTileNotifyClientToServer::parse, PacketTileNotifyClientToServer.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.Handler::handle); DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncServerToClient.class, PacketContainerSyncServerToClient::compose, PacketContainerSyncServerToClient::parse, PacketContainerSyncServerToClient.Handler::handle); DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, OverlayTextMessage.class, OverlayTextMessage::compose, OverlayTextMessage::parse, OverlayTextMessage.Handler::handle); DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncServerToClient.class, PacketContainerSyncServerToClient::compose, PacketContainerSyncServerToClient::parse, PacketContainerSyncServerToClient.Handler::handle);
} DEFAULT_CHANNEL.registerMessage(++discr, OverlayTextMessage.class, OverlayTextMessage::compose, OverlayTextMessage::parse, OverlayTextMessage.Handler::handle);
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity notifications //--------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------- // Tile entity notifications
//--------------------------------------------------------------------------------------------------------------------
public interface IPacketTileNotifyReceiver
{ public interface IPacketTileNotifyReceiver
default void onServerPacketReceived(CompoundNBT nbt) {} {
default void onClientPacketReceived(PlayerEntity player, CompoundNBT nbt) {} default void onServerPacketReceived(CompoundTag nbt) {}
} default void onClientPacketReceived(Player player, CompoundTag nbt) {}
}
public static class PacketTileNotifyClientToServer
{ public static class PacketTileNotifyClientToServer
CompoundNBT nbt = null; {
BlockPos pos = BlockPos.ZERO; CompoundTag nbt = null;
BlockPos pos = BlockPos.ZERO;
public static void sendToServer(BlockPos pos, CompoundNBT nbt)
{ if((pos!=null) && (nbt!=null)) DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(pos, nbt)); } public static void sendToServer(BlockPos pos, CompoundTag nbt)
{ if((pos!=null) && (nbt!=null)) DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(pos, nbt)); }
public static void sendToServer(TileEntity te, CompoundNBT nbt)
{ if((te!=null) && (nbt!=null)) DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(te, nbt)); } public static void sendToServer(BlockEntity te, CompoundTag nbt)
{ if((te!=null) && (nbt!=null)) DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(te, nbt)); }
public PacketTileNotifyClientToServer()
{} public PacketTileNotifyClientToServer()
{}
public PacketTileNotifyClientToServer(BlockPos pos, CompoundNBT nbt)
{ this.nbt = nbt; this.pos = pos; } public PacketTileNotifyClientToServer(BlockPos pos, CompoundTag nbt)
{ this.nbt = nbt; this.pos = pos; }
public PacketTileNotifyClientToServer(TileEntity te, CompoundNBT nbt)
{ this.nbt = nbt; pos = te.getBlockPos(); } public PacketTileNotifyClientToServer(BlockEntity te, CompoundTag nbt)
{ this.nbt = nbt; pos = te.getBlockPos(); }
public static PacketTileNotifyClientToServer parse(final PacketBuffer buf)
{ return new PacketTileNotifyClientToServer(buf.readBlockPos(), buf.readNbt()); } public static PacketTileNotifyClientToServer parse(final FriendlyByteBuf buf)
{ return new PacketTileNotifyClientToServer(buf.readBlockPos(), buf.readNbt()); }
public static void compose(final PacketTileNotifyClientToServer pkt, final PacketBuffer buf)
{ buf.writeBlockPos(pkt.pos); buf.writeNbt(pkt.nbt); } public static void compose(final PacketTileNotifyClientToServer pkt, final FriendlyByteBuf buf)
{ buf.writeBlockPos(pkt.pos); buf.writeNbt(pkt.nbt); }
public static class Handler
{ public static class Handler
public static void handle(final PacketTileNotifyClientToServer pkt, final Supplier<NetworkEvent.Context> ctx) {
{ public static void handle(final PacketTileNotifyClientToServer pkt, final Supplier<NetworkEvent.Context> ctx)
ctx.get().enqueueWork(() -> { {
PlayerEntity player = ctx.get().getSender(); ctx.get().enqueueWork(() -> {
World world = player.level; Player player = ctx.get().getSender();
if(world == null) return; if(player==null) return;
final TileEntity te = world.getBlockEntity(pkt.pos); Level world = player.level;
if(!(te instanceof IPacketTileNotifyReceiver)) return; final BlockEntity te = world.getBlockEntity(pkt.pos);
((IPacketTileNotifyReceiver)te).onClientPacketReceived(ctx.get().getSender(), pkt.nbt); if(!(te instanceof IPacketTileNotifyReceiver)) return;
}); ((IPacketTileNotifyReceiver)te).onClientPacketReceived(ctx.get().getSender(), pkt.nbt);
ctx.get().setPacketHandled(true); });
} ctx.get().setPacketHandled(true);
} }
} }
}
public static class PacketTileNotifyServerToClient
{ public static class PacketTileNotifyServerToClient
CompoundNBT nbt = null; {
BlockPos pos = BlockPos.ZERO; CompoundTag nbt = null;
BlockPos pos = BlockPos.ZERO;
public static void sendToPlayer(PlayerEntity player, TileEntity te, CompoundNBT nbt)
{ public static void sendToPlayer(Player player, BlockEntity te, CompoundTag nbt)
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (te==null) || (nbt==null)) return; {
DEFAULT_CHANNEL.sendTo(new PacketTileNotifyServerToClient(te, nbt), ((ServerPlayerEntity)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT); if((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (te==null) || (nbt==null)) return;
} DEFAULT_CHANNEL.sendTo(new PacketTileNotifyServerToClient(te, nbt), ((ServerPlayer)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
public static void sendToPlayers(TileEntity te, CompoundNBT nbt)
{ public static void sendToPlayers(BlockEntity te, CompoundTag nbt)
if(te==null || te.getLevel().isClientSide()) return; {
for(PlayerEntity player: te.getLevel().players()) sendToPlayer(player, te, nbt); if(te==null || te.getLevel()==null) return;
} for(Player player: te.getLevel().players()) sendToPlayer(player, te, nbt);
}
public PacketTileNotifyServerToClient()
{} public PacketTileNotifyServerToClient()
{}
public PacketTileNotifyServerToClient(BlockPos pos, CompoundNBT nbt)
{ this.nbt=nbt; this.pos=pos; } public PacketTileNotifyServerToClient(BlockPos pos, CompoundTag nbt)
{ this.nbt=nbt; this.pos=pos; }
public PacketTileNotifyServerToClient(TileEntity te, CompoundNBT nbt)
{ this.nbt=nbt; pos=te.getBlockPos(); } public PacketTileNotifyServerToClient(BlockEntity te, CompoundTag nbt)
{ this.nbt=nbt; pos=te.getBlockPos(); }
public static PacketTileNotifyServerToClient parse(final PacketBuffer buf)
{ return new PacketTileNotifyServerToClient(buf.readBlockPos(), buf.readNbt()); } public static PacketTileNotifyServerToClient parse(final FriendlyByteBuf buf)
{ return new PacketTileNotifyServerToClient(buf.readBlockPos(), buf.readNbt()); }
public static void compose(final PacketTileNotifyServerToClient pkt, final PacketBuffer buf)
{ buf.writeBlockPos(pkt.pos); buf.writeNbt(pkt.nbt); } public static void compose(final PacketTileNotifyServerToClient pkt, final FriendlyByteBuf buf)
{ buf.writeBlockPos(pkt.pos); buf.writeNbt(pkt.nbt); }
public static class Handler
{ public static class Handler
public static void handle(final PacketTileNotifyServerToClient pkt, final Supplier<NetworkEvent.Context> ctx) {
{ public static void handle(final PacketTileNotifyServerToClient pkt, final Supplier<NetworkEvent.Context> ctx)
ctx.get().enqueueWork(() -> { {
World world = SidedProxy.getWorldClientSide(); ctx.get().enqueueWork(() -> {
if(world == null) return; Level world = SidedProxy.getWorldClientSide();
final TileEntity te = world.getBlockEntity(pkt.pos); if(world == null) return;
if(!(te instanceof IPacketTileNotifyReceiver)) return; final BlockEntity te = world.getBlockEntity(pkt.pos);
((IPacketTileNotifyReceiver)te).onServerPacketReceived(pkt.nbt); if(!(te instanceof IPacketTileNotifyReceiver)) return;
}); ((IPacketTileNotifyReceiver)te).onServerPacketReceived(pkt.nbt);
ctx.get().setPacketHandled(true); });
} ctx.get().setPacketHandled(true);
} }
} }
}
//--------------------------------------------------------------------------------------------------------------------
// (GUI) Container synchrsonisation //--------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------- // (GUI) Container synchronization
//--------------------------------------------------------------------------------------------------------------------
public interface INetworkSynchronisableContainer
{ public interface INetworkSynchronisableContainer
void onServerPacketReceived(int windowId, CompoundNBT nbt); {
void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt); void onServerPacketReceived(int windowId, CompoundTag nbt);
} void onClientPacketReceived(int windowId, Player player, CompoundTag nbt);
}
public static class PacketContainerSyncClientToServer
{ public static class PacketContainerSyncClientToServer
int id = -1; {
CompoundNBT nbt = null; int id = -1;
CompoundTag nbt = null;
public static void sendToServer(int windowId, CompoundNBT nbt)
{ if(nbt!=null) DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(windowId, nbt)); } public static void sendToServer(int windowId, CompoundTag nbt)
{ if(nbt!=null) DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(windowId, nbt)); }
public static void sendToServer(Container container, CompoundNBT nbt)
{ if(nbt!=null) DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(container.containerId, nbt)); } public static void sendToServer(AbstractContainerMenu container, CompoundTag nbt)
{ if(nbt!=null) DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(container.containerId, nbt)); }
public PacketContainerSyncClientToServer()
{} public PacketContainerSyncClientToServer()
{}
public PacketContainerSyncClientToServer(int id, CompoundNBT nbt)
{ this.nbt = nbt; this.id = id; } public PacketContainerSyncClientToServer(int id, CompoundTag nbt)
{ this.nbt = nbt; this.id = id; }
public static PacketContainerSyncClientToServer parse(final PacketBuffer buf)
{ return new PacketContainerSyncClientToServer(buf.readInt(), buf.readNbt()); } public static PacketContainerSyncClientToServer parse(final FriendlyByteBuf buf)
{ return new PacketContainerSyncClientToServer(buf.readInt(), buf.readNbt()); }
public static void compose(final PacketContainerSyncClientToServer pkt, final PacketBuffer buf)
{ buf.writeInt(pkt.id); buf.writeNbt(pkt.nbt); } public static void compose(final PacketContainerSyncClientToServer pkt, final FriendlyByteBuf buf)
{ buf.writeInt(pkt.id); buf.writeNbt(pkt.nbt); }
public static class Handler
{ public static class Handler
public static void handle(final PacketContainerSyncClientToServer pkt, final Supplier<NetworkEvent.Context> ctx) {
{ public static void handle(final PacketContainerSyncClientToServer pkt, final Supplier<NetworkEvent.Context> ctx)
ctx.get().enqueueWork(() -> { {
PlayerEntity player = ctx.get().getSender(); ctx.get().enqueueWork(() -> {
if(!(player.containerMenu instanceof INetworkSynchronisableContainer)) return; Player player = ctx.get().getSender();
if(player.containerMenu.containerId != pkt.id) return; if((player==null) || !(player.containerMenu instanceof INetworkSynchronisableContainer)) return;
((INetworkSynchronisableContainer)player.containerMenu).onClientPacketReceived(pkt.id, player,pkt.nbt); if(player.containerMenu.containerId != pkt.id) return;
}); ((INetworkSynchronisableContainer)player.containerMenu).onClientPacketReceived(pkt.id, player,pkt.nbt);
ctx.get().setPacketHandled(true); });
} ctx.get().setPacketHandled(true);
} }
} }
}
public static class PacketContainerSyncServerToClient
{ public static class PacketContainerSyncServerToClient
int id = -1; {
CompoundNBT nbt = null; int id = -1;
CompoundTag nbt = null;
public static void sendToPlayer(PlayerEntity player, int windowId, CompoundNBT nbt)
{ public static void sendToPlayer(Player player, int windowId, CompoundTag nbt)
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (nbt==null)) return; {
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(windowId, nbt), ((ServerPlayerEntity)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT); if((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (nbt==null)) return;
} DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(windowId, nbt), ((ServerPlayer)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
public static void sendToPlayer(PlayerEntity player, Container container, CompoundNBT nbt)
{ public static void sendToPlayer(Player player, AbstractContainerMenu container, CompoundTag nbt)
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (nbt==null)) return; {
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(container.containerId, nbt), ((ServerPlayerEntity)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT); if((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (nbt==null)) return;
} DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(container.containerId, nbt), ((ServerPlayer)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
public static <C extends Container & INetworkSynchronisableContainer>
void sendToListeners(World world, C container, CompoundNBT nbt) public static <C extends AbstractContainerMenu & INetworkSynchronisableContainer>
{ void sendToListeners(Level world, C container, CompoundTag nbt)
for(PlayerEntity player: world.players()) { {
if(player.containerMenu.containerId != container.containerId) continue; for(Player player: world.players()) {
sendToPlayer(player, container.containerId, nbt); if(player.containerMenu.containerId != container.containerId) continue;
} sendToPlayer(player, container.containerId, nbt);
} }
}
public PacketContainerSyncServerToClient()
{} public PacketContainerSyncServerToClient()
{}
public PacketContainerSyncServerToClient(int id, CompoundNBT nbt)
{ this.nbt=nbt; this.id=id; } public PacketContainerSyncServerToClient(int id, CompoundTag nbt)
{ this.nbt=nbt; this.id=id; }
public static PacketContainerSyncServerToClient parse(final PacketBuffer buf)
{ return new PacketContainerSyncServerToClient(buf.readInt(), buf.readNbt()); } public static PacketContainerSyncServerToClient parse(final FriendlyByteBuf buf)
{ return new PacketContainerSyncServerToClient(buf.readInt(), buf.readNbt()); }
public static void compose(final PacketContainerSyncServerToClient pkt, final PacketBuffer buf)
{ buf.writeInt(pkt.id); buf.writeNbt(pkt.nbt); } public static void compose(final PacketContainerSyncServerToClient pkt, final FriendlyByteBuf buf)
{ buf.writeInt(pkt.id); buf.writeNbt(pkt.nbt); }
public static class Handler
{ public static class Handler
public static void handle(final PacketContainerSyncServerToClient pkt, final Supplier<NetworkEvent.Context> ctx) {
{ public static void handle(final PacketContainerSyncServerToClient pkt, final Supplier<NetworkEvent.Context> ctx)
ctx.get().enqueueWork(() -> { {
PlayerEntity player = SidedProxy.getPlayerClientSide(); ctx.get().enqueueWork(() -> {
if(!(player.containerMenu instanceof INetworkSynchronisableContainer)) return; Player player = SidedProxy.getPlayerClientSide();
if(player.containerMenu.containerId != pkt.id) return; if((player==null) || !(player.containerMenu instanceof INetworkSynchronisableContainer)) return;
((INetworkSynchronisableContainer)player.containerMenu).onServerPacketReceived(pkt.id,pkt.nbt); if(player.containerMenu.containerId != pkt.id) return;
}); ((INetworkSynchronisableContainer)player.containerMenu).onServerPacketReceived(pkt.id,pkt.nbt);
ctx.get().setPacketHandled(true); });
} ctx.get().setPacketHandled(true);
} }
} }
}
//--------------------------------------------------------------------------------------------------------------------
// Main window GUI text message //--------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------- // Main window GUI text message
//--------------------------------------------------------------------------------------------------------------------
public static class OverlayTextMessage
{ public static class OverlayTextMessage
public static final int DISPLAY_TIME_MS = 3000; {
private static BiConsumer<ITextComponent, Integer> handler_ = null; public static final int DISPLAY_TIME_MS = 3000;
private ITextComponent data_; private static BiConsumer<Component, Integer> handler_ = null;
private int delay_ = DISPLAY_TIME_MS; private final Component data_;
private ITextComponent data() { return data_; } private int delay_ = DISPLAY_TIME_MS;
private int delay() { return delay_; } private Component data() { return data_; }
private int delay() { return delay_; }
public static void setHandler(BiConsumer<ITextComponent, Integer> handler) public static void setHandler(BiConsumer<Component, Integer> handler)
{ if(handler_==null) handler_ = handler; } { if(handler_==null) handler_ = handler; }
public static void sendToPlayer(PlayerEntity player, ITextComponent message, int delay) public static void sendToPlayer(Player player, Component message, int delay)
{ {
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer)) return; if((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer)) return;
DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayerEntity)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT); DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayer)player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
} }
public OverlayTextMessage() public OverlayTextMessage()
{ data_ = new TranslationTextComponent("[unset]"); } { data_ = new TranslatableComponent("[unset]"); }
public OverlayTextMessage(final ITextComponent tct, int delay) public OverlayTextMessage(final Component tct, int delay)
{ data_ = (ITextComponent)tct.copy(); delay_ = delay; } { data_ = tct.copy(); delay_ = delay; }
public static OverlayTextMessage parse(final PacketBuffer buf) public static OverlayTextMessage parse(final FriendlyByteBuf buf)
{ {
try { try {
return new OverlayTextMessage((ITextComponent)buf.readComponent(), DISPLAY_TIME_MS); return new OverlayTextMessage(buf.readComponent(), DISPLAY_TIME_MS);
} catch(Throwable e) { } catch(Throwable e) {
return new OverlayTextMessage(new TranslationTextComponent("[incorrect translation]"), DISPLAY_TIME_MS); return new OverlayTextMessage(new TranslatableComponent("[incorrect translation]"), DISPLAY_TIME_MS);
} }
} }
public static void compose(final OverlayTextMessage pkt, final PacketBuffer buf) public static void compose(final OverlayTextMessage pkt, final FriendlyByteBuf buf)
{ {
try { try {
buf.writeComponent(pkt.data()); buf.writeComponent(pkt.data());
} catch(Throwable e) { } catch(Throwable e) {
Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e.toString()); Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e);
} }
} }
public static class Handler public static class Handler
{ {
public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx) public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx)
{ {
if(handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay())); if(handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay()));
ctx.get().setPacketHandled(true); ctx.get().setPacketHandled(true);
} }
} }
} }
} }

View file

@ -1,199 +1,197 @@
/* /*
* @file OptionalRecipeCondition.java * @file OptionalRecipeCondition.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Recipe condition to enable opt'ing out JSON based recipes. * Recipe condition to enable opt'ing out JSON based recipes.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.block.Block; import net.minecraft.core.Registry;
import net.minecraft.item.Item; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.ITag; import net.minecraft.tags.*;
import net.minecraft.tags.TagCollectionManager; import net.minecraft.util.GsonHelper;
import net.minecraft.util.ResourceLocation; import net.minecraft.world.item.Item;
import net.minecraft.util.JSONUtils; import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.crafting.conditions.ICondition; import net.minecraftforge.common.crafting.conditions.ICondition;
import net.minecraftforge.common.crafting.conditions.IConditionSerializer; import net.minecraftforge.common.crafting.conditions.IConditionSerializer;
import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistries; import com.google.gson.JsonArray;
import com.google.gson.JsonArray; import com.google.gson.JsonElement;
import com.google.gson.JsonElement; import com.google.gson.JsonObject;
import com.google.gson.JsonObject; import net.minecraftforge.registries.IForgeRegistry;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.Collection;
import java.util.Map; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
public class OptionalRecipeCondition implements ICondition public class OptionalRecipeCondition implements ICondition
{ {
private static ResourceLocation NAME; private static ResourceLocation NAME;
private final List<ResourceLocation> all_required; private final List<ResourceLocation> all_required;
private final List<ResourceLocation> any_missing; private final List<ResourceLocation> any_missing;
private final List<ResourceLocation> all_required_tags; private final List<ResourceLocation> all_required_tags;
private final List<ResourceLocation> any_missing_tags; private final List<ResourceLocation> any_missing_tags;
private final @Nullable ResourceLocation result; private final @Nullable ResourceLocation result;
private final boolean result_is_tag; private final boolean result_is_tag;
private final boolean experimental; private final boolean experimental;
private static boolean with_experimental = false; private static boolean with_experimental = false;
private static boolean without_recipes = false; private static boolean without_recipes = false;
private static Predicate<Block> block_optouts = (block)->false; private static Predicate<Block> block_optouts = (block)->false;
private static Predicate<Item> item_optouts = (item)->false; private static Predicate<Item> item_optouts = (item)->false;
public static void init(String modid, Logger logger) public static void init(String modid, Logger logger)
{ {
NAME = new ResourceLocation(modid, "optional"); NAME = new ResourceLocation(modid, "optional");
} }
public static void on_config(boolean enable_experimental, boolean disable_all_recipes, public static void on_config(boolean enable_experimental, boolean disable_all_recipes,
Predicate<Block> block_optout_provider, Predicate<Block> block_optout_provider,
Predicate<Item> item_optout_provider) Predicate<Item> item_optout_provider)
{ {
with_experimental = enable_experimental; with_experimental = enable_experimental;
without_recipes = disable_all_recipes; without_recipes = disable_all_recipes;
block_optouts = block_optout_provider; block_optouts = block_optout_provider;
item_optouts = item_optout_provider; item_optouts = item_optout_provider;
} }
public OptionalRecipeCondition(ResourceLocation result, List<ResourceLocation> required, List<ResourceLocation> missing, List<ResourceLocation> required_tags, List<ResourceLocation> missing_tags, boolean isexperimental, boolean result_is_tag) public OptionalRecipeCondition(ResourceLocation result, List<ResourceLocation> required, List<ResourceLocation> missing, List<ResourceLocation> required_tags, List<ResourceLocation> missing_tags, boolean isexperimental, boolean result_is_tag)
{ {
all_required = required; all_required = required;
any_missing = missing; any_missing = missing;
all_required_tags = required_tags; all_required_tags = required_tags;
any_missing_tags = missing_tags; any_missing_tags = missing_tags;
this.result = result; this.result = result;
this.result_is_tag = result_is_tag; this.result_is_tag = result_is_tag;
experimental=isexperimental; experimental=isexperimental;
} }
@Override @Override
public ResourceLocation getID() public ResourceLocation getID()
{ return NAME; } { return NAME; }
@Override @Override
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Optional recipe, all-required: ["); sb.append("Optional recipe, all-required: [");
for(ResourceLocation e:all_required) sb.append(e.toString()).append(","); for(ResourceLocation e:all_required) sb.append(e.toString()).append(",");
for(ResourceLocation e:all_required_tags) sb.append("#").append(e.toString()).append(","); for(ResourceLocation e:all_required_tags) sb.append("#").append(e.toString()).append(",");
sb.delete(sb.length()-1, sb.length()).append("], any-missing: ["); sb.delete(sb.length()-1, sb.length()).append("], any-missing: [");
for(ResourceLocation e:any_missing) sb.append(e.toString()).append(","); for(ResourceLocation e:any_missing) sb.append(e.toString()).append(",");
for(ResourceLocation e:any_missing_tags) sb.append("#").append(e.toString()).append(","); for(ResourceLocation e:any_missing_tags) sb.append("#").append(e.toString()).append(",");
sb.delete(sb.length()-1, sb.length()).append("]"); sb.delete(sb.length()-1, sb.length()).append("]");
if(experimental) sb.append(" EXPERIMENTAL"); if(experimental) sb.append(" EXPERIMENTAL");
return sb.toString(); return sb.toString();
} }
@Override @Override
public boolean test() public boolean test()
{ {
if(without_recipes) return false; if(without_recipes) return false;
if((experimental) && (!with_experimental)) return false; if((experimental) && (!with_experimental)) return false;
final IForgeRegistry<Item> item_registry = ForgeRegistries.ITEMS; final IForgeRegistry<Item> item_registry = ForgeRegistries.ITEMS;
final Map<ResourceLocation, ITag<Item>> item_tags = TagCollectionManager.getInstance().getItems().getAllTags(); final Collection<ResourceLocation> item_tags = SerializationTags.getInstance().getOrEmpty(Registry.ITEM_REGISTRY).getAvailableTags();
if(result != null) { if(result != null) {
boolean item_registered = item_registry.containsKey(result); boolean item_registered = item_registry.containsKey(result);
if(!item_registered) return false; // required result not registered if(!item_registered) return false; // required result not registered
if(item_registered && item_optouts.test(item_registry.getValue(result))) return false; if(item_registered && item_optouts.test(item_registry.getValue(result))) return false;
if(ForgeRegistries.BLOCKS.containsKey(result) && block_optouts.test(ForgeRegistries.BLOCKS.getValue(result))) return false; if(ForgeRegistries.BLOCKS.containsKey(result) && block_optouts.test(ForgeRegistries.BLOCKS.getValue(result))) return false;
} }
if(!all_required.isEmpty()) { if(!all_required.isEmpty()) {
for(ResourceLocation rl:all_required) { for(ResourceLocation rl:all_required) {
if(!item_registry.containsKey(rl)) return false; if(!item_registry.containsKey(rl)) return false;
} }
} }
if(!all_required_tags.isEmpty()) { if(!all_required_tags.isEmpty()) {
for(ResourceLocation rl:all_required_tags) { for(ResourceLocation rl:all_required_tags) {
if(!item_tags.containsKey(rl)) return false; if(!item_tags.contains(rl)) return false;
if(item_tags.get(rl).getValues().isEmpty()) return false; }
} }
} if(!any_missing.isEmpty()) {
if(!any_missing.isEmpty()) { for(ResourceLocation rl:any_missing) {
for(ResourceLocation rl:any_missing) { if(!item_registry.containsKey(rl)) return true;
if(!item_registry.containsKey(rl)) return true; }
} return false;
return false; }
} if(!any_missing_tags.isEmpty()) {
if(!any_missing_tags.isEmpty()) { for(ResourceLocation rl:any_missing_tags) {
for(ResourceLocation rl:any_missing_tags) { if(!item_tags.contains(rl)) return true;
if(!item_tags.containsKey(rl)) return true; }
if(item_tags.get(rl).getValues().isEmpty()) return true; return false;
} }
return false; return true;
} }
return true;
} public static class Serializer implements IConditionSerializer<OptionalRecipeCondition>
{
public static class Serializer implements IConditionSerializer<OptionalRecipeCondition> public static final Serializer INSTANCE = new Serializer();
{
public static final Serializer INSTANCE = new Serializer(); @Override
public ResourceLocation getID()
@Override { return OptionalRecipeCondition.NAME; }
public ResourceLocation getID()
{ return OptionalRecipeCondition.NAME; } @Override
public void write(JsonObject json, OptionalRecipeCondition condition)
@Override {
public void write(JsonObject json, OptionalRecipeCondition condition) JsonArray required = new JsonArray();
{ JsonArray missing = new JsonArray();
JsonArray required = new JsonArray(); for(ResourceLocation e:condition.all_required) required.add(e.toString());
JsonArray missing = new JsonArray(); for(ResourceLocation e:condition.any_missing) missing.add(e.toString());
for(ResourceLocation e:condition.all_required) required.add(e.toString()); json.add("required", required);
for(ResourceLocation e:condition.any_missing) missing.add(e.toString()); json.add("missing", missing);
json.add("required", required); if(condition.result != null) {
json.add("missing", missing); json.addProperty("result", (condition.result_is_tag ? "#" : "") + condition.result);
if(condition.result != null) { }
json.addProperty("result", (condition.result_is_tag ? "#" : "") + condition.result.toString()); }
}
} @Override
public OptionalRecipeCondition read(JsonObject json)
@Override {
public OptionalRecipeCondition read(JsonObject json) List<ResourceLocation> required = new ArrayList<>();
{ List<ResourceLocation> missing = new ArrayList<>();
List<ResourceLocation> required = new ArrayList<>(); List<ResourceLocation> required_tags = new ArrayList<>();
List<ResourceLocation> missing = new ArrayList<>(); List<ResourceLocation> missing_tags = new ArrayList<>();
List<ResourceLocation> required_tags = new ArrayList<>(); ResourceLocation result = null;
List<ResourceLocation> missing_tags = new ArrayList<>(); boolean experimental = false;
ResourceLocation result = null; boolean result_is_tag = false;
boolean experimental = false; if(json.has("result")) {
boolean result_is_tag = false; String s = json.get("result").getAsString();
if(json.has("result")) { if(s.startsWith("#")) {
String s = json.get("result").getAsString(); result = new ResourceLocation(s.substring(1));
if(s.startsWith("#")) { result_is_tag = true;
result = new ResourceLocation(s.substring(1)); } else {
result_is_tag = true; result = new ResourceLocation(s);
} else { }
result = new ResourceLocation(s); }
} if(json.has("required")) {
} for(JsonElement e:GsonHelper.getAsJsonArray(json, "required")) {
if(json.has("required")) { String s = e.getAsString();
for(JsonElement e:JSONUtils.getAsJsonArray(json, "required")) { if(s.startsWith("#")) {
String s = e.getAsString(); required_tags.add(new ResourceLocation(s.substring(1)));
if(s.startsWith("#")) { } else {
required_tags.add(new ResourceLocation(s.substring(1))); required.add(new ResourceLocation(s));
} else { }
required.add(new ResourceLocation(s)); }
} }
} if(json.has("missing")) {
} for(JsonElement e:GsonHelper.getAsJsonArray(json, "missing")) {
if(json.has("missing")) { String s = e.getAsString();
for(JsonElement e:JSONUtils.getAsJsonArray(json, "missing")) { if(s.startsWith("#")) {
String s = e.getAsString(); missing_tags.add(new ResourceLocation(s.substring(1)));
if(s.startsWith("#")) { } else {
missing_tags.add(new ResourceLocation(s.substring(1))); missing.add(new ResourceLocation(s));
} else { }
missing.add(new ResourceLocation(s)); }
} }
} if(json.has("experimental")) experimental = json.get("experimental").getAsBoolean();
} return new OptionalRecipeCondition(result, required, missing, required_tags, missing_tags, experimental, result_is_tag);
if(json.has("experimental")) experimental = json.get("experimental").getAsBoolean(); }
return new OptionalRecipeCondition(result, required, missing, required_tags, missing_tags, experimental, result_is_tag); }
} }
}
}

View file

@ -1,117 +1,116 @@
/* /*
* @file Overlay.java * @file Overlay.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Renders status messages in one line. * Renders status messages in one line.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.platform.Window;
import net.minecraft.client.MainWindow; import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Font;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.client.Minecraft;
import net.minecraft.util.text.ITextComponent; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component;
import net.minecraft.client.gui.AbstractGui; import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.entity.player.Player;
import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
public class Overlay
{ public class Overlay
public static void register() {
{ public static void register()
if(SidedProxy.mc() != null) { {
MinecraftForge.EVENT_BUS.register(new TextOverlayGui()); if(SidedProxy.mc() != null) {
Networking.OverlayTextMessage.setHandler(TextOverlayGui::show); MinecraftForge.EVENT_BUS.register(new TextOverlayGui());
} Networking.OverlayTextMessage.setHandler(TextOverlayGui::show);
} }
}
public static void show(PlayerEntity player, final ITextComponent message)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, 3000); } public static void show(Player player, final Component message)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, 3000); }
public static void show(PlayerEntity player, final ITextComponent message, int delay)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, delay); } public static void show(Player player, final Component message, int delay)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, delay); }
// -----------------------------------------------------------------------------
// Client side handler // -----------------------------------------------------------------------------
// ----------------------------------------------------------------------------- // Client side handler
// -----------------------------------------------------------------------------
@Mod.EventBusSubscriber(Dist.CLIENT)
@OnlyIn(Dist.CLIENT) @Mod.EventBusSubscriber(Dist.CLIENT)
public static class TextOverlayGui extends AbstractGui @OnlyIn(Dist.CLIENT)
{ public static class TextOverlayGui extends Screen
private static final ITextComponent EMPTY_TEXT = new StringTextComponent(""); {
private static double overlay_y_ = 0.75; private static final Component EMPTY_TEXT = new TextComponent("");
private static int text_color_ = 0x00ffaa00; private static double overlay_y_ = 0.75;
private static int border_color_ = 0xaa333333; private static int text_color_ = 0x00ffaa00;
private static int background_color1_ = 0xaa333333; private static int border_color_ = 0xaa333333;
private static int background_color2_ = 0xaa444444; private static int background_color1_ = 0xaa333333;
private final Minecraft mc; private static int background_color2_ = 0xaa444444;
private static long deadline_; private final Minecraft mc;
private static ITextComponent text_; private static long deadline_;
private static Component text_;
public static void on_config(double overlay_y)
{ public static void on_config(double overlay_y)
overlay_y_ = overlay_y; { on_config(overlay_y, 0x00ffaa00, 0xaa333333, 0xaa333333, 0xaa444444); }
// currently const, just to circumvent "useless variable" warnings
text_color_ = 0x00ffaa00; public static void on_config(double overlay_y, int text_color, int border_color, int background_color1, int background_color2)
border_color_ = 0xaa333333; {
background_color1_ = 0xaa333333; overlay_y_ = overlay_y;
background_color2_ = 0xaa444444; text_color_ = text_color;
} border_color_ = border_color;
background_color1_ = background_color1;
public static synchronized ITextComponent text() background_color2_ = background_color2;
{ return text_; } }
public static synchronized long deadline() public static synchronized Component text()
{ return deadline_; } { return text_; }
public static synchronized void hide() public static synchronized long deadline()
{ deadline_ = 0; text_ = EMPTY_TEXT; } { return deadline_; }
public static synchronized void show(ITextComponent s, int displayTimeoutMs) public static synchronized void hide()
{ text_ = (s==null)?(EMPTY_TEXT):(s.copy()); deadline_ = System.currentTimeMillis() + displayTimeoutMs; } { deadline_ = 0; text_ = EMPTY_TEXT; }
public static synchronized void show(String s, int displayTimeoutMs) public static synchronized void show(Component s, int displayTimeoutMs)
{ text_ = ((s==null)||(s.isEmpty()))?(EMPTY_TEXT):(new StringTextComponent(s)); deadline_ = System.currentTimeMillis() + displayTimeoutMs; } { text_ = (s==null)?(EMPTY_TEXT):(s.copy()); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
TextOverlayGui() public static synchronized void show(String s, int displayTimeoutMs)
{ super(); mc = SidedProxy.mc(); } { text_ = ((s==null)||(s.isEmpty()))?(EMPTY_TEXT):(new TextComponent(s)); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
@SubscribeEvent TextOverlayGui()
public void onRenderGui(RenderGameOverlayEvent.Post event) { super(new TextComponent("")); mc = SidedProxy.mc(); }
{
if(event.getType() != RenderGameOverlayEvent.ElementType.CHAT) return; @SubscribeEvent
if(deadline() < System.currentTimeMillis()) return; public void onRenderGui(RenderGameOverlayEvent.Post event)
if(text()==EMPTY_TEXT) return; {
String txt = text().getString(); if(event.getType() != RenderGameOverlayEvent.ElementType.CHAT) return;
if(txt.isEmpty()) return; if(deadline() < System.currentTimeMillis()) return;
MatrixStack mxs = event.getMatrixStack(); if(text()==EMPTY_TEXT) return;
final MainWindow win = mc.getWindow(); String txt = text().getString();
final FontRenderer fr = mc.font; if(txt.isEmpty()) return;
final boolean was_unicode = fr.isBidirectional(); PoseStack mxs = event.getMatrixStack();
try { final Window win = mc.getWindow();
final int cx = win.getGuiScaledWidth() / 2; final Font fr = mc.font;
final int cy = (int)(win.getGuiScaledHeight() * overlay_y_); final boolean was_unicode = fr.isBidirectional();
final int w = fr.width(txt); final int cx = win.getGuiScaledWidth() / 2;
final int h = fr.lineHeight; final int cy = (int)(win.getGuiScaledHeight() * overlay_y_);
fillGradient(mxs, cx-(w/2)-3, cy-2, cx+(w/2)+2, cy+h+2, 0xaa333333, 0xaa444444); final int w = fr.width(txt);
hLine(mxs, cx-(w/2)-3, cx+(w/2)+2, cy-2, 0xaa333333); final int h = fr.lineHeight;
hLine(mxs, cx-(w/2)-3, cx+(w/2)+2, cy+h+2, 0xaa333333); fillGradient(mxs, cx-(w/2)-3, cy-2, cx+(w/2)+2, cy+h+2, 0xaa333333, 0xaa444444);
vLine(mxs, cx-(w/2)-3, cy-2, cy+h+2, 0xaa333333); hLine(mxs, cx-(w/2)-3, cx+(w/2)+2, cy-2, 0xaa333333);
vLine(mxs, cx+(w/2)+2, cy-2, cy+h+2, 0xaa333333); hLine(mxs, cx-(w/2)-3, cx+(w/2)+2, cy+h+2, 0xaa333333);
drawCenteredString(mxs, fr, text(), cx , cy+1, 0x00ffaa00); vLine(mxs, cx-(w/2)-3, cy-2, cy+h+2, 0xaa333333);
} finally { vLine(mxs, cx+(w/2)+2, cy-2, cy+h+2, 0xaa333333);
; // fr.setBidiFlag(was_unicode); drawCenteredString(mxs, fr, text(), cx , cy+1, 0x00ffaa00);
} }
} }
}
}
}

View file

@ -1,158 +1,158 @@
/* /*
* @file RfEnergy.java * @file RfEnergy.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* General RF/FE energy handling functionality. * General RF/FE energy handling functionality.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.core.BlockPos;
import net.minecraft.tileentity.TileEntity; import net.minecraft.core.Direction;
import net.minecraft.util.Direction; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.Mth;
import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.Level;
import net.minecraft.world.World; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class RfEnergy public class RfEnergy
{ {
public static int feed(World world, BlockPos pos, @Nullable Direction side, int rf_energy) public static int feed(Level world, BlockPos pos, @Nullable Direction side, int rf_energy)
{ {
final TileEntity te = world.getBlockEntity(pos); final BlockEntity te = world.getBlockEntity(pos);
if(te == null) return 0; if(te == null) return 0;
final IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, side).orElse(null); final IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, side).orElse(null);
if(es == null) return 0; if(es == null) return 0;
return es.receiveEnergy(rf_energy, false); return es.receiveEnergy(rf_energy, false);
} }
public static class Battery implements IEnergyStorage public static class Battery implements IEnergyStorage
{ {
protected int capacity_; protected int capacity_;
protected int charge_rate_; protected int charge_rate_;
protected int discharge_rate_; protected int discharge_rate_;
protected int energy_; protected int energy_;
public Battery(int capacity) public Battery(int capacity)
{ this(capacity, capacity); } { this(capacity, capacity); }
public Battery(int capacity, int transfer_rate) public Battery(int capacity, int transfer_rate)
{ this(capacity, transfer_rate, transfer_rate, 0); } { this(capacity, transfer_rate, transfer_rate, 0); }
public Battery(int capacity, int charge_rate, int discharge_rate) public Battery(int capacity, int charge_rate, int discharge_rate)
{ this(capacity, charge_rate, discharge_rate, 0); } { this(capacity, charge_rate, discharge_rate, 0); }
public Battery(int capacity, int charge_rate, int discharge_rate, int energy) public Battery(int capacity, int charge_rate, int discharge_rate, int energy)
{ {
capacity_ = Math.max(capacity, 1); capacity_ = Math.max(capacity, 1);
charge_rate_ = MathHelper.clamp(charge_rate, 0, capacity_); charge_rate_ = Mth.clamp(charge_rate, 0, capacity_);
discharge_rate_ = MathHelper.clamp(discharge_rate, 0, capacity_); discharge_rate_ = Mth.clamp(discharge_rate, 0, capacity_);
energy_ = MathHelper.clamp(energy, 0, capacity_); energy_ = Mth.clamp(energy, 0, capacity_);
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
public Battery setMaxEnergyStored(int capacity) public Battery setMaxEnergyStored(int capacity)
{ capacity_ = Math.max(capacity, 1); return this; } { capacity_ = Math.max(capacity, 1); return this; }
public Battery setEnergyStored(int energy) public Battery setEnergyStored(int energy)
{ energy_ = MathHelper.clamp(energy, 0, capacity_); return this; } { energy_ = Mth.clamp(energy, 0, capacity_); return this; }
public Battery setChargeRate(int in_rate) public Battery setChargeRate(int in_rate)
{ charge_rate_ = MathHelper.clamp(in_rate, 0, capacity_); return this; } { charge_rate_ = Mth.clamp(in_rate, 0, capacity_); return this; }
public Battery setDischargeRate(int out_rate) public Battery setDischargeRate(int out_rate)
{ discharge_rate_ = MathHelper.clamp(out_rate, 0, capacity_); return this; } { discharge_rate_ = Mth.clamp(out_rate, 0, capacity_); return this; }
public int getChargeRate() public int getChargeRate()
{ return charge_rate_; } { return charge_rate_; }
public int getDischargeRate() public int getDischargeRate()
{ return discharge_rate_; } { return discharge_rate_; }
public boolean isEmpty() public boolean isEmpty()
{ return energy_ <= 0; } { return energy_ <= 0; }
public boolean isFull() public boolean isFull()
{ return energy_ >= capacity_; } { return energy_ >= capacity_; }
public int getSOC() public int getSOC()
{ return (int)MathHelper.clamp((100.0 * energy_ / capacity_ + .5), 0, 100); } { return (int)Mth.clamp((100.0 * energy_ / capacity_ + .5), 0, 100); }
public int getComparatorOutput() public int getComparatorOutput()
{ return (int)MathHelper.clamp((15.0 * energy_ / capacity_ + .2), 0, 15); } { return (int)Mth.clamp((15.0 * energy_ / capacity_ + .2), 0, 15); }
public boolean draw(int energy) public boolean draw(int energy)
{ {
if(energy_ < energy) return false; if(energy_ < energy) return false;
energy_ -= energy; energy_ -= energy;
return true; return true;
} }
public boolean feed(int energy) public boolean feed(int energy)
{ {
energy_ = Math.min(energy_+energy, capacity_); energy_ = Math.min(energy_+energy, capacity_);
return energy_ >= capacity_; return energy_ >= capacity_;
} }
public Battery clear() public Battery clear()
{ energy_ = 0; return this; } { energy_ = 0; return this; }
public Battery load(CompoundNBT nbt, String key) public Battery load(CompoundTag nbt, String key)
{ setEnergyStored(nbt.getInt(key)); return this; } { setEnergyStored(nbt.getInt(key)); return this; }
public Battery load(CompoundNBT nbt) public Battery load(CompoundTag nbt)
{ return load(nbt, "Energy"); } { return load(nbt, "Energy"); }
public CompoundNBT save(CompoundNBT nbt, String key) public CompoundTag save(CompoundTag nbt, String key)
{ nbt.putInt(key, energy_); return nbt; } { nbt.putInt(key, energy_); return nbt; }
public CompoundNBT save(CompoundNBT nbt) public CompoundTag save(CompoundTag nbt)
{ return save(nbt, "Energy"); } { return save(nbt, "Energy"); }
public LazyOptional<IEnergyStorage> createEnergyHandler() public LazyOptional<IEnergyStorage> createEnergyHandler()
{ return LazyOptional.of(() -> (IEnergyStorage)this); } { return LazyOptional.of(() -> this); }
// IEnergyStorage ------------------------------------------------------------------------------------ // IEnergyStorage ------------------------------------------------------------------------------------
@Override @Override
public int receiveEnergy(int feed_energy, boolean simulate) public int receiveEnergy(int feed_energy, boolean simulate)
{ {
if(!canReceive()) return 0; if(!canReceive()) return 0;
int e = Math.min(Math.min(charge_rate_, feed_energy), capacity_-energy_); int e = Math.min(Math.min(charge_rate_, feed_energy), capacity_-energy_);
if(!simulate) energy_ += e; if(!simulate) energy_ += e;
return e; return e;
} }
@Override @Override
public int extractEnergy(int draw_energy, boolean simulate) public int extractEnergy(int draw_energy, boolean simulate)
{ {
if(!canExtract()) return 0; if(!canExtract()) return 0;
int e = Math.min(Math.min(discharge_rate_, draw_energy), energy_); int e = Math.min(Math.min(discharge_rate_, draw_energy), energy_);
if(!simulate) energy_ -= e; if(!simulate) energy_ -= e;
return e; return e;
} }
@Override @Override
public int getEnergyStored() public int getEnergyStored()
{ return energy_; } { return energy_; }
@Override @Override
public int getMaxEnergyStored() public int getMaxEnergyStored()
{ return capacity_; } { return capacity_; }
@Override @Override
public boolean canExtract() public boolean canExtract()
{ return discharge_rate_ > 0; } { return discharge_rate_ > 0; }
@Override @Override
public boolean canReceive() public boolean canReceive()
{ return charge_rate_ > 0; } { return charge_rate_ > 0; }
} }
} }

View file

@ -0,0 +1,45 @@
/*
* @file RsSignals.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* General redstone signal related functionality.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
public class RsSignals
{
public static boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction realSide)
{
return state.isSignalSource();
}
public static int fromContainer(@Nullable Container container)
{
if(container == null) return 0;
final double max = container.getMaxStackSize();
if(max <= 0) return 0;
boolean nonempty = false;
double fill_level = 0;
for(int i=0; i<container.getContainerSize(); ++i) {
ItemStack stack = container.getItem(i);
if(stack.isEmpty() || (stack.getMaxStackSize()<=0)) continue;
fill_level += ((double)stack.getCount()) / Math.min(max, stack.getMaxStackSize());
nonempty = true;
}
fill_level /= container.getContainerSize();
return (int)(Math.floor(fill_level * 14) + (nonempty?1:0)); // vanilla compliant calculation.
}
}

View file

@ -1,67 +1,66 @@
/* /*
* @file SidedProxy.java * @file SidedProxy.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* General client/server sideness selection proxy. * General client/server sideness selection proxy.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.World; import net.minecraft.world.level.Level;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Optional; import java.util.Optional;
public class SidedProxy public class SidedProxy
{ {
@Nullable @Nullable
public static PlayerEntity getPlayerClientSide() public static Player getPlayerClientSide()
{ return proxy.getPlayerClientSide(); } { return proxy.getPlayerClientSide(); }
@Nullable @Nullable
public static World getWorldClientSide() public static Level getWorldClientSide()
{ return proxy.getWorldClientSide(); } { return proxy.getWorldClientSide(); }
@Nullable @Nullable
public static Minecraft mc() public static Minecraft mc()
{ return proxy.mc(); } { return proxy.mc(); }
@Nullable @Nullable
public static Optional<Boolean> isCtrlDown() public static Optional<Boolean> isCtrlDown()
{ return proxy.isCtrlDown(); } { return proxy.isCtrlDown(); }
@Nullable @Nullable
public static Optional<Boolean> isShiftDown() public static Optional<Boolean> isShiftDown()
{ return proxy.isShiftDown(); } { return proxy.isShiftDown(); }
// -------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------
// @todo: check conditions for safeRunForDist() private static final ISidedProxy proxy = DistExecutor.unsafeRunForDist(()->ClientProxy::new, ()->ServerProxy::new);
private static ISidedProxy proxy = DistExecutor.unsafeRunForDist(()->ClientProxy::new, ()->ServerProxy::new);
private interface ISidedProxy
private interface ISidedProxy {
{ default @Nullable Player getPlayerClientSide() { return null; }
default @Nullable PlayerEntity getPlayerClientSide() { return null; } default @Nullable Level getWorldClientSide() { return null; }
default @Nullable World getWorldClientSide() { return null; } default @Nullable Minecraft mc() { return null; }
default @Nullable Minecraft mc() { return null; } default Optional<Boolean> isCtrlDown() { return Optional.empty(); }
default Optional<Boolean> isCtrlDown() { return Optional.empty(); } default Optional<Boolean> isShiftDown() { return Optional.empty(); }
default Optional<Boolean> isShiftDown() { return Optional.empty(); } }
}
private static final class ClientProxy implements ISidedProxy
private static final class ClientProxy implements ISidedProxy {
{ public @Nullable Player getPlayerClientSide() { return Minecraft.getInstance().player; }
public @Nullable PlayerEntity getPlayerClientSide() { return Minecraft.getInstance().player; } public @Nullable Level getWorldClientSide() { return Minecraft.getInstance().level; }
public @Nullable World getWorldClientSide() { return Minecraft.getInstance().level; } public @Nullable Minecraft mc() { return Minecraft.getInstance(); }
public @Nullable Minecraft mc() { return Minecraft.getInstance(); } public Optional<Boolean> isCtrlDown() { return Optional.of(Auxiliaries.isCtrlDown()); }
public Optional<Boolean> isCtrlDown() { return Optional.of(Auxiliaries.isCtrlDown()); } public Optional<Boolean> isShiftDown() { return Optional.of(Auxiliaries.isShiftDown()); }
public Optional<Boolean> isShiftDown() { return Optional.of(Auxiliaries.isShiftDown()); } }
}
private static final class ServerProxy implements ISidedProxy
private static final class ServerProxy implements ISidedProxy {
{ }
}
}
}

View file

@ -1,115 +1,118 @@
/* /*
* @file Tooltip.java * @file Tooltip.java
* @author Stefan Wilhelm (wile) * @author Stefan Wilhelm (wile)
* @copyright (C) 2020 Stefan Wilhelm * @copyright (C) 2020 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT) * @license MIT (see https://opensource.org/licenses/MIT)
* *
* Delayed tooltip for a selected area. Constructed with a * Delayed tooltip for a selected area. Constructed with a
* GUI, invoked in `render()`. * GUI, invoked in `render()`.
*/ */
package wile.engineersdecor.libmc.detail; package wile.engineersdecor.libmc.detail;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.screen.inventory.ContainerScreen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.inventory.container.Container; import net.minecraft.network.chat.Component;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.Mth;
import net.minecraft.util.text.ITextComponent; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public class TooltipDisplay public class TooltipDisplay
{ {
private static long default_delay = 800; private static long default_delay = 450;
private static int default_max_deviation = 1; private static int default_max_deviation = 1;
public static void config(long delay, int max_deviation) public static void config(long delay, int max_deviation)
{ {
default_delay = MathHelper.clamp(delay, 500, 5000); default_delay = Mth.clamp(delay, 500, 5000);
default_max_deviation = MathHelper.clamp(max_deviation, 1, 5); default_max_deviation = Mth.clamp(max_deviation, 1, 5);
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
public static class TipRange public static class TipRange
{ {
public final int x0,y0,x1,y1; public final int x0,y0,x1,y1;
public final Supplier<ITextComponent> text; public final Supplier<Component> text;
public TipRange(int x, int y, int w, int h, ITextComponent text) public TipRange(int x, int y, int w, int h, Component text)
{ this(x,y,w,h,()->text); } { this(x,y,w,h,()->text); }
public TipRange(int x, int y, int w, int h, Supplier<ITextComponent> text) public TipRange(int x, int y, int w, int h, Supplier<Component> text)
{ this.text=text; this.x0=x; this.y0=y; this.x1=x0+w-1; this.y1=y0+h-1; } { this.text=text; this.x0=x; this.y0=y; this.x1=x0+w-1; this.y1=y0+h-1; }
} }
// --------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------
private List<TipRange> ranges = new ArrayList<>(); private List<TipRange> ranges = new ArrayList<>();
private long delay = default_delay; private long delay = default_delay;
private int max_deviation = default_max_deviation; private int max_deviation = default_max_deviation;
private int x_last, y_last; private int x_last, y_last;
private long t; private long t;
private static boolean had_render_exception = false; private static boolean had_render_exception = false;
public TooltipDisplay() public TooltipDisplay()
{ t = System.currentTimeMillis(); } { t = System.currentTimeMillis(); }
public void init(List<TipRange> ranges, long delay_ms, int max_deviation_xy) public TooltipDisplay init(List<TipRange> ranges, long delay_ms, int max_deviation_xy)
{ {
this.ranges = ranges; this.ranges = ranges;
this.delay = delay_ms; this.delay = delay_ms;
this.max_deviation = max_deviation_xy; this.max_deviation = max_deviation_xy;
t = System.currentTimeMillis(); t = System.currentTimeMillis();
x_last = y_last = 0; x_last = y_last = 0;
} return this;
}
public void init(List<TipRange> ranges)
{ init(ranges, default_delay, default_max_deviation); } public TooltipDisplay init(List<TipRange> ranges)
{ return init(ranges, default_delay, default_max_deviation); }
public void init(TipRange... ranges)
{ init(Arrays.asList(ranges), default_delay, default_max_deviation); } public TooltipDisplay init(TipRange... ranges)
{ return init(Arrays.asList(ranges), default_delay, default_max_deviation); }
public void resetTimer()
{ t = System.currentTimeMillis(); } public TooltipDisplay delay(int ms)
{ delay = (ms<=0) ? default_delay : ms; return this; }
public <T extends Container> boolean render(MatrixStack mx, final ContainerScreen<T> gui, int x, int y)
{ public void resetTimer()
if(had_render_exception) return false; { t = System.currentTimeMillis(); }
if((Math.abs(x-x_last) > max_deviation) || (Math.abs(y-y_last) > max_deviation)) {
x_last = x; public <T extends AbstractContainerMenu> boolean render(PoseStack mx, final AbstractContainerScreen<T> gui, int x, int y)
y_last = y; {
resetTimer(); if(had_render_exception) return false;
return false; if((Math.abs(x-x_last) > max_deviation) || (Math.abs(y-y_last) > max_deviation)) {
} else if(Math.abs(System.currentTimeMillis()-t) < delay) { x_last = x;
return false; y_last = y;
} else if(ranges.stream().noneMatch( resetTimer();
(tip)->{ return false;
if((x<tip.x0) || (x>tip.x1) || (y<tip.y0) || (y>tip.y1)) return false; } else if(Math.abs(System.currentTimeMillis()-t) < delay) {
String text = tip.text.get().getString(); return false;
if(!text.isEmpty() && (!text.startsWith("block."))) { } else if(ranges.stream().noneMatch(
try { (tip)->{
gui.renderTooltip(mx, tip.text.get(), x, y); if((x<tip.x0) || (x>tip.x1) || (y<tip.y0) || (y>tip.y1)) return false;
} catch(Exception ex) { String text = tip.text.get().getString();
had_render_exception = true; if(text.isEmpty()) return false;
Auxiliaries.logError("Tooltip rendering disabled due to exception: '" + ex.getMessage() + "'"); try {
return false; gui.renderTooltip(mx, tip.text.get(), x, y);
} } catch(Exception ex) {
} had_render_exception = true;
return true; Auxiliaries.logError("Tooltip rendering disabled due to exception: '" + ex.getMessage() + "'");
}) return false;
){ }
resetTimer(); return true;
return false; })
} else { ){
return true; resetTimer();
} return false;
} } else {
} return true;
}
}
}

View file

@ -0,0 +1,119 @@
package wile.engineersdecor.libmc.ui;
import net.minecraft.util.Mth;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.function.BiConsumer;
public class Containers
{
// -------------------------------------------------------------------------------------------------------------------
// Slots
// -------------------------------------------------------------------------------------------------------------------
public static class StorageSlot extends Slot
{
protected BiConsumer<ItemStack, ItemStack> slot_change_action_ = (oldStack, newStack)->{};
protected int stack_limit_ = 64;
public boolean enabled = true;
public StorageSlot(Container inventory, int index, int x, int y)
{ super(inventory, index, x, y); }
public StorageSlot setSlotStackLimit(int limit)
{ stack_limit_ = Mth.clamp(limit, 1, 64); return this; }
public int getMaxStackSize()
{ return stack_limit_; }
public StorageSlot setSlotChangeNotifier(BiConsumer<ItemStack, ItemStack> action)
{ slot_change_action_ = action; return this; }
@Override
public void onQuickCraft(ItemStack oldStack, ItemStack newStack)
{ slot_change_action_.accept(oldStack, newStack);} // no crafting trigger
@Override
public void set(ItemStack stack)
{
if(stack.sameItem(getItem())) {
super.set(stack);
} else {
final ItemStack before = getItem().copy();
super.set(stack); // whatever this does else next to setting inventory.
slot_change_action_.accept(before, getItem());
}
}
@Override
public boolean mayPlace(ItemStack stack)
{ return enabled && this.container.canPlaceItem(this.getSlotIndex(), stack); }
@Override
public int getMaxStackSize(ItemStack stack)
{ return Math.min(getMaxStackSize(), stack_limit_); }
@OnlyIn(Dist.CLIENT)
public boolean isActive()
{ return enabled; }
}
public static class LockedSlot extends Slot
{
protected int stack_limit_ = 64;
public boolean enabled = true;
public LockedSlot(Container inventory, int index, int x, int y)
{ super(inventory, index, x, y); }
public LockedSlot setSlotStackLimit(int limit)
{ stack_limit_ = Mth.clamp(limit, 1, 64); return this; }
public int getMaxStackSize()
{ return stack_limit_; }
@Override
public int getMaxStackSize(ItemStack stack)
{ return Math.min(getMaxStackSize(), stack_limit_); }
@Override
public boolean mayPlace(ItemStack stack)
{ return false; }
@Override
public boolean mayPickup(Player player)
{ return false; }
@OnlyIn(Dist.CLIENT)
public boolean isActive()
{ return enabled; }
}
public static class HiddenSlot extends Slot
{
public HiddenSlot(Container inventory, int index)
{ super(inventory, index, 0, 0); }
@Override
public int getMaxStackSize(ItemStack stack)
{ return getMaxStackSize(); }
@Override
public boolean mayPlace(ItemStack stack)
{ return false; }
@Override
public boolean mayPickup(Player player)
{ return false; }
@OnlyIn(Dist.CLIENT)
public boolean isActive()
{ return false; }
}
}

View file

@ -0,0 +1,322 @@
package wile.engineersdecor.libmc.ui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.sounds.SoundManager;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.TooltipDisplay;
import java.util.function.Consumer;
import java.util.function.Function;
public class Guis
{
// -------------------------------------------------------------------------------------------------------------------
// Gui base
// -------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static abstract class ContainerGui<T extends AbstractContainerMenu> extends AbstractContainerScreen<T>
{
protected final ResourceLocation background_image_;
protected final Player player_;
protected final Guis.BackgroundImage gui_background_;
protected final TooltipDisplay tooltip_ = new TooltipDisplay();
public ContainerGui(T menu, Inventory player_inv, Component title, String background_image, int width, int height)
{
super(menu, player_inv, title);
this.background_image_ = new ResourceLocation(Auxiliaries.modid(), background_image);
this.player_ = player_inv.player;
this.imageWidth = width;
this.imageHeight = height;
gui_background_ = new Guis.BackgroundImage(background_image_, width, height, new Coord2d(0,0));
}
public ContainerGui(T menu, Inventory player_inv, Component title, String background_image)
{
super(menu, player_inv, title);
this.background_image_ = new ResourceLocation(Auxiliaries.modid(), background_image);
this.player_ = player_inv.player;
gui_background_ = new Guis.BackgroundImage(background_image_, imageWidth, imageHeight, new Coord2d(0,0));
}
@Override
public void init()
{
super.init();
gui_background_.init(this, new Coord2d(0,0)).show();
}
@Override
public void render(PoseStack mx, int mouseX, int mouseY, float partialTicks)
{
renderBackground(mx);
super.render(mx, mouseX, mouseY, partialTicks);
if(!tooltip_.render(mx, this, mouseX, mouseY)) renderTooltip(mx, mouseX, mouseY);
}
@Override
protected void renderLabels(PoseStack mx, int x, int y)
{}
@Override
@SuppressWarnings("deprecation")
protected final void renderBg(PoseStack mx, float partialTicks, int mouseX, int mouseY)
{
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
gui_background_.draw(mx, this);
renderBgWidgets(mx, partialTicks, mouseX, mouseY);
RenderSystem.disableBlend();
}
public final ResourceLocation getBackgroundImage()
{ return background_image_; }
protected void renderBgWidgets(PoseStack mx, float partialTicks, int mouseX, int mouseY)
{}
protected void renderItemTemplate(PoseStack mx, ItemStack stack, int x, int y)
{
final ItemRenderer ir = itemRenderer;
final int main_zl = getBlitOffset();
final float zl = ir.blitOffset;
final int x0 = getGuiLeft();
final int y0 = getGuiTop();
ir.blitOffset = -80;
ir.renderGuiItem(stack, x0+x, y0+y);
RenderSystem.disableColorLogicOp(); //RenderSystem.disableColorMaterial();
RenderSystem.enableDepthTest(); //RenderSystem.enableAlphaTest();
RenderSystem.defaultBlendFunc();
RenderSystem.enableBlend();
ir.blitOffset = zl;
setBlitOffset(100);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.setShaderColor(0.7f, 0.7f, 0.7f, 0.8f);
RenderSystem.setShaderTexture(0, background_image_);
blit(mx, x0+x, y0+y, x, y, 16, 16);
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
setBlitOffset(main_zl);
}
}
// -------------------------------------------------------------------------------------------------------------------
// Gui elements
// -------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class Coord2d
{
public final int x, y;
public Coord2d(int x, int y) { this.x=x; this.y=y; }
}
@OnlyIn(Dist.CLIENT)
public static class UiWidget extends net.minecraft.client.gui.components.AbstractWidget
{
protected static final Component EMPTY_TEXT = new TextComponent("");
protected static final Function<UiWidget,Component> NO_TOOLTIP = (uiw)->EMPTY_TEXT;
@SuppressWarnings("all") private Function<UiWidget,Component> tooltip_ = NO_TOOLTIP;
public UiWidget(int x, int y, int width, int height, Component title)
{ super(x, y, width, height, title); }
public UiWidget init(Screen parent)
{
this.x += ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>)parent).getGuiLeft() : 0);
this.y += ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>)parent).getGuiTop() : 0);
return this;
}
public UiWidget init(Screen parent, Coord2d position)
{
this.x = position.x + ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>)parent).getGuiLeft() : 0);
this.y = position.y + ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>)parent).getGuiTop() : 0);
return this;
}
public int getWidth()
{ return this.width; }
public int getHeight()
{ return this.height; }
public UiWidget show()
{ visible = true; return this; }
public UiWidget hide()
{ visible = false; return this; }
@Override
public void renderButton(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks)
{
super.renderButton(matrixStack, mouseX, mouseY, partialTicks);
if(isHovered()) renderToolTip(matrixStack, mouseX, mouseY);
}
@Override
@SuppressWarnings("all")
public void renderToolTip(PoseStack matrixStack, int mouseX, int mouseY)
{
if(tooltip_ == NO_TOOLTIP) return;
/// todo: need a Screen for that, not sure if adding a reference initialized in init() may cause GC problems.
}
@Override
public void updateNarration(NarrationElementOutput element_output)
{}
}
@OnlyIn(Dist.CLIENT)
public static class HorizontalProgressBar extends UiWidget
{
private final Coord2d texture_position_base_;
private final Coord2d texture_position_filled_;
private final ResourceLocation atlas_;
private double progress_max_ = 100;
private double progress_ = 0;
public HorizontalProgressBar(ResourceLocation atlas, int width, int height, Coord2d base_texture_xy, Coord2d filled_texture_xy)
{
super(0, 0, width, height, EMPTY_TEXT);
atlas_ = atlas;
texture_position_base_ = base_texture_xy;
texture_position_filled_ = filled_texture_xy;
}
public HorizontalProgressBar setProgress(double progress)
{ progress_ = Mth.clamp(progress, 0, progress_max_); return this; }
public double getProgress()
{ return progress_; }
public HorizontalProgressBar setMaxProgress(double progress)
{ progress_max_ = Math.max(progress, 0); return this; }
public double getMaxProgress()
{ return progress_max_; }
public HorizontalProgressBar show()
{ visible = true; return this; }
public HorizontalProgressBar hide()
{ visible = false; return this; }
@Override
public void playDownSound(SoundManager handler)
{}
@Override
protected void renderBg(PoseStack mx, Minecraft mc, int x, int y)
{}
@Override
public void renderButton(PoseStack mx, int mouseX, int mouseY, float partialTicks)
{
RenderSystem.setShaderTexture(0, atlas_);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
blit(mx, x, y, texture_position_base_.x, texture_position_base_.y, width, height);
if((progress_max_ > 0) && (progress_ > 0)) {
int w = Mth.clamp((int)Math.round((progress_ * width) / progress_max_), 0, width);
blit(mx, x, y, texture_position_filled_.x, texture_position_filled_.y, w, height);
}
}
}
@OnlyIn(Dist.CLIENT)
public static class BackgroundImage extends UiWidget
{
private final ResourceLocation atlas_;
private final Coord2d atlas_position_;
public boolean visible;
public BackgroundImage(ResourceLocation atlas, int width, int height, Coord2d atlas_position)
{
super(0, 0, width, height, EMPTY_TEXT);
atlas_ = atlas;
atlas_position_ = atlas_position;
this.width = width;
this.height = height;
visible = true;
}
public void draw(PoseStack mx, Screen parent)
{
if(!visible) return;
RenderSystem.setShaderTexture(0, atlas_);
parent.blit(mx, x, y, atlas_position_.x, atlas_position_.y, width, height);
}
}
@OnlyIn(Dist.CLIENT)
public static class CheckBox extends UiWidget
{
private final Coord2d texture_position_off_;
private final Coord2d texture_position_on_;
private final ResourceLocation atlas_;
private boolean checked_ = false;
private Consumer<CheckBox> on_click_ = (checkbox)->{};
public CheckBox(ResourceLocation atlas, int width, int height, Coord2d atlas_texture_position_off, Coord2d atlas_texture_position_on)
{
super(0, 0, width, height, EMPTY_TEXT);
texture_position_off_ = atlas_texture_position_off;
texture_position_on_ = atlas_texture_position_on;
atlas_ = atlas;
}
public boolean checked()
{ return checked_; }
public CheckBox checked(boolean on)
{ checked_ = on; return this; }
public CheckBox onclick(Consumer<CheckBox> action)
{ on_click_ = action; return this; }
@Override
public void onClick(double mouseX, double mouseY)
{ checked_ = !checked_; on_click_.accept(this); }
@Override
public void renderButton(PoseStack mx, int mouseX, int mouseY, float partialTicks)
{
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, atlas_);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
Coord2d pos = checked_ ? texture_position_on_ : texture_position_off_;
blit(mx, x, y, pos.x, pos.y, width, height);
}
}
}

View file

@ -1,7 +1,7 @@
# @file mods.toml # @file mods.toml
# @spec TOML v0.5.0 (https://github.com/toml-lang/toml) # @spec TOML v0.5.0 (https://github.com/toml-lang/toml)
modLoader="javafml" modLoader="javafml"
loaderVersion="[33,)" loaderVersion="[37,)"
issueTrackerURL="https://github.com/stfwi/engineers-decor/issues/" issueTrackerURL="https://github.com/stfwi/engineers-decor/issues/"
license="MIT" license="MIT"
@ -19,13 +19,13 @@ logoFile="logo.png"
[[dependencies.engineersdecor]] [[dependencies.engineersdecor]]
modId="forge" modId="forge"
mandatory=true mandatory=true
versionRange="[35.0.2,)" versionRange="[37,)"
ordering="NONE" ordering="NONE"
side="BOTH" side="BOTH"
[[dependencies.engineersdecor]] [[dependencies.engineersdecor]]
modId="minecraft" modId="minecraft"
mandatory=true mandatory=true
versionRange="[1.16.4,)" versionRange="[1.17.1,1.18)"
ordering="NONE" ordering="NONE"
side="BOTH" side="BOTH"

View file

@ -1,14 +0,0 @@
{
"variants": {
"": [
{ "model": "engineersdecor:block/concrete/gas_concrete_model0" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model1" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model2" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model3" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model4" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model5" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model6" },
{ "model": "engineersdecor:block/concrete/gas_concrete_model7" }
]
}
}

View file

@ -1,16 +0,0 @@
{
"variants": {
"tvariant=0,type=bottom": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s0v0_model" },
"tvariant=1,type=bottom": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s0v1_model" },
"tvariant=2,type=bottom": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s0v2_model" },
"tvariant=3,type=bottom": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s0v3_model" },
"tvariant=0,type=top": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s1v0_model" },
"tvariant=1,type=top": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s1v1_model" },
"tvariant=2,type=top": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s1v2_model" },
"tvariant=3,type=top": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s1v3_model" },
"tvariant=0,type=double": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s2v0_model" },
"tvariant=1,type=double": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s2v1_model" },
"tvariant=2,type=double": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s2v2_model" },
"tvariant=3,type=double": { "model": "engineersdecor:block/slab/specific/gas_concrete_slab_s2v3_model" }
}
}

View file

@ -1,44 +0,0 @@
{
"variants": {
"facing=east,half=bottom,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs" },
"facing=west,half=bottom,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "y": 180, "uvlock": true },
"facing=south,half=bottom,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "y": 90, "uvlock": true },
"facing=north,half=bottom,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "y": 270, "uvlock": true },
"facing=east,half=bottom,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer" },
"facing=west,half=bottom,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 180, "uvlock": true },
"facing=south,half=bottom,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 90, "uvlock": true },
"facing=north,half=bottom,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 270, "uvlock": true },
"facing=east,half=bottom,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 270, "uvlock": true },
"facing=west,half=bottom,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 90, "uvlock": true },
"facing=south,half=bottom,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer" },
"facing=north,half=bottom,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "y": 180, "uvlock": true },
"facing=east,half=bottom,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner" },
"facing=west,half=bottom,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 180, "uvlock": true },
"facing=south,half=bottom,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 90, "uvlock": true },
"facing=north,half=bottom,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 270, "uvlock": true },
"facing=east,half=bottom,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 270, "uvlock": true },
"facing=west,half=bottom,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 90, "uvlock": true },
"facing=south,half=bottom,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner" },
"facing=north,half=bottom,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "y": 180, "uvlock": true },
"facing=east,half=top,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "x": 180, "uvlock": true },
"facing=west,half=top,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "x": 180, "y": 180, "uvlock": true },
"facing=south,half=top,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "x": 180, "y": 90, "uvlock": true },
"facing=north,half=top,shape=straight": { "model": "engineersdecor:block/concrete/gas_concrete_stairs", "x": 180, "y": 270, "uvlock": true },
"facing=east,half=top,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 90, "uvlock": true },
"facing=west,half=top,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 270, "uvlock": true },
"facing=south,half=top,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 180, "uvlock": true },
"facing=north,half=top,shape=outer_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "uvlock": true },
"facing=east,half=top,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "uvlock": true },
"facing=west,half=top,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 180, "uvlock": true },
"facing=south,half=top,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 90, "uvlock": true },
"facing=north,half=top,shape=outer_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_outer", "x": 180, "y": 270, "uvlock": true },
"facing=east,half=top,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 90, "uvlock": true },
"facing=west,half=top,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 270, "uvlock": true },
"facing=south,half=top,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 180, "uvlock": true },
"facing=north,half=top,shape=inner_right": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "uvlock": true },
"facing=east,half=top,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "uvlock": true },
"facing=west,half=top,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 180, "uvlock": true },
"facing=south,half=top,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 90, "uvlock": true },
"facing=north,half=top,shape=inner_left": { "model": "engineersdecor:block/concrete/gas_concrete_stairs_inner", "x": 180, "y": 270, "uvlock": true }
}
}

View file

@ -1,13 +0,0 @@
{
"multipart": [
{ "when": { "up": "true" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_post" } },
{ "when": { "north": "low" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "uvlock": true } },
{ "when": { "east": "low" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 90, "uvlock": true } },
{ "when": { "south": "low" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 180, "uvlock": true } },
{ "when": { "west": "low" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 270, "uvlock": true } },
{ "when": { "north": "tall" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "uvlock": true } },
{ "when": { "east": "tall" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 90, "uvlock": true } },
{ "when": { "south": "tall" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 180, "uvlock": true } },
{ "when": { "west": "tall" }, "apply": { "model": "engineersdecor:block/concrete/gas_concrete_wall_side", "y": 270, "uvlock": true } }
]
}

View file

@ -1,19 +0,0 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_gas_concrete_se_model" }
}
}

View file

@ -1 +0,0 @@
{ "variants": { "": { "model": "engineersdecor:block/furniture/treated_wood_side_table_model" } } }

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Empty barrel: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Empty barrel: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB of §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB of §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Empty, max %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Empty, max %2$s mB",
"block.engineersdecor.gas_concrete": "Gas Concrete Block",
"block.engineersdecor.gas_concrete.help": "Low hardness, high production\n yield concrete. Easy to break\n decorative concrete block.",
"block.engineersdecor.gas_concrete_slab": "Gas Concrete Slab",
"block.engineersdecor.gas_concrete_slab.help": "Low hardness concrete slab.\n Easy to break decorative concrete.",
"block.engineersdecor.gas_concrete_stairs": "Gas Concrete Stairs",
"block.engineersdecor.gas_concrete_stairs.help": "Low hardness concrete stairs.\n Easy to break decorative concrete.",
"block.engineersdecor.gas_concrete_wall": "Gas Concrete Wall",
"block.engineersdecor.gas_concrete_wall.help": "Low hardness concrete wall.\n Easy to break decorative concrete.",
"block.engineersdecor.halfslab_gas_concrete": "Gas Concrete Slice",
"block.engineersdecor.halfslab_gas_concrete.help": "Vertically stackable slice.\n Right/left click with the slice\n stack on the top or bottom surface\n to add/remove slices.",
"block.engineersdecor.halfslab_rebar_concrete": "Rebar Concrete Slice", "block.engineersdecor.halfslab_rebar_concrete": "Rebar Concrete Slice",
"block.engineersdecor.halfslab_rebar_concrete.help": "Vertically stackable slice.\n Right/left click with the slice\n stack on the top or bottom surface\n to add/remove slices.", "block.engineersdecor.halfslab_rebar_concrete.help": "Vertically stackable slice.\n Right/left click with the slice\n stack on the top or bottom surface\n to add/remove slices.",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Aluminum Sheet Metal Slice", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Aluminum Sheet Metal Slice",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

View file

@ -113,16 +113,6 @@
"block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB", "block.engineersdecor.fluid_barrel.status.empty": "Barril vacío: §6%1$s§r / %2$s mB",
"block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r", "block.engineersdecor.fluid_barrel.status.tip": "§6%1$s§r / %2$s mB de §6%3$s§r",
"block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB", "block.engineersdecor.fluid_barrel.status.tip.empty": "Vacío, máx. %2$s mB",
"block.engineersdecor.gas_concrete": "Bloque de hormigón de baja dureza",
"block.engineersdecor.gas_concrete.help": "Hormigón de baja dureza y alta producción.\n Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_slab": "Losa de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_slab.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_stairs": "Escaleras de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_stairs.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.gas_concrete_wall": "Muro de hormigón de baja dureza",
"block.engineersdecor.gas_concrete_wall.help": "Bloque de hormigón decorativo fácil de romper.",
"block.engineersdecor.halfslab_gas_concrete": "Capa fina de hormigón de baja dureza",
"block.engineersdecor.halfslab_gas_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones.",
"block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado", "block.engineersdecor.halfslab_rebar_concrete": "Capa fina de hormigón armado",
"block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..", "block.engineersdecor.halfslab_rebar_concrete.help": "Porción apilable verticalmente.\n Haga clic derecho/izquierdo con la pila de porciones en la superficie superior o inferior para añadir/eliminar porciones..",
"block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio", "block.engineersdecor.halfslab_sheetmetal_aluminum": "Capa fina de enchapado de aluminio",

Some files were not shown because too many files have changed in this diff Show more