1.16 branched from develop.

This commit is contained in:
stfwi 2020-08-02 17:25:18 +02:00
parent f40453bf0e
commit 32612507f1
1178 changed files with 42198 additions and 2 deletions

5
.gitattributes vendored Normal file
View file

@ -0,0 +1,5 @@
# Disable autocrlf on generated files, they always generate with LF
# Add any extra files or paths here to make git stop saying they
# are changed when only line endings change.
src/generated/**/.cache/cache text eol=lf
src/generated/**/*.json text eol=lf

96
Makefile Normal file
View file

@ -0,0 +1,96 @@
# @file Makefile
# @author Stefan Wilhelm (wile)
# @license MIT
#
# GNU Make makefile based build relay.
# Note for reviewers/clones: This file is a auxiliary script for my setup.
# It's not needed to build the mod.
#
MOD_JAR_PREFIX=engineersdecor-
MOD_JAR=$(filter-out %-sources.jar,$(wildcard build/libs/${MOD_JAR_PREFIX}*.jar))
ifeq ($(OS),Windows_NT)
GRADLE=gradlew.bat --no-daemon
GRADLE_STOP=gradlew.bat --stop
DJS=djs
else
GRADLE=./gradlew --no-daemon
GRADLE_STOP=./gradlew --stop
DJS=djs
endif
TASK=$(DJS) ../meta/lib/tasks.js
wildcardr=$(foreach d,$(wildcard $1*),$(call wildcardr,$d/,$2) $(filter $(subst *,%,$2),$d))
#
# Targets
#
.PHONY: default mod data init clean clean-all mrproper all run install sanitize dist-check dist start-server assets
default: mod
all: clean clean-all mod | install
mod:
@echo "[1.16] Building mod using gradle ..."
@$(GRADLE) build $(GRADLE_OPTS)
assets:
@echo "[1.16] Running asset generators ..."
@$(TASK) assets
data:
@echo "[1.16] Running data generators ..."
@$(TASK) datagen
clean:
@echo "[1.16] Cleaning ..."
@rm -rf src/generated
@rm -rf mcmodsrepo
@rm -f build/libs/*
@$(GRADLE) clean
clean-all:
@echo "[1.16] Cleaning using gradle ..."
@rm -rf mcmodsrepo
@rm -f dist/*
@rm -rf run/logs/
@rm -rf run/crash-reports/
@$(GRADLE) clean
mrproper: clean-all
@rm -f meta/*.*
@rm -rf run/
@rm -rf out/
@rm -f .project
@rm -f .classpath
init:
@echo "[1.16] Initialising eclipse workspace using gradle ..."
@$(GRADLE) eclipse
sanitize:
@echo "[1.16] Running sanitising tasks ..."
@$(TASK) sanitize
@$(TASK) sync-languages
@$(TASK) version-check
@$(TASK) update-json
@git status -s .
install: $(MOD_JAR) |
@$(TASK) install
start-server: install
@$(TASK) start-server
dist-check:
@echo "[1.16] Running dist checks ..."
@$(TASK) dist-check
dist-files: clean-all init mod
@echo "[1.16] Distribution files ..."
@mkdir -p dist
@cp build/libs/$(MOD_JAR_PREFIX)* dist/
@$(TASK) dist
dist: sanitize dist-check dist-files

120
build.gradle Normal file
View file

@ -0,0 +1,120 @@
// @file build.gradle
// Engineer's decor mod gradle build relay (mc1.15.1)
import net.minecraftforge.gradle.common.task.SignJar
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
}
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
//----------------------------------------------------------------------------------------------------------------------
version = "${version_engineersdecor}"
group = "wile.engineersdecor"
archivesBaseName = "engineersdecor-${version_minecraft}"
repositories {
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
}
minecraft {
mappings channel: 'snapshot', version: "${version_fml_mappings}"
// accessTransformer = file('build/resources/main/META-INF/accesstransformer.cfg')
runs {
client {
workingDirectory project.file('run')
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug'
mods {
engineersdecor {
source sourceSets.main
}
}
}
server {
workingDirectory project.file('run')
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug'
mods {
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
}
}
}
}
}
dependencies {
minecraft "net.minecraftforge:forge:${version_forge_minecraft}"
compileOnly fg.deobf("mezz.jei:jei-${version_jei}:api")
runtimeOnly fg.deobf("mezz.jei:jei-${version_jei}")
}
processResources {
outputs.upToDateWhen { false } // thx to @tterrag for this hint
doLast { file("${sourceSets.main.output.resourcesDir}/.gitversion-engineersdecor").text = 'git log "-1" "--format=%h"'.execute().in.text.trim() }
}
jar {
manifest {
attributes([
"Specification-Title": "engineersdecor",
"Specification-Vendor": "wilechaote",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": "${version_engineersdecor}",
"Implementation-Vendor" :"wilechaote",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
def reobfFile = file("$buildDir/reobfJar/output.jar")
def reobfArtifact = artifacts.add('default', reobfFile) { type 'jar'; builtBy 'reobfJar' }
def signing = { ->
def sp = new Properties()
sp.keystore_file = project.keystore_file
sp.keystore_alias = project.keystore_alias
sp.keystore_pass = project.keystore_pass
sp.keystore_keypass = project.keystore_keypass
sp.fingerprint_sha1 = project.fingerprint_sha1
if(file("signing.properties").exists()) file("signing.properties").withInputStream { sp.load(it) }
return sp
}();
task signJar(type: SignJar, dependsOn: jar) {
onlyIf { signing.hasProperty("keystore_file") }
if(signing.hasProperty("keystore_file")) {
keyStore = signing.getProperty("keystore_file")
alias = signing.getProperty("keystore_alias")
storePass = signing.getProperty("keystore_pass")
keyPass = signing.getProperty("keystore_keypass")
inputFile = jar.archivePath
outputFile = jar.archivePath
}
}
build.dependsOn signJar
publishing {
publications { mavenJava(MavenPublication) { artifact reobfArtifact } }
repositories { maven { url "file:///${project.projectDir}/mcmodsrepo" } }
}

8
gradle.properties Normal file
View file

@ -0,0 +1,8 @@
# @file gradle.properties
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.16.1
version_forge_minecraft=1.16.1-32.0.97
version_fml_mappings=20200514-1.16
version_jei=1.16.1:7.0.0.6
version_engineersdecor=1.1.1-b6

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

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

172
gradlew vendored Normal file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@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=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.16.1": {
"1.1.1-b5": "[M] Transmuted the \"Treated Wood Crafting Table\" to a \"Metal Crafting Table\" to de-duplicate the new Immersive Engineering \"Engineer's Crafting Table\".\n[F] Fixed metal pole culling (issue #109, thx Alsett).",
"1.1.1-b4": "[F] Fixed Fluid Barrel tooltip text duplicate.\n[F] Fixed IE hard-dependency opt-out.\n[M] Side Table model and shape refined.\n[A] Dense Grit Dirt added.\n[M] Wood textures slightly darker, obsolete textures removed.",
"1.1.1-b3": "[A] Ceiling Edge Light added.\n[A] Iron Bulb Light added.\n[A] Iron Hatch added.\n[A] Fluid Barrel added.\n[M] Gas Concrete texture made slightly darker.",
"1.1.1-b2": "[M] Nerfed Solar Panel output default config value from 45RF/t to 40RF/t.\n[F] Fixed conditional IE tag dependency of alternative/standalone recipes.",
@ -8,7 +9,7 @@
"1.1.1-a1": "[A] Initial port."
},
"promos": {
"1.16.1-recommended": "1.1.1-b4",
"1.16.1-latest": "1.1.1-b4"
"1.16.1-recommended": "1.1.1-b5",
"1.16.1-latest": "1.1.1-b5"
}
}

42
readme.md Normal file
View file

@ -0,0 +1,42 @@
## Engineer's Decor (MC1.15.1)
Mod sources for Minecraft version 1.15.1.
- Description, credits, and features: Please see the readme in the repository root.
- Compiled mod distribution channel is curseforge: https://www.curseforge.com/minecraft/mc-mods/engineers-decor/files.
----
## Version history
~ v1.1.1-b6 [F] Iron Hatch rendering fixed (issue #113, ty KrAzYGEEK32).
- v1.1.1-b5 [M] Transmuted the "Treated Wood Crafting Table" to a "Metal Crafting Table"
to de-duplicate the new Immersive Engineering "Engineer's Crafting Table".
[F] Fixed metal pole culling (issue #109, thx Alsett).
- v1.1.1-b4 [F] Fixed Fluid Barrel tooltip text duplicate.
[F] Fixed IE hard-dependency opt-out.
[M] Side Table model and shape refined.
[A] Dense Grit Dirt added.
[M] Wood textures slightly darker, obsolete textures removed.
- v1.1.1-b3 [A] Ceiling Edge Light added.
[A] Iron Bulb Light added.
[A] Iron Hatch added.
[A] Fluid Barrel added.
[M] Gas Concrete texture made slightly darker.
- v1.1.1-b2 [M] Nerfed Solar Panel output default config value from 45RF/t to 40RF/t.
[F] Fixed conditional IE tag dependency of alternative/standalone recipes.
- v1.1.1-b1 [A] JEI integration ported.
[M] Logical server side config moved from COMMON to SERVER (world/serverconfig).
[F] Labeled Crate recipe condition for missing Immersive Engineering fixed.
[F] Mouse scrolling works for Labeled Crate and TW Crafting Table.
- v1.1.1-a1 [A] Initial port.
----

View file

@ -0,0 +1,707 @@
/*
* @file ModConfig.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Main class for module settings. Handles reading and
* saving the config file.
*/
package wile.engineersdecor;
import wile.engineersdecor.blocks.*;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraftforge.common.ForgeConfigSpec;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
public class ModConfig
{
//--------------------------------------------------------------------------------------------------------------------
private static final Logger LOGGER = ModEngineersDecor.logger();
private static final String MODID = ModEngineersDecor.MODID;
public static final CommonConfig COMMON;
public static final ServerConfig SERVER;
public static final ClientConfig CLIENT;
public static final ForgeConfigSpec COMMON_CONFIG_SPEC;
public static final ForgeConfigSpec SERVER_CONFIG_SPEC;
public static final ForgeConfigSpec CLIENT_CONFIG_SPEC;
static {
final Pair<CommonConfig, ForgeConfigSpec> common_ = (new ForgeConfigSpec.Builder()).configure(CommonConfig::new);
COMMON_CONFIG_SPEC = common_.getRight();
COMMON = common_.getLeft();
final Pair<ServerConfig, ForgeConfigSpec> server_ = (new ForgeConfigSpec.Builder()).configure(ServerConfig::new);
SERVER_CONFIG_SPEC = server_.getRight();
SERVER = server_.getLeft();
final Pair<ClientConfig, ForgeConfigSpec> client_ = (new ForgeConfigSpec.Builder()).configure(ClientConfig::new);
CLIENT_CONFIG_SPEC = client_.getRight();
CLIENT = client_.getLeft();
}
//--------------------------------------------------------------------------------------------------------------------
public static class ClientConfig
{
public final ForgeConfigSpec.BooleanValue without_tooltips;
public final ForgeConfigSpec.BooleanValue without_ters;
ClientConfig(ForgeConfigSpec.Builder builder)
{
builder.comment("Settings not loaded on servers.")
.push("client");
// --- OPTOUTS ------------------------------------------------------------
{
builder.comment("Opt-out settings")
.push("optout");
without_tooltips = builder
.translation(MODID + ".config.without_tooltips")
.comment("Disable CTRL-SHIFT item tooltip display.")
.define("without_tooltips", false);
without_ters = builder
.translation(MODID + ".config.without_ters")
.comment("Disable all TERs (tile entity renderers).")
.define("without_ters", false);
}
builder.pop();
}
}
//--------------------------------------------------------------------------------------------------------------------
public static class CommonConfig
{
CommonConfig(ForgeConfigSpec.Builder builder)
{
}
}
//--------------------------------------------------------------------------------------------------------------------
public static class ServerConfig
{
// Optout
public final ForgeConfigSpec.ConfigValue<String> pattern_excludes;
public final ForgeConfigSpec.ConfigValue<String> pattern_includes;
public final ForgeConfigSpec.BooleanValue without_clinker_bricks;
public final ForgeConfigSpec.BooleanValue without_slag_bricks;
public final ForgeConfigSpec.BooleanValue without_rebar_concrete;
public final ForgeConfigSpec.BooleanValue without_gas_concrete;
public final ForgeConfigSpec.BooleanValue without_walls;
public final ForgeConfigSpec.BooleanValue without_stairs;
public final ForgeConfigSpec.BooleanValue without_ie_concrete_wall;
public final ForgeConfigSpec.BooleanValue without_panzer_glass;
public final ForgeConfigSpec.BooleanValue without_ladders;
public final ForgeConfigSpec.BooleanValue without_treated_wood_furniture;
public final ForgeConfigSpec.BooleanValue without_metal_furniture;
public final ForgeConfigSpec.BooleanValue without_windows;
public final ForgeConfigSpec.BooleanValue without_light_sources;
public final ForgeConfigSpec.BooleanValue without_slabs;
public final ForgeConfigSpec.BooleanValue without_halfslabs;
public final ForgeConfigSpec.BooleanValue without_poles;
public final ForgeConfigSpec.BooleanValue without_hsupports;
public final ForgeConfigSpec.BooleanValue without_sign_plates;
public final ForgeConfigSpec.BooleanValue without_floor_grating;
public final ForgeConfigSpec.BooleanValue without_crafting_table;
public final ForgeConfigSpec.BooleanValue without_lab_furnace;
public final ForgeConfigSpec.BooleanValue without_electrical_furnace;
public final ForgeConfigSpec.BooleanValue without_valves;
public final ForgeConfigSpec.BooleanValue without_passive_fluid_accumulator;
public final ForgeConfigSpec.BooleanValue without_waste_incinerator;
public final ForgeConfigSpec.BooleanValue without_factory_dropper;
public final ForgeConfigSpec.BooleanValue without_factory_hopper;
public final ForgeConfigSpec.BooleanValue without_factory_placer;
public final ForgeConfigSpec.BooleanValue without_block_breaker;
public final ForgeConfigSpec.BooleanValue without_solar_panel;
public final ForgeConfigSpec.BooleanValue without_fluid_funnel;
public final ForgeConfigSpec.BooleanValue without_mineral_smelter;
public final ForgeConfigSpec.BooleanValue without_milking_machine;
public final ForgeConfigSpec.BooleanValue without_tree_cutter;
public final ForgeConfigSpec.BooleanValue without_labeled_crate;
public final ForgeConfigSpec.BooleanValue without_fences;
public final ForgeConfigSpec.BooleanValue without_chair_sitting;
public final ForgeConfigSpec.BooleanValue without_mob_chair_sitting;
public final ForgeConfigSpec.BooleanValue without_ladder_speed_boost;
public final ForgeConfigSpec.BooleanValue without_crafting_table_history;
public final ForgeConfigSpec.BooleanValue without_direct_slab_pickup;
public final ForgeConfigSpec.BooleanValue with_creative_mode_device_drops;
// Misc
public final ForgeConfigSpec.BooleanValue with_experimental;
public final ForgeConfigSpec.BooleanValue without_recipes;
// Tweaks
public final ForgeConfigSpec.IntValue furnace_smelting_speed_percent;
public final ForgeConfigSpec.IntValue furnace_fuel_efficiency_percent;
public final ForgeConfigSpec.IntValue furnace_boost_energy_consumption;
public final ForgeConfigSpec.IntValue e_furnace_speed_percent;
public final ForgeConfigSpec.IntValue e_furnace_power_consumption;
public final ForgeConfigSpec.IntValue small_solar_panel_peak_production;
public final ForgeConfigSpec.BooleanValue e_furnace_automatic_pulling;
public final ForgeConfigSpec.DoubleValue chair_mob_sitting_probability_percent;
public final ForgeConfigSpec.DoubleValue chair_mob_standup_probability_percent;
public final ForgeConfigSpec.BooleanValue without_crafting_mouse_scrolling;
public final ForgeConfigSpec.IntValue pipevalve_max_flowrate;
public final ForgeConfigSpec.IntValue pipevalve_redstone_gain;
public final ForgeConfigSpec.IntValue block_breaker_power_consumption;
public final ForgeConfigSpec.IntValue block_breaker_reluctance;
public final ForgeConfigSpec.IntValue block_breaker_min_breaking_time;
public final ForgeConfigSpec.BooleanValue block_breaker_requires_power;
public final ForgeConfigSpec.IntValue tree_cuttter_energy_consumption;
public final ForgeConfigSpec.IntValue tree_cuttter_cutting_time_needed;
public final ForgeConfigSpec.BooleanValue tree_cuttter_requires_power;
public final ForgeConfigSpec.IntValue milking_machine_energy_consumption;
public final ForgeConfigSpec.IntValue milking_machine_milking_delay;
ServerConfig(ForgeConfigSpec.Builder builder)
{
builder.comment("Settings affecting the logical server side.")
.push("server");
// --- OPTOUTS ------------------------------------------------------------
{
builder.comment("Opt-out settings")
.push("optout");
pattern_excludes = builder
.translation(MODID + ".config.pattern_excludes")
.comment("Opt-out any block by its registry name ('*' wildcard matching, "
+ "comma separated list, whitespaces ignored. You must match the whole name, "
+ "means maybe add '*' also at the begin and end. Example: '*wood*,*steel*' "
+ "excludes everything that has 'wood' or 'steel' in the registry name. "
+ "The matching result is also traced in the log file. ")
.define("pattern_excludes", "");
pattern_includes = builder
.translation(MODID + ".config.pattern_includes")
.comment("Prevent blocks from being opt'ed by registry name ('*' wildcard matching, "
+ "comma separated list, whitespaces ignored. Evaluated before all other opt-out checks. "
+ "You must match the whole name, means maybe add '*' also at the begin and end. Example: "
+ "'*wood*,*steel*' includes everything that has 'wood' or 'steel' in the registry name."
+ "The matching result is also traced in the log file.")
.define("pattern_includes", "");
without_clinker_bricks = builder
.translation(MODID + ".config.without_clinker_bricks")
.comment("Disable clinker bricks and derived blocks.")
.define("without_clinker_bricks", false);
without_slag_bricks = builder
.translation(MODID + ".config.without_slag_bricks")
.comment("Disable slag bricks and derived blocks.")
.define("without_slag_bricks", false);
without_rebar_concrete = builder
.translation(MODID + ".config.without_rebar_concrete")
.comment("Disable rebar concrete and derived blocks.")
.define("without_rebar_concrete", false);
without_gas_concrete = builder
.translation(MODID + ".config.without_gas_concrete")
.comment("Disable gas concrete and derived blocks.")
.define("without_gas_concrete", false);
without_walls = builder
.translation(MODID + ".config.without_walls")
.comment("Disable all mod wall blocks.")
.define("without_walls", false);
without_stairs = builder
.translation(MODID + ".config.without_stairs")
.comment("Disable all mod stairs blocks.")
.define("without_stairs", false);
without_ie_concrete_wall = builder
.translation(MODID + ".config.without_ie_concrete_wall")
.comment("Disable IE concrete wall.")
.define("without_ie_concrete_wall", false);
without_panzer_glass = builder
.translation(MODID + ".config.without_panzer_glass")
.comment("Disable panzer glass and derived blocks.")
.define("without_panzer_glass", false);
without_crafting_table = builder
.translation(MODID + ".config.without_crafting_table")
.comment("Disable crafting table.")
.define("without_crafting_table", false);
without_lab_furnace = builder
.translation(MODID + ".config.without_lab_furnace")
.comment("Disable small lab furnace.")
.define("without_lab_furnace", false);
without_electrical_furnace = builder
.translation(MODID + ".config.without_electrical_furnace")
.comment("Disable small electrical pass-through furnace.")
.define("without_electrical_furnace", false);
without_treated_wood_furniture = builder
.translation(MODID + ".config.without_treated_wood_furniture")
.comment("Disable treated wood table, stool, windowsill, etc.")
.define("without_treated_wood_furniture", false);
without_metal_furniture = builder
.translation(MODID + ".config.without_metal_furniture")
.comment("Disable metal tables, etc.")
.define("without_metal_furniture", false);
without_windows = builder
.translation(MODID + ".config.without_windows")
.comment("Disable treated wood window, etc.")
.define("without_windows", false);
without_light_sources = builder
.translation(MODID + ".config.without_light_sources")
.comment("Disable light sources")
.define("without_light_sources", false);
without_ladders = builder
.translation(MODID + ".config.without_ladders")
.comment("Disable ladders")
.define("without_ladders", false);
without_chair_sitting = builder
.translation(MODID + ".config.without_chair_sitting")
.comment("Disable possibility to sit on stools and chairs.")
.define("without_chair_sitting", false);
without_mob_chair_sitting = builder
.translation(MODID + ".config.without_mob_chair_sitting")
.comment("Disable that mobs will sit on chairs and stools.")
.define("without_mob_chair_sitting", false);
without_ladder_speed_boost = builder
.translation(MODID + ".config.without_ladder_speed_boost")
.comment("Disable the speed boost of ladders in this mod.")
.define("without_ladder_speed_boost", false);
without_crafting_table_history = builder
.translation(MODID + ".config.without_crafting_table_history")
.comment("Disable history refabrication feature of the crafting table.")
.define("without_crafting_table_history", false);
without_valves = builder
.translation(MODID + ".config.without_valves")
.comment("Disable check valve, and redstone controlled valves.")
.define("without_valves", false);
without_passive_fluid_accumulator = builder
.translation(MODID + ".config.without_passive_fluid_accumulator")
.comment("Disable the passive fluid accumulator.")
.define("without_passive_fluid_accumulator", false);
without_waste_incinerator = builder
.translation(MODID + ".config.without_waste_incinerator")
.comment("Disable item disposal/trash/void incinerator device.")
.define("without_waste_incinerator", false);
without_sign_plates = builder
.translation(MODID + ".config.without_sign_plates")
.comment("Disable decorative sign plates (caution, hazards, etc).")
.define("without_sign_plates", false);
without_floor_grating = builder
.translation(MODID + ".config.without_floor_grating")
.comment("Disable floor gratings.")
.define("without_floor_grating", false);
without_factory_dropper = builder
.translation(MODID + ".config.without_factory_dropper")
.comment("Disable the factory dropper.")
.define("without_factory_dropper", false);
without_factory_hopper = builder
.translation(MODID + ".config.without_factory_hopper")
.comment("Disable the factory hopper.")
.define("without_factory_hopper", false);
without_factory_placer = builder
.translation(MODID + ".config.without_factory_placer")
.comment("Disable the factory placer.")
.define("without_factory_placer", false);
without_block_breaker = builder
.translation(MODID + ".config.without_block_breaker")
.comment("Disable the small block breaker.")
.define("without_block_breaker", false);
without_solar_panel = builder
.translation(MODID + ".config.without_solar_panel")
.comment("Disable the small solar panel.")
.define("without_solar_panel", false);
without_fluid_funnel = builder
.translation(MODID + ".config.without_fluid_funnel")
.comment("Disable the small fluid collection funnel.")
.define("without_fluid_funnel", false);
without_mineral_smelter = builder
.translation(MODID + ".config.without_mineral_smelter")
.comment("Disable the small mineral smelter.")
.define("without_mineral_smelter", false);
without_milking_machine = builder
.translation(MODID + ".config.without_milking_machine")
.comment("Disable the small milking machine.")
.define("without_milking_machine", false);
without_tree_cutter = builder
.translation(MODID + ".config.without_tree_cutter")
.comment("Disable the small tree cutter.")
.define("without_tree_cutter", false);
without_labeled_crate = builder
.translation(MODID + ".config.without_labeled_crate")
.comment("Disable labeled crate.")
.define("without_labeled_crate", false);
without_slabs = builder
.translation(MODID + ".config.without_slabs")
.comment("Disable horizontal half-block slab.")
.define("without_slabs", false);
without_halfslabs = builder
.translation(MODID + ".config.without_halfslabs")
.comment("Disable stackable 1/8 block slices.")
.define("without_halfslabs", false);
without_poles = builder
.translation(MODID + ".config.without_poles")
.comment("Disable poles of any material.")
.define("without_poles", false);
without_hsupports = builder
.translation(MODID + ".config.without_hsupports")
.comment("Disable horizontal supports like the double-T support.")
.define("without_hsupports", false);
without_recipes = builder
.translation(MODID + ".config.without_recipes")
.comment("Disable all internal recipes, allowing to use alternative pack recipes.")
.define("without_recipes", false);
without_fences = builder
.translation(MODID + ".config.without_fences")
.comment("Disable all fences and fence gates.")
.define("without_fences", false);
builder.pop();
}
// --- MISC ---------------------------------------------------------------
{
builder.comment("Miscellaneous settings")
.push("miscellaneous");
with_experimental = builder
.translation(MODID + ".config.with_experimental")
.comment("Enables experimental features. Use at own risk.")
.define("with_experimental", false);
without_direct_slab_pickup = builder
.translation(MODID + ".config.without_direct_slab_pickup")
.comment("Disable directly picking up layers from slabs and slab " +
" slices by left clicking while looking up/down.")
.define("without_direct_slab_pickup", false);
with_creative_mode_device_drops = builder
.translation(MODID + ".config.with_creative_mode_device_drops")
.comment("Enable that devices are dropped as item also in creative mode, allowing " +
" to relocate them with contents and settings.")
.define("with_creative_mode_device_drops", false);
builder.pop();
}
// --- TWEAKS -------------------------------------------------------------
{
builder.comment("Tweaks")
.push("tweaks");
furnace_smelting_speed_percent = builder
.translation(MODID + ".config.furnace_smelting_speed_percent")
.comment("Defines, in percent, how fast the lab furnace smelts compared to " +
"a vanilla furnace. 100% means vanilla furnace speed, 150% means the " +
"lab furnace is faster. The value can be changed on-the-fly for tuning.")
.defineInRange("furnace_smelting_speed_percent", 130, 50, 800);
furnace_fuel_efficiency_percent = builder
.translation(MODID + ".config.furnace_fuel_efficiency_percent")
.comment("Defines, in percent, how fuel efficient the lab furnace is, compared " +
"to a vanilla furnace. 100% means vanilla furnace consumiton, 200% means " +
"the lab furnace needs about half the fuel of a vanilla furnace, " +
"The value can be changed on-the-fly for tuning.")
.defineInRange("furnace_fuel_efficiency_percent", 100, 50, 400);
furnace_boost_energy_consumption = builder
.translation(MODID + ".config.furnace_boost_energy_consumption")
.comment("Defines the energy consumption (per tick) for speeding up the smelting process. " +
"If IE is installed, an external heater has to be inserted into an auxiliary slot " +
"of the lab furnace. The power source needs to be able to provide at least 4 times " +
"this consumption (fixed threshold value). The value can be changed on-the-fly for tuning. " +
"The default value corresponds to the IE heater consumption.")
.defineInRange("furnace_boost_energy_consumption", 24, 2, 1024);
chair_mob_sitting_probability_percent = builder
.translation(MODID + ".config.chair_mob_sitting_probability_percent")
.comment("Defines, in percent, how high the probability is that a mob sits on a chair " +
"when colliding with it. Can be changed on-the-fly for tuning.")
.defineInRange("chair_mob_sitting_probability_percent", 10.0, 0.0, 80.0);
chair_mob_standup_probability_percent = builder
.translation(MODID + ".config.chair_mob_standup_probability_percent")
.comment("Defines, in percent, probable it is that a mob leaves a chair when sitting " +
"on it. The 'dice is rolled' about every 20 ticks. There is also a minimum " +
"Sitting time of about 3s. The config value can be changed on-the-fly for tuning.")
.defineInRange("chair_mob_standup_probability_percent", 1.0, 1e-3, 10.0);
without_crafting_mouse_scrolling = builder
.translation(MODID + ".config.without_crafting_mouse_scrolling")
.comment("Disables increasing/decreasing the crafting grid items by scrolling over the crafting result slot.")
.define("without_crafting_mouse_scrolling", false);
pipevalve_max_flowrate = builder
.translation(MODID + ".config.pipevalve_max_flowrate")
.comment("Defines how many millibuckets can be transferred (per tick) through the valves. " +
"That is technically the 'storage size' specified for blocks that want to fill " +
"fluids into the valve (the valve has no container and forward that to the output " +
"block), The value can be changed on-the-fly for tuning. ")
.defineInRange("pipevalve_max_flowrate", 1000, 1, 32000);
pipevalve_redstone_gain = builder
.translation(MODID + ".config.pipevalve_redstone_gain")
.comment("Defines how many millibuckets per redstone signal strength can be transferred per tick " +
"through the analog redstone controlled valves. Note: power 0 is always off, power 15 is always " +
"the max flow rate. Between power 1 and 14 this scaler will result in a flow = 'redstone slope' * 'current redstone power'. " +
"The value can be changed on-the-fly for tuning. ")
.defineInRange("pipevalve_redstone_gain", 20, 1, 32000);
e_furnace_speed_percent = builder
.translation(MODID + ".config.e_furnace_speed_percent")
.comment("Defines, in percent, how fast the electrical furnace smelts compared to " +
"a vanilla furnace. 100% means vanilla furnace speed, 150% means the " +
"electrical furnace is faster. The value can be changed on-the-fly for tuning.")
.defineInRange("e_furnace_speed_percent", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_SPEED_PERCENT, 50, 800);
e_furnace_power_consumption = builder
.translation(MODID + ".config.e_furnace_power_consumption")
.comment("Defines how much RF per tick the the electrical furnace consumed (average) for smelting. " +
"The feeders transferring items from/to adjacent have this consumption/8 for each stack transaction. " +
"The default value is only slightly higher than a furnace with an IE external heater (and no burning fuel inside)." +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("e_furnace_power_consumption", EdElectricalFurnace.ElectricalFurnaceTileEntity.DEFAULT_ENERGY_CONSUMPTION, 8, 4096);
e_furnace_automatic_pulling = builder
.translation(MODID + ".config.e_furnace_automatic_pulling")
.comment("Defines if the electrical furnace automatically pulls items from an inventory at the input side." +
"The config value can be changed on-the-fly for tuning.")
.define("e_furnace_automatic_pulling", false);
small_solar_panel_peak_production = builder
.translation(MODID + ".config.small_solar_panel_peak_production")
.comment("Defines the peak power production (at noon) of the Small Solar Panel. " +
"Note that the agerage power is much less, as no power is produced at all during the night, " +
"and the power curve is nonlinear rising/falling during the day. Bad weather conditions also " +
"decrease the production. The config value can be changed on-the-fly for tuning.")
.defineInRange("small_solar_panel_peak_production", EdSolarPanel.SolarPanelTileEntity.DEFAULT_PEAK_POWER, 2, 4096);
block_breaker_power_consumption = builder
.translation(MODID + ".config.block_breaker_power_consumption")
.comment("Defines how much RF power the Small Block Breaker requires to magnificently increase the processing speed. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("block_breaker_power_consumption", EdBreaker.BreakerTileEntity.DEFAULT_BOOST_ENERGY, 4, 1024);
block_breaker_reluctance = builder
.translation(MODID + ".config.block_breaker_reluctance")
.comment("Defines how much time the Small Block Breaker needs per block hardness, " +
"means: 'reluctance' * hardness + min_time, you change the 'reluctance' here." +
"The unit is ticks/hardness. " + "The config value can be changed on-the-fly for tuning.")
.defineInRange("block_breaker_reluctance", EdBreaker.BreakerTileEntity.DEFAULT_BREAKING_RELUCTANCE, 5, 50);
block_breaker_min_breaking_time = builder
.translation(MODID + ".config.block_breaker_min_breaking_time")
.comment("Defines how much time the Small Block Breaker needs at least, better said it's an offset: " +
"'reluctance' * hardness + min_time, you change the 'min_time' here, value " +
"in ticks." + "The config value can be changed on-the-fly for tuning.")
.defineInRange("block_breaker_min_breaking_time", EdBreaker.BreakerTileEntity.DEFAULT_MIN_BREAKING_TIME, 10, 100);
block_breaker_requires_power = builder
.translation(MODID + ".config.block_breaker_requires_power")
.comment("Defines if the Small Block Breaker does not work without RF power.")
.define("block_breaker_requires_power", false);
tree_cuttter_energy_consumption = builder
.translation(MODID + ".config.tree_cuttter_energy_consumption")
.comment("Defines how much RF power the Small Tree Cutter requires to magnificently increase the processing speed. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("tree_cuttter_energy_consumption", EdTreeCutter.TreeCutterTileEntity.DEFAULT_BOOST_ENERGY, 4, 1024);
tree_cuttter_cutting_time_needed = builder
.translation(MODID + ".config.tree_cuttter_cutting_time_needed")
.comment("Defines how much time the Small Tree Cutter needs to cut a tree without RF power. " +
"The value is in seconds. With energy it is 6 times faster. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("tree_cuttter_cutting_time_needed", EdTreeCutter.TreeCutterTileEntity.DEFAULT_CUTTING_TIME_NEEDED, 10, 240);
tree_cuttter_requires_power = builder
.translation(MODID + ".config.tree_cuttter_requires_power")
.comment("Defines if the Small Tree Cutter does not work without RF power.")
.define("tree_cuttter_requires_power", false);
milking_machine_energy_consumption = builder
.translation(MODID + ".config.milking_machine_energy_consumption")
.comment("Defines how much time the Small Milking Machine needs work. " +
"Note this is a permanent standby power, not only when the device does something. " +
"Use zero to disable energy dependency and energy handling of the machine. " +
"The config value can be changed on-the-fly for tuning.")
.defineInRange("milking_machine_energy_consumption", EdMilker.MilkerTileEntity.DEFAULT_ENERGY_CONSUMPTION, 0, 1024);
milking_machine_milking_delay = builder
.translation(MODID + ".config.milking_machine_milking_delay")
.comment("Defines (for each individual cow) the minimum time between milking." )
.defineInRange("milking_machine_milking_delay", EdMilker.MilkerTileEntity.DEFAULT_MILKING_DELAY_PER_COW, 1000, 24000);
builder.pop();
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// Optout checks
//--------------------------------------------------------------------------------------------------------------------
public static final boolean isOptedOut(final @Nullable Block block)
{ return isOptedOut(block.asItem()); }
public static final boolean isOptedOut(final @Nullable Item item)
{ return (item!=null) && optouts_.contains(item.getRegistryName().getPath()); }
public static boolean withExperimental()
{ return with_experimental_features_; }
public static boolean withoutRecipes()
{ return without_recipes_; }
//--------------------------------------------------------------------------------------------------------------------
// Cache
//--------------------------------------------------------------------------------------------------------------------
private static final CompoundNBT server_config_ = new CompoundNBT();
private static HashSet<String> optouts_ = new HashSet<>();
private static boolean with_experimental_features_ = false;
private static boolean without_recipes_ = false;
public static boolean without_crafting_table = false;
public static boolean immersiveengineering_installed = false;
public static boolean without_direct_slab_pickup = false;
public static boolean with_creative_mode_device_drops = false;
public static final CompoundNBT getServerConfig() // config that may be synchronized from server to client via net pkg.
{ return server_config_; }
private static final void updateOptouts()
{
final ArrayList<String> includes_ = new ArrayList<String>();
final ArrayList<String> excludes_ = new ArrayList<String>();
{
String inc = SERVER.pattern_includes.get().toLowerCase().replaceAll(MODID+":", "").replaceAll("[^*_,a-z0-9]", "");
if(SERVER.pattern_includes.get() != inc) SERVER.pattern_includes.set(inc);
if(!inc.isEmpty()) LOGGER.info("Config pattern includes: '" + inc + "'");
String[] incl = inc.split(",");
includes_.clear();
for(int i=0; i< incl.length; ++i) {
incl[i] = incl[i].replaceAll("[*]", ".*?");
if(!incl[i].isEmpty()) includes_.add(incl[i]);
}
}
{
String exc = SERVER.pattern_excludes.get().toLowerCase().replaceAll(MODID+":", "").replaceAll("[^*_,a-z0-9]", "");
if(!exc.isEmpty()) LOGGER.info("Config pattern excludes: '" + exc + "'");
String[] excl = exc.split(",");
excludes_.clear();
for(int i=0; i< excl.length; ++i) {
excl[i] = excl[i].replaceAll("[*]", ".*?");
if(!excl[i].isEmpty()) excludes_.add(excl[i]);
}
}
{
boolean with_log_details = false;
HashSet<String> optouts = new HashSet<>();
ModContent.getRegisteredItems().stream().filter((Item item) -> {
if(item == null) return true;
if(SERVER == null) return false;
return false;
}).forEach(
e -> optouts.add(e.getRegistryName().getPath())
);
ModContent.getRegisteredBlocks().stream().filter((Block block) -> {
if(block==null) return true;
if(block==ModContent.SIGN_MODLOGO) return true;
if(COMMON==null) return false;
try {
if(!SERVER.with_experimental.get()) {
if(block instanceof Auxiliaries.IExperimentalFeature) return true;
if(ModContent.isExperimentalBlock(block)) return true;
}
// Hard IE dependent blocks
if(!immersiveengineering_installed) {
if((block instanceof IDecorBlock) && ((((IDecorBlock)block).config() & DecorBlock.CFG_HARD_IE_DEPENDENT)!=0)) return true;
if((block instanceof DecorBlock.Normal) && ((((DecorBlock.Normal)block).config & DecorBlock.CFG_HARD_IE_DEPENDENT)!=0)) return true;
if((block instanceof StandardBlocks.BaseBlock) && ((((StandardBlocks.BaseBlock)block).config & DecorBlock.CFG_HARD_IE_DEPENDENT)!=0)) return true;
}
// Force-include/exclude pattern matching
final String rn = block.getRegistryName().getPath();
try {
for(String e : includes_) {
if(rn.matches(e)) {
if(with_log_details) LOGGER.info("Optout force include: "+rn);
return false;
}
}
for(String e : excludes_) {
if(rn.matches(e)) {
if(with_log_details) LOGGER.info("Optout force exclude: "+rn);
return true;
}
}
} catch(Throwable ex) {
LOGGER.error("optout include pattern failed, disabling.");
includes_.clear();
excludes_.clear();
}
// Early non-opt out type based evaluation
if(block instanceof EdCraftingTable.CraftingTableBlock) return SERVER.without_crafting_table.get();
if(block instanceof EdElectricalFurnace.ElectricalFurnaceBlock) return SERVER.without_electrical_furnace.get();
if((block instanceof EdFurnace.FurnaceBlock)&&(!(block instanceof EdElectricalFurnace.ElectricalFurnaceBlock))) return SERVER.without_lab_furnace.get();
if(block instanceof EdFluidAccumulator.FluidAccumulatorBlock) return SERVER.without_passive_fluid_accumulator.get();
if(block instanceof EdWasteIncinerator.WasteIncineratorBlock) return SERVER.without_waste_incinerator.get();
if(block instanceof EdDropper.DropperBlock) return SERVER.without_factory_dropper.get();
if(block instanceof EdPlacer.PlacerBlock) return SERVER.without_factory_placer.get();
if(block instanceof EdBreaker.BreakerBlock) return SERVER.without_block_breaker.get();
if(block instanceof EdSlabSliceBlock) return SERVER.without_halfslabs.get();
if(block instanceof EdLadderBlock) return SERVER.without_ladders.get();
if(block instanceof EdWindowBlock) return SERVER.without_windows.get();
if(block instanceof EdPipeValve.PipeValveBlock) return SERVER.without_valves.get();
if(block instanceof EdHorizontalSupportBlock) return SERVER.without_hsupports.get();
if(block instanceof EdFloorGratingBlock) return SERVER.without_floor_grating.get();
if(block instanceof EdHopper.HopperBlock) return SERVER.without_factory_hopper.get();
if(block instanceof EdFluidFunnel.FluidFunnelBlock) return SERVER.without_fluid_funnel.get();
if(block instanceof EdSolarPanel.SolarPanelBlock) return SERVER.without_solar_panel.get();
if(block instanceof EdMineralSmelter.MineralSmelterBlock) return SERVER.without_mineral_smelter.get();
if(block instanceof EdMilker.MilkerBlock) return SERVER.without_milking_machine.get();
if(block instanceof EdTreeCutter.TreeCutterBlock) return SERVER.without_tree_cutter.get();
if(block instanceof EdLabeledCrate.LabeledCrateBlock) return SERVER.without_labeled_crate.get();
// Type based evaluation where later filters may match, too
if(SERVER.without_slabs.get()&&(block instanceof EdSlabBlock)) return true;
if(SERVER.without_stairs.get()&&(block instanceof EdStairsBlock)) return true;
if(SERVER.without_walls.get()&&(block instanceof EdWallBlock)) return true;
if(SERVER.without_poles.get()&&(block instanceof EdStraightPoleBlock)) return true;
// String matching based evaluation
if(SERVER.without_clinker_bricks.get()&&(rn.startsWith("clinker_brick_"))) return true;
if(SERVER.without_slag_bricks.get()&&rn.startsWith("slag_brick_")) return true;
if(SERVER.without_rebar_concrete.get()&&rn.startsWith("rebar_concrete")) return true;
if(SERVER.without_gas_concrete.get()&&rn.startsWith("gas_concrete")) return true;
if(SERVER.without_ie_concrete_wall.get()&&rn.startsWith("ie_concrete_wall")) return true;
if(SERVER.without_panzer_glass.get()&&rn.startsWith("panzerglass_")) return true;
if(SERVER.without_light_sources.get()&&rn.endsWith("_light")) return true;
if(SERVER.without_sign_plates.get()&&rn.startsWith("sign_")) return true;
if(SERVER.without_treated_wood_furniture.get()) {
if(block instanceof EdChair.ChairBlock) return true;
if(rn.equals("treated_wood_table")) return true;
if(rn.equals("treated_wood_stool")) return true;
if(rn.equals("treated_wood_windowsill")) return true;
if(rn.equals("treated_wood_broad_windowsill")) return true;
if(rn.equals("treated_wood_side_table")) return true;
}
if(SERVER.without_metal_furniture.get()) {
if(rn.equals("steel_table")) return true;
}
if(SERVER.without_fences.get()) {
if(block instanceof EdFenceBlock) return true;
if(block instanceof EdDoubleGateBlock) return true;
}
} catch(Exception ex) {
LOGGER.error("Exception evaluating the optout config: '"+ex.getMessage()+"'");
}
return false;
}).forEach(
e -> optouts.add(e.getRegistryName().getPath())
);
optouts_ = optouts;
}
}
public static final void apply()
{
with_experimental_features_ = SERVER.with_experimental.get();
if(with_experimental_features_) LOGGER.info("Config: EXPERIMENTAL FEATURES ENABLED.");
immersiveengineering_installed = Auxiliaries.isModLoaded("immersiveengineering");
updateOptouts();
without_crafting_table = isOptedOut(ModContent.CRAFTING_TABLE);
without_recipes_ = SERVER.without_recipes.get();
without_direct_slab_pickup = SERVER.without_direct_slab_pickup.get();
// -----------------------------------------------------------------------------------------------------------------
EdFurnace.FurnaceTileEntity.on_config(SERVER.furnace_smelting_speed_percent.get(), SERVER.furnace_fuel_efficiency_percent.get(), SERVER.furnace_boost_energy_consumption.get());
EdChair.on_config(SERVER.without_chair_sitting.get(), SERVER.without_mob_chair_sitting.get(), SERVER.chair_mob_sitting_probability_percent.get(), SERVER.chair_mob_standup_probability_percent.get());
EdLadderBlock.on_config(SERVER.without_ladder_speed_boost.get());
EdCraftingTable.on_config(SERVER.without_crafting_table_history.get(), false, SERVER.without_crafting_mouse_scrolling.get());
EdPipeValve.on_config(SERVER.pipevalve_max_flowrate.get(), SERVER.pipevalve_redstone_gain.get());
EdElectricalFurnace.ElectricalFurnaceTileEntity.on_config(SERVER.e_furnace_speed_percent.get(), SERVER.e_furnace_power_consumption.get(), SERVER.e_furnace_automatic_pulling.get());
EdSolarPanel.SolarPanelTileEntity.on_config(SERVER.small_solar_panel_peak_production.get());
EdBreaker.BreakerTileEntity.on_config(SERVER.block_breaker_power_consumption.get(), SERVER.block_breaker_reluctance.get(), SERVER.block_breaker_min_breaking_time.get(), SERVER.block_breaker_requires_power.get());
EdTreeCutter.TreeCutterTileEntity.on_config(SERVER.tree_cuttter_energy_consumption.get(), SERVER.tree_cuttter_cutting_time_needed.get(), SERVER.tree_cuttter_requires_power.get());
EdMilker.MilkerTileEntity.on_config(SERVER.milking_machine_energy_consumption.get(), SERVER.milking_machine_milking_delay.get());
EdSlabBlock.on_config(!SERVER.without_direct_slab_pickup.get());
EdSlabSliceBlock.on_config(!SERVER.without_direct_slab_pickup.get());
// currently no file config planned
EdLabeledCrate.on_config(false);
EdFluidFunnel.on_config(with_experimental_features_); // @todo: double check for abuse as pump first
EdDropper.on_config(with_experimental_features_); // @todo: double check handler cross-mod-compat first
EdFluidBarrel.on_config(12000, 1000);
// -----------------------------------------------------------------------------------------------------------------
{
// Check if the config is already synchronized or has to be synchronised.
server_config_.putBoolean("tree_cuttter_requires_power", SERVER.tree_cuttter_requires_power.get());
server_config_.putBoolean("block_breaker_requires_power", SERVER.block_breaker_requires_power.get());
{
String s = String.join(",", optouts_);
server_config_.putString("optout", s);
if(!s.isEmpty()) LOGGER.info("Opt-outs:" + s);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
package wile.engineersdecor;
import wile.engineersdecor.blocks.*;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.OptionalRecipeCondition;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.ItemGroup;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.*;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Mod("engineersdecor")
public class ModEngineersDecor
{
public static final String MODID = "engineersdecor";
public static final String MODNAME = "Engineer's Decor";
public static final int VERSION_DATAFIXER = 0;
private static final Logger LOGGER = LogManager.getLogger();
private static boolean config_loaded = false;
public ModEngineersDecor()
{
Auxiliaries.init(MODID, LOGGER, ModConfig::getServerConfig);
Auxiliaries.logGitVersion(MODNAME);
OptionalRecipeCondition.init(MODID, LOGGER);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSendImc);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onRecvImc);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup);
ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.SERVER, ModConfig.SERVER_CONFIG_SPEC);
ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.CLIENT, ModConfig.CLIENT_CONFIG_SPEC);
MinecraftForge.EVENT_BUS.register(this);
}
public static final Logger logger() { return LOGGER; }
//
// Events
//
private void onSetup(final FMLCommonSetupEvent event)
{
LOGGER.info("Registering recipe condition processor ...");
CraftingHelper.register(OptionalRecipeCondition.Serializer.INSTANCE);
wile.engineersdecor.libmc.detail.Networking.init(MODID);
if(config_loaded) {
try {
logger().info("Applying loaded config file.");
ModConfig.apply();
} catch(Throwable e) {
logger().error("Failed to apply config: " + e.getMessage());
}
} else {
logger().info("Cannot apply config, load event was not casted yet.");
}
}
private void onClientSetup(final FMLClientSetupEvent event)
{
ModContent.registerContainerGuis(event);
ModContent.registerTileEntityRenderers(event);
ModContent.processContentClientSide(event);
wile.engineersdecor.libmc.detail.Overlay.register();
}
private void onSendImc(final InterModEnqueueEvent event)
{
InterModComms.sendTo("inventorysorter", "containerblacklist", ()->ModContent.CT_TREATED_WOOD_CRAFTING_TABLE.getRegistryName());
InterModComms.sendTo("inventorysorter", "slotblacklist", ()-> EdCraftingTable.CraftingOutputSlot.class.getName());
InterModComms.sendTo("inventorysorter", "slotblacklist", ()-> EdCraftingTable.CraftingGridSlot.class.getName());
}
private void onRecvImc(final InterModProcessEvent event)
{}
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class ForgeEvents
{
@SubscribeEvent
public static void onBlocksRegistry(final RegistryEvent.Register<Block> event)
{ ModContent.registerBlocks(event); }
@SubscribeEvent
public static void onItemRegistry(final RegistryEvent.Register<Item> event)
{ ModContent.registerBlockItems(event); }
@SubscribeEvent
public static void onTileEntityRegistry(final RegistryEvent.Register<TileEntityType<?>> event)
{ ModContent.registerTileEntities(event); }
@SubscribeEvent
public static void onRegisterEntityTypes(final RegistryEvent.Register<EntityType<?>> event)
{ ModContent.registerEntities(event); }
@SubscribeEvent
public static void onRegisterContainerTypes(final RegistryEvent.Register<ContainerType<?>> event)
{ ModContent.registerContainers(event); }
// @SubscribeEvent
public static void onServerStarting(FMLServerStartingEvent event)
{}
@SubscribeEvent
public static void onConfigLoad(net.minecraftforge.fml.config.ModConfig.Loading configEvent)
{ config_loaded = true; }
@SubscribeEvent
public static void onConfigReload(net.minecraftforge.fml.config.ModConfig.Reloading configEvent)
{
try {
ModEngineersDecor.logger().info("Config file changed {}", configEvent.getConfig().getFileName());
ModConfig.apply();
} 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 ItemGroup ITEMGROUP = (new ItemGroup("tab" + MODID) {
@OnlyIn(Dist.CLIENT)
public ItemStack createIcon()
{ return new ItemStack(ModContent.SIGN_MODLOGO); }
});
//
// Player update event
//
@SubscribeEvent
public void onPlayerEvent(final LivingEvent.LivingUpdateEvent event)
{
if(!(event.getEntity() instanceof PlayerEntity)) return;
final PlayerEntity player = (PlayerEntity)event.getEntity();
if(player.world == null) return;
if(player.isOnLadder()) EdLadderBlock.onPlayerUpdateEvent(player);
}
}

View file

@ -0,0 +1,119 @@
/*
* @file DecorBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Common functionality class for decor blocks.
* Mainly needed for:
* - MC block defaults.
* - Tooltip functionality
* - Model initialisation
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.blocks.StandardBlocks.IStandardBlock;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.block.Block;
import java.util.ArrayList;
import java.util.function.Supplier;
public class DecorBlock
{
public static final long CFG_DEFAULT = StandardBlocks.CFG_DEFAULT;
public static final long CFG_CUTOUT = StandardBlocks.CFG_CUTOUT;
public static final long CFG_MIPPED = StandardBlocks.CFG_MIPPED;
public static final long CFG_TRANSLUCENT = StandardBlocks.CFG_TRANSLUCENT;
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 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_HARD_IE_DEPENDENT = 0x8000000000000000L;
public static class Normal extends StandardBlocks.BaseBlock implements IDecorBlock
{
public Normal(long conf, Block.Properties properties)
{ super(conf, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16,16 )); }
public Normal(long conf, Block.Properties properties, AxisAlignedBB aabb)
{ super(conf, properties, aabb);}
public Normal(long conf, Block.Properties properties, VoxelShape voxel_shape)
{ super(conf, properties, voxel_shape); }
public Normal(long conf, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(conf, properties, aabbs); }
}
public static class WaterLoggable extends StandardBlocks.WaterLoggable implements IStandardBlock, IWaterLoggable
{
public WaterLoggable(long config, Block.Properties properties)
{ super(config, properties); }
public WaterLoggable(long config, Block.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public WaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public WaterLoggable(long config, Block.Properties properties, VoxelShape voxel_shape)
{ super(config, properties, voxel_shape); }
}
public static class Directed extends StandardBlocks.Directed implements IDecorBlock
{
public Directed(long config, Block.Properties properties, final AxisAlignedBB unrotatedAABB)
{ super(config, properties, unrotatedAABB); }
public Directed(long config, Block.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, properties, unrotatedAABBs); }
public Directed(long config, Block.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, Block.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public DirectedWaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public DirectedWaterLoggable(long config, Block.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, Block.Properties properties, final AxisAlignedBB unrotatedAABB)
{ super(config, properties, unrotatedAABB); }
public Horizontal(long config, Block.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, properties, unrotatedAABBs); }
public Horizontal(long config, Block.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, Block.Properties properties, AxisAlignedBB aabb)
{ super(config, properties, aabb); }
public HorizontalWaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config, properties, aabbs); }
public HorizontalWaterLoggable(long config, Block.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config, properties, shape_supplier); }
}
}

View file

@ -0,0 +1,360 @@
/*
* @file EdBreaker.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small Block Breaker
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.GameRules;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
public class EdBreaker
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class BreakerBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock
{
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public BreakerBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, builder, unrotatedAABBs); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(ACTIVE); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(ACTIVE, false); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BreakerTileEntity(); }
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.get(ACTIVE))) return;
final double rv = rnd.nextDouble();
if(rv > 0.8) return;
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.get(HORIZONTAL_FACING)) {
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;
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
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BreakerTileEntity)) return;
((BreakerTileEntity)te).block_updated();
}
@Override
@SuppressWarnings("deprecation")
public boolean canProvidePower(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof BreakerTileEntity) ((BreakerTileEntity)te).state_message(player);
return ActionResultType.SUCCESS;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BreakerTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage
{
public static final int IDLE_TICK_INTERVAL = 40;
public static final int TICK_INTERVAL = 5;
public static final int BOOST_FACTOR = 8;
public static final int DEFAULT_BOOST_ENERGY = 64;
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;
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int energy_max = 32000;
private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE;
private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME;
private static boolean requires_power = false;
private int tick_timer_;
private int active_timer_;
private int proc_time_elapsed_;
private int time_needed_;
private int energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
requires_power = power_required;
ModEngineersDecor.logger().info("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");
}
public BreakerTileEntity()
{ super(ModContent.TET_SMALL_BLOCK_BREAKER); }
public BreakerTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((proc_time_elapsed_ > 0) && (time_needed_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)time_needed_) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_block_breaker.status", null, new Object[]{soc, energy_max, progress }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
}
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return boost_energy_consumption*2; }
@Override
public int getEnergyStored()
{ return energy_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max(energy_max-energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}
// Capability export ----------------------------------------------------------------------------
@Override
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);
}
// ITickable ------------------------------------------------------------------------------------
private static HashSet<Block> blacklist = new HashSet<>();
static {
blacklist.add(Blocks.AIR);
blacklist.add(Blocks.BEDROCK);
blacklist.add(Blocks.FIRE);
blacklist.add(Blocks.END_PORTAL);
blacklist.add(Blocks.END_GATEWAY);
blacklist.add(Blocks.END_PORTAL_FRAME);
blacklist.add(Blocks.NETHER_PORTAL);
blacklist.add(Blocks.BARRIER);
}
private static boolean isBreakable(BlockState state, BlockPos pos, World world)
{
final Block block = state.getBlock();
if(blacklist.contains(block)) return false;
if(state.getMaterial().isLiquid()) return false;
if(block.isAir(state, world, pos)) return false;
float bh = state.getBlockHardness(world, pos);
if((bh<0) || (bh>55)) return false;
return true;
}
private static void spawnBlockAsEntity(World world, BlockPos pos, ItemStack stack) {
if(world.isRemote || stack.isEmpty() || (!world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) || world.restoringBlockSnapshots) return;
ItemEntity e = new ItemEntity(world,
((world.rand.nextFloat()*0.1)+0.5) + pos.getX(),
((world.rand.nextFloat()*0.1)+0.5) + pos.getY(),
((world.rand.nextFloat()*0.1)+0.5) + pos.getZ(),
stack
);
e.setDefaultPickupDelay();
e.setMotion((world.rand.nextFloat()*0.1-0.05), (world.rand.nextFloat()*0.1-0.03), (world.rand.nextFloat()*0.1-0.05));
world.addEntity(e);
}
private boolean breakBlock(BlockState state, BlockPos pos, World world)
{
if(world.isRemote || (!(world instanceof ServerWorld)) || world.restoringBlockSnapshots) return false; // retry next cycle
List<ItemStack> drops;
final Block block = state.getBlock();
drops = Block.getDrops(state, (ServerWorld)world, pos, world.getTileEntity(pos));
world.removeBlock(pos, false);
for(ItemStack drop:drops) spawnBlockAsEntity(world, pos, drop);
SoundType stype = state.getBlock().getSoundType(state, world, pos, null);
if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
return true;
}
@Override
@SuppressWarnings("deprecation")
public void tick()
{
if(--tick_timer_ > 0) return;
final BlockState device_state = world.getBlockState(pos);
if(!(device_state.getBlock() instanceof BreakerBlock)) return;
if(world.isRemote) {
if(!device_state.get(BreakerBlock.ACTIVE)) {
tick_timer_ = TICK_INTERVAL;
} else {
tick_timer_ = 1;
// not sure if is so cool to do this each tick ... may be simplified/removed again.
SoundEvent sound = SoundEvents.BLOCK_WOOD_HIT;
BlockState target_state = world.getBlockState(pos.offset(device_state.get(BreakerBlock.HORIZONTAL_FACING)));
SoundType stype = target_state.getBlock().getSoundType(target_state);
if((stype == SoundType.CLOTH) || (stype == SoundType.PLANT) || (stype == SoundType.SNOW)) {
sound = SoundEvents.BLOCK_WOOL_HIT;
} else if((stype == SoundType.GROUND) || (stype == SoundType.SAND)) {
sound = SoundEvents.BLOCK_GRAVEL_HIT;
}
world.playSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundCategory.BLOCKS, 0.1f, 1.2f, false);
}
} else {
tick_timer_ = TICK_INTERVAL;
final BlockPos target_pos = pos.offset(device_state.get(BreakerBlock.HORIZONTAL_FACING));
final BlockState target_state = world.getBlockState(target_pos);
if((world.isBlockPowered(pos)) || (!isBreakable(target_state, target_pos, world))) {
if(device_state.get(BreakerBlock.ACTIVE)) world.setBlockState(pos, device_state.with(BreakerBlock.ACTIVE, false), 1|2);
proc_time_elapsed_ = 0;
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
time_needed_ = MathHelper.clamp((int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time, min_breaking_time, MAX_BREAKING_TIME);
if(energy_ >= boost_energy_consumption) {
energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL * (1+BOOST_FACTOR);
time_needed_ += min_breaking_time * (3*BOOST_FACTOR/5);
active_timer_ = 2;
} else if(!requires_power) {
proc_time_elapsed_ += TICK_INTERVAL;
active_timer_ = 1024;
} else if(active_timer_ > 0) {
--active_timer_;
}
boolean active = (active_timer_ > 0);
if(requires_power && !active) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
}
if(proc_time_elapsed_ >= time_needed_) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;
}
if(device_state.get(BreakerBlock.ACTIVE) != active) {
world.setBlockState(pos, device_state.with(BreakerBlock.ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -0,0 +1,192 @@
/*
* @file EdChair.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Full block characteristics class.
*/
package wile.engineersdecor.blocks;
import net.minecraft.entity.monster.piglin.PiglinEntity;
import net.minecraft.util.math.vector.Vector3d;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.*;
import net.minecraft.entity.monster.*;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.IPacket;
import net.minecraft.util.math.*;
import net.minecraft.util.*;
import net.minecraftforge.fml.network.FMLPlayMessages;
import net.minecraftforge.fml.network.NetworkHooks;
import java.util.List;
import java.util.Random;
public class EdChair
{
private static boolean sitting_enabled = true;
private static double sitting_probability = 0.1;
private static double standup_probability = 0.01;
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);
standup_probability = (without_sitting||without_mob_sitting) ? 1.0 : MathHelper.clamp(standup_probability_percent/100, 1e-6, 1e-2);
ModEngineersDecor.logger().info("Config chairs sit:" + sitting_enabled + ", mob-sit: " + (sitting_probability*100) + "%, stand up: " + (standup_probability)+"%");
}
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class ChairBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock
{
public ChairBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, builder.tickRandomly(), unrotatedAABBs); }
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(!sitting_enabled) return ActionResultType.PASS;
if(!world.isRemote) EntityChair.sit(world, player, pos);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity)
{
if(sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof MobEntity)) EntityChair.sit(world, (LivingEntity)entity, pos);
}
@Override
public void tick(BlockState state, ServerWorld world, BlockPos pos, Random rnd)
{
if((!sitting_enabled) || (sitting_probability < 1e-6)) return;
final List<LivingEntity> entities = world.getEntitiesWithinAABB(MobEntity.class, new AxisAlignedBB(pos).grow(2,1,2).expand(0,1,0), e->true);
if(entities.isEmpty()) return;
int index = rnd.nextInt(entities.size());
if((index < 0) || (index >= entities.size())) return;
EntityChair.sit(world, entities.get(index), pos);
}
}
//--------------------------------------------------------------------------------------------------------------------
// Entity
//--------------------------------------------------------------------------------------------------------------------
public static class EntityChair extends Entity
{
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);
public EntityChair(EntityType<? extends Entity> entityType, World world)
{
super(entityType, world);
preventEntitySpawning=true;
setMotion(Vector3d.ZERO);
canUpdate(true);
noClip=true;
}
public EntityChair(World world)
{ this(ModContent.ET_CHAIR, world); }
public static EntityChair customClientFactory(FMLPlayMessages.SpawnEntity spkt, World world)
{ return new EntityChair(world); }
public IPacket<?> createSpawnPacket()
{ return NetworkHooks.getEntitySpawningPacket(this); }
public static boolean accepts_mob(LivingEntity entity)
{
if(!(entity instanceof net.minecraft.entity.monster.MonsterEntity)) return false;
if((entity.getType().getSize().height > 2.5) || (entity.getType().getSize().height > 2.0)) return false;
if(entity instanceof ZombieEntity) return true;
if(entity instanceof ZombieVillagerEntity) return true;
if(entity instanceof ZombifiedPiglinEntity) return true;
if(entity instanceof PiglinEntity) return true;
if(entity instanceof HuskEntity) return true;
if(entity instanceof StrayEntity) return true;
if(entity instanceof SkeletonEntity) return true;
if(entity instanceof WitherSkeletonEntity) return true;
return false;
}
public static void sit(World world, LivingEntity sitter, BlockPos pos)
{
if(!sitting_enabled) return;
if((world==null) || (world.isRemote) || (sitter==null) || (pos==null)) return;
if((!(sitter instanceof PlayerEntity)) && (!accepts_mob(sitter))) return;
if(!world.getEntitiesWithinAABB(EntityChair.class, new AxisAlignedBB(pos)).isEmpty()) return;
if(sitter.isBeingRidden() || (!sitter.isAlive()) || (sitter.isPassenger()) ) return;
if((!world.isAirBlock(pos.up())) || (!world.isAirBlock(pos.up(2)))) return;
boolean on_top_of_block_position = true;
boolean use_next_negative_y_position = false;
EntityChair chair = new EntityChair(world);
BlockPos chair_pos = chair.func_233580_cy_();
chair.chair_pos = pos;
chair.t_sit = 5;
chair.prevPosX = chair_pos.getX();
chair.prevPosY = chair_pos.getY();
chair.prevPosZ = chair_pos.getZ();
chair.setPosition(pos.getX()+x_offset,pos.getY()+y_offset,pos.getZ()+z_offset);
world.addEntity(chair);
sitter.startRiding(chair, true);
}
@Override
protected void registerData() {}
@Override
protected void readAdditional(CompoundNBT compound) {}
@Override
protected void writeAdditional(CompoundNBT compound) {}
@Override
public boolean canBePushed()
{ return false; }
@Override
public double getMountedYOffset()
{ return 0.0; }
@Override
public void tick()
{
if(world.isRemote) return;
super.tick();
if(--t_sit > 0) return;
Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0);
if((sitter==null) || (!sitter.isAlive())) {
this.remove();
return;
}
boolean abort = (!sitting_enabled);
final BlockState state = world.getBlockState(chair_pos);
if((state==null) || (!(state.getBlock() instanceof ChairBlock))) abort = true;
if(!world.isAirBlock(chair_pos.up())) abort = true;
if((!(sitter instanceof PlayerEntity)) && (Math.random() < standup_probability)) abort = true;
if(abort) {
for(Entity e:getPassengers()) {
if(e.isAlive()) e.stopRiding();
}
this.remove();
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,155 @@
/*
* @file EdDoubleGateBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Gate blocks that can be one or two segments high.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.*;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.pathfinding.PathType;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
public class EdDoubleGateBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock
{
public static final IntegerProperty SEGMENT = IntegerProperty.create("segment", 0, 1);
public static final BooleanProperty OPEN = FenceGateBlock.OPEN;
public static final int SEGMENT_LOWER = 0;
public static final int SEGMENT_UPPER = 1;
protected final ArrayList<VoxelShape> collision_shapes_;
public EdDoubleGateBlock(long config, Block.Properties properties, AxisAlignedBB aabb)
{ this(config, properties, new AxisAlignedBB[]{aabb}); }
public EdDoubleGateBlock(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{
super(config, properties, aabbs);
AxisAlignedBB[] caabbs = new AxisAlignedBB[aabbs.length];
for(int i=0; i<caabbs.length; ++i) caabbs[i] = aabbs[i].expand(0, 0.5, 0);
collision_shapes_ = new ArrayList<VoxelShape>(Arrays.asList(
VoxelShapes.fullCube(),
VoxelShapes.fullCube(),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.NORTH, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.SOUTH, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.WEST, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(caabbs, Direction.EAST, true)),
VoxelShapes.fullCube(),
VoxelShapes.fullCube()
));
}
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return state.get(OPEN) ? VoxelShapes.empty() : collision_shapes_.get(state.get(HORIZONTAL_FACING).getIndex() & 0x7); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(SEGMENT).add(OPEN); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return getInitialState(super.getStateForPlacement(context), context.getWorld(), context.getPos()); }
@Override
@SuppressWarnings("deprecation")
public BlockState updatePostPlacement(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{ return getInitialState(super.updatePostPlacement(state, facing, facingState, world, pos, facingPos), world, pos); }
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if((rayTraceResult.getFace()==Direction.UP) || (rayTraceResult.getFace()==Direction.DOWN) && (player.getHeldItem(hand).getItem()==this.asItem())) return ActionResultType.PASS;
if(world.isRemote) return ActionResultType.SUCCESS;
final boolean open = !state.get(OPEN);
world.setBlockState(pos, state.with(OPEN, open),2|8|16);
if(state.get(SEGMENT) == SEGMENT_UPPER) {
final BlockState adjacent = world.getBlockState(pos.down());
if(adjacent.getBlock()==this) world.setBlockState(pos.down(), adjacent.with(OPEN, open), 2|8|16);
} else {
final BlockState adjacent = world.getBlockState(pos.up());
if(adjacent.getBlock()==this) world.setBlockState(pos.up(), adjacent.with(OPEN, open), 2|8|16);
}
world.playSound(null, pos, open?SoundEvents.BLOCK_IRON_DOOR_OPEN:SoundEvents.BLOCK_IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public boolean allowsMovement(BlockState state, IBlockReader world, BlockPos pos, PathType type)
{ return state.get(OPEN); }
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
{
if(world.isRemote) return;
boolean powered = false;
BlockState adjacent;
BlockPos adjacent_pos;
if(state.get(SEGMENT) == SEGMENT_UPPER) {
adjacent_pos = pos.down();
adjacent = world.getBlockState(adjacent_pos);
if(adjacent.getBlock()!=this) adjacent = null;
if(world.getRedstonePower(pos.up(), Direction.UP) > 0) {
powered = true;
} else if((adjacent!=null) && (world.isBlockPowered(pos.down(2)))) {
powered = true;
}
} else {
adjacent_pos = pos.up();
adjacent = world.getBlockState(adjacent_pos);
if(adjacent.getBlock()!=this) adjacent = null;
if(world.isBlockPowered(pos)) {
powered = true;
} else if((adjacent!=null) && (world.getRedstonePower(pos.up(2), Direction.UP) > 0)) {
powered = true;
}
}
boolean sound = false;
if(powered != state.get(OPEN)) {
world.setBlockState(pos, state.with(OPEN, powered), 2|8|16);
sound = true;
}
if((adjacent != null) && (powered != adjacent.get(OPEN))) {
world.setBlockState(adjacent_pos, adjacent.with(OPEN, powered), 2|8|16);
sound = true;
}
if(sound) {
world.playSound(null, pos, powered?SoundEvents.BLOCK_IRON_DOOR_OPEN:SoundEvents.BLOCK_IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f);
}
}
// -------------------------------------------------------------------------------------------------------------------
private BlockState getInitialState(BlockState state, IWorld world, BlockPos pos)
{
final BlockState down = world.getBlockState(pos.down());
if(down.getBlock() == this) return state.with(SEGMENT, SEGMENT_UPPER).with(OPEN, down.get(OPEN)).with(HORIZONTAL_FACING, down.get(HORIZONTAL_FACING));
final BlockState up = world.getBlockState(pos.up());
if(up.getBlock() == this) return state.with(SEGMENT, SEGMENT_LOWER).with(OPEN, up.get(OPEN)).with(HORIZONTAL_FACING, up.get(HORIZONTAL_FACING));
return state.with(SEGMENT, SEGMENT_LOWER).with(OPEN, false);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,887 @@
/*
* @file EdElectricalFurnace.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* ED small electrical pass-through furnace.
*/
package wile.engineersdecor.blocks;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.util.text.TranslationTextComponent;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.detail.Inventories;
import wile.engineersdecor.libmc.detail.Networking;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.block.BlockState;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.item.*;
import net.minecraft.item.crafting.AbstractCookingRecipe;
import net.minecraft.item.crafting.FurnaceRecipe;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.*;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.inventory.*;
import net.minecraft.inventory.container.*;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.stats.Stats;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import wile.engineersdecor.libmc.detail.TooltipDisplay;
import wile.engineersdecor.libmc.detail.TooltipDisplay.TipRange;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Random;
public class EdElectricalFurnace
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class ElectricalFurnaceBlock extends EdFurnace.FurnaceBlock implements IDecorBlock
{
public ElectricalFurnaceBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, builder, unrotatedAABBs); }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdElectricalFurnace.ElectricalFurnaceTileEntity(); }
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity)) return ActionResultType.FAIL;
if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return ActionResultType.FAIL;
NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te);
player.addStat(Stats.INTERACT_WITH_FURNACE);
return ActionResultType.SUCCESS;
}
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
world.setBlockState(pos, state.with(LIT, false));
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return;
CompoundNBT inventory_nbt = stack.getTag().getCompound("inventory");
if(inventory_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof EdElectricalFurnace.ElectricalFurnaceTileEntity)) return;
ElectricalFurnaceTileEntity bte = (EdElectricalFurnace.ElectricalFurnaceTileEntity)te;
bte.readnbt(inventory_nbt);
bte.markDirty();
world.setBlockState(pos, state.with(LIT, bte.burning()));
}
@Override
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class ElectricalFurnaceTileEntity extends EdFurnace.FurnaceTileEntity implements ITickableTileEntity, INameable, IInventory, INamedContainerProvider, ISidedInventory, IEnergyStorage
{
public static final IRecipeType<FurnaceRecipe> RECIPE_TYPE = IRecipeType.SMELTING;
public static final int NUM_OF_FIELDS = 7;
public static final int TICK_INTERVAL = 4;
public static final int FIFO_INTERVAL = 20;
public static final int HEAT_CAPACITY = 200;
public static final int HEAT_INCREMENT = 20;
public static final int MAX_ENERGY_TRANSFER = 1024;
public static final int MAX_ENERGY_BUFFER = 32000;
public static final int MAX_SPEED_SETTING = 3;
public static final int NUM_OF_SLOTS = 7;
public static final int SMELTING_INPUT_SLOT_NO = 0;
public static final int SMELTING_AUX_SLOT_NO = 1;
public static final int SMELTING_OUTPUT_SLOT_NO = 2;
public static final int FIFO_INPUT_0_SLOT_NO = 3;
public static final int FIFO_INPUT_1_SLOT_NO = 4;
public static final int FIFO_OUTPUT_0_SLOT_NO = 5;
public static final int FIFO_OUTPUT_1_SLOT_NO = 6;
public static final int DEFAULT_SPEED_PERCENT = 200;
public static final int DEFAULT_ENERGY_CONSUMPTION = 16;
public static final int DEFAULT_SCALED_ENERGY_CONSUMPTION = DEFAULT_ENERGY_CONSUMPTION * HEAT_INCREMENT * DEFAULT_SPEED_PERCENT / 100;
// Config ----------------------------------------------------------------------------------
private static boolean with_automatic_inventory_pulling_ = false;
private static int energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION;
private static int transfer_energy_consumption_ = DEFAULT_SCALED_ENERGY_CONSUMPTION / 8;
private static int proc_speed_percent_ = DEFAULT_SPEED_PERCENT;
private static double speed_setting_factor_[] = {0.0, 1.0, 1.5, 2.0};
public static void on_config(int speed_percent, int standard_energy_per_tick, boolean with_automatic_inventory_pulling)
{
proc_speed_percent_ = MathHelper.clamp(speed_percent, 10, 500);
energy_consumption_ = MathHelper.clamp(standard_energy_per_tick, 4, 4096) * HEAT_INCREMENT * proc_speed_percent_ / 100;
transfer_energy_consumption_ = MathHelper.clamp(energy_consumption_ / 8, 8, HEAT_INCREMENT);
with_automatic_inventory_pulling_ = with_automatic_inventory_pulling;
ModEngineersDecor.logger().info("Config electrical furnace speed:" + proc_speed_percent_ + ", power consumption:" + energy_consumption_);
}
// ElectricalFurnaceTileEntity -----------------------------------------------------------------------------
private int burntime_left_ = 0;
private int proc_time_elapsed_ = 0;
private int proc_time_needed_ = 0;
private int energy_stored_ = 0;
private int field_max_energy_stored_ = 0;
private int field_isburning_ = 0;
private int speed_ = 1;
private int tick_timer_ = 0;
private int fifo_timer_ = 0;
private boolean enabled_ = false;
public ElectricalFurnaceTileEntity()
{ this(ModContent.TET_SMALL_ELECTRICAL_FURNACE); }
public ElectricalFurnaceTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void reset()
{
super.reset();
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
burntime_left_ = 0;
proc_time_elapsed_ = 0;
proc_time_needed_ = 0;
fifo_timer_ = 0;
tick_timer_ = 0;
energy_stored_ = 0;
speed_ = 1;
field_max_energy_stored_ = getMaxEnergyStored();
field_isburning_ = 0;
}
public void readnbt(CompoundNBT nbt)
{
ItemStackHelper.loadAllItems(nbt, this.stacks_);
while(this.stacks_.size() < NUM_OF_SLOTS) this.stacks_.add(ItemStack.EMPTY);
burntime_left_ = nbt.getInt("BurnTime");
proc_time_elapsed_ = nbt.getInt("CookTime");
proc_time_needed_ = nbt.getInt("CookTimeTotal");
energy_stored_ = nbt.getInt("Energy");
speed_ = nbt.getInt("SpeedSetting");
speed_ = (speed_ < 0) ? (1) : ((speed_>MAX_SPEED_SETTING) ? MAX_SPEED_SETTING : speed_);
}
protected void writenbt(CompoundNBT nbt)
{
nbt.putInt("BurnTime", MathHelper.clamp(burntime_left_, 0, HEAT_CAPACITY));
nbt.putInt("CookTime", MathHelper.clamp(proc_time_elapsed_, 0, MAX_BURNTIME));
nbt.putInt("CookTimeTotal", MathHelper.clamp(proc_time_needed_, 0, MAX_BURNTIME));
nbt.putInt("Energy", MathHelper.clamp(energy_stored_, 0, MAX_ENERGY_BUFFER));
nbt.putInt("SpeedSetting", MathHelper.clamp(speed_, -1, MAX_SPEED_SETTING));
ItemStackHelper.saveAllItems(nbt, stacks_);
}
public int getComparatorOutput()
{
return (energy_stored_ <= 0) ? (0) : (
(stacks_.get(FIFO_INPUT_1_SLOT_NO).isEmpty() ? 0 : 5) +
(stacks_.get(FIFO_INPUT_0_SLOT_NO).isEmpty() ? 0 : 5) +
(stacks_.get(SMELTING_INPUT_SLOT_NO).isEmpty() ? 0 : 5)
);
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
Arrays.stream(item_handlers).forEach(LazyOptional::invalidate);
item_handler_.invalidate();
energy_handler_.invalidate();
}
// INameable -------------------------------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Small electrical furnace"); }
// IContainerProvider ----------------------------------------------------------------------
@Override
public Container createMenu(int id, PlayerInventory inventory, PlayerEntity player )
{ return new EdElectricalFurnace.ElectricalFurnaceContainer(id, inventory, this, IWorldPosCallable.of(world, pos), fields); }
// IInventory ------------------------------------------------------------------------------
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{
switch(index) {
case SMELTING_INPUT_SLOT_NO:
case FIFO_INPUT_0_SLOT_NO:
case FIFO_INPUT_1_SLOT_NO:
return true;
default:
return false;
}
}
@Override
public ItemStack getStackInSlot(int index)
{ return ((index < 0) || (index >= SIDED_INV_SLOTS.length)) ? ItemStack.EMPTY : stacks_.get(SIDED_INV_SLOTS[index]); }
// Fields -----------------------------------------------------------------------------------------------
protected final IIntArray fields = new IntArray(ElectricalFurnaceTileEntity.NUM_OF_FIELDS)
{
@Override
public int get(int id)
{
switch(id) {
case 0: return ElectricalFurnaceTileEntity.this.burntime_left_;
case 1: return ElectricalFurnaceTileEntity.this.energy_stored_;
case 2: return ElectricalFurnaceTileEntity.this.proc_time_elapsed_;
case 3: return ElectricalFurnaceTileEntity.this.proc_time_needed_;
case 4: return ElectricalFurnaceTileEntity.this.speed_;
case 5: return ElectricalFurnaceTileEntity.this.field_max_energy_stored_;
case 6: return ElectricalFurnaceTileEntity.this.field_isburning_;
default: return 0;
}
}
@Override
public void set(int id, int value)
{
switch(id) {
case 0: ElectricalFurnaceTileEntity.this.burntime_left_ = value; break;
case 1: ElectricalFurnaceTileEntity.this.energy_stored_ = value; break;
case 2: ElectricalFurnaceTileEntity.this.proc_time_elapsed_ = value; break;
case 3: ElectricalFurnaceTileEntity.this.proc_time_needed_ = value; break;
case 4: ElectricalFurnaceTileEntity.this.speed_ = value; break;
case 5: ElectricalFurnaceTileEntity.this.field_max_energy_stored_ = value; break;
case 6: ElectricalFurnaceTileEntity.this.field_isburning_ = value; break;
}
}
};
// ISidedInventory ----------------------------------------------------------------------------
private static final int[] SIDED_INV_SLOTS = new int[] {
SMELTING_INPUT_SLOT_NO, SMELTING_AUX_SLOT_NO, SMELTING_OUTPUT_SLOT_NO,
FIFO_INPUT_0_SLOT_NO, FIFO_INPUT_1_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO
};
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack itemStackIn, Direction direction)
{ return ((index==FIFO_INPUT_0_SLOT_NO) || (index==FIFO_INPUT_1_SLOT_NO)) && isItemValidForSlot(index, itemStackIn); }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return (index==FIFO_OUTPUT_0_SLOT_NO) || (index==FIFO_OUTPUT_1_SLOT_NO); }
// IEnergyStorage ----------------------------------------------------------------------------
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return MAX_ENERGY_BUFFER; }
@Override
public int getEnergyStored()
{ return energy_stored_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0;
int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_));
if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER;
if(!simulate) {energy_stored_ += n; markDirty(); }
return n;
}
// IItemHandler --------------------------------------------------------------------------------
protected static class BItemHandler implements IItemHandler
{
private ElectricalFurnaceTileEntity te;
BItemHandler(ElectricalFurnaceTileEntity te)
{ this.te = te; }
@Override
public int getSlots()
{ return SIDED_INV_SLOTS.length; }
@Override
@Nonnull
public ItemStack getStackInSlot(int index)
{ return te.getStackInSlot(index); }
@Override
public int getSlotLimit(int index)
{ return te.getInventoryStackLimit(); }
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack)
{ return true; }
@Override
@Nonnull
public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate)
{
if(stack.isEmpty()) return ItemStack.EMPTY;
if((index < 0) || (index >= SIDED_INV_SLOTS.length)) return ItemStack.EMPTY;
int slotno = SIDED_INV_SLOTS[index];
ItemStack slotstack = getStackInSlot(slotno);
if(!slotstack.isEmpty()) {
if(slotstack.getCount() >= Math.min(slotstack.getMaxStackSize(), getSlotLimit(index))) return stack;
if(!ItemHandlerHelper.canItemStacksStack(stack, slotstack)) return stack;
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(index)) - slotstack.getCount();
if(stack.getCount() <= n) {
if(!simulate) {
ItemStack copy = stack.copy();
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
}
return ItemStack.EMPTY;
} else {
stack = stack.copy();
if(!simulate) {
ItemStack copy = stack.split(n);
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
return stack;
} else {
stack.shrink(n);
return stack;
}
}
} else {
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(index));
if(n < stack.getCount()) {
stack = stack.copy();
if(!simulate) {
te.setInventorySlotContents(slotno, stack.split(n));
return stack;
} else {
stack.shrink(n);
return stack;
}
} else {
if(!simulate) te.setInventorySlotContents(slotno, stack);
return ItemStack.EMPTY;
}
}
}
@Override
@Nonnull
public ItemStack extractItem(int index, int amount, boolean simulate)
{
if(amount == 0) return ItemStack.EMPTY;
if((index < 0) || (index >= SIDED_INV_SLOTS.length)) return ItemStack.EMPTY;
int slotno = SIDED_INV_SLOTS[index];
ItemStack stackInSlot = getStackInSlot(slotno);
if(stackInSlot.isEmpty()) return ItemStack.EMPTY;
if(!te.canExtractItem(slotno, stackInSlot, Direction.DOWN)) return ItemStack.EMPTY;
if(simulate) {
if(stackInSlot.getCount() < amount) return stackInSlot.copy();
ItemStack ostack = stackInSlot.copy();
ostack.setCount(amount);
return ostack;
} else {
ItemStack ostack = te.decrStackSize(slotno, Math.min(stackInSlot.getCount(), amount));
te.markDirty();
return ostack;
}
}
}
// Capability export ----------------------------------------------------------------------------
protected LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> new BItemHandler(this));
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast();
return super.getCapability(capability, facing);
}
// ITickableTileEntity -------------------------------------------------------------------------
@Override
public void tick()
{
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
if(!(world.getBlockState(pos).getBlock() instanceof ElectricalFurnaceBlock)) return;
final boolean was_burning = burning();
if(was_burning) burntime_left_ -= TICK_INTERVAL;
if(burntime_left_ < 0) burntime_left_ = 0;
if(world.isRemote) return;
boolean update_blockstate = (was_burning != burning());
boolean dirty = update_blockstate;
boolean shift_in = false;
boolean shift_out = false;
if(--fifo_timer_ <= 0) {
fifo_timer_ = FIFO_INTERVAL/TICK_INTERVAL;
if(transferItems(FIFO_OUTPUT_0_SLOT_NO, FIFO_OUTPUT_1_SLOT_NO, 64)) { dirty = true; } else { shift_out = true; }
if(transferItems(SMELTING_OUTPUT_SLOT_NO, FIFO_OUTPUT_0_SLOT_NO, 64)) dirty = true;
if(transferItems(FIFO_INPUT_0_SLOT_NO, SMELTING_INPUT_SLOT_NO, 64)) dirty = true;
if(transferItems(FIFO_INPUT_1_SLOT_NO, FIFO_INPUT_0_SLOT_NO, 64)) { dirty = true; } else { shift_in = true; }
}
if(energy_stored_ < energy_consumption()) {
enabled_ = false;
} else if(energy_stored_ >= (MAX_ENERGY_BUFFER/2)) {
enabled_ = true;
}
if((!(stacks_.get(SMELTING_INPUT_SLOT_NO)).isEmpty()) && (enabled_) && (speed_>0) && (speed_<=MAX_SPEED_SETTING)) {
IRecipe last_recipe = currentRecipe();
updateCurrentRecipe();
if(currentRecipe() != last_recipe) {
proc_time_elapsed_ = 0;
proc_time_needed_ = getSmeltingTimeNeeded(world, stacks_.get(SMELTING_INPUT_SLOT_NO));
}
final boolean can_smelt = canSmeltCurrentItem();
if((!can_smelt) && (getSmeltingResult(stacks_.get(SMELTING_INPUT_SLOT_NO)).isEmpty())) {
// bypass
if(transferItems(SMELTING_INPUT_SLOT_NO, SMELTING_OUTPUT_SLOT_NO, 1)) dirty = true;
} else {
// smelt
if(!burning() && can_smelt) {
if(heat_up()) { dirty = true; update_blockstate = true; }
}
if(burning() && can_smelt) {
if(heat_up()) dirty = true;
proc_time_elapsed_ += (int)(TICK_INTERVAL * proc_speed_percent_ * speed_setting_factor_[speed_] / 100);
if(proc_time_elapsed_ >= proc_time_needed_) {
proc_time_elapsed_ = 0;
proc_time_needed_ = getSmeltingTimeNeeded(world, stacks_.get(SMELTING_INPUT_SLOT_NO));
smeltCurrentItem();
dirty = true;
shift_out = true;
}
} else {
proc_time_elapsed_ = 0;
}
}
} else if(proc_time_elapsed_ > 0) {
proc_time_elapsed_ -= ((stacks_.get(SMELTING_INPUT_SLOT_NO)).isEmpty() ? 20 : 1);
if(proc_time_elapsed_ < 0) { proc_time_elapsed_ = 0; shift_out = true; update_blockstate = true; }
}
if(update_blockstate) {
dirty = true;
sync_blockstate();
}
if(adjacent_inventory_shift(shift_in, shift_out)) dirty = true;
if(dirty) markDirty();
field_max_energy_stored_ = getMaxEnergyStored();
field_isburning_ = burning() ? 1 : 0;
}
// Furnace --------------------------------------------------------------------------------------
protected void updateCurrentRecipe()
{ setCurrentRecipe(getSmeltingResult(RECIPE_TYPE, world, stacks_.get(SMELTING_INPUT_SLOT_NO))); }
public boolean burning()
{ return burntime_left_ > 0; }
private boolean is_accepted_hopper(final ItemStack stack)
{ return (stack.getItem() == Blocks.HOPPER.asItem()) || (stack.getItem() == ModContent.FACTORY_HOPPER.asItem()); }
private boolean transferItems(final int index_from, final int index_to, int count)
{
ItemStack from = stacks_.get(index_from);
if(from.isEmpty()) return false;
ItemStack to = stacks_.get(index_to);
if(from.getCount() < count) count = from.getCount();
if(count <= 0) return false;
boolean changed = true;
if(to.isEmpty()) {
stacks_.set(index_to, from.split(count));
} else if(to.getCount() >= to.getMaxStackSize()) {
changed = false;
} else if(Inventories.areItemStacksDifferent(from, to)) {
changed = false;
} else {
if((to.getCount()+count) >= to.getMaxStackSize()) {
from.shrink(to.getMaxStackSize()-to.getCount());
to.setCount(to.getMaxStackSize());
} else {
from.shrink(count);
to.grow(count);
}
}
if(from.isEmpty() && from!=ItemStack.EMPTY) {
stacks_.set(index_from, ItemStack.EMPTY);
changed = true;
}
return changed;
}
private boolean adjacent_inventory_shift(boolean inp, boolean out)
{
boolean dirty = false;
if(energy_stored_ < transfer_energy_consumption_) return false;
final BlockState state = world.getBlockState(pos);
if(!(state.getBlock() instanceof ElectricalFurnaceBlock)) return false;
final Direction out_facing = state.get(ElectricalFurnaceBlock.HORIZONTAL_FACING);
if(out && (!stacks_.get(FIFO_OUTPUT_1_SLOT_NO).isEmpty())) {
TileEntity te = world.getTileEntity(pos.offset(out_facing));
if(te!=null) {
IItemHandler hnd = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, out_facing).orElse(null);
if(hnd != null) {
ItemStack remaining = ItemHandlerHelper.insertItemStacked(hnd, stacks_.get(FIFO_OUTPUT_1_SLOT_NO).copy(), false);
stacks_.set(FIFO_OUTPUT_1_SLOT_NO, remaining);
energy_stored_ -= transfer_energy_consumption_;
dirty = true;
}
}
}
if(with_automatic_inventory_pulling_ || is_accepted_hopper(stacks_.get(SMELTING_AUX_SLOT_NO))) {
final Direction inp_facing = state.get(ElectricalFurnaceBlock.HORIZONTAL_FACING).getOpposite();
if(inp && (stacks_.get(FIFO_INPUT_1_SLOT_NO).isEmpty())) {
TileEntity te = world.getTileEntity(pos.offset(inp_facing));
if(te!=null) {
IItemHandler hnd = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, inp_facing).orElse(null);
if(hnd != null) {
for(int i=0; i< hnd.getSlots(); ++i) {
ItemStack adj_stack = hnd.getStackInSlot(i);
if(!adj_stack.isEmpty()) {
ItemStack my_stack = adj_stack.copy();
if(my_stack.getCount() > getInventoryStackLimit()) my_stack.setCount(getInventoryStackLimit());
adj_stack.shrink(my_stack.getCount());
stacks_.set(FIFO_INPUT_1_SLOT_NO, my_stack);
energy_stored_ -= transfer_energy_consumption_;
dirty = true;
break;
}
}
}
}
}
}
return dirty;
}
int energy_consumption()
{
switch(speed_) {
case 1: return energy_consumption_;
case 2: return energy_consumption_ * 2;
case 3: return energy_consumption_ * 4;
default: return 0;
}
}
private boolean heat_up()
{
int p = energy_consumption();
if((p<=0) || (energy_stored_ < p)) return false;
if(burntime_left_ >= (HEAT_CAPACITY-HEAT_INCREMENT)) return false;
energy_stored_ -= p;
burntime_left_ += HEAT_INCREMENT;
this.markDirty();
return true; // returns TE dirty
}
private void sync_blockstate()
{
final BlockState state = world.getBlockState(pos);
if((state.getBlock() instanceof ElectricalFurnaceBlock) && (state.get(ElectricalFurnaceBlock.LIT) != burning())) {
world.setBlockState(pos, state.with(ElectricalFurnaceBlock.LIT, burning()), 2);
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// Container
//--------------------------------------------------------------------------------------------------------------------
public static class ElectricalFurnaceContainer extends Container implements Networking.INetworkSynchronisableContainer
{
private static final int PLAYER_INV_START_SLOTNO = 7;
protected final PlayerEntity player_;
protected final IInventory inventory_;
protected final IWorldPosCallable wpc_;
private final IIntArray fields_;
private final IRecipeType<? extends AbstractCookingRecipe> recipe_type_;
public int field(int index) { return fields_.get(index); }
public PlayerEntity player() { return player_ ; }
public IInventory inventory() { return inventory_ ; }
public World world() { return player_.world; }
public ElectricalFurnaceContainer(int cid, PlayerInventory player_inventory)
{ this(cid, player_inventory, new Inventory(ElectricalFurnaceTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY, new IntArray(ElectricalFurnaceTileEntity.NUM_OF_FIELDS)); }
private ElectricalFurnaceContainer(int cid, PlayerInventory player_inventory, IInventory block_inventory, IWorldPosCallable wpc, IIntArray fields)
{
super(ModContent.CT_SMALL_ELECTRICAL_FURNACE, cid);
player_ = player_inventory.player;
inventory_ = block_inventory;
wpc_ = wpc;
fields_ = fields;
recipe_type_ = ElectricalFurnaceTileEntity.RECIPE_TYPE;
addSlot(new Slot(inventory_, 0, 59, 28)); // smelting input
addSlot(new Slot(inventory_, 1, 16, 52)); // aux
addSlot(new EdFurnace.FurnaceContainer.BSlotResult(player_, inventory_, 2, 101, 28)); // smelting result
addSlot(new EdFurnace.FurnaceContainer.BSlotInpFifo(inventory_, 3, 34, 28)); // input fifo 0
addSlot(new EdFurnace.FurnaceContainer.BSlotInpFifo(inventory_, 4, 16, 28)); // input fifo 1
addSlot(new EdFurnace.FurnaceContainer.BSlotOutFifo(player_, inventory_, 5, 126, 28)); // out fifo 0
addSlot(new EdFurnace.FurnaceContainer.BSlotOutFifo(player_, inventory_, 6, 144, 28)); // out fifo 1
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35
}
}
this.trackIntArray(fields_); // === Add reference holders
}
@Override
public boolean canInteractWith(PlayerEntity player)
{ return inventory_.isUsableByPlayer(player); }
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int index)
{
Slot slot = getSlot(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index==2) || (index==5) || (index==6)) {
// Output slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, true)) return ItemStack.EMPTY;
slot.onSlotChange(slot_stack, transferred);
} else if((index==0) || (index==3) || (index==4)) {
// Input slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if(index==1) {
// Bypass slot
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player inventory
if(ElectricalFurnaceTileEntity.canSmelt(world(), slot_stack)) {
if(
(!mergeItemStack(slot_stack, 0, 1, false)) && // smelting input
(!mergeItemStack(slot_stack, 3, 4, false)) && // fifo0
(!mergeItemStack(slot_stack, 4, 5, false)) // fifo1
) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index < PLAYER_INV_START_SLOTNO+27)) {
// player inventory --> player hotbar
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO+27, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO+27) && (index < PLAYER_INV_START_SLOTNO+36) && (!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+27, false))) {
// player hotbar --> player inventory
return ItemStack.EMPTY;
}
} else {
// invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
// INetworkSynchronisableContainer ---------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public void onGuiAction(CompoundNBT nbt)
{ Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt); }
@OnlyIn(Dist.CLIENT)
public void onGuiAction(String key, int value)
{ CompoundNBT nbt=new CompoundNBT(); nbt.putInt(key, value); Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt); }
@Override
public void onServerPacketReceived(int windowId, CompoundNBT nbt)
{}
public void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt)
{
if(!(inventory_ instanceof ElectricalFurnaceTileEntity)) return;
ElectricalFurnaceTileEntity te = (ElectricalFurnaceTileEntity)inventory_;
if(nbt.contains("speed")) te.speed_ = MathHelper.clamp(nbt.getInt("speed"), 0, ElectricalFurnaceTileEntity.MAX_SPEED_SETTING);
te.markDirty();
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class ElectricalFurnaceGui extends ContainerScreen<ElectricalFurnaceContainer>
{
protected final PlayerEntity player_;
protected final TooltipDisplay tooltip_ = new TooltipDisplay();
public ElectricalFurnaceGui(ElectricalFurnaceContainer container, PlayerInventory player_inventory, ITextComponent title)
{ super(container, player_inventory, title); this.player_ = player_inventory.player; }
@Override
public void func_231160_c_/*init*/()
{
super.func_231160_c_();
{
final String prefix = ModContent.SMALL_ELECTRICAL_FURNACE.getTranslationKey() + ".tooltips.";
final int x0 = getGuiLeft(), y0 = getGuiTop();
final Slot aux = container.getSlot(ElectricalFurnaceTileEntity.SMELTING_AUX_SLOT_NO);
tooltip_.init(
new TipRange(x0+135, y0+50, 25, 25, new TranslationTextComponent(prefix + "speed")),
new TipRange(x0+aux.xPos, y0+aux.yPos, 16, 16, new TranslationTextComponent(prefix + "auxslot"))
);
}
}
@Override
public void func_230430_a_/*render*/(MatrixStack mx, int mouseX, int mouseY, float partialTicks)
{
func_230446_a_/*renderBackground*/(mx);
super.func_230430_a_(mx, mouseX, mouseY, partialTicks);
if(!tooltip_.render(mx, this, mouseX, mouseY)) func_230459_a_/*renderHoveredToolTip*/(mx, mouseX, mouseY);
}
@Override
protected void func_230451_b_(MatrixStack mx, int x, int y)
{}
@Override
@SuppressWarnings("deprecation")
protected void func_230450_a_/*drawGuiContainerBackgroundLayer*/(MatrixStack mx, float partialTicks, int mouseX, int mouseY)
{
RenderSystem.enableBlend();
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
getMinecraft().getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/small_electrical_furnace_gui.png"));
final int x0=guiLeft, y0=guiTop, w=xSize, h=ySize;
func_238474_b_(mx, x0, y0, 0, 0, w, h);
if(getContainer().field(6)!=0) {
final int hi = 13;
final int k = heat_px(hi);
func_238474_b_(mx, x0+62, y0+55+hi-k, 177, hi-k, 13, k);
}
func_238474_b_(mx, x0+79, y0+30, 176, 15, 1+progress_px(17), 15);
int we = energy_px(32, 8);
if(we>0) func_238474_b_(mx, x0+90, y0+54, 185, 30, we, 13);
switch(getContainer().field(4)) {
case 0: func_238474_b_(mx, x0+144, y0+57, 180, 57, 6, 9); break;
case 1: func_238474_b_(mx, x0+142, y0+58, 190, 58, 9, 6); break;
case 2: func_238474_b_(mx, x0+144, y0+56, 200, 57, 6, 9); break;
case 3: func_238474_b_(mx, x0+143, y0+58, 210, 58, 9, 6); break;
default: break;
}
RenderSystem.disableBlend();
}
@Override
public boolean func_231044_a_/*mouseClicked*/(double mouseX, double mouseY, int mouseButton)
{
tooltip_.resetTimer();
ElectricalFurnaceContainer container = (ElectricalFurnaceContainer)getContainer();
int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5);
if((!isPointInRegion(136, 48, 28, 28, mouseX, mouseY))) {
return super.func_231044_a_(mouseX, mouseY, mouseButton);
} else if(isPointInRegion(144, 64, 6, 10, mouseX, mouseY)) {
container.onGuiAction("speed", 0);
} else if(isPointInRegion(134, 58, 10, 6, mouseX, mouseY)) {
container.onGuiAction("speed", 1);
} else if(isPointInRegion(144, 48, 6, 10, mouseX, mouseY)) {
container.onGuiAction("speed", 2);
} else if(isPointInRegion(150, 58, 10, 6, mouseX, mouseY)) {
container.onGuiAction("speed", 3);
}
return true;
}
private int progress_px(int pixels)
{ final int tc=getContainer().field(2), T=getContainer().field(3); return ((T>0) && (tc>0)) ? (tc * pixels / T) : (0); }
private int heat_px(int pixels)
{
int k = ((getContainer().field(0) * (pixels+1)) / (EdElectricalFurnace.ElectricalFurnaceTileEntity.HEAT_CAPACITY));
return (k < pixels) ? k : pixels;
}
private int energy_px(int maxwidth, int quantization)
{
int emax = getContainer().field(5);
int k = ((maxwidth * getContainer().field(1) * 9) / 8) /((emax>0?emax:1)+1);
k = (k >= maxwidth-2) ? maxwidth : k;
if(quantization > 0) k = ((k+(quantization/2))/quantization) * quantization;
return k;
}
}
}

View file

@ -0,0 +1,29 @@
/*
* @file EdFenceBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Wall blocks.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.StandardFenceBlock;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorldReader;
import net.minecraft.block.*;
public class EdFenceBlock extends StandardFenceBlock implements IDecorBlock
{
public EdFenceBlock(long config, Block.Properties properties)
{ super(config, properties); }
public EdFenceBlock(long config, Block.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)
{ return ((facingState.getBlock()) instanceof EdDoubleGateBlock) || super.attachesTo(facingState, world, facingPos, side); }
}

View file

@ -0,0 +1,72 @@
/*
* @file EdFloorGratingBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Floor gratings.
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class EdFloorGratingBlock extends DecorBlock.WaterLoggable implements IDecorBlock
{
public EdFloorGratingBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos)
{ return true; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
@SuppressWarnings("deprecation")
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity)
{
if(!(entity instanceof ItemEntity)) return;
final boolean colliding = ((entity.getPositionVec().y-pos.getY()) > 0.7);
if(colliding || (entity.getMotion().getY() > 0)) {
double x = pos.getX() + 0.5;
double y = MathHelper.clamp(entity.getPositionVec().y-0.3, pos.getY(), pos.getY()+0.6);
double z = pos.getZ() + 0.5;
double vx = entity.getMotion().getX();
double vy = entity.getMotion().getY();
double vz = entity.getMotion().getZ();
if(colliding) {
vx = 0;
vy = -0.3;
vz = 0;
if((entity.getPositionVec().y-pos.getY()) > 0.8) y = pos.getY() + 0.6;
entity.prevPosX = x+0.1;
entity.prevPosY = y+0.1;
entity.prevPosZ = z+0.1;
}
vy = MathHelper.clamp(vy, -0.3, 0);
entity.setMotion(vx, vy, vz);
entity.fallDistance = 0;
entity.setPositionAndUpdate(x,y,z);
}
}
}

View file

@ -0,0 +1,273 @@
/*
* @file EdFluidAccumulator.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* A device that collects fluids from adjacent tank outputs
* when a pump drains on the (only) output side. Shall support
* high flow rates, and a vavuum suction delay. Shall not drain
* high amounts of fluid from the adjacent tanks when no fluid
* is requested at the output port. Shall drain balanced from
* the adjacent input sides.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class EdFluidAccumulator
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class FluidAccumulatorBlock extends DecorBlock.Directed implements IDecorBlock
{
public FluidAccumulatorBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdFluidAccumulator.FluidAccumulatorTileEntity(); }
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidAccumulatorTileEntity)) return ActionResultType.FAIL;
((FluidAccumulatorTileEntity)te).send_device_stats(player);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof FluidAccumulatorTileEntity) ((FluidAccumulatorTileEntity)te).block_changed();
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class FluidAccumulatorTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider
{
protected static int tick_idle_interval = 20; // ca 1000ms, simulates suction delay and saves CPU when not drained.
protected static int max_flowrate = 1000;
private Direction block_facing_ = Direction.NORTH;
private FluidStack tank_ = FluidStack.EMPTY;
private int last_drain_request_amount_ = 0;
private int vacuum_ = 0;
private int tick_timer_ = 0;
private int round_robin_ = 0;
private boolean initialized_ = false;
private int total_volume_filled_ = 0;
private int total_volume_drained_ = 0;
public void send_device_stats(PlayerEntity player)
{
int t_vol = tank_.getAmount();
Auxiliaries.playerChatMessage(player,"" + t_vol + "mB");
}
public void block_changed()
{ initialized_ = false; tick_timer_ = MathHelper.clamp(tick_timer_ , 0, tick_idle_interval); }
// TileEntity ------------------------------------------------------------------------------
public FluidAccumulatorTileEntity()
{ this(ModContent.TET_PASSIVE_FLUID_ACCUMULATOR); }
public FluidAccumulatorTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{
super.func_230337_a_(state, nbt);
tank_ = (!nbt.contains("tank")) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
}
@Override
public CompoundNBT write(CompoundNBT nbt)
{
super.write(nbt);
if(!tank_.isEmpty()) nbt.put("tank", tank_.writeToNBT(new CompoundNBT()));
return nbt;
}
@Override
public void remove()
{
super.remove();
fill_handler_.invalidate();
fluid_handler_.invalidate();
}
// Input flow handler ---------------------------------------------------------------------
private static class InputFillHandler implements IFluidHandler
{
private final FluidAccumulatorTileEntity parent_;
InputFillHandler(FluidAccumulatorTileEntity parent) { parent_ = parent; }
@Override public int getTanks() { return 0; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return max_flowrate; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@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; }
}
// Output flow handler ---------------------------------------------------------------------
private static class OutputFlowHandler implements IFluidHandler
{
private final FluidAccumulatorTileEntity te;
OutputFlowHandler(FluidAccumulatorTileEntity parent) { te = parent; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return te.tank_.copy(); }
@Override public int getTankCapacity(int tank) { return max_flowrate; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action)
{
if((resource==null) || (te.tank_.isEmpty())) return FluidStack.EMPTY;
return (!(te.tank_.isFluidEqual(resource))) ? (FluidStack.EMPTY) : drain(resource.getAmount(), action);
}
@Override public FluidStack drain(int maxDrain, FluidAction action)
{
if(!te.initialized_) return FluidStack.EMPTY;
if((action==FluidAction.EXECUTE) && (maxDrain > 0)) te.last_drain_request_amount_ = maxDrain;
if(te.tank_.isEmpty()) return FluidStack.EMPTY;
maxDrain = MathHelper.clamp(maxDrain ,0 , te.tank_.getAmount());
FluidStack res = te.tank_.copy();
if(action!=FluidAction.EXECUTE) return res;
res.setAmount(maxDrain);
te.tank_.setAmount(te.tank_.getAmount()-maxDrain);
if(te.tank_.getAmount() <= 0) te.tank_ = FluidStack.EMPTY;
te.total_volume_drained_ += res.getAmount();
return res;
}
}
// ICapabilityProvider --------------------------------------------------------------------
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new OutputFlowHandler(this));
private final LazyOptional<IFluidHandler> fill_handler_ = LazyOptional.of(() -> new InputFillHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
if(initialized_) {
if(facing == block_facing_) return fluid_handler_.cast();
if(facing != null) return fill_handler_.cast();
}
return LazyOptional.empty();
}
return super.getCapability(capability, facing);
}
// ITickable--------------------------------------------------------------------------------
public void tick()
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = tick_idle_interval;
BlockState state = world.getBlockState(pos);
if(!(state.getBlock() instanceof FluidAccumulatorBlock)) return;
if(!initialized_) {
initialized_ = true;
block_facing_ = state.get(FluidAccumulatorBlock.FACING);
}
int n_requested = last_drain_request_amount_;
last_drain_request_amount_ = 0;
if(n_requested > 0) {
vacuum_ += 2;
if(vacuum_ > 5) vacuum_ = 5;
} else {
if((--vacuum_) <= 0) {
vacuum_ = 0;
if(!tank_.isEmpty()) {
return; // nothing to do, noone's draining.
} else {
n_requested = 10; // drip in
}
}
}
boolean has_refilled = false;
n_requested += (vacuum_ * 50);
int tank_buffer_needed = n_requested;
if(tank_buffer_needed > max_flowrate) tank_buffer_needed = max_flowrate;
for(int i=0; i<6; ++i) {
if(++round_robin_ > 5) round_robin_ = 0;
if(n_requested <= 0) break;
if((tank_.getAmount() >= tank_buffer_needed)) break;
final Direction f = Direction.byIndex(round_robin_);
if(f == block_facing_) continue;
final TileEntity te = world.getTileEntity(pos.offset(f));
if((te==null) || (te instanceof FluidAccumulatorTileEntity)) continue;
final IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f.getOpposite()).orElse(null);
if(fh==null) continue;
if(tank_.isEmpty()) {
FluidStack res = fh.drain(n_requested, FluidAction.EXECUTE).copy();
if(res.isEmpty()) continue;
total_volume_filled_ += res.getAmount();
tank_ = res.copy();
has_refilled = true;
} else {
if((tank_.getAmount() + n_requested) > max_flowrate) n_requested = (max_flowrate - tank_.getAmount());
FluidStack rq = tank_.copy();
rq.setAmount(n_requested);
FluidStack res = fh.drain(rq, FluidAction.SIMULATE);
if(!res.isFluidEqual(rq)) continue;
res = fh.drain(rq, FluidAction.EXECUTE);
if(res.isEmpty()) continue;
tank_.setAmount(tank_.getAmount()+res.getAmount());
total_volume_filled_ += res.getAmount();
has_refilled = true;
if(tank_.getAmount() >= max_flowrate) break;
}
}
if(has_refilled) tick_timer_ = 0;
}
}
}

View file

@ -0,0 +1,411 @@
/*
* @file EdFluidBarrel.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Simple fluid tank with a built-in gauge.
*/
package wile.engineersdecor.blocks;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.util.*;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Fluidics;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class EdFluidBarrel
{
//--------------------------------------------------------------------------------------------------------------------
// Config
//--------------------------------------------------------------------------------------------------------------------
private static int capacity_ = 12000;
private static int item_fluid_handler_transfer_rate_ = 1000;
private static int tile_fluid_handler_transfer_rate_ = 1000;
public static void on_config(int tank_capacity, int transfer_rate)
{
capacity_ = MathHelper.clamp(tank_capacity, 2000, 64000);
tile_fluid_handler_transfer_rate_ = MathHelper.clamp(tank_capacity, 50, 4096);
item_fluid_handler_transfer_rate_ = tile_fluid_handler_transfer_rate_;
}
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class FluidBarrelBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock, StandardBlocks.IBlockItemFactory
{
public static final int FILL_LEVEL_MAX = 4;
public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
public FluidBarrelBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{
super(config, builder, unrotatedAABB);
setDefaultState(super.getDefaultState().with(FACING, Direction.UP).with(FILL_LEVEL, 0));
}
// IBlockItemFactory ----------------------------------------------------------------------------
@Override
public BlockItem getBlockItem(Block block, Item.Properties builder)
{ return new FluidBarrelItem(block, builder); }
// IStandardBlock --------------------------------------------------------------------------------
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, final TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof FluidBarrelTileEntity)) return stacks;
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = ((FluidBarrelTileEntity) te).clear_getnbt();
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
return stacks;
}
// Block/IForgeBlock -----------------------------------------------------------------------------
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(final ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{
if(
(!(stack.getItem() instanceof FluidBarrelItem)) ||
(Auxiliaries.Tooltip.helpCondition())
) {
super.addInformation(stack, world, tooltip, flag); return;
}
FluidStack fs = FluidBarrelItem.getFluid(stack);
if(!fs.isEmpty()) {
tooltip.add(Auxiliaries.localizable(getTranslationKey()+".status.tip", null, new Object[] {
Integer.toString(fs.getAmount()),
Integer.toString(capacity_),
new TranslationTextComponent(fs.getTranslationKey())
}));
} else {
tooltip.add(Auxiliaries.localizable(getTranslationKey()+".status.tip.empty", null, new Object[] {
"0",
Integer.toString(capacity_),
}));
}
if(!Auxiliaries.Tooltip.extendedTipCondition()) {
super.addInformation(stack, world, tooltip, flag);
}
}
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdFluidBarrel.FluidBarrelTileEntity(); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(FILL_LEVEL); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
BlockState state = super.getStateForPlacement(context);
if(!context.getPlayer().isSneaking()) state = state.with(FACING, Direction.UP);
return state;
}
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidBarrelTileEntity)) return;
((FluidBarrelTileEntity)te).readnbt(te_nbt);
((FluidBarrelTileEntity)te).markDirty();
world.getPendingBlockTicks().scheduleTick(pos, this, 4);
}
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(player.getHeldItem(hand).getItem() == asItem()) return ActionResultType.PASS; // Pass that to block placement.
if(world.isRemote) return ActionResultType.SUCCESS;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidBarrelTileEntity)) return ActionResultType.FAIL;
if(!((FluidBarrelTileEntity)te).handlePlayerInteraction(state, world, pos, player, hand)) return ActionResultType.PASS;
world.getPendingBlockTicks().scheduleTick(pos, this, 4);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState state, World world, BlockPos pos)
{
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidBarrelTileEntity)) return 0;
return (int)MathHelper.clamp(((FluidBarrelTileEntity)te).getNormalizedFillLevel() * 15, 0, 15);
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class FluidBarrelTileEntity extends TileEntity implements ICapabilityProvider, ITickableTileEntity
{
private final int TICK_INTERVAL = 10;
private final Fluidics.Tank tank_ = new Fluidics.Tank(capacity_);
private int tick_timer_ = 0;
public boolean handlePlayerInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand)
{
if(world.isRemote()) return false;
{
Tuple<Fluid,Integer> transferred = Fluidics.manualTrackedFluidHandlerInteraction(world, pos, null, player, hand);
if(transferred==null) {
world.playSound(null, pos, SoundEvents.BLOCK_IRON_TRAPDOOR_OPEN, SoundCategory.BLOCKS, 0.2f, 0.02f);
} else if(transferred.getB() > 0) {
SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.ITEM_BUCKET_EMPTY_LAVA: SoundEvents.ITEM_BUCKET_EMPTY;
world.playSound(null, pos, se, SoundCategory.BLOCKS, 1f, 1f);
} else {
SoundEvent se = (transferred.getA()==Fluids.LAVA) ? SoundEvents.ITEM_BUCKET_FILL_LAVA : SoundEvents.ITEM_BUCKET_FILL;
world.playSound(null, pos, se, SoundCategory.BLOCKS, 1f, 1f);
}
}
{
int vol = tank_.getFluidAmount();
int cap = tank_.getCapacity();
String name = Auxiliaries.localizable(tank_.getFluid().getTranslationKey()).getString();
if((vol>0) && (cap>0)) {
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status", null, new Object[]{
Integer.toString(vol), Integer.toString(cap), name
}));
} else {
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.fluid_barrel.status.empty", null, new Object[]{
Integer.toString(vol), Integer.toString(cap)
}));
}
}
return true;
}
public double getNormalizedFillLevel()
{ return (tank_.isEmpty()) ? (0) : ((double)tank_.getFluidAmount()/(double)tank_.getCapacity()); }
public void readnbt(CompoundNBT nbt)
{
if(nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) tank_.readnbt(nbt.getCompound("tank"));
}
public CompoundNBT writenbt(CompoundNBT nbt)
{
if(!tank_.isEmpty()) nbt.put("tank", tank_.writenbt(new CompoundNBT()));
return nbt;
}
protected void on_tank_changed()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
// TileEntity ------------------------------------------------------------------------------
public FluidBarrelTileEntity()
{ this(ModContent.TET_FLUID_BARREL); }
public FluidBarrelTileEntity(TileEntityType<?> te_type)
{
super(te_type);
tank_.setInteractionNotifier((t,d)->on_tank_changed());
}
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); return writenbt(nbt); }
@Override
public void remove()
{ super.remove(); fluid_handler_.invalidate(); }
public CompoundNBT clear_getnbt()
{
CompoundNBT nbt = new CompoundNBT();
if(!tank_.isEmpty()) nbt.put("tank", tank_.writenbt(new CompoundNBT()));
return nbt;
}
// ICapabilityProvider --------------------------------------------------------------------
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(tank_));
@Override
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();
return super.getCapability(capability, facing);
}
// ITickableTileEntity --------------------------------------------------------------------
private boolean transfer_down()
{
if(tank_.isEmpty()) return false;
final IFluidHandler fh = FluidUtil.getFluidHandler(world, pos.down(), Direction.UP).orElse(null);
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_);
final int nfilled = fh.fill(fs, FluidAction.EXECUTE);
if(nfilled <= 0) return false;
tank_.drain(nfilled, FluidAction.EXECUTE);
return true;
}
public void tick()
{
if((world.isRemote()) || (--tick_timer_>=0)) return;
tick_timer_ = TICK_INTERVAL;
final BlockState state = getBlockState();
final Block block = state.getBlock();
if(!(block instanceof FluidBarrelBlock)) return;
if(state.get(FluidBarrelBlock.FACING).getAxis().isVertical()) transfer_down(); // tick_timer_ ==> 1 if something was transferred, otherwise no need to waste CPU
double norm_level = getNormalizedFillLevel();
int fill_level = (norm_level <= 0) ? 0 : ((int)MathHelper.clamp((norm_level * FluidBarrelBlock.FILL_LEVEL_MAX)+0.5, 1, FluidBarrelBlock.FILL_LEVEL_MAX));
if(fill_level != state.get(FluidBarrelBlock.FILL_LEVEL)) {
world.setBlockState(pos, state.with(FluidBarrelBlock.FILL_LEVEL, fill_level), 2);
world.notifyNeighborsOfStateChange(pos, block);
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// Block item
//--------------------------------------------------------------------------------------------------------------------
public static class FluidBarrelItem extends BlockItem
{
public FluidBarrelItem(Block block, Item.Properties builder)
{ super(block, builder); }
private static CompoundNBT read_fluid_nbt(ItemStack stack)
{
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return new CompoundNBT();
final CompoundNBT nbt = stack.getTag().getCompound("tedata");
if(!nbt.contains("tank", Constants.NBT.TAG_COMPOUND)) return new CompoundNBT();
return nbt.getCompound("tank");
}
private static void write_fluid_nbt(ItemStack stack, CompoundNBT fluid_nbt)
{
if((fluid_nbt==null) || (fluid_nbt.isEmpty())) {
if((!stack.hasTag()) || (!stack.getTag().contains("tedata", Constants.NBT.TAG_COMPOUND))) return;
final CompoundNBT tag = stack.getTag();
final CompoundNBT tedata = tag.getCompound("tedata");
if(tedata.contains("tank")) tedata.remove("tank");
if(tedata.isEmpty()) tag.remove("tedata");
stack.setTag(tag.isEmpty() ? null : tag);
} else {
CompoundNBT tag = stack.getTag();
if(tag==null) tag = new CompoundNBT();
CompoundNBT tedata = tag.getCompound("tedata");
if(tedata==null) tedata = new CompoundNBT();
tedata.put("tank", fluid_nbt);
tag.put("tedata", tedata);
stack.setTag(tag);
}
}
public static FluidStack getFluid(ItemStack stack)
{
final CompoundNBT nbt = read_fluid_nbt(stack);
return (nbt.isEmpty()) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt));
}
@Override
public int getItemStackLimit(ItemStack stack)
{ 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); }
}
}

View file

@ -0,0 +1,464 @@
/*
* @file EdFluidFunnel.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* A device that collects and stores fluid blocks above it.
* Tracks flowing fluid to their source blocks. Compatible
* with vanilla infinite water source.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import net.minecraft.block.*;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.fluid.FluidState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.*;
import net.minecraft.util.math.vector.*;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.*;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import wile.engineersdecor.ModEngineersDecor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class EdFluidFunnel
{
private static boolean with_device_fluid_handler_collection = false;
public static void on_config(boolean with_tank_fluid_collection)
{
with_device_fluid_handler_collection = with_tank_fluid_collection;
ModEngineersDecor.logger().info("Config fluid funnel: tank-fluid-collection:"+with_device_fluid_handler_collection);
}
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class FluidFunnelBlock extends DecorBlock.Normal implements IDecorBlock
{
public static final int FILL_LEVEL_MAX = 3;
public static final IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
public FluidFunnelBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(FILL_LEVEL); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(FILL_LEVEL, 0); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new FluidFunnelTileEntity(); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState state, World world, BlockPos pos)
{ return MathHelper.clamp((state.get(FILL_LEVEL)*5), 0, 15); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidFunnelTileEntity)) return;
((FluidFunnelTileEntity)te).readnbt(te_nbt);
((FluidFunnelTileEntity)te).markDirty();
world.setBlockState(pos, state.with(FILL_LEVEL, 0));
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, final TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof FluidFunnelTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = new CompoundNBT();
((FluidFunnelTileEntity)te).writenbt(te_nbt);
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
stacks.add(new ItemStack(this, 1));
}
return stacks;
}
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof FluidFunnelTileEntity)) return ActionResultType.FAIL;
return FluidUtil.interactWithFluidHandler(player, hand, world, pos, rayTraceResult.getFace()) ? ActionResultType.SUCCESS : ActionResultType.FAIL;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{ TileEntity te = world.getTileEntity(pos); if(te instanceof FluidFunnelTileEntity) ((FluidFunnelTileEntity)te).block_changed(); }
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class FluidFunnelTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider, IFluidTank
{
public static final int TANK_CAPACITY = 3000;
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 MAX_TRACK_RADIUS = 16;
public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72;
public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024;
public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS*MAX_TRACK_RADIUS;
public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16;
private FluidStack tank_ = FluidStack.EMPTY;
private int tick_timer_ = 0;
private int collection_timer_ = 0;
private int no_fluid_found_counter_ = 0;
private int intensive_search_counter_ = 0;
private int total_pick_counter_ = 0;
private BlockPos last_pick_pos_ = BlockPos.ZERO;
private ArrayList<Vector3i> search_offsets_ = null;
public void block_changed()
{ tick_timer_ = TICK_INTERVAL; } // collect after flowing fluid has a stable state, otherwise it looks odd.
public FluidFunnelTileEntity()
{ this(ModContent.TET_SMALL_FLUID_FUNNEL); }
public FluidFunnelTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt)
{
tank_ = (!nbt.contains("tank")) ? (FluidStack.EMPTY) : (FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
}
public void writenbt(CompoundNBT nbt)
{
if(!tank_.isEmpty()) nbt.put("tank", tank_.writeToNBT(new CompoundNBT()));
}
// TileEntity -----------------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
fluid_handler_.invalidate();
}
// ICapabilityProvider / Output flow handler ----------------------------------------------------------
private static class OutputFluidHandler implements IFluidHandler
{
private final FluidFunnelTileEntity te;
OutputFluidHandler(FluidFunnelTileEntity parent) { te = parent; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return te.tank_.copy(); }
@Override public int getTankCapacity(int tank) { return TANK_CAPACITY; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return te.drain(resource, action); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return te.drain(maxDrain, action); }
}
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new OutputFluidHandler(this));
@Override
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();
return super.getCapability(capability, facing);
}
// IFluidTank ------------------------------------------------------------------------------------------
@Override
@Nonnull
public FluidStack getFluid()
{ return tank_.copy(); }
@Override
public int getFluidAmount()
{ return tank_.getAmount(); }
@Override
public int getCapacity()
{ return TANK_CAPACITY; }
@Override
public boolean isFluidValid(FluidStack stack)
{ return true; }
@Override
public int fill(FluidStack resource, FluidAction action)
{ return 0; }
@Override
@Nonnull
public FluidStack drain(FluidStack resource, FluidAction action)
{
if((resource==null) || (tank_.isEmpty())) return FluidStack.EMPTY;
return (!(tank_.isFluidEqual(resource))) ? (FluidStack.EMPTY) : drain(resource.getAmount(), action);
}
@Override
@Nonnull
public FluidStack drain(int maxDrain, FluidAction action)
{
if(tank_.isEmpty()) return FluidStack.EMPTY;
FluidStack res = tank_.copy();
maxDrain = MathHelper.clamp(maxDrain ,0 , tank_.getAmount());
res.setAmount(maxDrain);
if(action != FluidAction.EXECUTE) return res;
tank_.setAmount(tank_.getAmount()-maxDrain);
if(tank_.getAmount() <= 0) tank_ = FluidStack.EMPTY;
return res;
}
// ITickableTileEntity --------------------------------------------------------------------------------
private FluidState get_fluidstate(BlockPos pos)
{
// todo: check if getFluidState() is enough
final Block collection_block = world.getBlockState(pos).getBlock();
if((!(collection_block instanceof IFluidBlock)) && (!(collection_block instanceof FlowingFluidBlock)) && (!(collection_block instanceof IWaterLoggable))) {
return Fluids.EMPTY.getDefaultState();
}
return world.getFluidState(pos);
}
private boolean try_pick(BlockPos pos, FluidState fluidstate)
{
if(!fluidstate.isSource()) return false;
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null).orElse(null);
FluidStack fs;
if(hnd != null) {
fs = hnd.drain(TANK_CAPACITY, FluidAction.EXECUTE); // IFluidBlock
} else {
fs = new FluidStack(fluidstate.getFluid(), 1000);
BlockState state = world.getBlockState(pos);
if(state instanceof IBucketPickupHandler) {
((IBucketPickupHandler)state).pickupFluid(world, pos, state);
} else {
world.setBlockState(pos, Blocks.AIR.getDefaultState(), 1|2); // ok we can't leave the block, that would be an infinite source of an unknown fluid.
}
}
if((fs==null) || (fs.isEmpty())) return false; // it's marked nonnull but I don't trust every modder - including meself ...
if(tank_.isEmpty()) {
tank_ = fs.copy();
} else if(tank_.isFluidEqual(fs)) {
tank_.setAmount(MathHelper.clamp(tank_.getAmount()+fs.getAmount(), 0, TANK_CAPACITY));
} else {
return false;
}
return true;
}
private boolean can_pick(BlockPos pos, FluidState fluidstate)
{
if(fluidstate.isSource()) return true;
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null).orElse(null);
if(hnd == null) return false;
FluidStack fs = hnd.drain(TANK_CAPACITY, FluidAction.SIMULATE); // don't trust that everyone returns nonnull
return ((fs!=null) && (!fs.isEmpty())) && (fluidstate.getFluid().isEquivalentTo(fs.getFluid()));
}
private void rebuild_search_offsets(boolean intensive)
{
search_offsets_ = new ArrayList<>(9);
search_offsets_.add(new Vector3i(0, 1, 0)); // up first
{
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(intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs);
search_offsets_.addAll(ofs);
}
if(intensive) {
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)));
Collections.shuffle(ofs);
search_offsets_.addAll(ofs);
}
}
private boolean try_collect(final BlockPos collection_pos)
{
FluidState collection_fluidstate = get_fluidstate(collection_pos);
if(collection_fluidstate.isEmpty()) return false;
Fluid fluid_to_collect = collection_fluidstate.getFluid();
if((!tank_.isEmpty()) && (!tank_.getFluid().isEquivalentTo(fluid_to_collect))) return false;
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((last_pick_pos_==null) || (last_pick_pos_.distanceSq(collection_pos) > MAX_TRACK_RADIUS_SQ)) { last_pick_pos_ = collection_pos; search_offsets_ = null; }
BlockPos pos = last_pick_pos_;
HashSet<BlockPos> checked = new HashSet<>();
Stack<BlockPos> trail = new Stack<BlockPos>();
trail.add(pos);
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_; }
if(search_offsets_ == null) rebuild_search_offsets(intensive);
int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE;
while(++steps <= max) {
int num_adjacent = 0;
for(int i=0; i<search_offsets_.size(); ++i) {
BlockPos p = pos.add(search_offsets_.get(i));
if(checked.contains(p)) continue;
checked.add(p);
++steps;
FluidState fluidstate = get_fluidstate(p);
if(fluidstate.getFluid().isEquivalentTo(fluid_to_collect)) {
++num_adjacent;
pos = p;
trail.push(pos);
if(steps < MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2) {
// check for same fluid above (only source blocks)
final int max_surface_search = (MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2)-steps;
for(int k=0; k<max_surface_search; ++k) {
FluidState fs = get_fluidstate(pos.up());
if(!can_pick(pos.up(), fs)) break;
fluidstate = fs;
pos = pos.up();
trail.push(pos);
}
}
if(try_pick(pos, fluidstate)) {
last_pick_pos_ = pos;
no_fluid_found_counter_ = 0;
search_offsets_ = null;
// probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes.
if((++total_pick_counter_ > 50) && world.rand.nextInt(10)==0) last_pick_pos_ = collection_pos;
//println("PASS " + steps + " - " + (pos.subtract(collection_pos)));
return true;
}
}
}
if(trail.isEmpty()) break; // reset search
if(num_adjacent==0) pos = trail.pop();
}
//println("FAIL=" + steps + " - " + (pos.subtract(collection_pos)));
//String s = new String(); for(BlockPos p:checked) s += "\n" + p; println(s);
if(intensive_search_counter_ > 2) world.removeBlock(pos, false);
last_pick_pos_ = collection_pos;
search_offsets_ = null; // try other search order
++no_fluid_found_counter_;
return false;
}
public void tick()
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
collection_timer_ += TICK_INTERVAL;
final BlockState funnel_state = world.getBlockState(pos);
if(!(funnel_state.getBlock() instanceof FluidFunnelBlock)) return;
boolean dirty = false;
// Collection
if((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_==null) || (tank_.getAmount() <= (TANK_CAPACITY-1000)))) {
collection_timer_ = 0;
if(!world.isBlockPowered(pos)) { // redstone disable feature
if(last_pick_pos_==null) last_pick_pos_ = pos.up();
TileEntity te = with_device_fluid_handler_collection ? (world.getTileEntity(pos.up())) : (null);
if(te != null) {
IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.DOWN).orElse(null);
if(fh == null) {
te = null;
} else if(tank_.isEmpty()) {
FluidStack fs = fh.drain(1000, FluidAction.EXECUTE);
if((fs!=null) && (!fs.isEmpty())) tank_ = fs.copy();
dirty = true;
} else {
FluidStack todrain = new FluidStack(tank_.getFluid(), 1000);
FluidStack fs = fh.drain(todrain, FluidAction.EXECUTE);
if((fs!=null) && (!fs.isEmpty())) tank_.setAmount(tank_.getAmount()+fs.getAmount());
dirty = true;
}
}
if(te==null) {
if(try_collect(pos.up())) dirty = true;
}
}
}
// Gravity fluid transfer
if((tank_.getAmount() >= 1000)) {
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos.down(), Direction.UP).orElse(null);
if(fh != null) {
FluidStack fs = new FluidStack(tank_.getFluid(), 1000);
int nfilled = MathHelper.clamp(fh.fill(fs, FluidAction.EXECUTE), 0, 1000);
tank_.shrink(nfilled);
dirty = true;
}
}
// Block state
int fill_level = (tank_==null) ? 0 : (MathHelper.clamp(tank_.getAmount()/1000,0, FluidFunnelBlock.FILL_LEVEL_MAX));
if(funnel_state.get(FluidFunnelBlock.FILL_LEVEL) != fill_level) world.setBlockState(pos, funnel_state.with(FluidFunnelBlock.FILL_LEVEL, fill_level), 2|16);
if(dirty) markDirty();
}
}
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,20 @@
/*
* @file EdSoilBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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, Block.Properties builder)
{ super(config, builder); }
}

View file

@ -0,0 +1,100 @@
/*
* @file EdFloorGratingBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Floor gratings.
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.pathfinding.PathType;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.ArrayList;
public class EdHatchBlock extends DecorBlock.HorizontalWaterLoggable implements IDecorBlock
{
public static final BooleanProperty OPEN = BlockStateProperties.OPEN;
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
protected final ArrayList<VoxelShape> vshapes_open;
public EdHatchBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABBClosed, final AxisAlignedBB unrotatedAABBOpen)
{
super(config, builder, unrotatedAABBClosed); vshapes_open = makeHorizontalShapeLookup(new AxisAlignedBB[]{unrotatedAABBOpen});
setDefaultState(super.getDefaultState().with(OPEN, false).with(POWERED, false));
}
public EdHatchBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABBsClosed, final AxisAlignedBB[] unrotatedAABBsOpen)
{ super(config, builder, unrotatedAABBsClosed); vshapes_open = makeHorizontalShapeLookup(unrotatedAABBsOpen); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return state.get(OPEN) ? vshapes_open.get((state.get(HORIZONTAL_FACING)).getIndex() & 0x7) : super.getShape(state, source, pos, selectionContext); }
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos)
{ return state.get(OPEN); }
@Override
@SuppressWarnings("deprecation")
public boolean allowsMovement(BlockState state, IBlockReader world, BlockPos pos, PathType type)
{ return state.get(OPEN); }
@Override
public boolean isLadder(BlockState state, IWorldReader world, BlockPos pos, LivingEntity entity)
{ return state.get(OPEN); }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(OPEN, POWERED); }
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.CONSUME;
boolean open = !state.get(OPEN);
world.setBlockState(pos, state.with(OPEN, open), 1|2);
world.playSound(null, pos, open?SoundEvents.BLOCK_IRON_DOOR_OPEN:SoundEvents.BLOCK_IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving)
{
if((world.isRemote) || (!(state.getBlock() instanceof EdHatchBlock))) return;
boolean powered = world.isBlockPowered(pos);
if(powered == state.get(POWERED)) return;
if(powered != state.get(OPEN)) world.playSound(null, pos, powered?SoundEvents.BLOCK_IRON_DOOR_OPEN:SoundEvents.BLOCK_IRON_DOOR_CLOSE, SoundCategory.BLOCKS, 0.7f, 1.4f);
world.setBlockState(pos, state.with(OPEN, powered).with(POWERED, powered), 1|2);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
/*
* @file EdHorizontalSupportBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Horizontal ceiling support. Symmetric x axis, fixed in
* xz plane, therefore boolean placement state.
*/
package wile.engineersdecor.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.entity.EntityType;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.util.*;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
public class EdHorizontalSupportBlock extends DecorBlock.WaterLoggable implements IDecorBlock
{
public static final BooleanProperty EASTWEST = BooleanProperty.create("eastwest");
public static final BooleanProperty LEFTBEAM = BooleanProperty.create("leftbeam");
public static final BooleanProperty RIGHTBEAM = BooleanProperty.create("rightbeam");
public static final IntegerProperty DOWNCONNECT = IntegerProperty.create("downconnect", 0, 2);
protected final ArrayList<VoxelShape> AABBs;
public EdHorizontalSupportBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{
super(config|DecorBlock.CFG_HORIZIONTAL, builder);
AABBs = new ArrayList<VoxelShape>(Arrays.asList(
// Effective bounding box
VoxelShapes.create(Auxiliaries.getRotatedAABB(unrotatedAABB.grow(2.0/16, 0, 0), Direction.NORTH, true)),
VoxelShapes.create(Auxiliaries.getRotatedAABB(unrotatedAABB.grow(2.0/16, 0, 0), Direction.WEST, true)),
// Displayed bounding box
VoxelShapes.create(Auxiliaries.getRotatedAABB(unrotatedAABB, Direction.NORTH, true)),
VoxelShapes.create(Auxiliaries.getRotatedAABB(unrotatedAABB, Direction.WEST, true))
));
}
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return AABBs.get(state.get(EASTWEST) ? 0x1 : 0x0); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(EASTWEST, RIGHTBEAM, LEFTBEAM, DOWNCONNECT); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return temp_block_update_until_better(super.getStateForPlacement(context).with(EASTWEST, context.getPlacementHorizontalFacing().getAxis()==Direction.Axis.X), context.getWorld(), context.getPos()); }
private BlockState temp_block_update_until_better(BlockState state, IWorld world, BlockPos pos)
{
boolean ew = state.get(EASTWEST);
final BlockState rstate = world.getBlockState((!ew) ? (pos.east()) : (pos.south()) );
final BlockState lstate = world.getBlockState((!ew) ? (pos.west()) : (pos.north()) );
final BlockState dstate = world.getBlockState(pos.down());
int down_connector = 0;
if((dstate.getBlock() instanceof EdStraightPoleBlock)) {
final Direction dfacing = dstate.get(EdStraightPoleBlock.FACING);
final EdStraightPoleBlock pole = (EdStraightPoleBlock)dstate.getBlock();
if((dfacing.getAxis() == Direction.Axis.Y)) {
if((pole== ModContent.THICK_STEEL_POLE) || ((pole==ModContent.THICK_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) {
down_connector = 2;
} else if((pole==ModContent.THIN_STEEL_POLE) || ((pole==ModContent.THIN_STEEL_POLE_HEAD) && (dfacing==Direction.UP))) {
down_connector = 1;
}
}
}
return state.with(RIGHTBEAM, (rstate.getBlock()==this) && (rstate.get(EASTWEST) != ew))
.with(LEFTBEAM , (lstate.getBlock()==this) && (lstate.get(EASTWEST) != ew))
.with(DOWNCONNECT , down_connector);
}
@Override
@SuppressWarnings("deprecation")
public BlockState updatePostPlacement(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{ return temp_block_update_until_better(state, world, pos); }
@Deprecated
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos)
{ world.setBlockState(pos, temp_block_update_until_better(state, world, pos)); }
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, Rotation rot)
{ return (rot==Rotation.CLOCKWISE_180) ? state : state.with(EASTWEST, !state.get(EASTWEST)); }
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state; }
}

View file

@ -0,0 +1,799 @@
/*
* @file EdLabeledCrate.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Storage crate with a content hint.
*/
package wile.engineersdecor.blocks;
import com.mojang.blaze3d.matrix.MatrixStack;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.blocks.StandardBlocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Inventories.InventoryRange;
import wile.engineersdecor.libmc.detail.Inventories.SlotRange;
import wile.engineersdecor.libmc.detail.Networking;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.World;
import net.minecraft.block.material.PushReaction;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.item.*;
import net.minecraft.inventory.*;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.inventory.container.ClickType;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import com.mojang.blaze3d.platform.GlStateManager;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class EdLabeledCrate
{
private static boolean with_gui_mouse_handling = true;
private static final HashSet<Item> unstorable_containers = new HashSet<Item>();
public static void on_config(boolean without_gui_mouse_handling)
{
with_gui_mouse_handling = !without_gui_mouse_handling;
// Currently no config, using a tag for this small feature may be uselessly stressing the registry.
unstorable_containers.clear();
unstorable_containers.add(ModContent.LABELED_CRATE.asItem());
unstorable_containers.add(Items.SHULKER_BOX);
}
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class LabeledCrateBlock extends StandardBlocks.Horizontal implements IDecorBlock
{
public LabeledCrateBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState blockState, World world, BlockPos pos)
{ return Container.calcRedstone(world.getTileEntity(pos)); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new LabeledCrateTileEntity(); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof LabeledCrateTileEntity)) return;
((LabeledCrateTileEntity)te).readnbt(te_nbt);
((LabeledCrateTileEntity)te).markDirty();
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, final TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof LabeledCrateTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = ((LabeledCrateTileEntity) te).reset_getnbt();
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
for(ItemStack stack: ((LabeledCrateTileEntity)te).stacks_) stacks.add(stack);
((LabeledCrateTileEntity)te).reset_getnbt();
}
return stacks;
}
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof LabeledCrateTileEntity)) return ActionResultType.SUCCESS;
if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return ActionResultType.SUCCESS;
NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te);
return ActionResultType.SUCCESS;
}
@Override
public PushReaction getPushReaction(BlockState state)
{ return PushReaction.BLOCK; }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(final ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{
if(!Auxiliaries.Tooltip.extendedTipCondition() || Auxiliaries.Tooltip.helpCondition()) {
super.addInformation(stack, world, tooltip, flag);
return;
}
NonNullList<ItemStack> items = NonNullList.withSize(LabeledCrateTileEntity.NUM_OF_SLOTS, ItemStack.EMPTY);
int num_used_slots = 0;
int total_items = 0;
if(stack.hasTag() && stack.getTag().contains("tedata")) {
final CompoundNBT nbt = stack.getTag().getCompound("tedata");
if(nbt.contains("Items")) {
ItemStackHelper.loadAllItems(nbt, items);
for(int i=0; i<LabeledCrateTileEntity.ITEMFRAME_SLOTNO; ++i) {
final ItemStack st = items.get(i);
if(st.isEmpty()) continue;
++num_used_slots;
total_items += st.getCount();
}
}
}
int num_free_slots = LabeledCrateTileEntity.ITEMFRAME_SLOTNO - num_used_slots;
ItemStack frameStack = items.get(LabeledCrateTileEntity.ITEMFRAME_SLOTNO);
tooltip.add(Auxiliaries.localizable(getTranslationKey()+".tip", null, new Object[] {
(frameStack.isEmpty() ? (new StringTextComponent("-/-")) : (new TranslationTextComponent(frameStack.getTranslationKey()))),
num_used_slots,
num_free_slots,
total_items
}));
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class LabeledCrateTileEntity extends TileEntity implements INameable, IInventory, INamedContainerProvider, ISidedInventory
{
public static final int NUM_OF_FIELDS = 1;
public static final int NUM_OF_SLOTS = 55;
public static final int ITEMFRAME_SLOTNO = 54;
// BTileEntity -----------------------------------------------------------------------------
protected NonNullList<ItemStack> stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
public LabeledCrateTileEntity()
{ this(ModContent.TET_LABELED_CRATE); }
public LabeledCrateTileEntity(TileEntityType<?> te_type)
{ super(te_type); reset(); }
public CompoundNBT reset_getnbt()
{
CompoundNBT nbt = new CompoundNBT();
writenbt(nbt);
reset();
return nbt;
}
protected void reset()
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
}
public void readnbt(CompoundNBT compound)
{
NonNullList<ItemStack> stacks = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
if(!compound.isEmpty()) ItemStackHelper.loadAllItems(compound, stacks);
while(stacks.size() < NUM_OF_SLOTS) stacks.add(ItemStack.EMPTY);
stacks_ = stacks;
}
protected void writenbt(CompoundNBT compound)
{
if(!stacks_.stream().allMatch(ItemStack::isEmpty)) ItemStackHelper.saveAllItems(compound, stacks_);
}
public ItemStack getItemFrameStack()
{ return (stacks_.size() > ITEMFRAME_SLOTNO) ? (stacks_.get(ITEMFRAME_SLOTNO)) : (ItemStack.EMPTY); }
protected static boolean inacceptable(ItemStack stack)
{ return (stack.hasTag() && (!stack.getTag().isEmpty()) && (unstorable_containers.contains(stack.getItem()))); }
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
item_handler_.invalidate();
}
@Override
public CompoundNBT getUpdateTag()
{ CompoundNBT nbt = super.getUpdateTag(); writenbt(nbt); return nbt; }
@Override
@Nullable
public SUpdateTileEntityPacket getUpdatePacket()
{ return new SUpdateTileEntityPacket(pos, 1, getUpdateTag()); }
@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) // on client
{
//@todo: check if needed: super.read(pkt.getNbtCompound());
readnbt(pkt.getNbtCompound());
super.onDataPacket(net, pkt);
}
@Override
public void handleUpdateTag(BlockState state, CompoundNBT tag) // on client
{ func_230337_a_/*read*/(state, tag); }
@OnlyIn(Dist.CLIENT)
public double getMaxRenderDistanceSquared()
{ return 1600; }
// INameable ---------------------------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Small Waste Incinerator"); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getCustomName()
{ return getName(); }
// IContainerProvider ----------------------------------------------------------------------
@Override
public ITextComponent getDisplayName()
{ return INameable.super.getDisplayName(); }
@Override
public Container createMenu(int id, PlayerInventory inventory, PlayerEntity player )
{ return new LabeledCrateContainer(id, inventory, this, IWorldPosCallable.of(world, pos), fields); }
// IInventory ------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return ((index >= 0) && (index < getSizeInventory())) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{
if(stack.getCount() > getInventoryStackLimit()) stack.setCount(getInventoryStackLimit());
stacks_.set(index, stack);
markDirty();
if(getWorld() instanceof ServerWorld) {
// This should result in sending TE data (getUpdateTag etc) to the client for the TER.
BlockState state = world.getBlockState(getPos());
getWorld().notifyBlockUpdate(getPos(), state, state, 2|16|32);
}
}
@Override
public int getInventoryStackLimit()
{ return 64; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(PlayerEntity player)
{ return getPos().distanceSq(player.func_233580_cy_()) < 36; }
@Override
public void openInventory(PlayerEntity player)
{}
@Override
public void closeInventory(PlayerEntity player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return (index != ITEMFRAME_SLOTNO) && (!inacceptable(stack)); }
@Override
public void clear()
{ stacks_.clear(); }
// Fields -----------------------------------------------------------------------------------------------
protected final IIntArray fields = new IntArray(LabeledCrateTileEntity.NUM_OF_FIELDS)
{
@Override
public int get(int id)
{
switch(id) {
default: return 0;
}
}
@Override
public void set(int id, int value)
{
switch(id) {
default: break;
}
}
};
// ISidedInventory ----------------------------------------------------------------------------
private static final int[] SIDED_INV_SLOTS;
static {
// that useless unoptimised language ... no proper inline conv to int[]?
// private static final int[] SIDED_INV_SLOTS = IntStream.rangeClosed(0, BTileEntity.NUM_OF_SLOTS-2).boxed().collect(Collectors.toList()).toArray();
SIDED_INV_SLOTS = new int[LabeledCrateTileEntity.NUM_OF_SLOTS-1];
for(int i=0; i<SIDED_INV_SLOTS.length; ++i) SIDED_INV_SLOTS[i] = i;
}
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack stack, Direction direction)
{ return true; }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return true; }
// IItemHandler --------------------------------------------------------------------------------
protected static class BItemHandler implements IItemHandler
{
private LabeledCrateTileEntity te;
BItemHandler(LabeledCrateTileEntity te)
{ this.te = te; }
@Override
public int getSlots()
{ return ITEMFRAME_SLOTNO; } // iframe slot is the last
@Override
public int getSlotLimit(int index)
{ return te.getInventoryStackLimit(); }
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack)
{ return te.isItemValidForSlot(slot, stack); }
@Override
@Nonnull
public ItemStack insertItem(int slotno, @Nonnull ItemStack stack, boolean simulate)
{
if(stack.isEmpty()) return ItemStack.EMPTY;
if((slotno < 0) || ((slotno >= NUM_OF_SLOTS)) || ((slotno == ITEMFRAME_SLOTNO)) ) return stack;
if((!isItemValid(slotno, stack))) return stack;
ItemStack slotstack = getStackInSlot(slotno);
if(!slotstack.isEmpty()) {
if(slotstack.getCount() >= Math.min(slotstack.getMaxStackSize(), getSlotLimit(slotno))) return stack;
if(!ItemHandlerHelper.canItemStacksStack(stack, slotstack)) return stack;
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(slotno)) - slotstack.getCount();
if(stack.getCount() <= n) {
if(!simulate) {
ItemStack copy = stack.copy();
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
}
return ItemStack.EMPTY;
} else {
stack = stack.copy();
if(!simulate) {
ItemStack copy = stack.split(n);
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
return stack;
} else {
stack.shrink(n);
return stack;
}
}
} else {
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(slotno));
if(n < stack.getCount()) {
stack = stack.copy();
if(!simulate) {
te.setInventorySlotContents(slotno, stack.split(n));
return stack;
} else {
stack.shrink(n);
return stack;
}
} else {
if(!simulate) te.setInventorySlotContents(slotno, stack);
return ItemStack.EMPTY;
}
}
}
@Override
@Nonnull
public ItemStack extractItem(int index, int amount, boolean simulate)
{
if((index < 0) || ((index >= NUM_OF_SLOTS)) || ((index == ITEMFRAME_SLOTNO)) ) return ItemStack.EMPTY;
if(!simulate) return ItemStackHelper.getAndSplit(te.stacks_, index, amount);
ItemStack stack = te.stacks_.get(index).copy();
if(stack.getCount() > amount) stack.setCount(amount);
return stack;
}
@Override
@Nonnull
public ItemStack getStackInSlot(int index)
{ return te.getStackInSlot(index); }
}
// Capability export ----------------------------------------------------------------------------
protected LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> new LabeledCrateTileEntity.BItemHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
return super.getCapability(capability, facing);
}
}
//--------------------------------------------------------------------------------------------------------------------
// Container
//--------------------------------------------------------------------------------------------------------------------
public static class LabeledCrateContainer extends Container implements Networking.INetworkSynchronisableContainer
{
protected static final String QUICK_MOVE_ALL = "quick-move-all";
protected static final String INCREASE_STACK = "increase-stack";
protected static final String DECREASE_STACK = "decrease-stack";
//------------------------------------------------------------------------------------------------------------------
protected static class StorageSlot extends Slot
{
StorageSlot(IInventory inventory, int index, int x, int y)
{ super(inventory, index, x, y); }
@Override
public int getSlotStackLimit()
{ return 64; }
@Override
public boolean isItemValid(ItemStack stack)
{ return !LabeledCrateTileEntity.inacceptable(stack); }
}
//------------------------------------------------------------------------------------------------------------------
private static final int PLAYER_INV_START_SLOTNO = LabeledCrateTileEntity.NUM_OF_SLOTS;
private static final int NUM_OF_CONTAINER_SLOTS = LabeledCrateTileEntity.NUM_OF_SLOTS + 36;
protected static final int STORAGE_SLOT_BEGIN = 0;
protected static final int STORAGE_SLOT_END = LabeledCrateTileEntity.ITEMFRAME_SLOTNO;
protected static final int PLAYER_SLOT_BEGIN = LabeledCrateTileEntity.NUM_OF_SLOTS;
protected static final int PLAYER_SLOT_END = LabeledCrateTileEntity.NUM_OF_SLOTS+36;
protected final PlayerEntity player_;
protected final IInventory inventory_;
protected final IWorldPosCallable wpc_;
private final IIntArray fields_;
private final SlotRange player_inventory_slot_range;
private final SlotRange crate_slot_range;
//------------------------------------------------------------------------------------------------------------------
public int field(int index) { return fields_.get(index); }
public PlayerEntity player() { return player_ ; }
public IInventory inventory() { return inventory_ ; }
public World world() { return player_.world; }
//------------------------------------------------------------------------------------------------------------------
public LabeledCrateContainer(int cid, PlayerInventory player_inventory)
{ this(cid, player_inventory, new Inventory(LabeledCrateTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY, new IntArray(LabeledCrateTileEntity.NUM_OF_FIELDS)); }
private LabeledCrateContainer(int cid, PlayerInventory player_inventory, IInventory block_inventory, IWorldPosCallable wpc, IIntArray fields)
{
super(ModContent.CT_LABELED_CRATE, cid);
player_ = player_inventory.player;
inventory_ = block_inventory;
wpc_ = wpc;
fields_ = fields;
crate_slot_range = new SlotRange(inventory_, 0, LabeledCrateTileEntity.ITEMFRAME_SLOTNO);
player_inventory_slot_range = new SlotRange(player_inventory, 0, 36);
int i=-1;
// storage slots (stacks 0 to 53)
for(int y=0; y<6; ++y) {
for(int x=0; x<9; ++x) {
int xpos = 28+x*18, ypos = 10+y*18;
addSlot(new StorageSlot(inventory_, ++i, xpos, ypos));
}
}
// picture frame slot (54)
addSlot(new Slot(new InventoryRange(inventory_, 54, 1), 0, 191, 100) {
@Override public int getSlotStackLimit(){return 1;}
});
// player slots
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x, 28+x*18, 183)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x+y*9+9, 28+x*18, 125+y*18)); // player slots: 9..35
}
}
}
@Override
public boolean canInteractWith(PlayerEntity player)
{ return inventory_.isUsableByPlayer(player); }
@Override
public boolean canMergeSlot(ItemStack stack, Slot slot)
{ return (slot.getSlotStackLimit() > 1); }
@Override
public void onContainerClosed(PlayerEntity player)
{ super.onContainerClosed(player); }
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int index)
{
Slot slot = getSlot(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index>=0) && (index<PLAYER_INV_START_SLOTNO)) {
// Crate slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player slot
if(!mergeItemStack(slot_stack, 0, PLAYER_INV_START_SLOTNO-1, false)) return ItemStack.EMPTY;
} else {
// Invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
// Container client/server synchronisation --------------------------------------------------
@OnlyIn(Dist.CLIENT)
public void onGuiAction(String message, CompoundNBT nbt)
{
nbt.putString("action", message);
Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt);
}
@Override
public void onServerPacketReceived(int windowId, CompoundNBT nbt)
{}
@Override
public void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt)
{
boolean changed = false;
if(!nbt.contains("action")) return;
final int slotId = nbt.contains("slot") ? nbt.getInt("slot") : -1;
switch(nbt.getString("action")) {
case QUICK_MOVE_ALL: {
if((slotId >= STORAGE_SLOT_BEGIN) && (slotId < STORAGE_SLOT_END) && (getSlot(slotId).getHasStack())) {
final Slot slot = getSlot(slotId);
ItemStack remaining = slot.getStack();
slot.putStack(ItemStack.EMPTY);
final ItemStack ref_stack = remaining.copy();
ref_stack.setCount(ref_stack.getMaxStackSize());
for(int i=crate_slot_range.end_slot-crate_slot_range.start_slot; (i>0) && (!remaining.isEmpty()); --i) {
remaining = player_inventory_slot_range.insert(remaining, false, 0, true, true);
if(!remaining.isEmpty()) break;
remaining = crate_slot_range.extract(ref_stack);
}
if(!remaining.isEmpty()) {
slot.putStack(remaining); // put back
}
} else if((slotId >= PLAYER_SLOT_BEGIN) && (slotId < PLAYER_SLOT_END) && (getSlot(slotId).getHasStack())) {
final Slot slot = getSlot(slotId);
ItemStack remaining = slot.getStack();
slot.putStack(ItemStack.EMPTY);
final ItemStack ref_stack = remaining.copy();
ref_stack.setCount(ref_stack.getMaxStackSize());
for(int i=player_inventory_slot_range.end_slot-player_inventory_slot_range.start_slot; (i>0) && (!remaining.isEmpty()); --i) {
remaining = crate_slot_range.insert(remaining, false, 0, false, true);
if(!remaining.isEmpty()) break;
remaining = player_inventory_slot_range.extract(ref_stack);
}
if(!remaining.isEmpty()) {
slot.putStack(remaining); // put back
}
}
changed = true;
} break;
case INCREASE_STACK: {
} break;
case DECREASE_STACK: {
} break;
}
if(changed) {
inventory_.markDirty();
player.inventory.markDirty();
detectAndSendChanges();
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class LabeledCrateGui extends ContainerScreen<LabeledCrateContainer>
{
protected final PlayerEntity player_;
public LabeledCrateGui(LabeledCrateContainer container, PlayerInventory player_inventory, ITextComponent title)
{
super(container, player_inventory, title);
player_ = player_inventory.player;
xSize = 213;
ySize = 206;
}
@Override
public void func_231160_c_/*init*/()
{ super.func_231160_c_(); }
@Override
public void func_230430_a_/*render*/(MatrixStack mx, int mouseX, int mouseY, float partialTicks)
{
func_230446_a_/*renderBackground*/(mx);
super.func_230430_a_(mx, mouseX, mouseY, partialTicks);
func_230459_a_/*renderHoveredToolTip*/(mx, mouseX, mouseY);
}
@Override
protected void func_230451_b_(MatrixStack mx, int x, int y)
{}
@Override
@SuppressWarnings("deprecation")
protected void func_230450_a_/*drawGuiContainerBackgroundLayer*/(MatrixStack mx, float partialTicks, int mouseX, int mouseY)
{
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
getMinecraft().getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/labeled_crate_gui.png"));
final int x0=guiLeft, y0=this.guiTop, w=xSize, h=ySize;
func_238474_b_(mx, x0, y0, 0, 0, w, h);
}
//------------------------------------------------------------------------------------------------------------------
protected void action(String message)
{ action(message, new CompoundNBT()); }
protected void action(String message, CompoundNBT nbt)
{ getContainer().onGuiAction(message, nbt); }
@Override
protected void handleMouseClick(Slot slot, int slotId, int button, ClickType type)
{
if(!with_gui_mouse_handling) {
super.handleMouseClick(slot, slotId, button, type);
} else if((type == ClickType.QUICK_MOVE) && (slot!=null) && slot.getHasStack() && Auxiliaries.isShiftDown() && Auxiliaries.isCtrlDown()) {
CompoundNBT nbt = new CompoundNBT();
nbt.putInt("slot", slotId);
action(LabeledCrateContainer.QUICK_MOVE_ALL, nbt);
} else {
super.handleMouseClick(slot, slotId, button, type);
}
}
@Override
public boolean func_231043_a_/*mouseScrolled*/(double mouseX, double mouseY, double wheel_inc)
{
if(!with_gui_mouse_handling) return super.func_231043_a_/*mouseScrolled*/(mouseX, mouseY, wheel_inc);
final Slot slot = getSlotUnderMouse();
if(!slot.getHasStack()) return true;
final int count = slot.getStack().getCount();
int limit = (Auxiliaries.isShiftDown() ? 2 : 1) * (Auxiliaries.isCtrlDown() ? 4 : 1);
if(wheel_inc > 0.1) {
if(count > 0) {
if((count < slot.getStack().getMaxStackSize()) && (count < slot.getSlotStackLimit())) {
CompoundNBT nbt = new CompoundNBT();
nbt.putInt("slot", slot.slotNumber);
if(limit > 1) nbt.putInt("limit", limit);
action(LabeledCrateContainer.INCREASE_STACK, nbt);
}
}
} else if(wheel_inc < -0.1) {
if(count > 0) {
CompoundNBT nbt = new CompoundNBT();
nbt.putInt("slot", slot.slotNumber);
if(limit > 1) nbt.putInt("limit", limit);
action(LabeledCrateContainer.DECREASE_STACK, nbt);
}
}
return true;
}
}
}

View file

@ -0,0 +1,98 @@
/*
* @file EdLadderBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Ladder block. The implementation is based on the vanilla
* net.minecraft.block.BlockLadder. Minor changes to enable
* later configuration (for block list based construction
* time configuration), does not drop when the block behind
* is broken, etc.
*/
package wile.engineersdecor.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.entity.EntityType;
import net.minecraft.util.math.vector.*;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.*;
import net.minecraft.block.material.PushReaction;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import java.util.List;
public class EdLadderBlock extends LadderBlock implements IDecorBlock
{
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));
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 VoxelShape EDLADDER_NORTH_AABB = VoxelShapes.create(Auxiliaries.getRotatedAABB(EDLADDER_UNROTATED_AABB, Direction.NORTH, false));
private static boolean without_speed_boost_ = false;
public static void on_config(boolean without_speed_boost)
{ without_speed_boost_ = without_speed_boost; }
public EdLadderBlock(long config, Block.Properties builder)
{ super(builder); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.CUTOUT; }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos)
{
switch ((Direction)state.get(FACING)) {
case NORTH: return EDLADDER_NORTH_AABB;
case SOUTH: return EDLADDER_SOUTH_AABB;
case WEST: return EDLADDER_WEST_AABB;
default: return EDLADDER_EAST_AABB;
}
}
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
@SuppressWarnings("deprecation")
public PushReaction getPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
// Player update event, forwarded from the main mod instance.
public static void onPlayerUpdateEvent(final PlayerEntity player)
{
if((without_speed_boost_) || (player.func_233570_aj_()/*onGround*/) || (!player.isOnLadder()) || (player.isSteppingCarefully()) || (player.isSpectator())) return;
double lvy = player.getLookVec().y;
if(Math.abs(lvy) < 0.94) return;
final BlockPos pos = player.func_233580_cy_();
final BlockState state = player.world.getBlockState(pos);
if(!(state.getBlock() instanceof EdLadderBlock)) return;
player.fallDistance = 0;
player.setMotionMultiplier(state, new Vector3d(0.2, (lvy>0)?(3):(6), 0.2));
}
}

View file

@ -0,0 +1,774 @@
/*
* @file EdMilker.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Frequently attracts and milks nearby cows
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Fluidics;
import wile.engineersdecor.libmc.detail.Inventories;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.detail.ExternalObjects;
import net.minecraft.world.World;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.CreatureEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.passive.CowEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.*;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.math.vector.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.PlayerMainInvWrapper;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import wile.engineersdecor.libmc.detail.Overlay;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class EdMilker
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class MilkerBlock extends DecorBlock.Horizontal implements IDecorBlock
{
public static final BooleanProperty FILLED = BooleanProperty.create("filled");
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public MilkerBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABBs)
{ super(config, builder, unrotatedAABBs); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(ACTIVE); builder.add(FILLED); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(FILLED, false).with(ACTIVE, false); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState state, World world, BlockPos pos)
{
MilkerTileEntity te = getTe(world, pos);
return (te==null) ? 0 : MathHelper.clamp((16 * te.fluid_level())/MilkerTileEntity.TANK_CAPACITY, 0, 15);
}
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new MilkerTileEntity(); }
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
if(world.isRemote) return ActionResultType.SUCCESS;
MilkerTileEntity te = getTe(world, pos);
if(te==null) return ActionResultType.FAIL;
final ItemStack in_stack = player.getHeldItem(hand);
final ItemStack out_stack = MilkerTileEntity.milk_filled_container_item(in_stack);
if(in_stack.isEmpty()) {
te.state_message(player);
return ActionResultType.SUCCESS;
} else if(out_stack.isEmpty() && (te.fluid_handler()!=null)) {
return FluidUtil.interactWithFluidHandler(player, hand, te.fluid_handler()) ? ActionResultType.SUCCESS : ActionResultType.FAIL;
} else {
boolean drained = false;
IItemHandler player_inventory = new PlayerMainInvWrapper(player.inventory);
if(te.fluid_level() >= MilkerTileEntity.BUCKET_SIZE) {
final ItemStack insert_stack = out_stack.copy();
ItemStack remainder = ItemHandlerHelper.insertItemStacked(player_inventory, insert_stack, false);
if(remainder.getCount() < insert_stack.getCount()) {
te.drain(MilkerTileEntity.BUCKET_SIZE);
in_stack.shrink(1);
drained = true;
if(remainder.getCount() > 0) {
final ItemEntity ei = new ItemEntity(world, player.getPositionVec().getX(), player.getPositionVec().getY()+0.5, player.getPositionVec().getZ(), remainder);
ei.setPickupDelay(40);
ei.setMotion(0,0,0);
world.addEntity(ei);
}
}
}
if(drained) {
world.playSound(null, pos, SoundEvents.ITEM_BUCKET_FILL, SoundCategory.BLOCKS, 0.8f, 1f);
}
}
return ActionResultType.SUCCESS;
}
@Nullable
private MilkerTileEntity getTe(World world, BlockPos pos)
{ final TileEntity te=world.getTileEntity(pos); return (!(te instanceof MilkerTileEntity)) ? (null) : ((MilkerTileEntity)te); }
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class MilkerTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage, IFluidTank, ICapabilityProvider
{
public static final int BUCKET_SIZE = 1000;
public static final int TICK_INTERVAL = 80;
public static final int PROCESSING_TICK_INTERVAL = 20;
public static final int TANK_CAPACITY = BUCKET_SIZE * 12;
public static final int MAX_MILKING_TANK_LEVEL = TANK_CAPACITY-500;
public static final int FILLED_INDICATION_THRESHOLD = BUCKET_SIZE;
public static final int MAX_ENERGY_BUFFER = 16000;
public static final int MAX_ENERGY_TRANSFER = 512;
public static final int DEFAULT_ENERGY_CONSUMPTION = 0;
public static final int DEFAULT_MILKING_DELAY_PER_COW = 4000;
private static final FluidStack NO_MILK_FLUID = new FluidStack(Fluids.WATER, 0);
private static final Direction FLUID_TRANSFER_DIRECTRIONS[] = {Direction.DOWN,Direction.EAST,Direction.SOUTH,Direction.WEST,Direction.NORTH};
private enum MilkingState { IDLE, PICKED, COMING, POSITIONING, MILKING, LEAVING, WAITING }
private static FluidStack milk_fluid_ = NO_MILK_FLUID;
private static HashMap<ItemStack, ItemStack> milk_containers_ = new HashMap<>();
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
private static long min_milking_delay_per_cow_ticks = DEFAULT_MILKING_DELAY_PER_COW;
private int tick_timer_;
private int energy_stored_;
private int tank_level_ = 0;
private UUID tracked_cow_ = null;
private MilkingState state_ = MilkingState.IDLE;
private int state_timeout_ = 0;
private int state_timer_ = 0;
private BlockPos tracked_cow_original_position_ = null;
public static void on_config(int energy_consumption_per_tick, int min_milking_delay_per_cow)
{
energy_consumption = MathHelper.clamp(energy_consumption_per_tick, 0, 1024);
min_milking_delay_per_cow_ticks = MathHelper.clamp(min_milking_delay_per_cow, 1000, 24000);
{
Fluid milk = null; // FluidRe.getFluid("milk");
if(milk != null) milk_fluid_ = new FluidStack(milk, BUCKET_SIZE);
}
{
milk_containers_.put(new ItemStack(Items.BUCKET), new ItemStack(Items.MILK_BUCKET));
if(ExternalObjects.BOTTLED_MILK_BOTTLE_DRINKLABLE!=null) milk_containers_.put(new ItemStack(Items.GLASS_BOTTLE), new ItemStack(ExternalObjects.BOTTLED_MILK_BOTTLE_DRINKLABLE));
}
ModEngineersDecor.logger().info(
"Config milker: energy consumption:" + energy_consumption + "rf/t"
+ ((milk_fluid_==null)?"":" [milk fluid available]")
+ ((ExternalObjects.BOTTLED_MILK_BOTTLE_DRINKLABLE==null)?"":" [bottledmilk mod available]")
);
}
public MilkerTileEntity()
{ this(ModContent.TET_SMALL_MILKING_MACHINE); }
public MilkerTileEntity(TileEntityType<?> te_type)
{ super(te_type); reset(); }
public void reset()
{
tank_level_ = 0;
energy_stored_ = 0;
tick_timer_ = 0;
tracked_cow_ = null;
state_ = MilkingState.IDLE;
state_timeout_ = 0;
}
public CompoundNBT destroy_getnbt()
{
final UUID cowuid = tracked_cow_;
CompoundNBT nbt = new CompoundNBT();
writenbt(nbt, false); reset();
if(cowuid == null) return nbt;
world.getEntitiesWithinAABB(CowEntity.class, new AxisAlignedBB(pos).grow(16, 16, 16), e->e.getUniqueID().equals(cowuid)).forEach(e->e.setNoAI(false));
return nbt;
}
public void readnbt(CompoundNBT nbt, boolean update_packet)
{
tank_level_ = nbt.getInt("tank");
energy_stored_ = nbt.getInt("energy");
}
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{
if(tank_level_ > 0) nbt.putInt("tank", tank_level_);
if(energy_stored_ > 0) nbt.putInt("energy", energy_stored_ );
}
private IFluidHandler fluid_handler()
{ return fluid_handler_.orElse(null); }
private int fluid_level()
{ return MathHelper.clamp(tank_level_, 0, TANK_CAPACITY); }
private void drain(int amount)
{ tank_level_ = MathHelper.clamp(tank_level_-BUCKET_SIZE, 0, TANK_CAPACITY); markDirty(); }
public void state_message(PlayerEntity player)
{
ITextComponent rf = (energy_consumption <= 0) ? (new StringTextComponent("")) : (Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status.rf", null, new Object[]{energy_stored_}));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_milking_machine.status", null, new Object[]{tank_level_, rf}));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt, false); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt, false); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
fluid_handler_.invalidate();
}
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override public boolean canExtract() { return false; }
@Override public boolean canReceive() { return true; }
@Override public int getMaxEnergyStored() { return MAX_ENERGY_BUFFER; }
@Override public int getEnergyStored() { return energy_stored_; }
@Override public int extractEnergy(int maxExtract, boolean simulate) { return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0;
int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_));
if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER;
if(!simulate) {energy_stored_ += n; markDirty(); }
return n;
}
// IFluidHandler ---------------------------------------------------------------------------------------
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new Fluidics.SingleTankFluidHandler(this));
// IFluidTank ------------------------------------------------------------------------------------------
private boolean has_milk_fluid()
{ return !(NO_MILK_FLUID.isFluidEqual(milk_fluid_)); }
@Override
@Nonnull
public FluidStack getFluid()
{ return has_milk_fluid() ? (new FluidStack(milk_fluid_, fluid_level())) : (FluidStack.EMPTY); }
@Override
public int getFluidAmount()
{ return has_milk_fluid() ? fluid_level() : 0; }
@Override
public int getCapacity()
{ return TANK_CAPACITY; }
@Override
public boolean isFluidValid(FluidStack stack)
{ return has_milk_fluid() && stack.isFluidEqual(milk_fluid_); }
@Override
public int fill(FluidStack resource, FluidAction action)
{ return 0; }
@Override
@Nonnull
public FluidStack drain(FluidStack resource, FluidAction action)
{ return (!resource.isFluidEqual(milk_fluid_)) ? (FluidStack.EMPTY) : drain(resource.getAmount(), action); }
@Override
@Nonnull
public FluidStack drain(int maxDrain, FluidAction action)
{
if((!has_milk_fluid()) || (fluid_level() <= 0)) return FluidStack.EMPTY;
FluidStack fs = milk_fluid_.copy();
fs.setAmount(Math.min(fs.getAmount(), fluid_level()));
if(action==FluidAction.EXECUTE) tank_level_ -= fs.getAmount();
return fs;
}
// ICapabilityProvider ---------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if((capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) && has_milk_fluid()) return fluid_handler_.cast();
if((capability == CapabilityEnergy.ENERGY) && (energy_consumption>0)) return energy_handler_.cast();
return super.getCapability(capability, facing);
}
// ITickable ------------------------------------------------------------------------------------
private static final HashMap<Integer, Long> tracked_cows_ = new HashMap<Integer, Long>();
private void log(String s)
{} // println("Milker|" + s); may be enabled with config, for dev was println
private static ItemStack milk_filled_container_item(ItemStack stack)
{ return milk_containers_.entrySet().stream().filter(e->Inventories.areItemStacksIdentical(e.getKey(), stack)).map(Map.Entry::getValue).findFirst().orElse(ItemStack.EMPTY); }
private boolean fill_adjacent_inventory_item_containers(Direction block_facing)
{
// Check inventory existence, back to down is preferred, otherwise sort back into same inventory.
IItemHandler src = Inventories.itemhandler(world, pos.offset(block_facing), block_facing.getOpposite());
IItemHandler dst = Inventories.itemhandler(world, pos.down(), Direction.UP);
if(src==null) { src = dst; } else if(dst==null) { dst = src; }
if((src==null) || (dst==null)) return false;
boolean dirty = false;
while((tank_level_ >= BUCKET_SIZE)) {
boolean inserted = false;
for(Entry<ItemStack,ItemStack> e:milk_containers_.entrySet()) {
if(Inventories.extract(src, e.getKey(), 1, true).isEmpty()) continue;
if(!Inventories.insert(dst, e.getValue().copy(), false).isEmpty()) continue;
Inventories.extract(src, e.getKey(), 1, false);
tank_level_ -= BUCKET_SIZE;
inserted = true;
dirty = true;
break;
}
if(!inserted) break;
}
return dirty;
}
private void release_cow(CowEntity cow)
{
log("release cow");
if(cow != null) {
cow.setNoAI(false);
SingleMoveGoal.abortFor(cow);
tracked_cows_.remove(cow.getEntityId());
for(int id:tracked_cows_.keySet().stream().filter(i->cow.getEntityWorld().getEntityByID(i)==null).collect(Collectors.toList())) {
tracked_cows_.remove(id);
}
}
tracked_cow_ = null;
state_ = MilkingState.IDLE;
tick_timer_ = TICK_INTERVAL;
}
private boolean milking_process()
{
if((tracked_cow_ == null) && (fluid_level() >= MAX_MILKING_TANK_LEVEL)) return false; // nothing to do
final Direction facing = world.getBlockState(getPos()).get(MilkerBlock.HORIZONTAL_FACING).getOpposite();
final Vector3d target_pos = Vector3d.func_237489_a_(getPos().offset(facing)).add(0.5,0,0.5);
CowEntity cow = null;
{
AxisAlignedBB aabb = new AxisAlignedBB(pos.offset(facing, 3)).grow(4, 2, 4);
final long t = world.getGameTime();
final List<CowEntity> cows = world.getEntitiesWithinAABB(CowEntity.class, aabb,
e-> {
if(e.getUniqueID().equals(tracked_cow_)) return true;
if((tracked_cow_!=null) || e.isChild() || e.isInLove() || e.isBeingRidden()) return false;
if(!e.getNavigator().noPath()) return false;
if(Math.abs(tracked_cows_.getOrDefault(e.getEntityId(), 0L)-t) < min_milking_delay_per_cow_ticks) return false;
return true;
}
);
if(cows.size() == 1) {
cow = cows.get(0); // tracked or only one
} else if(cows.size() > 1) {
cow = cows.get(world.rand.nextInt(cows.size()-1)); // pick one
}
}
if((state_ != MilkingState.IDLE) && ((state_timeout_ -= PROCESSING_TICK_INTERVAL) <= 0)) { release_cow(cow); log("Cow motion timeout"); cow = null; }
if((cow == null) || (!cow.isAlive())) { release_cow(cow); cow = null; }
if(tracked_cow_ == null) state_ = MilkingState.IDLE;
if(cow == null) { log("Init: No cow"); return false; } // retry next cycle
tick_timer_ = PROCESSING_TICK_INTERVAL;
state_timer_ -= PROCESSING_TICK_INTERVAL;
if(state_timer_ > 0) return false;
switch(state_) { // Let's do this the old school FSA sequencing way ...
case IDLE: {
final List<LivingEntity> blocking_entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(pos.offset(facing)).grow(0.5, 0.5, 0.5));
if(blocking_entities.size() > 0) {
tick_timer_ = TICK_INTERVAL;
log("Idle: Position blocked");
if(blocking_entities.get(0) instanceof CowEntity) {
CowEntity blocker = (CowEntity)blocking_entities.get(0);
BlockPos p = getPos().offset(facing,2);
log("Idle: Shove off");
blocker.setNoAI(false);
SingleMoveGoal.startFor(blocker, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().func_233580_cy_())>100));
}
return false;
}
if(cow.getLeashed() || cow.isChild() || cow.isInLove() || (!cow.func_233570_aj_()/*onGround*/) || cow.isBeingRidden() || cow.isSprinting()) return false;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
tracked_cow_ = cow.getUniqueID();
state_ = MilkingState.PICKED;
state_timeout_ = 200;
tracked_cow_original_position_ = cow.func_233580_cy_();
log("Idle: Picked cow " + tracked_cow_);
return false;
}
case PICKED: {
SingleMoveGoal.startFor(
cow, target_pos, 2, 1.0,
(goal, world, pos)->(pos.distanceSq(goal.getCreature().func_233580_cy_())>100),
(goal, world, pos)->{
log("move: position reached");
goal.getCreature().setLocationAndAngles(goal.getTargetPosition().getX(), goal.getTargetPosition().getY(), goal.getTargetPosition().getZ(), facing.getHorizontalAngle(), 0);
},
(goal, world, pos)->{
log("move: aborted");
}
);
state_ = MilkingState.COMING;
state_timeout_ = 400; // 15s should be enough
log("Picked: coming to " + target_pos);
return false;
}
case COMING: {
if(target_pos.squareDistanceTo(cow.getPositionVec()) <= 1) {
log("Coming: position reached");
state_ = MilkingState.POSITIONING;
state_timeout_ = 100; // 5s
} else if((!SingleMoveGoal.isActiveFor(cow))) {
release_cow(cow);
log("Coming: aborted");
} else {
state_timeout_ -= 100;
}
return false;
}
case POSITIONING: {
log("Positioning: start milking");
SingleMoveGoal.abortFor(cow);
cow.setNoAI(true);
cow.setLocationAndAngles(target_pos.getX(), target_pos.getY(), target_pos.getZ(), facing.getHorizontalAngle(), 0);
world.playSound(null, pos, SoundEvents.ENTITY_COW_MILK, SoundCategory.BLOCKS, 0.5f, 1f);
state_timeout_ = 600;
state_ = MilkingState.MILKING;
state_timer_ = 30;
return false;
}
case MILKING: {
tank_level_ = MathHelper.clamp(tank_level_+BUCKET_SIZE, 0, TANK_CAPACITY);
state_timeout_ = 600;
state_ = MilkingState.LEAVING;
state_timer_ = 20;
cow.setNoAI(false);
cow.getNavigator().clearPath();
log("Milking: done, leave");
return true;
}
case LEAVING: {
BlockPos p = (tracked_cow_original_position_ != null) ? (tracked_cow_original_position_) : getPos().offset(facing,2).offset(facing.rotateYCCW());
SingleMoveGoal.startFor(cow, p, 2, 1.0, (goal, world, pos)->(pos.distanceSq(goal.getCreature().func_233580_cy_())>100));
state_timeout_ = 600;
state_timer_ = 500;
tick_timer_ = TICK_INTERVAL;
state_ = MilkingState.WAITING;
tracked_cows_.put(cow.getEntityId(), cow.getEntityWorld().getGameTime());
log("Leaving: process done");
return true;
}
case WAITING: {
// wait for the timeout to kick in until starting with the next.
tick_timer_ = TICK_INTERVAL;
if(state_timer_ < 40) {
tracked_cow_ = null;
release_cow(null);
}
log("Waiting time elapsed");
return true;
}
default: {
release_cow(cow);
}
}
return (tracked_cow_ != null);
}
@Override
public void tick()
{
if((world.isRemote) || ((--tick_timer_ > 0))) return;
tick_timer_ = TICK_INTERVAL;
boolean dirty = false;
final BlockState block_state = world.getBlockState(pos);
if(!(block_state.getBlock() instanceof MilkerBlock)) return;
if(!world.isBlockPowered(pos) || (state_ != MilkingState.IDLE)) {
if(energy_consumption > 0) {
if(energy_stored_ <= 0) return;
energy_stored_ = MathHelper.clamp(energy_stored_-energy_consumption, 0, MAX_ENERGY_BUFFER);
}
// Track and milk cows
if(milking_process()) dirty = true;
// Fluid transfer
if((milk_fluid_.getAmount() > 0) && (fluid_level() >= BUCKET_SIZE)) {
log("Fluid transfer");
for(Direction facing: FLUID_TRANSFER_DIRECTRIONS) {
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos.offset(facing), facing.getOpposite()).orElse(null);
if(fh == null) continue;
FluidStack fs = milk_fluid_.copy();
fs.setAmount(BUCKET_SIZE);
int nfilled = MathHelper.clamp(fh.fill(fs, FluidAction.EXECUTE), 0, BUCKET_SIZE);
if(nfilled <= 0) continue;
tank_level_ -= nfilled;
if(tank_level_ < 0) tank_level_ = 0;
dirty = true;
break;
}
}
// Adjacent inventory update, only done just after milking to prevent waste of server cpu.
if((!dirty) && (fluid_level() >= BUCKET_SIZE)) {
log("Try item transfer");
if(fill_adjacent_inventory_item_containers(block_state.get(MilkerBlock.HORIZONTAL_FACING))) dirty = true;
}
}
// State update
BlockState new_state = block_state.with(MilkerBlock.FILLED, fluid_level()>=FILLED_INDICATION_THRESHOLD).with(MilkerBlock.ACTIVE, state_==MilkingState.MILKING);
if(block_state != new_state) world.setBlockState(pos, new_state,1|2|16);
if(dirty) markDirty();
}
}
public static class SingleMoveGoal extends net.minecraft.entity.ai.goal.MoveToBlockGoal
{
@FunctionalInterface public interface TargetPositionInValidCheck { boolean test(SingleMoveGoal goal, IWorldReader world, BlockPos pos); }
@FunctionalInterface public interface StrollEvent { void apply(SingleMoveGoal goal, IWorldReader world, Vector3d pos); }
private static void log(String s) {} // println("SingleMoveGoal: "+s);
private static final HashMap<Integer, SingleMoveGoal> tracked_entities_ = new HashMap<Integer, SingleMoveGoal>();
private static final int motion_timeout = 20*20;
private boolean aborted_;
private boolean in_position_;
private boolean was_aborted_;
private Vector3d target_pos_;
private TargetPositionInValidCheck abort_condition_;
private StrollEvent on_target_position_reached_;
private StrollEvent on_aborted_;
public SingleMoveGoal(CreatureEntity creature, Vector3d pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
super(creature, speed, 32, 32);
abort_condition_ = abort_condition;
on_target_position_reached_ = on_position_reached;
on_aborted_ = on_aborted;
destinationBlock = new BlockPos(pos.getX(), pos.getY(), pos.getZ());
timeoutCounter = 0;
runDelay = 0;
aborted_ = false;
was_aborted_ = false;
target_pos_ = pos;
}
public static void startFor(CreatureEntity entity, BlockPos target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition)
{ startFor(entity, new Vector3d(target_pos.getX(),target_pos.getY(),target_pos.getZ()), priority, speed, abort_condition, null, null); }
public static boolean startFor(CreatureEntity entity, Vector3d target_pos, int priority, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
synchronized(tracked_entities_) {
SingleMoveGoal goal = tracked_entities_.getOrDefault(entity.getEntityId(), null);
if(goal != null) {
if(!goal.aborted()) return false; // that is still running.
entity.goalSelector.removeGoal(goal);
}
log("::start("+entity.getEntityId()+")");
goal = new SingleMoveGoal(entity, target_pos, speed, abort_condition, on_position_reached, on_aborted);
tracked_entities_.put(entity.getEntityId(), goal);
entity.goalSelector.addGoal(priority, goal);
return true;
}
}
public static boolean isActiveFor(CreatureEntity entity)
{ return (entity != null) && (entity.goalSelector.getRunningGoals().anyMatch(
g->((g.getGoal()) instanceof SingleMoveGoal) && (!((SingleMoveGoal)(g.getGoal())).aborted())
)); }
public static void abortFor(CreatureEntity entity)
{
log("::abort("+entity.getEntityId()+")");
if(entity.isAlive()) {
entity.goalSelector.getRunningGoals().filter(g->(g.getGoal()) instanceof SingleMoveGoal).forEach(g->((SingleMoveGoal)g.getGoal()).abort());
}
final World world = entity.getEntityWorld();
if(world != null) {
// @todo: check nicer way to filter a map.
List<Integer> to_remove = tracked_entities_.keySet().stream().filter(i->(world.getEntityByID(i) == null)).collect(Collectors.toList());
for(int id:to_remove)tracked_entities_.remove(id);
}
}
public Vector3d getTargetPosition()
{ return target_pos_; }
public CreatureEntity getCreature()
{ return creature; }
public synchronized void abort()
{ aborted_ = true; }
public synchronized boolean aborted()
{ return aborted_; }
public synchronized void initialize(Vector3d target_pos, double speed, TargetPositionInValidCheck abort_condition, @Nullable StrollEvent on_position_reached, @Nullable StrollEvent on_aborted)
{
abort_condition_ = abort_condition;
on_target_position_reached_ = on_position_reached;
on_aborted_ = on_aborted;
destinationBlock = new BlockPos(target_pos.getX(), target_pos.getY(), target_pos.getZ());
timeoutCounter = 0;
runDelay = 0;
aborted_ = false;
was_aborted_ = false;
target_pos_ = new Vector3d(target_pos.getX(), target_pos.getY(), target_pos.getZ());
// this.movementSpeed = speed; -> that is final, need to override tick and func_whatever
}
@Override
public void resetTask()
{ runDelay = 0; timeoutCounter = 0; }
@Override
public double getTargetDistanceSq()
{ return 0.7; }
@Override
public boolean shouldMove()
{ return (!aborted()) && (timeoutCounter & 0x7) == 0; }
@Override
public boolean shouldExecute()
{
if(aborted_) {
if((!was_aborted_) && (on_aborted_!=null)) on_aborted_.apply(this, creature.world, target_pos_);
was_aborted_ = true;
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
synchronized(this) { aborted_ = true; }
return false;
} else if(--runDelay > 0) {
return false;
} else {
runDelay = 10;
return true;
}
}
@Override
public void startExecuting()
{
timeoutCounter = 0;
if(!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), this.movementSpeed)) {
abort();
log("startExecuting() -> abort, no path");
} else {
log("startExecuting() -> started");
}
}
public boolean shouldContinueExecuting()
{
if(aborted()) {
log("shouldContinueExecuting() -> already aborted");
return false;
} else if(creature.getNavigator().noPath()) {
if((!creature.getNavigator().setPath(creature.getNavigator().getPathToPos(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), 0), movementSpeed))) {
log("shouldContinueExecuting() -> abort, no path");
abort();
return false;
} else {
return true;
}
} else if(timeoutCounter > motion_timeout) {
log("shouldContinueExecuting() -> abort, timeout");
abort();
return false;
} else if(!shouldMoveTo(creature.world, destinationBlock)) {
log("shouldContinueExecuting() -> abort, !shouldMoveTo()");
abort();
return false;
} else {
log("shouldContinueExecuting() -> yes");
return true;
}
}
@Override
protected boolean shouldMoveTo(IWorldReader world, BlockPos pos)
{
if(abort_condition_.test(this, world, pos)) {
log("shouldMoveTo() -> abort_condition");
return false;
} else {
return true;
}
}
@Override
public void tick()
{
BlockPos testpos = new BlockPos(target_pos_.getX(), creature.getPositionVec().getY(), target_pos_.getZ());
if(!testpos.withinDistance(creature.getPositionVec(), getTargetDistanceSq())) {
if((++timeoutCounter > motion_timeout)) {
log("tick() -> abort, timeoutCounter");
abort();
return;
}
if(shouldMove() && (!creature.getNavigator().tryMoveToXYZ(target_pos_.getX(), target_pos_.getY(), target_pos_.getZ(), movementSpeed))) {
log("tick() -> abort, !tryMoveToXYZ()");
abort();
}
} else {
log("tick() -> abort, in position)");
in_position_ = true;
abort();
if(on_target_position_reached_ != null) on_target_position_reached_.apply(this, creature.world, target_pos_);
}
}
}
}

View file

@ -0,0 +1,663 @@
/*
* @file EdMineralSmelter.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small highly insulated stone liquification furnace
* (magmatic phase).
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.block.*;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.fluid.Fluids;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
public class EdMineralSmelter
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class MineralSmelterBlock extends DecorBlock.Horizontal implements IDecorBlock
{
public static final int PHASE_MAX = 3;
public static final IntegerProperty PHASE = IntegerProperty.create("phase", 0, PHASE_MAX);
public MineralSmelterBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PHASE); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(PHASE, 0); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState state, World world, BlockPos pos)
{ return MathHelper.clamp((state.get(PHASE)*5), 0, 15); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdMineralSmelter.MineralSmelterTileEntity(); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof MineralSmelterTileEntity)) return stacks;
((MineralSmelterTileEntity)te).reset_process();
stacks.add(new ItemStack(this, 1));
return stacks;
}
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(player.isSneaking()) return ActionResultType.PASS;
if(world.isRemote) return ActionResultType.SUCCESS;
MineralSmelterTileEntity te = getTe(world, pos);
if(te==null) return ActionResultType.FAIL;
final ItemStack stack = player.getHeldItem(hand);
boolean dirty = false;
if(te.accepts_lava_container(stack)) {
if(stack.isItemEqualIgnoreDurability(MineralSmelterTileEntity.BUCKET_STACK)) { // check how this works with item capabilities or so
if(te.fluid_level() >= MineralSmelterTileEntity.MAX_BUCKET_EXTRACT_FLUID_LEVEL) {
if(stack.getCount() > 1) {
int target_stack_index = -1;
for(int i=0; i<player.inventory.getSizeInventory(); ++i) {
if(player.inventory.getStackInSlot(i).isEmpty()) {
target_stack_index = i;
break;
}
}
if(target_stack_index >= 0) {
te.reset_process();
stack.shrink(1);
player.setHeldItem(hand, stack);
player.inventory.setInventorySlotContents(target_stack_index, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy());
world.playSound(null, pos, SoundEvents.ITEM_BUCKET_FILL_LAVA, SoundCategory.BLOCKS, 1f, 1f);
dirty = true;
}
} else {
te.reset_process();
player.setHeldItem(hand, MineralSmelterTileEntity.LAVA_BUCKET_STACK.copy());
world.playSound(null, pos, SoundEvents.ITEM_BUCKET_FILL_LAVA, SoundCategory.BLOCKS, 1f, 1f);
dirty = true;
}
}
}
} else if(stack.isEmpty()) {
final ItemStack istack = te.getStackInSlot(1).copy();
if(te.phase() > MineralSmelterTileEntity.PHASE_WARMUP) player.setFire(1);
if(!istack.isEmpty()) {
istack.setCount(1);
player.setHeldItem(hand, istack);
te.reset_process();
dirty = true;
}
} else if(te.insert(stack.copy(),false)) {
stack.shrink(1);
dirty = true;
}
if(dirty) player.inventory.markDirty();
return ActionResultType.SUCCESS;
}
@Override
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if(state.getBlock()!=this) return;
IParticleData particle = ParticleTypes.SMOKE;
switch(state.get(PHASE)) {
case MineralSmelterTileEntity.PHASE_WARMUP:
return;
case MineralSmelterTileEntity.PHASE_HOT:
if(rnd.nextInt(10) > 4) return;
break;
case MineralSmelterTileEntity.PHASE_MAGMABLOCK:
if(rnd.nextInt(10) > 7) return;
particle = ParticleTypes.LARGE_SMOKE;
break;
case MineralSmelterTileEntity.PHASE_LAVA:
if(rnd.nextInt(10) > 2) return;
particle = ParticleTypes.LAVA;
break;
default:
return;
}
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xr=rnd.nextDouble()*0.4-0.2, yr=rnd.nextDouble()*0.5, zr=rnd.nextDouble()*0.4-0.2;
world.addParticle(particle, x+xr, y+yr, z+zr, 0.0, 0.0, 0.0);
}
@Nullable
private MineralSmelterTileEntity getTe(World world, BlockPos pos)
{ final TileEntity te=world.getTileEntity(pos); return (!(te instanceof MineralSmelterTileEntity)) ? (null) : ((MineralSmelterTileEntity)te); }
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class MineralSmelterTileEntity extends TileEntity implements INameable, ITickableTileEntity, ISidedInventory, IEnergyStorage, ICapabilityProvider
{
public static final int TICK_INTERVAL = 20;
public static final int MAX_FLUID_LEVEL = 1000;
public static final int MAX_BUCKET_EXTRACT_FLUID_LEVEL = 900;
public static final int MAX_ENERGY_BUFFER = 32000;
public static final int MAX_ENERGY_TRANSFER = 8192;
public static final int DEFAULT_ENERGY_CONSUMPTION = 92;
public static final int DEFAULT_HEATUP_RATE = 2; // -> 50s for one smelting process
public static final int PHASE_WARMUP = 0;
public static final int PHASE_HOT = 1;
public static final int PHASE_MAGMABLOCK = 2;
public static final int PHASE_LAVA = 3;
private static final ItemStack MAGMA_STACK = new ItemStack(Blocks.MAGMA_BLOCK);
private static final ItemStack BUCKET_STACK = new ItemStack(Items.BUCKET);
private static final ItemStack LAVA_BUCKET_STACK = new ItemStack(Items.LAVA_BUCKET);
private static final FluidStack LAVA_BUCKET_FLUID_STACK = new FluidStack(Fluids.LAVA, 1000);
private static Set<Item> accepted_minerals = new HashSet<Item>();
private static Set<Item> accepted_lava_contrainers = new HashSet<Item>();
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
private static int heatup_rate = DEFAULT_HEATUP_RATE;
private static int cooldown_rate = 1;
private int tick_timer_;
private int energy_stored_;
private int progress_;
private int fluid_level_;
private boolean force_block_update_;
private NonNullList<ItemStack> stacks_ = NonNullList.<ItemStack>withSize(2, ItemStack.EMPTY);
static {
// Lava containers
accepted_lava_contrainers.add(Items.BUCKET);
}
public static void on_config(int consumption, int heatup_per_second)
{
energy_consumption = MathHelper.clamp(consumption, 8, 4096);
heatup_rate = MathHelper.clamp(heatup_per_second, 1, 5);
cooldown_rate = MathHelper.clamp(heatup_per_second/2, 1, 5);
ModEngineersDecor.logger().info("Config mineal smelter energy consumption:" + energy_consumption + "rf/t, heat-up rate: " + heatup_rate + "%/s.");
}
public MineralSmelterTileEntity()
{ this(ModContent.TET_MINERAL_SMELTER); }
public MineralSmelterTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public int progress()
{ return progress_; }
public int phase()
{
if(progress_ >= 100) return PHASE_LAVA;
if(progress_ >= 90) return PHASE_MAGMABLOCK;
if(progress_ >= 5) return PHASE_HOT;
return PHASE_WARMUP;
}
public int fluid_level()
{ return fluid_level_; }
public int comparator_signal()
{ return phase() * 5; }
private boolean accepts_lava_container(ItemStack stack)
{ return accepted_lava_contrainers.contains(stack.getItem()); }
private boolean accepts_input(ItemStack stack)
{
if(!stacks_.get(0).isEmpty()) return false;
if(fluid_level() > MAX_BUCKET_EXTRACT_FLUID_LEVEL) {
return accepts_lava_container(stack);
} else {
if(stack.getItem().getTags().contains(new ResourceLocation(ModEngineersDecor.MODID, "accepted_mineral_smelter_input"))) return true;
return accepted_minerals.contains(stack.getItem());
}
}
public boolean insert(final ItemStack stack, boolean simulate)
{
if(stack.isEmpty() || (!accepts_input(stack))) return false;
if(!simulate) {
ItemStack st = stack.copy();
st.setCount(st.getMaxStackSize());
stacks_.set(0, st);
if(!accepts_lava_container(stack)) progress_ = 0;
force_block_update_ = true;
}
return true;
}
public ItemStack extract(boolean simulate)
{
ItemStack stack = stacks_.get(1).copy();
if(stack.isEmpty()) return ItemStack.EMPTY;
if(!simulate) reset_process();
return stack;
}
protected void reset_process()
{
stacks_ = NonNullList.<ItemStack>withSize(2, ItemStack.EMPTY);
force_block_update_ = true;
fluid_level_ = 0;
tick_timer_ = 0;
progress_ = 0;
}
public void readnbt(CompoundNBT nbt)
{
energy_stored_ = nbt.getInt("energy");
progress_ = nbt.getInt("progress");
fluid_level_ = nbt.getInt("fluidlevel");
ItemStackHelper.loadAllItems(nbt, stacks_);
if(stacks_.size() != 2) reset_process();
}
protected void writenbt(CompoundNBT nbt)
{
nbt.putInt("energy", MathHelper.clamp(energy_stored_,0 , MAX_ENERGY_BUFFER));
nbt.putInt("progress", MathHelper.clamp(progress_,0 , 100));
nbt.putInt("fluidlevel", MathHelper.clamp(fluid_level_,0 , MAX_FLUID_LEVEL));
ItemStackHelper.saveAllItems(nbt, stacks_);
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
fluid_handler_.invalidate();
item_handler_.invalidate();
}
// INamedContainerProvider / INameable ------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Lab furnace"); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getCustomName()
{ return getName(); }
// IInventory ------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return ((index >= 0) && (index < getSizeInventory())) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{ if(stack.getCount()>getInventoryStackLimit()){stack.setCount(getInventoryStackLimit());} stacks_.set(index, stack); markDirty(); }
@Override
public int getInventoryStackLimit()
{ return 1; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(PlayerEntity player)
{ return ((world.getTileEntity(pos) == this) && (player.getDistanceSq(pos.getX()+0.5d, pos.getY()+0.5d, pos.getZ()+0.5d) <= 64.0d)); }
@Override
public void openInventory(PlayerEntity player)
{}
@Override
public void closeInventory(PlayerEntity player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return ((index==0) && accepts_input(stack)) || (index==1); }
@Override
public void clear()
{ reset_process(); }
// ISidedInventory ----------------------------------------------------------------------------
private static final int[] SIDED_INV_SLOTS = new int[] {0,1};
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack stack, Direction direction)
{ return (index==0) && isItemValidForSlot(index, stack); }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return (index==1) && (!stacks_.get(1).isEmpty()); }
// IItemHandler --------------------------------------------------------------------------------
private LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> (IItemHandler)new BItemHandler(this));
protected static class BItemHandler implements IItemHandler
{
private MineralSmelterTileEntity te;
BItemHandler(MineralSmelterTileEntity te)
{ this.te = te; }
@Override
public int getSlots()
{ return 2; }
@Override
public int getSlotLimit(int index)
{ return te.getInventoryStackLimit(); }
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack)
{ return te.isItemValidForSlot(slot, stack); }
@Override
@Nonnull
public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate)
{
ItemStack rstack = stack.copy();
if((index!=0) || (!te.insert(stack.copy(), simulate))) return rstack;
rstack.shrink(1);
return rstack;
}
@Override
@Nonnull
public ItemStack extractItem(int index, int amount, boolean simulate)
{ return (index!=1) ? ItemStack.EMPTY : te.extract(simulate); }
@Override
@Nonnull
public ItemStack getStackInSlot(int index)
{ return te.getStackInSlot(index); }
}
// IFluidHandler --------------------------------------------------------------------------------
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new BFluidHandler(this));
private static class BFluidHandler implements IFluidHandler
{
private final FluidStack lava;
private final MineralSmelterTileEntity te;
BFluidHandler(MineralSmelterTileEntity te)
{ this.te = te; lava = new net.minecraftforge.fluids.FluidStack(net.minecraft.fluid.Fluids.LAVA, 1); }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return new FluidStack(lava, te.fluid_level()); }
@Override public int getTankCapacity(int tank) { return 1000; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return (tank==0) && (stack.isFluidEqual(lava)); }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override
public FluidStack drain(FluidStack resource, FluidAction action)
{ return resource.isFluidEqual(lava) ? drain(resource.getAmount(), action) : FluidStack.EMPTY; }
@Override
public FluidStack drain(int maxDrain, FluidAction action)
{
maxDrain = Math.min(maxDrain, te.fluid_level());
if(action == FluidAction.EXECUTE) te.fluid_level_ -= maxDrain;
return (maxDrain > 0) ? (new FluidStack(lava, maxDrain)) : FluidStack.EMPTY;
}
}
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return MAX_ENERGY_BUFFER; }
@Override
public int getEnergyStored()
{ return energy_stored_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0;
int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_));
if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER;
if(!simulate) {energy_stored_ += n; markDirty(); }
return n;
}
// Capability export ----------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction 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();
return super.getCapability(capability, facing);
}
// ITickable ------------------------------------------------------------------------------------
@Override
public void tick()
{
if(world.isRemote) return;
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
BlockState state = world.getBlockState(pos);
if(!(state.getBlock() instanceof MineralSmelterBlock)) return;
boolean dirty = false;
final int last_phase = phase();
final ItemStack istack = stacks_.get(0);
if(istack.isEmpty() && (fluid_level()==0)) {
progress_ = 0;
} else if((energy_stored_ <= 0) || (world.isBlockPowered(pos))) {
progress_ = MathHelper.clamp(progress_-cooldown_rate, 0,100);
} else if(progress_ >= 100) {
progress_ = 100;
energy_stored_ = MathHelper.clamp(energy_stored_-((energy_consumption*TICK_INTERVAL)/20), 0, MAX_ENERGY_BUFFER);
} else {
energy_stored_ = MathHelper.clamp(energy_stored_-(energy_consumption*TICK_INTERVAL), 0, MAX_ENERGY_BUFFER);
progress_ = MathHelper.clamp(progress_+heatup_rate, 0, 100);
}
int new_phase = phase();
boolean is_lava_container = accepts_lava_container(istack);
if(is_lava_container || (new_phase != last_phase)) {
if(is_lava_container) {
// That stays in the slot until its extracted or somone takes it out.
if(istack.isItemEqual(BUCKET_STACK)) {
if(!stacks_.get(1).isItemEqual(LAVA_BUCKET_STACK)) {
if(fluid_level() >= MAX_BUCKET_EXTRACT_FLUID_LEVEL) {
stacks_.set(1, LAVA_BUCKET_STACK);
world.playSound(null, pos, SoundEvents.ITEM_BUCKET_FILL_LAVA, SoundCategory.BLOCKS, 0.2f, 1.3f);
} else {
stacks_.set(1, istack.copy());
}
dirty = true;
}
} else {
stacks_.set(1, istack.copy());
// Out stack -> Somehow the filled container or container with fluid+fluid_level().
}
} else if(new_phase > last_phase) {
switch(new_phase) {
case PHASE_LAVA:
fluid_level_ = MAX_FLUID_LEVEL;
stacks_.set(1, ItemStack.EMPTY);
stacks_.set(0, ItemStack.EMPTY);
world.playSound(null, pos, SoundEvents.BLOCK_LAVA_AMBIENT, SoundCategory.BLOCKS, 0.2f, 1.0f);
dirty = true;
break;
case PHASE_MAGMABLOCK:
stacks_.set(1, MAGMA_STACK.copy());
world.playSound(null, pos, SoundEvents.BLOCK_FIRE_AMBIENT, SoundCategory.BLOCKS, 0.2f, 0.8f);
dirty = true;
break;
case PHASE_HOT:
world.playSound(null, pos, SoundEvents.BLOCK_FIRE_AMBIENT, SoundCategory.BLOCKS, 0.2f, 0.8f);
break;
}
} else {
switch(new_phase) {
case PHASE_MAGMABLOCK:
stacks_.set(0, (fluid_level_ >= MAX_BUCKET_EXTRACT_FLUID_LEVEL) ? (MAGMA_STACK.copy()) : (ItemStack.EMPTY));
stacks_.set(1, stacks_.get(0).copy());
fluid_level_ = 0;
world.playSound(null, pos, SoundEvents.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.5f, 1.1f);
dirty = true;
break;
case PHASE_HOT:
if(istack.isItemEqual(MAGMA_STACK)) {
stacks_.set(1, new ItemStack(Blocks.OBSIDIAN));
} else {
stacks_.set(1, new ItemStack(Blocks.COBBLESTONE));
}
world.playSound(null, pos, SoundEvents.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.3f, 0.9f);
dirty = true;
break;
case PHASE_WARMUP:
world.playSound(null, pos, SoundEvents.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.3f, 0.7f);
break;
}
}
} else if((phase()==PHASE_LAVA) && (fluid_level() >= MAX_BUCKET_EXTRACT_FLUID_LEVEL)) {
// Fluid transfer check
final IFluidHandler fh = FluidUtil.getFluidHandler(world, getPos().down(), Direction.UP).orElse(null);
if(fh != null) {
int n = fh.fill(LAVA_BUCKET_FLUID_STACK.copy(), FluidAction.SIMULATE);
if(n >= LAVA_BUCKET_FLUID_STACK.getAmount()/2) {
n = fh.fill(LAVA_BUCKET_FLUID_STACK.copy(), FluidAction.EXECUTE);
if(n > 0) {
reset_process();
world.playSound(null, pos, SoundEvents.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.3f, 0.7f);
}
}
}
}
// Block state
if((force_block_update_ || (state.get(MineralSmelterBlock.PHASE) != new_phase))) {
state = state.with(MineralSmelterBlock.PHASE, new_phase);
world.setBlockState(pos, state,3|16);
world.notifyNeighborsOfStateChange(getPos(), state.getBlock());
force_block_update_ = false;
}
if(dirty) markDirty();
}
}
}

View file

@ -0,0 +1,277 @@
/*
* @file EdPipeValve.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Basically a piece of pipe that does not connect to
* pipes on the side, and conducts fluids only in one way.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.Direction;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class EdPipeValve
{
public static final int CFG_CHECK_VALVE = 0x1;
public static final int CFG_ANALOG_VALVE = 0x2;
public static final int CFG_REDSTONE_CONTROLLED_VALVE = 0x4;
public static void on_config(int container_size_decl, int redstone_slope)
{
PipeValveTileEntity.fluid_maxflow_mb = MathHelper.clamp(container_size_decl, 1, 10000);
PipeValveTileEntity.redstone_flow_slope_mb = MathHelper.clamp(redstone_slope, 1, 10000);
ModEngineersDecor.logger().info("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig");
}
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class PipeValveBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock
{
public static final BooleanProperty RS_CN_N = BooleanProperty.create("rs_n");
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_W = BooleanProperty.create("rs_w");
public static final BooleanProperty RS_CN_U = BooleanProperty.create("rs_u");
public static final BooleanProperty RS_CN_D = BooleanProperty.create("rs_d");
public final int valve_config;
public static void on_config(int container_size_decl, int redstone_slope)
{
PipeValveTileEntity.fluid_maxflow_mb = MathHelper.clamp(container_size_decl, 1, 10000);
PipeValveTileEntity.redstone_flow_slope_mb = MathHelper.clamp(redstone_slope, 1, 10000);
ModEngineersDecor.logger().info("Config pipe valve: maxflow:" + PipeValveTileEntity.fluid_maxflow_mb + "mb, redstone amp:" + PipeValveTileEntity.redstone_flow_slope_mb + "mb/sig");
}
public PipeValveBlock(long config, int valve_config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); this.valve_config = valve_config; }
private BlockState get_rsconnector_state(BlockState state, IWorld world, BlockPos pos, @Nullable BlockPos fromPos)
{
if((valve_config & (CFG_REDSTONE_CONTROLLED_VALVE))==0) return state;
Direction.Axis bfa = state.get(FACING).getAxis();
for(Direction f:Direction.values()) {
boolean cn = (f.getAxis() != bfa);
if(cn) {
BlockPos nbp = pos.offset(f);
if((fromPos != null) && (!nbp.equals(fromPos))) continue; // do not change connectors except form the frompos.
BlockState nbs = world.getBlockState(nbp);
if((nbs.getBlock() instanceof PipeValveBlock) || ((!nbs.canProvidePower()) && (!nbs.canConnectRedstone(world, nbp, f.getOpposite())))) cn = false;
}
switch(f) {
case NORTH: state = state.with(RS_CN_N, cn); break;
case SOUTH: state = state.with(RS_CN_S, cn); break;
case EAST: state = state.with(RS_CN_E, cn); break;
case WEST: state = state.with(RS_CN_W, cn); break;
case UP: state = state.with(RS_CN_U, cn); break;
case DOWN: state = state.with(RS_CN_D, cn); break;
}
}
return state;
}
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return VoxelShapes.fullCube(); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(RS_CN_N, RS_CN_S, RS_CN_E, RS_CN_W, RS_CN_U, RS_CN_D); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
return super.getStateForPlacement(context).with(RS_CN_N, false).with(RS_CN_S, false).with(RS_CN_E, false)
.with(RS_CN_W, false).with(RS_CN_U, false).with(RS_CN_D, false);
}
@Override
@SuppressWarnings("deprecation")
public BlockState updatePostPlacement(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{ return get_rsconnector_state(state, world, pos, null); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{ world.notifyNeighborsOfStateChange(pos,this); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new PipeValveTileEntity(); }
@Override
public BlockState rotate(BlockState state, IWorld world, BlockPos pos, Rotation direction)
{ return get_rsconnector_state(state, world, pos, null); } // don't rotate at all
@Override
public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, @Nullable Direction side)
{ return (side!=null) && (side!=state.get(FACING)) && (side!=state.get(FACING).getOpposite()); }
@Override
@SuppressWarnings("deprecation")
public boolean canProvidePower(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
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;
private Direction block_facing_ = null;
private boolean filling_ = false;
private int valve_config_;
public PipeValveTileEntity()
{ this(ModContent.TET_STRAIGHT_PIPE_VALVE); }
public PipeValveTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
private Direction block_facing()
{
BlockState st = getWorld().getBlockState(getPos());
return (st.getBlock() instanceof PipeValveBlock) ? st.get(PipeValveBlock.FACING) : Direction.NORTH;
}
private long valve_config()
{
if(valve_config_ <= 0) {
final Block block = getWorld().getBlockState(getPos()).getBlock();
if(block instanceof PipeValveBlock) valve_config_ = ((PipeValveBlock)block).valve_config;
}
return valve_config_;
}
// TileEntity -----------------------------------------------------------------------------
@Override
public void remove()
{
super.remove();
back_flow_handler_.invalidate();
fluid_handler_.invalidate();
}
// ICapabilityProvider --------------------------------------------------------------------
private LazyOptional<IFluidHandler> back_flow_handler_ = LazyOptional.of(() -> (IFluidHandler)new BackFlowHandler());
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new MainFlowHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
Direction bf = block_facing();
if(facing == bf) return back_flow_handler_.cast();
if(facing == bf.getOpposite()) return fluid_handler_.cast();
return LazyOptional.empty();
}
return super.getCapability(capability, facing);
}
// IFluidHandlers
@Nullable
private IFluidHandler forward_fluid_handler()
{
final TileEntity te = world.getTileEntity(pos.offset(block_facing()));
if(te == null) return null;
return te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing().getOpposite()).orElse(null);
}
// Forward flow handler --
private static class MainFlowHandler implements IFluidHandler
{
private PipeValveTileEntity te;
public MainFlowHandler(PipeValveTileEntity te) { this.te = te; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return fluid_maxflow_mb; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY; }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY; }
@Override public int fill(FluidStack resource, FluidAction action)
{
if(te.filling_) return 0;
final IFluidHandler fh = te.forward_fluid_handler();
if(fh==null) return 0;
FluidStack res = resource.copy();
if((te.valve_config() & CFG_REDSTONE_CONTROLLED_VALVE) != 0) {
int rs = te.world.getRedstonePowerFromNeighbors(te.pos);
if(rs <= 0) return 0;
if(((te.valve_config() & CFG_ANALOG_VALVE) != 0) && (rs < 15)) res.setAmount(MathHelper.clamp(rs * redstone_flow_slope_mb, 1, res.getAmount()));
}
if(res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb);
te.filling_ = true;
int n_filled = fh.fill(res, action);
te.filling_ = false;
return n_filled;
}
}
// Back flow prevention handler --
private static class BackFlowHandler implements IFluidHandler
{
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { 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; }
}
}
}

View file

@ -0,0 +1,794 @@
/*
* @file EdPlacer.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Block placer and planter, factory automation suitable.
*/
package wile.engineersdecor.blocks;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.util.math.vector.Vector3d;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.detail.Networking;
import net.minecraft.block.*;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.item.*;
import net.minecraft.inventory.*;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.SoundEvents;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.fml.network.NetworkHooks;
import com.mojang.blaze3d.systems.RenderSystem;
import wile.engineersdecor.libmc.detail.TooltipDisplay;
import wile.engineersdecor.libmc.detail.TooltipDisplay.TipRange;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class EdPlacer
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class PlacerBlock extends DecorBlock.Directed implements IDecorBlock
{
public PlacerBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return VoxelShapes.fullCube(); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState blockState, World world, BlockPos pos)
{ return Container.calcRedstone(world.getTileEntity(pos)); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new PlacerTileEntity(); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof PlacerTileEntity)) return;
((PlacerTileEntity)te).readnbt(te_nbt, false);
((PlacerTileEntity)te).reset_rtstate();
((PlacerTileEntity)te).markDirty();
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, final TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof PlacerTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = ((PlacerTileEntity) te).clear_getnbt();
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
for(ItemStack stack: ((PlacerTileEntity)te).stacks_) {
if(!stack.isEmpty()) stacks.add(stack);
}
((PlacerTileEntity)te).reset_rtstate();
}
return stacks;
}
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof PlacerTileEntity)) return ActionResultType.FAIL;
if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return ActionResultType.FAIL;
NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te);
return ActionResultType.SUCCESS;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof PlacerTileEntity)) return;
((PlacerTileEntity)te).block_updated();
}
@Override
@SuppressWarnings("deprecation")
public boolean canProvidePower(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class PlacerTileEntity extends TileEntity implements ITickableTileEntity, INameable, IInventory, INamedContainerProvider, ISidedInventory
{
public static final int TICK_INTERVAL = 40;
public static final int NUM_OF_SLOTS = 18;
public static final int NUM_OF_FIELDS = 3;
///
public static final int LOGIC_INVERTED = 0x01;
public static final int LOGIC_CONTINUOUS = 0x02;
///
private boolean block_power_signal_ = false;
private boolean block_power_updated_ = false;
private int logic_ = LOGIC_INVERTED|LOGIC_CONTINUOUS;
private int current_slot_index_ = 0;
private int tick_timer_ = 0;
protected NonNullList<ItemStack> stacks_;
public static void on_config(int cooldown_ticks)
{
// ModEngineersDecor.logger.info("Config factory placer:");
}
public PlacerTileEntity()
{ this(ModContent.TET_FACTORY_PLACER); }
public PlacerTileEntity(TileEntityType<?> te_type)
{
super(te_type);
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
reset_rtstate();
}
public CompoundNBT clear_getnbt()
{
CompoundNBT nbt = new CompoundNBT();
writenbt(nbt, false);
for(int i=0; i<stacks_.size(); ++i) stacks_.set(i, ItemStack.EMPTY);
reset_rtstate();
block_power_updated_ = false;
return nbt;
}
public void reset_rtstate()
{
block_power_signal_ = false;
block_power_updated_ = false;
}
public void readnbt(CompoundNBT nbt, boolean update_packet)
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
ItemStackHelper.loadAllItems(nbt, stacks_);
while(stacks_.size() < NUM_OF_SLOTS) stacks_.add(ItemStack.EMPTY);
block_power_signal_ = nbt.getBoolean("powered");
current_slot_index_ = nbt.getInt("act_slot_index");
logic_ = nbt.getInt("logic");
}
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{
ItemStackHelper.saveAllItems(nbt, stacks_);
nbt.putBoolean("powered", block_power_signal_);
nbt.putInt("act_slot_index", current_slot_index_);
nbt.putInt("logic", logic_);
}
public void block_updated()
{
boolean powered = world.isBlockPowered(pos);
if(block_power_signal_ != powered) block_power_updated_ = true;
block_power_signal_ = powered;
if(block_power_updated_) {
tick_timer_ = 1;
} else if(tick_timer_ > 4) {
tick_timer_ = 4;
}
}
public boolean is_input_slot(int index)
{ return (index >= 0) && (index < NUM_OF_SLOTS); }
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt, false); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt, false); return nbt; }
@Override
public void remove()
{
super.remove();
Arrays.stream(item_handlers).forEach(LazyOptional::invalidate);
}
// INamable ----------------------------------------------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Factory placer"); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getCustomName()
{ return getName(); }
// INamedContainerProvider ------------------------------------------------------------------------------
@Override
public ITextComponent getDisplayName()
{ return INameable.super.getDisplayName(); }
@Override
public Container createMenu(int id, PlayerInventory inventory, PlayerEntity player )
{ return new PlacerContainer(id, inventory, this, IWorldPosCallable.of(world, pos), fields); }
// IInventory -------------------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return (index < getSizeInventory()) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{
if((index<0) || (index >= NUM_OF_SLOTS)) return;
stacks_.set(index, stack);
if(stack.getCount() > getInventoryStackLimit()) stack.setCount(getInventoryStackLimit());
if(tick_timer_ > 8) tick_timer_ = 8;
markDirty();
}
@Override
public int getInventoryStackLimit()
{ return 64; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(PlayerEntity player)
{ return getPos().distanceSq(player.func_233580_cy_()) < 36; }
@Override
public void openInventory(PlayerEntity player)
{}
@Override
public void closeInventory(PlayerEntity player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return (index>=0) && (index<NUM_OF_SLOTS); }
@Override
public void clear()
{ for(int i=0; i<stacks_.size(); ++i) stacks_.set(i, ItemStack.EMPTY); } // should search a better vectorizing method here.
// Fields -----------------------------------------------------------------------------------------------
protected final IIntArray fields = new IntArray(PlacerTileEntity.NUM_OF_FIELDS)
{
@Override
public int get(int id)
{
switch(id) {
case 0: return logic_;
case 1: return block_power_signal_ ? 1 : 0;
case 2: return MathHelper.clamp(current_slot_index_, 0, NUM_OF_SLOTS-1);
default: return 0;
}
}
@Override
public void set(int id, int value)
{
switch(id) {
case 0: logic_ = value; return;
case 1: block_power_signal_ = (value != 0); return;
case 2: current_slot_index_ = MathHelper.clamp(value, 0, NUM_OF_SLOTS-1); return;
default: return;
}
}
};
// ISidedInventory --------------------------------------------------------------------------------------
LazyOptional<? extends IItemHandler>[] item_handlers = SidedInvWrapper.create(this, Direction.UP);
private static final int[] SIDED_INV_SLOTS;
static {
SIDED_INV_SLOTS = new int[NUM_OF_SLOTS];
for(int i=0; i<NUM_OF_SLOTS; ++i) SIDED_INV_SLOTS[i] = i;
}
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack stack, Direction direction)
{ return is_input_slot(index) && isItemValidForSlot(index, stack); }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return false; }
// Capability export ------------------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handlers[0].cast();
return super.getCapability(capability, facing);
}
// ITickable and aux methods ----------------------------------------------------------------------------
private static int next_slot(int i)
{ return (i<NUM_OF_SLOTS-1) ? (i+1) : 0; }
private boolean spit_out(Direction facing)
{ return spit_out(facing, false); }
private boolean spit_out(Direction facing, boolean all)
{
ItemStack stack = stacks_.get(current_slot_index_);
ItemStack drop = stack.copy();
if(!all) {
stack.shrink(1);
stacks_.set(current_slot_index_, stack);
drop.setCount(1);
} else {
stacks_.set(current_slot_index_, ItemStack.EMPTY);
}
for(int i=0; i<8; ++i) {
BlockPos p = pos.offset(facing, i);
if(!world.isAirBlock(p)) continue;
world.addEntity(new ItemEntity(world, (p.getX()+0.5), (p.getY()+0.5), (p.getZ()+0.5), drop));
world.playSound(null, p, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 0.7f, 0.8f);
break;
}
return true;
}
private boolean try_place(Direction facing, boolean triggered)
{
if(world.isRemote) return false;
BlockPos placement_pos = pos.offset(facing);
if(world.getTileEntity(placement_pos) != null) return false;
ItemStack current_stack = ItemStack.EMPTY;
for(int i=0; i<NUM_OF_SLOTS; ++i) {
if(current_slot_index_ >= NUM_OF_SLOTS) current_slot_index_ = 0;
current_stack = stacks_.get(current_slot_index_);
if(!current_stack.isEmpty()) break;
current_slot_index_ = next_slot(current_slot_index_);
}
if(current_stack.isEmpty()) { current_slot_index_ = 0; return false; }
boolean no_space = false;
final Item item = current_stack.getItem();
Block block = Block.getBlockFromItem(item);
if(block == Blocks.AIR) {
if(item != null) {
return spit_out(facing); // Item not accepted
} else {
// try next slot
}
} else if(block instanceof IPlantable) {
if(world.isAirBlock(placement_pos)) {
// plant here, block below has to be valid soil.
BlockState soilstate = world.getBlockState(placement_pos.down());
if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, Direction.UP, (IPlantable)block)) {
block = Blocks.AIR;
}
} else {
// adjacent block is the soil, plant above if the soil is valid.
BlockState soilstate = world.getBlockState(placement_pos);
if(soilstate.getBlock() == block) {
// The plant is already planted from the case above.
block = Blocks.AIR;
no_space = true;
} else if(!world.isAirBlock(placement_pos.up())) {
// If this is the soil an air block is needed above, if that is blocked we can't plant.
block = Blocks.AIR;
no_space = true;
} else if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, Direction.UP, (IPlantable)block)) {
// Would be space above, but it's not the right soil for the plant.
block = Blocks.AIR;
} else {
// Ok, plant above.
placement_pos = placement_pos.up();
}
}
} else if(
(!world.getBlockState(placement_pos).getMaterial().isReplaceable()) ||
(!world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(placement_pos), (Entity e)->{
if(e.canBeCollidedWith()) return true;
if(triggered) return false;
if((e instanceof ItemEntity)) {
if((e.getMotion().getY() > 0) || (e.getMotion().getY() < -0.5)) return true; // not falling or falling by
if(Math.abs(e.getMotion().getX())+Math.abs(e.getMotion().getZ()) > 0) return true; // not straight
}
return false;
}).isEmpty())
) {
block = Blocks.AIR;
no_space = true;
}
// println("PLACE " + current_stack + " --> " + block + " at " + placement_pos.subtract(pos) + "( item=" + item + ")");
if(block != Blocks.AIR) {
try {
BlockItemUseContext use_context = null;
{
final FakePlayer placer = net.minecraftforge.common.util.FakePlayerFactory.getMinecraft((ServerWorld)world);
if(placer != null) {
ItemStack placement_stack = current_stack.copy();
placement_stack.setCount(1);
ItemStack held = placer.getHeldItem(Hand.MAIN_HAND);
placer.setHeldItem(Hand.MAIN_HAND, placement_stack);
use_context = new BlockItemUseContext(new ItemUseContext(placer, Hand.MAIN_HAND, new BlockRayTraceResult(new Vector3d(0.5,0,0.5), Direction.DOWN, placement_pos, false)));
placer.setHeldItem(Hand.MAIN_HAND, held);
}
}
final BlockState placement_state = (use_context==null) ? (block.getDefaultState()) : (block.getStateForPlacement(use_context));
if(placement_state == null) {
return spit_out(facing);
} else if((use_context!=null) && (item instanceof BlockItem)) {
if(((BlockItem)item).tryPlace(use_context) != ActionResultType.FAIL) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
} else if(block instanceof IPlantable) {
if(world.setBlockState(placement_pos, placement_state, 1|2|8)) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
}
} else {
return spit_out(facing);
}
} else {
if(world.setBlockState(placement_pos, placement_state, 1|2|8)) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
}
}
current_stack.shrink(1);
stacks_.set(current_slot_index_, current_stack);
return true;
} catch(Throwable e) {
// The block really needs a player or other issues happened during placement.
// A hard crash should not be fired here, instead spit out the item to indicated that this
// block is not compatible.
ModEngineersDecor.logger().error("Exception while trying to place " + ((block==null)?(""):(""+block)) + ", spitting out. Exception is: " + e);
world.removeBlock(placement_pos, false);
return spit_out(facing, true);
}
}
if((!no_space) && (!current_stack.isEmpty())) {
// There is space, but the current plant cannot be planted there, so try next.
for(int i=0; i<NUM_OF_SLOTS; ++i) {
current_slot_index_ = next_slot(current_slot_index_);
if(!stacks_.get(current_slot_index_).isEmpty()) break;
}
}
return false;
}
@Override
public void tick()
{
// Tick cycle pre-conditions
if(world.isRemote) return;
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
// Cycle init
final BlockState state = world.getBlockState(pos);
if(!(state.getBlock() instanceof PlacerBlock)) { block_power_signal_= false; return; }
final boolean updated = block_power_updated_;
final boolean rssignal = ((logic_ & LOGIC_INVERTED)!=0)==(!block_power_signal_);
final boolean trigger = (rssignal && ((updated) || ((logic_ & LOGIC_CONTINUOUS)!=0)));
final Direction placer_facing = state.get(PlacerBlock.FACING);
boolean dirty = updated;
// Trigger edge detection for next cycle
{
boolean tr = world.isBlockPowered(pos);
block_power_updated_ = (block_power_signal_ != tr);
block_power_signal_ = tr;
if(block_power_updated_) dirty = true;
}
// Placing
if(trigger && try_place(placer_facing, rssignal && updated)) dirty = true;
if(dirty) markDirty();
if(trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Container
//--------------------------------------------------------------------------------------------------------------------
public static class PlacerContainer extends Container implements Networking.INetworkSynchronisableContainer
{
private static final int PLAYER_INV_START_SLOTNO = PlacerTileEntity.NUM_OF_SLOTS;
private final PlayerEntity player_;
private final IInventory inventory_;
private final IWorldPosCallable wpc_;
private final IIntArray fields_;
public final int field(int index) { return fields_.get(index); }
public PlacerContainer(int cid, PlayerInventory player_inventory)
{ this(cid, player_inventory, new Inventory(PlacerTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY, new IntArray(PlacerTileEntity.NUM_OF_FIELDS)); }
private PlacerContainer(int cid, PlayerInventory player_inventory, IInventory block_inventory, IWorldPosCallable wpc, IIntArray fields)
{
super(ModContent.CT_FACTORY_PLACER, cid);
fields_ = fields;
wpc_ = wpc;
player_ = player_inventory.player;
inventory_ = block_inventory;
int i=-1;
// device slots (stacks 0 to 17)
for(int y=0; y<3; ++y) {
for(int x=0; x<6; ++x) {
int xpos = 11+x*18, ypos = 9+y*17;
addSlot(new Slot(inventory_, ++i, xpos, ypos));
}
}
// player slots
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x, 9+x*18, 129)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x+y*9+9, 9+x*18, 71+y*18)); // player slots: 9..35
}
}
this.trackIntArray(fields_); // === Add reference holders
}
@Override
public boolean canInteractWith(PlayerEntity player)
{ return inventory_.isUsableByPlayer(player); }
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int index)
{
Slot slot = getSlot(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index>=0) && (index<PLAYER_INV_START_SLOTNO)) {
// Device slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player slot
if(!mergeItemStack(slot_stack, 0, PlacerTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY;
} else {
// invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
// INetworkSynchronisableContainer ---------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public void onGuiAction(CompoundNBT nbt)
{ Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt); }
@OnlyIn(Dist.CLIENT)
public void onGuiAction(String key, int value)
{
CompoundNBT nbt = new CompoundNBT();
nbt.putInt(key, value);
Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt);
}
@Override
public void onServerPacketReceived(int windowId, CompoundNBT nbt)
{}
@Override
public void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt)
{
if(!(inventory_ instanceof PlacerTileEntity)) return;
PlacerTileEntity te = (PlacerTileEntity)inventory_;
if(nbt.contains("logic")) te.logic_ = nbt.getInt("logic");
if(nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger")!=0)) { te.block_power_signal_=true; te.block_power_updated_=true; te.tick_timer_=1; }
te.markDirty();
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class PlacerGui extends ContainerScreen<PlacerContainer>
{
protected final PlayerEntity player_;
protected final TooltipDisplay tooltip_ = new TooltipDisplay();
public PlacerGui(PlacerContainer container, PlayerInventory player_inventory, ITextComponent title)
{ super(container, player_inventory, title); this.player_ = player_inventory.player; }
@Override
public void func_231160_c_/*init*/()
{
super.func_231160_c_();
{
final String prefix = ModContent.FACTORY_PLACER.getTranslationKey() + ".tooltips.";
final int x0 = getGuiLeft(), y0 = getGuiTop();
tooltip_.init(
new TipRange(x0+133, y0+49, 9, 9, new TranslationTextComponent(prefix + "rssignal")),
new TipRange(x0+145, y0+49, 9, 9, new TranslationTextComponent(prefix + "inversion")),
new TipRange(x0+159, y0+49, 9, 9, new TranslationTextComponent(prefix + "triggermode"))
);
}
}
@Override
public void func_230430_a_/*render*/(MatrixStack mx, int mouseX, int mouseY, float partialTicks)
{
func_230446_a_/*renderBackground*/(mx);
super.func_230430_a_(mx, mouseX, mouseY, partialTicks);
if(!tooltip_.render(mx, this, mouseX, mouseY)) func_230459_a_/*renderHoveredToolTip*/(mx, mouseX, mouseY);
}
@Override
protected void func_230451_b_(MatrixStack mx, int x, int y)
{}
@Override
public boolean func_231044_a_/*mouseClicked*/(double mouseX, double mouseY, int mouseButton)
{
tooltip_.resetTimer();
PlacerContainer container = (PlacerContainer)getContainer();
int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5);
if((!isPointInRegion(126, 1, 49, 60, mouseX, mouseY))) {
return super.func_231044_a_(mouseX, mouseY, mouseButton);
} else if(isPointInRegion(133, 49, 9, 9, mouseX, mouseY)) {
container.onGuiAction("manual_trigger", 1);
} else if(isPointInRegion(145, 49, 9, 9, mouseX, mouseY)) {
container.onGuiAction("logic", container.field(0) ^ PlacerTileEntity.LOGIC_INVERTED);
} else if(isPointInRegion(159, 49, 7, 9, mouseX, mouseY)) {
container.onGuiAction("logic", container.field(0) ^ PlacerTileEntity.LOGIC_CONTINUOUS);
}
return true;
}
@Override
@SuppressWarnings("deprecation")
protected void func_230450_a_/*drawGuiContainerBackgroundLayer*/(MatrixStack mx, float partialTicks, int mouseX, int mouseY)
{
RenderSystem.enableBlend();
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.getMinecraft().getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/factory_placer_gui.png"));
final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize();
func_238474_b_(mx, x0, y0, 0, 0, w, h);
PlacerContainer container = (PlacerContainer)getContainer();
// active slot
{
int slot_index = container.field(2);
if((slot_index < 0) || (slot_index >= PlacerTileEntity.NUM_OF_SLOTS)) slot_index = 0;
int x = (x0+10+((slot_index % 6) * 18));
int y = (y0+8+((slot_index / 6) * 17));
func_238474_b_(mx, x, y, 200, 8, 18, 18);
}
// redstone input
{
if(container.field(1) != 0) {
func_238474_b_(mx, x0+133, y0+49, 217, 49, 9, 9);
}
}
// trigger logic
{
int inverter_offset = ((container.field(0) & PlacerTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0;
func_238474_b_(mx, x0+145, y0+49, 177+inverter_offset, 49, 9, 9);
int pulse_mode_offset = ((container.field(0) & PlacerTileEntity.LOGIC_CONTINUOUS ) != 0) ? 9 : 0;
func_238474_b_(mx, x0+159, y0+49, 199+pulse_mode_offset, 49, 9, 9);
}
RenderSystem.disableBlend();
}
}
}

View file

@ -0,0 +1,18 @@
/*
* @file EdSlabBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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, Block.Properties builder)
{ super(config, builder); }
}

View file

@ -0,0 +1,19 @@
/*
* @file EdSlabSliceBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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.*;
public class EdSlabSliceBlock extends SlabSliceBlock implements IDecorBlock
{
public EdSlabSliceBlock(long config, Block.Properties builder)
{ super(config, builder); }
}

View file

@ -0,0 +1,231 @@
/*
* @file EdSolarPanel.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Smaller (cutout) block with a defined facing.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.LightType;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import javax.annotation.Nullable;
public class EdSolarPanel
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class SolarPanelBlock extends DecorBlock.Normal implements IDecorBlock
{
public static final IntegerProperty EXPOSITION = IntegerProperty.create("exposition", 0, 4);
public SolarPanelBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{
super(config, builder, unrotatedAABB);
setDefaultState(stateContainer.getBaseState().with(EXPOSITION, 1));
}
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(EXPOSITION); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdSolarPanel.SolarPanelTileEntity(); }
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof SolarPanelTileEntity) ((SolarPanelTileEntity)te).state_message(player);
return ActionResultType.SUCCESS;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class SolarPanelTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider, IEnergyStorage
{
public static final int DEFAULT_PEAK_POWER = 40;
public static final int TICK_INTERVAL = 8;
public static final int ACCUMULATION_INTERVAL = 4;
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;
private static int max_power_storage_ = 64000;
private static int max_feed_power = 8192;
private int tick_timer_ = 0;
private int recalc_timer_ = 0;
private int accumulated_power_ = 0;
private int current_production_ = 0;
private int current_feedin_ = 0;
private boolean output_enabled_ = false;
public static void on_config(int peak_power_per_tick)
{
peak_power_per_tick_ = MathHelper.clamp(peak_power_per_tick, 2, 8192);
ModEngineersDecor.logger().info("Config small solar panel: Peak production:" + peak_power_per_tick_ + "/tick");
}
//------------------------------------------------------------------------------------------------------------------
public SolarPanelTileEntity()
{ this(ModContent.TET_SMALL_SOLAR_PANEL); }
public SolarPanelTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt, boolean update_packet)
{ accumulated_power_ = nbt.getInt("energy"); }
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{ nbt.putInt("energy", accumulated_power_); }
public void state_message(PlayerEntity player)
{
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", null, new Object[]{soc, max_power_storage_, current_production_, current_feedin_ }));
}
// IEnergyStorage --------------------------------------------------------------------------
@Override
public boolean canExtract()
{ return true; }
@Override
public boolean canReceive()
{ return false; }
@Override
public int getMaxEnergyStored()
{ return max_power_storage_; }
@Override
public int getEnergyStored()
{ return accumulated_power_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{
int p = Math.min(accumulated_power_, maxExtract);
if(!simulate) accumulated_power_ -= p;
return p;
}
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{ return 0; }
// ICapabilityProvider ---------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
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);
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt, false); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt, false); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
}
@Override
public void tick()
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = TICK_INTERVAL;
BlockState state = world.getBlockState(pos);
if(!(state.getBlock() instanceof SolarPanelBlock)) return;
current_feedin_ = 0;
if(output_enabled_) {
for(int i=0; (i<transfer_directions_.length) && (accumulated_power_>0); ++i) {
final Direction f = transfer_directions_[i];
TileEntity te = world.getTileEntity(pos.offset(f));
if(te==null) continue;
IEnergyStorage es = te.getCapability(CapabilityEnergy.ENERGY, f.getOpposite()).orElse(null);
if((es==null) || (!es.canReceive())) continue;
int fed = es.receiveEnergy(Math.min(accumulated_power_, max_feed_power * TICK_INTERVAL), false);
accumulated_power_ = MathHelper.clamp(accumulated_power_-fed,0, accumulated_power_);
current_feedin_ += fed;
}
}
current_feedin_ /= TICK_INTERVAL;
if((accumulated_power_ <= 0) || (current_feedin_ <= 0)) output_enabled_ = false; // feed-in power: no need to waste CPU if noone needs power.
if(!world.canBlockSeeSky(pos)) {
tick_timer_ = TICK_INTERVAL * 5;
current_production_ = 0;
if(state.get((SolarPanelBlock.EXPOSITION))!=2) world.setBlockState(pos, state.with(SolarPanelBlock.EXPOSITION, 2));
return;
}
if(--recalc_timer_ > 0) return;
recalc_timer_ = ACCUMULATION_INTERVAL + ((int)(Math.random()+.5));
int theta = ((((int)(world.getCelestialAngleRadians(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.with(SolarPanelBlock.EXPOSITION, e);
if(nstate != state) world.setBlockState(pos, nstate, 1|2);
final double eff = (1.0-((world.getRainStrength(1f)*0.6)+(world.getThunderStrength(1f)*0.3)));
final double ll = ((double)(world.getLightManager().getLightEngine(LightType.SKY).getLightFor(getPos())))/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_ >= (max_power_storage_/5)) output_enabled_ = true;
}
}
}

View file

@ -0,0 +1,22 @@
/*
* @file EdStairsBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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, Block.Properties properties)
{ super(config, state, properties); }
public EdStairsBlock(long config, java.util.function.Supplier<BlockState> state, Block.Properties properties)
{ super(config, state, properties); }
}

View file

@ -0,0 +1,41 @@
/*
* @file EdStraightPoleBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Smaller (cutout) block with a defined facing.
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.world.World;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nullable;
public class EdStraightPoleBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock
{
public EdStraightPoleBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
Direction facing = context.getFace();
BlockState state = super.getStateForPlacement(context).with(FACING, facing);
if((config & DecorBlock.CFG_FLIP_PLACEMENT_IF_SAME) != 0) {
World world = context.getWorld();
BlockPos pos = context.getPos();
if(world.getBlockState(pos.offset(facing.getOpposite())).getBlock() instanceof EdStraightPoleBlock) {
state = state.with(FACING, state.get(FACING).getOpposite());
}
}
return state;
}
}

View file

@ -0,0 +1,324 @@
/*
* @file EdTestBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Creative mod testing block
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.fluid.Fluids;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class EdTestBlock
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class TestBlock extends DecorBlock.Directed implements Auxiliaries.IExperimentalFeature, IDecorBlock
{
public TestBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return VoxelShapes.fullCube(); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new TestTileEntity(); }
@Override
public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, @Nullable Direction side)
{ return true; }
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, TileEntity te, boolean explosion)
{
ArrayList<ItemStack> list = new ArrayList<ItemStack>();
list.add(new ItemStack(this, 1));
return list;
}
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof TestTileEntity)) return ActionResultType.SUCCESS;
((TestTileEntity)te).activated(player, hand, hit);
return ActionResultType.SUCCESS;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class TestTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider //, IItemHandler, IEnergyStorage
{
private int tick_interval_ = 10;
private int passive_tank_capacity_ = 32000;
private FluidStack passive_tank_ = FluidStack.EMPTY;
private FluidStack passive_drain_fluidstack_ = new FluidStack(Fluids.WATER, 1000);
private int passive_drain_max_flowrate_ = 1000;
private int passive_fill_max_flowrate_ = 1000;
private int passive_num_drained_general_mb_ = 0;
private int passive_num_drained_specific_mb_ = 0;
private int passive_num_filled_specific_mb_ = 0;
private int passive_num_fh_interactions_ = 0;
private FluidStack active_fill_fluidstack_ = FluidStack.EMPTY;
private int active_num_filled_ = 0;
private int tick_timer_ = 0;
// ------------------------------------------------------------------------------------------
public TestTileEntity()
{ this(ModContent.TET_TEST_BLOCK); }
public TestTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
// ------------------------------------------------------------------------------------------
private Direction block_facing()
{
BlockState st = getWorld().getBlockState(getPos());
return (st.getBlock() instanceof TestBlock) ? st.get(TestBlock.FACING) : Direction.NORTH;
}
private String dump_fluid_stack(FluidStack fs)
{
String s = "";
if(fs.getFluid().getRegistryName().getNamespace() != "minecraft") s += fs.getFluid().getRegistryName().getNamespace()+":";
s += fs.getFluid().getRegistryName().getPath();
s += " x" + fs.getAmount();
return "[" + s + "]";
}
public void activated(PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
if(world.isRemote()) return;
final ItemStack held_stack = player.getHeldItem(hand);
// Empty hand -> statistics
{
if(held_stack.isEmpty()) {
String message = "";
if(passive_num_filled_specific_mb_>0 || passive_num_drained_specific_mb_>0 || passive_num_drained_general_mb_>0) {
message += "Fluid handler: filled:" + passive_num_filled_specific_mb_ + ", drained:" + (passive_num_drained_specific_mb_+ passive_num_drained_general_mb_) + ", interactions:" + passive_num_fh_interactions_ + "\n";
}
if(active_num_filled_>0) {
message += "Fluid insertion:" + active_num_filled_ + "mb, (current:" + dump_fluid_stack(active_fill_fluidstack_) + ")\n";
}
if(message.isEmpty()) {
message = "No fluid, energy, or item interactions done yet.";
}
Auxiliaries.playerChatMessage(player, message);
return;
}
}
// Fluid container -> set fluid to insert, increase/decrease amount.
{
final IFluidHandlerItem fhi = held_stack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY).orElse(null);
if((fhi != null)) {
int ntanks = fhi.getTanks();
if(ntanks == 0) return;
int capacity = fhi.getTankCapacity(0);
FluidStack fs = fhi.drain(capacity, FluidAction.SIMULATE);
if(!fs.isEmpty()) {
if(active_fill_fluidstack_.isEmpty()) {
active_fill_fluidstack_ = fs.copy();
Auxiliaries.playerChatMessage(player, "Fluid insertion fluid set: " + dump_fluid_stack(active_fill_fluidstack_));
} else if(fs.isFluidEqual(active_fill_fluidstack_)) {
active_fill_fluidstack_.grow(fs.getAmount());
Auxiliaries.playerChatMessage(player, "Fluid insertion flowrate increased: " + dump_fluid_stack(active_fill_fluidstack_));
} else {
int amount = active_fill_fluidstack_.getAmount();
active_fill_fluidstack_ = fs.copy();
active_fill_fluidstack_.setAmount(amount);
Auxiliaries.playerChatMessage(player, "Fluid insertion fluid changed: " + dump_fluid_stack(active_fill_fluidstack_));
}
} else {
if(!active_fill_fluidstack_.isEmpty()) {
active_fill_fluidstack_.shrink(1000);
if(active_fill_fluidstack_.isEmpty()) active_fill_fluidstack_ = FluidStack.EMPTY;
Auxiliaries.playerChatMessage(player, "Fluid insertion flowrate decreased: " + dump_fluid_stack(active_fill_fluidstack_));
} else {
Auxiliaries.playerChatMessage(player, "Fluid insertion disabled.");
}
}
passive_drain_fluidstack_ = active_fill_fluidstack_.copy(); // currently no difference
return;
}
}
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{
super.func_230337_a_(state, nbt);
if(nbt.contains("passive_tank")) passive_tank_ = FluidStack.loadFluidStackFromNBT(nbt.getCompound("passive_tank"));
if(nbt.contains("passive_drain")) passive_drain_fluidstack_ = FluidStack.loadFluidStackFromNBT(nbt.getCompound("passive_drain"));
if(nbt.contains("active")) active_fill_fluidstack_ = FluidStack.loadFluidStackFromNBT(nbt.getCompound("active"));
}
@Override
public CompoundNBT write(CompoundNBT nbt)
{
super.write(nbt);
if(!passive_tank_.isEmpty()) nbt.put("passive_tank", passive_tank_.writeToNBT(new CompoundNBT()));
if(!passive_drain_fluidstack_.isEmpty()) nbt.put("passive_drain", passive_drain_fluidstack_.writeToNBT(new CompoundNBT()));
if(!active_fill_fluidstack_.isEmpty()) nbt.put("active", active_fill_fluidstack_.writeToNBT(new CompoundNBT()));
return nbt;
}
@Override
public void remove()
{
super.remove();
fluid_handler_.invalidate();
}
// ICapabilityProvider --------------------------------------------------------------------
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new MainFluidHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
if(facing != block_facing()) return fluid_handler_.cast();
return LazyOptional.empty();
}
return super.getCapability(capability, facing);
}
// IFluidHandler ---------------------------------------------------------------------------
private static class MainFluidHandler implements IFluidHandler
{
private TestTileEntity te;
public MainFluidHandler(TestTileEntity te)
{ this.te = te; }
@Override public int getTanks()
{ return 1; }
@Override public FluidStack getFluidInTank(int tank)
{ return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank)
{ return te.passive_tank_capacity_; }
@Override public FluidStack drain(FluidStack resource, FluidAction action)
{
++te.passive_num_fh_interactions_;
if(resource.isEmpty()) return FluidStack.EMPTY;
if(!resource.isFluidEqual(te.passive_drain_fluidstack_)) return FluidStack.EMPTY;
FluidStack st = resource.copy();
st.setAmount(MathHelper.clamp(st.getAmount(), 0, te.passive_drain_max_flowrate_));
if(st.isEmpty()) return FluidStack.EMPTY;
if(action==FluidAction.EXECUTE) te.passive_num_drained_specific_mb_ += st.getAmount();
return st;
}
@Override public FluidStack drain(int maxDrain, FluidAction action)
{
++te.passive_num_fh_interactions_;
maxDrain = MathHelper.clamp(maxDrain, 0, te.passive_drain_max_flowrate_);
if((te.passive_drain_fluidstack_.isEmpty()) || (maxDrain<=0)) return FluidStack.EMPTY;
if(action==FluidAction.EXECUTE) te.passive_num_drained_general_mb_ += maxDrain;
FluidStack st = te.passive_drain_fluidstack_.copy();
st.setAmount(maxDrain);
return st;
}
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack)
{ return true; }
@Override public int fill(FluidStack resource, FluidAction action)
{
++te.passive_num_fh_interactions_;
int amount = MathHelper.clamp(resource.getAmount(), 0, te.passive_fill_max_flowrate_);
if(action == FluidAction.EXECUTE) {
te.passive_num_filled_specific_mb_ += amount;
if(te.passive_tank_.isFluidEqual(resource)) {
int level = (int)MathHelper.clamp((long)te.passive_tank_.getAmount() + (long)amount, (long)0, (long)Integer.MAX_VALUE);
te.passive_tank_.setAmount(level);
}
}
return amount;
}
}
// ITickableTileEntity ----------------------------------------------------------------------
private void fluid_insertion()
{
if(active_fill_fluidstack_.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos.offset(block_facing()));
if(te == null) return;
final IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing().getOpposite()).orElse(null);
if(fh == null) return;
int filled = fh.fill(active_fill_fluidstack_.copy(), FluidAction.EXECUTE);
active_num_filled_ += filled;
}
@Override
public void tick()
{
if(world.isRemote) return;
if(--tick_timer_ > 0) return;
tick_interval_ = MathHelper.clamp(tick_interval_ ,1 , 200);
tick_timer_ = tick_interval_;
fluid_insertion();
}
}
}

View file

@ -0,0 +1,261 @@
/*
* @file EdTreeCutter.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small Tree Cutter
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.TreeCutting;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import wile.engineersdecor.libmc.detail.Overlay;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable;
import java.util.Random;
public class EdTreeCutter
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class TreeCutterBlock extends DecorBlock.Horizontal implements IDecorBlock
{
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public TreeCutterBlock(long config, Block.Properties builder, final AxisAlignedBB[] unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(ACTIVE); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(ACTIVE, false); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new TreeCutterTileEntity(); }
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.get(ACTIVE))) return;
final double rv = rnd.nextDouble();
if(rv > 0.8) return;
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.get(HORIZONTAL_FACING)) {
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;
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
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof TreeCutterTileEntity) ((TreeCutterTileEntity)te).state_message(player);
return ActionResultType.SUCCESS;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class TreeCutterTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage
{
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;
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;
private static int cutting_time_needed = 20 * DEFAULT_CUTTING_TIME_NEEDED;
private static boolean requires_power = false;
private int tick_timer_;
private int active_timer_;
private int proc_time_elapsed_;
private int energy_;
public static void on_config(int boost_energy_per_tick, int cutting_time_seconds, boolean power_required)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 4, 4096);
energy_max = Math.max(boost_energy_consumption * 10, 10000);
cutting_time_needed = 20 * MathHelper.clamp(cutting_time_seconds, 10, 240);
requires_power = power_required;
ModEngineersDecor.logger().info("Config tree cutter: Boost energy consumption:" + boost_energy_consumption + "rf/t" + (requires_power?" (power required for operation) ":"") + ", cutting time " + cutting_time_needed + "t." );
}
public TreeCutterTileEntity()
{ super(ModContent.TET_SMALL_TREE_CUTTER); }
public TreeCutterTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt)
{ energy_ = nbt.getInt("energy"); }
private void writenbt(CompoundNBT nbt)
{ nbt.putInt("energy", energy_); }
public void state_message(PlayerEntity player)
{
String progress = "0";
if((active_timer_ > 0) && (cutting_time_needed > 0) && (active_timer_ > 0)) {
progress = Integer.toString((int)MathHelper.clamp((((double)proc_time_elapsed_) / ((double)cutting_time_needed) * 100), 0, 100));
}
String soc = Integer.toString(MathHelper.clamp((energy_*100/energy_max),0,100));
Overlay.show(player, Auxiliaries.localizable("block.engineersdecor.small_tree_cutter.status", null, new Object[]{soc, energy_max, progress, (cutting_time_needed/20) }));
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
}
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return boost_energy_consumption*2; }
@Override
public int getEnergyStored()
{ return energy_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
maxReceive = MathHelper.clamp(maxReceive, 0, Math.max((energy_max) - energy_, 0));
if(!simulate) energy_ += maxReceive;
return maxReceive;
}
// Capability export ----------------------------------------------------------------------------
@Override
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);
}
// ITickable ------------------------------------------------------------------------------------
@Override
public void tick()
{
if(--tick_timer_ > 0) return;
final BlockState device_state = world.getBlockState(pos);
if(!(device_state.getBlock() instanceof TreeCutterBlock)) { tick_timer_ = TICK_INTERVAL; return; }
if(world.isRemote) {
if(!device_state.get(TreeCutterBlock.ACTIVE)) {
tick_timer_ = TICK_INTERVAL;
} else {
tick_timer_ = 1;
world.playSound(pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_WOOD_HIT, SoundCategory.BLOCKS, 0.1f, 1.0f, false);
}
} else {
tick_timer_ = TICK_INTERVAL;
final BlockPos tree_pos = pos.offset(device_state.get(TreeCutterBlock.HORIZONTAL_FACING));
final BlockState tree_state = world.getBlockState(tree_pos);
if(!TreeCutting.canChop(tree_state) || (world.isBlockPowered(pos))) {
if(device_state.get(TreeCutterBlock.ACTIVE)) world.setBlockState(pos, device_state.with(TreeCutterBlock.ACTIVE, false), 1|2);
proc_time_elapsed_ = 0;
active_timer_ = 0;
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
proc_time_elapsed_ += TICK_INTERVAL;
if(energy_ >= boost_energy_consumption) {
energy_ -= boost_energy_consumption;
proc_time_elapsed_ += TICK_INTERVAL*BOOST_FACTOR;
active_timer_ = 2;
} else if(!requires_power) {
active_timer_ = 1024;
} else if(active_timer_ > 0) {
--active_timer_;
}
boolean active = (active_timer_ > 0);
if(requires_power && !active) {
proc_time_elapsed_ = Math.max(0, proc_time_elapsed_ - 2*TICK_INTERVAL);
}
if(proc_time_elapsed_ >= cutting_time_needed) {
proc_time_elapsed_ = 0;
TreeCutting.chopTree(world, tree_state, tree_pos, 512, false);
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_WOOD_BREAK, SoundCategory.BLOCKS, 1.0f, 1.0f);
active = false;
}
if(device_state.get(TreeCutterBlock.ACTIVE) != active) {
world.setBlockState(pos, device_state.with(TreeCutterBlock.ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -0,0 +1,18 @@
/*
* @file EdWallBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Wall blocks.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.libmc.blocks.VariantWallBlock;
import net.minecraft.block.*;
public class EdWallBlock extends VariantWallBlock implements IDecorBlock
{
public EdWallBlock(long config, Block.Properties builder)
{ super(config, builder); }
}

View file

@ -0,0 +1,726 @@
/*
* @file EdWasteIncinerator.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Trash/void/nullifier device with internal fifos.
*/
package wile.engineersdecor.blocks;
import com.mojang.blaze3d.matrix.MatrixStack;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.blocks.EdFurnace.FurnaceBlock;
import wile.engineersdecor.libmc.detail.Inventories;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.world.World;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.SoundEvents;
import net.minecraft.item.*;
import net.minecraft.inventory.*;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.*;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class EdWasteIncinerator
{
//--------------------------------------------------------------------------------------------------------------------
// Block
//--------------------------------------------------------------------------------------------------------------------
public static class WasteIncineratorBlock extends DecorBlock.Normal implements IDecorBlock
{
public static final BooleanProperty LIT = FurnaceBlock.LIT;
public WasteIncineratorBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(LIT); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(LIT, false); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState blockState, World world, BlockPos pos)
{ return Container.calcRedstone(world.getTileEntity(pos)); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new EdWasteIncinerator.WasteIncineratorTileEntity(); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof EdWasteIncinerator.WasteIncineratorTileEntity)) return;
((EdWasteIncinerator.WasteIncineratorTileEntity)te).readnbt(te_nbt);
((EdWasteIncinerator.WasteIncineratorTileEntity)te).markDirty();
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, final TileEntity te, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
if(!(te instanceof WasteIncineratorTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = ((WasteIncineratorTileEntity) te).reset_getnbt();
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
for(ItemStack stack: ((WasteIncineratorTileEntity)te).stacks_) stacks.add(stack);
((WasteIncineratorTileEntity)te).reset_getnbt();
}
return stacks;
}
@Override
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return ActionResultType.SUCCESS;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof WasteIncineratorTileEntity)) return ActionResultType.FAIL;
if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return ActionResultType.FAIL;
NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te);
return ActionResultType.SUCCESS;
}
@Override
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.get(LIT))) return;
final double rv = rnd.nextDouble();
if(rv > 0.5) return;
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xr=rnd.nextDouble()*0.4-0.2, yr=rnd.nextDouble()*0.5, zr=rnd.nextDouble()*0.4-0.2;
world.addParticle(ParticleTypes.SMOKE, x+xr, y+yr, z+zr, 0.0, 0.0, 0.0);
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class WasteIncineratorTileEntity extends TileEntity implements ITickableTileEntity, INameable, IInventory, INamedContainerProvider, ISidedInventory, IEnergyStorage
{
public static final int NUM_OF_FIELDS = 1;
public static final int TICK_INTERVAL = 20;
public static final int ENERGIZED_TICK_INTERVAL = 5;
public static final int INCINERATION_STACK_DECREMENT = 4;
public static final int MAX_ENERGY_BUFFER = 16000;
public static final int MAX_ENERGY_TRANSFER = 256;
public static final int DEFAULT_ENERGY_CONSUMPTION = 16;
public static final int NUM_OF_SLOTS = 16;
public static final int INPUT_SLOT_NO = 0;
public static final int BURN_SLOT_NO = NUM_OF_SLOTS-1;
// Config ----------------------------------------------------------------------------------
private static int energy_consumption = DEFAULT_ENERGY_CONSUMPTION;
public static void on_config(int speed_percent, int fuel_efficiency_percent, int boost_energy_per_tick)
{
energy_consumption = MathHelper.clamp(boost_energy_per_tick, 4, 4096);
ModEngineersDecor.logger().info("Config waste incinerator boost energy consumption:" + energy_consumption);
}
// WasteIncineratorTileEntity -----------------------------------------------------------------------------
private int tick_timer_;
private int check_timer_;
private int energy_stored_;
protected NonNullList<ItemStack> stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
public WasteIncineratorTileEntity()
{ this(ModContent.TET_WASTE_INCINERATOR); }
public WasteIncineratorTileEntity(TileEntityType<?> te_type)
{ super(te_type); reset(); }
public CompoundNBT reset_getnbt()
{
CompoundNBT nbt = new CompoundNBT();
writenbt(nbt);
reset();
return nbt;
}
protected void reset()
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
check_timer_ = 0;
tick_timer_ = 0;
}
public void readnbt(CompoundNBT compound)
{
NonNullList<ItemStack> stacks = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
ItemStackHelper.loadAllItems(compound, stacks);
while(stacks.size() < NUM_OF_SLOTS) stacks.add(ItemStack.EMPTY);
stacks_ = stacks;
energy_stored_ = compound.getInt("Energy");
}
protected void writenbt(CompoundNBT compound)
{
compound.putInt("Energy", MathHelper.clamp(energy_stored_,0 , MAX_ENERGY_BUFFER));
ItemStackHelper.saveAllItems(compound, stacks_);
}
// TileEntity ------------------------------------------------------------------------------
@Override
public void func_230337_a_(BlockState state, CompoundNBT nbt)
{ super.func_230337_a_(state, nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
@Override
public void remove()
{
super.remove();
energy_handler_.invalidate();
item_handler_.invalidate();
}
// INameable ---------------------------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Small Waste Incinerator"); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getCustomName()
{ return getName(); }
// IContainerProvider ----------------------------------------------------------------------
@Override
public ITextComponent getDisplayName()
{ return INameable.super.getDisplayName(); }
@Override
public Container createMenu(int id, PlayerInventory inventory, PlayerEntity player )
{ return new EdWasteIncinerator.WasteIncineratorContainer(id, inventory, this, IWorldPosCallable.of(world, pos), fields); }
// IInventory ------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return ((index >= 0) && (index < getSizeInventory())) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{
if(stack.getCount() > getInventoryStackLimit()) stack.setCount(getInventoryStackLimit());
stacks_.set(index, stack);
markDirty();
}
@Override
public int getInventoryStackLimit()
{ return 64; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(PlayerEntity player)
{ return getPos().distanceSq(player.func_233580_cy_()) < 36; }
@Override
public void openInventory(PlayerEntity player)
{}
@Override
public void closeInventory(PlayerEntity player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return (index==0); }
@Override
public void clear()
{ stacks_.clear(); }
// Fields -----------------------------------------------------------------------------------------------
protected final IIntArray fields = new IntArray(WasteIncineratorTileEntity.NUM_OF_FIELDS)
{
@Override
public int get(int id)
{
switch(id) {
default: return 0;
}
}
@Override
public void set(int id, int value)
{
switch(id) {
default: break;
}
}
};
// ISidedInventory ----------------------------------------------------------------------------
private static final int[] SIDED_INV_SLOTS = new int[] { INPUT_SLOT_NO };
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack itemStackIn, Direction direction)
{ return isItemValidForSlot(index, itemStackIn); }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return false; }
// IEnergyStorage ----------------------------------------------------------------------------
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return MAX_ENERGY_BUFFER; }
@Override
public int getEnergyStored()
{ return energy_stored_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
if(energy_stored_ >= MAX_ENERGY_BUFFER) return 0;
int n = Math.min(maxReceive, (MAX_ENERGY_BUFFER - energy_stored_));
if(n > MAX_ENERGY_TRANSFER) n = MAX_ENERGY_TRANSFER;
if(!simulate) {energy_stored_ += n; markDirty(); }
return n;
}
// IItemHandler --------------------------------------------------------------------------------
protected static class BItemHandler implements IItemHandler
{
private WasteIncineratorTileEntity te;
BItemHandler(WasteIncineratorTileEntity te)
{ this.te = te; }
@Override
public int getSlots()
{ return 1; }
@Override
public int getSlotLimit(int index)
{ return te.getInventoryStackLimit(); }
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack)
{ return true; }
@Override
@Nonnull
public ItemStack insertItem(int index, @Nonnull ItemStack stack, boolean simulate)
{
if(stack.isEmpty()) return ItemStack.EMPTY;
if(index != 0) return ItemStack.EMPTY;
int slotno = 0;
ItemStack slotstack = getStackInSlot(slotno);
if(!slotstack.isEmpty())
{
if(slotstack.getCount() >= Math.min(slotstack.getMaxStackSize(), getSlotLimit(index))) return stack;
if(!ItemHandlerHelper.canItemStacksStack(stack, slotstack)) return stack;
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(index)) - slotstack.getCount();
if(stack.getCount() <= n) {
if(!simulate) {
ItemStack copy = stack.copy();
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
}
return ItemStack.EMPTY;
} else {
stack = stack.copy();
if(!simulate) {
ItemStack copy = stack.split(n);
copy.grow(slotstack.getCount());
te.setInventorySlotContents(slotno, copy);
return stack;
} else {
stack.shrink(n);
return stack;
}
}
} else {
if(!te.canInsertItem(slotno, stack, Direction.UP) || (!te.isItemValidForSlot(slotno, stack))) return stack;
int n = Math.min(stack.getMaxStackSize(), getSlotLimit(index));
if(n < stack.getCount()) {
stack = stack.copy();
if(!simulate) {
te.setInventorySlotContents(slotno, stack.split(n));
return stack;
} else {
stack.shrink(n);
return stack;
}
} else {
if(!simulate) te.setInventorySlotContents(slotno, stack);
return ItemStack.EMPTY;
}
}
}
@Override
@Nonnull
public ItemStack extractItem(int index, int amount, boolean simulate)
{ return ItemStack.EMPTY; }
@Override
@Nonnull
public ItemStack getStackInSlot(int index)
{ return te.getStackInSlot(index); }
}
// Capability export ----------------------------------------------------------------------------
protected LazyOptional<IItemHandler> item_handler_ = LazyOptional.of(() -> new WasteIncineratorTileEntity.BItemHandler(this));
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handler_.cast();
if(capability == CapabilityEnergy.ENERGY) return energy_handler_.cast();
return super.getCapability(capability, facing);
}
// ITickableTileEntity ---------------------------------------------------------------------------
@Override
public void tick()
{
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
if(world.isRemote) return;
boolean dirty = false;
ItemStack processing_stack = stacks_.get(BURN_SLOT_NO);
final boolean was_processing = !processing_stack.isEmpty();
boolean is_processing = was_processing;
boolean new_stack_processing = false;
if((!stacks_.get(0).isEmpty()) && transferItems(0, 1, getInventoryStackLimit())) dirty = true;
ItemStack first_stack = stacks_.get(0);
boolean shift = !first_stack.isEmpty();
if(is_processing) {
processing_stack.shrink(INCINERATION_STACK_DECREMENT);
if(processing_stack.getCount() <= 0) {
processing_stack = ItemStack.EMPTY;
is_processing = false;
}
stacks_.set(BURN_SLOT_NO, processing_stack);
if(energy_stored_ >= (energy_consumption * TICK_INTERVAL)) {
energy_stored_ -= (energy_consumption * TICK_INTERVAL);
tick_timer_ = ENERGIZED_TICK_INTERVAL;
}
dirty = true;
}
if(shift) {
boolean transferred = false;
for(int i=BURN_SLOT_NO-1; i>0; --i) {
transferred |= transferItems(i-1, i, getInventoryStackLimit());
}
if((!is_processing) && (!transferred)) {
shiftStacks(0, BURN_SLOT_NO);
dirty = true;
}
}
if((was_processing != is_processing) || (new_stack_processing)) {
if(new_stack_processing) world.playSound(null, pos, SoundEvents.BLOCK_LAVA_AMBIENT, SoundCategory.BLOCKS, 0.05f, 2.4f);
final BlockState state = world.getBlockState(pos);
if(state.getBlock() instanceof WasteIncineratorBlock) {
world.setBlockState(pos, state.with(WasteIncineratorBlock.LIT, is_processing), 2|16);
}
}
if(dirty) markDirty();
}
// Aux methods ----------------------------------------------------------------------------------
private ItemStack shiftStacks(final int index_from, final int index_to)
{
if(index_from >= index_to) return ItemStack.EMPTY;
ItemStack out_stack = ItemStack.EMPTY;
ItemStack stack = stacks_.get(index_from);
for(int i=index_from+1; i<=index_to; ++i) {
out_stack = stacks_.get(i);
stacks_.set(i, stack);
stack = out_stack;
}
stacks_.set(index_from, ItemStack.EMPTY);
return out_stack;
}
private boolean transferItems(final int index_from, final int index_to, int count)
{
ItemStack from = stacks_.get(index_from);
if(from.isEmpty()) return false;
ItemStack to = stacks_.get(index_to);
if(from.getCount() < count) count = from.getCount();
if(count <= 0) return false;
boolean changed = true;
if(to.isEmpty()) {
stacks_.set(index_to, from.split(count));
} else if(to.getCount() >= to.getMaxStackSize()) {
changed = false;
} else if(Inventories.areItemStacksDifferent(from, to)) {
changed = false;
} else {
if((to.getCount()+count) >= to.getMaxStackSize()) {
from.shrink(to.getMaxStackSize()-to.getCount());
to.setCount(to.getMaxStackSize());
} else {
from.shrink(count);
to.grow(count);
}
}
if(from.isEmpty() && from!=ItemStack.EMPTY) {
stacks_.set(index_from, ItemStack.EMPTY);
changed = true;
}
return changed;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Container
//--------------------------------------------------------------------------------------------------------------------
public static class WasteIncineratorContainer extends Container
{
private static final int PLAYER_INV_START_SLOTNO = WasteIncineratorTileEntity.NUM_OF_SLOTS;
protected final PlayerEntity player_;
protected final IInventory inventory_;
protected final IWorldPosCallable wpc_;
private final IIntArray fields_;
private int proc_time_needed_;
public int field(int index) { return fields_.get(index); }
public PlayerEntity player() { return player_ ; }
public IInventory inventory() { return inventory_ ; }
public World world() { return player_.world; }
public WasteIncineratorContainer(int cid, PlayerInventory player_inventory)
{ this(cid, player_inventory, new Inventory(WasteIncineratorTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY, new IntArray(WasteIncineratorTileEntity.NUM_OF_FIELDS)); }
private WasteIncineratorContainer(int cid, PlayerInventory player_inventory, IInventory block_inventory, IWorldPosCallable wpc, IIntArray fields)
{
super(ModContent.CT_WASTE_INCINERATOR, cid);
player_ = player_inventory.player;
inventory_ = block_inventory;
wpc_ = wpc;
fields_ = fields;
int i=-1;
addSlot(new Slot(inventory_, ++i, 13, 9));
addSlot(new Slot(inventory_, ++i, 37, 12));
addSlot(new Slot(inventory_, ++i, 54, 13));
addSlot(new Slot(inventory_, ++i, 71, 14));
addSlot(new Slot(inventory_, ++i, 88, 15));
addSlot(new Slot(inventory_, ++i, 105, 16));
addSlot(new Slot(inventory_, ++i, 122, 17));
addSlot(new Slot(inventory_, ++i, 139, 18));
addSlot(new Slot(inventory_, ++i, 144, 38));
addSlot(new Slot(inventory_, ++i, 127, 39));
addSlot(new Slot(inventory_, ++i, 110, 40));
addSlot(new Slot(inventory_, ++i, 93, 41));
addSlot(new Slot(inventory_, ++i, 76, 42));
addSlot(new Slot(inventory_, ++i, 59, 43));
addSlot(new Slot(inventory_, ++i, 42, 44));
addSlot(new Slot(inventory_, ++i, 17, 58));
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x, 8+x*18, 144)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35
}
}
}
@Override
public boolean canInteractWith(PlayerEntity player)
{ return inventory_.isUsableByPlayer(player); }
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int index)
{
Slot slot = getSlot(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index>=0) && (index<PLAYER_INV_START_SLOTNO)) {
// Device slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, true)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player slot
if(!mergeItemStack(slot_stack, 0, PLAYER_INV_START_SLOTNO-1, true)) return ItemStack.EMPTY;
} else {
// invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class WasteIncineratorGui extends ContainerScreen<WasteIncineratorContainer>
{
protected final PlayerEntity player_;
public WasteIncineratorGui(WasteIncineratorContainer container, PlayerInventory player_inventory, ITextComponent title)
{ super(container, player_inventory, title); this.player_ = player_inventory.player; }
@Override
public void func_231160_c_/*init*/()
{ super.func_231160_c_(); }
@Override
public void func_230430_a_/*render*/(MatrixStack mx, int mouseX, int mouseY, float partialTicks)
{
func_230446_a_/*renderBackground*/(mx);
super.func_230430_a_(mx, mouseX, mouseY, partialTicks);
func_230459_a_/*renderHoveredToolTip*/(mx, mouseX, mouseY);
}
@Override
protected void func_230451_b_(MatrixStack mx, int x, int y)
{}
@Override
@SuppressWarnings("deprecation")
protected void func_230450_a_/*drawGuiContainerBackgroundLayer*/(MatrixStack mx, float partialTicks, int mouseX, int mouseY)
{
RenderSystem.enableBlend();
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
getMinecraft().getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/small_waste_incinerator_gui.png"));
final int x0=guiLeft, y0=this.guiTop, w=xSize, h=ySize;
func_238474_b_(mx, x0, y0, 0, 0, w, h);
RenderSystem.disableBlend();
}
}
}

View file

@ -0,0 +1,59 @@
/*
* @file EdWindowBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Mod windows.
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import javax.annotation.Nullable;
public class EdWindowBlock extends DecorBlock.DirectedWaterLoggable implements IDecorBlock
{
public EdWindowBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.TRANSLUCENT; }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
Direction facing = context.getPlacementHorizontalFacing();
if(Math.abs(context.getPlayer().getLookVec().y) > 0.9) {
facing = context.getNearestLookingDirection();
} else {
for(Direction f: Direction.values()) {
BlockState st = context.getWorld().getBlockState(context.getPos().offset(f));
if(st.getBlock() == this) {
facing = st.get(FACING);
break;
}
}
}
return super.getStateForPlacement(context).with(FACING, facing);
}
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public boolean isTransparent(BlockState state)
{ return true; }
}

View file

@ -0,0 +1,15 @@
/*
* @file IDecorBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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

@ -0,0 +1,25 @@
/*
* @file ExtItems.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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("immersiveengineering:external_heater")
public static final Item IE_EXTERNAL_HEATER = null;
@ObjectHolder("bottledmilk:milk_bottle_drinkable")
public static final Item BOTTLED_MILK_BOTTLE_DRINKLABLE = null;
public static final void onPostInit()
{}
}

View file

@ -0,0 +1,168 @@
/*
* @file ModTesrs.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Yet unstructured initial experiments with TESRs.
* May be structured after I know what I am doing there.
*/
package wile.engineersdecor.detail;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.blocks.EdCraftingTable;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.*;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import com.mojang.blaze3d.matrix.MatrixStack;
import wile.engineersdecor.blocks.EdCraftingTable.CraftingTableBlock;
import wile.engineersdecor.blocks.EdLabeledCrate;
public class ModRenderers
{
//--------------------------------------------------------------------------------------------------------------------
// InvisibleEntityRenderer
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class InvisibleEntityRenderer<T extends Entity> extends EntityRenderer<T>
{
private final Minecraft mc = Minecraft.getInstance();
public InvisibleEntityRenderer(EntityRendererManager renderManagerIn)
{ super(renderManagerIn); }
public void func_225623_a_/*render*/(T p_225623_1_, float p_225623_2_, float p_225623_3_, MatrixStack p_225623_4_, IRenderTypeBuffer p_225623_5_, int p_225623_6_)
{}
public Vector3d func_225627_b_/*likely getOffset*/(T p_225627_1_, float p_225627_2_)
{ return Vector3d.ZERO; }
@SuppressWarnings("deprecation")
public ResourceLocation getEntityTexture(T entity)
{ return AtlasTexture.LOCATION_BLOCKS_TEXTURE; }
protected boolean canRenderName(T entity)
{ return false; }
protected void func_225629_a_/*renderName/renderLabel*/(T p_225629_1_, String p_225629_2_, MatrixStack p_225629_3_, IRenderTypeBuffer p_225629_4_, int p_225629_5_)
{}
}
//--------------------------------------------------------------------------------------------------------------------
// Crafting table
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class CraftingTableTer extends TileEntityRenderer<EdCraftingTable.CraftingTableTileEntity>
{
private static int tesr_error_counter = 4;
private static float scaler = 0.1f;
private static float gap = 0.19f;
private static float yrotations[] = {0, 90, 180, 270}; // [hdirection] S-W-N-E
private static float offsets[][][] = { // [hdirection][slotindex][xz]
{ {-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} }, // 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); }
@Override
@SuppressWarnings("deprecation")
public void render(final EdCraftingTable.CraftingTableTileEntity te, float unused1, MatrixStack mxs, IRenderTypeBuffer buf, int i5, int i6)
{
if(tesr_error_counter <= 0) return;
try {
final int di = MathHelper.clamp(te.getWorld().getBlockState(te.getPos()).get(CraftingTableBlock.HORIZONTAL_FACING).getHorizontalIndex(), 0, 3);
long posrnd = te.getPos().toLong();
posrnd = (posrnd>>16)^(posrnd<<1);
for(int i=0; i<9; ++i) {
final ItemStack stack = te.getStackInSlot(i);
if(stack.isEmpty()) continue;
float prnd = ((float)(((Integer.rotateRight(stack.getItem().hashCode()^(int)posrnd,(stack.getCount()+i)&31)))&1023))/1024f;
float rndo = gap * ((prnd*0.1f)-0.05f);
float ox = gap * offsets[di][i][0], oz = gap * offsets[di][i][1];
float oy = 0.5f;
float ry = ((yrotations[di]+180) + ((prnd*60)-30)) % 360;
if(stack.isEmpty()) return;
mxs.push();
mxs.translate(0.5+ox, 0.5+oy, 0.5+oz);
mxs.rotate(Vector3f.XP.rotationDegrees(90.0f));
mxs.rotate(Vector3f.ZP.rotationDegrees(ry));
mxs.translate(rndo, rndo, 0);
mxs.scale(scaler, scaler, scaler);
Minecraft.getInstance().getItemRenderer().renderItem(stack, net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.FIXED, i5, i6, mxs, buf);
mxs.pop();
}
} 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)
public static class DecorLabeledCrateTer extends TileEntityRenderer<EdLabeledCrate.LabeledCrateTileEntity>
{
private static int tesr_error_counter = 4;
private static float scaler = 0.35f;
double tr[][]= { // [hdirection=S-W-N-E][param]
{ +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
{ +15.5/32, -8.0/32, -8.0/32, 270.0 }, // W
};
public DecorLabeledCrateTer(TileEntityRendererDispatcher dispatcher)
{ super(dispatcher); }
@Override
@SuppressWarnings("deprecation")
public void render(final EdLabeledCrate.LabeledCrateTileEntity te, float unused1, MatrixStack mxs, IRenderTypeBuffer buf, int i5, int i6)
{
if(tesr_error_counter<=0) return;
try {
final ItemStack stack = te.getItemFrameStack();
if(stack.isEmpty()) return;
final int di = MathHelper.clamp(te.getWorld().getBlockState(te.getPos()).get(EdLabeledCrate.LabeledCrateBlock.HORIZONTAL_FACING).getHorizontalIndex(), 0, 3);
double ox = tr[di][0], oy = tr[di][1], oz = tr[di][2];
float ry = (float)tr[di][3];
mxs.push();
//GlStateManager.disableLighting();
//RenderHelper.enableStandardItemLighting();
mxs.translate(0.5+ox, 0.5+oy, 0.5+oz);
mxs.rotate(Vector3f.YP.rotationDegrees(ry));
mxs.scale(scaler, scaler, scaler);
Minecraft.getInstance().getItemRenderer().renderItem(stack, net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.FIXED, i5, i6, mxs, buf);
//RenderHelper.disableStandardItemLighting();
//GlStateManager.enableLighting();
mxs.pop();
} catch(Throwable e) {
if(--tesr_error_counter<=0) {
ModEngineersDecor.logger().error("TER was disabled (because broken), exception was: " + e.getMessage());
}
}
}
}
}

View file

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

View file

@ -0,0 +1,76 @@
/*
* @file JEIPlugin.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* JEI plugin (see https://github.com/mezz/JustEnoughItems/wiki/Creating-Plugins)
*/
package wile.engineersdecor.eapi.jei;
//public class JEIPlugin {}
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.ModConfig;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.blocks.EdCraftingTable;
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
@mezz.jei.api.JeiPlugin
public class JEIPlugin implements mezz.jei.api.IModPlugin
{
@Override
public ResourceLocation getPluginUid()
{ return new ResourceLocation(ModEngineersDecor.MODID, "jei_plugin_uid"); }
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration)
{
if(!ModConfig.without_crafting_table) {
try {
registration.addRecipeTransferHandler(
EdCraftingTable.CraftingTableContainer.class,
VanillaRecipeCategoryUid.CRAFTING,
1, 9, 10, 44
);
} catch(Throwable e) {
ModEngineersDecor.logger().warn("Exception in JEI crafting table handler registration: '" + e.getMessage() + "'.");
}
}
}
@Override
public void onRuntimeAvailable(IJeiRuntime jeiRuntime)
{
HashSet<Item> blacklisted = new HashSet<>();
for(Block e: ModContent.getRegisteredBlocks()) {
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))) {
blacklisted.add(e);
}
}
if(!blacklisted.isEmpty()) {
List<ItemStack> blacklist = blacklisted.stream().map(ItemStack::new).collect(Collectors.toList());
try {
jeiRuntime.getIngredientManager().removeIngredientsAtRuntime(VanillaTypes.ITEM, blacklist);
} catch(Exception e) {
ModEngineersDecor.logger().warn("Exception in JEI opt-out processing: '" + e.getMessage() + "', skipping further JEI optout processing.");
}
}
}
}

View file

@ -0,0 +1,212 @@
/*
* @file BlockDecorHalfSlab.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.world.World;
import net.minecraft.world.IWorld;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.*;
import net.minecraft.block.BlockState;
import net.minecraft.state.IntegerProperty;
import net.minecraft.entity.EntityType;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.*;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.math.vector.*;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SlabSliceBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock
{
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14);
protected static final VoxelShape AABBs[] = {
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 2./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 4./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 6./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 8./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 10./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 12./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 14./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 2./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 4./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 6./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 8./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 10./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 12./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0, 14./16, 0, 1, 16./16, 1)),
VoxelShapes.create(new AxisAlignedBB(0,0,0,1,1,1)) // <- with 4bit fill
};
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 static void on_config(boolean direct_slab_pickup)
{ with_pickup = direct_slab_pickup; }
public SlabSliceBlock(long config, Block.Properties builder)
{ super(config, builder); }
protected boolean is_cube(BlockState state)
{ return state.get(PARTS) == 0x07; }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{
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
public RenderTypeHint getRenderTypeHint()
{ return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return AABBs[state.get(PARTS) & 0xf]; }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(PARTS); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
final BlockPos pos = context.getPos();
BlockState state = context.getWorld().getBlockState(pos);
if(state.getBlock() == this) {
int parts = state.get(PARTS);
if(parts == 7) return null; // -> is already a full block.
parts += (parts < 7) ? 1 : -1;
if(parts==7) state = state.with(WATERLOGGED, false);
return state.with(PARTS, parts);
} else {
final Direction face = context.getFace();
final BlockState placement_state = super.getStateForPlacement(context); // fluid state
if(face == Direction.UP) return placement_state.with(PARTS, 0);
if(face == Direction.DOWN) return placement_state.with(PARTS, 14);
if(!face.getAxis().isHorizontal()) return placement_state;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return placement_state.with(PARTS, isupper ? 14 : 0);
}
}
@Override
@SuppressWarnings("deprecation")
public boolean isReplaceable(BlockState state, BlockItemUseContext context)
{
if(context.getItem().getItem() != this.asItem()) return false;
if(!context.replacingClickedOnBlock()) return true;
final Direction face = context.getFace();
final int parts = state.get(PARTS);
if(parts == 7) return false;
if((face == Direction.UP) && (parts < 7)) return true;
if((face == Direction.DOWN) && (parts > 7)) return true;
if(!face.getAxis().isHorizontal()) return false;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return isupper ? (parts==0) : (parts==1);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, Rotation rot)
{ return state; }
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state; }
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, TileEntity te, boolean explosion)
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.get(PARTS) & 0xf]))); }
@Override
@SuppressWarnings("deprecation")
public void onBlockClicked(BlockState state, World world, BlockPos pos, PlayerEntity player)
{
if((world.isRemote) || (!with_pickup)) return;
final ItemStack stack = player.getHeldItemMainhand();
if(stack.isEmpty() || (Block.getBlockFromItem(stack.getItem()) != this)) return;
if(stack.getCount() >= stack.getMaxStackSize()) return;
Vector3d lv = player.getLookVec();
Direction facing = Direction.getFacingFromVector((float)lv.x, (float)lv.y, (float)lv.z);
if((facing != Direction.UP) && (facing != Direction.DOWN)) return;
if(state.getBlock() != this) return;
int parts = state.get(PARTS);
if((facing == Direction.DOWN) && (parts <= 7)) {
if(parts > 0) {
world.setBlockState(pos, state.with(PARTS, parts-1), 3);
} else {
world.removeBlock(pos, false);
}
} else if((facing == Direction.UP) && (parts >= 7)) {
if(parts < 14) {
world.setBlockState(pos, state.with(PARTS, parts + 1), 3);
} else {
world.removeBlock(pos, false);
}
} else {
return;
}
if(!player.isCreative()) {
stack.grow(1);
if(player.inventory != null) player.inventory.markDirty(); // @todo: check if inventory can actually be null
}
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());
}
@Override
public boolean receiveFluid(IWorld world, BlockPos pos, BlockState state, FluidState fluidState)
{ return (state.get(PARTS)==14) ? false : super.receiveFluid(world, pos, state, fluidState); }
@Override
public boolean canContainFluid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.get(PARTS)==14) ? false : super.canContainFluid(world, pos, state, fluid); }
}

View file

@ -0,0 +1,421 @@
/*
* @file StandardBlocks.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Common functionality class for decor blocks.
* Mainly needed for:
* - MC block defaults.
* - Tooltip functionality
* - Model initialisation
*/
package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.block.*;
import net.minecraft.entity.EntityType;
import net.minecraft.state.DirectionProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.loot.LootParameters;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.block.material.PushReaction;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.IBlockReader;
import net.minecraft.loot.LootContext;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.util.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Supplier;
public class StandardBlocks
{
public static final long CFG_DEFAULT = 0x0000000000000000L; // no special config
public static final long CFG_CUTOUT = 0x0000000000000001L; // cutout rendering
public static final long CFG_MIPPED = 0x0000000000000002L; // cutout mipped rendering
public static final long CFG_TRANSLUCENT = 0x0000000000000004L; // indicates a block/pane is glass like (transparent, etc)
public static final long CFG_WATERLOGGABLE = 0x0000000000000008L; // The derived block extends IWaterLoggable
public static final long CFG_HORIZIONTAL = 0x0000000000000010L; // horizontal block, affects bounding box calculation at construction time and placement
public static final long CFG_LOOK_PLACEMENT = 0x0000000000000020L; // placed in direction the player is looking when placing.
public static final long CFG_FACING_PLACEMENT = 0x0000000000000040L; // placed on the facing the player has clicked.
public static final long CFG_OPPOSITE_PLACEMENT = 0x0000000000000080L; // placed placed in the opposite direction of the face the player clicked.
public static final long CFG_FLIP_PLACEMENT_IF_SAME = 0x0000000000000100L; // placement direction flipped if an instance of the same class was clicked
public static final long CFG_FLIP_PLACEMENT_SHIFTCLICK = 0x0000000000000200L; // placement direction flipped if player is sneaking
public static final long CFG_STRICT_CONNECTIONS = 0x0000000000000400L; // blocks do not connect to similar blocks around (implementation details may vary a bit)
public interface IStandardBlock
{
default long config()
{ return 0; }
default boolean hasDynamicDropList()
{ return false; }
default List<ItemStack> dropList(BlockState state, World world, BlockPos pos, @Nullable TileEntity te, boolean explosion)
{ return Collections.singletonList((!world.isRemote()) ? (new ItemStack(state.getBlock().asItem())) : (ItemStack.EMPTY)); }
enum RenderTypeHint { SOLID,CUTOUT,CUTOUT_MIPPED,TRANSLUCENT,TRANSLUCENT_NO_CRUMBLING }
default RenderTypeHint getRenderTypeHint()
{ return RenderTypeHint.SOLID; }
}
public interface IBlockItemFactory
{
// BlockItem factory for item registry. Only invoked once.
BlockItem getBlockItem(Block block, Item.Properties builder);
}
public static class BaseBlock extends Block implements IStandardBlock
{
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public final long config;
public final VoxelShape vshape;
public BaseBlock(long conf, Block.Properties properties)
{ this(conf, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16,16 )); }
public BaseBlock(long conf, Block.Properties properties, AxisAlignedBB aabb)
{ super(properties); config = conf; vshape = VoxelShapes.create(aabb); }
public BaseBlock(long conf, Block.Properties properties, VoxelShape voxel_shape)
{ super(properties); config = conf; vshape = voxel_shape; }
public BaseBlock(long conf, Block.Properties properties, AxisAlignedBB[] aabbs)
{
super(properties); config = conf;
VoxelShape shape = VoxelShapes.empty();
for(AxisAlignedBB aabb:aabbs) shape = VoxelShapes.combine(shape, VoxelShapes.create(aabb), IBooleanFunction.OR);
vshape = shape;
}
@Override
public long config()
{ return config; }
@Override
@SuppressWarnings("deprecation")
public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult hit)
{ return ActionResultType.PASS; }
@Override
@SuppressWarnings("deprecation")
public void tick(BlockState state, ServerWorld world, BlockPos pos, Random rnd)
{}
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return ((config & CFG_CUTOUT)!=0) ? RenderTypeHint.CUTOUT : RenderTypeHint.SOLID; }
@Override
@SuppressWarnings("deprecation")
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return vshape; }
@Override
@SuppressWarnings("deprecation")
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return vshape; }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
BlockState state = super.getStateForPlacement(context);
if((config & CFG_WATERLOGGABLE)!=0) {
FluidState fs = context.getWorld().getFluidState(context.getPos());
state = state.with(WATERLOGGED,fs.getFluid()==Fluids.WATER);
}
return state;
}
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
@SuppressWarnings("deprecation")
public PushReaction getPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
@Override
@SuppressWarnings("deprecation")
public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving)
{
if(state.hasTileEntity() && (state.getBlock() != newState.getBlock())) {
world.removeTileEntity(pos);
world.updateComparatorOutputLevel(pos, this);
}
}
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDrops(BlockState state, LootContext.Builder builder)
{
final BlockPos pos = builder.get(LootParameters.POSITION);
final ServerWorld world = builder.getWorld();
final Float explosion_radius = builder.get(LootParameters.EXPLOSION_RADIUS);
final TileEntity te = builder.get(LootParameters.BLOCK_ENTITY);
if((!hasDynamicDropList()) || (pos==null) || (world==null)) return super.getDrops(state, builder);
boolean is_explosion = (explosion_radius!=null) && (explosion_radius > 0);
return dropList(state, world, pos, te, is_explosion);
}
@Override
public boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos)
{
if((config & CFG_WATERLOGGABLE)!=0) {
if(state.get(WATERLOGGED)) return false;
}
return super.propagatesSkylightDown(state, reader, pos);
}
@Override
@SuppressWarnings("deprecation")
public FluidState getFluidState(BlockState state)
{
if((config & CFG_WATERLOGGABLE)!=0) {
return state.get(WATERLOGGED) ? Fluids.WATER.getStillFluidState(false) : super.getFluidState(state);
}
return super.getFluidState(state);
}
@Override
@SuppressWarnings("deprecation")
public BlockState updatePostPlacement(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{
if((config & CFG_WATERLOGGABLE)!=0) {
if(state.get(WATERLOGGED)) world.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
}
return state;
}
}
public static class WaterLoggable extends BaseBlock implements IWaterLoggable, IStandardBlock
{
public WaterLoggable(long config, Block.Properties properties)
{ super(config|CFG_WATERLOGGABLE, properties); }
public WaterLoggable(long config, Block.Properties properties, AxisAlignedBB aabb)
{ super(config|CFG_WATERLOGGABLE, properties, aabb); }
public WaterLoggable(long config, Block.Properties properties, VoxelShape voxel_shape)
{ super(config|CFG_WATERLOGGABLE, properties, voxel_shape); }
public WaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config|CFG_WATERLOGGABLE, properties, aabbs); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(WATERLOGGED); }
}
public static class Directed extends BaseBlock implements IStandardBlock
{
public static final DirectionProperty FACING = DirectionalBlock.FACING;
protected final ArrayList<VoxelShape> vshapes;
public Directed(long config, Block.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{
super(config, properties);
setDefaultState(stateContainer.getBaseState().with(FACING, Direction.UP));
vshapes = shape_supplier.get();
}
public Directed(long config, Block.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{
this(config, properties, ()->{
final boolean is_horizontal = ((config & CFG_HORIZIONTAL)!=0);
return new ArrayList<VoxelShape>(Arrays.asList(
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.DOWN, is_horizontal)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.UP, is_horizontal)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.NORTH, is_horizontal)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.SOUTH, is_horizontal)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.WEST, is_horizontal)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.EAST, is_horizontal)),
VoxelShapes.fullCube(),
VoxelShapes.fullCube()
));
});
}
public Directed(long config, Block.Properties properties, final AxisAlignedBB unrotatedAABB)
{ this(config, properties, new AxisAlignedBB[]{unrotatedAABB}); }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return vshapes.get((state.get(FACING)).getIndex() & 0x7); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(FACING); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
Direction facing = context.getFace();
if((config & (CFG_HORIZIONTAL|CFG_LOOK_PLACEMENT)) == (CFG_HORIZIONTAL|CFG_LOOK_PLACEMENT)) {
// horizontal placement in direction the player is looking
facing = context.getPlacementHorizontalFacing();
} else if((config & (CFG_HORIZIONTAL|CFG_LOOK_PLACEMENT)) == (CFG_HORIZIONTAL)) {
// horizontal placement on a face
if(((facing==Direction.UP)||(facing==Direction.DOWN))) return null;
} else if((config & CFG_LOOK_PLACEMENT)!=0) {
// placement in direction the player is looking, with up and down
facing = context.getNearestLookingDirection();
} else {
// default: placement on the face the player clicking
}
if((config & CFG_OPPOSITE_PLACEMENT)!=0) facing = facing.getOpposite();
if(((config & CFG_FLIP_PLACEMENT_SHIFTCLICK) != 0) && (context.getPlayer().isSneaking())) facing = facing.getOpposite();
return super.getStateForPlacement(context).with(FACING, facing);
}
}
public static class Horizontal extends BaseBlock implements IStandardBlock
{
public static final DirectionProperty HORIZONTAL_FACING = HorizontalBlock.HORIZONTAL_FACING;
protected final ArrayList<VoxelShape> vshapes;
public Horizontal(long config, Block.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{
super(config|CFG_HORIZIONTAL, properties);
setDefaultState(stateContainer.getBaseState().with(HORIZONTAL_FACING, Direction.NORTH));
vshapes = shape_supplier.get();
}
public Horizontal(long config, Block.Properties properties, final AxisAlignedBB[] unrotatedAABBs)
{ this(config, properties, ()->makeHorizontalShapeLookup(unrotatedAABBs)); }
public Horizontal(long config, Block.Properties properties, final AxisAlignedBB unrotatedAABB)
{ this(config, properties, new AxisAlignedBB[]{unrotatedAABB}); }
protected static ArrayList<VoxelShape> makeHorizontalShapeLookup(final AxisAlignedBB[] unrotatedAABBs)
{
return new ArrayList<VoxelShape>(Arrays.asList(
VoxelShapes.fullCube(),
VoxelShapes.fullCube(),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.NORTH, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.SOUTH, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.WEST, true)),
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.EAST, true)),
VoxelShapes.fullCube(),
VoxelShapes.fullCube()
));
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return vshapes.get((state.get(HORIZONTAL_FACING)).getIndex() & 0x7); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(HORIZONTAL_FACING); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
Direction facing = context.getFace();
if((config & CFG_LOOK_PLACEMENT) != 0) {
// horizontal placement in direction the player is looking
facing = context.getPlacementHorizontalFacing();
} else {
// horizontal placement on a face
facing = ((facing==Direction.UP)||(facing==Direction.DOWN)) ? (context.getPlacementHorizontalFacing()) : facing;
}
if((config & CFG_OPPOSITE_PLACEMENT)!=0) facing = facing.getOpposite();
if(((config & CFG_FLIP_PLACEMENT_SHIFTCLICK) != 0) && (context.getPlayer().isSneaking())) facing = facing.getOpposite();
return super.getStateForPlacement(context).with(HORIZONTAL_FACING, facing);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, Rotation rot)
{ return state.with(HORIZONTAL_FACING, rot.rotate(state.get(HORIZONTAL_FACING))); }
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state.rotate(mirrorIn.toRotation(state.get(HORIZONTAL_FACING))); }
}
public static class DirectedWaterLoggable extends Directed implements IWaterLoggable, IStandardBlock
{
public DirectedWaterLoggable(long config, Block.Properties properties, AxisAlignedBB aabb)
{ super(config|CFG_WATERLOGGABLE, properties, aabb); }
public DirectedWaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config|CFG_WATERLOGGABLE, properties, aabbs); }
public DirectedWaterLoggable(long config, Block.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config|CFG_WATERLOGGABLE, properties, shape_supplier); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(WATERLOGGED); }
}
public static class HorizontalWaterLoggable extends Horizontal implements IWaterLoggable, IStandardBlock
{
public HorizontalWaterLoggable(long config, Block.Properties properties, AxisAlignedBB aabb)
{ super(config|CFG_WATERLOGGABLE|CFG_HORIZIONTAL, properties, aabb); }
public HorizontalWaterLoggable(long config, Block.Properties properties, AxisAlignedBB[] aabbs)
{ super(config|CFG_WATERLOGGABLE|CFG_HORIZIONTAL, properties, aabbs); }
public HorizontalWaterLoggable(long config, Block.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier)
{ super(config|CFG_WATERLOGGABLE|CFG_HORIZIONTAL, properties, shape_supplier); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(WATERLOGGED); }
}
}

View file

@ -0,0 +1,193 @@
/*
* @file StandardFenceBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Wall blocks.
*/
package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.properties.BlockStateProperties;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.world.*;
import net.minecraft.fluid.FluidState;
import net.minecraft.entity.EntityType;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.state.StateContainer;
import net.minecraft.block.*;
import net.minecraft.block.material.PushReaction;
import net.minecraft.block.BlockState;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import javax.annotation.Nullable;
import java.util.*;
public class StandardFenceBlock extends WallBlock implements StandardBlocks.IStandardBlock
{
public static final BooleanProperty UP = BlockStateProperties.UP;
public static final EnumProperty<WallHeight> WALL_EAST = BlockStateProperties.field_235908_S_;
public static final EnumProperty<WallHeight> WALL_NORTH = BlockStateProperties.field_235909_T_;
public static final EnumProperty<WallHeight> WALL_SOUTH = BlockStateProperties.field_235910_U_;
public static final EnumProperty<WallHeight> WALL_WEST = BlockStateProperties.field_235911_V_;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private final Map<BlockState, VoxelShape> shape_voxels;
private final Map<BlockState, VoxelShape> collision_shape_voxels;
private final long config;
public StandardFenceBlock(long config, Block.Properties properties)
{ this(config, properties, 1.5,16, 1.5, 0, 14, 16); }
public StandardFenceBlock(long config, Block.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(properties);
shape_voxels = buildShapes(pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y);
collision_shape_voxels = buildShapes(pole_width,24, pole_width, 0, 24, 24);
this.config = config;
}
@Override
public long config()
{ return config; }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
private static VoxelShape combinedShape(VoxelShape pole, WallHeight height, VoxelShape low, VoxelShape high)
{
if(height == WallHeight.TALL) return VoxelShapes.or(pole, high);
if(height == WallHeight.LOW) return VoxelShapes.or(pole, low);
return pole;
}
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)
{
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.makeCuboidShape(px0, 0, px0, px1, pole_height, px1);
VoxelShape vs1 = Block.makeCuboidShape(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
VoxelShape vs2 = Block.makeCuboidShape(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
VoxelShape vs3 = Block.makeCuboidShape(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
VoxelShape vs4 = Block.makeCuboidShape(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
VoxelShape vs5 = Block.makeCuboidShape(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
VoxelShape vs6 = Block.makeCuboidShape(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
VoxelShape vs7 = Block.makeCuboidShape(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
VoxelShape vs8 = Block.makeCuboidShape(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
for(Boolean up : UP.getAllowedValues()) {
for(WallHeight wh_east : WALL_EAST.getAllowedValues()) {
for(WallHeight wh_north : WALL_NORTH.getAllowedValues()) {
for(WallHeight wh_west : WALL_WEST.getAllowedValues()) {
for(WallHeight wh_south : WALL_SOUTH.getAllowedValues()) {
VoxelShape shape = VoxelShapes.empty();
shape = combinedShape(shape, wh_east, vs4, vs8);
shape = combinedShape(shape, wh_west, vs3, vs7);
shape = combinedShape(shape, wh_north, vs1, vs5);
shape = combinedShape(shape, wh_south, vs2, vs6);
if(up) shape = VoxelShapes.or(shape, vp);
BlockState bs = getDefaultState().with(UP, up)
.with(WALL_EAST, wh_east)
.with(WALL_NORTH, wh_north)
.with(WALL_WEST, wh_west)
.with(WALL_SOUTH, wh_south);
builder.put(bs.with(WATERLOGGED, false), shape);
builder.put(bs.with(WATERLOGGED, true), shape);
}
}
}
}
}
return builder.build();
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return shape_voxels.getOrDefault(state, VoxelShapes.fullCube()); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return collision_shape_voxels.getOrDefault(state, VoxelShapes.fullCube()); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); }
protected boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side)
{
final Block block = facingState.getBlock();
if((block instanceof FenceGateBlock) || (block instanceof StandardFenceBlock) || (block instanceof VariantWallBlock)) return true;
final BlockState oppositeState = world.getBlockState(facingPos.offset(side, 2));
if(!(oppositeState.getBlock() instanceof StandardFenceBlock)) return false;
return facingState.isNormalCube(world, facingPos) && hasSolidSide(facingState, world, facingPos, side);
}
protected WallHeight selectWallHeight(IWorldReader world, BlockPos pos, Direction direction)
{
return WallHeight.LOW; // @todo: implement
}
public BlockState getStateForPlacement(BlockItemUseContext context)
{
IWorldReader world = context.getWorld();
BlockPos pos = context.getPos();
FluidState fs = context.getWorld().getFluidState(context.getPos());
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
return getDefaultState()
.with(UP, not_straight)
.with(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE)
.with(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE)
.with(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE)
.with(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE)
.with(WATERLOGGED, fs.getFluid() == Fluids.WATER);
}
@Override
public BlockState updatePostPlacement(BlockState state, Direction side, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{
if(state.get(BlockStateProperties.WATERLOGGED)) world.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
if(side == Direction.DOWN) return super.updatePostPlacement(state, side, facingState, world, pos, facingPos);
boolean n = (side==Direction.NORTH) ? attachesTo(facingState, world, facingPos, side) : (state.get(WALL_NORTH)!=WallHeight.NONE);
boolean e = (side==Direction.EAST) ? attachesTo(facingState, world, facingPos, side) : (state.get(WALL_EAST) !=WallHeight.NONE);
boolean s = (side==Direction.SOUTH) ? attachesTo(facingState, world, facingPos, side) : (state.get(WALL_SOUTH)!=WallHeight.NONE);
boolean w = (side==Direction.WEST) ? attachesTo(facingState, world, facingPos, side) : (state.get(WALL_WEST) !=WallHeight.NONE);
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
return state.with(UP, not_straight)
.with(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE)
.with(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE)
.with(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE)
.with(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE);
}
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
@SuppressWarnings("deprecation")
public PushReaction getPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
}

View file

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

View file

@ -0,0 +1,199 @@
/*
* @file VariantSlabBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Standard half block horizontal slab characteristics class.
*/
package wile.engineersdecor.libmc.blocks;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.state.StateContainer;
import net.minecraft.block.*;
import net.minecraft.block.BlockState;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.SlabType;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.math.vector.*;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class VariantSlabBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock
{
public static final EnumProperty<SlabType> TYPE = BlockStateProperties.SLAB_TYPE;
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
protected static final VoxelShape AABBs[] = {
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
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)), // both slabs
VoxelShapes.create(new AxisAlignedBB(0, 0./16, 0, 1, 16./16, 1)) // << 2bit fill
};
protected static final int num_slabs_contained_in_parts_[] = {1,1,2,2};
private static boolean with_pickup = false;
public static void on_config(boolean direct_slab_pickup)
{ with_pickup = direct_slab_pickup; }
protected boolean is_cube(BlockState state)
{ return state.get(TYPE) == SlabType.DOUBLE; }
public VariantSlabBlock(long config, Block.Properties builder)
{ super(config, builder); setDefaultState(getDefaultState().with(TYPE, SlabType.BOTTOM)); }
@Override
public RenderTypeHint getRenderTypeHint()
{ return (((config & StandardBlocks.CFG_TRANSLUCENT)!=0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT)); }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{
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)
@SuppressWarnings("deprecation")
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side)
{ return (adjacentBlockState==state) ? true : super.isSideInvisible(state, adjacentBlockState, side); }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext)
{ return AABBs[state.get(TYPE).ordinal() & 0x3]; }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return getShape(state, world, pos, selectionContext); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(TYPE, TEXTURE_VARIANT); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{
BlockPos pos = context.getPos();
if(context.getWorld().getBlockState(pos).getBlock() == this) return context.getWorld().getBlockState(pos).with(TYPE, SlabType.DOUBLE).with(WATERLOGGED, false);
final int rnd = MathHelper.clamp((int)(MathHelper.getPositionRandom(context.getPos()) & 0x3), 0, 3);
final Direction face = context.getFace();
final BlockState placement_state = super.getStateForPlacement(context).with(TEXTURE_VARIANT, rnd); // fluid state
if(face == Direction.UP) return placement_state.with(TYPE, SlabType.BOTTOM);
if(face == Direction.DOWN) return placement_state.with(TYPE, SlabType.TOP);
if(!face.getAxis().isHorizontal()) return placement_state;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return placement_state.with(TYPE, isupper ? SlabType.TOP : SlabType.BOTTOM);
}
@Override
@SuppressWarnings("deprecation")
public boolean isReplaceable(BlockState state, BlockItemUseContext context)
{
if(context.getItem().getItem() != this.asItem()) return false;
if(!context.replacingClickedOnBlock()) return true;
final Direction face = context.getFace();
final SlabType type = state.get(TYPE);
if((face == Direction.UP) && (type==SlabType.BOTTOM)) return true;
if((face == Direction.DOWN) && (type==SlabType.TOP)) return true;
if(!face.getAxis().isHorizontal()) return false;
final boolean isupper = ((context.getHitVec().getY() - context.getPos().getY()) > 0.5);
return isupper ? (type==SlabType.BOTTOM) : (type==SlabType.TOP);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, Rotation rot)
{ return state; }
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, Mirror mirrorIn)
{ return state; }
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, TileEntity te, boolean explosion)
{ return new ArrayList<ItemStack>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.get(TYPE).ordinal() & 0x3]))); }
@Override
@SuppressWarnings("deprecation")
public void onBlockClicked(BlockState state, World world, BlockPos pos, PlayerEntity player)
{
if((world.isRemote) || (!with_pickup)) return;
final ItemStack stack = player.getHeldItemMainhand();
if(stack.isEmpty() || (Block.getBlockFromItem(stack.getItem()) != this)) return;
if(stack.getCount() >= stack.getMaxStackSize()) return;
Vector3d lv = player.getLookVec();
Direction facing = Direction.getFacingFromVector((float)lv.x, (float)lv.y, (float)lv.z);
if((facing != Direction.UP) && (facing != Direction.DOWN)) return;
if(state.getBlock() != this) return;
SlabType type = state.get(TYPE);
if(facing == Direction.DOWN) {
if(type == SlabType.DOUBLE) {
world.setBlockState(pos, state.with(TYPE, SlabType.BOTTOM), 3);
} else {
world.removeBlock(pos, false);
}
} else if(facing == Direction.UP) {
if(type == SlabType.DOUBLE) {
world.setBlockState(pos, state.with(TYPE, SlabType.TOP), 3);
} else {
world.removeBlock(pos, false);
}
}
if(!player.isCreative()) {
stack.grow(1);
if(player.inventory != null) player.inventory.markDirty();
}
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());
}
@Override
public boolean receiveFluid(IWorld world, BlockPos pos, BlockState state, FluidState fluidState)
{ return (state.get(TYPE)==SlabType.DOUBLE) ? false : super.receiveFluid(world, pos, state, fluidState); }
@Override
public boolean canContainFluid(IBlockReader world, BlockPos pos, BlockState state, Fluid fluid)
{ return (state.get(TYPE)==SlabType.DOUBLE) ? false : super.canContainFluid(world, pos, state, fluid); }
}

View file

@ -0,0 +1,197 @@
/*
* @file VariantWallBlock.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Wall blocks.
*/
package wile.engineersdecor.libmc.blocks;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.math.shapes.VoxelShapes;
import wile.engineersdecor.libmc.detail.Auxiliaries;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.world.*;
import net.minecraft.fluid.FluidState;
import net.minecraft.entity.EntityType;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.state.StateContainer;
import net.minecraft.util.math.MathHelper;
import net.minecraft.block.*;
import net.minecraft.block.material.PushReaction;
import net.minecraft.block.BlockState;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack;
import net.minecraft.state.IntegerProperty;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
public class VariantWallBlock extends WallBlock implements StandardBlocks.IStandardBlock
{
public static final BooleanProperty UP = BlockStateProperties.UP;
public static final EnumProperty<WallHeight> WALL_EAST = BlockStateProperties.field_235908_S_;
public static final EnumProperty<WallHeight> WALL_NORTH = BlockStateProperties.field_235909_T_;
public static final EnumProperty<WallHeight> WALL_SOUTH = BlockStateProperties.field_235910_U_;
public static final EnumProperty<WallHeight> WALL_WEST = BlockStateProperties.field_235911_V_;
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 7);
private final Map<BlockState, VoxelShape> shape_voxels;
private final Map<BlockState, VoxelShape> collision_shape_voxels;
private final long config;
public VariantWallBlock(long config, Block.Properties builder)
{
super(builder);
shape_voxels = buildWallShapes(4, 16, 4, 0, 16, 16);
collision_shape_voxels = buildWallShapes(6, 16, 5, 0, 24, 24);
this.config = config;
}
@Override
public long config()
{ return config; }
@Override
@OnlyIn(Dist.CLIENT)
public void addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag)
{ Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); }
private static VoxelShape combinedShape(VoxelShape pole, WallHeight height, VoxelShape low, VoxelShape high)
{
if(height == WallHeight.TALL) return VoxelShapes.or(pole, high);
if(height == WallHeight.LOW) return VoxelShapes.or(pole, low);
return pole;
}
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)
{
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.makeCuboidShape(px0, 0, px0, px1, pole_height, px1);
VoxelShape vs1 = Block.makeCuboidShape(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
VoxelShape vs2 = Block.makeCuboidShape(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
VoxelShape vs3 = Block.makeCuboidShape(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
VoxelShape vs4 = Block.makeCuboidShape(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
VoxelShape vs5 = Block.makeCuboidShape(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
VoxelShape vs6 = Block.makeCuboidShape(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
VoxelShape vs7 = Block.makeCuboidShape(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
VoxelShape vs8 = Block.makeCuboidShape(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
for(Boolean up : UP.getAllowedValues()) {
for(WallHeight wh_east : WALL_EAST.getAllowedValues()) {
for(WallHeight wh_north : WALL_NORTH.getAllowedValues()) {
for(WallHeight wh_west : WALL_WEST.getAllowedValues()) {
for(WallHeight wh_south : WALL_SOUTH.getAllowedValues()) {
VoxelShape shape = VoxelShapes.empty();
shape = combinedShape(shape, wh_east, vs4, vs8);
shape = combinedShape(shape, wh_west, vs3, vs7);
shape = combinedShape(shape, wh_north, vs1, vs5);
shape = combinedShape(shape, wh_south, vs2, vs6);
if(up) shape = VoxelShapes.or(shape, vp);
BlockState bs = getDefaultState().with(UP, up)
.with(WALL_EAST, wh_east)
.with(WALL_NORTH, wh_north)
.with(WALL_WEST, wh_west)
.with(WALL_SOUTH, wh_south);
final VoxelShape tvs = shape;
TEXTURE_VARIANT.getAllowedValues().forEach((tv)->{
builder.put(bs.with(TEXTURE_VARIANT, tv).with(WATERLOGGED, false), tvs);
builder.put(bs.with(TEXTURE_VARIANT, tv).with(WATERLOGGED, true), tvs);
});
}
}
}
}
}
return builder.build();
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return shape_voxels.getOrDefault(state, VoxelShapes.fullCube()); }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return collision_shape_voxels.getOrDefault(state, VoxelShapes.fullCube()); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(TEXTURE_VARIANT); }
private boolean attachesTo(BlockState facingState, IWorldReader world, BlockPos facingPos, Direction side)
{
final Block block = facingState.getBlock();
if((block instanceof FenceGateBlock) || (block instanceof WallBlock)) return true;
final BlockState oppositeState = world.getBlockState(facingPos.offset(side, 2));
if(!(oppositeState.getBlock() instanceof VariantWallBlock)) return false;
return facingState.isNormalCube(world, facingPos) && hasSolidSide(facingState, world, facingPos, side);
}
protected WallHeight selectWallHeight(IWorldReader world, BlockPos pos, Direction direction)
{
return WallHeight.LOW; // @todo: implement
}
public BlockState getStateForPlacement(BlockItemUseContext context)
{
IWorldReader world = context.getWorld();
BlockPos pos = context.getPos();
FluidState fs = context.getWorld().getFluidState(context.getPos());
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
return getDefaultState().with(UP, not_straight)
.with(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE)
.with(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE)
.with(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE)
.with(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE)
.with(WATERLOGGED, fs.getFluid()==Fluids.WATER);
}
@Override
public BlockState updatePostPlacement(BlockState state, Direction side, BlockState facingState, IWorld world, BlockPos pos, BlockPos facingPos)
{
if(state.get(WATERLOGGED)) world.getPendingFluidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
if(side == Direction.DOWN) return super.updatePostPlacement(state, side, facingState, world, pos, facingPos);
boolean n = (side==Direction.NORTH) ? this.attachesTo(facingState, world, facingPos, side) : state.get(WALL_NORTH)!=WallHeight.NONE;
boolean e = (side==Direction.EAST) ? this.attachesTo(facingState, world, facingPos, side) : state.get(WALL_EAST)!=WallHeight.NONE;
boolean s = (side==Direction.SOUTH) ? this.attachesTo(facingState, world, facingPos, side) : state.get(WALL_SOUTH)!=WallHeight.NONE;
boolean w = (side==Direction.WEST) ? this.attachesTo(facingState, world, facingPos, side) : state.get(WALL_WEST)!=WallHeight.NONE;
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
return state.with(UP, not_straight)
.with(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallHeight.NONE)
.with(WALL_EAST , e ? selectWallHeight(world, pos, Direction.EAST) : WallHeight.NONE)
.with(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallHeight.NONE)
.with(WALL_WEST , w ? selectWallHeight(world, pos, Direction.WEST) : WallHeight.NONE)
.with(TEXTURE_VARIANT, ((int)MathHelper.getPositionRandom(pos)) & 0x7);
}
@Override
public boolean canCreatureSpawn(BlockState state, IBlockReader world, BlockPos pos, EntitySpawnPlacementRegistry.PlacementType type, @Nullable EntityType<?> entityType)
{ return false; }
@Override
public boolean canSpawnInBlock()
{ return false; }
@Override
@SuppressWarnings("deprecation")
public PushReaction getPushReaction(BlockState state)
{ return PushReaction.NORMAL; }
}

View file

@ -0,0 +1,48 @@
/*
* @file BlockStateDataGen.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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.client.model.generators.*;
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

@ -0,0 +1,96 @@
/*
* @file LootTableGen.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 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 act(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)
.setParameterSet(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.toJson(tab), fp);
} catch(Exception e) {
LOGGER.error("Failed to save loottable '"+fp+"', exception: " + e);
}
});
}
private LootTable.Builder defaultBlockDrops(String rl_path, Block block)
{
ItemLootEntry.Builder iltb = ItemLootEntry.builder(block);
iltb.acceptFunction(CopyName.builder(Source.BLOCK_ENTITY));
if(block.hasTileEntity(block.getDefaultState())) {
iltb.acceptFunction(CopyNbt.builder(CopyNbt.Source.BLOCK_ENTITY));
}
return LootTable.builder().addLootPool(LootPool.builder().name(rl_path).rolls(ConstantRange.of(1)).addEntry(iltb));
}
}

View file

@ -0,0 +1,305 @@
/*
* @file Auxiliaries.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* General commonly used functionality.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.client.util.InputMappings;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.SharedConstants;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.item.ItemStack;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Auxiliaries
{
private static String modid;
private static Logger logger;
private static Supplier<CompoundNBT> server_config_supplier = ()->new CompoundNBT();
public static void init(String modid, Logger logger, Supplier<CompoundNBT> server_config_supplier)
{
Auxiliaries.modid = modid;
Auxiliaries.logger = logger;
Auxiliaries.server_config_supplier = server_config_supplier;
}
// -------------------------------------------------------------------------------------------------------------------
// Mod specific exports
// -------------------------------------------------------------------------------------------------------------------
public static String modid()
{ return modid; }
public static Logger logger()
{ return logger; }
// -------------------------------------------------------------------------------------------------------------------
// Sideness, system/environment, tagging interfaces
// -------------------------------------------------------------------------------------------------------------------
public interface IExperimentalFeature {}
public static final boolean isModLoaded(final String registry_name)
{ return ModList.get().isLoaded(registry_name); }
public static final boolean isDevelopmentMode()
{ return SharedConstants.developmentMode; }
@OnlyIn(Dist.CLIENT)
public static final boolean isShiftDown()
{
return (InputMappings.isKeyDown(SidedProxy.mc().getMainWindow().getHandle(), GLFW.GLFW_KEY_LEFT_SHIFT) ||
InputMappings.isKeyDown(SidedProxy.mc().getMainWindow().getHandle(), GLFW.GLFW_KEY_RIGHT_SHIFT));
}
@OnlyIn(Dist.CLIENT)
public static final boolean isCtrlDown()
{
return (InputMappings.isKeyDown(SidedProxy.mc().getMainWindow().getHandle(), GLFW.GLFW_KEY_LEFT_CONTROL) ||
InputMappings.isKeyDown(SidedProxy.mc().getMainWindow().getHandle(), GLFW.GLFW_KEY_RIGHT_CONTROL));
}
// -------------------------------------------------------------------------------------------------------------------
// Logging
// -------------------------------------------------------------------------------------------------------------------
public static final void logInfo(final String msg)
{ logger.info(msg); }
public static final void logWarn(final String msg)
{ logger.warn(msg); }
public static final void logError(final String msg)
{ logger.error(msg); }
// -------------------------------------------------------------------------------------------------------------------
// Localization, text formatting
// -------------------------------------------------------------------------------------------------------------------
/**
* 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, @Nullable TextFormatting color, Object... args)
{
TranslationTextComponent tr = new TranslationTextComponent((modtrkey.startsWith("block.") || (modtrkey.startsWith("item."))) ? (modtrkey) : (modid+"."+modtrkey), args);
if(color!=null) tr.func_240701_a_(color);
return tr;
}
public static TranslationTextComponent localizable(String modtrkey)
{ return localizable(modtrkey, null); }
public static TranslationTextComponent localizable_block_key(String blocksubkey)
{ return new TranslationTextComponent("block."+modid+"."+blocksubkey); }
@OnlyIn(Dist.CLIENT)
public static String localize(String translationKey, Object... args)
{
TranslationTextComponent tr = new TranslationTextComponent(translationKey, args);
tr.func_240701_a_(TextFormatting.RESET);
final String ft = tr.getString();
if(ft.contains("${")) {
// Non-recursive, non-argument lang file entry cross referencing.
Pattern pt = Pattern.compile("\\$\\{([^}]+)\\}");
Matcher mt = pt.matcher(ft);
StringBuffer sb = new StringBuffer();
while(mt.find()) {
String m = mt.group(1);
if(m.contains("?")) {
String[] kv = m.split("\\?", 2);
String key = kv[0].trim();
boolean not = key.startsWith("!");
if(not) key = key.replaceFirst("!", "");
m = kv[1].trim();
if(!server_config_supplier.get().contains(key)) {
m = "";
} else {
boolean r = server_config_supplier.get().getBoolean(key);
if(not) r = !r;
if(!r) m = "";
}
}
mt.appendReplacement(sb, (new TranslationTextComponent(m)).getString().trim());
}
mt.appendTail(sb);
return sb.toString();
} else {
return ft;
}
}
/**
* Returns true if a given key is translated for the current language.
*/
@OnlyIn(Dist.CLIENT)
public static boolean hasTranslation(String key)
{ return net.minecraft.client.resources.I18n.hasKey(key); }
public static final class Tooltip
{
@OnlyIn(Dist.CLIENT)
public static boolean extendedTipCondition()
{ return isShiftDown(); }
@OnlyIn(Dist.CLIENT)
public static boolean helpCondition()
{ return isShiftDown() && isCtrlDown(); }
/**
* 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
* no translation found).
*/
@OnlyIn(Dist.CLIENT)
public static boolean addInformation(@Nullable String advancedTooltipTranslationKey, @Nullable String helpTranslationKey, List<ITextComponent> tooltip, ITooltipFlag flag, boolean addAdvancedTooltipHints)
{
// Note: intentionally not using keybinding here, this must be `control` or `shift`. MC uses lwjgl Keyboard,
// so using this also here should be ok.
final boolean help_available = (helpTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".help");
final boolean tip_available = (advancedTooltipTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".tip");
if((!help_available) && (!tip_available)) return false;
if(helpCondition()) {
if(!help_available) return false;
String s = localize(helpTranslationKey + ".help");
if(s.isEmpty()) return false;
tooltip.add(new StringTextComponent(s)); // @todo: check how to optimise that (to use TranslationTextComponent directly without compat losses)
return true;
} else if(extendedTipCondition()) {
if(!tip_available) return false;
String s = localize(advancedTooltipTranslationKey + ".tip");
if(s.isEmpty()) return false;
tooltip.add(new StringTextComponent(s));
return true;
} else if(addAdvancedTooltipHints) {
String s = "";
if(tip_available) s += localize(modid + ".tooltip.hint.extended") + (help_available ? " " : "");
if(help_available) s += localize(modid + ".tooltip.hint.help");
tooltip.add(new StringTextComponent(s));
}
return false;
}
/**
* 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 value see method pattern above.
*/
@OnlyIn(Dist.CLIENT)
public static boolean addInformation(ItemStack stack, @Nullable IBlockReader world, List<ITextComponent> tooltip, ITooltipFlag flag, boolean addAdvancedTooltipHints)
{ return addInformation(stack.getTranslationKey(), stack.getTranslationKey(), tooltip, flag, addAdvancedTooltipHints); }
}
@SuppressWarnings("unused")
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));
}
// -------------------------------------------------------------------------------------------------------------------
// Block handling
// -------------------------------------------------------------------------------------------------------------------
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); }
public static final AxisAlignedBB getRotatedAABB(AxisAlignedBB bb, Direction new_facing, boolean horizontal_rotation)
{
if(!horizontal_rotation) {
switch(new_facing.getIndex()) {
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
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
case 4: return new AxisAlignedBB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W
case 5: return new AxisAlignedBB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E
}
} else {
switch(new_facing.getIndex()) {
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
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
case 4: return new AxisAlignedBB( bb.minZ, bb.minY, 1-bb.maxX, bb.maxZ, bb.maxY, 1-bb.minX); // W
case 5: return new AxisAlignedBB(1-bb.maxZ, bb.minY, bb.minX, 1-bb.minZ, bb.maxY, bb.maxX); // E
}
}
return bb;
}
public static final AxisAlignedBB[] getRotatedAABB(AxisAlignedBB[] bbs, Direction new_facing, boolean horizontal_rotation)
{
AxisAlignedBB[] transformed = new AxisAlignedBB[bbs.length];
for(int i=0; i<bbs.length; ++i) transformed[i] = getRotatedAABB(bbs[i], new_facing, horizontal_rotation);
return transformed;
}
public static final VoxelShape getUnionShape(AxisAlignedBB[] aabbs)
{
VoxelShape shape = VoxelShapes.empty();
for(AxisAlignedBB aabb: aabbs) shape = VoxelShapes.combine(shape, VoxelShapes.create(aabb), IBooleanFunction.OR);
return shape;
}
// -------------------------------------------------------------------------------------------------------------------
// 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,306 @@
/*
* @file Fluidics.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* General fluid handling functionality.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidActionResult;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class Fluidics
{
/**
* Dedicated fluid handler for a single tank.
*/
public static class SingleTankFluidHandler implements IFluidHandler
{
private final IFluidTank tank_;
public SingleTankFluidHandler(IFluidTank tank) { tank_ = tank; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return tank_.getFluid(); }
@Override public int getTankCapacity(int tank) { return tank_.getCapacity(); }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return tank_.isFluidValid(stack); }
@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); }
}
/**
* Simple fluid tank, validator concept according to reference implementation by KingLemming.
*/
public static class Tank implements IFluidTank
{
private Predicate<FluidStack> validator_ = ((e)->true);
private BiConsumer<Tank,Integer> interaction_notifier_ = ((tank,diff)->{});
private FluidStack fluid_ = FluidStack.EMPTY;
private int capacity_;
public Tank(int capacity)
{ capacity_ = capacity; }
public Tank readnbt(CompoundNBT nbt)
{ setFluid(FluidStack.loadFluidStackFromNBT(nbt)); return this; }
public CompoundNBT writenbt(CompoundNBT nbt)
{ fluid_.writeToNBT(nbt); return nbt; }
public int getCapacity()
{ return capacity_; }
public Tank setCapacity(int capacity)
{ capacity_ = capacity; return this; }
public Tank setValidator(Predicate<FluidStack> validator)
{ validator_ = (validator!=null) ? validator : ((e)->true); return this; }
public Tank setInteractionNotifier(BiConsumer<Tank,Integer> notifier)
{ interaction_notifier_ = (notifier!=null) ? notifier : ((tank,diff)->{}); return this; }
@Nonnull
public FluidStack getFluid()
{ return fluid_; }
public void setFluid(@Nullable FluidStack stack)
{ fluid_ = (stack==null) ? FluidStack.EMPTY : stack; }
public int getFluidAmount()
{ return fluid_.getAmount(); }
public boolean isEmpty()
{ return fluid_.isEmpty(); }
public boolean isFluidValid(FluidStack stack)
{ return validator_.test(stack); }
@Override
public int fill(FluidStack fs, FluidAction action)
{
if(fs.isEmpty() || (!isFluidValid(fs))) {
return 0;
} else if(action.simulate()) {
if(fluid_.isEmpty()) return Math.min(capacity_, fs.getAmount());
if(!fluid_.isFluidEqual(fs)) return 0;
return Math.min(capacity_-fluid_.getAmount(), fs.getAmount());
} else if(fluid_.isEmpty()) {
fluid_ = new FluidStack(fs, Math.min(capacity_, fs.getAmount()));
return fluid_.getAmount();
} else if(!fluid_.isFluidEqual(fs)) {
return 0;
} else {
int amount = capacity_ - fluid_.getAmount();
if(fs.getAmount() < amount) {
fluid_.grow(fs.getAmount());
amount = fs.getAmount();
} else {
fluid_.setAmount(capacity_);
}
if(amount != 0) interaction_notifier_.accept(this, amount);
return amount;
}
}
@Nonnull
@Override
public FluidStack drain(FluidStack fs, FluidAction action)
{ return ((fs.isEmpty()) || (!fs.isFluidEqual(fluid_))) ? FluidStack.EMPTY : drain(fs.getAmount(), action); }
@Nonnull
@Override
public FluidStack drain(int maxDrain, FluidAction action)
{
final int amount = Math.min(fluid_.getAmount(), maxDrain);
final FluidStack stack = new FluidStack(fluid_, amount);
if((amount > 0) && action.execute()) {
fluid_.shrink(amount);
if(fluid_.isEmpty()) fluid_ = FluidStack.EMPTY;
if(amount != 0) interaction_notifier_.accept(this, -amount);
}
return stack;
}
}
/**
* Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
*/
public static boolean manualFluidHandlerInteraction(World world, BlockPos pos, @Nullable Direction side, PlayerEntity player, Hand hand)
{ return manualTrackedFluidHandlerInteraction(world, pos, side, player, hand) != null; }
/**
* 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.
*/
public static @Nullable Tuple<Fluid, Integer> manualTrackedFluidHandlerInteraction(World world, BlockPos pos, @Nullable Direction side, PlayerEntity player, Hand hand)
{
if(world.isRemote()) return null;
final ItemStack held = player.getHeldItem(hand);
if(held.isEmpty()) return null;
final IFluidHandler fh = FluidUtil.getFluidHandler(world, pos, side).orElse(null);
if(fh==null) return null;
final IItemHandler ih = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(null);
if(ih==null) return null;
FluidActionResult far = FluidUtil.tryFillContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
if(!far.isSuccess()) far = FluidUtil.tryEmptyContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
if(!far.isSuccess()) return null;
final ItemStack rstack = far.getResult().copy();
player.setHeldItem(hand, far.getResult());
final IFluidHandler fh_before = FluidUtil.getFluidHandler(held).orElse(null);
final IFluidHandler fh_after = FluidUtil.getFluidHandler(rstack).orElse(null);
if((fh_before==null) || (fh_after==null) || (fh_after.getTanks()!=fh_before.getTanks())) return null; // should not be, but y'never know.
for(int i=0; i<fh_before.getTanks(); ++i) {
final int vol_before = fh_before.getFluidInTank(i).getAmount();
final int vol_after = fh_after.getFluidInTank(i).getAmount();
if(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;
}
/**
* Fluid tank access when itemized.
*/
public static class FluidContainerItemCapabilityWrapper implements IFluidHandlerItem, ICapabilityProvider
{
private final LazyOptional<IFluidHandlerItem> handler_ = LazyOptional.of(()->this);
private final Function<ItemStack, CompoundNBT> nbt_getter_;
private final BiConsumer<ItemStack, CompoundNBT> nbt_setter_;
private final Predicate<FluidStack> validator_;
private final ItemStack container_;
private final int capacity_;
private final int transfer_rate_;
public FluidContainerItemCapabilityWrapper(ItemStack container, int capacity, int transfer_rate,
Function<ItemStack, CompoundNBT> nbt_getter,
BiConsumer<ItemStack, CompoundNBT> nbt_setter,
Predicate<FluidStack> validator)
{
container_ = container;
capacity_ = capacity;
transfer_rate_ = transfer_rate;
nbt_getter_ = nbt_getter;
nbt_setter_ = nbt_setter;
validator_ = (validator!=null) ? validator : (e->true);
}
@Override
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction side)
{ return (capability == CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY) ? handler_.cast() : LazyOptional.empty(); }
protected FluidStack readnbt()
{
final CompoundNBT nbt = nbt_getter_.apply(container_);
return ((nbt==null) || (nbt.isEmpty())) ? FluidStack.EMPTY : FluidStack.loadFluidStackFromNBT(nbt);
}
protected void writenbt(FluidStack fs)
{
CompoundNBT nbt = new CompoundNBT();
if(!fs.isEmpty()) fs.writeToNBT(nbt);
nbt_setter_.accept(container_, nbt);
}
@Override
public ItemStack getContainer()
{ return container_; }
@Override
public int getTanks()
{ return 1; }
@Override
public FluidStack getFluidInTank(int tank)
{ return readnbt(); }
@Override
public int getTankCapacity(int tank)
{ return capacity_; }
@Override
public boolean isFluidValid(int tank, FluidStack fs)
{ return isFluidValid(fs); }
public boolean isFluidValid(FluidStack fs)
{ return validator_.test(fs); }
@Override
public int fill(FluidStack fs, FluidAction action)
{
if((fs.isEmpty()) || (!isFluidValid(fs) || (container_.getCount()!=1))) return 0;
FluidStack tank = readnbt();
final int amount = Math.min(Math.min(fs.getAmount(),transfer_rate_), capacity_-tank.getAmount());
if(amount <= 0) return 0;
if(tank.isEmpty()) {
if(action.execute()) {
tank = new FluidStack(fs.getFluid(), amount, fs.getTag());
writenbt(tank);
}
} else {
if(!tank.isFluidEqual(fs)) {
return 0;
} else if(action.execute()) {
tank.grow(amount);
writenbt(tank);
}
}
return amount;
}
@Override
public FluidStack drain(FluidStack fs, FluidAction action)
{
if((fs.isEmpty()) || (container_.getCount()!=1)) return FluidStack.EMPTY;
final FluidStack tank = readnbt();
if((!tank.isEmpty()) && (!tank.isFluidEqual(fs))) return FluidStack.EMPTY;
return drain(fs.getAmount(), action);
}
@Override
public FluidStack drain(int max, FluidAction action)
{
if((max<=0) || (container_.getCount()!=1)) return FluidStack.EMPTY;
FluidStack tank = readnbt();
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;
}
}
}

View file

@ -0,0 +1,348 @@
/*
* @file Inventories.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* General inventory item handling functionality.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.wrapper.InvWrapper;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class Inventories
{
public static boolean areItemStacksIdentical(ItemStack a, ItemStack b)
{ return (a.getItem()==b.getItem()) && ItemStack.areItemStackTagsEqual(a, b); }
public static boolean areItemStacksDifferent(ItemStack a, ItemStack b)
{ return (a.getItem()!=b.getItem()) || (!ItemStack.areItemStackTagsEqual(a, b)); }
public static IItemHandler itemhandler(World world, BlockPos pos, @Nullable Direction side)
{
TileEntity te = world.getTileEntity(pos);
if(te==null) return null;
IItemHandler ih = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).orElse(null);
if(ih!=null) return ih;
if((side!=null) && (te instanceof ISidedInventory)) return new SidedInvWrapper((ISidedInventory)te, side);
if(te instanceof IInventory) return new InvWrapper((IInventory)te);
return null;
}
public static ItemStack insert(IItemHandler handler, ItemStack stack , boolean simulate)
{ return ItemHandlerHelper.insertItemStacked(handler, stack, simulate); }
public static ItemStack insert(TileEntity te, @Nullable Direction side, ItemStack stack, boolean simulate)
{
if(te==null) return stack;
IItemHandler hnd = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side).orElse(null);
if(hnd != null) {
hnd = (IItemHandler)te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side);
} else if((side!=null) && (te instanceof ISidedInventory)) {
hnd = new SidedInvWrapper((ISidedInventory)te, side);
} else if(te instanceof IInventory) {
hnd = new InvWrapper((IInventory)te);
}
return (hnd==null) ? stack : insert(hnd, stack, simulate);
}
public static ItemStack insert(World world, BlockPos pos, @Nullable Direction side, ItemStack stack, boolean simulate)
{ return insert(world.getTileEntity(pos), side, stack, simulate); }
public static ItemStack extract(IItemHandler inventory, @Nullable ItemStack match, int amount, boolean simulate)
{
if((inventory==null) || (amount<=0)) return ItemStack.EMPTY;
final int max = inventory.getSlots();
ItemStack out_stack = ItemStack.EMPTY;
for(int i=0; i<max; ++i) {
final ItemStack stack = inventory.getStackInSlot(i);
if(stack.isEmpty()) continue;
if(out_stack.isEmpty()) {
if((match!=null) && areItemStacksDifferent(stack, match)) continue;
out_stack = inventory.extractItem(i, amount, simulate);
} else if(areItemStacksIdentical(stack, out_stack)) {
ItemStack es = inventory.extractItem(i, (amount-out_stack.getCount()), simulate);
out_stack.grow(es.getCount());
}
if(out_stack.getCount() >= amount) break;
}
return out_stack;
}
private static ItemStack checked(ItemStack stack)
{ return stack.isEmpty() ? ItemStack.EMPTY : stack; } // explicit EMPTY return
//--------------------------------------------------------------------------------------------------------------------
public static class SlotRange
{
public final IInventory inventory;
public final int start_slot, end_slot;
public SlotRange(IInventory inv, int start, int end)
{ inventory=inv; start_slot=start; end_slot=end; }
/**
* Returns the number of stacks that match the given stack with NBT.
*/
public int stackMatchCount(final ItemStack ref_stack)
{
int n = 0; // ... std::accumulate() the old school way.
for(int i = start_slot; i < end_slot; ++i) {
if(areItemStacksIdentical(ref_stack, inventory.getStackInSlot(i))) ++n;
}
return n;
}
public int totalMatchingItemCount(final ItemStack ref_stack)
{
int n = 0;
for(int i = start_slot; i < end_slot; ++i) {
ItemStack stack = inventory.getStackInSlot(i);
if(areItemStacksIdentical(ref_stack, stack)) n += stack.getCount();
}
return n;
}
/**
* Moves as much items from the stack to the slots in range [start_slot, end_slot] of the inventory,
* filling up existing stacks first, then (player inventory only) checks appropriate empty slots next
* to stacks that have that item already, and last uses any empty slot that can be found.
* Returns the stack that is still remaining in the referenced `stack`.
*/
public ItemStack insert(final ItemStack stack_to_move, boolean only_fillup, int limit, boolean reverse, boolean force_group_stacks)
{
final ItemStack mvstack = stack_to_move.copy();
if((mvstack.isEmpty()) || (start_slot < 0) || (end_slot > inventory.getSizeInventory())) return checked(mvstack);
int limit_left = (limit>0) ? (Math.min(limit, mvstack.getMaxStackSize())) : (mvstack.getMaxStackSize());
boolean matches[] = new boolean[end_slot];
boolean empties[] = new boolean[end_slot];
int num_matches = 0;
for(int i = start_slot; i < end_slot; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
final ItemStack stack = inventory.getStackInSlot(sno);
if(stack.isEmpty()) {
empties[sno] = true;
} else if(areItemStacksIdentical(stack, mvstack)) {
matches[sno] = true;
++num_matches;
}
}
// first iteration: fillup existing stacks
for(int i = start_slot; i < end_slot; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
if((empties[sno]) || (!matches[sno])) continue;
final ItemStack stack = inventory.getStackInSlot(sno);
int nmax = Math.min(limit_left, stack.getMaxStackSize() - stack.getCount());
if(mvstack.getCount() <= nmax) {
stack.setCount(stack.getCount()+mvstack.getCount());
inventory.setInventorySlotContents(sno, stack);
return ItemStack.EMPTY;
} else {
stack.grow(nmax);
mvstack.shrink(nmax);
inventory.setInventorySlotContents(sno, stack);
limit_left -= nmax;
}
}
if(only_fillup) return checked(mvstack);
if((num_matches>0) && ((force_group_stacks) || (inventory instanceof PlayerInventory))) {
// second iteration: use appropriate empty slots,
// a) between
{
int insert_start = -1;
int insert_end = -1;
int i = start_slot+1;
for(;i < end_slot-1; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
if(insert_start < 0) {
if(matches[sno]) insert_start = sno;
} else if(matches[sno]) {
insert_end = sno;
}
}
for(i=insert_start;i < insert_end; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
if((!empties[sno]) || (!inventory.isItemValidForSlot(sno, mvstack))) continue;
int nmax = Math.min(limit_left, mvstack.getCount());
ItemStack moved = mvstack.copy();
moved.setCount(nmax);
mvstack.shrink(nmax);
inventory.setInventorySlotContents(sno, moved);
return checked(mvstack);
}
}
// b) before/after
{
for(int i = start_slot+1; i < end_slot-1; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
if(!matches[sno]) continue;
int ii = (empties[sno-1]) ? (sno-1) : (empties[sno+1] ? (sno+1) : -1);
if((ii >= 0) && (inventory.isItemValidForSlot(ii, mvstack))) {
int nmax = Math.min(limit_left, mvstack.getCount());
ItemStack moved = mvstack.copy();
moved.setCount(nmax);
mvstack.shrink(nmax);
inventory.setInventorySlotContents(ii, moved);
return checked(mvstack);
}
}
}
}
// third iteration: use any empty slots
for(int i = start_slot; i < end_slot; ++i) {
final int sno = reverse ? (end_slot-1-i) : (i);
if((!empties[sno]) || (!inventory.isItemValidForSlot(sno, mvstack))) continue;
int nmax = Math.min(limit_left, mvstack.getCount());
ItemStack placed = mvstack.copy();
placed.setCount(nmax);
mvstack.shrink(nmax);
inventory.setInventorySlotContents(sno, placed);
return checked(mvstack);
}
return checked(mvstack);
}
public ItemStack insert(final ItemStack stack_to_move, boolean only_fillup, int limit)
{ return insert(stack_to_move, only_fillup, limit, false, false); }
public ItemStack insert(final ItemStack stack_to_move, boolean only_fillup)
{ return insert(stack_to_move, only_fillup, 0, false, false); }
public ItemStack insert(final ItemStack stack_to_move)
{ return insert(stack_to_move, false, 0, false, false); }
/**
* Moves as much items from the slots in range [start_slot, end_slot] of the inventory into a new stack.
* Implicitly shrinks the inventory stacks and the `request_stack`.
*/
public ItemStack extract(final ItemStack request_stack)
{
if(request_stack.isEmpty()) return ItemStack.EMPTY;
final IInventory inventory = this.inventory;
List<ItemStack> matches = new ArrayList<>();
for(int i = start_slot; i < end_slot; ++i) {
final ItemStack stack = inventory.getStackInSlot(i);
if((!stack.isEmpty()) && (areItemStacksIdentical(stack, request_stack))) {
if(stack.hasTag()) {
final CompoundNBT nbt = stack.getTag();
int n = nbt.size();
if((n > 0) && (nbt.contains("Damage"))) --n;
if(n > 0) continue;
}
matches.add(stack);
}
}
matches.sort((a,b) -> Integer.compare(a.getCount(), b.getCount()));
if(matches.isEmpty()) return ItemStack.EMPTY;
int n_left = request_stack.getCount();
ItemStack fetched_stack = matches.get(0).split(n_left);
n_left -= fetched_stack.getCount();
for(int i=1; (i<matches.size()) && (n_left>0); ++i) {
ItemStack stack = matches.get(i).split(n_left);
n_left -= stack.getCount();
fetched_stack.grow(stack.getCount());
}
return checked(fetched_stack);
}
}
public static class InventoryRange implements IInventory
{
public final IInventory inventory;
public final int offset, size;
public InventoryRange(IInventory inventory, int offset, int size)
{ this.inventory = inventory; this.offset = offset; this.size = size; }
public void clear()
{ inventory.clear(); }
public int getSizeInventory()
{ return size; }
public boolean isEmpty()
{ for(int i=0; i<size; ++i) if(!inventory.getStackInSlot(offset+i).isEmpty()){return false;} return true; }
public ItemStack getStackInSlot(int index)
{ return inventory.getStackInSlot(offset+index); }
public ItemStack decrStackSize(int index, int count)
{ return inventory.decrStackSize(offset+index, count); }
public ItemStack removeStackFromSlot(int index)
{ return inventory.removeStackFromSlot(offset+index); }
public void setInventorySlotContents(int index, ItemStack stack)
{ inventory.setInventorySlotContents(offset+index, stack); }
public int getInventoryStackLimit()
{ return inventory.getInventoryStackLimit(); }
public void markDirty()
{ inventory.markDirty(); }
public boolean isUsableByPlayer(PlayerEntity player)
{ return inventory.isUsableByPlayer(player); }
public void openInventory(PlayerEntity player)
{ inventory.openInventory(player); }
public void closeInventory(PlayerEntity player)
{ inventory.closeInventory(player); }
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return inventory.isItemValidForSlot(offset+index, stack); }
}
//--------------------------------------------------------------------------------------------------------------------
public static NonNullList<ItemStack> readNbtStacks(CompoundNBT nbt, String key, int size)
{
NonNullList<ItemStack> stacks = NonNullList.withSize(size, ItemStack.EMPTY);
if((nbt == null) || (!nbt.contains(key,10))) return stacks;
CompoundNBT stacknbt = nbt.getCompound(key);
ItemStackHelper.loadAllItems(stacknbt, stacks);
return stacks;
}
public static CompoundNBT writeNbtStacks(CompoundNBT nbt, String key, NonNullList<ItemStack> stacks, boolean omit_trailing_empty)
{
CompoundNBT stacknbt = new CompoundNBT();
if(omit_trailing_empty) {
for(int i=stacks.size()-1; i>=0; --i) {
if(!stacks.get(i).isEmpty()) break;
stacks.remove(i);
}
}
ItemStackHelper.saveAllItems(stacknbt, stacks);
if(nbt == null) nbt = new CompoundNBT();
nbt.put(key, stacknbt);
return nbt;
}
public static CompoundNBT writeNbtStacks(CompoundNBT nbt, String key, NonNullList<ItemStack> stacks)
{ return writeNbtStacks(nbt, key, stacks, false); }
}

View file

@ -0,0 +1,303 @@
/*
* @file Networking.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Main client/server message handling.
*/
package wile.engineersdecor.libmc.detail;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.world.World;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.fml.network.NetworkDirection;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class Networking
{
private static final String PROTOCOL = "1";
private static SimpleChannel DEFAULT_CHANNEL;
public static void init(String modid)
{
DEFAULT_CHANNEL = NetworkRegistry.ChannelBuilder
.named(new ResourceLocation(modid, "default_ch"))
.clientAcceptedVersions(PROTOCOL::equals).serverAcceptedVersions(PROTOCOL::equals).networkProtocolVersion(() -> PROTOCOL)
.simpleChannel();
int discr = -1;
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyClientToServer.class, PacketTileNotifyClientToServer::compose, PacketTileNotifyClientToServer::parse, PacketTileNotifyClientToServer.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle);
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.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
//--------------------------------------------------------------------------------------------------------------------
public interface IPacketTileNotifyReceiver
{
default void onServerPacketReceived(CompoundNBT nbt) {}
default void onClientPacketReceived(PlayerEntity player, CompoundNBT nbt) {}
}
public static class PacketTileNotifyClientToServer
{
CompoundNBT 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(TileEntity te, CompoundNBT nbt)
{ if((te!=null) && (nbt!=null)) DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(te, nbt)); }
public PacketTileNotifyClientToServer()
{}
public PacketTileNotifyClientToServer(BlockPos pos, CompoundNBT nbt)
{ this.nbt = nbt; this.pos = pos; }
public PacketTileNotifyClientToServer(TileEntity te, CompoundNBT nbt)
{ this.nbt = nbt; pos = te.getPos(); }
public static PacketTileNotifyClientToServer parse(final PacketBuffer buf)
{ return new PacketTileNotifyClientToServer(buf.readBlockPos(), buf.readCompoundTag()); }
public static void compose(final PacketTileNotifyClientToServer pkt, final PacketBuffer buf)
{ buf.writeBlockPos(pkt.pos); buf.writeCompoundTag(pkt.nbt); }
public static class Handler
{
public static void handle(final PacketTileNotifyClientToServer pkt, final Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
PlayerEntity player = ctx.get().getSender();
World world = player.world;
if(world == null) return;
final TileEntity te = world.getTileEntity(pkt.pos);
if(!(te instanceof IPacketTileNotifyReceiver)) return;
((IPacketTileNotifyReceiver)te).onClientPacketReceived(ctx.get().getSender(), pkt.nbt);
});
ctx.get().setPacketHandled(true);
}
}
}
public static class PacketTileNotifyServerToClient
{
CompoundNBT nbt = null;
BlockPos pos = BlockPos.ZERO;
public static void sendToPlayer(PlayerEntity player, TileEntity te, CompoundNBT nbt)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (te==null) || (nbt==null)) return;
DEFAULT_CHANNEL.sendTo(new PacketTileNotifyServerToClient(te, nbt), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public PacketTileNotifyServerToClient()
{}
public PacketTileNotifyServerToClient(BlockPos pos, CompoundNBT nbt)
{ this.nbt=nbt; this.pos=pos; }
public PacketTileNotifyServerToClient(TileEntity te, CompoundNBT nbt)
{ this.nbt=nbt; pos=te.getPos(); }
public static PacketTileNotifyServerToClient parse(final PacketBuffer buf)
{ return new PacketTileNotifyServerToClient(buf.readBlockPos(), buf.readCompoundTag()); }
public static void compose(final PacketTileNotifyServerToClient pkt, final PacketBuffer buf)
{ buf.writeBlockPos(pkt.pos); buf.writeCompoundTag(pkt.nbt); }
public static class Handler
{
public static void handle(final PacketTileNotifyServerToClient pkt, final Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
World world = SidedProxy.getWorldClientSide();
if(world == null) return;
final TileEntity te = world.getTileEntity(pkt.pos);
if(!(te instanceof IPacketTileNotifyReceiver)) return;
((IPacketTileNotifyReceiver)te).onServerPacketReceived(pkt.nbt);
});
ctx.get().setPacketHandled(true);
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// (GUI) Container synchrsonisation
//--------------------------------------------------------------------------------------------------------------------
public interface INetworkSynchronisableContainer
{
void onServerPacketReceived(int windowId, CompoundNBT nbt);
void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt);
}
public static class PacketContainerSyncClientToServer
{
int id = -1;
CompoundNBT nbt = null;
public static void sendToServer(int windowId, CompoundNBT 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.windowId, nbt)); }
public PacketContainerSyncClientToServer()
{}
public PacketContainerSyncClientToServer(int id, CompoundNBT nbt)
{ this.nbt = nbt; this.id = id; }
public static PacketContainerSyncClientToServer parse(final PacketBuffer buf)
{ return new PacketContainerSyncClientToServer(buf.readInt(), buf.readCompoundTag()); }
public static void compose(final PacketContainerSyncClientToServer pkt, final PacketBuffer buf)
{ buf.writeInt(pkt.id); buf.writeCompoundTag(pkt.nbt); }
public static class Handler
{
public static void handle(final PacketContainerSyncClientToServer pkt, final Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
PlayerEntity player = ctx.get().getSender();
if(!(player.openContainer instanceof INetworkSynchronisableContainer)) return;
if(player.openContainer.windowId != pkt.id) return;
((INetworkSynchronisableContainer)player.openContainer).onClientPacketReceived(pkt.id, player,pkt.nbt);
});
ctx.get().setPacketHandled(true);
}
}
}
public static class PacketContainerSyncServerToClient
{
int id = -1;
CompoundNBT nbt = null;
public static void sendToPlayer(PlayerEntity player, int windowId, CompoundNBT nbt)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (nbt==null)) return;
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(windowId, nbt), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public static void sendToPlayer(PlayerEntity player, Container container, CompoundNBT nbt)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer) || (nbt==null)) return;
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(container.windowId, nbt), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public static <C extends Container & INetworkSynchronisableContainer>
void sendToListeners(World world, C container, CompoundNBT nbt)
{
for(PlayerEntity player: world.getPlayers()) {
if(player.openContainer.windowId != container.windowId) continue;
sendToPlayer(player, container.windowId, nbt);
}
}
public PacketContainerSyncServerToClient()
{}
public PacketContainerSyncServerToClient(int id, CompoundNBT nbt)
{ this.nbt=nbt; this.id=id; }
public static PacketContainerSyncServerToClient parse(final PacketBuffer buf)
{ return new PacketContainerSyncServerToClient(buf.readInt(), buf.readCompoundTag()); }
public static void compose(final PacketContainerSyncServerToClient pkt, final PacketBuffer buf)
{ buf.writeInt(pkt.id); buf.writeCompoundTag(pkt.nbt); }
public static class Handler
{
public static void handle(final PacketContainerSyncServerToClient pkt, final Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
PlayerEntity player = SidedProxy.getPlayerClientSide();
if(!(player.openContainer instanceof INetworkSynchronisableContainer)) return;
if(player.openContainer.windowId != pkt.id) return;
((INetworkSynchronisableContainer)player.openContainer).onServerPacketReceived(pkt.id,pkt.nbt);
});
ctx.get().setPacketHandled(true);
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// Main window GUI text message
//--------------------------------------------------------------------------------------------------------------------
public static class OverlayTextMessage
{
public static final int DISPLAY_TIME_MS = 3000;
private static BiConsumer<ITextComponent, Integer> handler_ = null;
private ITextComponent data_;
private int delay_ = DISPLAY_TIME_MS;
private ITextComponent data() { return data_; }
private int delay() { return delay_; }
public static void setHandler(BiConsumer<ITextComponent, Integer> handler)
{ if(handler_==null) handler_ = handler; }
public static void sendToPlayer(PlayerEntity player, ITextComponent message, int delay)
{
if((!(player instanceof ServerPlayerEntity)) || (player instanceof FakePlayer)) return;
DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayerEntity)player).connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
}
public OverlayTextMessage()
{ data_ = new TranslationTextComponent("[unset]"); }
public OverlayTextMessage(final ITextComponent tct, int delay)
{ data_ = (ITextComponent)tct.func_230532_e_(); delay_ = delay; }
public static OverlayTextMessage parse(final PacketBuffer buf)
{
try {
return new OverlayTextMessage((ITextComponent)buf.readTextComponent(), DISPLAY_TIME_MS);
} catch(Throwable e) {
return new OverlayTextMessage(new TranslationTextComponent("[incorrect translation]"), DISPLAY_TIME_MS);
}
}
public static void compose(final OverlayTextMessage pkt, final PacketBuffer buf)
{
try {
buf.writeTextComponent(pkt.data());
} catch(Throwable e) {
Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e.toString());
}
}
public static class Handler
{
public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx)
{
if(handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay()));
ctx.get().setPacketHandled(true);
}
}
}
}

View file

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

View file

@ -0,0 +1,115 @@
/*
* @file Overlay.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Renders status messages in one line.
*/
package wile.engineersdecor.libmc.detail;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.MainWindow;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Mod;
public class Overlay
{
public static void register()
{
if(SidedProxy.mc() != null) {
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(PlayerEntity player, final ITextComponent message, int delay)
{ Networking.OverlayTextMessage.sendToPlayer(player, message, delay); }
// -----------------------------------------------------------------------------
// Client side handler
// -----------------------------------------------------------------------------
@Mod.EventBusSubscriber(Dist.CLIENT)
@OnlyIn(Dist.CLIENT)
public static class TextOverlayGui extends AbstractGui
{
private static double overlay_y_ = 0.75;
private static int text_color_ = 0x00ffaa00;
private static int border_color_ = 0xaa333333;
private static int background_color1_ = 0xaa333333;
private static int background_color2_ = 0xaa444444;
private final Minecraft mc;
private static long deadline_;
private static String text_;
public static void on_config(double overlay_y)
{
overlay_y_ = overlay_y;
// currently const, just to circumvent "useless variable" warnings
text_color_ = 0x00ffaa00;
border_color_ = 0xaa333333;
background_color1_ = 0xaa333333;
background_color2_ = 0xaa444444;
}
public static synchronized String text()
{ return text_; }
public static synchronized long deadline()
{ return deadline_; }
public static synchronized void hide()
{ deadline_ = 0; text_ = ""; }
public static synchronized void show(ITextComponent s, int displayTimeoutMs)
{ text_ = (s==null)?(""):(s.getString()); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
public static synchronized void show(String s, int displayTimeoutMs)
{ text_ = (s == null) ? ("") : (s); deadline_ = System.currentTimeMillis() + displayTimeoutMs; }
TextOverlayGui()
{ super(); mc = SidedProxy.mc(); }
@SubscribeEvent
public void onRenderGui(RenderGameOverlayEvent.Post event)
{
if(event.getType() != RenderGameOverlayEvent.ElementType.CHAT) return;
if(deadline() < System.currentTimeMillis()) return;
String txt = text();
MatrixStack mxs = event.getMatrixStack();
if(txt.isEmpty()) return;
final MainWindow win = mc.getMainWindow();
final FontRenderer fr = mc.fontRenderer;
final boolean was_unicode = fr.getBidiFlag();
try {
final int cx = win.getScaledWidth() / 2;
final int cy = (int)(win.getScaledHeight() * overlay_y_);
final int w = fr.getStringWidth(txt);
final int h = fr.FONT_HEIGHT;
func_238468_a_(mxs,cx-(w/2)-3, cy-2, cx+(w/2)+2, cy+h+2, 0xaa333333, 0xaa444444);
func_238465_a_(mxs,cx-(w/2)-3, cx+(w/2)+2, cy-2, 0xaa333333);
func_238465_a_(mxs,cx-(w/2)-3, cx+(w/2)+2, cy+h+2, 0xaa333333);
func_238473_b_(mxs,cx-(w/2)-3, cy-2, cy+h+2, 0xaa333333);
func_238473_b_(mxs,cx+(w/2)+2, cy-2, cy+h+2, 0xaa333333);
func_238471_a_(mxs, fr, txt, cx , cy+1, 0x00ffaa00); // drawCenteredString
} finally {
; // fr.setBidiFlag(was_unicode);
}
}
}
}

View file

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

View file

@ -0,0 +1,103 @@
/*
* @file Tooltip.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2018 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Delayed tooltip for a selected area. Constructed with a
* GUI, invoked in `render()`.
*/
package wile.engineersdecor.libmc.detail;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.inventory.container.Container;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@OnlyIn(Dist.CLIENT)
public class TooltipDisplay
{
private static long default_delay = 1500;
private static int default_max_deviation = 1;
public static void config(long delay, int max_deviation)
{
default_delay = MathHelper.clamp(delay, 500, 5000);
default_max_deviation = MathHelper.clamp(max_deviation, 1, 5);
}
// ---------------------------------------------------------------------------------------------------
public static class TipRange
{
public final int x0,y0,x1,y1;
public final ITextComponent text;
public TipRange(int x, int y, int w, int h, ITextComponent text)
{ 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 long delay = default_delay;
private int max_deviation = default_max_deviation;
private int x_last, y_last;
private long t;
public TooltipDisplay()
{ t = System.currentTimeMillis(); }
public void init(List<TipRange> ranges, long delay_ms, int max_deviation_xy)
{
this.ranges = ranges;
this.delay = delay_ms;
this.max_deviation = max_deviation_xy;
t = System.currentTimeMillis();
x_last = y_last = 0;
}
public void init(List<TipRange> ranges)
{ init(ranges, default_delay, default_max_deviation); }
public void init(TipRange... ranges)
{ init(Arrays.asList(ranges), default_delay, default_max_deviation); }
public void resetTimer()
{ t = System.currentTimeMillis(); }
public <T extends Container> boolean render(MatrixStack mx, final ContainerScreen<T> gui, int x, int y)
{
if((Math.abs(x-x_last) > max_deviation) || (Math.abs(y-y_last) > max_deviation)) {
x_last = x;
y_last = y;
resetTimer();
return false;
} else if(Math.abs(System.currentTimeMillis()-t) < delay) {
return false;
} else if(ranges.stream().noneMatch(
(tip)->{
if((x<tip.x0) || (x>tip.x1) || (y<tip.y0) || (y>tip.y1)) return false;
String text = tip.text.getString();
if(!text.isEmpty() && (!text.startsWith("block."))) {
gui.renderToolTip(mx, Collections.singletonList(tip.text), x, y, Minecraft.getInstance().fontRenderer);
}
return true;
})
){
resetTimer();
return false;
} else {
return true;
}
}
}

View file

@ -0,0 +1,30 @@
# @file mods.toml
# @spec TOML v0.5.0 (https://github.com/toml-lang/toml)
modLoader="javafml"
loaderVersion="[28,)"
issueTrackerURL="https://github.com/stfwi/engineers-decor/issues/"
[[mods]]
modId="engineersdecor"
version="${file.jarVersion}"
displayName="Engineer's Decor"
description="Adds cosmetic blocks for the engineer's workshop, factory and home."
authors="wilechaote"
credits="BluSunrize, Damien Hazard, malte0811, et al., the Forge Smiths, the Modders of the World."
updateJSONURL="https://raw.githubusercontent.com/stfwi/engineers-decor/develop/1.16/meta/update.json"
displayURL="https://github.com/stfwi/engineers-decor/"
logoFile="logo.png"
[[dependencies.engineersdecor]]
modId="forge"
mandatory=true
versionRange="[32.0.75,)"
ordering="NONE"
side="BOTH"
[[dependencies.engineersdecor]]
modId="minecraft"
mandatory=true
versionRange="[1.16.1]"
ordering="NONE"
side="BOTH"

View file

@ -0,0 +1,14 @@
{
"variants": {
"": [
{ "model": "engineersdecor:block/brick/clinker_brick_model0" },
{ "model": "engineersdecor:block/brick/clinker_brick_model1" },
{ "model": "engineersdecor:block/brick/clinker_brick_model2" },
{ "model": "engineersdecor:block/brick/clinker_brick_model3" },
{ "model": "engineersdecor:block/brick/clinker_brick_model4" },
{ "model": "engineersdecor:block/brick/clinker_brick_model5" },
{ "model": "engineersdecor:block/brick/clinker_brick_model6" },
{ "model": "engineersdecor:block/brick/clinker_brick_model7" }
]
}
}

View file

@ -0,0 +1,16 @@
{
"variants": {
"tvariant=0,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s0v0_model" },
"tvariant=1,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s0v1_model" },
"tvariant=2,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s0v2_model" },
"tvariant=3,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s0v3_model" },
"tvariant=0,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s1v0_model" },
"tvariant=1,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s1v1_model" },
"tvariant=2,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s1v2_model" },
"tvariant=3,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s1v3_model" },
"tvariant=0,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s2v0_model" },
"tvariant=1,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s2v1_model" },
"tvariant=2,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s2v2_model" },
"tvariant=3,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_slab_s2v3_model" }
}
}

View file

@ -0,0 +1,14 @@
{
"variants": {
"": [
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model0" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model1" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model2" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model3" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model4" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model5" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model6" },
{ "model": "engineersdecor:block/brick/clinker_brick_stained_model7" }
]
}
}

View file

@ -0,0 +1,16 @@
{
"variants": {
"tvariant=0,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s0v0_model" },
"tvariant=1,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s0v1_model" },
"tvariant=2,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s0v2_model" },
"tvariant=3,type=bottom": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s0v3_model" },
"tvariant=0,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s1v0_model" },
"tvariant=1,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s1v1_model" },
"tvariant=2,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s1v2_model" },
"tvariant=3,type=top": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s1v3_model" },
"tvariant=0,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s2v0_model" },
"tvariant=1,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s2v1_model" },
"tvariant=2,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s2v2_model" },
"tvariant=3,type=double": { "model": "engineersdecor:block/slab/specific/clinker_brick_stained_slab_s2v3_model" }
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,14 @@
{
"variants": {
"": [
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model0" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model1" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model2" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model3" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model4" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model5" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model6" },
{ "model": "engineersdecor:block/soil/dense_grit_dirt_model7" }
]
}
}

View file

@ -0,0 +1,14 @@
{
"variants": {
"": [
{ "model": "engineersdecor:block/soil/dense_grit_sand_model0" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model1" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model2" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model3" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model4" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model5" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model6" },
{ "model": "engineersdecor:block/soil/dense_grit_sand_model7" }
]
}
}

View file

@ -0,0 +1,16 @@
{
"variants": {
"facing=north,open=false": { "model": "engineersdecor:block/device/factory_dropper_model" },
"facing=south,open=false": { "model": "engineersdecor:block/device/factory_dropper_model", "y":180 },
"facing=west,open=false": { "model": "engineersdecor:block/device/factory_dropper_model", "y":270 },
"facing=east,open=false": { "model": "engineersdecor:block/device/factory_dropper_model", "y":90 },
"facing=up,open=false": { "model": "engineersdecor:block/device/factory_dropper_model", "x":270 },
"facing=down,open=false": { "model": "engineersdecor:block/device/factory_dropper_model", "x":90 },
"facing=north,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open" },
"facing=south,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open", "y":180 },
"facing=west,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open", "y":270 },
"facing=east,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open", "y":90 },
"facing=up,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open", "x":270 },
"facing=down,open=true": { "model": "engineersdecor:block/device/factory_dropper_model_open", "x":90 }
}
}

View file

@ -0,0 +1,10 @@
{
"variants": {
"facing=north": { "model": "engineersdecor:block/device/factory_hopper_model" },
"facing=south": { "model": "engineersdecor:block/device/factory_hopper_model", "y":180 },
"facing=west": { "model": "engineersdecor:block/device/factory_hopper_model", "y":270 },
"facing=east": { "model": "engineersdecor:block/device/factory_hopper_model", "y":90 },
"facing=up": { "model": "engineersdecor:block/device/factory_hopper_model_up" },
"facing=down": { "model": "engineersdecor:block/device/factory_hopper_model_down" }
}
}

View file

@ -0,0 +1,10 @@
{
"variants": {
"facing=north": { "model": "engineersdecor:block/device/factory_placer_model" },
"facing=south": { "model": "engineersdecor:block/device/factory_placer_model", "y":180 },
"facing=west": { "model": "engineersdecor:block/device/factory_placer_model", "y":270 },
"facing=east": { "model": "engineersdecor:block/device/factory_placer_model", "y":90 },
"facing=up": { "model": "engineersdecor:block/device/factory_placer_model", "x":270 },
"facing=down": { "model": "engineersdecor:block/device/factory_placer_model", "x":90 }
}
}

View file

@ -0,0 +1,34 @@
{
"variants": {
"facing=north,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0" },
"facing=south,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0", "y":180 },
"facing=west,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0", "y":270 },
"facing=east,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0", "y":90 },
"facing=up,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0", "x":270 },
"facing=down,level=0": { "model": "engineersdecor:block/pipe/fluid_barrel_model0", "x":90 },
"facing=north,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1" },
"facing=south,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1", "y":180 },
"facing=west,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1", "y":270 },
"facing=east,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1", "y":90 },
"facing=up,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1", "x":270 },
"facing=down,level=1": { "model": "engineersdecor:block/pipe/fluid_barrel_model1", "x":90 },
"facing=north,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2" },
"facing=south,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2", "y":180 },
"facing=west,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2", "y":270 },
"facing=east,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2", "y":90 },
"facing=up,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2", "x":270 },
"facing=down,level=2": { "model": "engineersdecor:block/pipe/fluid_barrel_model2", "x":90 },
"facing=north,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3" },
"facing=south,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3", "y":180 },
"facing=west,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3", "y":270 },
"facing=east,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3", "y":90 },
"facing=up,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3", "x":270 },
"facing=down,level=3": { "model": "engineersdecor:block/pipe/fluid_barrel_model3", "x":90 },
"facing=north,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4" },
"facing=south,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4", "y":180 },
"facing=west,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4", "y":270 },
"facing=east,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4", "y":90 },
"facing=up,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4", "x":270 },
"facing=down,level=4": { "model": "engineersdecor:block/pipe/fluid_barrel_model4", "x":90 }
}
}

View file

@ -0,0 +1,14 @@
{
"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

@ -0,0 +1,16 @@
{
"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

@ -0,0 +1,44 @@
{
"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

@ -0,0 +1,13 @@
{
"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

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_clinker_brick_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_concrete_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"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

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_rebar_concrete_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_aluminum_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_copper_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_gold_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_iron_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_sheetmetal_steel_se_model" }
}
}

View file

@ -0,0 +1,19 @@
{
"variants": {
"parts=0": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s0_model" },
"parts=1": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s1_model" },
"parts=2": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s2_model" },
"parts=3": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s3_model" },
"parts=4": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s4_model" },
"parts=5": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s5_model" },
"parts=6": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s6_model" },
"parts=7": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s7_model" },
"parts=8": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s8_model" },
"parts=9": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_s9_model" },
"parts=10": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_sa_model" },
"parts=11": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_sb_model" },
"parts=12": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_sc_model" },
"parts=13": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_sd_model" },
"parts=14": { "model": "engineersdecor:block/slab/specific/halfslab_treated_wood_se_model" }
}
}

View file

@ -0,0 +1,10 @@
{
"variants": {
"facing=north": { "model": "engineersdecor:block/light/bulb_light_model" },
"facing=south": { "model": "engineersdecor:block/light/bulb_light_model", "y":180 },
"facing=west": { "model": "engineersdecor:block/light/bulb_light_model", "y":270 },
"facing=east": { "model": "engineersdecor:block/light/bulb_light_model", "y":90 },
"facing=up": { "model": "engineersdecor:block/light/bulb_light_model", "x":270 },
"facing=down": { "model": "engineersdecor:block/light/bulb_light_model", "x":90 }
}
}

View file

@ -0,0 +1,10 @@
{
"variants": {
"facing=north": { "model": "engineersdecor:block/light/ceiling_edge_light_model" },
"facing=south": { "model": "engineersdecor:block/light/ceiling_edge_light_model", "y":180 },
"facing=west": { "model": "engineersdecor:block/light/ceiling_edge_light_model", "y":270 },
"facing=east": { "model": "engineersdecor:block/light/ceiling_edge_light_model", "y":90 },
"facing=up": { "model": "engineersdecor:block/light/ceiling_edge_light_model", "x":270 },
"facing=down": { "model": "engineersdecor:block/light/ceiling_edge_light_model", "x":90 }
}
}

View file

@ -0,0 +1,10 @@
{
"variants": {
"facing=north": { "model": "engineersdecor:block/light/floor_edge_light_model" },
"facing=south": { "model": "engineersdecor:block/light/floor_edge_light_model", "y":180 },
"facing=west": { "model": "engineersdecor:block/light/floor_edge_light_model", "y":270 },
"facing=east": { "model": "engineersdecor:block/light/floor_edge_light_model", "y":90 },
"facing=up": { "model": "engineersdecor:block/light/floor_edge_light_model", "x":270 },
"facing=down": { "model": "engineersdecor:block/light/floor_edge_light_model", "x":90 }
}
}

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