53 Commits
master ... 1.21

Author SHA1 Message Date
Christian Knaapen
64b0fbc0aa Updated dependencies 2025-03-22 11:47:57 +01:00
Mrbysco
9476ecbcc5 Initial port to 1.21.1
TODO:
* Fix the ghost block rendering
* Fix the placement canceling on setting coordinates
2025-03-01 15:09:01 +01:00
Mrbysco
778ae7ddd9 Port to 1.20.4 2024-11-20 17:06:19 +01:00
Mrbysco
08c3b4b42d Switch Power Level to a player capability 2023-11-21 20:22:20 +01:00
Mrbysco
28d47a0cbb Fix textures not being recognized
Mojang no longer automatically loads textures in `textures/items` as they now load textures from `textures/item`
2023-11-21 02:48:06 +01:00
Mrbysco
00cb48b929 Fix loot modifiers
altenative was renamed to any_of
2023-11-21 02:46:50 +01:00
Mrbysco
b75cfc45d8 Fix missing calls 2023-11-18 20:49:10 +01:00
Mrbysco
ca44da71e7 Fresh port to 1.20
Note: I changed `BuildModeCategoryEnum` to actually use the Color class instead of using Vector4f. Same counts for `RadialMenu`
2023-11-18 00:38:56 +01:00
Christian Knaapen
8570bde282 Fixed not being able to load when the newest version of Create is installed. 2023-10-22 13:38:47 +02:00
Christian Knaapen
5fb2edb97a Trying to fix flywheel dependency some more. 2023-07-18 22:46:09 +02:00
Christian Knaapen
0a9b764d3f Fixed build.gradle referencing older flywheel.
Updated logo, pack version
2023-07-18 21:16:49 +02:00
Christian Knaapen
82ee3eefaa Fixed not being able to load mod with latest Create mod version: Updated Flyweel dependency to 0.6.9. Updated russian translation. 2023-07-07 21:27:35 +02:00
Christian Knaapen
f033c261d7 Fixed not being able to place blocks if placement reach is 0, falls back to vanilla reach now. 2023-06-28 16:28:01 +02:00
Christian Knaapen
0dad523e13 Updated icons 2023-06-25 20:00:14 +02:00
Christian Knaapen
9c9e4d04b7 Added power level indicator with extensive tooltip to radial menu. 2023-06-24 22:40:12 +02:00
Christian Knaapen
8288e5db97 Added powerlevel command. Updated reach upgrade behaviour. 2023-06-24 21:13:02 +02:00
Christian Knaapen
9e680fc2dc Added power level items and their loot modifiers. 2023-06-24 16:45:36 +02:00
Christian Knaapen
33033666b9 Renamed reach to power level. Separated reach config for more control.
Added draft icons for elastic hand, muscles and building technique book.
Separated BlockUtilities clientside.
2023-06-11 20:40:39 +02:00
Christian Knaapen
594b5e1c0d Decreasing itemstacks in survival. Disabled replacing blocks in survival. Expanded config options for survival.
Added keybinds to switch to previous build mode, and to switch between disabled and previous.
Updated RandomizerBags to use new placement method in vanilla mode. Removed SurvivalHelper placement methods.
2023-06-11 17:23:01 +02:00
Christian Knaapen
410d154276 Enabled for survival. Calculating missing items. Showing itemstacks at cursor. Showing preview blocks in red.
Increased max blocks placed at once.
Filtering coordinates that are not loaded or outside of world border.
Sending items instead of itemstacks to server.
2023-05-18 18:46:23 +02:00
Christian Knaapen
6046b9f5fa Updated russian translation. 2023-04-21 18:12:03 +02:00
Christian Knaapen
a7dd0823e1 Fixed redo and running on server. 2023-02-08 20:34:05 +01:00
Christian Knaapen
df1efef63b WIP UndoRedo overhaul continued.
More validation in ServerBlockPlacer.
Added BlockPlacerHelper.
2023-02-05 13:22:41 +01:00
Christian Knaapen
23b8c0c9fc WIP UndoRedo overhaul.
Fixed concurrent modification issue in ServerBlockPlacer.
2023-02-04 00:30:30 +01:00
Christian Knaapen
bda0908731 Added server validation: allowInSurvival and whitelist.
Instantly placing blocksets of size 1.
Added server config and moved some config values around.
2023-02-03 21:14:29 +01:00
Christian Knaapen
589059fd44 Better text drawing in radial menu using tooltips.
Hold shift for description.
Fixed keybind text.
2023-02-03 20:00:08 +01:00
Christian Knaapen
07667efe98 Icons for modifier settings and replace modes. Rendering mirror lines and areas in world.
Removed shaders.
2023-02-02 00:45:21 +01:00
Christian Knaapen
d4cb0c6858 Radial mirror GUI, and saving modifiers.
Saving modifiers in player persistent data on serverside, and loading when new player joins. This is separate per world.
Modifier GUI panels are a bit larger now, and position fields are wider.
2023-02-01 20:54:48 +01:00
Christian
953a3fe923 Fixed several issues. Mirroring vertically.
Modifier panel up/down buttons updating.
Updating neighbouring blocks when placing blocks.
Removed separate icon files.
2023-01-30 01:37:59 +01:00
Christian
420b06d414 Modifier settings GUI for array and mirror, with header.
New parts in modifiers gui texture. 
Added reordering modifiers.
Replaced all icon rendering with AllIcons class.
Delayed blockset placing in server.
Modifiers now work when buildmode is DISABLED.
Modifiers now work with breaking when buildmode would break a single block.
Modifiers screen renders tooltip on top of everything.
New labeledScrollInput class, extension of Create's ScrollInput.
BlockPreviews ignores config settings to hide itself when placing more than 1 block.
Fixed mirroring wrong horizontal axis.
2023-01-29 23:57:47 +01:00
Christian
c46ae91e99 Created icon sheet. 2023-01-28 20:29:15 +01:00
Christian
2a84c7ca41 WIP Continued modifier settings gui overhaul 2023-01-28 18:19:57 +01:00
Christian
98ebc61bb8 WIP Modifier settings gui overhaul 2023-01-28 02:08:19 +01:00
Christian Knaapen
ed2609f97e Overhauled modifiers to run clientside only. BuilderChain applies rotation and mirror to blockstates.
Removed ModifierSettingsManager, ModifierSettingsMessage and capabilities.
2023-01-26 02:09:50 +01:00
Christian Knaapen
a760565b66 Added gui classes and some textures from Create. 2023-01-24 23:46:45 +01:00
Christian Knaapen
4ea34fc854 Added buttons in radial menu for replace modes. Implemented/fixed replace modes.
Made ModeOptions clientside and UndoRedo serverside.
New config option: only show previews when building, default true.
Overhauled drawOutlinesIfNoBlockInHand, now drawOutlineAtBreakPosition.
Fixed not being able to break blocks.
2023-01-24 02:06:43 +01:00
Christian Knaapen
af4ef73375 Using BlockSets. Added filtering and several replace settings.
Overhauled build.gradle based on Create. Added flywheel as jarInJar, no external dependency anymore. New mappings channel from parchment.
Added replace modes: air, air and solid, solid, offhand filtered. Offhand filtering looks in randomizer bag as well.
Protecting tile entities by default.
Other mods can cancel placement through BlockEvent.OnBlockPlaced, such as FTBChunks.
BlockPreviews.drawOutlinesIfNoBlockInHand overhaul.
2023-01-22 20:36:48 +01:00
Christian Knaapen
ae75e48c01 Added sounds and renewing randomizer bag at right times.
Added text with instruction under actionbar while placing/breaking.
2023-01-22 03:29:37 +01:00
Christian Knaapen
ef687c0658 Overhaul continues continuing.
Saving data and serverside state through persistent player data.
Overhauled block previews.
Removed mode capability.
Added create item and network classes.
Using create BlockHelper to place and break blocks.
2023-01-22 00:11:28 +01:00
Christian Knaapen
617fc82114 Overhaul continues.
Updated build modes, determining blockstates.
Everything compiles and runs.
2023-01-21 01:41:00 +01:00
Christian Knaapen
349c673553 WIP Overhaul
Block calculation only on client, then send to server to place.
BuilderChain system to decouple buildmodes, buildmodifiers and randomizer bag.
2023-01-20 22:26:18 +01:00
Christian Knaapen
91834f9b60 Renamed helper package to utilities. Added systems. Removed direction parameter when placing blocks. 2023-01-18 22:02:19 +01:00
Christian Knaapen
cce5e2e28d Several tweaks 2023-01-18 19:28:42 +01:00
Christian Knaapen
fe69a81622 Merge branch 'create-utilities' into 1.19 2023-01-17 22:42:12 +01:00
Christian Knaapen
5f27eedde2 Keeping outlines until animation is done. Fix itemstack not found error when undoing in creative.
Disabled create test code.
2023-01-17 22:41:38 +01:00
Christian Knaapen
cd3afbbb82 Overhauled block previews to work with ticks. Added place and break animation.
Added DelayedBlockPlacer to play appear animation, then place.
Split BuildConfig into CommonConfig and ClientConfig.
Fixed itemstack not found error in creative.
Added scale and color options to ghost blocks.
2023-01-17 02:30:08 +01:00
Christian Knaapen
fa1aadcd86 Using Create outlines and ghost blocks for block previews.
Fixed item names.
2023-01-16 01:53:09 +01:00
Christian Knaapen
c2d413fad0 Added missing resources.
Modified mods.toml.
Fixed crash and double rendering of modifiers.
2023-01-15 23:16:22 +01:00
Christian Knaapen
064973b07b Added code from Create. Added Flywheel dependency.
Restructured client and mod events to match create.
Copied accesstransformer. Added Create, CreateClient, ClientEvents and CommonEvents proxies.
2023-01-15 22:21:50 +01:00
Christian Knaapen
ea59fef099 Changed reach upgrade recipes. Made radial menu smaller.
Cleaned up radial menu code.
Added russian language.
Tweaked cube icon.
2023-01-14 15:35:43 +01:00
Christian Knaapen
5e6c42d372 Fixed disassembling blocks with the Create Wrench duplicating the item when a build mode other than Normal is used. Disabled break outlines in survival, because breaking multiple blocks in survival is deprecated. 2022-10-25 21:52:14 +02:00
Christian Knaapen
14460c2c01 Fixed keybindings not being registered. 2022-10-24 21:01:19 +02:00
Christian Knaapen
3b31a74780 Merge branch 'master' into 1.19 2022-07-30 10:48:08 +02:00
289 changed files with 14191 additions and 7006 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

6
.gitignore vendored
View File

@@ -20,7 +20,7 @@ build
# other
eclipse
run
logs/*
runs
run-data
# Files from Forge MDK
forge*changelog.txt
repo

View File

@@ -1,140 +1,193 @@
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
mavenCentral()
plugins {
id 'java-library'
id 'eclipse'
id 'idea'
id 'maven-publish'
id 'net.neoforged.gradle.userdev' version "${neogradle_version}"
id 'com.matthewprenger.cursegradle' version "${cursegradle_version}"
}
boolean ponderInWorkspace = findProject(':Ponder') != null
ext.buildNumber = System.getenv('BUILD_NUMBER')
version = mod_version
group = 'nl.requios.effortlessbuilding'
base {
archivesName = "effortlessbuilding-${artifact_minecraft_version}"
}
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + ' (' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
runs {
// applies to all the run configs below
configureEach { net.neoforged.gradle.dsl.common.runs.run.Run run ->
// Recommended logging data for a userdev environment
systemProperty 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
systemProperty 'forge.logging.console.level', 'debug'
//Limit ram usage for the dev environment to 4GB
jvmArgument '-Xmx4G'
if (run.project.javaToolchains.launcherFor(java.toolchain).map { it.metadata.vendor }.getOrElse("").contains("JetBrains")) {
jvmArgument '-XX:+AllowEnhancedClassRedefinition'
}
modSource project.sourceSets.main
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1+', changing: true
client {
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'neoforge.enabledGameTestNamespaces', "effortlessbuilding"
if (ponderInWorkspace) {
dependencies {
runtime project(':Ponder')
}
}
}
server {
systemProperty 'neoforge.enabledGameTestNamespaces', "effortlessbuilding"
programArgument '--nogui'
}
data {
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
// workingDirectory project.file('run-data')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
programArguments.addAll '--mod', "effortlessbuilding", '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
}
}
apply plugin: 'net.minecraftforge.gradle'
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
version = '1.18-2.34'
group = 'nl.requios.effortlessbuilding' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'effortlessbuilding'
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
minecraft {
// The mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD Snapshot are built nightly.
// stable_# Stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: 'official', version: '1.19'
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP)
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
mods {
effortlessbuilding {
source sourceSets.main
}
repositories {
maven {
// location of the maven for Registrate and Flywheel
name = 'tterrag maven'
url = 'https://maven.tterrag.com'
}
maven {
// Ponder, Flywheel
url = "https://maven.createmod.net"
}
maven { url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven" } // NeoForge config api port, needed by ponder
exclusiveContent {
forRepository {
maven {
url "https://cursemaven.com"
}
}
server {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP)
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
mods {
effortlessbuilding {
source sourceSets.main
}
}
filter {
includeGroup "curse.maven"
}
data {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP)
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
mods {
effortlessbuilding {
source sourceSets.main
}
}
}
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
mavenLocal()
flatDir {
dirs 'libs'
}
}
dependencies {
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft 'net.minecraftforge:forge:1.19-41.0.99'
implementation "net.neoforged:neoforge:${neo_version}"
// Real mod deobf dependency examples - these get remapped to your current mappings
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}:api") // Adds JEI API as a compile dependency
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}:${jei_version}") // Adds the full JEI mod as a runtime dependency
// implementation fg.deobf("com.tterrag.registrate:Registrate:MC${mc_version}-${registrate_version}") // Adds registrate as a dependency
jarJar("net.createmod.ponder:Ponder-NeoForge-${minecraft_version}:${ponder_version}")
// Examples using mod jars from ./libs
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
// For more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
compileOnly("dev.engine-room.flywheel:flywheel-neoforge-api-${flywheel_minecraft_version}:${flywheel_version}")
runtimeOnly(jarJar("dev.engine-room.flywheel:flywheel-neoforge-${flywheel_minecraft_version}:${flywheel_version}") {
version {
strictly "[1.0,2.0)"
prefer flywheel_version
}
})
runtimeOnly("dev.engine-room.vanillin:vanillin-neoforge-${flywheel_minecraft_version}:${vanillin_version}")
if (ponderInWorkspace) {
implementation(project(":ponder:Common"))
implementation(project(":ponder:NeoForge"))
} else {
implementation("net.createmod.ponder:Ponder-NeoForge-${minecraft_version}:${ponder_version}")
}
}
sourceSets.main.resources {
srcDir 'src/generated/resources'
exclude '.cache/'
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
idea {
module {
downloadSources = true
downloadJavadoc = true
}
}
compileJava {
options.compilerArgs = ['-Xdiags:verbose']
}
// Example for how to get properties into the manifest for reading by the runtime..
jar {
manifest {
attributes([
"Specification-Title": "effortlessbuilding",
"Specification-Vendor": "requios",
"Specification-Version": "1", // We are version 1 of ourselves
"Specification-Version": "1",
"Implementation-Title": project.name,
"Implementation-Version": "${version}",
"Implementation-Version": project.jar.archiveVersion,
"Implementation-Vendor" :"requios",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
// Example configuration to allow publishing using the maven-publish task
// This is the preferred method to reobfuscate your jar file
jar.finalizedBy('reobfJar')
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
//publish.dependsOn('reobfJar')
task jarJarRelease {
group = 'jarjar'
doLast {
tasks.jarJar {
classifier = ''
}
}
finalizedBy tasks.jarJar
}
java {
withSourcesJar()
withJavadocJar()
}
tasks.build.dependsOn tasks.jarJar
publishing {
publications {
mavenJava(MavenPublication) {
artifact jar
artifactId = base.archivesName.get()
from components.java
// fg.component(it)
}
}
repositories {
maven {
url "file:///${project.projectDir}/mcmodsrepo"
if (project.hasProperty('mavendir')) {
maven { url mavendir }
}
}
}

View File

@@ -2,3 +2,20 @@
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
mod_version = 3.10
artifact_minecraft_version = 1.21.1
minecraft_version = 1.21.1
neo_version = 21.1.129
neogradle.subsystems.parchment.minecraftVersion = 1.21.1
neogradle.subsystems.parchment.mappingsVersion = 2024.11.17
neogradle.subsystems.conventions.runs.create-default-run-per-type = false
neogradle_version = 7.0.181
cursegradle_version = 1.4.0
flywheel_minecraft_version = 1.21.1
flywheel_version = 1.0.2
vanillin_version = 1.0.0-beta-11
ponder_version = 1.0.46

Binary file not shown.

View File

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

54
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright <EFBFBD> 2015-2021 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -32,10 +34,10 @@
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,12 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,22 +134,29 @@ 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.
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +217,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

37
gradlew.bat vendored
View File

@@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 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
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

13
settings.gradle Normal file
View File

@@ -0,0 +1,13 @@
pluginManagement {
repositories {
gradlePluginPortal()
maven {
name = 'Official NeoForge Maven'
url = 'https://maven.neoforged.net/releases'
}
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
}

View File

@@ -0,0 +1,61 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.systems.RenderSystem;
import net.createmod.catnip.gui.UIRenderHelper;
import net.createmod.catnip.gui.element.ScreenElement;
import net.createmod.catnip.theme.Color;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
public enum AllGuiTextures implements ScreenElement {
ARRAY_ENTRY("modifiers", 226, 64),
MIRROR_ENTRY("modifiers", 0, 64, 226, 64),
RADIAL_MIRROR_ENTRY("modifiers", 0, 128, 226, 64),
ENABLE_BUTTON_BACKGROUND("modifiers", 234, 0, 9, 9),
CHECKMARK("modifiers", 243, 0, 10, 9),
ARROW_UP("modifiers", 234, 9, 9, 9),
ARROW_DOWN("modifiers", 243, 9, 9, 9),
TRASH("modifiers", 234, 18, 9, 9),
;
public final ResourceLocation location;
public int width, height;
public int startX, startY;
private AllGuiTextures(String location, int width, int height) {
this(location, 0, 0, width, height);
}
private AllGuiTextures(int startX, int startY) {
this("icons", startX * 16, startY * 16, 16, 16);
}
private AllGuiTextures(String location, int startX, int startY, int width, int height) {
this(EffortlessBuilding.MODID, location, startX, startY, width, height);
}
private AllGuiTextures(String namespace, String location, int startX, int startY, int width, int height) {
this.location = ResourceLocation.fromNamespaceAndPath(namespace, "textures/gui/" + location + ".png");
this.width = width;
this.height = height;
this.startX = startX;
this.startY = startY;
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
@OnlyIn(Dist.CLIENT)
@Override
public void render(GuiGraphics ms, int x, int y) {
ms.blit(location, x, y, 0, startX, startY, width, height, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(GuiGraphics ms, int x, int y, Color c) {
bind();
UIRenderHelper.drawColoredTexture(ms, c, x, y, startX, startY, width, height);
}
}

View File

@@ -0,0 +1,142 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.createmod.catnip.gui.element.DelegatedStencilElement;
import net.createmod.catnip.gui.element.ScreenElement;
import net.createmod.catnip.theme.Color;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.joml.Matrix4f;
public class AllIcons implements ScreenElement {
public static final ResourceLocation ICON_ATLAS = EffortlessBuilding.asResource("textures/gui/icons.png");
public static final int ICON_ATLAS_SIZE = 256;
private static int x = 0, y = -1;
private int iconX;
private int iconY;
public static final AllIcons
I_SETTINGS = newRow(),
I_UNDO = next(),
I_REDO = next(),
I_REPLACE = next(),
I_REPLACE_AIR = next(),
I_REPLACE_BLOCKS_AND_AIR = next(),
I_REPLACE_BLOCKS = next(),
I_REPLACE_OFFHAND_FILTERED = next(),
I_PROTECT_TILE_ENTITIES = next();
public static final AllIcons
I_DISABLE = newRow(),
I_SINGLE = next(),
I_LINE = next(),
I_WALL = next(),
I_FLOOR = next(),
I_CUBE = next(),
I_DIAGONAL_LINE = next(),
I_DIAGONAL_WALL = next(),
I_SLOPED_FLOOR = next(),
I_CIRCLE = next(),
I_CYLINDER = next(),
I_SPHERE = next(),
I_PYRAMID = next(),
I_CONE = next(),
I_DOME = next();
public static final AllIcons
I_NORMAL_SPEED = newRow(),
I_FAST_SPEED = next(),
I_FILLED = next(),
I_HOLLOW = next(),
I_CUBE_FILLED = next(),
I_CUBE_HOLLOW = next(),
I_CUBE_SKELETON = next(),
I_SHORT_EDGE = next(),
I_LONG_EDGE = next(),
I_CIRCLE_START_CORNER = next(),
I_CIRCLE_START_CENTER = next(),
I_THICKNESS_1 = next(),
I_THICKNESS_3 = next(),
I_THICKNESS_5 = next();
public static final AllIcons
I_PLAYER = newRow(),
I_BLOCK_CENTER = next(),
I_BLOCK_CORNER = next(),
I_HIDE_LINES = next(),
I_SHOW_LINES = next(),
I_HIDE_AREAS = next(),
I_SHOW_AREAS = next(),
I_X_OFF = next(),
I_X_ON = next(),
I_Y_OFF = next(),
I_Y_ON = next(),
I_Z_OFF = next(),
I_Z_ON = next(),
I_ALTERNATE_OFF = next(),
I_ALTERNATE_ON = next();
public AllIcons(int x, int y) {
iconX = x * 16;
iconY = y * 16;
}
private static AllIcons next() {
return new AllIcons(++x, y);
}
private static AllIcons newRow() {
return new AllIcons(x = 0, ++y);
}
@OnlyIn(Dist.CLIENT)
public void render(GuiGraphics guiGraphics, int x, int y) {
guiGraphics.blit(ICON_ATLAS, x, y, 0, iconX, iconY, 16, 16, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, MultiBufferSource buffer, int color) {
VertexConsumer builder = buffer.getBuffer(RenderType.textSeeThrough(ICON_ATLAS));
Matrix4f matrix = ms.last().pose();
Color rgb = new Color(color);
int light = LightTexture.FULL_BRIGHT;
Vec3 vec1 = new Vec3(0, 0, 0);
Vec3 vec2 = new Vec3(0, 1, 0);
Vec3 vec3 = new Vec3(1, 1, 0);
Vec3 vec4 = new Vec3(1, 0, 0);
float u1 = iconX * 1f / ICON_ATLAS_SIZE;
float u2 = (iconX + 16) * 1f / ICON_ATLAS_SIZE;
float v1 = iconY * 1f / ICON_ATLAS_SIZE;
float v2 = (iconY + 16) * 1f / ICON_ATLAS_SIZE;
vertex(builder, matrix, vec1, rgb, u1, v1, light);
vertex(builder, matrix, vec2, rgb, u1, v2, light);
vertex(builder, matrix, vec3, rgb, u2, v2, light);
vertex(builder, matrix, vec4, rgb, u2, v1, light);
}
@OnlyIn(Dist.CLIENT)
private void vertex(VertexConsumer builder, Matrix4f matrix, Vec3 vec, Color rgb, float u, float v, int light) {
builder.addVertex(matrix, (float) vec.x, (float) vec.y, (float) vec.z)
.setColor(rgb.getRed(), rgb.getGreen(), rgb.getBlue(), 255)
.setUv(u, v)
.setLight(light);
}
@OnlyIn(Dist.CLIENT)
public DelegatedStencilElement asStencil() {
return new DelegatedStencilElement().withStencilRenderer((ms, w, h, alpha) -> this.render(ms, 0, 0)).withBounds(16, 16);
}
}

View File

@@ -1,114 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
public class BuildConfig {
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
public static final Reach reach = new Reach(builder);
public static final SurvivalBalancers survivalBalancers = new SurvivalBalancers(builder);
public static final Visuals visuals = new Visuals(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Reach {
public final ForgeConfigSpec.ConfigValue<Boolean> enableReachUpgrades;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachCreative;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel0;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel1;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel2;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel3;
public Reach(ForgeConfigSpec.Builder builder) {
builder.push("Reach");
enableReachUpgrades = builder
.comment("Reach: how far away the player can place blocks using mirror/array etc.",
"Enable the crafting of reach upgrades to increase reach.",
"If disabled, reach is set to level 3 for survival players.")
.define("enableReachUpgrades", true);
maxReachCreative = builder
.comment("Maximum reach in creative",
"Keep in mind that chunks need to be loaded to be able to place blocks inside.")
.define("maxReachCreative", 200);
maxReachLevel0 = builder
.comment("Maximum reach in survival without upgrades",
"Reach upgrades are craftable consumables that permanently increase reach.",
"Set to 0 to disable Effortless Building until the player has consumed a reach upgrade.")
.define("maxReachLevel0", 20);
maxReachLevel1 = builder
.comment("Maximum reach in survival with one upgrade")
.define("maxReachLevel1", 50);
maxReachLevel2 = builder
.comment("Maximum reach in survival with two upgrades")
.define("maxReachLevel2", 100);
maxReachLevel3 = builder
.comment("Maximum reach in survival with three upgrades")
.define("maxReachLevel3", 200);
builder.pop();
}
}
public static class SurvivalBalancers {
public final ForgeConfigSpec.ConfigValue<Integer> quickReplaceMiningLevel;
public final ForgeConfigSpec.ConfigValue<Integer> undoStackSize;
public SurvivalBalancers(ForgeConfigSpec.Builder builder) {
builder.push("SurvivalBalancers");
quickReplaceMiningLevel = builder
.comment("Determines what blocks can be replaced in survival.",
"-1: only blocks that can be harvested by hand (default)",
"0: blocks that can be harvested with wooden or gold tools",
"1: blocks that can be harvested with stone tools",
"2: blocks that can be harvested with iron tools",
"3: blocks that can be harvested with diamond tools",
"4: blocks that can be harvested with netherite tools")
.defineInRange("quickReplaceMiningLevel", -1, -1, 3);
undoStackSize = builder
.comment("How many placements are remembered for the undo functionality.")
.worldRestart()
.define("undoStackSize", 10);
builder.pop();
}
}
public static class Visuals {
public final ForgeConfigSpec.ConfigValue<Boolean> alwaysShowBlockPreview;
public final ForgeConfigSpec.ConfigValue<Double> dissolveTimeMultiplier;
public final ForgeConfigSpec.ConfigValue<Integer> shaderThreshold;
public final ForgeConfigSpec.ConfigValue<Boolean> useShaders;
public Visuals(ForgeConfigSpec.Builder builder) {
builder.push("Visuals");
alwaysShowBlockPreview = builder
.comment("Show a block preview if you have a block in hand even in the 'Normal' build mode")
.define("alwaysShowBlockPreview", false);
dissolveTimeMultiplier = builder
.comment("How long the dissolve effect takes when placing blocks.",
"Default between 30 and 60 ticks, you can multiply that here.",
"Recommended values:",
"Snappy: 0.7",
"Relaxing: 1.5")
.define("dissolveTimeMultiplier", 1.0);
shaderThreshold = builder
.comment("Switch to using the simple performance shader when placing more than this many blocks.")
.define("shaderTreshold", 1500);
useShaders = builder
.comment("Use fancy shaders while placing blocks")
.define("useShaders", true);
builder.pop();
}
}
}

View File

@@ -0,0 +1,51 @@
package nl.requios.effortlessbuilding;
import net.neoforged.neoforge.common.ModConfigSpec;
import static net.neoforged.neoforge.common.ModConfigSpec.BooleanValue;
import static net.neoforged.neoforge.common.ModConfigSpec.Builder;
import static net.neoforged.neoforge.common.ModConfigSpec.IntValue;
public class ClientConfig {
private static final Builder builder = new Builder();
public static final Visuals visuals = new Visuals(builder);
public static final ModConfigSpec spec = builder.build();
public static class Visuals {
public final BooleanValue showBlockPreviews;
public final BooleanValue onlyShowBlockPreviewsWhenBuilding;
public final IntValue maxBlockPreviews;
public final IntValue appearAnimationLength;
public final IntValue breakAnimationLength;
public Visuals(Builder builder) {
builder.push("Visuals");
showBlockPreviews = builder
.comment("Show previews of the blocks while placing them")
.define("showBlockPreviews", true);
onlyShowBlockPreviewsWhenBuilding = builder
.comment("Show block previews only when actively using a build mode")
.define("onlyShowBlockPreviewsWhenBuilding", true);
maxBlockPreviews = builder
.comment("Don't show block previews when placing more than this many blocks. " +
"The outline will always be rendered.")
.defineInRange("maxBlockPreviews", 400, 0, 5000);
appearAnimationLength = builder
.comment("How long it takes for a block to appear when placed in ticks.",
"Set to 0 to disable animation.")
.defineInRange("appearAnimationLength", 5, 0, 100);
breakAnimationLength = builder
.comment("How long the break animation is in ticks.",
"Set to 0 to disable animation.")
.defineInRange("breakAnimationLength", 10, 0, 100);
builder.pop();
}
}
}

View File

@@ -0,0 +1,208 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.player.Player;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.ClientTickEvent;
import net.neoforged.neoforge.client.event.InputEvent;
import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent;
import net.neoforged.neoforge.client.event.ScreenEvent;
import net.neoforged.neoforge.client.settings.KeyConflictContext;
import net.neoforged.neoforge.client.settings.KeyModifier;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui;
import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifiersScreen;
import org.lwjgl.glfw.GLFW;
@EventBusSubscriber(Dist.CLIENT)
public class ClientEvents {
public static KeyMapping[] keyBindings;
public static int ticksInGame = 0;
private static int placeCooldown = 0;
private static int breakCooldown = 0;
//Mod Bus Events
@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
@SubscribeEvent
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
keyBindings = new KeyMapping[6];
keyBindings[0] = new KeyMapping("key.effortlessbuilding.mode.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_LEFT_ALT, 0), "key.effortlessbuilding.category");
keyBindings[1] = new KeyMapping("key.effortlessbuilding.hud.desc", KeyConflictContext.IN_GAME, InputConstants.getKey(GLFW.GLFW_KEY_KP_ADD, 0), "key.effortlessbuilding.category");
keyBindings[2] = new KeyMapping("key.effortlessbuilding.undo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Z, 0), "key.effortlessbuilding.category");
keyBindings[3] = new KeyMapping("key.effortlessbuilding.redo.desc", KeyConflictContext.IN_GAME, KeyModifier.CONTROL, InputConstants.getKey(GLFW.GLFW_KEY_Y, 0), "key.effortlessbuilding.category");
keyBindings[4] = new KeyMapping("key.effortlessbuilding.previous_build_mode.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
keyBindings[5] = new KeyMapping("key.effortlessbuilding.disable_build_mode_toggle.desc", KeyConflictContext.IN_GAME, InputConstants.UNKNOWN, "key.effortlessbuilding.category");
for (KeyMapping keyBinding : keyBindings) {
event.register(keyBinding);
}
}
// @SubscribeEvent
// public static void registerShaders(RegisterShadersEvent event) throws IOException {
// event.registerShader(new ShaderInstance(event.getResourceManager(),
// EffortlessBuilding.modLoc("dissolve"),
// DefaultVertexFormat.BLOCK),
// shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance);
// }
}
@SubscribeEvent
public static void onClientTickPre(ClientTickEvent.Pre event) {
if (!isGameActive()) return;
EffortlessBuildingClient.BUILDER_CHAIN.onTick();
onMouseInput();
EffortlessBuildingClient.BLOCK_PREVIEWS.onTick();
}
@SubscribeEvent
public static void onClientTickPost(ClientTickEvent.Post event) {
if (!isGameActive()) return;
Screen gui = Minecraft.getInstance().screen;
if (gui == null || !gui.isPauseScreen()) {
ticksInGame++;
}
}
private static void onMouseInput() {
Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player;
if (player == null) return;
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
if (mc.screen != null ||
RadialMenu.instance.isVisible()) {
return;
}
if (mc.options.keyUse.isDown()) {
if (placeCooldown <= 0) {
placeCooldown = 4;
EffortlessBuildingClient.BUILDER_CHAIN.onRightClick();
} else if (buildMode == BuildModeEnum.SINGLE) {
placeCooldown--;
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0;
}
} else {
placeCooldown = 0;
}
if (mc.options.keyAttack.isDown()) {
//Break block in distance in creative (or survival if enabled in config)
if (breakCooldown <= 0) {
breakCooldown = 4;
EffortlessBuildingClient.BUILDER_CHAIN.onLeftClick();
} else if (buildMode == BuildModeEnum.SINGLE) {
breakCooldown--;
if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0;
}
} else {
breakCooldown = 0;
}
}
@SubscribeEvent(receiveCanceled = true)
public static void onKeyPress(InputEvent.Key event) {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null)
return;
//Radial menu
if (keyBindings[0].isDown()) {
if (!AttachmentHandler.isDisabled(player)) {
if (!RadialMenu.instance.isVisible()) {
Minecraft.getInstance().setScreen(RadialMenu.instance);
}
} else {
EffortlessBuilding.log(player, "Build modes are disabled until your reach has increased. Increase your reach with craftable reach upgrades.");
}
}
//Show Modifier Settings GUI
if (keyBindings[1].consumeClick()) {
openModifierSettings();
}
//Undo (Ctrl+Z)
if (keyBindings[2].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.UNDO);
}
//Redo (Ctrl+Y)
if (keyBindings[3].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.REDO);
}
//Previous build mode
if (keyBindings[4].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.PREVIOUS_BUILD_MODE);
}
//Disable build mode toggle
if (keyBindings[5].consumeClick()) {
ModeOptions.performAction(player, ModeOptions.ActionEnum.DISABLE_BUILD_MODE_TOGGLE);
}
}
public static void openModifierSettings() {
Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player;
if (player == null) return;
//Disabled if max reach is 0, might be set in the config that way.
if (AttachmentHandler.isDisabled(player)) {
EffortlessBuilding.log(player, "Build modifiers are disabled until your power level has increased. Increase your power level by consuming certain items.");
} else {
mc.setScreen(new ModifiersScreen());
}
}
public static void openPlayerSettings() {
Minecraft mc = Minecraft.getInstance();
mc.setScreen(new PlayerSettingsGui());
}
@SubscribeEvent
public static void onGuiOpen(ScreenEvent.Opening event) {
Player player = Minecraft.getInstance().player;
if (player != null) {
EffortlessBuildingClient.BUILDER_CHAIN.cancel();
}
}
public static boolean isKeybindDown(int keybindIndex) {
return InputConstants.isKeyDown(
Minecraft.getInstance().getWindow().getWindow(),
keyBindings[keybindIndex].getKey().getValue());
}
public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
}
}

View File

@@ -0,0 +1,146 @@
package nl.requios.effortlessbuilding;
import net.neoforged.neoforge.common.ModConfigSpec;
import static net.neoforged.neoforge.common.ModConfigSpec.Builder;
import static net.neoforged.neoforge.common.ModConfigSpec.IntValue;
public class CommonConfig {
private static final Builder builder = new Builder();
public static final Reach reach = new Reach(builder);
public static final MaxBlocksPlacedAtOnce maxBlocksPlacedAtOnce = new MaxBlocksPlacedAtOnce(builder);
public static final MaxBlocksPerAxis maxBlocksPerAxis = new MaxBlocksPerAxis(builder);
public static final MaxMirrorRadius maxMirrorRadius = new MaxMirrorRadius(builder);
public static final ModConfigSpec spec = builder.build();
public static class Reach {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public Reach(Builder builder) {
builder.push("Reach");
creative = builder
.comment("How far away the player can place and break blocks.")
.defineInRange("reachCreative", 200, 0, 1000);
level0 = builder
.comment("Maximum reach in survival without upgrades",
"Consume Power Level upgrades to permanently increase this.")
.defineInRange("reachLevel0", 0, 0, 1000);
level1 = builder
.defineInRange("reachLevel1", 8, 0, 1000);
level2 = builder
.defineInRange("reachLevel2", 16, 0, 1000);
level3 = builder
.defineInRange("reachLevel3", 32, 0, 1000);
builder.pop();
}
}
public static class MaxBlocksPlacedAtOnce {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxBlocksPlacedAtOnce(Builder builder) {
builder.push("MaxBlocksPlacedAtOnce");
creative = builder
.comment("How many blocks can be placed in one click.")
.defineInRange("maxBlocksPlacedAtOnceCreative", 10000, 0, 100000);
level0 = builder
.comment("In survival without upgrades",
"Consume Power Level upgrades to permanently increase this.",
"Set to 0 to disable Effortless Building until the player has increased their Building Power Level.")
.defineInRange("maxBlocksPlacedAtOnceLevel0", 128, 0, 100000);
level1 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel1", 192, 0, 100000);
level2 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel2", 320, 0, 100000);
level3 = builder
.defineInRange("maxBlocksPlacedAtOnceLevel3", 640, 0, 100000);
builder.pop();
}
}
public static class MaxBlocksPerAxis {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxBlocksPerAxis(Builder builder) {
builder.push("MaxBlocksPerAxis");
creative = builder
.comment("How many blocks can be placed at once per axis when using build modes (e.g. walls).",
"Also affects the array modifier.")
.defineInRange("maxBlocksPerAxisCreative", 1000, 0, 1000);
level0 = builder
.comment("In survival without upgrades",
"Consume Power Level upgrades to permanently increase this.")
.defineInRange("maxBlocksPerAxisLevel0", 8, 0, 1000);
level1 = builder
.defineInRange("maxBlocksPerAxisLevel1", 12, 0, 1000);
level2 = builder
.defineInRange("maxBlocksPerAxisLevel2", 16, 0, 1000);
level3 = builder
.defineInRange("maxBlocksPerAxisLevel3", 20, 0, 1000);
builder.pop();
}
}
public static class MaxMirrorRadius {
public final IntValue creative;
public final IntValue level0;
public final IntValue level1;
public final IntValue level2;
public final IntValue level3;
public MaxMirrorRadius(Builder builder) {
builder.push("MaxMirrorRadius");
creative = builder
.comment("The maximum (radial) mirror radius.")
.defineInRange("maxMirrorRadiusCreative", 200, 0, 1000);
level0 = builder
.comment("Maximum reach in survival without upgrades",
"Consume Power Level upgrades upgrades to permanently increase this.")
.defineInRange("maxMirrorRadiusLevel0", 16, 0, 1000);
level1 = builder
.defineInRange("maxMirrorRadiusLevel1", 32, 0, 1000);
level2 = builder
.defineInRange("maxMirrorRadiusLevel2", 48, 0, 1000);
level3 = builder
.defineInRange("maxMirrorRadiusLevel3", 64, 0, 1000);
builder.pop();
}
}
}

View File

@@ -0,0 +1,157 @@
package nl.requios.effortlessbuilding;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.common.util.FakePlayer;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.level.BlockEvent;
import net.neoforged.neoforge.event.tick.LevelTickEvent;
import nl.requios.effortlessbuilding.attachment.PowerLevel;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.network.message.ModifierSettingsPacket;
import nl.requios.effortlessbuilding.network.message.PowerLevelPacket;
import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.utilities.PowerLevelCommand;
@EventBusSubscriber
public class CommonEvents {
//Mod Bus Events
// @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
}
@SubscribeEvent
public static void registerCommands(RegisterCommandsEvent event) {
PowerLevelCommand.register(event.getDispatcher());
}
@SubscribeEvent
public static void onTick(LevelTickEvent.Pre event) {
Level level = event.getLevel();
if (level.isClientSide) return;
if (!level.dimension().equals(Level.OVERWORLD)) return;
EffortlessBuilding.SERVER_BLOCK_PLACER.tick();
}
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
if (event.getLevel().isClientSide()) return; //Never called clientside anyway, but just to be sure
if (!(event.getEntity() instanceof Player player)) return;
if (event.getEntity() instanceof FakePlayer) return;
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
if (!ServerBuildState.isLikeVanilla(player)) {
//Only cancel if itemblock in hand
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
if (isPlayerHoldingBlock(player)) {
event.setCanceled(true);
//TODO Notify client to not decrease itemstack
}
}
}
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getLevel().isClientSide()) return;
Player player = event.getPlayer();
if (player instanceof FakePlayer) return;
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (!ServerBuildState.isLikeVanilla(player) && powerLevel.canBreakFar(player)) {
event.setCanceled(true);
}
}
private static boolean isPlayerHoldingBlock(Player player) {
ItemStack currentItemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
return currentItemStack.getItem() instanceof BlockItem ||
(CompatHelper.isItemBlockProxy(currentItemStack) && !player.isShiftKeyDown());
}
@SubscribeEvent
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) return;
ServerBuildState.handleNewPlayer(player);
((ServerPlayer)player).connection.send(new ModifierSettingsPacket(player));
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
((ServerPlayer)player).connection.send(new PowerLevelPacket(powerLevel.getPowerLevel()));
}
@SubscribeEvent
public static void onClone(PlayerEvent.Clone event) {
// If not dead, player is returning from the End
if (!event.isWasDeath()) return;
Player original = event.getOriginal();
Player clone = event.getEntity();
// Copy the power level from the original player to the clone
if (event.isWasDeath() && event.getOriginal().hasData(EffortlessBuilding.POWER_LEVEL)) {
event.getEntity().setData(EffortlessBuilding.POWER_LEVEL, event.getOriginal().getData(EffortlessBuilding.POWER_LEVEL));
}
}
@SubscribeEvent
public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerLoggedOutEvent triggers on client side");
return;
}
EffortlessBuilding.UNDO_REDO.clear(player);
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerRespawnEvent triggers on client side");
return;
}
//TODO check if this is needed
ServerBuildState.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) {
EffortlessBuilding.log("PlayerChangedDimensionEvent triggers on client side");
return;
}
//Undo redo has no dimension data, so clear it
EffortlessBuilding.UNDO_REDO.clear(player);
//TODO disable build mode and modifiers?
}
}

View File

@@ -1,37 +1,57 @@
package nl.requios.effortlessbuilding;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.Item;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.network.IContainerFactory;
import net.minecraftforge.registries.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import nl.requios.effortlessbuilding.capability.ModeCapabilityManager;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.ItemStack;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.fml.loading.FMLEnvironment;
import net.neoforged.neoforge.attachment.AttachmentType;
import net.neoforged.neoforge.client.gui.ConfigurationScreen;
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.common.loot.IGlobalLootModifier;
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
import net.neoforged.neoforge.network.IContainerFactory;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import nl.requios.effortlessbuilding.attachment.PowerLevel;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagContainer;
import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagContainer;
import nl.requios.effortlessbuilding.gui.RandomizerBagContainer;
import nl.requios.effortlessbuilding.item.*;
import nl.requios.effortlessbuilding.item.DiamondRandomizerBagItem;
import nl.requios.effortlessbuilding.item.GoldenRandomizerBagItem;
import nl.requios.effortlessbuilding.item.PowerLevelItem;
import nl.requios.effortlessbuilding.item.RandomizerBagItem;
import nl.requios.effortlessbuilding.item.ReachUpgrade1Item;
import nl.requios.effortlessbuilding.item.ReachUpgrade2Item;
import nl.requios.effortlessbuilding.item.ReachUpgrade3Item;
import nl.requios.effortlessbuilding.item.SingleItemLootModifier;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.proxy.ClientProxy;
import nl.requios.effortlessbuilding.proxy.IProxy;
import nl.requios.effortlessbuilding.proxy.ServerProxy;
import nl.requios.effortlessbuilding.systems.ItemUsageTracker;
import nl.requios.effortlessbuilding.systems.ServerBlockPlacer;
import nl.requios.effortlessbuilding.systems.UndoRedo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.function.Supplier;
@Mod(EffortlessBuilding.MODID)
public class EffortlessBuilding {
@@ -39,59 +59,74 @@ public class EffortlessBuilding {
public static final Logger logger = LogManager.getLogger();
public static EffortlessBuilding instance;
public static IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::new);
public static final ServerBlockPlacer SERVER_BLOCK_PLACER = new ServerBlockPlacer();
public static final UndoRedo UNDO_REDO = new UndoRedo();
public static final ItemUsageTracker ITEM_USAGE_TRACKER = new ItemUsageTracker();
//Registration
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
private static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, EffortlessBuilding.MODID);
private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);
private static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create(BuiltInRegistries.MENU, EffortlessBuilding.MODID);
private static final DeferredRegister<MapCodec<? extends IGlobalLootModifier>> LOOT_MODIFIERS = DeferredRegister.create(NeoForgeRegistries.Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, EffortlessBuilding.MODID);
private static final DeferredRegister<AttachmentType<?>> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.ATTACHMENT_TYPES, EffortlessBuilding.MODID);
public static final RegistryObject<Item> RANDOMIZER_BAG_ITEM = ITEMS.register("randomizer_bag", RandomizerBagItem::new);
public static final RegistryObject<Item> GOLDEN_RANDOMIZER_BAG_ITEM = ITEMS.register("golden_randomizer_bag", GoldenRandomizerBagItem::new);
public static final RegistryObject<Item> DIAMOND_RANDOMIZER_BAG_ITEM = ITEMS.register("diamond_randomizer_bag", DiamondRandomizerBagItem::new);
public static final RegistryObject<Item> REACH_UPGRADE_1_ITEM = ITEMS.register("reach_upgrade1", ReachUpgrade1Item::new);
public static final RegistryObject<Item> REACH_UPGRADE_2_ITEM = ITEMS.register("reach_upgrade2", ReachUpgrade2Item::new);
public static final RegistryObject<Item> REACH_UPGRADE_3_ITEM = ITEMS.register("reach_upgrade3", ReachUpgrade3Item::new);
public static final DeferredItem<RandomizerBagItem> RANDOMIZER_BAG_ITEM = ITEMS.register("randomizer_bag", RandomizerBagItem::new);
public static final DeferredItem<GoldenRandomizerBagItem> GOLDEN_RANDOMIZER_BAG_ITEM = ITEMS.register("golden_randomizer_bag", GoldenRandomizerBagItem::new);
public static final DeferredItem<DiamondRandomizerBagItem> DIAMOND_RANDOMIZER_BAG_ITEM = ITEMS.register("diamond_randomizer_bag", DiamondRandomizerBagItem::new);
public static final DeferredItem<ReachUpgrade1Item> REACH_UPGRADE_1_ITEM = ITEMS.register("reach_upgrade1", ReachUpgrade1Item::new);
public static final DeferredItem<ReachUpgrade2Item> REACH_UPGRADE_2_ITEM = ITEMS.register("reach_upgrade2", ReachUpgrade2Item::new);
public static final DeferredItem<ReachUpgrade3Item> REACH_UPGRADE_3_ITEM = ITEMS.register("reach_upgrade3", ReachUpgrade3Item::new);
public static final DeferredItem<PowerLevelItem> MUSCLES_ITEM = ITEMS.register("muscles", PowerLevelItem::new);
public static final DeferredItem<PowerLevelItem> ELASTIC_HAND_ITEM = ITEMS.register("elastic_hand", PowerLevelItem::new);
public static final DeferredItem<PowerLevelItem> BUILDING_TECHNIQUES_BOOK_ITEM = ITEMS.register("building_techniques_book", PowerLevelItem::new);
public static final RegistryObject<MenuType<RandomizerBagContainer>> RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("randomizer_bag", () -> registerContainer(RandomizerBagContainer::new));
public static final RegistryObject<MenuType<GoldenRandomizerBagContainer>> GOLDEN_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("golden_randomizer_bag", () -> registerContainer(GoldenRandomizerBagContainer::new));
public static final RegistryObject<MenuType<DiamondRandomizerBagContainer>> DIAMOND_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("diamond_randomizer_bag", () -> registerContainer(DiamondRandomizerBagContainer::new));
public static final Supplier<MenuType<RandomizerBagContainer>> RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("randomizer_bag", () -> registerContainer(RandomizerBagContainer::new));
public static final Supplier<MenuType<GoldenRandomizerBagContainer>> GOLDEN_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("golden_randomizer_bag", () -> registerContainer(GoldenRandomizerBagContainer::new));
public static final Supplier<MenuType<DiamondRandomizerBagContainer>> DIAMOND_RANDOMIZER_BAG_CONTAINER = CONTAINERS.register("diamond_randomizer_bag", () -> registerContainer(DiamondRandomizerBagContainer::new));
public EffortlessBuilding() {
public static final Supplier<MapCodec<SingleItemLootModifier>> SINGLE_ITEM_LOOT_MODIFIER = EffortlessBuilding.LOOT_MODIFIERS.register("single_item_loot_modifier", SingleItemLootModifier.CODEC);
public static final Supplier<AttachmentType<PowerLevel>> POWER_LEVEL = ATTACHMENT_TYPES.register("power_level", () -> AttachmentType.serializable(PowerLevel::new).build());
public EffortlessBuilding(IEventBus modEventBus, ModContainer container, Dist dist) {
instance = this;
// Register ourselves for server and other game events we are interested in
FMLJavaModLoadingContext.get().getModEventBus().register(this);
IEventBus forgeEventBus = NeoForge.EVENT_BUS;
ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus());
CONTAINERS.register(FMLJavaModLoadingContext.get().getModEventBus());
modEventBus.addListener(EffortlessBuilding::setup);
modEventBus.addListener(EffortlessBuilding::addTabContents);
modEventBus.addListener(PacketHandler::setupPackets);
ITEMS.register(modEventBus);
CONTAINERS.register(modEventBus);
LOOT_MODIFIERS.register(modEventBus);
ATTACHMENT_TYPES.register(modEventBus);
//Register config
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec);
container.registerConfig(ModConfig.Type.COMMON, CommonConfig.spec);
container.registerConfig(ModConfig.Type.SERVER, ServerConfig.spec);
if (dist.isClient()) {
container.registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec);
container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new);
EffortlessBuildingClient.onConstructorClient(modEventBus, forgeEventBus);
}
}
public static <T extends AbstractContainerMenu> MenuType<T> registerContainer(IContainerFactory<T> fact){
MenuType<T> type = new MenuType<T>(fact);
return type;
}
@SubscribeEvent
public void setup(final FMLCommonSetupEvent event) {
PacketHandler.register();
proxy.setup(event);
public static void setup(final FMLCommonSetupEvent event) {
CompatHelper.setup();
}
@SubscribeEvent
public void clientSetup(final FMLClientSetupEvent event) {
proxy.clientSetup(event);
public static void addTabContents(final BuildCreativeModeTabContentsEvent event) {
if (event.getTabKey() == CreativeModeTabs.TOOLS_AND_UTILITIES) {
List<ItemStack> stacks = ITEMS.getEntries().stream().map(reg -> new ItemStack(reg.get())).toList();
event.acceptAll(stacks);
}
}
@SubscribeEvent
public void registerCapabilities(RegisterCapabilitiesEvent event){
event.register(ModifierCapabilityManager.IModifierCapability.class);
event.register(ModeCapabilityManager.IModeCapability.class);
public static <T extends AbstractContainerMenu> MenuType<T> registerContainer(IContainerFactory<T> fact) {
MenuType<T> type = new MenuType<T>(fact, FeatureFlags.REGISTRY.allFlags());
return type;
}
public static void log(String msg) {
@@ -108,6 +143,18 @@ public class EffortlessBuilding {
//Log with translation supported, call either on client or server (which then sends a message)
public static void logTranslate(Player player, String prefix, String translationKey, String suffix, boolean actionBar) {
proxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
if (FMLEnvironment.dist.isClient()) {
ClientProxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
} else {
ServerProxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
}
}
public static void logError(String msg) {
logger.error(msg);
}
public static ResourceLocation asResource(String path) {
return ResourceLocation.fromNamespaceAndPath(MODID, path);
}
}

View File

@@ -0,0 +1,35 @@
package nl.requios.effortlessbuilding;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagScreen;
import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen;
import nl.requios.effortlessbuilding.gui.RandomizerBagScreen;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import nl.requios.effortlessbuilding.systems.BuildSettings;
import nl.requios.effortlessbuilding.systems.BuilderChain;
import nl.requios.effortlessbuilding.systems.BuilderFilter;
import nl.requios.effortlessbuilding.systems.ItemUsageTracker;
public class EffortlessBuildingClient {
public static final BuilderChain BUILDER_CHAIN = new BuilderChain();
public static final BuildModes BUILD_MODES = new BuildModes();
public static final BuildModifiers BUILD_MODIFIERS = new BuildModifiers();
public static final BuildSettings BUILD_SETTINGS = new BuildSettings();
public static final BlockPreviews BLOCK_PREVIEWS = new BlockPreviews();
public static final BuilderFilter BUILDER_FILTER = new BuilderFilter();
public static final ItemUsageTracker ITEM_USAGE_TRACKER = new ItemUsageTracker();
public static void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) {
modEventBus.addListener(EffortlessBuildingClient::registerMenuScreens);
}
public static void registerMenuScreens(final RegisterMenuScreensEvent event) {
event.register(EffortlessBuilding.RANDOMIZER_BAG_CONTAINER.get(), RandomizerBagScreen::new);
event.register(EffortlessBuilding.GOLDEN_RANDOMIZER_BAG_CONTAINER.get(), GoldenRandomizerBagScreen::new);
event.register(EffortlessBuilding.DIAMOND_RANDOMIZER_BAG_CONTAINER.get(), DiamondRandomizerBagScreen::new);
}
}

View File

@@ -1,163 +0,0 @@
package nl.requios.effortlessbuilding;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.capability.ModeCapabilityManager;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.network.AddUndoMessage;
import nl.requios.effortlessbuilding.network.ClearUndoMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.RequestLookAtMessage;
@Mod.EventBusSubscriber(modid = EffortlessBuilding.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EventHandler {
@SubscribeEvent
public static void attachCapabilities(AttachCapabilitiesEvent<Entity> event) {
if (event.getObject() instanceof FakePlayer) return;
if (event.getObject() instanceof Player) {
event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_modifier"), new ModifierCapabilityManager.Provider());
event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_mode"), new ModeCapabilityManager.Provider());
}
}
@SubscribeEvent
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
if (event.getLevel().isClientSide()) return;
if (!(event.getEntity() instanceof Player)) return;
if (event.getEntity() instanceof FakePlayer) return;
//Cancel event if necessary
ServerPlayer player = ((ServerPlayer) event.getEntity());
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode();
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
if (buildMode != BuildModes.BuildModeEnum.NORMAL) {
event.setCanceled(true);
} else if (modifierSettings.doQuickReplace()) {
//Cancel event and send message if QuickReplace
event.setCanceled(true);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), new RequestLookAtMessage(true));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState()));
} else {
//NORMAL mode, let vanilla handle block placing
//But modifiers should still work
//Send message to client, which sends message back with raytrace info
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), new RequestLookAtMessage(false));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState()));
}
// Stat<ResourceLocation> blocksPlacedStat = StatList.CUSTOM.get(new ResourceLocation(EffortlessBuilding.MODID, "blocks_placed"));
// player.getStats().increment(player, blocksPlacedStat, 1);
//
// System.out.println(player.getStats().getValue(blocksPlacedStat));
}
@SubscribeEvent
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getLevel().isClientSide()) return;
if (event.getPlayer() instanceof FakePlayer) return;
//Cancel event if necessary
//If cant break far then dont cancel event ever
BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(event.getPlayer()).getBuildMode();
if (buildMode != BuildModes.BuildModeEnum.NORMAL && ReachHelper.canBreakFar(event.getPlayer())) {
event.setCanceled(true);
} else {
//NORMAL mode, let vanilla handle block breaking
//But modifiers and QuickReplace should still work
//Dont break the original block yourself, otherwise Tinkers Hammer and Veinminer won't work
BuildModes.onBlockBroken(event.getPlayer(), event.getPos(), false);
//Add to undo stack in client
if (event.getPlayer() instanceof ServerPlayer && event.getState() != null && event.getPos() != null) {
PacketDistributor.PacketTarget packetTarget = PacketDistributor.PLAYER.with(() -> (ServerPlayer) event.getPlayer());
if (packetTarget != null)
PacketHandler.INSTANCE.send(packetTarget, new AddUndoMessage(event.getPos(), event.getState(), Blocks.AIR.defaultBlockState()));
}
}
}
@SubscribeEvent
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
ModifierSettingsManager.handleNewPlayer(player);
ModeSettingsManager.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) return;
UndoRedo.clear(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage());
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
ModifierSettingsManager.handleNewPlayer(player);
ModeSettingsManager.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
if (event.getEntity() instanceof FakePlayer) return;
Player player = event.getEntity();
if (player.getCommandSenderWorld().isClientSide) return;
//Set build mode to normal
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
modeSettings.setBuildMode(BuildModes.BuildModeEnum.NORMAL);
ModeSettingsManager.setModeSettings(player, modeSettings);
//Disable modifiers
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
modifierSettings.getMirrorSettings().enabled = false;
modifierSettings.getRadialMirrorSettings().enabled = false;
modifierSettings.getArraySettings().enabled = false;
ModifierSettingsManager.setModifierSettings(player, modifierSettings);
ModifierSettingsManager.handleNewPlayer(player);
ModeSettingsManager.handleNewPlayer(player);
UndoRedo.clear(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new ClearUndoMessage());
}
@SubscribeEvent
public static void onPlayerClone(PlayerEvent.Clone event) {
if (event.getEntity() instanceof FakePlayer) return;
//Attach capabilities on death, otherwise crash
Player oldPlayer = event.getOriginal();
oldPlayer.revive();
Player newPlayer = event.getEntity();
ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer));
ModeSettingsManager.setModeSettings(newPlayer, ModeSettingsManager.getModeSettings(oldPlayer));
}
}

View File

@@ -1,31 +0,0 @@
package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.render.BuildRenderTypes;
import java.io.IOException;
import java.util.HashMap;
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, value = {Dist.CLIENT})
public class ModClientEventHandler {
@SubscribeEvent
public static void registerShaders(RegisterShadersEvent event) throws IOException {
event.registerShader(new ShaderInstance(event.getResourceManager(),
new ResourceLocation(EffortlessBuilding.MODID, "dissolve"),
DefaultVertexFormat.BLOCK),
shaderInstance -> BuildRenderTypes.dissolveShaderInstance = shaderInstance);
}
}

View File

@@ -0,0 +1,62 @@
package nl.requios.effortlessbuilding;
import net.neoforged.neoforge.common.ModConfigSpec;
import java.util.Arrays;
import java.util.List;
import static net.neoforged.neoforge.common.ModConfigSpec.BooleanValue;
import static net.neoforged.neoforge.common.ModConfigSpec.Builder;
import static net.neoforged.neoforge.common.ModConfigSpec.ConfigValue;
import static net.neoforged.neoforge.common.ModConfigSpec.IntValue;
public class ServerConfig {
private static final Builder builder = new Builder();
public static final ServerConfig.Validation validation = new ServerConfig.Validation(builder);
public static final ServerConfig.Memory memory = new ServerConfig.Memory(builder);
public static final ModConfigSpec spec = builder.build();
public static class Validation {
public final BooleanValue allowInSurvival;
public final BooleanValue useWhitelist;
public final ConfigValue<List<? extends String>> whitelist;
public final IntValue maxBlocksPlacedAtOnce;
public Validation(Builder builder) {
builder.push("Validation");
allowInSurvival = builder
.comment("Allow use of the mod for players that are in survival mode. Otherwise, only creative mode players can use the mod.")
.define("allowInSurvival", true);
useWhitelist = builder
.comment("Use a whitelist to determine which players can use the mod. If false, all players can use the mod.")
.define("useWhitelist", false);
whitelist = builder
.comment("List of player names that can use the mod.")
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
maxBlocksPlacedAtOnce = builder
.comment("Maximum number of blocks that can be placed at once. This is a last check. If you want the player to receive visual feedback instead of an error message, change values in the common config.")
.defineInRange("maxBlocksPlacedAtOnce", 10000, 1, 100000);
builder.pop();
}
}
public static class Memory {
public final IntValue undoStackSize;
public Memory(Builder builder) {
builder.push("Memory");
undoStackSize = builder
.comment("How many sets of blocks are remembered for the undo functionality, per player.")
.worldRestart()
.defineInRange("undoStackSize", 50, 10, 200);
builder.pop();
}
}
}

View File

@@ -0,0 +1,130 @@
package nl.requios.effortlessbuilding.attachment;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.message.PowerLevelPacket;
public class AttachmentHandler {
public static final ResourceLocation POWER_LEVEL_CAP = EffortlessBuilding.asResource("power_level");
public static void syncToClient(Player player) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel == null) return; //Should never be null but just to be sure
((ServerPlayer)player).connection.send(new PowerLevelPacket(powerLevel.getPowerLevel()));
}
//Helper methods to reduce boilerplate code
public static boolean canReplaceBlocks(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.canReplaceBlocks(player);
}
}
return false;
}
public static int getMaxBlocksPerAxis(Player player, boolean nextPowerLevel) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getMaxBlocksPerAxis(player, nextPowerLevel);
}
}
return CommonConfig.maxBlocksPerAxis.level0.get();
}
public static int getMaxBlocksPlacedAtOnce(Player player, boolean nextPowerLevel) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getMaxBlocksPlacedAtOnce(player, nextPowerLevel);
}
}
return CommonConfig.maxBlocksPlacedAtOnce.level0.get();
}
public static int getMaxMirrorRadius(Player player, boolean nextPowerLevel) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getMaxMirrorRadius(player, nextPowerLevel);
}
}
return CommonConfig.maxMirrorRadius.level0.get();
}
public static int getBuildModeReach(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getBuildModeReach(player);
}
}
return CommonConfig.maxMirrorRadius.level0.get() + 6;
}
public static int getPlacementReach(Player player, boolean nextPowerLevel) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getPlacementReach(player, nextPowerLevel);
}
}
return CommonConfig.reach.level0.get();
}
public static int getPowerLevel(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getPowerLevel();
}
}
return 0;
}
public static int getNextPowerLevel(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.getNextPowerLevel();
}
}
return 0;
}
public static boolean canIncreasePowerLevel(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.canIncreasePowerLevel();
}
}
return false;
}
public static boolean isDisabled(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.isDisabled(player);
}
}
return false;
}
public static boolean canBreakFar(Player player) {
if (player != null) {
PowerLevel powerLevel = player.getData(EffortlessBuilding.POWER_LEVEL);
if (powerLevel != null) {
return powerLevel.canBreakFar(player);
}
}
return false;
}
}

View File

@@ -0,0 +1,106 @@
package nl.requios.effortlessbuilding.attachment;
import net.minecraft.core.HolderLookup.Provider;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import net.neoforged.neoforge.common.util.INBTSerializable;
import nl.requios.effortlessbuilding.CommonConfig;
public class PowerLevel implements INBTSerializable<CompoundTag> {
public static final int MAX_POWER_LEVEL = 3; //Common access
public PowerLevel() {
}
private int powerLevel = 0;
public int getPowerLevel() {
return this.powerLevel;
}
public int getNextPowerLevel() {
return Math.min(getPowerLevel() + 1, MAX_POWER_LEVEL);
}
public void setPowerLevel(int powerLevel) {
this.powerLevel = powerLevel;
}
public boolean canIncreasePowerLevel() {
return getPowerLevel() < MAX_POWER_LEVEL;
}
public void increasePowerLevel() {
if (canIncreasePowerLevel()) {
setPowerLevel(getPowerLevel() + 1);
}
}
public int getPlacementReach(Player player, boolean nextPowerLevel) {
if (player.isCreative()) return CommonConfig.reach.creative.get();
return switch (nextPowerLevel ? getNextPowerLevel() : getPowerLevel()) {
case 1 -> CommonConfig.reach.level1.get();
case 2 -> CommonConfig.reach.level2.get();
case 3 -> CommonConfig.reach.level3.get();
default -> CommonConfig.reach.level0.get();
};
}
//How far away we can detect the second and third click of build modes (distance to player)
public int getBuildModeReach(Player player) {
//A bit further than placement reach, so you can build lines when looking to the side without having to move.
return getPlacementReach(player, false) + 6;
}
public int getMaxBlocksPlacedAtOnce(Player player, boolean nextPowerLevel) {
if (player.isCreative()) return CommonConfig.maxBlocksPlacedAtOnce.creative.get();
return switch (nextPowerLevel ? getNextPowerLevel() : getPowerLevel()) {
case 1 -> CommonConfig.maxBlocksPlacedAtOnce.level1.get();
case 2 -> CommonConfig.maxBlocksPlacedAtOnce.level2.get();
case 3 -> CommonConfig.maxBlocksPlacedAtOnce.level3.get();
default -> CommonConfig.maxBlocksPlacedAtOnce.level0.get();
};
}
public int getMaxBlocksPerAxis(Player player, boolean nextPowerLevel) {
if (player.isCreative()) return CommonConfig.maxBlocksPerAxis.creative.get();
return switch (nextPowerLevel ? getNextPowerLevel() : getPowerLevel()) {
case 1 -> CommonConfig.maxBlocksPerAxis.level1.get();
case 2 -> CommonConfig.maxBlocksPerAxis.level2.get();
case 3 -> CommonConfig.maxBlocksPerAxis.level3.get();
default -> CommonConfig.maxBlocksPerAxis.level0.get();
};
}
public int getMaxMirrorRadius(Player player, boolean nextPowerLevel) {
if (player.isCreative()) return CommonConfig.maxMirrorRadius.creative.get();
return switch (getPowerLevel() + (nextPowerLevel ? 1 : 0)) {
case 1 -> CommonConfig.maxMirrorRadius.level1.get();
case 2 -> CommonConfig.maxMirrorRadius.level2.get();
case 3 -> CommonConfig.maxMirrorRadius.level3.get();
default -> CommonConfig.maxMirrorRadius.level0.get();
};
}
public boolean isDisabled(Player player) {
return getMaxBlocksPlacedAtOnce(player, false) <= 0 || getMaxBlocksPerAxis(player, false) <= 0;
}
public boolean canBreakFar(Player player) {
return player.getAbilities().instabuild;
}
public boolean canReplaceBlocks(Player player) {
return player.getAbilities().instabuild;
}
public CompoundTag serializeNBT(Provider provider) {
CompoundTag tag = new CompoundTag();
tag.putInt("powerLevel", getPowerLevel());
return tag;
}
public void deserializeNBT(Provider provider, CompoundTag nbt) {
setPowerLevel(nbt.getInt("powerLevel"));
}
}

View File

@@ -1,39 +1,19 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.UUID;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public abstract class BaseBuildMode implements IBuildMode {
//In singleplayer client and server variables are shared
//Split everything that needs separate values and may not be called twice in one click
protected Dictionary<UUID, Integer> rightClickClientTable = new Hashtable<>();
protected Dictionary<UUID, Integer> rightClickServerTable = new Hashtable<>();
protected Dictionary<UUID, BlockPos> firstPosTable = new Hashtable<>();
protected Dictionary<UUID, Direction> sideHitTable = new Hashtable<>();
protected Dictionary<UUID, Vec3> hitVecTable = new Hashtable<>();
protected int clicks;
@Override
public void initialize(Player player) {
rightClickClientTable.put(player.getUUID(), 0);
rightClickServerTable.put(player.getUUID(), 0);
firstPosTable.put(player.getUUID(), BlockPos.ZERO);
sideHitTable.put(player.getUUID(), Direction.UP);
hitVecTable.put(player.getUUID(), Vec3.ZERO);
public void initialize() {
clicks = 0;
}
@Override
public Direction getSideHit(Player player) {
return sideHitTable.get(player.getUUID());
}
@Override
public Vec3 getHitVec(Player player) {
return hitVecTable.get(player.getUUID());
public boolean onClick(BlockSet blocks) {
clicks++;
return false;
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.buildmode;
import net.createmod.catnip.theme.Color;
public enum BuildModeCategoryEnum {
BASIC(new Color(0f, .5f, 1f, .8f)),
DIAGONAL(new Color(0.56f, 0.28f, 0.87f, .8f)),
CIRCULAR(new Color(0.29f, 0.76f, 0.3f, 1f)),
ROOF(new Color(0.83f, 0.87f, 0.23f, .8f));
public final Color color;
BuildModeCategoryEnum(Color color) {
this.color = color;
}
}

View File

@@ -0,0 +1,56 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.AllIcons;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Circle;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Cube;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Cylinder;
import nl.requios.effortlessbuilding.buildmode.buildmodes.DiagonalLine;
import nl.requios.effortlessbuilding.buildmode.buildmodes.DiagonalWall;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Disabled;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Floor;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Line;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Single;
import nl.requios.effortlessbuilding.buildmode.buildmodes.SlopeFloor;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Sphere;
import nl.requios.effortlessbuilding.buildmode.buildmodes.Wall;
public enum BuildModeEnum {
DISABLED("normal", new Disabled(), BuildModeCategoryEnum.BASIC, AllIcons.I_DISABLE),
SINGLE("normal_plus", new Single(), BuildModeCategoryEnum.BASIC, AllIcons.I_SINGLE, ModeOptions.OptionEnum.BUILD_SPEED),
LINE("line", new Line(), BuildModeCategoryEnum.BASIC, AllIcons.I_LINE /*, OptionEnum.THICKNESS*/),
WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, AllIcons.I_WALL, ModeOptions.OptionEnum.FILL),
FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, AllIcons.I_FLOOR, ModeOptions.OptionEnum.FILL),
CUBE("cube", new Cube(), BuildModeCategoryEnum.BASIC, AllIcons.I_CUBE, ModeOptions.OptionEnum.CUBE_FILL),
DIAGONAL_LINE("diagonal_line", new DiagonalLine(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_DIAGONAL_LINE /*, OptionEnum.THICKNESS*/),
DIAGONAL_WALL("diagonal_wall", new DiagonalWall(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_DIAGONAL_WALL /*, OptionEnum.FILL*/),
SLOPE_FLOOR("slope_floor", new SlopeFloor(), BuildModeCategoryEnum.DIAGONAL, AllIcons.I_SLOPED_FLOOR, ModeOptions.OptionEnum.RAISED_EDGE),
CIRCLE("circle", new Circle(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_CIRCLE, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL),
CYLINDER("cylinder", new Cylinder(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_CYLINDER, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL),
SPHERE("sphere", new Sphere(), BuildModeCategoryEnum.CIRCULAR, AllIcons.I_SPHERE, ModeOptions.OptionEnum.CIRCLE_START, ModeOptions.OptionEnum.FILL);
// PYRAMID("pyramid", new Pyramid(), BuildModeCategoryEnum.ROOF),
// CONE("cone", new Cone(), BuildModeCategoryEnum.ROOF),
// DOME("dome", new Dome(), BuildModeCategoryEnum.ROOF);
private final String name;
public final IBuildMode instance;
public final BuildModeCategoryEnum category;
public final AllIcons icon;
public final ModeOptions.OptionEnum[] options;
BuildModeEnum(String name, IBuildMode instance, BuildModeCategoryEnum category, AllIcons icon, ModeOptions.OptionEnum... options) {
this.name = name;
this.instance = instance;
this.category = category;
this.icon = icon;
this.options = options;
}
public String getNameKey() {
return "effortlessbuilding.mode." + name;
}
public String getDescriptionKey() {
return "effortlessbuilding.modedescription." + name;
}
}

View File

@@ -1,182 +1,57 @@
package nl.requios.effortlessbuilding.buildmode;
import com.mojang.math.Vector4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmode.buildmodes.*;
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
import nl.requios.effortlessbuilding.network.BlockBrokenMessage;
import nl.requios.effortlessbuilding.network.BlockPlacedMessage;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import static nl.requios.effortlessbuilding.buildmode.ModeOptions.OptionEnum;
import nl.requios.effortlessbuilding.network.message.IsUsingBuildModePacket;
import nl.requios.effortlessbuilding.utilities.BlockSet;
@OnlyIn(Dist.CLIENT)
public class BuildModes {
private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
private BuildModeEnum previousBuildMode = BuildModeEnum.DISABLED;
private BuildModeEnum beforeDisabledBuildMode = BuildModeEnum.SINGLE;
//Static variables are shared between client and server in singleplayer
//We need them separate
public static Dictionary<Player, Boolean> currentlyBreakingClient = new Hashtable<>();
public static Dictionary<Player, Boolean> currentlyBreakingServer = new Hashtable<>();
//Uses a network message to get the previous raytraceresult from the player
//The server could keep track of all raytraceresults but this might lag with many players
//Raytraceresult is needed for sideHit and hitVec
public static void onBlockPlacedMessage(Player player, BlockPlacedMessage message) {
//Check if not in the middle of breaking
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
if (currentlyBreaking.get(player) != null && currentlyBreaking.get(player)) {
//Cancel breaking
initializeMode(player);
return;
}
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
BuildModeEnum buildMode = modeSettings.getBuildMode();
BlockPos startPos = null;
if (message.isBlockHit() && message.getBlockPos() != null) {
startPos = message.getBlockPos();
//Offset in direction of sidehit if not quickreplace and not replaceable
//TODO 1.13 replaceable
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, message.getSideHit());
if (!modifierSettings.doQuickReplace() && !replaceable && !becomesDoubleSlab) {
startPos = startPos.relative(message.getSideHit());
}
//Get under tall grass and other replaceable blocks
if (modifierSettings.doQuickReplace() && replaceable) {
startPos = startPos.below();
}
//Check if player reach does not exceed startpos
int maxReach = ReachHelper.getMaxReach(player);
if (buildMode != BuildModeEnum.NORMAL && player.blockPosition().distSqr(startPos) > maxReach * maxReach) {
EffortlessBuilding.log(player, "Placement exceeds your reach.");
return;
}
}
//Even when no starting block is found, call buildmode instance
//We might want to place things in the air
List<BlockPos> coordinates = buildMode.instance.onRightClick(player, startPos, message.getSideHit(), message.getHitVec(), modifierSettings.doQuickReplace());
if (coordinates.isEmpty()) {
currentlyBreaking.put(player, false);
return;
}
//Limit number of blocks you can place
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
if (coordinates.size() > limit) {
coordinates = coordinates.subList(0, limit);
}
Direction sideHit = buildMode.instance.getSideHit(player);
if (sideHit == null) sideHit = message.getSideHit();
Vec3 hitVec = buildMode.instance.getHitVec(player);
if (hitVec == null) hitVec = message.getHitVec();
BuildModifiers.onBlockPlaced(player, coordinates, sideHit, hitVec, message.getPlaceStartPos());
//Only works when finishing a buildmode is equal to placing some blocks
//No intermediate blocks allowed
currentlyBreaking.remove(player);
public void findCoordinates(BlockSet blocks, Player player) {
buildMode.instance.findCoordinates(blocks);
}
//Use a network message to break blocks in the distance using clientside mouse input
public static void onBlockBrokenMessage(Player player, BlockBrokenMessage message) {
BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null;
onBlockBroken(player, startPos, true);
public BuildModeEnum getBuildMode() {
return buildMode;
}
public static void onBlockBroken(Player player, BlockPos startPos, boolean breakStartPos) {
public void setBuildMode(BuildModeEnum buildMode) {
this.buildMode = buildMode;
//Check if not in the middle of placing
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) {
//Cancel placing
initializeMode(player);
return;
PacketDistributor.sendToServer(new IsUsingBuildModePacket(this.buildMode != BuildModeEnum.DISABLED));
EffortlessBuilding.log(Minecraft.getInstance().player, I18n.get(buildMode.getNameKey()), true);
}
public void activatePreviousBuildMode() {
var temp = buildMode;
setBuildMode(previousBuildMode);
previousBuildMode = temp;
}
public void activateDisableBuildModeToggle(){
if (buildMode == BuildModeEnum.DISABLED) {
setBuildMode(beforeDisabledBuildMode);
} else {
beforeDisabledBuildMode = buildMode;
setBuildMode(BuildModeEnum.DISABLED);
}
if (!ReachHelper.canBreakFar(player)) return;
//If first click
if (currentlyBreaking.get(player) == null) {
//If startpos is null, dont do anything
if (startPos == null) return;
}
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
//Get coordinates
BuildModeEnum buildMode = modeSettings.getBuildMode();
List<BlockPos> coordinates = buildMode.instance.onRightClick(player, startPos, Direction.UP, Vec3.ZERO, true);
if (coordinates.isEmpty()) {
currentlyBreaking.put(player, true);
return;
}
//Let buildmodifiers break blocks
BuildModifiers.onBlockBroken(player, coordinates, breakStartPos);
//Only works when finishing a buildmode is equal to breaking some blocks
//No intermediate blocks allowed
currentlyBreaking.remove(player);
}
public static List<BlockPos> findCoordinates(Player player, BlockPos startPos, boolean skipRaytrace) {
List<BlockPos> coordinates = new ArrayList<>();
ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player);
coordinates.addAll(modeSettings.getBuildMode().instance.findCoordinates(player, startPos, skipRaytrace));
return coordinates;
}
public static void initializeMode(Player player) {
//Resetting mode, so not placing or breaking
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
currentlyBreaking.remove(player);
ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player);
}
public static boolean isCurrentlyPlacing(Player player) {
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player);
}
public static boolean isCurrentlyBreaking(Player player) {
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null && currentlyBreaking.get(player);
}
//Either placing or breaking
public static boolean isActive(Player player) {
Dictionary<Player, Boolean> currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null;
public void onCancel() {
getBuildMode().instance.initialize();
}
//Find coordinates on a line bound by a plane
@@ -188,9 +63,6 @@ public class BuildModes {
return new Vec3(x, y, z);
}
//-- Common build mode functionality --//
public static Vec3 findYBound(double y, Vec3 start, Vec3 look) {
//then x and z are
double x = (y - start.y) / look.y * look.x + start.x;
@@ -237,7 +109,7 @@ public class BuildModes {
if (!skipRaytrace) {
//collision within a 1 block radius to selected is fine
ClipContext rayTraceContext = new ClipContext(start, lineBound, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player);
HitResult rayTraceResult = player.level.clip(rayTraceContext);
HitResult rayTraceResult = player.level().clip(rayTraceContext);
intersects = rayTraceResult != null && rayTraceResult.getType() == HitResult.Type.BLOCK &&
planeBound.subtract(rayTraceResult.getLocation()).lengthSqr() > 4;
}
@@ -247,54 +119,4 @@ public class BuildModes {
!intersects;
}
public enum BuildModeEnum {
NORMAL("normal", new Normal(), BuildModeCategoryEnum.BASIC),
NORMAL_PLUS("normal_plus", new NormalPlus(), BuildModeCategoryEnum.BASIC, OptionEnum.BUILD_SPEED),
LINE("line", new Line(), BuildModeCategoryEnum.BASIC /*, OptionEnum.THICKNESS*/),
WALL("wall", new Wall(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
FLOOR("floor", new Floor(), BuildModeCategoryEnum.BASIC, OptionEnum.FILL),
CUBE("cube", new Cube(), BuildModeCategoryEnum.BASIC, OptionEnum.CUBE_FILL),
DIAGONAL_LINE("diagonal_line", new DiagonalLine(), BuildModeCategoryEnum.DIAGONAL /*, OptionEnum.THICKNESS*/),
DIAGONAL_WALL("diagonal_wall", new DiagonalWall(), BuildModeCategoryEnum.DIAGONAL /*, OptionEnum.FILL*/),
SLOPE_FLOOR("slope_floor", new SlopeFloor(), BuildModeCategoryEnum.DIAGONAL, OptionEnum.RAISED_EDGE),
CIRCLE("circle", new Circle(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL),
CYLINDER("cylinder", new Cylinder(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL),
SPHERE("sphere", new Sphere(), BuildModeCategoryEnum.CIRCULAR, OptionEnum.CIRCLE_START, OptionEnum.FILL);
// PYRAMID("pyramid", new Pyramid(), BuildModeCategoryEnum.ROOF),
// CONE("cone", new Cone(), BuildModeCategoryEnum.ROOF),
// DOME("dome", new Dome(), BuildModeCategoryEnum.ROOF);
private final String name;
public final IBuildMode instance;
public final BuildModeCategoryEnum category;
public final OptionEnum[] options;
BuildModeEnum(String name, IBuildMode instance, BuildModeCategoryEnum category, OptionEnum... options) {
this.name = name;
this.instance = instance;
this.category = category;
this.options = options;
}
public String getNameKey() {
return "effortlessbuilding.mode." + name;
}
public String getDescriptionKey() {
return "effortlessbuilding.modedescription." + name;
}
}
public enum BuildModeCategoryEnum {
BASIC(new Vector4f(0f, .5f, 1f, .8f)),
DIAGONAL(new Vector4f(0.56f, 0.28f, 0.87f, .8f)),
CIRCULAR(new Vector4f(0.29f, 0.76f, 0.3f, 1f)),
ROOF(new Vector4f(0.83f, 0.87f, 0.23f, .8f));
public final Vector4f color;
BuildModeCategoryEnum(Vector4f color) {
this.color = color;
}
}
}

View File

@@ -1,25 +1,14 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public interface IBuildMode {
//Fired when a player selects a buildmode and when it needs to initializeMode
void initialize(Player player);
//Reset values here, start over
void initialize();
//Fired when a block would be placed
//Return a list of coordinates where you want to place blocks
List<BlockPos> onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace);
//Returns if we should place blocks now
boolean onClick(BlockSet blocks);
//Fired continuously for visualization purposes
List<BlockPos> findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace);
Direction getSideHit(Player player);
Vec3 getHitVec(Player player);
void findCoordinates(BlockSet blocks);
}

View File

@@ -1,12 +1,18 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.ChatFormatting;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.AllIcons;
import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.proxy.ClientProxy;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.network.message.PerformRedoPacket;
import nl.requios.effortlessbuilding.network.message.PerformUndoPacket;
import nl.requios.effortlessbuilding.systems.BuildSettings;
@OnlyIn(Dist.CLIENT)
public class ModeOptions {
private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED;
@@ -59,116 +65,106 @@ public class ModeOptions {
return circleStart;
}
//Called on both client and server
public static void performAction(Player player, ActionEnum action) {
if (action == null) return;
switch (action) {
case UNDO:
UndoRedo.undo(player);
break;
case REDO:
UndoRedo.redo(player);
break;
case REPLACE:
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
modifierSettings.setQuickReplace(!modifierSettings.doQuickReplace());
EffortlessBuilding.log(player, "Set " + ChatFormatting.GOLD + "Quick Replace " + ChatFormatting.RESET + (
modifierSettings.doQuickReplace() ? "on" : "off"), true);
break;
case OPEN_MODIFIER_SETTINGS:
if (player.level.isClientSide)
ClientProxy.openModifierSettings();
break;
case OPEN_PLAYER_SETTINGS:
if (player.level.isClientSide)
ClientProxy.openPlayerSettings();
break;
case UNDO -> PacketDistributor.sendToServer(new PerformUndoPacket());
case REDO -> PacketDistributor.sendToServer(new PerformRedoPacket());
case OPEN_MODIFIER_SETTINGS -> ClientEvents.openModifierSettings();
case OPEN_PLAYER_SETTINGS -> ClientEvents.openPlayerSettings();
case PREVIOUS_BUILD_MODE -> EffortlessBuildingClient.BUILD_MODES.activatePreviousBuildMode();
case DISABLE_BUILD_MODE_TOGGLE -> EffortlessBuildingClient.BUILD_MODES.activateDisableBuildModeToggle();
case NORMAL_SPEED:
buildSpeed = ActionEnum.NORMAL_SPEED;
break;
case FAST_SPEED:
buildSpeed = ActionEnum.FAST_SPEED;
break;
case FULL:
fill = ActionEnum.FULL;
break;
case HOLLOW:
fill = ActionEnum.HOLLOW;
break;
case CUBE_FULL:
cubeFill = ActionEnum.CUBE_FULL;
break;
case CUBE_HOLLOW:
cubeFill = ActionEnum.CUBE_HOLLOW;
break;
case CUBE_SKELETON:
cubeFill = ActionEnum.CUBE_SKELETON;
break;
case SHORT_EDGE:
raisedEdge = ActionEnum.SHORT_EDGE;
break;
case LONG_EDGE:
raisedEdge = ActionEnum.LONG_EDGE;
break;
case THICKNESS_1:
lineThickness = ActionEnum.THICKNESS_1;
break;
case THICKNESS_3:
lineThickness = ActionEnum.THICKNESS_3;
break;
case THICKNESS_5:
lineThickness = ActionEnum.THICKNESS_5;
break;
case CIRCLE_START_CENTER:
circleStart = ActionEnum.CIRCLE_START_CENTER;
break;
case CIRCLE_START_CORNER:
circleStart = ActionEnum.CIRCLE_START_CORNER;
break;
case REPLACE_ONLY_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_AIR);
case REPLACE_BLOCKS_AND_AIR -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.BLOCKS_AND_AIR);
case REPLACE_ONLY_BLOCKS -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.ONLY_BLOCKS);
case REPLACE_FILTERED_BY_OFFHAND -> EffortlessBuildingClient.BUILD_SETTINGS.setReplaceMode(BuildSettings.ReplaceMode.FILTERED_BY_OFFHAND);
case TOGGLE_PROTECT_TILE_ENTITIES -> EffortlessBuildingClient.BUILD_SETTINGS.toggleProtectTileEntities();
case NORMAL_SPEED -> buildSpeed = ActionEnum.NORMAL_SPEED;
case FAST_SPEED -> buildSpeed = ActionEnum.FAST_SPEED;
case FULL -> fill = ActionEnum.FULL;
case HOLLOW -> fill = ActionEnum.HOLLOW;
case CUBE_FULL -> cubeFill = ActionEnum.CUBE_FULL;
case CUBE_HOLLOW -> cubeFill = ActionEnum.CUBE_HOLLOW;
case CUBE_SKELETON -> cubeFill = ActionEnum.CUBE_SKELETON;
case SHORT_EDGE -> raisedEdge = ActionEnum.SHORT_EDGE;
case LONG_EDGE -> raisedEdge = ActionEnum.LONG_EDGE;
case THICKNESS_1 -> lineThickness = ActionEnum.THICKNESS_1;
case THICKNESS_3 -> lineThickness = ActionEnum.THICKNESS_3;
case THICKNESS_5 -> lineThickness = ActionEnum.THICKNESS_5;
case CIRCLE_START_CENTER -> circleStart = ActionEnum.CIRCLE_START_CENTER;
case CIRCLE_START_CORNER -> circleStart = ActionEnum.CIRCLE_START_CORNER;
}
if (player.level.isClientSide &&
action != ActionEnum.REPLACE &&
if (player.level().isClientSide &&
action != ActionEnum.OPEN_MODIFIER_SETTINGS &&
action != ActionEnum.OPEN_PLAYER_SETTINGS) {
action != ActionEnum.OPEN_PLAYER_SETTINGS &&
action != ActionEnum.PREVIOUS_BUILD_MODE &&
action != ActionEnum.DISABLE_BUILD_MODE_TOGGLE) {
EffortlessBuilding.logTranslate(player, "", action.name, "", true);
EffortlessBuilding.logTranslate(player, "", action.getNameKey(), "", true);
}
}
public enum ActionEnum {
UNDO("effortlessbuilding.action.undo"),
REDO("effortlessbuilding.action.redo"),
REPLACE("effortlessbuilding.action.replace"),
OPEN_MODIFIER_SETTINGS("effortlessbuilding.action.open_modifier_settings"),
OPEN_PLAYER_SETTINGS("effortlessbuilding.action.open_player_settings"),
UNDO("undo", AllIcons.I_UNDO),
REDO("redo", AllIcons.I_REDO),
OPEN_MODIFIER_SETTINGS("open_modifier_settings", AllIcons.I_SETTINGS),
OPEN_PLAYER_SETTINGS("open_player_settings", AllIcons.I_SETTINGS),
PREVIOUS_BUILD_MODE("previous_build_mode", AllIcons.I_SINGLE),
DISABLE_BUILD_MODE_TOGGLE("disable_build_mode_toggle", AllIcons.I_DISABLE),
NORMAL_SPEED("effortlessbuilding.action.normal_speed"),
FAST_SPEED("effortlessbuilding.action.fast_speed"),
REPLACE_ONLY_AIR("replace_only_air", AllIcons.I_REPLACE_AIR),
REPLACE_BLOCKS_AND_AIR("replace_blocks_and_air", AllIcons.I_REPLACE_BLOCKS_AND_AIR),
REPLACE_ONLY_BLOCKS("replace_only_blocks", AllIcons.I_REPLACE_BLOCKS),
REPLACE_FILTERED_BY_OFFHAND("replace_filtered_by_offhand", AllIcons.I_REPLACE_OFFHAND_FILTERED),
TOGGLE_PROTECT_TILE_ENTITIES("toggle_protect_tile_entities", AllIcons.I_PROTECT_TILE_ENTITIES),
FULL("effortlessbuilding.action.full"),
HOLLOW("effortlessbuilding.action.hollow"),
NORMAL_SPEED("normal_speed", AllIcons.I_NORMAL_SPEED),
FAST_SPEED("fast_speed", AllIcons.I_FAST_SPEED),
CUBE_FULL("effortlessbuilding.action.full"),
CUBE_HOLLOW("effortlessbuilding.action.hollow"),
CUBE_SKELETON("effortlessbuilding.action.skeleton"),
FULL("full", AllIcons.I_FILLED),
HOLLOW("hollow", AllIcons.I_HOLLOW),
SHORT_EDGE("effortlessbuilding.action.short_edge"),
LONG_EDGE("effortlessbuilding.action.long_edge"),
CUBE_FULL("full", AllIcons.I_CUBE_FILLED),
CUBE_HOLLOW("hollow", AllIcons.I_CUBE_HOLLOW),
CUBE_SKELETON("skeleton", AllIcons.I_CUBE_SKELETON),
THICKNESS_1("effortlessbuilding.action.thickness_1"),
THICKNESS_3("effortlessbuilding.action.thickness_3"),
THICKNESS_5("effortlessbuilding.action.thickness_5"),
SHORT_EDGE("short_edge", AllIcons.I_SHORT_EDGE),
LONG_EDGE("long_edge", AllIcons.I_LONG_EDGE),
CIRCLE_START_CORNER("effortlessbuilding.action.start_corner"),
CIRCLE_START_CENTER("effortlessbuilding.action.start_center");
THICKNESS_1("thickness_1", AllIcons.I_THICKNESS_1),
THICKNESS_3("thickness_3", AllIcons.I_THICKNESS_3),
THICKNESS_5("thickness_5", AllIcons.I_THICKNESS_5),
CIRCLE_START_CORNER("start_corner", AllIcons.I_CIRCLE_START_CORNER),
CIRCLE_START_CENTER("start_center", AllIcons.I_CIRCLE_START_CENTER);
public String name;
public AllIcons icon;
ActionEnum(String name) {
ActionEnum(String name, AllIcons icon) {
this.name = name;
this.icon = icon;
}
public String getName() {
return name;
}
public String getNameKey() {
return "effortlessbuilding.action." + name;
}
public String getDescriptionKey() {
return "effortlessbuilding.action." + name + ".description";
}
}

View File

@@ -1,97 +0,0 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.capability.ModeCapabilityManager;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.network.ModeSettingsMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
import javax.annotation.Nonnull;
@Mod.EventBusSubscriber
public class ModeSettingsManager {
//Retrieves the buildsettings of a player through the modeCapability capability
//Never returns null
@Nonnull
public static ModeSettings getModeSettings(Player player) {
LazyOptional<ModeCapabilityManager.IModeCapability> modeCapability =
player.getCapability(ModeCapabilityManager.MODE_CAPABILITY, null);
if (modeCapability.isPresent()) {
ModeCapabilityManager.IModeCapability capability = modeCapability.orElse(null);
if (capability.getModeData() == null){
capability.setModeData(new ModeSettings());
}
return capability.getModeData();
}
EffortlessBuilding.logger.warn("Player does not have modeCapability: " + player);
//Return dummy settings
return new ModeSettings();
}
public static void setModeSettings(Player player, ModeSettings modeSettings) {
if (player == null) {
EffortlessBuilding.log("Cannot set buildmode settings, player is null");
return;
}
LazyOptional<ModeCapabilityManager.IModeCapability> modeCapability =
player.getCapability(ModeCapabilityManager.MODE_CAPABILITY, null);
modeCapability.ifPresent((capability) -> {
capability.setModeData(modeSettings);
BuildModes.initializeMode(player);
});
if (!modeCapability.isPresent()) {
EffortlessBuilding.log(player, "Saving buildmode settings failed.");
}
}
public static String sanitize(ModeSettings modeSettings, Player player) {
int maxReach = ReachHelper.getMaxReach(player);
String error = "";
//TODO sanitize
return error;
}
public static void handleNewPlayer(Player player) {
//Makes sure player has mode settings (if it doesnt it will create it)
getModeSettings(player);
//Only on server
if (!player.level.isClientSide) {
//Send to client
ModeSettingsMessage msg = new ModeSettingsMessage(getModeSettings(player));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), msg);
}
}
public static class ModeSettings {
private BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.NORMAL;
public ModeSettings() {
}
public ModeSettings(BuildModes.BuildModeEnum buildMode) {
this.buildMode = buildMode;
}
public BuildModes.BuildModeEnum getBuildMode() {
return this.buildMode;
}
public void setBuildMode(BuildModes.BuildModeEnum buildMode) {
this.buildMode = buildMode;
}
}
}

View File

@@ -1,17 +1,130 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
public abstract class ThreeClicksBuildMode extends BaseBuildMode {
protected Dictionary<UUID, BlockPos> secondPosTable = new Hashtable<>();
protected BlockEntry firstBlockEntry;
protected BlockEntry secondBlockEntry;
//Finds height after floor has been chosen in buildmodes with 3 clicks
@Override
public void initialize() {
super.initialize();
firstBlockEntry = null;
secondBlockEntry = null;
}
@Override
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
//First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again
if (firstBlockEntry == null) clicks = 0;
} else if (clicks == 2) {
//Second click, find second position
if (blocks.size() == 0) {
clicks = 0;
return false;
}
var player = Minecraft.getInstance().player;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
secondBlockEntry = new BlockEntry(secondPos);
} else {
//Third click, place blocks
clicks = 0;
return true;
}
return false;
}
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
if (clicks == 1) {
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
//Limit amount of blocks we can place per row
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
} else {
var player = Minecraft.getInstance().player;
BlockPos firstPos = firstBlockEntry.blockPos;
BlockPos secondPos = secondBlockEntry.blockPos;
BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, true);
if (thirdPos == null) return;
//Limit amount of blocks you can place per row
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
int x1 = firstPos.getX(), x2 = secondPos.getX(), x3 = thirdPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY(), y3 = thirdPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ(), z3 = thirdPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
if (x3 - x1 >= axisLimit) x3 = x1 + axisLimit - 1;
if (x1 - x3 >= axisLimit) x3 = x1 - axisLimit + 1;
if (y3 - y1 >= axisLimit) y3 = y1 + axisLimit - 1;
if (y1 - y3 >= axisLimit) y3 = y1 - axisLimit + 1;
if (z3 - z1 >= axisLimit) z3 = z1 + axisLimit - 1;
if (z1 - z3 >= axisLimit) z3 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = thirdPos;
}
}
public static BlockPos findHeight(Player player, BlockPos secondPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
@@ -27,7 +140,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
criteriaList.add(new HeightCriteria(zBound, secondPos, start));
//Remove invalid criteria
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
int reach = AttachmentHandler.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -52,127 +165,13 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
}
}
}
return new BlockPos(selected.lineBound);
return BlockPos.containing(selected.lineBound);
}
@Override
public void initialize(Player player) {
super.initialize(player);
secondPosTable.put(player.getUUID(), BlockPos.ZERO);
}
@Override
public List<BlockPos> onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUUID());
rightClickNr++;
rightClickTable.put(player.getUUID(), rightClickNr);
if (rightClickNr == 1) {
//If clicking in air, reset and try again
if (blockPos == null) {
rightClickTable.put(player.getUUID(), 0);
return list;
}
//First click, remember starting position
firstPosTable.put(player.getUUID(), blockPos);
sideHitTable.put(player.getUUID(), sideHit);
hitVecTable.put(player.getUUID(), hitVec);
//Keep list empty, dont place any blocks yet
} else if (rightClickNr == 2) {
//Second click, find other floor point
BlockPos firstPos = firstPosTable.get(player.getUUID());
BlockPos secondPos = findSecondPos(player, firstPos, true);
if (secondPos == null) {
rightClickTable.put(player.getUUID(), 1);
return list;
}
secondPosTable.put(player.getUUID(), secondPos);
} else {
//Third click, place diagonal wall with height
list = findCoordinates(player, blockPos, skipRaytrace);
rightClickTable.put(player.getUUID(), 0);
}
return list;
}
@Override
public List<BlockPos> findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUUID());
if (rightClickNr == 0) {
if (blockPos != null)
list.add(blockPos);
} else if (rightClickNr == 1) {
BlockPos firstPos = firstPosTable.get(player.getUUID());
BlockPos secondPos = findSecondPos(player, firstPos, true);
if (secondPos == null) return list;
//Limit amount of blocks you can place per row
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
//Add diagonal line from first to second
list.addAll(getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2));
} else {
BlockPos firstPos = firstPosTable.get(player.getUUID());
BlockPos secondPos = secondPosTable.get(player.getUUID());
BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, skipRaytrace);
if (thirdPos == null) return list;
//Limit amount of blocks you can place per row
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX(), x3 = thirdPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY(), y3 = thirdPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ(), z3 = thirdPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
if (x3 - x1 >= axisLimit) x3 = x1 + axisLimit - 1;
if (x1 - x3 >= axisLimit) x3 = x1 - axisLimit + 1;
if (y3 - y1 >= axisLimit) y3 = y1 + axisLimit - 1;
if (y1 - y3 >= axisLimit) y3 = y1 - axisLimit + 1;
if (z3 - z1 >= axisLimit) z3 = z1 + axisLimit - 1;
if (z1 - z3 >= axisLimit) z3 = z1 - axisLimit + 1;
//Add diagonal line from first to third
list.addAll(getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3));
}
return list;
}
// protected abstract BlockEntry findSecondPos(List<BlockEntry> blocks);
//Finds the place of the second block pos
// @Deprecated
protected abstract BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace);
//Finds the place of the third block pos
@@ -199,7 +198,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
//Make it from a plane into a line, on y axis only
private Vec3 toLongestLine(Vec3 boundVec, BlockPos secondPos) {
BlockPos bound = new BlockPos(boundVec);
BlockPos bound = BlockPos.containing(boundVec);
return new Vec3(secondPos.getX(), bound.getY(), secondPos.getZ());
}

View File

@@ -1,81 +1,75 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.UUID;
public abstract class TwoClicksBuildMode extends BaseBuildMode {
protected BlockEntry firstBlockEntry;
@Override
public List<BlockPos> onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUUID());
rightClickNr++;
rightClickTable.put(player.getUUID(), rightClickNr);
if (rightClickNr == 1) {
//If clicking in air, reset and try again
if (blockPos == null) {
rightClickTable.put(player.getUUID(), 0);
return list;
}
//First click, remember starting position
firstPosTable.put(player.getUUID(), blockPos);
sideHitTable.put(player.getUUID(), sideHit);
hitVecTable.put(player.getUUID(), hitVec);
//Keep list empty, dont place any blocks yet
} else {
//Second click, place blocks
list = findCoordinates(player, blockPos, skipRaytrace);
rightClickTable.put(player.getUUID(), 0);
}
return list;
public void initialize() {
super.initialize();
firstBlockEntry = null;
}
@Override
public List<BlockPos> findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUUID());
BlockPos firstPos = firstPosTable.get(player.getUUID());
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
//First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
//If clicking in air, reset and try again
if (firstBlockEntry == null) clicks = 0;
if (rightClickNr == 0) {
if (blockPos != null)
list.add(blockPos);
} else {
BlockPos secondPos = findSecondPos(player, firstPos, skipRaytrace);
if (secondPos == null) return list;
//Limit amount of blocks we can place per row
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
list.addAll(getAllBlocks(player, x1, y1, z1, x2, y2, z2));
//Second click, place blocks
clicks = 0;
return true;
}
return false;
}
return list;
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
//Limit amount of blocks we can place per row
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
int z1 = firstPos.getZ(), z2 = secondPos.getZ();
//limit axis
if (x2 - x1 >= axisLimit) x2 = x1 + axisLimit - 1;
if (x1 - x2 >= axisLimit) x2 = x1 - axisLimit + 1;
if (y2 - y1 >= axisLimit) y2 = y1 + axisLimit - 1;
if (y1 - y2 >= axisLimit) y2 = y1 - axisLimit + 1;
if (z2 - z1 >= axisLimit) z2 = z1 + axisLimit - 1;
if (z1 - z2 >= axisLimit) z2 = z1 - axisLimit + 1;
blocks.clear();
for (BlockPos pos : getAllBlocks(player, x1, y1, z1, x2, y2, z2)) {
if (blocks.containsKey(pos)) continue;
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
}
//Finds the place of the second block pos based on criteria (floor must be on same height as first click, wall on same plane etc)

View File

@@ -1,8 +1,8 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
@@ -20,7 +20,7 @@ public class DiagonalLine extends ThreeClicksBuildMode {
int iterations = (int) Math.ceil(first.distanceTo(second) * sampleMultiplier);
for (double t = 0; t <= 1.0; t += 1.0 / iterations) {
Vec3 lerp = first.add(second.subtract(first).scale(t));
BlockPos candidate = new BlockPos(lerp);
BlockPos candidate = BlockPos.containing(lerp);
//Only add if not equal to the last in the list
if (list.isEmpty() || !list.get(list.size() - 1).equals(candidate))
list.add(candidate);

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;

View File

@@ -0,0 +1,22 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Disabled implements IBuildMode {
@Override
public void initialize() {
}
@Override
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -1,12 +1,12 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
@@ -24,7 +24,7 @@ public class Floor extends TwoClicksBuildMode {
criteriaList.add(new Criteria(yBound, start));
//Remove invalid criteria
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
int reach = AttachmentHandler.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -33,7 +33,7 @@ public class Floor extends TwoClicksBuildMode {
//Then only 1 can be valid, return that one
Criteria selected = criteriaList.get(0);
return new BlockPos(selected.planeBound);
return BlockPos.containing(selected.planeBound);
}
public static List<BlockPos> getFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {

View File

@@ -1,11 +1,11 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
@@ -31,7 +31,7 @@ public class Line extends TwoClicksBuildMode {
criteriaList.add(new Criteria(zBound, firstPos, start));
//Remove invalid criteria
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
int reach = AttachmentHandler.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -58,7 +58,7 @@ public class Line extends TwoClicksBuildMode {
}
return new BlockPos(selected.lineBound);
return BlockPos.containing(selected.lineBound);
}
public static List<BlockPos> getLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@@ -89,7 +89,7 @@ public class Line extends TwoClicksBuildMode {
public static void addZLineBlocks(List<BlockPos> list, int z1, int z2, int x, int y) {
for (int z = z1; z1 < z2 ? z <= z2 : z >= z2; z += z1 < z2 ? 1 : -1) {
list.add(new BlockPos(x, y, z));
list.add(BlockPos.containing(x, y, z));
}
}
@@ -119,7 +119,7 @@ public class Line extends TwoClicksBuildMode {
//Make it from a plane into a line
//Select the axis that is longest
private Vec3 toLongestLine(Vec3 boundVec, BlockPos firstPos) {
BlockPos bound = new BlockPos(boundVec);
BlockPos bound = BlockPos.containing(boundVec);
BlockPos firstToSecond = bound.subtract(firstPos);
firstToSecond = new BlockPos(Math.abs(firstToSecond.getX()), Math.abs(firstToSecond.getY()), Math.abs(firstToSecond.getZ()));

View File

@@ -1,41 +0,0 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import java.util.ArrayList;
import java.util.List;
public class Normal implements IBuildMode {
@Override
public void initialize(Player player) {
}
@Override
public List<BlockPos> onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
if (blockPos != null) list.add(blockPos);
return list;
}
@Override
public List<BlockPos> findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
if (blockPos != null) list.add(blockPos);
return list;
}
@Override
public Direction getSideHit(Player player) {
return null;
}
@Override
public Vec3 getHitVec(Player player) {
return null;
}
}

View File

@@ -1,41 +0,0 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import java.util.ArrayList;
import java.util.List;
public class NormalPlus implements IBuildMode {
@Override
public void initialize(Player player) {
}
@Override
public List<BlockPos> onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
if (blockPos != null) list.add(blockPos);
return list;
}
@Override
public List<BlockPos> findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
if (blockPos != null) list.add(blockPos);
return list;
}
@Override
public Direction getSideHit(Player player) {
return null;
}
@Override
public Vec3 getHitVec(Player player) {
return null;
}
}

View File

@@ -2,11 +2,8 @@ package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
public class Pyramid extends ThreeClicksBuildMode {

View File

@@ -0,0 +1,22 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Single implements IBuildMode {
@Override
public void initialize() {
}
@Override
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -1,10 +1,10 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
@@ -15,7 +15,7 @@ public class SlopeFloor extends ThreeClicksBuildMode {
public static List<BlockPos> getSlopeFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
//Determine whether to use x or z axis to slope up
boolean onXAxis = true;

View File

@@ -1,8 +1,8 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;

View File

@@ -1,12 +1,12 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
@@ -28,7 +28,7 @@ public class Wall extends TwoClicksBuildMode {
criteriaList.add(new Criteria(zBound, firstPos, start, look));
//Remove invalid criteria
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
int reach = AttachmentHandler.getBuildModeReach(player);
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -48,7 +48,7 @@ public class Wall extends TwoClicksBuildMode {
}
}
return new BlockPos(selected.planeBound);
return BlockPos.containing(selected.planeBound);
}
public static List<BlockPos> getWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {

View File

@@ -1,104 +1,63 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraft.core.Vec3i;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.List;
public class Array extends BaseModifier {
public class Array {
public Vec3i offset = BlockPos.ZERO;
public int count = 5;
public static List<BlockPos> findCoordinates(Player player, BlockPos startPos) {
List<BlockPos> coordinates = new ArrayList<>();
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled || offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0) return;
//find arraysettings for the player
ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings();
if (!isEnabled(a)) return coordinates;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
var pos = blockEntry.blockPos;
for (int i = 0; i < count; i++) {
pos = pos.offset(offset);
if (blocks.containsKey(pos)) continue;
BlockPos pos = startPos;
Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ());
for (int i = 0; i < a.count; i++) {
pos = pos.offset(offset);
coordinates.add(pos);
}
return coordinates;
}
public static List<BlockState> findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List<ItemStack> itemStacks) {
List<BlockState> blockStates = new ArrayList<>();
//find arraysettings for the player that placed the block
ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings();
if (!isEnabled(a)) return blockStates;
BlockPos pos = startPos;
Vec3i offset = new Vec3i(a.offset.getX(), a.offset.getY(), a.offset.getZ());
//Randomizer bag synergy
AbstractRandomizerBagItem randomizerBagItem = null;
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) {
randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ;
bagInventory = randomizerBagItem.getBagInventory(itemStack);
}
for (int i = 0; i < a.count; i++) {
pos = pos.offset(offset);
//Randomizer bag synergy
if (randomizerBagItem != null) {
itemStack = randomizerBagItem.pickRandomStack(bagInventory);
blockState = BuildModifiers
.getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3(0, 0, 0), InteractionHand.MAIN_HAND);
var newBlockEntry = new BlockEntry(pos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
blocks.add(newBlockEntry);
}
//blockState = blockState.getBlock().getStateForPlacement(player.world, pos, )
blockStates.add(blockState);
itemStacks.add(itemStack);
}
return blockStates;
}
public static boolean isEnabled(ArraySettings a) {
if (a == null || !a.enabled) return false;
return a.offset.getX() != 0 || a.offset.getY() != 0 || a.offset.getZ() != 0;
}
public static class ArraySettings {
public boolean enabled = false;
public BlockPos offset = BlockPos.ZERO;
public int count = 5;
public ArraySettings() {
}
public ArraySettings(boolean enabled, BlockPos offset, int count) {
this.enabled = enabled;
this.offset = offset;
this.count = count;
}
public int getReach() {
//find largest offset
int x = Math.abs(offset.getX());
int y = Math.abs(offset.getY());
int z = Math.abs(offset.getZ());
int largestOffset = Math.max(Math.max(x, y), z);
return largestOffset * count;
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
}
public int getReach() {
//find largest offset
int x = Math.abs(offset.getX());
int y = Math.abs(offset.getY());
int z = Math.abs(offset.getZ());
int largestOffset = Math.max(Math.max(x, y), z);
return largestOffset * count;
}
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putIntArray("offset", new int[]{offset.getX(), offset.getY(), offset.getZ()});
compound.putInt("count", count);
return compound;
}
@Override
public void deserializeNBT(CompoundTag compound) {
super.deserializeNBT(compound);
int[] offsetArray = compound.getIntArray("offset");
offset = new Vec3i(offsetArray[0], offsetArray[1], offsetArray[2]);
count = compound.getInt("count");
}
}

View File

@@ -0,0 +1,24 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public abstract class BaseModifier {
public boolean enabled = true;
public abstract void findCoordinates(BlockSet blocks, Player player);
public abstract void onPowerLevelChanged(int powerLevel);
public CompoundTag serializeNBT() {
CompoundTag compound = new CompoundTag();
compound.putString("type", this.getClass().getSimpleName());
compound.putBoolean("enabled", enabled);
return compound;
}
public void deserializeNBT(CompoundTag compound) {
enabled = compound.getBoolean("enabled");
}
}

View File

@@ -1,50 +0,0 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import java.util.List;
public class BlockSet {
private final List<BlockPos> coordinates;
private final List<BlockState> previousBlockStates;
private final List<BlockState> newBlockStates;
private final Vec3 hitVec;
private final BlockPos firstPos;
private final BlockPos secondPos;
public BlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates, Vec3 hitVec,
BlockPos firstPos, BlockPos secondPos) {
this.coordinates = coordinates;
this.previousBlockStates = previousBlockStates;
this.newBlockStates = newBlockStates;
this.hitVec = hitVec;
this.firstPos = firstPos;
this.secondPos = secondPos;
}
public List<BlockPos> getCoordinates() {
return coordinates;
}
public List<BlockState> getPreviousBlockStates() {
return previousBlockStates;
}
public List<BlockState> getNewBlockStates() {
return newBlockStates;
}
public Vec3 getHitVec() {
return hitVec;
}
public BlockPos getFirstPos() {
return firstPos;
}
public BlockPos getSecondPos() {
return secondPos;
}
}

View File

@@ -1,267 +1,160 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.Blocks;
import net.createmod.catnip.nbt.NBTHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.level.Level;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.helper.InventoryHelper;
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.network.message.ModifierSettingsPacket;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@OnlyIn(Dist.CLIENT)
public class BuildModifiers {
private List<BaseModifier> modifierSettingsList = new ArrayList<>();
//Called from BuildModes
public static void onBlockPlaced(Player player, List<BlockPos> startCoordinates, Direction sideHit, Vec3 hitVec, boolean placeStartPos) {
Level world = player.level;
AbstractRandomizerBagItem.renewRandomness();
public List<BaseModifier> getModifierSettingsList() {
return Collections.unmodifiableList(modifierSettingsList);
}
//Format hitvec to 0.x
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z)));
public void addModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.add(modifierSettings);
}
//find coordinates and blockstates
List<BlockPos> coordinates = findCoordinates(player, startCoordinates);
List<ItemStack> itemStacks = new ArrayList<>();
List<BlockState> blockStates = findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks);
public void removeModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.remove(modifierSettings);
}
//check if valid blockstates
if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) return;
public void removeModifierSettings(int index) {
modifierSettingsList.remove(index);
}
//remember previous blockstates for undo
List<BlockState> previousBlockStates = new ArrayList<>(coordinates.size());
List<BlockState> newBlockStates = new ArrayList<>(coordinates.size());
for (BlockPos coordinate : coordinates) {
previousBlockStates.add(world.getBlockState(coordinate));
}
public void moveUp(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
if (world.isClientSide) {
Collections.swap(modifierSettingsList, index, index - 1);
}
BlockPreviewRenderer.onBlocksPlaced();
public void moveDown(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
newBlockStates = blockStates;
Collections.swap(modifierSettingsList, index, index + 1);
}
} else {
public void setFirst(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
//place blocks
for (int i = placeStartPos ? 0 : 1; i < coordinates.size(); i++) {
BlockPos blockPos = coordinates.get(i);
BlockState blockState = blockStates.get(i);
ItemStack itemStack = itemStacks.get(i);
modifierSettingsList.remove(index);
modifierSettingsList.add(0, modifierSettings);
}
if (world.isLoaded(blockPos)) {
//check itemstack empty
if (itemStack.isEmpty()) {
//try to find new stack, otherwise continue
itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock());
if (itemStack.isEmpty()) continue;
}
SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false);
}
}
public void setLast(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
//find actual new blockstates for undo
for (BlockPos coordinate : coordinates) {
newBlockStates.add(world.getBlockState(coordinate));
}
}
modifierSettingsList.remove(index);
modifierSettingsList.add(modifierSettings);
}
//Set first previousBlockState to empty if in NORMAL mode, to make undo/redo work
//(Block is placed by the time it gets here, and unplaced after this)
if (!placeStartPos) previousBlockStates.set(0, Blocks.AIR.defaultBlockState());
public void clearAllModifierSettings() {
modifierSettingsList.clear();
}
//If all new blockstates are air then no use in adding it, no block was actually placed
//Can happen when e.g. placing one block in yourself
if (Collections.frequency(newBlockStates, Blocks.AIR.defaultBlockState()) != newBlockStates.size()) {
//add to undo stack
BlockPos firstPos = startCoordinates.get(0);
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos));
public void findCoordinates(BlockSet blocks, Player player) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.findCoordinates(blocks, player);
}
}
public static void onBlockBroken(Player player, List<BlockPos> startCoordinates, boolean breakStartPos) {
Level world = player.level;
List<BlockPos> coordinates = findCoordinates(player, startCoordinates);
if (coordinates.isEmpty()) return;
//remember previous blockstates for undo
List<BlockState> previousBlockStates = new ArrayList<>(coordinates.size());
List<BlockState> newBlockStates = new ArrayList<>(coordinates.size());
for (BlockPos coordinate : coordinates) {
previousBlockStates.add(world.getBlockState(coordinate));
public void onPowerLevelChanged(int powerLevel) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.onPowerLevelChanged(powerLevel);
}
if (world.isClientSide) {
BlockPreviewRenderer.onBlocksBroken();
//list of air blockstates
for (int i = 0; i < coordinates.size(); i++) {
newBlockStates.add(Blocks.AIR.defaultBlockState());
}
} else {
//If the player is going to instabreak grass or a plant, only break other instabreaking things
boolean onlyInstaBreaking = !player.isCreative() &&
world.getBlockState(startCoordinates.get(0)).getDestroySpeed(world, startCoordinates.get(0)) == 0f;
//break all those blocks
for (int i = breakStartPos ? 0 : 1; i < coordinates.size(); i++) {
BlockPos coordinate = coordinates.get(i);
if (world.isLoaded(coordinate) && !world.isEmptyBlock(coordinate)) {
if (!onlyInstaBreaking || world.getBlockState(coordinate).getDestroySpeed(world, coordinate) == 0f) {
SurvivalHelper.breakBlock(world, player, coordinate, false);
}
}
}
//find actual new blockstates for undo
for (BlockPos coordinate : coordinates) {
newBlockStates.add(world.getBlockState(coordinate));
}
}
//Set first newBlockState to empty if in NORMAL mode, to make undo/redo work
//(Block isn't broken yet by the time it gets here, and broken after this)
if (!breakStartPos) newBlockStates.set(0, Blocks.AIR.defaultBlockState());
//add to undo stack
BlockPos firstPos = startCoordinates.get(0);
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
Vec3 hitVec = new Vec3(0.5, 0.5, 0.5);
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos));
}
public static List<BlockPos> findCoordinates(Player player, List<BlockPos> posList) {
List<BlockPos> coordinates = new ArrayList<>();
//Add current blocks being placed too
coordinates.addAll(posList);
//Find mirror/array/radial mirror coordinates for each blockpos
for (BlockPos blockPos : posList) {
List<BlockPos> arrayCoordinates = Array.findCoordinates(player, blockPos);
coordinates.addAll(arrayCoordinates);
coordinates.addAll(Mirror.findCoordinates(player, blockPos));
coordinates.addAll(RadialMirror.findCoordinates(player, blockPos));
//get mirror for each array coordinate
for (BlockPos coordinate : arrayCoordinates) {
coordinates.addAll(Mirror.findCoordinates(player, coordinate));
coordinates.addAll(RadialMirror.findCoordinates(player, coordinate));
}
}
return coordinates;
public CompoundTag serializeNBT() {
var compoundTag = new CompoundTag();
compoundTag.put("modifierSettingsList", NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT));
return compoundTag;
}
public static List<BlockPos> findCoordinates(Player player, BlockPos blockPos) {
return findCoordinates(player, new ArrayList<>(Collections.singletonList(blockPos)));
public void deserializeNBT(CompoundTag compoundTag) {
var listTag = compoundTag.getList("modifierSettingsList", Tag.TAG_COMPOUND);
modifierSettingsList = NBTHelper.readCompoundList(listTag, tag -> {
var modifier = createModifier(tag.getString("type"));
modifier.deserializeNBT(tag);
return modifier;
});
}
public static List<BlockState> findBlockStates(Player player, List<BlockPos> posList, Vec3 hitVec, Direction facing, List<ItemStack> itemStacks) {
List<BlockState> blockStates = new ArrayList<>();
itemStacks.clear();
public void save() {
PacketDistributor.sendToServer(new ModifierSettingsPacket(serializeNBT()));
//Get itemstack
ItemStack itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) {
itemStack = player.getItemInHand(InteractionHand.OFF_HAND);
}
if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) {
return blockStates;
}
//Get ItemBlock stack
ItemStack itemBlock = ItemStack.EMPTY;
if (itemStack.getItem() instanceof BlockItem) itemBlock = itemStack;
else itemBlock = CompatHelper.getItemBlockFromStack(itemStack);
AbstractRandomizerBagItem.resetRandomness();
//Add blocks in posList first
for (BlockPos blockPos : posList) {
if (!(itemStack.getItem() instanceof BlockItem)) itemBlock = CompatHelper.getItemBlockFromStack(itemStack);
BlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, InteractionHand.MAIN_HAND);
if (blockState == null) continue;
blockStates.add(blockState);
itemStacks.add(itemBlock);
}
for (BlockPos blockPos : posList) {
BlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, InteractionHand.MAIN_HAND);
if (blockState == null) continue;
List<BlockState> arrayBlockStates = Array.findBlockStates(player, blockPos, blockState, itemStack, itemStacks);
blockStates.addAll(arrayBlockStates);
blockStates.addAll(Mirror.findBlockStates(player, blockPos, blockState, itemStack, itemStacks));
blockStates.addAll(RadialMirror.findBlockStates(player, blockPos, blockState, itemStack, itemStacks));
//add mirror for each array coordinate
List<BlockPos> arrayCoordinates = Array.findCoordinates(player, blockPos);
for (int i = 0; i < arrayCoordinates.size(); i++) {
BlockPos coordinate = arrayCoordinates.get(i);
BlockState blockState1 = arrayBlockStates.get(i);
if (blockState1 == null) continue;
blockStates.addAll(Mirror.findBlockStates(player, coordinate, blockState1, itemStack, itemStacks));
blockStates.addAll(RadialMirror.findBlockStates(player, coordinate, blockState1, itemStack, itemStacks));
}
//Adjust blockstates for torches and ladders etc to place on a valid side
//TODO optimize findCoordinates (done twice now)
//TODO fix mirror
// List<BlockPos> coordinates = findCoordinates(player, startPos);
// for (int i = 0; i < blockStates.size(); i++) {
// blockStates.set(i, blockStates.get(i).getBlock().getStateForPlacement(player.world, coordinates.get(i), facing,
// (float) hitVec.x, (float) hitVec.y, (float) hitVec.z, itemStacks.get(i).getMetadata(), player, EnumHand.MAIN_HAND));
// }
}
return blockStates;
//Save locally as well?
// var listTag = NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT);
// player.getPersistentData().put(DATA_KEY, listTag);
}
public static boolean isEnabled(ModifierSettingsManager.ModifierSettings modifierSettings, BlockPos startPos) {
return Mirror.isEnabled(modifierSettings.getMirrorSettings(), startPos) ||
Array.isEnabled(modifierSettings.getArraySettings()) ||
RadialMirror.isEnabled(modifierSettings.getRadialMirrorSettings(), startPos) ||
modifierSettings.doQuickReplace();
}
public static BlockState getBlockStateFromItem(ItemStack itemStack, Player player, BlockPos blockPos, Direction facing, Vec3 hitVec, InteractionHand hand) {
return Block.byItem(itemStack.getItem()).getStateForPlacement(new BlockPlaceContext(new UseOnContext(player, hand, new BlockHitResult(hitVec, facing, blockPos, false))));
}
//Returns true if equal (or both null)
public static boolean compareCoordinates(List<BlockPos> coordinates1, List<BlockPos> coordinates2) {
if (coordinates1 == null && coordinates2 == null) return true;
if (coordinates1 == null || coordinates2 == null) return false;
//Check count, not actual values
if (coordinates1.size() == coordinates2.size()) {
if (coordinates1.size() == 1) {
return coordinates1.get(0).equals(coordinates2.get(0));
}
return true;
} else {
return false;
private BaseModifier createModifier(String type) {
switch (type) {
case "Mirror": return new Mirror();
case "Array": return new Array();
case "RadialMirror": return new RadialMirror();
default: throw new IllegalArgumentException("Unknown modifier type: " + type);
}
// return coordinates1.equals(coordinates2);
}
// public static String sanitize(ModifierSettingsManager.ModifierSettings modifierSettings, Player player) {
// int maxReach = ReachHelper.getMaxReach(player);
// String error = "";
//
// //Mirror settings
// Mirror.MirrorSettings m = modifierSettings.getMirrorSettings();
// if (m.radius < 1) {
// m.radius = 1;
// error += "Mirror size has to be at least 1. This has been corrected. ";
// }
// if (m.getReach() > maxReach) {
// m.radius = maxReach / 2;
// error += "Mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
// }
//
// //Array settings
// Array.ArraySettings a = modifierSettings.getArraySettings();
// if (a.count < 0) {
// a.count = 0;
// error += "Array count may not be negative. It has been reset to 0.";
// }
//
// if (a.getReach() > maxReach) {
// a.count = 0;
// error += "Array exceeds your maximum reach of " + maxReach + ". Array count has been reset to 0. ";
// }
//
// //Radial mirror settings
// RadialMirror.RadialMirrorSettings r = modifierSettings.getRadialMirrorSettings();
// if (r.slices < 2) {
// r.slices = 2;
// error += "Radial mirror needs to have at least 2 slices. Slices has been set to 2.";
// }
//
// if (r.radius < 1) {
// r.radius = 1;
// error += "Radial mirror radius has to be at least 1. This has been corrected. ";
// }
// if (r.getReach() > maxReach) {
// r.radius = maxReach / 2;
// error += "Radial mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
// }
//
// return error;
// }
}

View File

@@ -1,234 +1,150 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.properties.Half;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.List;
public class Mirror extends BaseModifier {
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public boolean mirrorX = true;
public boolean mirrorY = false;
public boolean mirrorZ = false;
public int radius = 10;
public boolean drawLines = true;
public boolean drawPlanes = true;
import net.minecraft.world.level.block.DirectionalBlock;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.SlabBlock;
import net.minecraft.world.level.block.StairBlock;
import net.minecraft.world.level.block.state.BlockState;
public class Mirror {
public static List<BlockPos> findCoordinates(Player player, BlockPos startPos) {
List<BlockPos> coordinates = new ArrayList<>();
//find mirrorsettings for the player
MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings();
if (!isEnabled(m, startPos)) return coordinates;
if (m.mirrorX) coordinateMirrorX(m, startPos, coordinates);
if (m.mirrorY) coordinateMirrorY(m, startPos, coordinates);
if (m.mirrorZ) coordinateMirrorZ(m, startPos, coordinates);
return coordinates;
public Mirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
}
private static void coordinateMirrorX(MirrorSettings m, BlockPos oldBlockPos, List<BlockPos> coordinates) {
//find mirror position
double x = m.position.x + (m.position.x - oldBlockPos.getX() - 0.5);
BlockPos newBlockPos = new BlockPos(x, oldBlockPos.getY(), oldBlockPos.getZ());
coordinates.add(newBlockPos);
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!(enabled && (mirrorX || mirrorY || mirrorZ))) return;
if (m.mirrorY) coordinateMirrorY(m, newBlockPos, coordinates);
if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates);
}
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
private static void coordinateMirrorY(MirrorSettings m, BlockPos oldBlockPos, List<BlockPos> coordinates) {
//find mirror position
double y = m.position.y + (m.position.y - oldBlockPos.getY() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), y, oldBlockPos.getZ());
coordinates.add(newBlockPos);
if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates);
}
private static void coordinateMirrorZ(MirrorSettings m, BlockPos oldBlockPos, List<BlockPos> coordinates) {
//find mirror position
double z = m.position.z + (m.position.z - oldBlockPos.getZ() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), oldBlockPos.getY(), z);
coordinates.add(newBlockPos);
}
public static List<BlockState> findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List<ItemStack> itemStacks) {
List<BlockState> blockStates = new ArrayList<>();
//find mirrorsettings for the player
MirrorSettings m = ModifierSettingsManager.getModifierSettings(player).getMirrorSettings();
if (!isEnabled(m, startPos)) return blockStates;
//Randomizer bag synergy
AbstractRandomizerBagItem randomizerBagItem = null;
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) {
randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ;
bagInventory = randomizerBagItem.getBagInventory(itemStack);
if (mirrorX) performMirrorX(blocks, blockEntry);
if (mirrorY) performMirrorY(blocks, blockEntry);
if (mirrorZ) performMirrorZ(blocks, blockEntry);
}
if (m.mirrorX)
blockStateMirrorX(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks);
if (m.mirrorY)
blockStateMirrorY(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks);
if (m.mirrorZ)
blockStateMirrorZ(player, m, startPos, blockState, bagInventory, itemStack, InteractionHand.MAIN_HAND, blockStates, itemStacks);
return blockStates;
}
private static void blockStateMirrorX(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
@Override
public void onPowerLevelChanged(int powerLevel) {
radius = AttachmentHandler.getMaxMirrorRadius(Minecraft.getInstance().player, false);
}
private void performMirrorX(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double x = m.position.x + (m.position.x - oldBlockPos.getX() - 0.5);
BlockPos newBlockPos = new BlockPos(x, oldBlockPos.getY(), oldBlockPos.getZ());
double x = position.x + (position.x - blockEntry.blockPos.getX() - 0.5);
BlockPos newBlockPos = BlockPos.containing(x, blockEntry.blockPos.getY(), blockEntry.blockPos.getZ());
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand);
}
if (blocks.containsKey(newBlockPos)) return;
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.world.level.block.Mirror.FRONT_BACK);
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorX = !newBlockEntry.mirrorX;
blocks.add(newBlockEntry);
//Store blockstate and itemstack
blockStates.add(newBlockState);
itemStacks.add(itemStack);
if (m.mirrorY)
blockStateMirrorY(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks);
if (m.mirrorZ)
blockStateMirrorZ(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks);
if (mirrorY) performMirrorY(blocks, newBlockEntry);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private static void blockStateMirrorY(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
private void performMirrorY(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double y = m.position.y + (m.position.y - oldBlockPos.getY() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), y, oldBlockPos.getZ());
double y = position.y + (position.y - blockEntry.blockPos.getY() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), y, blockEntry.blockPos.getZ());
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand);
}
if (blocks.containsKey(newBlockPos)) return;
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : getVerticalMirror(oldBlockState);
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorY = !newBlockEntry.mirrorY;
blocks.add(newBlockEntry);
//Store blockstate and itemstack
blockStates.add(newBlockState);
itemStacks.add(itemStack);
if (m.mirrorZ)
blockStateMirrorZ(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private static void blockStateMirrorZ(Player player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, InteractionHand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
private void performMirrorZ(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double z = m.position.z + (m.position.z - oldBlockPos.getZ() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), oldBlockPos.getY(), z);
double z = position.z + (position.z - blockEntry.blockPos.getZ() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), blockEntry.blockPos.getY(), z);
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ((AbstractRandomizerBagItem)itemStack.getItem()).pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3(0, 0, 0), hand);
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorZ = !newBlockEntry.mirrorZ;
blocks.add(newBlockEntry);
}
public boolean isWithinRange(BlockPos blockPos) {
return !(blockPos.getX() + 0.5 < position.x - radius) && !(blockPos.getX() + 0.5 > position.x + radius) &&
!(blockPos.getY() + 0.5 < position.y - radius) && !(blockPos.getY() + 0.5 > position.y + radius) &&
!(blockPos.getZ() + 0.5 < position.z - radius) && !(blockPos.getZ() + 0.5 > position.z + radius);
}
public int getReach() {
return radius * 2; //Change ModifierSettings#setReachUpgrade too
}
public void toggleMirrorAxis(int index) {
switch (index) {
case 0 -> mirrorX = !mirrorX;
case 1 -> mirrorY = !mirrorY;
case 2 -> mirrorZ = !mirrorZ;
}
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.world.level.block.Mirror.LEFT_RIGHT);
//Store blockstate and itemstack
blockStates.add(newBlockState);
itemStacks.add(itemStack);
}
public static boolean isEnabled(MirrorSettings m, BlockPos startPos) {
if (m == null || !m.enabled || (!m.mirrorX && !m.mirrorY && !m.mirrorZ)) return false;
//within mirror distance
return !(startPos.getX() + 0.5 < m.position.x - m.radius) && !(startPos.getX() + 0.5 > m.position.x + m.radius) &&
!(startPos.getY() + 0.5 < m.position.y - m.radius) && !(startPos.getY() + 0.5 > m.position.y + m.radius) &&
!(startPos.getZ() + 0.5 < m.position.z - m.radius) && !(startPos.getZ() + 0.5 > m.position.z + m.radius);
}
private static BlockState getVerticalMirror(BlockState blockState) {
//Stairs
if (blockState.getBlock() instanceof StairBlock) {
if (blockState.getValue(StairBlock.HALF) == Half.BOTTOM) {
return blockState.setValue(StairBlock.HALF, Half.TOP);
} else {
return blockState.setValue(StairBlock.HALF, Half.BOTTOM);
public boolean getMirrorAxis(int index) {
switch (index) {
case 0 -> {
return mirrorX;
}
case 1 -> {
return mirrorY;
}
case 2 -> {
return mirrorZ;
}
}
//Slabs
if (blockState.getBlock() instanceof SlabBlock) {
if (blockState.getValue(SlabBlock.TYPE) == SlabType.DOUBLE) {
return blockState;
} else if (blockState.getValue(SlabBlock.TYPE) == SlabType.BOTTOM) {
return blockState.setValue(SlabBlock.TYPE, SlabType.TOP);
} else {
return blockState.setValue(SlabBlock.TYPE, SlabType.BOTTOM);
}
}
//Buttons, endrod, observer, piston
if (blockState.getBlock() instanceof DirectionalBlock) {
if (blockState.getValue(DirectionalBlock.FACING) == Direction.DOWN) {
return blockState.setValue(DirectionalBlock.FACING, Direction.UP);
} else if (blockState.getValue(DirectionalBlock.FACING) == Direction.UP) {
return blockState.setValue(DirectionalBlock.FACING, Direction.DOWN);
}
}
//Dispenser, dropper
if (blockState.getBlock() instanceof DispenserBlock) {
if (blockState.getValue(DispenserBlock.FACING) == Direction.DOWN) {
return blockState.setValue(DispenserBlock.FACING, Direction.UP);
} else if (blockState.getValue(DispenserBlock.FACING) == Direction.UP) {
return blockState.setValue(DispenserBlock.FACING, Direction.DOWN);
}
}
return blockState;
return false;
}
public static class MirrorSettings {
public boolean enabled = false;
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public boolean mirrorX = true, mirrorY = false, mirrorZ = false;
public int radius = 10;
public boolean drawLines = true, drawPlanes = true;
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putDouble("positionX", position.x);
compound.putDouble("positionY", position.y);
compound.putDouble("positionZ", position.z);
compound.putBoolean("mirrorX", mirrorX);
compound.putBoolean("mirrorY", mirrorY);
compound.putBoolean("mirrorZ", mirrorZ);
compound.putInt("radius", radius);
compound.putBoolean("drawLines", drawLines);
compound.putBoolean("drawPlanes", drawPlanes);
return compound;
}
public MirrorSettings() {
}
public MirrorSettings(boolean mirrorEnabled, Vec3 position, boolean mirrorX, boolean mirrorY, boolean mirrorZ, int radius, boolean drawLines, boolean drawPlanes) {
this.enabled = mirrorEnabled;
this.position = position;
this.mirrorX = mirrorX;
this.mirrorY = mirrorY;
this.mirrorZ = mirrorZ;
this.radius = radius;
this.drawLines = drawLines;
this.drawPlanes = drawPlanes;
}
public int getReach() {
return radius * 2; //Change ModifierSettings#setReachUpgrade too
}
@Override
public void deserializeNBT(CompoundTag compound) {
super.deserializeNBT(compound);
position = new Vec3(compound.getDouble("positionX"), compound.getDouble("positionY"), compound.getDouble("positionZ"));
mirrorX = compound.getBoolean("mirrorX");
mirrorY = compound.getBoolean("mirrorY");
mirrorZ = compound.getBoolean("mirrorZ");
radius = compound.getInt("radius");
drawLines = compound.getBoolean("drawLines");
drawPlanes = compound.getBoolean("drawPlanes");
}
}

View File

@@ -1,215 +0,0 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.BuildConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import nl.requios.effortlessbuilding.network.ModifierSettingsMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
import javax.annotation.Nonnull;
@Mod.EventBusSubscriber
public class ModifierSettingsManager {
//Retrieves the buildsettings of a player through the modifierCapability capability
//Never returns null
@Nonnull
public static ModifierSettings getModifierSettings(Player player) {
LazyOptional<ModifierCapabilityManager.IModifierCapability> modifierCapability =
player.getCapability(ModifierCapabilityManager.MODIFIER_CAPABILITY, null);
if (modifierCapability.isPresent()) {
ModifierCapabilityManager.IModifierCapability capability = modifierCapability.orElse(null);
if (capability.getModifierData() == null){
capability.setModifierData(new ModifierSettings());
}
return capability.getModifierData();
}
EffortlessBuilding.logger.warn("Player does not have modifierCapability: " + player);
//Return dummy settings
return new ModifierSettings();
}
public static void setModifierSettings(Player player, ModifierSettings modifierSettings) {
if (player == null) {
EffortlessBuilding.log("Cannot set buildsettings, player is null");
return;
}
LazyOptional<ModifierCapabilityManager.IModifierCapability> modifierCapability =
player.getCapability(ModifierCapabilityManager.MODIFIER_CAPABILITY, null);
modifierCapability.ifPresent((capability) -> {
capability.setModifierData(modifierSettings);
});
if (!modifierCapability.isPresent()) {
EffortlessBuilding.log(player, "Saving buildsettings failed.");
}
}
public static String sanitize(ModifierSettings modifierSettings, Player player) {
int maxReach = ReachHelper.getMaxReach(player);
String error = "";
//Mirror settings
Mirror.MirrorSettings m = modifierSettings.getMirrorSettings();
if (m.radius < 1) {
m.radius = 1;
error += "Mirror size has to be at least 1. This has been corrected. ";
}
if (m.getReach() > maxReach) {
m.radius = maxReach / 2;
error += "Mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
}
//Array settings
Array.ArraySettings a = modifierSettings.getArraySettings();
if (a.count < 0) {
a.count = 0;
error += "Array count may not be negative. It has been reset to 0.";
}
if (a.getReach() > maxReach) {
a.count = 0;
error += "Array exceeds your maximum reach of " + maxReach + ". Array count has been reset to 0. ";
}
//Radial mirror settings
RadialMirror.RadialMirrorSettings r = modifierSettings.getRadialMirrorSettings();
if (r.slices < 2) {
r.slices = 2;
error += "Radial mirror needs to have at least 2 slices. Slices has been set to 2.";
}
if (r.radius < 1) {
r.radius = 1;
error += "Radial mirror radius has to be at least 1. This has been corrected. ";
}
if (r.getReach() > maxReach) {
r.radius = maxReach / 2;
error += "Radial mirror exceeds your maximum reach of " + (maxReach / 2) + ". Radius has been set to " + (maxReach / 2) + ". ";
}
//Other
if (modifierSettings.reachUpgrade < 0) {
modifierSettings.reachUpgrade = 0;
}
if (modifierSettings.reachUpgrade > 3) {
modifierSettings.reachUpgrade = 3;
}
return error;
}
public static void handleNewPlayer(Player player) {
//Makes sure player has modifier settings (if it doesnt it will create it)
getModifierSettings(player);
//Only on server
if (!player.level.isClientSide) {
//Send to client
ModifierSettingsMessage msg = new ModifierSettingsMessage(getModifierSettings(player));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), msg);
}
}
public static class ModifierSettings {
private Mirror.MirrorSettings mirrorSettings;
private Array.ArraySettings arraySettings;
private RadialMirror.RadialMirrorSettings radialMirrorSettings;
private boolean quickReplace = false;
private int reachUpgrade = 0;
public ModifierSettings() {
mirrorSettings = new Mirror.MirrorSettings();
arraySettings = new Array.ArraySettings();
radialMirrorSettings = new RadialMirror.RadialMirrorSettings();
}
public ModifierSettings(Mirror.MirrorSettings mirrorSettings, Array.ArraySettings arraySettings,
RadialMirror.RadialMirrorSettings radialMirrorSettings, boolean quickReplace, int reachUpgrade) {
this.mirrorSettings = mirrorSettings;
this.arraySettings = arraySettings;
this.radialMirrorSettings = radialMirrorSettings;
this.quickReplace = quickReplace;
this.reachUpgrade = reachUpgrade;
}
public Mirror.MirrorSettings getMirrorSettings() {
if (this.mirrorSettings == null) this.mirrorSettings = new Mirror.MirrorSettings();
return this.mirrorSettings;
}
public void setMirrorSettings(Mirror.MirrorSettings mirrorSettings) {
if (mirrorSettings == null) return;
this.mirrorSettings = mirrorSettings;
}
public Array.ArraySettings getArraySettings() {
if (this.arraySettings == null) this.arraySettings = new Array.ArraySettings();
return this.arraySettings;
}
public void setArraySettings(Array.ArraySettings arraySettings) {
if (arraySettings == null) return;
this.arraySettings = arraySettings;
}
public RadialMirror.RadialMirrorSettings getRadialMirrorSettings() {
if (this.radialMirrorSettings == null) this.radialMirrorSettings = new RadialMirror.RadialMirrorSettings();
return this.radialMirrorSettings;
}
public void setRadialMirrorSettings(RadialMirror.RadialMirrorSettings radialMirrorSettings) {
if (radialMirrorSettings == null) return;
this.radialMirrorSettings = radialMirrorSettings;
}
public boolean doQuickReplace() {
return quickReplace;
}
public void setQuickReplace(boolean quickReplace) {
this.quickReplace = quickReplace;
}
public int getReachUpgrade() {
return reachUpgrade;
}
public void setReachUpgrade(int reachUpgrade) {
this.reachUpgrade = reachUpgrade;
//Set mirror radius to max
int reach = 10;
switch (reachUpgrade) {
case 0:
reach = BuildConfig.reach.maxReachLevel0.get();
break;
case 1:
reach = BuildConfig.reach.maxReachLevel1.get();
break;
case 2:
reach = BuildConfig.reach.maxReachLevel2.get();
break;
case 3:
reach = BuildConfig.reach.maxReachLevel3.get();
break;
}
if (this.mirrorSettings != null)
this.mirrorSettings.radius = reach / 2;
if (this.radialMirrorSettings != null)
this.radialMirrorSettings.radius = reach / 2;
}
}
}

View File

@@ -1,129 +1,122 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.List;
public class RadialMirror extends BaseModifier {
public class RadialMirror {
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public int slices = 4;
public boolean alternate = false;
public int radius = 20;
public boolean drawLines = true;
public boolean drawPlanes = false;
public static List<BlockPos> findCoordinates(Player player, BlockPos startPos) {
List<BlockPos> coordinates = new ArrayList<>();
public RadialMirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
}
//find radial mirror settings for the player
RadialMirrorSettings r = ModifierSettingsManager.getModifierSettings(player).getRadialMirrorSettings();
if (!isEnabled(r, startPos)) return coordinates;
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled) return;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
performRadialMirror(blocks, blockEntry);
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
radius = AttachmentHandler.getMaxMirrorRadius(Minecraft.getInstance().player, false);
}
public void performRadialMirror(BlockSet blocks, BlockEntry blockEntry) {
//get angle between slices
double sliceAngle = 2 * Math.PI / r.slices;
double sliceAngle = 2 * Math.PI / slices;
Vec3 startVec = new Vec3(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f);
Vec3 relStartVec = startVec.subtract(r.position);
//Get start vector relative to mirror center
Vec3 relStartVec = Vec3.atCenterOf(blockEntry.blockPos).subtract(position);
double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z);
double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z); //between -PI and PI
//TODO change to Abs if alternative?
if (startAngleToCenter < 0) startAngleToCenter += Math.PI;
double startAngleInSlice = startAngleToCenter % sliceAngle;
for (int i = 1; i < r.slices; i++) {
for (int i = 1; i < slices; i++) {
double curAngle = sliceAngle * i;
//alternate mirroring of slices
if (r.alternate && i % 2 == 1) {
boolean doAlternate = alternate && i % 2 == 1;
if (doAlternate) {
curAngle = curAngle - startAngleInSlice + (sliceAngle - startAngleInSlice);
}
Vec3 relNewVec = relStartVec.yRot((float) curAngle);
BlockPos newBlockPos = new BlockPos(r.position.add(relNewVec));
if (!coordinates.contains(newBlockPos) && !newBlockPos.equals(startPos)) coordinates.add(newBlockPos);
}
BlockPos newBlockPos = BlockPos.containing(position.add(relNewVec));
return coordinates;
if (blocks.containsKey(newBlockPos)) continue;
BlockEntry newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
//rotate block
double angleToCenter = Mth.atan2(relNewVec.x, relNewVec.z); //between -PI and PI
rotateBlockEntry(newBlockEntry, angleToCenter, doAlternate);
blocks.add(newBlockEntry);
}
}
public static List<BlockState> findBlockStates(Player player, BlockPos startPos, BlockState blockState, ItemStack itemStack, List<ItemStack> itemStacks) {
List<BlockState> blockStates = new ArrayList<>();
List<BlockPos> coordinates = new ArrayList<>(); //to keep track of duplicates
private void rotateBlockEntry(BlockEntry blockEntry, double angleToCenter, boolean alternate) {
//find radial mirror settings for the player that placed the block
RadialMirrorSettings r = ModifierSettingsManager.getModifierSettings(player).getRadialMirrorSettings();
if (!isEnabled(r, startPos)) return blockStates;
//get angle between slices
double sliceAngle = 2 * Math.PI / r.slices;
Vec3 startVec = new Vec3(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f);
Vec3 relStartVec = startVec.subtract(r.position);
double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z);
double startAngleToCenterMod = startAngleToCenter < 0 ? startAngleToCenter + Math.PI : startAngleToCenter;
double startAngleInSlice = startAngleToCenterMod % sliceAngle;
//Rotate the original blockstate
blockState = rotateOriginalBlockState(player, startPos, startAngleToCenter, blockState);
//Randomizer bag synergy
AbstractRandomizerBagItem randomizerBagItem = null;
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof AbstractRandomizerBagItem) {
randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem() ;
bagInventory = randomizerBagItem.getBagInventory(itemStack);
}
BlockState newBlockState;
for (int i = 1; i < r.slices; i++) {
newBlockState = blockState;
double curAngle = sliceAngle * i;
//alternate mirroring of slices
if (r.alternate && i % 2 == 1) {
curAngle = curAngle - startAngleInSlice + (sliceAngle - startAngleInSlice);
if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.CLOCKWISE_180);
if (alternate) {
// blockEntry.mirrorX = !blockEntry.mirrorX;
}
Vec3 relNewVec = relStartVec.yRot((float) curAngle);
BlockPos newBlockPos = new BlockPos(r.position.add(relNewVec));
if (coordinates.contains(newBlockPos) || newBlockPos.equals(startPos)) continue; //filter out duplicates
coordinates.add(newBlockPos);
//Randomizer bag synergy
if (randomizerBagItem != null) {
itemStack = randomizerBagItem.pickRandomStack(bagInventory);
newBlockState = BuildModifiers
.getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3(0, 0, 0), InteractionHand.MAIN_HAND);
newBlockState = rotateOriginalBlockState(player, startPos, startAngleToCenter, newBlockState);
} else if (angleToCenter < -0.251 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.CLOCKWISE_90);
if (alternate) {
// blockEntry.mirrorZ = !blockEntry.mirrorZ;
}
} else if (angleToCenter > 0.249 * Math.PI) {
blockEntry.rotation = blockEntry.rotation.getRotated(Rotation.COUNTERCLOCKWISE_90);
if (alternate) {
// blockEntry.mirrorZ = !blockEntry.mirrorZ;
}
} else {
if (alternate) {
// blockEntry.mirrorX = !blockEntry.mirrorX;
}
//rotate
newBlockState = rotateBlockState(player, startPos, relNewVec, newBlockState, r.alternate && i % 2 == 1);
blockStates.add(newBlockState);
itemStacks.add(itemStack);
}
return blockStates;
}
private static BlockState rotateOriginalBlockState(Player player, BlockPos startPos, double startAngleToCenter, BlockState blockState) {
BlockState newBlockState = blockState;
if (startAngleToCenter < -0.751 * Math.PI || startAngleToCenter > 0.749 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.CLOCKWISE_180);
} else if (startAngleToCenter < -0.251 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.COUNTERCLOCKWISE_90);
} else if (startAngleToCenter > 0.249 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.CLOCKWISE_90);
}
return newBlockState;
@@ -134,17 +127,17 @@ public class RadialMirror {
double angleToCenter = Mth.atan2(relVec.x, relVec.z); //between -PI and PI
if (angleToCenter < -0.751 * Math.PI || angleToCenter > 0.749 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_180);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.CLOCKWISE_180);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.FRONT_BACK);
}
} else if (angleToCenter < -0.251 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.CLOCKWISE_90);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.CLOCKWISE_90);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT);
}
} else if (angleToCenter > 0.249 * Math.PI) {
newBlockState = blockState.rotate(player.level, startPos, Rotation.COUNTERCLOCKWISE_90);
newBlockState = blockState.rotate(player.level(), startPos, Rotation.COUNTERCLOCKWISE_90);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT);
}
@@ -158,37 +151,36 @@ public class RadialMirror {
return newBlockState;
}
public static boolean isEnabled(RadialMirrorSettings r, BlockPos startPos) {
if (r == null || !r.enabled) return false;
return !(new Vec3(startPos.getX() + 0.5, startPos.getY() + 0.5, startPos.getZ() + 0.5).subtract(r.position).lengthSqr() >
r.radius * r.radius);
public boolean isWithinRange(BlockPos startPos) {
return (new Vec3(startPos.getX() + 0.5, startPos.getY() + 0.5, startPos.getZ() + 0.5).subtract(position).lengthSqr() < radius * radius);
}
public static class RadialMirrorSettings {
public boolean enabled = false;
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public int slices = 4;
public boolean alternate = false;
public int radius = 20;
public boolean drawLines = true, drawPlanes = false;
public RadialMirrorSettings() {
}
public RadialMirrorSettings(boolean enabled, Vec3 position, int slices, boolean alternate, int radius, boolean drawLines, boolean drawPlanes) {
this.enabled = enabled;
this.position = position;
this.slices = slices;
this.alternate = alternate;
this.radius = radius;
this.drawLines = drawLines;
this.drawPlanes = drawPlanes;
}
public int getReach() {
return radius * 2;
}
public int getReach() {
return radius * 2;
}
@Override
public CompoundTag serializeNBT() {
var compound = super.serializeNBT();
compound.putDouble("positionX", position.x);
compound.putDouble("positionY", position.y);
compound.putDouble("positionZ", position.z);
compound.putInt("slices", slices);
compound.putBoolean("alternate", alternate);
compound.putInt("radius", radius);
compound.putBoolean("drawLines", drawLines);
compound.putBoolean("drawPlanes", drawPlanes);
return compound;
}
@Override
public void deserializeNBT(CompoundTag nbt) {
super.deserializeNBT(nbt);
position = new Vec3(nbt.getDouble("positionX"), nbt.getDouble("positionY"), nbt.getDouble("positionZ"));
slices = nbt.getInt("slices");
alternate = nbt.getBoolean("alternate");
radius = nbt.getInt("radius");
drawLines = nbt.getBoolean("drawLines");
drawPlanes = nbt.getBoolean("drawPlanes");
}
}

View File

@@ -1,239 +0,0 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraft.server.level.ServerLevel;
import nl.requios.effortlessbuilding.BuildConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.helper.FixedStack;
import nl.requios.effortlessbuilding.helper.InventoryHelper;
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
import java.util.*;
public class UndoRedo {
//Undo and redo stacks per player
//Gets added to twice in singleplayer (server and client) if not careful. So separate stacks.
private static final Map<UUID, FixedStack<BlockSet>> undoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> undoStacksServer = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> redoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> redoStacksServer = new HashMap<>();
//add to undo stack
public static void addUndo(Player player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
//Assert coordinates is as long as previous and new blockstate lists
if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() ||
blockSet.getCoordinates().size() != blockSet.getNewBlockStates().size()) {
EffortlessBuilding.logger.error("Coordinates and blockstate lists are not equal length. Coordinates: {}. Previous blockstates: {}. New blockstates: {}.",
blockSet.getCoordinates().size(), blockSet.getPreviousBlockStates().size(), blockSet.getNewBlockStates().size());
}
//Warn if previous and new blockstate are equal
//Can happen in a lot of valid cases
// for (int i = 0; i < blockSet.getCoordinates().size(); i++) {
// if (blockSet.getPreviousBlockStates().get(i).equals(blockSet.getNewBlockStates().get(i))) {
// EffortlessBuilding.logger.warn("Previous and new blockstates are equal at index {}. Blockstate: {}.",
// i, blockSet.getPreviousBlockStates().get(i));
// }
// }
//If no stack exists, make one
if (!undoStacks.containsKey(player.getUUID())) {
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
}
undoStacks.get(player.getUUID()).push(blockSet);
}
private static void addRedo(Player player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
//(No asserts necessary, it's private)
//If no stack exists, make one
if (!redoStacks.containsKey(player.getUUID())) {
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
}
redoStacks.get(player.getUUID()).push(blockSet);
}
public static boolean undo(Player player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
if (!undoStacks.containsKey(player.getUUID())) return false;
FixedStack<BlockSet> undoStack = undoStacks.get(player.getUUID());
if (undoStack.isEmpty()) return false;
BlockSet blockSet = undoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
Vec3 hitVec = blockSet.getHitVec();
//Find up to date itemstacks in player inventory
List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
if (player.level.isClientSide) {
BlockPreviewRenderer.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos());
} else {
//break all those blocks, reset to what they were
for (int i = 0; i < coordinates.size(); i++) {
BlockPos coordinate = coordinates.get(i);
ItemStack itemStack = itemStacks.get(i);
if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
//get blockstate from itemstack
BlockState previousBlockState = Blocks.AIR.defaultBlockState();
if (itemStack.getItem() instanceof BlockItem) {
previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
}
if (player.level.isLoaded(coordinate)) {
//check itemstack empty
if (itemStack.isEmpty()) {
itemStack = findItemStackInInventory(player, previousBlockStates.get(i));
//get blockstate from new itemstack
if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
} else {
if (previousBlockStates.get(i).getBlock() != Blocks.AIR)
EffortlessBuilding.logTranslate(player, "", previousBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
previousBlockState = Blocks.AIR.defaultBlockState();
}
}
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
//if previousBlockState is air, placeBlock will set it to air
SurvivalHelper.placeBlock(player.level, player, coordinate, previousBlockState, itemStack, Direction.UP, hitVec, true, false, false);
}
}
}
//add to redo
addRedo(player, blockSet);
return true;
}
public static boolean redo(Player player) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
if (!redoStacks.containsKey(player.getUUID())) return false;
FixedStack<BlockSet> redoStack = redoStacks.get(player.getUUID());
if (redoStack.isEmpty()) return false;
BlockSet blockSet = redoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
Vec3 hitVec = blockSet.getHitVec();
//Find up to date itemstacks in player inventory
List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
if (player.level.isClientSide) {
BlockPreviewRenderer.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos());
} else {
//place blocks
for (int i = 0; i < coordinates.size(); i++) {
BlockPos coordinate = coordinates.get(i);
ItemStack itemStack = itemStacks.get(i);
if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
//get blockstate from itemstack
BlockState newBlockState = Blocks.AIR.defaultBlockState();
if (itemStack.getItem() instanceof BlockItem) {
newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
}
if (player.level.isLoaded(coordinate)) {
//check itemstack empty
if (itemStack.isEmpty()) {
itemStack = findItemStackInInventory(player, newBlockStates.get(i));
//get blockstate from new itemstack
if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
} else {
if (newBlockStates.get(i).getBlock() != Blocks.AIR)
EffortlessBuilding.logTranslate(player, "", newBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
newBlockState = Blocks.AIR.defaultBlockState();
}
}
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
SurvivalHelper.placeBlock(player.level, player, coordinate, newBlockState, itemStack, Direction.UP, hitVec, true, false, false);
}
}
}
//add to undo
addUndo(player, blockSet);
return true;
}
public static void clear(Player player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
if (undoStacks.containsKey(player.getUUID())) {
undoStacks.get(player.getUUID()).clear();
}
if (redoStacks.containsKey(player.getUUID())) {
redoStacks.get(player.getUUID()).clear();
}
}
private static List<ItemStack> findItemStacksInInventory(Player player, List<BlockState> blockStates) {
List<ItemStack> itemStacks = new ArrayList<>(blockStates.size());
for (BlockState blockState : blockStates) {
itemStacks.add(findItemStackInInventory(player, blockState));
}
return itemStacks;
}
private static ItemStack findItemStackInInventory(Player player, BlockState blockState) {
ItemStack itemStack = ItemStack.EMPTY;
if (blockState == null) return itemStack;
//First try previousBlockStates
//TODO try to find itemstack with right blockstate first
// then change line 103 back (get state from item)
itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock());
//then anything it drops
if (itemStack.isEmpty()) {
//Cannot check drops on clientside because loot tables are server only
if (!player.level.isClientSide) {
List<ItemStack> itemsDropped = Block.getDrops(blockState, (ServerLevel) player.level, BlockPos.ZERO, null);
for (ItemStack itemStackDropped : itemsDropped) {
if (itemStackDropped.getItem() instanceof BlockItem) {
Block block = ((BlockItem) itemStackDropped.getItem()).getBlock();
itemStack = InventoryHelper.findItemStackInInventory(player, block);
}
}
}
}
//then air
//(already empty)
return itemStack;
}
}

View File

@@ -1,37 +0,0 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ItemHandlerCapabilityProvider implements ICapabilitySerializable<CompoundTag> {
IItemHandler itemHandler;
public ItemHandlerCapabilityProvider(int size) {
itemHandler = new ItemStackHandler(size);
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.orEmpty(cap, LazyOptional.of(() -> itemHandler));
}
@Override
public CompoundTag serializeNBT() {
return ((ItemStackHandler) itemHandler).serializeNBT();
}
@Override
public void deserializeNBT(CompoundTag nbt) {
((ItemStackHandler) itemHandler).deserializeNBT(nbt);
}
}

View File

@@ -1,108 +0,0 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.core.Direction;
import net.minecraftforge.common.capabilities.*;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings;
@Mod.EventBusSubscriber
public class ModeCapabilityManager {
public static Capability<IModeCapability> MODE_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
// Allows for the capability to persist after death.
@SubscribeEvent
public static void clonePlayer(PlayerEvent.Clone event) {
LazyOptional<IModeCapability> original = event.getOriginal().getCapability(MODE_CAPABILITY, null);
LazyOptional<IModeCapability> clone = event.getEntity().getCapability(MODE_CAPABILITY, null);
clone.ifPresent(cloneModeCapability ->
original.ifPresent(originalModeCapability ->
cloneModeCapability.setModeData(originalModeCapability.getModeData())));
}
public interface IModeCapability {
ModeSettings getModeData();
void setModeData(ModeSettings modeSettings);
}
public static class ModeCapability implements IModeCapability {
private ModeSettings modeSettings;
@Override
public ModeSettings getModeData() {
return modeSettings;
}
@Override
public void setModeData(ModeSettings modeSettings) {
this.modeSettings = modeSettings;
}
}
public static class Provider extends CapabilityProvider<Provider> implements ICapabilitySerializable<Tag> {
private IModeCapability instance = new ModeCapability();
private LazyOptional<IModeCapability> modeCapabilityOptional = LazyOptional.of(() -> instance);
public Provider() {
super(Provider.class);
gatherCapabilities();
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (cap == MODE_CAPABILITY) return modeCapabilityOptional.cast();
return LazyOptional.empty();
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
modeCapabilityOptional.invalidate();
}
@Override
public void reviveCaps() {
super.reviveCaps();
modeCapabilityOptional = LazyOptional.of(() -> instance);
}
@Override
public Tag serializeNBT() {
CompoundTag compound = new CompoundTag();
ModeSettings modeSettings = instance.getModeData();
if (modeSettings == null) modeSettings = new ModeSettings();
//compound.putInteger("buildMode", modeSettings.getBuildMode().ordinal());
//TODO add mode settings
return compound;
}
@Override
public void deserializeNBT(Tag nbt) {
CompoundTag compound = (CompoundTag) nbt;
//BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.values()[compound.getInteger("buildMode")];
//TODO add mode settings
ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL);
instance.setModeData(modeSettings);
}
}
}

View File

@@ -1,186 +0,0 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.capabilities.*;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import nl.requios.effortlessbuilding.buildmodifier.Array;
import nl.requios.effortlessbuilding.buildmodifier.Mirror;
import nl.requios.effortlessbuilding.buildmodifier.RadialMirror;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings;
@Mod.EventBusSubscriber
public class ModifierCapabilityManager {
public final static Capability<IModifierCapability> MODIFIER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
// Allows for the capability to persist after death.
@SubscribeEvent
public static void clonePlayer(PlayerEvent.Clone event) {
LazyOptional<IModifierCapability> original = event.getOriginal().getCapability(MODIFIER_CAPABILITY, null);
LazyOptional<IModifierCapability> clone = event.getEntity().getCapability(MODIFIER_CAPABILITY, null);
clone.ifPresent(cloneModifierCapability ->
original.ifPresent(originalModifierCapability ->
cloneModifierCapability.setModifierData(originalModifierCapability.getModifierData())));
}
public interface IModifierCapability {
ModifierSettings getModifierData();
void setModifierData(ModifierSettings modifierSettings);
}
public static class ModifierCapability implements IModifierCapability {
private ModifierSettings modifierSettings;
@Override
public ModifierSettings getModifierData() {
return modifierSettings;
}
@Override
public void setModifierData(ModifierSettings modifierSettings) {
this.modifierSettings = modifierSettings;
}
}
public static class Provider extends CapabilityProvider<Provider> implements INBTSerializable<Tag> {
private final IModifierCapability instance = new ModifierCapability();
private LazyOptional<IModifierCapability> modifierCapabilityOptional = LazyOptional.of(() -> instance);
public Provider() {
super(Provider.class);
gatherCapabilities();
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (cap == MODIFIER_CAPABILITY) return modifierCapabilityOptional.cast();
return LazyOptional.empty();
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
modifierCapabilityOptional.invalidate();
}
@Override
public void reviveCaps() {
super.reviveCaps();
modifierCapabilityOptional = LazyOptional.of(() -> instance);
}
@Override
public Tag serializeNBT() {
CompoundTag compound = new CompoundTag();
ModifierSettings modifierSettings = instance.getModifierData();
if (modifierSettings == null) modifierSettings = new ModifierSettings();
//MIRROR
Mirror.MirrorSettings m = modifierSettings.getMirrorSettings();
if (m == null) m = new Mirror.MirrorSettings();
compound.putBoolean("mirrorEnabled", m.enabled);
compound.putDouble("mirrorPosX", m.position.x);
compound.putDouble("mirrorPosY", m.position.y);
compound.putDouble("mirrorPosZ", m.position.z);
compound.putBoolean("mirrorX", m.mirrorX);
compound.putBoolean("mirrorY", m.mirrorY);
compound.putBoolean("mirrorZ", m.mirrorZ);
compound.putInt("mirrorRadius", m.radius);
compound.putBoolean("mirrorDrawLines", m.drawLines);
compound.putBoolean("mirrorDrawPlanes", m.drawPlanes);
//ARRAY
Array.ArraySettings a = modifierSettings.getArraySettings();
if (a == null) a = new Array.ArraySettings();
compound.putBoolean("arrayEnabled", a.enabled);
compound.putInt("arrayOffsetX", a.offset.getX());
compound.putInt("arrayOffsetY", a.offset.getY());
compound.putInt("arrayOffsetZ", a.offset.getZ());
compound.putInt("arrayCount", a.count);
compound.putInt("reachUpgrade", modifierSettings.getReachUpgrade());
//compound.putBoolean("quickReplace", buildSettings.doQuickReplace()); dont save quickreplace
//RADIAL MIRROR
RadialMirror.RadialMirrorSettings r = modifierSettings.getRadialMirrorSettings();
if (r == null) r = new RadialMirror.RadialMirrorSettings();
compound.putBoolean("radialMirrorEnabled", r.enabled);
compound.putDouble("radialMirrorPosX", r.position.x);
compound.putDouble("radialMirrorPosY", r.position.y);
compound.putDouble("radialMirrorPosZ", r.position.z);
compound.putInt("radialMirrorSlices", r.slices);
compound.putBoolean("radialMirrorAlternate", r.alternate);
compound.putInt("radialMirrorRadius", r.radius);
compound.putBoolean("radialMirrorDrawLines", r.drawLines);
compound.putBoolean("radialMirrorDrawPlanes", r.drawPlanes);
return compound;
}
@Override
public void deserializeNBT(Tag nbt) {
CompoundTag compound = (CompoundTag) nbt;
//MIRROR
boolean mirrorEnabled = compound.getBoolean("mirrorEnabled");
Vec3 mirrorPosition = new Vec3(
compound.getDouble("mirrorPosX"),
compound.getDouble("mirrorPosY"),
compound.getDouble("mirrorPosZ"));
boolean mirrorX = compound.getBoolean("mirrorX");
boolean mirrorY = compound.getBoolean("mirrorY");
boolean mirrorZ = compound.getBoolean("mirrorZ");
int mirrorRadius = compound.getInt("mirrorRadius");
boolean mirrorDrawLines = compound.getBoolean("mirrorDrawLines");
boolean mirrorDrawPlanes = compound.getBoolean("mirrorDrawPlanes");
Mirror.MirrorSettings mirrorSettings = new Mirror.MirrorSettings(mirrorEnabled, mirrorPosition, mirrorX, mirrorY, mirrorZ, mirrorRadius, mirrorDrawLines, mirrorDrawPlanes);
//ARRAY
boolean arrayEnabled = compound.getBoolean("arrayEnabled");
BlockPos arrayOffset = new BlockPos(
compound.getInt("arrayOffsetX"),
compound.getInt("arrayOffsetY"),
compound.getInt("arrayOffsetZ"));
int arrayCount = compound.getInt("arrayCount");
Array.ArraySettings arraySettings = new Array.ArraySettings(arrayEnabled, arrayOffset, arrayCount);
int reachUpgrade = compound.getInt("reachUpgrade");
//boolean quickReplace = compound.getBoolean("quickReplace"); //dont load quickreplace
//RADIAL MIRROR
boolean radialMirrorEnabled = compound.getBoolean("radialMirrorEnabled");
Vec3 radialMirrorPosition = new Vec3(
compound.getDouble("radialMirrorPosX"),
compound.getDouble("radialMirrorPosY"),
compound.getDouble("radialMirrorPosZ"));
int radialMirrorSlices = compound.getInt("radialMirrorSlices");
boolean radialMirrorAlternate = compound.getBoolean("radialMirrorAlternate");
int radialMirrorRadius = compound.getInt("radialMirrorRadius");
boolean radialMirrorDrawLines = compound.getBoolean("radialMirrorDrawLines");
boolean radialMirrorDrawPlanes = compound.getBoolean("radialMirrorDrawPlanes");
RadialMirror.RadialMirrorSettings radialMirrorSettings = new RadialMirror.RadialMirrorSettings(radialMirrorEnabled, radialMirrorPosition,
radialMirrorSlices, radialMirrorAlternate, radialMirrorRadius, radialMirrorDrawLines, radialMirrorDrawPlanes);
ModifierSettings modifierSettings = new ModifierSettings(mirrorSettings, arraySettings, radialMirrorSettings, false, reachUpgrade);
instance.setModifierData(modifierSettings);
}
}
}

View File

@@ -1,10 +1,13 @@
package nl.requios.effortlessbuilding.compatibility;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.items.IItemHandler;
import nl.requios.effortlessbuilding.create.foundation.item.ItemHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
public class CompatHelper {
@@ -13,12 +16,16 @@ public class CompatHelper {
}
public static boolean isItemBlockProxy(ItemStack stack) {
return isItemBlockProxy(stack, true);
}
// Check if the item given is a proxy for blocks. For now, we check for the randomizer bag,
// /dank/null, or plain old blocks.
public static boolean isItemBlockProxy(ItemStack stack) {
public static boolean isItemBlockProxy(ItemStack stack, boolean seeBlockItemsAsProxies) {
Item item = stack.getItem();
if (item instanceof BlockItem)
return true;
return seeBlockItemsAsProxies;
return item instanceof AbstractRandomizerBagItem;
}
@@ -60,4 +67,21 @@ public class CompatHelper {
return ItemStack.EMPTY;
}
public static boolean containsBlock(ItemStack stack, Block block) {
if (stack == null || stack.isEmpty() || !isItemBlockProxy(stack)) {
return block == null || block == Blocks.AIR;
}
if (stack.getItem() instanceof BlockItem) {
return ((BlockItem) stack.getItem()).getBlock() == block;
}
if (stack.getItem() instanceof AbstractRandomizerBagItem randomizerBagItem) {
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
ItemStack firstMatch = ItemHelper.findFirstMatch(bagInventory, s -> s.getItem() instanceof BlockItem);
return firstMatch != null && !firstMatch.isEmpty();
}
return false;
}
}

View File

@@ -0,0 +1,34 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import org.lwjgl.glfw.GLFW;
public class AllKeys {
public static boolean isKeyDown(int key) {
return InputConstants.isKeyDown(Minecraft.getInstance()
.getWindow()
.getWindow(), key);
}
public static boolean isMouseButtonDown(int button) {
return GLFW.glfwGetMouseButton(Minecraft.getInstance()
.getWindow()
.getWindow(), button) == 1;
}
public static boolean ctrlDown() {
return Screen.hasControlDown();
}
public static boolean shiftDown() {
return Screen.hasShiftDown();
}
public static boolean altDown() {
return Screen.hasAltDown();
}
}

View File

@@ -0,0 +1,34 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.blaze3d.systems.RenderSystem;
import net.createmod.catnip.render.BindableTexture;
import net.minecraft.resources.ResourceLocation;
public enum AllSpecialTextures implements BindableTexture {
BLANK("blank.png"),
CHECKERED("checkerboard.png"),
THIN_CHECKERED("thin_checkerboard.png"),
CUTOUT_CHECKERED("cutout_checkerboard.png"),
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
SELECTION("selection.png"),
GLUE("glue.png"),
;
public static final String ASSET_PATH = "textures/special/";
private ResourceLocation location;
AllSpecialTextures(String filename) {
location = Create.asResource(ASSET_PATH + filename);
}
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
public ResourceLocation getLocation() {
return location;
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.create;
import com.mojang.logging.LogUtils;
import net.minecraft.resources.ResourceLocation;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import org.slf4j.Logger;
public class Create {
public static final String ID = EffortlessBuilding.MODID;
public static final Logger LOGGER = LogUtils.getLogger();
public static ResourceLocation asResource(String path) {
return ResourceLocation.fromNamespaceAndPath(EffortlessBuilding.MODID, path);
}
}

View File

@@ -0,0 +1,13 @@
package nl.requios.effortlessbuilding.create;
import net.createmod.catnip.render.SuperByteBufferCache;
import nl.requios.effortlessbuilding.create.foundation.utility.ghost.GhostBlocks;
public class CreateClient {
public static final SuperByteBufferCache BUFFER_CACHE = new SuperByteBufferCache();
public static final GhostBlocks GHOST_BLOCKS = new GhostBlocks();
public static void invalidateRenderers() {
CreateClient.BUFFER_CACHE.invalidate();
}
}

View File

@@ -0,0 +1,96 @@
package nl.requios.effortlessbuilding.create.events;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.levelWrappers.WrappedClientLevel;
import net.createmod.catnip.render.DefaultSuperRenderTypeBuffer;
import net.createmod.catnip.render.SuperRenderTypeBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.ClientTickEvent;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.client.event.ViewportEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.CreateClient;
import nl.requios.effortlessbuilding.create.foundation.utility.CameraAngleAnimationService;
@EventBusSubscriber(Dist.CLIENT)
public class ClientEvents {
private static final String ITEM_PREFIX = "item." + Create.ID;
private static final String BLOCK_PREFIX = "block." + Create.ID;
@SubscribeEvent
public static void onTick(ClientTickEvent.Post event) {
if (!isGameActive()) return;
AnimationTickHolder.tick();
CreateClient.GHOST_BLOCKS.tickGhosts();
// CreateClient.OUTLINER.tickOutlines();
CameraAngleAnimationService.tick();
}
@SubscribeEvent
public static void onLoadWorld(LevelEvent.Load event) {
LevelAccessor world = event.getLevel();
if (world.isClientSide() && world instanceof ClientLevel && !(world instanceof WrappedClientLevel)) {
CreateClient.invalidateRenderers();
AnimationTickHolder.reset();
}
}
@SubscribeEvent
public static void onUnloadWorld(LevelEvent.Unload event) {
if (!event.getLevel()
.isClientSide())
return;
CreateClient.invalidateRenderers();
AnimationTickHolder.reset();
}
@SubscribeEvent
public static void onRenderWorld(RenderLevelStageEvent event) {
if(event.getStage() != RenderLevelStageEvent.Stage.AFTER_CUTOUT_BLOCKS) return;
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera()
.getPosition();
float pt = AnimationTickHolder.getPartialTicks();
PoseStack ms = event.getPoseStack();
ms.pushPose();
ms.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z());
SuperRenderTypeBuffer buffer = DefaultSuperRenderTypeBuffer.getInstance();
CreateClient.GHOST_BLOCKS.renderAll(ms, buffer);
// CreateClient.OUTLINER.renderOutlines(ms, buffer, pt);
buffer.draw();
RenderSystem.enableCull();
ms.popPose();
}
@SubscribeEvent
public static void onCameraSetup(ViewportEvent.ComputeCameraAngles event) {
float partialTicks = AnimationTickHolder.getPartialTicks();
if (CameraAngleAnimationService.isYawAnimating())
event.setYaw(CameraAngleAnimationService.getYaw(partialTicks));
if (CameraAngleAnimationService.isPitchAnimating())
event.setPitch(CameraAngleAnimationService.getPitch(partialTicks));
}
public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
}
}

View File

@@ -0,0 +1,39 @@
package nl.requios.effortlessbuilding.create.events;
import net.createmod.catnip.data.WorldAttached;
import net.minecraft.world.level.LevelAccessor;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.level.LevelEvent;
@EventBusSubscriber
public class CommonEvents {
@SubscribeEvent
public static void onUnloadWorld(LevelEvent.Unload event) {
LevelAccessor world = event.getLevel();
WorldAttached.invalidateWorld(world);
}
// @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public static class ModBusEvents {
// @SubscribeEvent
// public static void addPackFinders(AddPackFindersEvent event) {
// if (event.getPackType() == PackType.CLIENT_RESOURCES) {
// IModFileInfo modFileInfo = ModList.get().getModFileById(Create.ID);
// if (modFileInfo == null) {
// Create.LOGGER.error("Could not find Create mod file info; built-in resource packs will be missing!");
// return;
// }
// IModFile modFile = modFileInfo.getFile();
// event.addRepositorySource((consumer, constructor) -> {
// consumer.accept(Pack.create(Create.asResource("legacy_copper").toString(), false, () -> new ModFilePackResources("Create Legacy Copper", modFile, "resourcepacks/legacy_copper"), constructor, Pack.Position.TOP, PackSource.DEFAULT));
// });
// }
// }
}
}

View File

@@ -0,0 +1,16 @@
package nl.requios.effortlessbuilding.create.foundation;
import net.createmod.catnip.lang.LangNumberFormat;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
public class ClientResourceReloadListener implements ResourceManagerReloadListener {
@Override
public void onResourceManagerReload(ResourceManager resourceManager) {
// CreateClient.invalidateRenderers();
// SoundScapes.invalidateAll();
LangNumberFormat.numberFormat.update();
}
}

View File

@@ -0,0 +1,29 @@
package nl.requios.effortlessbuilding.create.foundation.block.render;
import net.createmod.catnip.render.SpriteShiftEntry;
public class CTSpriteShiftEntry extends SpriteShiftEntry {
protected final CTType type;
public CTSpriteShiftEntry(CTType type) {
this.type = type;
}
public CTType getType() {
return type;
}
public float getTargetU(float localU, int index) {
float uOffset = (index % type.getSheetSize());
return getTarget().getU(
(getUnInterpolatedU(getOriginal(), localU) + uOffset) / ((float) type.getSheetSize()));
}
public float getTargetV(float localV, int index) {
float vOffset = (index / type.getSheetSize());
return getTarget().getV(
(getUnInterpolatedV(getOriginal(), localV) + vOffset) / ((float) type.getSheetSize()));
}
}

View File

@@ -0,0 +1,15 @@
package nl.requios.effortlessbuilding.create.foundation.block.render;
import net.minecraft.resources.ResourceLocation;
import nl.requios.effortlessbuilding.create.foundation.block.render.ConnectedTextureBehaviour.CTContext;
import nl.requios.effortlessbuilding.create.foundation.block.render.ConnectedTextureBehaviour.ContextRequirement;
public interface CTType {
ResourceLocation getId();
int getSheetSize();
ContextRequirement getContextRequirement();
int getTextureIndex(CTContext context);
}

View File

@@ -0,0 +1,283 @@
package nl.requios.effortlessbuilding.create.foundation.block.render;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ConnectedTextureBehaviour {
@Nullable
public CTSpriteShiftEntry getShift(BlockState state, RandomSource rand, Direction direction,
@NotNull TextureAtlasSprite sprite) {
return getShift(state, direction, sprite);
}
@Nullable
public abstract CTSpriteShiftEntry getShift(BlockState state, Direction direction,
@NotNull TextureAtlasSprite sprite);
// TODO: allow more than one data type per state/face?
@Nullable
public abstract CTType getDataType(BlockAndTintGetter world, BlockPos pos, BlockState state, Direction direction);
public boolean buildContextForOccludedDirections() {
return false;
}
protected boolean isBeingBlocked(BlockState state, BlockAndTintGetter reader, BlockPos pos, BlockPos otherPos,
Direction face) {
BlockPos blockingPos = otherPos.relative(face);
BlockState blockState = reader.getBlockState(pos);
BlockState blockingState = reader.getBlockState(blockingPos);
if (!Block.isFaceFull(blockingState.getShape(reader, blockingPos), face.getOpposite()))
return false;
if (face.getAxis()
.choose(pos.getX(), pos.getY(), pos.getZ()) != face.getAxis()
.choose(otherPos.getX(), otherPos.getY(), otherPos.getZ()))
return false;
return connectsTo(state,
getCTBlockState(reader, blockState, face.getOpposite(), pos.relative(face), blockingPos), reader, pos,
blockingPos, face);
}
public boolean connectsTo(BlockState state, BlockState other, BlockAndTintGetter reader, BlockPos pos,
BlockPos otherPos, Direction face, Direction primaryOffset, Direction secondaryOffset) {
return connectsTo(state, other, reader, pos, otherPos, face);
}
public boolean connectsTo(BlockState state, BlockState other, BlockAndTintGetter reader, BlockPos pos,
BlockPos otherPos, Direction face) {
return !isBeingBlocked(state, reader, pos, otherPos, face) && state.getBlock() == other.getBlock();
}
private boolean testConnection(BlockAndTintGetter reader, BlockPos currentPos, BlockState connectiveCurrentState,
Direction textureSide, final Direction horizontal, final Direction vertical, int sh, int sv) {
BlockState trueCurrentState = reader.getBlockState(currentPos);
BlockPos targetPos = currentPos.relative(horizontal, sh)
.relative(vertical, sv);
BlockState connectiveTargetState =
getCTBlockState(reader, trueCurrentState, textureSide, currentPos, targetPos);
return connectsTo(connectiveCurrentState, connectiveTargetState, reader, currentPos, targetPos, textureSide,
sh == 0 ? null : sh == -1 ? horizontal.getOpposite() : horizontal,
sv == 0 ? null : sv == -1 ? vertical.getOpposite() : vertical);
}
public BlockState getCTBlockState(BlockAndTintGetter reader, BlockState reference, Direction face, BlockPos fromPos,
BlockPos toPos) {
BlockState blockState = reader.getBlockState(toPos);
return blockState.getAppearance(reader, toPos, face, reference, fromPos);
}
protected boolean reverseUVs(BlockState state, Direction face) {
return false;
}
protected boolean reverseUVsHorizontally(BlockState state, Direction face) {
return reverseUVs(state, face);
}
protected boolean reverseUVsVertically(BlockState state, Direction face) {
return reverseUVs(state, face);
}
protected Direction getUpDirection(BlockAndTintGetter reader, BlockPos pos, BlockState state, Direction face) {
Axis axis = face.getAxis();
return axis.isHorizontal() ? Direction.UP : Direction.NORTH;
}
protected Direction getRightDirection(BlockAndTintGetter reader, BlockPos pos, BlockState state, Direction face) {
Axis axis = face.getAxis();
return axis == Axis.X ? Direction.SOUTH : Direction.WEST;
}
public CTContext buildContext(BlockAndTintGetter reader, BlockPos pos, BlockState state, Direction face,
ContextRequirement requirement) {
boolean positive = face.getAxisDirection() == AxisDirection.POSITIVE;
Direction h = getRightDirection(reader, pos, state, face);
Direction v = getUpDirection(reader, pos, state, face);
h = positive ? h.getOpposite() : h;
if (face == Direction.DOWN) {
v = v.getOpposite();
h = h.getOpposite();
}
final Direction horizontal = h;
final Direction vertical = v;
boolean flipH = reverseUVsHorizontally(state, face);
boolean flipV = reverseUVsVertically(state, face);
int sh = flipH ? -1 : 1;
int sv = flipV ? -1 : 1;
CTContext context = new CTContext();
if (requirement.up) {
context.up = testConnection(reader, pos, state, face, horizontal, vertical, 0, sv);
}
if (requirement.down) {
context.down = testConnection(reader, pos, state, face, horizontal, vertical, 0, -sv);
}
if (requirement.left) {
context.left = testConnection(reader, pos, state, face, horizontal, vertical, -sh, 0);
}
if (requirement.right) {
context.right = testConnection(reader, pos, state, face, horizontal, vertical, sh, 0);
}
if (requirement.topLeft) {
context.topLeft =
context.up && context.left && testConnection(reader, pos, state, face, horizontal, vertical, -sh, sv);
}
if (requirement.topRight) {
context.topRight =
context.up && context.right && testConnection(reader, pos, state, face, horizontal, vertical, sh, sv);
}
if (requirement.bottomLeft) {
context.bottomLeft = context.down && context.left
&& testConnection(reader, pos, state, face, horizontal, vertical, -sh, -sv);
}
if (requirement.bottomRight) {
context.bottomRight = context.down && context.right
&& testConnection(reader, pos, state, face, horizontal, vertical, sh, -sv);
}
return context;
}
public static class CTContext {
public static final CTContext EMPTY = new CTContext();
public boolean up, down, left, right;
public boolean topLeft, topRight, bottomLeft, bottomRight;
}
public static class ContextRequirement {
public final boolean up, down, left, right;
public final boolean topLeft, topRight, bottomLeft, bottomRight;
public ContextRequirement(boolean up, boolean down, boolean left, boolean right, boolean topLeft,
boolean topRight, boolean bottomLeft, boolean bottomRight) {
this.up = up;
this.down = down;
this.left = left;
this.right = right;
this.topLeft = topLeft;
this.topRight = topRight;
this.bottomLeft = bottomLeft;
this.bottomRight = bottomRight;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private boolean up, down, left, right;
private boolean topLeft, topRight, bottomLeft, bottomRight;
public Builder up() {
up = true;
return this;
}
public Builder down() {
down = true;
return this;
}
public Builder left() {
left = true;
return this;
}
public Builder right() {
right = true;
return this;
}
public Builder topLeft() {
topLeft = true;
return this;
}
public Builder topRight() {
topRight = true;
return this;
}
public Builder bottomLeft() {
bottomLeft = true;
return this;
}
public Builder bottomRight() {
bottomRight = true;
return this;
}
public Builder horizontal() {
left();
right();
return this;
}
public Builder vertical() {
up();
down();
return this;
}
public Builder axisAligned() {
horizontal();
vertical();
return this;
}
public Builder corners() {
topLeft();
topRight();
bottomLeft();
bottomRight();
return this;
}
public Builder all() {
axisAligned();
corners();
return this;
}
public ContextRequirement build() {
return new ContextRequirement(up, down, left, right, topLeft, topRight, bottomLeft, bottomRight);
}
}
}
public static abstract class Base extends ConnectedTextureBehaviour {
@Override
@Nullable
public abstract CTSpriteShiftEntry getShift(BlockState state, Direction direction,
@Nullable TextureAtlasSprite sprite);
@Override
@Nullable
public CTType getDataType(BlockAndTintGetter world, BlockPos pos, BlockState state, Direction direction) {
CTSpriteShiftEntry shift = getShift(state, direction, null);
if (shift == null) {
return null;
}
return shift.getType();
}
}
}

View File

@@ -0,0 +1,191 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.vertex.PoseStack;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.gui.TickableGuiEventListener;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.gui.widget.AbstractSimiWidget;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifiersScreenList;
import java.util.Collection;
import java.util.List;
@OnlyIn(Dist.CLIENT)
public abstract class AbstractSimiScreen extends Screen {
protected int windowWidth, windowHeight;
protected int windowXOffset, windowYOffset;
protected int guiLeft, guiTop;
protected AbstractSimiScreen(Component title) {
super(title);
}
protected AbstractSimiScreen() {
this(Components.immutableEmpty());
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowSize(int width, int height) {
windowWidth = width;
windowHeight = height;
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowOffset(int xOffset, int yOffset) {
windowXOffset = xOffset;
windowYOffset = yOffset;
}
@Override
protected void init() {
guiLeft = (width - windowWidth) / 2;
guiTop = (height - windowHeight) / 2;
guiLeft += windowXOffset;
guiTop += windowYOffset;
}
@Override
public void tick() {
for (GuiEventListener listener : children()) {
if (listener instanceof TickableGuiEventListener tickable) {
tickable.tick();
}
}
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (getFocused() != null && !getFocused().isMouseOver(pMouseX, pMouseY))
setFocused(null);
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public boolean isPauseScreen() {
return false;
}
@SuppressWarnings("unchecked")
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(W... widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(Collection<W> widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected void removeWidgets(GuiEventListener... widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
protected void removeWidgets(Collection<? extends GuiEventListener> widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
partialTicks = AnimationTickHolder.getPartialTicksUI();
PoseStack ms = graphics.pose();
ms.pushPose();
prepareFrame();
renderWindowBackground(graphics, mouseX, mouseY, partialTicks);
super.render(graphics, mouseX, mouseY, partialTicks);
renderWindow(graphics, mouseX, mouseY, partialTicks);
renderWindowForeground(graphics, mouseX, mouseY, partialTicks);
endFrame();
ms.popPose();
}
@Override
public void renderBackground(GuiGraphics pGuiGraphics, int pMouseX, int pMouseY, float pPartialTick) {
// super.renderBackground(pGuiGraphics, pMouseX, pMouseY, pPartialTick);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
boolean keyPressed = super.keyPressed(keyCode, scanCode, modifiers);
if (keyPressed || getFocused() instanceof EditBox)
return keyPressed;
InputConstants.Key mouseKey = InputConstants.getKey(keyCode, scanCode);
if (this.minecraft.options.keyInventory.isActiveAndMatches(mouseKey)) {
this.onClose();
return true;
}
return false;
}
protected void prepareFrame() {}
protected void renderWindowBackground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
this.renderTransparentBackground(graphics); //Manually draw background
}
protected abstract void renderWindow(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks);
protected void renderWindowForeground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
for (Renderable widget : renderables) {
if (widget instanceof AbstractSimiWidget simiWidget && simiWidget.isMouseOver(mouseX, mouseY)
&& simiWidget.visible) {
List<Component> tooltip = simiWidget.getToolTip();
if (tooltip.isEmpty())
continue;
int ttx = simiWidget.lockedTooltipX == -1 ? mouseX : simiWidget.lockedTooltipX + simiWidget.getX();
int tty = simiWidget.lockedTooltipY == -1 ? mouseY : simiWidget.lockedTooltipY + simiWidget.getY();
graphics.renderComponentTooltip(font, tooltip, ttx, tty);
}
//Added
if (widget instanceof ModifiersScreenList list) {
list.renderWindowForeground(graphics, mouseX, mouseY, partialTicks);
}
}
}
protected void endFrame() {}
@Deprecated
protected void debugWindowArea(GuiGraphics graphics) {
graphics.fill(guiLeft + windowWidth, guiTop + windowHeight, guiLeft, guiTop, 0xD3D3D3D3);
}
@Override
public GuiEventListener getFocused() {
GuiEventListener focused = super.getFocused();
if (focused instanceof AbstractWidget && !((AbstractWidget) focused).isFocused())
focused = null;
setFocused(focused);
return focused;
}
}

View File

@@ -0,0 +1,87 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import net.createmod.catnip.gui.UIRenderHelper;
import net.createmod.catnip.gui.element.ScreenElement;
import net.createmod.catnip.theme.Color;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.Create;
public enum AllGuiTextures implements ScreenElement {
// Widgets
BUTTON("widgets", 18, 18),
BUTTON_HOVER("widgets", 18, 0, 18, 18),
BUTTON_DOWN("widgets", 36, 0, 18, 18),
INDICATOR("widgets", 0, 18, 18, 6),
INDICATOR_WHITE("widgets", 18, 18, 18, 6),
INDICATOR_GREEN("widgets", 36, 18, 18, 6),
INDICATOR_YELLOW("widgets", 54, 18, 18, 6),
INDICATOR_RED("widgets", 72, 18, 18, 6),
HOTSLOT_ARROW("widgets", 24, 51, 20, 12),
HOTSLOT("widgets", 0, 68, 22, 22),
HOTSLOT_ACTIVE("widgets", 0, 46, 22, 22),
HOTSLOT_SUPER_ACTIVE("widgets", 27, 67, 24, 24),
SPEECH_TOOLTIP_BACKGROUND("widgets", 0, 24, 8, 8),
SPEECH_TOOLTIP_COLOR("widgets", 8, 24, 8, 8),
TRAIN_HUD_SPEED_BG("widgets", 0, 190, 182, 5),
TRAIN_HUD_SPEED("widgets", 0, 185, 182, 5),
TRAIN_HUD_THROTTLE("widgets", 0, 195, 182, 5),
TRAIN_HUD_THROTTLE_POINTER("widgets", 0, 209, 6, 9),
TRAIN_HUD_FRAME("widgets", 0, 200, 186, 7),
TRAIN_HUD_DIRECTION("widgets", 77, 165, 28, 20),
TRAIN_PROMPT_L("widgets", 8, 209, 3, 16),
TRAIN_PROMPT_R("widgets", 11, 209, 3, 16),
TRAIN_PROMPT("widgets", 0, 230, 256, 16),
;
public static final int FONT_COLOR = 0x575F7A;
public final ResourceLocation location;
public int width, height;
public int startX, startY;
private AllGuiTextures(String location, int width, int height) {
this(location, 0, 0, width, height);
}
private AllGuiTextures(int startX, int startY) {
this("icons", startX * 16, startY * 16, 16, 16);
}
private AllGuiTextures(String location, int startX, int startY, int width, int height) {
this(Create.ID, location, startX, startY, width, height);
}
private AllGuiTextures(String namespace, String location, int startX, int startY, int width, int height) {
this.location = ResourceLocation.fromNamespaceAndPath(namespace, "textures/gui/" + location + ".png");
this.width = width;
this.height = height;
this.startX = startX;
this.startY = startY;
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, location);
}
@OnlyIn(Dist.CLIENT)
public void render(GuiGraphics graphics, int x, int y) {
graphics.blit(location, x, y, startX, startY, width, height);
}
@OnlyIn(Dist.CLIENT)
public void render(GuiGraphics graphics, int x, int y, Color c) {
bind();
UIRenderHelper.drawColoredTexture(graphics, c, x, y, startX, startY, width, height);
}
}

View File

@@ -0,0 +1,216 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.createmod.catnip.gui.element.DelegatedStencilElement;
import net.createmod.catnip.gui.element.ScreenElement;
import net.createmod.catnip.theme.Color;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.Create;
import org.joml.Matrix4f;
public class AllIcons implements ScreenElement {
public static final ResourceLocation ICON_ATLAS = Create.asResource("textures/gui/create_icons.png");
public static final int ICON_ATLAS_SIZE = 256;
private static int x = 0, y = -1;
private int iconX;
private int iconY;
public static final AllIcons
I_ADD = newRow(),
I_TRASH = next(),
I_3x3 = next(),
I_TARGET = next(),
I_PRIORITY_VERY_LOW = next(),
I_PRIORITY_LOW = next(),
I_PRIORITY_HIGH = next(),
I_PRIORITY_VERY_HIGH = next(),
I_BLACKLIST = next(),
I_WHITELIST = next(),
I_WHITELIST_OR = next(),
I_WHITELIST_AND = next(),
I_WHITELIST_NOT = next(),
I_RESPECT_NBT = next(),
I_IGNORE_NBT = next();
public static final AllIcons
I_CONFIRM = newRow(),
I_NONE = next(),
I_OPEN_FOLDER = next(),
I_REFRESH = next(),
I_ACTIVE = next(),
I_PASSIVE = next(),
I_ROTATE_PLACE = next(),
I_ROTATE_PLACE_RETURNED = next(),
I_ROTATE_NEVER_PLACE = next(),
I_MOVE_PLACE = next(),
I_MOVE_PLACE_RETURNED = next(),
I_MOVE_NEVER_PLACE = next(),
I_CART_ROTATE = next(),
I_CART_ROTATE_PAUSED = next(),
I_CART_ROTATE_LOCKED = next();
public static final AllIcons
I_DONT_REPLACE = newRow(),
I_REPLACE_SOLID = next(),
I_REPLACE_ANY = next(),
I_REPLACE_EMPTY = next(),
I_CENTERED = next(),
I_ATTACHED = next(),
I_INSERTED = next(),
I_FILL = next(),
I_PLACE = next(),
I_REPLACE = next(),
I_CLEAR = next(),
I_OVERLAY = next(),
I_FLATTEN = next(),
I_LMB = next(),
I_SCROLL = next(),
I_RMB = next();
public static final AllIcons
I_TOOL_DEPLOY = newRow(),
I_SKIP_MISSING = next(),
I_SKIP_BLOCK_ENTITIES = next(),
I_DICE = next(),
I_TUNNEL_SPLIT = next(),
I_TUNNEL_FORCED_SPLIT = next(),
I_TUNNEL_ROUND_ROBIN = next(),
I_TUNNEL_FORCED_ROUND_ROBIN = next(),
I_TUNNEL_PREFER_NEAREST = next(),
I_TUNNEL_RANDOMIZE = next(),
I_TUNNEL_SYNCHRONIZE = next(),
I_TOOLBOX = next(),
I_VIEW_SCHEDULE = next(),
I_TOOL_MOVE_XZ = newRow(),
I_TOOL_MOVE_Y = next(),
I_TOOL_ROTATE = next(),
I_TOOL_MIRROR = next(),
I_ARM_ROUND_ROBIN = next(),
I_ARM_FORCED_ROUND_ROBIN = next(),
I_ARM_PREFER_FIRST = next(),
I_ADD_INVERTED_ATTRIBUTE = next(),
I_FLIP = next(),
I_PLAY = newRow(),
I_PAUSE = next(),
I_STOP = next(),
I_PLACEMENT_SETTINGS = next(),
I_ROTATE_CCW = next(),
I_HOUR_HAND_FIRST = next(),
I_MINUTE_HAND_FIRST = next(),
I_HOUR_HAND_FIRST_24 = next(),
I_PATTERN_SOLID = newRow(),
I_PATTERN_CHECKERED = next(),
I_PATTERN_CHECKERED_INVERSED = next(),
I_PATTERN_CHANCE_25 = next(),
I_PATTERN_CHANCE_50 = newRow(),
I_PATTERN_CHANCE_75 = next(),
I_FOLLOW_DIAGONAL = next(),
I_FOLLOW_MATERIAL = next(),
I_SCHEMATIC = newRow(),
I_SEQ_REPEAT = next(),
I_MTD_LEFT = newRow(),
I_MTD_CLOSE = next(),
I_MTD_RIGHT = next(),
I_MTD_SCAN = next(),
I_MTD_REPLAY = next(),
I_MTD_USER_MODE = next(),
I_MTD_SLOW_MODE = next(),
I_CONFIG_UNLOCKED = newRow(),
I_CONFIG_LOCKED = next(),
I_CONFIG_DISCARD = next(),
I_CONFIG_SAVE = next(),
I_CONFIG_RESET = next(),
I_CONFIG_BACK = next(),
I_CONFIG_PREV = next(),
I_CONFIG_NEXT = next(),
I_DISABLE = next(),
I_CONFIG_OPEN = next(),
I_FX_SURFACE_OFF = newRow(),
I_FX_SURFACE_ON = next(),
I_FX_FIELD_OFF = next(),
I_FX_FIELD_ON = next(),
I_FX_BLEND = next(),
I_FX_BLEND_OFF = next();
;
public AllIcons(int x, int y) {
iconX = x * 16;
iconY = y * 16;
}
private static AllIcons next() {
return new AllIcons(++x, y);
}
private static AllIcons newRow() {
return new AllIcons(x = 0, ++y);
}
@OnlyIn(Dist.CLIENT)
public void bind() {
RenderSystem.setShaderTexture(0, ICON_ATLAS);
}
@OnlyIn(Dist.CLIENT)
@Override
public void render(GuiGraphics graphics, int x, int y) {
graphics.blit(ICON_ATLAS, x, y, 0, iconX, iconY, 16, 16, 256, 256);
}
@OnlyIn(Dist.CLIENT)
public void render(PoseStack ms, MultiBufferSource buffer, int color) {
VertexConsumer builder = buffer.getBuffer(RenderType.text(ICON_ATLAS));
Matrix4f matrix = ms.last().pose();
Color rgb = new Color(color);
int light = LightTexture.FULL_BRIGHT;
Vec3 vec1 = new Vec3(0, 0, 0);
Vec3 vec2 = new Vec3(0, 1, 0);
Vec3 vec3 = new Vec3(1, 1, 0);
Vec3 vec4 = new Vec3(1, 0, 0);
float u1 = iconX * 1f / ICON_ATLAS_SIZE;
float u2 = (iconX + 16) * 1f / ICON_ATLAS_SIZE;
float v1 = iconY * 1f / ICON_ATLAS_SIZE;
float v2 = (iconY + 16) * 1f / ICON_ATLAS_SIZE;
vertex(builder, matrix, vec1, rgb, u1, v1, light);
vertex(builder, matrix, vec2, rgb, u1, v2, light);
vertex(builder, matrix, vec3, rgb, u2, v2, light);
vertex(builder, matrix, vec4, rgb, u2, v1, light);
}
@OnlyIn(Dist.CLIENT)
private void vertex(VertexConsumer builder, Matrix4f matrix, Vec3 vec, Color rgb, float u, float v, int light) {
builder.addVertex(matrix, (float) vec.x, (float) vec.y, (float) vec.z)
.setColor(rgb.getRed(), rgb.getGreen(), rgb.getBlue(), 255)
.setUv(u, v)
.setLight(light);
}
@OnlyIn(Dist.CLIENT)
public DelegatedStencilElement asStencil() {
return new DelegatedStencilElement().withStencilRenderer((ms, w, h, alpha) -> this.render(ms, 0, 0)).withBounds(16, 16);
}
}

View File

@@ -0,0 +1,83 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Axis;
import net.createmod.catnip.gui.ILightingSettings;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class CustomLightingSettings implements ILightingSettings {
private Vector3f light1;
private Vector3f light2;
private Matrix4f lightMatrix;
protected CustomLightingSettings(float yRot, float xRot) {
init(yRot, xRot, 0, 0, false);
}
protected CustomLightingSettings(float yRot1, float xRot1, float yRot2, float xRot2) {
init(yRot1, xRot1, yRot2, xRot2, true);
}
protected void init(float yRot1, float xRot1, float yRot2, float xRot2, boolean doubleLight) {
light1 = new Vector3f(0, 0, 1);
light1.rotate(Axis.YP.rotationDegrees(yRot1));
light1.rotate(Axis.XN.rotationDegrees(xRot1));
if (doubleLight) {
light2 = new Vector3f(0, 0, 1);
light2.rotate(Axis.YP.rotationDegrees(yRot2));
light2.rotate(Axis.XN.rotationDegrees(xRot2));
} else {
light2 = new Vector3f();
}
lightMatrix = new Matrix4f();
lightMatrix.identity();
}
@Override
public void applyLighting() {
RenderSystem.setShaderLights(light1, light2);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private float yRot1, xRot1;
private float yRot2, xRot2;
private boolean doubleLight;
public Builder firstLightRotation(float yRot, float xRot) {
yRot1 = yRot;
xRot1 = xRot;
return this;
}
public Builder secondLightRotation(float yRot, float xRot) {
yRot2 = yRot;
xRot2 = xRot;
doubleLight = true;
return this;
}
public Builder doubleLight() {
doubleLight = true;
return this;
}
public CustomLightingSettings build() {
if (doubleLight) {
return new CustomLightingSettings(yRot1, xRot1, yRot2, xRot2);
} else {
return new CustomLightingSettings(yRot1, xRot1);
}
}
}
}

View File

@@ -0,0 +1,198 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.Style;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.client.ClientHooks;
import net.neoforged.neoforge.client.event.RenderTooltipEvent;
import net.neoforged.neoforge.client.extensions.IGuiGraphicsExtension;
import net.neoforged.neoforge.common.NeoForge;
import org.joml.Matrix4f;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
public class RemovedGuiUtils {
@Nonnull
private static ItemStack cachedTooltipStack = ItemStack.EMPTY;
public static void preItemToolTip(@Nonnull ItemStack stack) {
cachedTooltipStack = stack;
}
public static void postItemToolTip() {
cachedTooltipStack = ItemStack.EMPTY;
}
public static void drawHoveringText(GuiGraphics graphics, List<? extends FormattedText> textLines, int mouseX,
int mouseY, int screenWidth, int screenHeight, int maxTextWidth, Font font) {
drawHoveringText(graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
IGuiGraphicsExtension.DEFAULT_BACKGROUND_COLOR, IGuiGraphicsExtension.DEFAULT_BORDER_COLOR_START, IGuiGraphicsExtension.DEFAULT_BORDER_COLOR_END,
font);
}
public static void drawHoveringText(GuiGraphics graphics, List<? extends FormattedText> textLines, int mouseX,
int mouseY, int screenWidth, int screenHeight, int maxTextWidth, int backgroundColor, int borderColorStart,
int borderColorEnd, Font font) {
drawHoveringText(cachedTooltipStack, graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
backgroundColor, borderColorStart, borderColorEnd, font);
}
public static void drawHoveringText(@Nonnull final ItemStack stack, GuiGraphics graphics,
List<? extends FormattedText> textLines, int mouseX, int mouseY, int screenWidth, int screenHeight,
int maxTextWidth, Font font) {
drawHoveringText(stack, graphics, textLines, mouseX, mouseY, screenWidth, screenHeight, maxTextWidth,
IGuiGraphicsExtension.DEFAULT_BACKGROUND_COLOR, IGuiGraphicsExtension.DEFAULT_BORDER_COLOR_START, IGuiGraphicsExtension.DEFAULT_BORDER_COLOR_END,
font);
}
public static void drawHoveringText(@Nonnull final ItemStack stack, GuiGraphics graphics,
List<? extends FormattedText> textLines, int mouseX, int mouseY, int screenWidth, int screenHeight,
int maxTextWidth, int backgroundColor, int borderColorStart, int borderColorEnd, Font font) {
if (textLines.isEmpty())
return;
List<ClientTooltipComponent> list = ClientHooks.gatherTooltipComponents(stack, textLines,
stack.getTooltipImage(), mouseX, screenWidth, screenHeight, font);
RenderTooltipEvent.Pre event =
new RenderTooltipEvent.Pre(stack, graphics, mouseX, mouseY, screenWidth, screenHeight, font, list, null);
if (NeoForge.EVENT_BUS.post(event).isCanceled())
return;
PoseStack pStack = graphics.pose();
mouseX = event.getX();
mouseY = event.getY();
screenWidth = event.getScreenWidth();
screenHeight = event.getScreenHeight();
font = event.getFont();
// RenderSystem.disableRescaleNormal();
RenderSystem.disableDepthTest();
int tooltipTextWidth = 0;
for (FormattedText textLine : textLines) {
int textLineWidth = font.width(textLine);
if (textLineWidth > tooltipTextWidth)
tooltipTextWidth = textLineWidth;
}
boolean needsWrap = false;
int titleLinesCount = 1;
int tooltipX = mouseX + 12;
if (tooltipX + tooltipTextWidth + 4 > screenWidth) {
tooltipX = mouseX - 16 - tooltipTextWidth;
if (tooltipX < 4) // if the tooltip doesn't fit on the screen
{
if (mouseX > screenWidth / 2)
tooltipTextWidth = mouseX - 12 - 8;
else
tooltipTextWidth = screenWidth - 16 - mouseX;
needsWrap = true;
}
}
if (maxTextWidth > 0 && tooltipTextWidth > maxTextWidth) {
tooltipTextWidth = maxTextWidth;
needsWrap = true;
}
if (needsWrap) {
int wrappedTooltipWidth = 0;
List<FormattedText> wrappedTextLines = new ArrayList<>();
for (int i = 0; i < textLines.size(); i++) {
FormattedText textLine = textLines.get(i);
List<FormattedText> wrappedLine = font.getSplitter()
.splitLines(textLine, tooltipTextWidth, Style.EMPTY);
if (i == 0)
titleLinesCount = wrappedLine.size();
for (FormattedText line : wrappedLine) {
int lineWidth = font.width(line);
if (lineWidth > wrappedTooltipWidth)
wrappedTooltipWidth = lineWidth;
wrappedTextLines.add(line);
}
}
tooltipTextWidth = wrappedTooltipWidth;
textLines = wrappedTextLines;
if (mouseX > screenWidth / 2)
tooltipX = mouseX - 16 - tooltipTextWidth;
else
tooltipX = mouseX + 12;
}
int tooltipY = mouseY - 12;
int tooltipHeight = 8;
if (textLines.size() > 1) {
tooltipHeight += (textLines.size() - 1) * 10;
if (textLines.size() > titleLinesCount)
tooltipHeight += 2; // gap between title lines and next lines
}
if (tooltipY < 4)
tooltipY = 4;
else if (tooltipY + tooltipHeight + 4 > screenHeight)
tooltipY = screenHeight - tooltipHeight - 4;
final int zLevel = 400;
RenderTooltipEvent.Color colorEvent = new RenderTooltipEvent.Color(stack, graphics, tooltipX, tooltipY,
font, backgroundColor, borderColorStart, borderColorEnd, list);
NeoForge.EVENT_BUS.post(colorEvent);
backgroundColor = colorEvent.getBackgroundStart();
borderColorStart = colorEvent.getBorderStart();
borderColorEnd = colorEvent.getBorderEnd();
pStack.pushPose();
Matrix4f mat = pStack.last()
.pose();
graphics.fillGradient(tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3,
tooltipY - 3, zLevel, backgroundColor, backgroundColor);
graphics.fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 3,
tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, zLevel, backgroundColor, backgroundColor);
graphics.fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3,
tooltipY + tooltipHeight + 3, zLevel, backgroundColor, backgroundColor);
graphics.fillGradient(tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3,
zLevel, backgroundColor, backgroundColor);
graphics.fillGradient(tooltipX + tooltipTextWidth + 3, tooltipY - 3,
tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, zLevel, backgroundColor, backgroundColor);
graphics.fillGradient(tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1,
tooltipY + tooltipHeight + 3 - 1, zLevel, borderColorStart, borderColorEnd);
graphics.fillGradient(tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1,
tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, zLevel, borderColorStart, borderColorEnd);
graphics.fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3,
tooltipY - 3 + 1, zLevel, borderColorStart, borderColorStart);
graphics.fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 2,
tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, zLevel, borderColorEnd, borderColorEnd);
MultiBufferSource.BufferSource renderType = graphics.bufferSource();
pStack.translate(0.0D, 0.0D, zLevel);
for (int lineNumber = 0; lineNumber < list.size(); ++lineNumber) {
ClientTooltipComponent line = list.get(lineNumber);
if (line != null)
line.renderText(font, tooltipX, tooltipY, mat, renderType);
if (lineNumber + 1 == titleLinesCount)
tooltipY += 2;
tooltipY += 10;
}
renderType.endBatch();
pStack.popPose();
RenderSystem.enableDepthTest();
}
}

View File

@@ -0,0 +1,234 @@
package nl.requios.effortlessbuilding.create.foundation.gui;
import net.createmod.catnip.data.Couple;
import net.createmod.catnip.theme.Color;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class Theme {
private static final List<Theme> THEMES = new ArrayList<>();
public static final Theme BASE = addTheme(new Theme());
public static Theme addTheme(@Nonnull Theme theme) {
THEMES.add(theme);
THEMES.sort(Comparator.comparingInt(Theme::getPriority).reversed());
return theme;
}
public static void removeTheme(Theme theme) {
THEMES.remove(theme);
}
public static void reload() {
THEMES.forEach(Theme::init);
}
private static ColorHolder resolve(String key) {
return THEMES
.stream()
.map(theme -> theme.get(key))
.filter(Objects::nonNull)
.findFirst()
.map(holder -> holder.lookupKey == null ? holder : resolve(holder.lookupKey))
.orElse(ColorHolder.MISSING);
}
@Nonnull public static Couple<Color> p(@Nonnull Key key) {return p(key.get());}
@Nonnull public static Couple<Color> p(String key) {return resolve(key).asPair();}
@Nonnull public static Color c(@Nonnull Key key, boolean first) {return c(key.get(), first);}
@Nonnull public static Color c(String key, boolean first) {return p(key).get(first);}
public static int i(@Nonnull Key key, boolean first) {return i(key.get(), first);}
public static int i(String key, boolean first) {return p(key).get(first).getRGB();}
@Nonnull public static Color c(@Nonnull Key key) {return c(key.get());}
@Nonnull public static Color c(String key) {return resolve(key).get();}
public static int i(@Nonnull Key key) {return i(key.get());}
public static int i(String key) {return resolve(key).get().getRGB();}
//-----------//
protected final Map<String, ColorHolder> colors;
private int priority = 0;
protected Theme() {
colors = new HashMap<>();
init();
}
/*
* Small note to addons: if you also want to make use of Theme,
* and add new Keys, please do not use mixins. Instead make a Theme
* subclass, override init and apply it via the static #addTheme
*
**/
protected void init() {
put(Key.BUTTON_IDLE, new Color(0xdd_8ab6d6, true), new Color(0x90_8ab6d6, true));
put(Key.BUTTON_HOVER, new Color(0xff_9ABBD3, true), new Color(0xd0_9ABBD3, true));
put(Key.BUTTON_CLICK, new Color(0xff_ffffff), new Color(0xee_ffffff));
put(Key.BUTTON_DISABLE, new Color(0x80_909090, true), new Color(0x60_909090, true));
put(Key.BUTTON_SUCCESS, new Color(0xcc_88f788, true), new Color(0xcc_20cc20, true));
put(Key.BUTTON_FAIL, new Color(0xcc_f78888, true), new Color(0xcc_cc2020, true));
put(Key.TEXT, new Color(0xff_eeeeee), new Color(0xff_a3a3a3));
put(Key.TEXT_DARKER, new Color(0xff_a3a3a3), new Color(0xff_808080));
put(Key.TEXT_ACCENT_STRONG, new Color(0xff_8ab6d6), new Color(0xff_8ab6d6));
put(Key.TEXT_ACCENT_SLIGHT, new Color(0xff_ddeeff), new Color(0xff_a0b0c0));
put(Key.STREAK, new Color(0x101010, false));
put(Key.VANILLA_TOOLTIP_BORDER, new Color(0x50_5000ff, true), new Color(0x50_28007f, true));
put(Key.VANILLA_TOOLTIP_BACKGROUND, new Color(0xf0_100010, true));
put(Key.PONDER_BUTTON_IDLE, new Color(0x60_c0c0ff, true), new Color(0x30_c0c0ff, true));
put(Key.PONDER_BUTTON_HOVER, new Color(0xf0_c0c0ff, true), new Color(0xa0_c0c0ff, true));
put(Key.PONDER_BUTTON_CLICK, new Color(0xff_ffffff), new Color(0xdd_ffffff));
put(Key.PONDER_BUTTON_DISABLE, new Color(0x80_909090, true), new Color(0x20_909090, true));
put(Key.PONDER_BACKGROUND_TRANSPARENT, new Color(0xdd_000000, true));
put(Key.PONDER_BACKGROUND_FLAT, new Color(0xff_000000, false));
put(Key.PONDER_BACKGROUND_IMPORTANT, new Color(0xdd_0e0e20, true));
put(Key.PONDER_IDLE, new Color(0x40ffeedd, true), new Color(0x20ffeedd, true));
put(Key.PONDER_HOVER, new Color(0x70ffffff, true), new Color(0x30ffffff, true));
put(Key.PONDER_HIGHLIGHT, new Color(0xf0ffeedd, true), new Color(0x60ffeedd, true));
put(Key.TEXT_WINDOW_BORDER, new Color(0x607a6000, true), new Color(0x207a6000, true));
put(Key.PONDER_BACK_ARROW, new Color(0xf0aa9999, true), new Color(0x30aa9999, true));
put(Key.PONDER_PROGRESSBAR, new Color(0x80ffeedd, true), new Color(0x50ffeedd, true));
put(Key.PONDER_MISSING_CREATE, new Color(0x70_984500, true), new Color(0x70_692400, true));
//put(Key.PONDER_MISSING_VANILLA, new Color(0x50_5000ff, true), new Color(0x50_300077, true));
lookup(Key.PONDER_MISSING_VANILLA, Key.VANILLA_TOOLTIP_BORDER);
put(Key.CONFIG_TITLE_A, new Color(0xffc69fbc, true), new Color(0xfff6b8bb, true));
put(Key.CONFIG_TITLE_B, new Color(0xfff6b8bb, true), new Color(0xfffbf994, true));
//put(Key., new Color(0x, true), new Color(0x, true));
}
protected void put(String key, Color c) {
colors.put(key, ColorHolder.single(c));
}
protected void put(Key key, Color c) {
put(key.get(), c);
}
protected void put(String key, Color c1, Color c2) {
colors.put(key, ColorHolder.pair(c1, c2));
}
protected void put(Key key, Color c1, Color c2) {
put(key.get(), c1, c2);
}
protected void lookup(Key key, Key source) {
colors.put(key.get(), ColorHolder.lookup(source.get()));
}
@Nullable protected ColorHolder get(String key) {
return colors.get(key);
}
public int getPriority() {
return priority;
}
public Theme setPriority(int priority) {
this.priority = priority;
return this;
}
public static class Key {
public static final Key BUTTON_IDLE = new Key();
public static final Key BUTTON_HOVER = new Key();
public static final Key BUTTON_CLICK = new Key();
public static final Key BUTTON_DISABLE = new Key();
public static final Key BUTTON_SUCCESS = new Key();
public static final Key BUTTON_FAIL = new Key();
public static final Key TEXT = new Key();
public static final Key TEXT_DARKER = new Key();
public static final Key TEXT_ACCENT_STRONG = new Key();
public static final Key TEXT_ACCENT_SLIGHT = new Key();
public static final Key STREAK = new Key();
public static final Key VANILLA_TOOLTIP_BORDER = new Key();
public static final Key VANILLA_TOOLTIP_BACKGROUND = new Key();
public static final Key PONDER_BACKGROUND_TRANSPARENT = new Key();
public static final Key PONDER_BACKGROUND_FLAT = new Key();
public static final Key PONDER_BACKGROUND_IMPORTANT = new Key();
public static final Key PONDER_IDLE = new Key();
public static final Key PONDER_HOVER = new Key();
public static final Key PONDER_HIGHLIGHT = new Key();
public static final Key TEXT_WINDOW_BORDER = new Key();
public static final Key PONDER_BACK_ARROW = new Key();
public static final Key PONDER_PROGRESSBAR = new Key();
public static final Key PONDER_MISSING_CREATE = new Key();
public static final Key PONDER_MISSING_VANILLA = new Key();
public static final Key PONDER_BUTTON_IDLE = new Key();
public static final Key PONDER_BUTTON_HOVER = new Key();
public static final Key PONDER_BUTTON_CLICK = new Key();
public static final Key PONDER_BUTTON_DISABLE = new Key();
public static final Key CONFIG_TITLE_A = new Key();
public static final Key CONFIG_TITLE_B = new Key();
private static int index = 0;
private final String s;
protected Key() {
this.s = "_" + index++;
}
protected Key(String s) {
this.s = s;
}
public String get() {
return s;
}
}
private static class ColorHolder {
private static final ColorHolder MISSING = ColorHolder.single(Color.BLACK);
private Couple<Color> colors;
private String lookupKey;
private static ColorHolder single(Color c) {
ColorHolder h = new ColorHolder();
h.colors = Couple.create(c.setImmutable(), c.setImmutable());
return h;
}
private static ColorHolder pair(Color first, Color second) {
ColorHolder h = new ColorHolder();
h.colors = Couple.create(first.setImmutable(), second.setImmutable());
return h;
}
private static ColorHolder lookup(String key) {
ColorHolder h = new ColorHolder();
h.lookupKey = key;
return h;
}
private Color get() {
return colors.getFirst();
}
private Couple<Color> asPair() {
return colors;
}
}
}

View File

@@ -0,0 +1,181 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import com.mojang.blaze3d.platform.InputConstants;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.gui.TickableGuiEventListener;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.gui.screens.inventory.ContainerScreen;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.gui.widget.AbstractSimiWidget;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@OnlyIn(Dist.CLIENT)
@ParametersAreNonnullByDefault
public abstract class AbstractSimiContainerScreen<T extends AbstractContainerMenu> extends AbstractContainerScreen<T> {
protected int windowXOffset, windowYOffset;
public AbstractSimiContainerScreen(T container, Inventory inv, Component title) {
super(container, inv, title);
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowSize(int width, int height) {
imageWidth = width;
imageHeight = height;
}
/**
* This method must be called before {@code super.init()}!
*/
protected void setWindowOffset(int xOffset, int yOffset) {
windowXOffset = xOffset;
windowYOffset = yOffset;
}
@Override
protected void init() {
super.init();
leftPos += windowXOffset;
topPos += windowYOffset;
}
@Override
protected void containerTick() {
for (GuiEventListener listener : children()) {
if (listener instanceof TickableGuiEventListener tickable) {
tickable.tick();
}
}
}
@SuppressWarnings("unchecked")
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(W... widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected <W extends GuiEventListener & Renderable & NarratableEntry> void addRenderableWidgets(Collection<W> widgets) {
for (W widget : widgets) {
addRenderableWidget(widget);
}
}
protected void removeWidgets(GuiEventListener... widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
protected void removeWidgets(Collection<? extends GuiEventListener> widgets) {
for (GuiEventListener widget : widgets) {
removeWidget(widget);
}
}
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
partialTicks = AnimationTickHolder.getPartialTicksUI();
super.render(graphics, mouseX, mouseY, partialTicks);
renderForeground(graphics, mouseX, mouseY, partialTicks);
}
@Override
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
// no-op to prevent screen- and inventory-title from being rendered at incorrect
// location
// could also set this.titleX/Y and this.playerInventoryTitleX/Y to the proper
// values instead
}
protected void renderForeground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderTooltip(graphics, mouseX, mouseY);
for (Renderable widget : renderables) {
if (widget instanceof AbstractSimiWidget simiWidget && simiWidget.isMouseOver(mouseX, mouseY)) {
List<Component> tooltip = simiWidget.getToolTip();
if (tooltip.isEmpty())
continue;
int ttx = simiWidget.lockedTooltipX == -1 ? mouseX : simiWidget.lockedTooltipX + simiWidget.getX();
int tty = simiWidget.lockedTooltipY == -1 ? mouseY : simiWidget.lockedTooltipY + simiWidget.getY();
graphics.renderComponentTooltip(font, tooltip, ttx, tty);
}
}
}
public int getLeftOfCentered(int textureWidth) {
return leftPos - windowXOffset + (imageWidth - textureWidth) / 2;
}
// public void renderPlayerInventory(GuiGraphics graphics, int x, int y) {
// AllGuiTextures.PLAYER_INVENTORY.render(graphics, x, y);
// graphics.drawString(font, playerInventoryTitle, x + 8, y + 6, 0x404040, false);
// }
@Override
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
InputConstants.Key mouseKey = InputConstants.getKey(pKeyCode, pScanCode);
if (getFocused() instanceof EditBox && this.minecraft.options.keyInventory.isActiveAndMatches(mouseKey))
return false;
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
}
@Override
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
if (getFocused() != null && !getFocused().isMouseOver(pMouseX, pMouseY))
setFocused(null);
return super.mouseClicked(pMouseX, pMouseY, pButton);
}
@Override
public GuiEventListener getFocused() {
GuiEventListener focused = super.getFocused();
if (focused instanceof AbstractWidget && !((AbstractWidget) focused).isFocused())
focused = null;
setFocused(focused);
return focused;
}
/**
* Used for moving JEI out of the way of extra things like block renders.
*
* @return the space that the GUI takes up outside the normal rectangle defined
* by {@link ContainerScreen}.
*/
public List<Rect2i> getExtraAreas() {
return Collections.emptyList();
}
@Deprecated
protected void debugWindowArea(GuiGraphics graphics) {
graphics.fill(leftPos + imageWidth, topPos + imageHeight, leftPos, topPos, 0xD3D3D3D3);
}
@Deprecated
protected void debugExtraAreas(GuiGraphics graphics) {
for (Rect2i area : getExtraAreas()) {
graphics.fill(area.getX() + area.getWidth(), area.getY() + area.getHeight(), area.getX(), area.getY(),
0xD3D3D3D3);
}
}
}

View File

@@ -0,0 +1,107 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.items.ItemStackHandler;
public abstract class GhostItemMenu<T> extends MenuBase<T> implements IClearableMenu {
public ItemStackHandler ghostInventory;
protected GhostItemMenu(MenuType<?> type, int id, Inventory inv, FriendlyByteBuf extraData) {
super(type, id, inv, extraData);
}
protected GhostItemMenu(MenuType<?> type, int id, Inventory inv, T contentHolder) {
super(type, id, inv, contentHolder);
}
protected abstract ItemStackHandler createGhostInventory();
protected abstract boolean allowRepeats();
@Override
protected void initAndReadInventory(T contentHolder) {
ghostInventory = createGhostInventory();
}
@Override
public void clearContents() {
for (int i = 0; i < ghostInventory.getSlots(); i++)
ghostInventory.setStackInSlot(i, ItemStack.EMPTY);
}
@Override
public boolean canTakeItemForPickAll(ItemStack stack, Slot slotIn) {
return slotIn.container == playerInventory;
}
@Override
public boolean canDragTo(Slot slotIn) {
if (allowRepeats())
return true;
return slotIn.container == playerInventory;
}
@Override
public void clicked(int slotId, int dragType, ClickType clickTypeIn, Player player) {
if (slotId < 36) {
super.clicked(slotId, dragType, clickTypeIn, player);
return;
}
if (clickTypeIn == ClickType.THROW)
return;
ItemStack held = getCarried();
int slot = slotId - 36;
if (clickTypeIn == ClickType.CLONE) {
if (player.isCreative() && held.isEmpty()) {
ItemStack stackInSlot = ghostInventory.getStackInSlot(slot)
.copy();
stackInSlot.setCount(stackInSlot.getMaxStackSize());
setCarried(stackInSlot);
return;
}
return;
}
ItemStack insert;
if (held.isEmpty()) {
insert = ItemStack.EMPTY;
} else {
insert = held.copy();
insert.setCount(1);
}
ghostInventory.setStackInSlot(slot, insert);
getSlot(slotId).setChanged();
}
@Override
public ItemStack quickMoveStack(Player playerIn, int index) {
if (index < 36) {
ItemStack stackToInsert = playerInventory.getItem(index);
for (int i = 0; i < ghostInventory.getSlots(); i++) {
ItemStack stack = ghostInventory.getStackInSlot(i);
if (!allowRepeats() && ItemStack.isSameItemSameComponents(stack, stackToInsert))
break;
if (stack.isEmpty()) {
ItemStack copy = stackToInsert.copy();
copy.setCount(1);
ghostInventory.insertItem(i, copy, false);
getSlot(i + 36).setChanged();
break;
}
}
} else {
ghostInventory.extractItem(index - 36, 1, false);
getSlot(index).setChanged();
}
return ItemStack.EMPTY;
}
}

View File

@@ -0,0 +1,11 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
public interface IClearableMenu {
default void sendClearPacket() {
// PacketDistributor.SERVER.noArg().send(new ClearMenuPacket());
}
public void clearContents();
}

View File

@@ -0,0 +1,70 @@
package nl.requios.effortlessbuilding.create.foundation.gui.container;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.create.foundation.utility.IInteractionChecker;
public abstract class MenuBase<T> extends AbstractContainerMenu {
public Player player;
public Inventory playerInventory;
public T contentHolder;
protected MenuBase(MenuType<?> type, int id, Inventory inv, FriendlyByteBuf extraData) {
super(type, id);
init(inv, createOnClient(extraData));
}
protected MenuBase(MenuType<?> type, int id, Inventory inv, T contentHolder) {
super(type, id);
init(inv, contentHolder);
}
protected void init(Inventory inv, T contentHolderIn) {
player = inv.player;
playerInventory = inv;
contentHolder = contentHolderIn;
initAndReadInventory(contentHolder);
addSlots();
broadcastChanges();
}
@OnlyIn(Dist.CLIENT)
protected abstract T createOnClient(FriendlyByteBuf extraData);
protected abstract void initAndReadInventory(T contentHolder);
protected abstract void addSlots();
protected abstract void saveData(T contentHolder);
protected void addPlayerSlots(int x, int y) {
for (int hotbarSlot = 0; hotbarSlot < 9; ++hotbarSlot)
this.addSlot(new Slot(playerInventory, hotbarSlot, x + hotbarSlot * 18, y + 58));
for (int row = 0; row < 3; ++row)
for (int col = 0; col < 9; ++col)
this.addSlot(new Slot(playerInventory, col + row * 9 + 9, x + col * 18, y + row * 18));
}
@Override
public void removed(Player playerIn) {
super.removed(playerIn);
saveData(contentHolder);
}
@Override
public boolean stillValid(Player player) {
if (contentHolder == null)
return false;
if (contentHolder instanceof IInteractionChecker)
return ((IInteractionChecker) contentHolder).canPlayerUse(player);
return true;
}
}

View File

@@ -0,0 +1,100 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.createmod.catnip.gui.TickableGuiEventListener;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import javax.annotation.Nonnull;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiConsumer;
public abstract class AbstractSimiWidget extends AbstractWidget implements TickableGuiEventListener {
public static final int HEADER_RGB = 0x5391E1;
public static final int HINT_RGB = 0x96B7E0;
protected float z;
protected boolean wasHovered = false;
protected List<Component> toolTip = new LinkedList<>();
protected BiConsumer<Integer, Integer> onClick = (_$, _$$) -> {};
public int lockedTooltipX = -1;
public int lockedTooltipY = -1;
protected AbstractSimiWidget(int x, int y) {
this(x, y, 16, 16);
}
protected AbstractSimiWidget(int x, int y, int width, int height) {
this(x, y, width, height, Components.immutableEmpty());
}
protected AbstractSimiWidget(int x, int y, int width, int height, Component message) {
super(x, y, width, height, message);
}
// @Override
// protected ClientTooltipPositioner createTooltipPositioner() {
// return DefaultTooltipPositioner.INSTANCE;
// }
public <T extends AbstractSimiWidget> T withCallback(BiConsumer<Integer, Integer> cb) {
this.onClick = cb;
//noinspection unchecked
return (T) this;
}
public <T extends AbstractSimiWidget> T withCallback(Runnable cb) {
return withCallback((_$, _$$) -> cb.run());
}
public <T extends AbstractSimiWidget> T atZLevel(float z) {
this.z = z;
//noinspection unchecked
return (T) this;
}
public List<Component> getToolTip() {
return toolTip;
}
@Override
public void tick() {}
@Override
public void renderWidget(@Nonnull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
beforeRender(graphics, mouseX, mouseY, partialTicks);
doRender(graphics, mouseX, mouseY, partialTicks);
afterRender(graphics, mouseX, mouseY, partialTicks);
wasHovered = isHoveredOrFocused();
}
protected void beforeRender(@Nonnull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
graphics.pose().pushPose();
}
protected void doRender(@Nonnull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
}
protected void afterRender(@Nonnull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
graphics.pose().popPose();
}
public void runCallback(double mouseX, double mouseY) {
onClick.accept((int) mouseX, (int) mouseY);
}
@Override
public void onClick(double mouseX, double mouseY) {
runCallback(mouseX, mouseY);
}
@Override
public void updateWidgetNarration(NarrationElementOutput pNarrationElementOutput) {
defaultButtonNarrationText(pNarrationElementOutput);
}
}

View File

@@ -0,0 +1,48 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.systems.RenderSystem;
import net.createmod.catnip.gui.element.ScreenElement;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.gui.AllGuiTextures;
public class IconButton extends AbstractSimiWidget {
protected ScreenElement icon;
public IconButton(int x, int y, ScreenElement icon) {
this(x, y, 18, 18, icon);
}
public IconButton(int x, int y, int w, int h, ScreenElement icon) {
super(x, y, w, h);
this.icon = icon;
}
@Override
public void doRender(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
if (visible) {
isHovered = mouseX >= getX() && mouseY >= getY() && mouseX < getX() + width && mouseY < getY() + height;
AllGuiTextures button = !active ? AllGuiTextures.BUTTON_DOWN
: isMouseOver(mouseX, mouseY) ? AllGuiTextures.BUTTON_HOVER : AllGuiTextures.BUTTON;
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
drawBg(graphics, button);
icon.render(graphics, getX() + 1, getY() + 1);
}
}
protected void drawBg(GuiGraphics graphics, AllGuiTextures button) {
graphics.blit(button.location, getX(), getY(), button.startX, button.startY, button.width, button.height);
}
public void setToolTip(Component text) {
toolTip.clear();
toolTip.add(text);
}
public void setIcon(ScreenElement icon) {
this.icon = icon;
}
}

View File

@@ -0,0 +1,40 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import nl.requios.effortlessbuilding.create.foundation.gui.AllGuiTextures;
import org.jetbrains.annotations.NotNull;
public class Indicator extends AbstractSimiWidget {
public State state;
public Indicator(int x, int y, Component tooltip) {
super(x, y, AllGuiTextures.INDICATOR.width, AllGuiTextures.INDICATOR.height);
this.toolTip = toolTip.isEmpty() ? ImmutableList.of() : ImmutableList.of(tooltip);
this.state = State.OFF;
}
@Override
public void renderWidget(@NotNull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
if (!visible)
return;
AllGuiTextures toDraw;
switch (state) {
case ON: toDraw = AllGuiTextures.INDICATOR_WHITE; break;
case OFF: toDraw = AllGuiTextures.INDICATOR; break;
case RED: toDraw = AllGuiTextures.INDICATOR_RED; break;
case YELLOW: toDraw = AllGuiTextures.INDICATOR_YELLOW; break;
case GREEN: toDraw = AllGuiTextures.INDICATOR_GREEN; break;
default: toDraw = AllGuiTextures.INDICATOR; break;
}
toDraw.render(graphics, getX(), getY());
}
public enum State {
OFF, ON,
RED, YELLOW, GREEN;
}
}

View File

@@ -0,0 +1,85 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import javax.annotation.Nonnull;
public class Label extends AbstractSimiWidget {
public Component text;
public String suffix;
protected boolean hasShadow;
protected int color;
protected Font font;
public Label(int x, int y, Component text) {
super(x, y, Minecraft.getInstance().font.width(text), 10);
font = Minecraft.getInstance().font;
this.text = Components.literal("Label");
color = 0xFFFFFF;
hasShadow = false;
suffix = "";
}
public Label colored(int color) {
this.color = color;
return this;
}
public Label withShadow() {
this.hasShadow = true;
return this;
}
public Label withSuffix(String s) {
suffix = s;
return this;
}
public void setTextAndTrim(Component newText, boolean trimFront, int maxWidthPx) {
Font fontRenderer = Minecraft.getInstance().font;
if (fontRenderer.width(newText) <= maxWidthPx) {
text = newText;
return;
}
String trim = "...";
int trimWidth = fontRenderer.width(trim);
String raw = newText.getString();
StringBuilder builder = new StringBuilder(raw);
int startIndex = trimFront ? 0 : raw.length() - 1;
int endIndex = !trimFront ? 0 : raw.length() - 1;
int step = (int) Math.signum(endIndex - startIndex);
for (int i = startIndex; i != endIndex; i += step) {
String sub = builder.substring(trimFront ? i : startIndex, trimFront ? endIndex + 1 : i + 1);
if (fontRenderer.width(Components.literal(sub).setStyle(newText.getStyle())) + trimWidth <= maxWidthPx) {
text = Components.literal(trimFront ? trim + sub : sub + trim).setStyle(newText.getStyle());
return;
}
}
}
@Override
protected void doRender(@Nonnull GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
if (text == null || text.getString().isEmpty())
return;
RenderSystem.setShaderColor(1, 1, 1, 1);
MutableComponent copy = text.plainCopy();
if (suffix != null && !suffix.isEmpty())
copy.append(suffix);
graphics.drawString(font, copy, getX(), getY(), color, hasShadow);
}
}

View File

@@ -0,0 +1,196 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.AllKeys;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.util.function.Consumer;
import java.util.function.Function;
public class ScrollInput extends AbstractSimiWidget {
protected Consumer<Integer> onScroll;
protected int state;
protected Component title = Lang.translateDirect("gui.scrollInput.defaultTitle");
protected final Component scrollToModify = Lang.translateDirect("gui.scrollInput.scrollToModify");
protected final Component shiftScrollsFaster = Lang.translateDirect("gui.scrollInput.shiftScrollsFaster");
protected Component hint = null;
protected Label displayLabel;
protected boolean inverted;
protected boolean soundPlayed;
protected Function<Integer, Component> formatter;
protected int min, max;
protected int shiftStep;
Function<StepContext, Integer> step;
public ScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
super(xIn, yIn, widthIn, heightIn);
state = 0;
min = 0;
max = 1;
shiftStep = 5;
step = standardStep();
formatter = i -> Components.literal(String.valueOf(i));
soundPlayed = false;
}
public Function<StepContext, Integer> standardStep() {
return c -> c.shift ? shiftStep : 1;
}
public ScrollInput inverted() {
inverted = true;
return this;
}
public ScrollInput withRange(int min, int max) {
this.min = min;
this.max = max;
return this;
}
public ScrollInput calling(Consumer<Integer> onScroll) {
this.onScroll = onScroll;
return this;
}
public ScrollInput format(Function<Integer, Component> formatter) {
this.formatter = formatter;
return this;
}
public ScrollInput removeCallback() {
this.onScroll = null;
return this;
}
public ScrollInput titled(MutableComponent title) {
this.title = title;
updateTooltip();
return this;
}
public ScrollInput addHint(MutableComponent hint) {
this.hint = hint;
updateTooltip();
return this;
}
public ScrollInput withStepFunction(Function<StepContext, Integer> step) {
this.step = step;
return this;
}
public ScrollInput writingTo(Label label) {
this.displayLabel = label;
if (label != null)
writeToLabel();
return this;
}
@Override
public void tick() {
super.tick();
soundPlayed = false;
}
public int getState() {
return state;
}
public ScrollInput setState(int state) {
this.state = state;
clampState();
updateTooltip();
if (displayLabel != null)
writeToLabel();
return this;
}
public ScrollInput withShiftStep(int step) {
shiftStep = step;
return this;
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
if (!this.visible || !this.isHovered) return false; //Added
if (inverted)
scrollX *= -1;
StepContext context = new StepContext();
context.control = AllKeys.ctrlDown();
context.shift = AllKeys.shiftDown();
context.currentValue = state;
context.forward = scrollX > 0;
int priorState = state;
boolean shifted = AllKeys.shiftDown();
int step = (int) Math.signum(scrollX) * this.step.apply(context);
state += step;
if (shifted)
state -= state % shiftStep;
clampState();
if (priorState != state) {
// if (!soundPlayed)
// Minecraft.getInstance()
// .getSoundManager()
// .play(SimpleSoundInstance.forUI(AllSoundEvents.SCROLL_VALUE.getMainEvent(),
// 1.5f + 0.1f * (state - min) / (max - min)));
// soundPlayed = true;
onChanged();
}
// return priorState != state;
return true; //Changed
}
protected void clampState() {
if (state >= max)
state = max - 1;
if (state < min)
state = min;
}
public void onChanged() {
if (displayLabel != null)
writeToLabel();
if (onScroll != null)
onScroll.accept(state);
updateTooltip();
}
protected void writeToLabel() {
displayLabel.text = formatter.apply(state);
}
protected void updateTooltip() {
toolTip.clear();
if (title == null)
return;
toolTip.add(title.plainCopy()
.withStyle(s -> s.withColor(HEADER_RGB)));
if (hint != null)
toolTip.add(hint.plainCopy()
.withStyle(s -> s.withColor(HINT_RGB)));
toolTip.add(scrollToModify.plainCopy()
.withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
toolTip.add(shiftScrollsFaster.plainCopy()
.withStyle(ChatFormatting.ITALIC, ChatFormatting.DARK_GRAY));
}
public static class StepContext {
public int currentValue;
public boolean forward;
public boolean shift;
public boolean control;
}
}

View File

@@ -0,0 +1,72 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.util.ArrayList;
import java.util.List;
public class SelectionScrollInput extends ScrollInput {
private final MutableComponent scrollToSelect = Lang.translateDirect("gui.scrollInput.scrollToSelect");
protected List<? extends Component> options;
public SelectionScrollInput(int xIn, int yIn, int widthIn, int heightIn) {
super(xIn, yIn, widthIn, heightIn);
options = new ArrayList<>();
inverted();
}
public ScrollInput forOptions(List<? extends Component> options) {
this.options = options;
this.max = options.size();
format(options::get);
updateTooltip();
return this;
}
@Override
protected void updateTooltip() {
toolTip.clear();
if (title == null)
return;
toolTip.add(title.plainCopy()
.withStyle(s -> s.withColor(HEADER_RGB)));
int min = Math.min(this.max - 16, state - 7);
int max = Math.max(this.min + 16, state + 8);
min = Math.max(min, this.min);
max = Math.min(max, this.max);
if (this.min + 1 == min)
min--;
if (min > this.min)
toolTip.add(Components.literal("> ...")
.withStyle(ChatFormatting.GRAY));
if (this.max - 1 == max)
max++;
for (int i = min; i < max; i++) {
if (i == state)
toolTip.add(Components.empty()
.append("-> ")
.append(options.get(i))
.withStyle(ChatFormatting.WHITE));
else
toolTip.add(Components.empty()
.append("> ")
.append(options.get(i))
.withStyle(ChatFormatting.GRAY));
}
if (max < this.max)
toolTip.add(Components.literal("> ...")
.withStyle(ChatFormatting.GRAY));
if (hint != null)
toolTip.add(hint.plainCopy()
.withStyle(s -> s.withColor(HINT_RGB)));
toolTip.add(scrollToSelect.plainCopy()
.withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.ITALIC));
}
}

View File

@@ -0,0 +1,25 @@
package nl.requios.effortlessbuilding.create.foundation.gui.widget;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import java.util.List;
public class TooltipArea extends AbstractSimiWidget {
public TooltipArea(int x, int y, int width, int height) {
super(x, y, width, height);
}
@Override
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
if (visible)
isHovered = mouseX >= getX() && mouseY >= getY() && mouseX < getX() + width && mouseY < getY() + height;
}
public TooltipArea withTooltip(List<Component> tooltip) {
this.toolTip = tooltip;
return this;
}
}

View File

@@ -0,0 +1,195 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.minecraft.ChatFormatting.AQUA;
import static net.minecraft.ChatFormatting.BLUE;
import static net.minecraft.ChatFormatting.DARK_GRAY;
import static net.minecraft.ChatFormatting.DARK_GREEN;
import static net.minecraft.ChatFormatting.DARK_PURPLE;
import static net.minecraft.ChatFormatting.DARK_RED;
import static net.minecraft.ChatFormatting.GOLD;
import static net.minecraft.ChatFormatting.GRAY;
import static net.minecraft.ChatFormatting.GREEN;
import static net.minecraft.ChatFormatting.LIGHT_PURPLE;
import static net.minecraft.ChatFormatting.RED;
import static net.minecraft.ChatFormatting.STRIKETHROUGH;
import static net.minecraft.ChatFormatting.WHITE;
import static net.minecraft.ChatFormatting.YELLOW;
import static nl.requios.effortlessbuilding.create.foundation.item.TooltipHelper.cutStringTextComponent;
import static nl.requios.effortlessbuilding.create.foundation.item.TooltipHelper.cutTextComponent;
public class ItemDescription {
public static final ItemDescription MISSING = new ItemDescription(null);
public static Component trim = Components.literal(" ").withStyle(WHITE, STRIKETHROUGH);
public enum Palette {
Blue(BLUE, AQUA),
Green(DARK_GREEN, GREEN),
Yellow(GOLD, YELLOW),
Red(DARK_RED, RED),
Purple(DARK_PURPLE, LIGHT_PURPLE),
Gray(DARK_GRAY, GRAY),
;
private Palette(ChatFormatting primary, ChatFormatting highlight) {
color = primary;
hColor = highlight;
}
public ChatFormatting color;
public ChatFormatting hColor;
}
private List<Component> lines;
private List<Component> linesOnShift;
private List<Component> linesOnCtrl;
private Palette palette;
public ItemDescription(Palette palette) {
this.palette = palette;
lines = new ArrayList<>();
linesOnShift = new ArrayList<>();
linesOnCtrl = new ArrayList<>();
}
public ItemDescription withSummary(Component summary) {
addStrings(linesOnShift, cutTextComponent(summary, palette.color, palette.hColor));
return this;
}
public static String makeProgressBar(int length, int filledLength) {
String bar = " ";
int emptySpaces = length - filledLength;
for (int i = 0; i < filledLength; i++)
bar += "\u2588";
for (int i = 0; i < emptySpaces; i++)
bar += "\u2592";
return bar + " ";
}
public ItemDescription withBehaviour(String condition, String behaviour) {
add(linesOnShift, Components.literal(condition).withStyle(GRAY));
addStrings(linesOnShift, cutStringTextComponent(behaviour, palette.color, palette.hColor, 1));
return this;
}
public ItemDescription withControl(String condition, String action) {
add(linesOnCtrl, Components.literal(condition).withStyle(GRAY));
addStrings(linesOnCtrl, cutStringTextComponent(action, palette.color, palette.hColor, 1));
return this;
}
public ItemDescription createTabs() {
boolean hasDescription = !linesOnShift.isEmpty();
boolean hasControls = !linesOnCtrl.isEmpty();
if (hasDescription || hasControls) {
String[] holdDesc = Lang.translateDirect("tooltip.holdForDescription", "$")
.getString()
.split("\\$");
String[] holdCtrl = Lang.translateDirect("tooltip.holdForControls", "$")
.getString()
.split("\\$");
MutableComponent keyShift = Lang.translateDirect("tooltip.keyShift");
MutableComponent keyCtrl = Lang.translateDirect("tooltip.keyCtrl");
for (List<Component> list : Arrays.asList(lines, linesOnShift, linesOnCtrl)) {
boolean shift = list == linesOnShift;
boolean ctrl = list == linesOnCtrl;
if (holdDesc.length != 2 || holdCtrl.length != 2) {
list.add(0, Components.literal("Invalid lang formatting!"));
continue;
}
if (hasControls) {
MutableComponent tabBuilder = Components.empty();
tabBuilder.append(Components.literal(holdCtrl[0]).withStyle(DARK_GRAY));
tabBuilder.append(keyCtrl.plainCopy()
.withStyle(ctrl ? WHITE : GRAY));
tabBuilder.append(Components.literal(holdCtrl[1]).withStyle(DARK_GRAY));
list.add(0, tabBuilder);
}
if (hasDescription) {
MutableComponent tabBuilder = Components.empty();
tabBuilder.append(Components.literal(holdDesc[0]).withStyle(DARK_GRAY));
tabBuilder.append(keyShift.plainCopy()
.withStyle(shift ? WHITE : GRAY));
tabBuilder.append(Components.literal(holdDesc[1]).withStyle(DARK_GRAY));
list.add(0, tabBuilder);
}
if (shift || ctrl)
list.add(hasDescription && hasControls ? 2 : 1, Components.immutableEmpty());
}
}
if (!hasDescription)
linesOnShift = lines;
if (!hasControls)
linesOnCtrl = lines;
return this;
}
public static String hightlight(String s, Palette palette) {
return palette.hColor + s + palette.color;
}
public static void addStrings(List<Component> infoList, List<Component> textLines) {
textLines.forEach(s -> add(infoList, s));
}
public static void add(List<Component> infoList, List<Component> textLines) {
infoList.addAll(textLines);
}
public static void add(List<Component> infoList, Component line) {
infoList.add(line);
}
public Palette getPalette() {
return palette;
}
public List<Component> addInformation(List<Component> tooltip) {
if (Screen.hasShiftDown()) {
tooltip.addAll(linesOnShift);
return tooltip;
}
if (Screen.hasControlDown()) {
tooltip.addAll(linesOnCtrl);
return tooltip;
}
tooltip.addAll(lines);
return tooltip;
}
public List<Component> getLines() {
return lines;
}
public List<Component> getLinesOnCtrl() {
return linesOnCtrl;
}
public List<Component> getLinesOnShift() {
return linesOnShift;
}
}

View File

@@ -0,0 +1,49 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.items.IItemHandlerModifiable;
public class ItemHandlerWrapper implements IItemHandlerModifiable {
private IItemHandlerModifiable wrapped;
public ItemHandlerWrapper(IItemHandlerModifiable wrapped) {
this.wrapped = wrapped;
}
@Override
public int getSlots() {
return wrapped.getSlots();
}
@Override
public ItemStack getStackInSlot(int slot) {
return wrapped.getStackInSlot(slot);
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
return wrapped.insertItem(slot, stack, simulate);
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
return wrapped.extractItem(slot, amount, simulate);
}
@Override
public int getSlotLimit(int slot) {
return wrapped.getSlotLimit(slot);
}
@Override
public boolean isItemValid(int slot, ItemStack stack) {
return wrapped.isItemValid(slot, stack);
}
@Override
public void setStackInSlot(int slot, ItemStack stack) {
wrapped.setStackInSlot(slot, stack);
}
}

View File

@@ -0,0 +1,276 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import net.createmod.catnip.data.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
import net.minecraft.util.Mth;
import net.minecraft.world.Containers;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.items.IItemHandler;
import org.apache.commons.lang3.mutable.MutableInt;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class ItemHelper {
public static void dropContents(Level world, BlockPos pos, IItemHandler inv) {
for (int slot = 0; slot < inv.getSlots(); slot++)
Containers.dropItemStack(world, pos.getX(), pos.getY(), pos.getZ(), inv.getStackInSlot(slot));
}
public static List<ItemStack> multipliedOutput(ItemStack in, ItemStack out) {
List<ItemStack> stacks = new ArrayList<>();
ItemStack result = out.copy();
result.setCount(in.getCount() * out.getCount());
while (result.getCount() > result.getMaxStackSize()) {
stacks.add(result.split(result.getMaxStackSize()));
}
stacks.add(result);
return stacks;
}
public static void addToList(ItemStack stack, List<ItemStack> stacks) {
for (ItemStack s : stacks) {
if (!ItemStack.isSameItemSameComponents(stack, s))
continue;
int transferred = Math.min(s.getOrDefault(DataComponents.MAX_STACK_SIZE, 64) - s.getCount(), stack.getCount());
s.grow(transferred);
stack.shrink(transferred);
}
if (stack.getCount() > 0)
stacks.add(stack);
}
public static boolean isSameInventory(IItemHandler h1, IItemHandler h2) {
if (h1 == null || h2 == null)
return false;
if (h1.getSlots() != h2.getSlots())
return false;
for (int slot = 0; slot < h1.getSlots(); slot++) {
if (h1.getStackInSlot(slot) != h2.getStackInSlot(slot))
return false;
}
return true;
}
public static int calcRedstoneFromInventory(@Nullable IItemHandler inv) {
if (inv == null)
return 0;
int i = 0;
float f = 0.0F;
int totalSlots = inv.getSlots();
for (int j = 0; j < inv.getSlots(); ++j) {
int slotLimit = inv.getSlotLimit(j);
if (slotLimit == 0) {
totalSlots--;
continue;
}
ItemStack itemstack = inv.getStackInSlot(j);
if (!itemstack.isEmpty()) {
f += (float) itemstack.getCount() / (float) Math.min(slotLimit, itemstack.getMaxStackSize());
++i;
}
}
if (totalSlots == 0)
return 0;
f = f / totalSlots;
return Mth.floor(f * 14.0F) + (i > 0 ? 1 : 0);
}
public static List<Pair<Ingredient, MutableInt>> condenseIngredients(NonNullList<Ingredient> recipeIngredients) {
List<Pair<Ingredient, MutableInt>> actualIngredients = new ArrayList<>();
Ingredients: for (Ingredient igd : recipeIngredients) {
for (Pair<Ingredient, MutableInt> pair : actualIngredients) {
ItemStack[] stacks1 = pair.getFirst()
.getItems();
ItemStack[] stacks2 = igd.getItems();
if (stacks1.length != stacks2.length)
continue;
for (int i = 0; i <= stacks1.length; i++) {
if (i == stacks1.length) {
pair.getSecond()
.increment();
continue Ingredients;
}
if (!ItemStack.matches(stacks1[i], stacks2[i]))
break;
}
}
actualIngredients.add(Pair.of(igd, new MutableInt(1)));
}
return actualIngredients;
}
public static boolean matchIngredients(Ingredient i1, Ingredient i2) {
if (i1 == i2)
return true;
ItemStack[] stacks1 = i1.getItems();
ItemStack[] stacks2 = i2.getItems();
if (stacks1 == stacks2)
return true;
if (stacks1.length == stacks2.length) {
for (int i = 0; i < stacks1.length; i++)
if (!ItemStack.isSameItem(stacks1[i], stacks2[i]))
return false;
return true;
}
return false;
}
public static boolean matchAllIngredients(NonNullList<Ingredient> ingredients) {
if (ingredients.size() <= 1)
return true;
Ingredient firstIngredient = ingredients.get(0);
for (int i = 1; i < ingredients.size(); i++)
if (!matchIngredients(firstIngredient, ingredients.get(i)))
return false;
return true;
}
public static enum ExtractionCountMode {
EXACTLY, UPTO
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> test, boolean simulate) {
return extract(inv, test, ExtractionCountMode.UPTO, 64, simulate);
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> test, int exactAmount, boolean simulate) {
return extract(inv, test, ExtractionCountMode.EXACTLY, exactAmount, simulate);
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> test, ExtractionCountMode mode, int amount,
boolean simulate) {
ItemStack extracting = ItemStack.EMPTY;
boolean amountRequired = mode == ExtractionCountMode.EXACTLY;
boolean checkHasEnoughItems = amountRequired;
boolean hasEnoughItems = !checkHasEnoughItems;
boolean potentialOtherMatch = false;
int maxExtractionCount = amount;
Extraction: do {
extracting = ItemStack.EMPTY;
for (int slot = 0; slot < inv.getSlots(); slot++) {
int amountToExtractFromThisSlot =
Math.min(maxExtractionCount - extracting.getCount(), inv.getStackInSlot(slot)
.getMaxStackSize());
ItemStack stack = inv.extractItem(slot, amountToExtractFromThisSlot, true);
if (stack.isEmpty())
continue;
if (!test.test(stack))
continue;
if (!extracting.isEmpty() && !canItemStackAmountsStack(stack, extracting)) {
potentialOtherMatch = true;
continue;
}
if (extracting.isEmpty())
extracting = stack.copy();
else
extracting.grow(stack.getCount());
if (!simulate && hasEnoughItems)
inv.extractItem(slot, stack.getCount(), false);
if (extracting.getCount() >= maxExtractionCount) {
if (checkHasEnoughItems) {
hasEnoughItems = true;
checkHasEnoughItems = false;
continue Extraction;
} else {
break Extraction;
}
}
}
if (!extracting.isEmpty() && !hasEnoughItems && potentialOtherMatch) {
ItemStack blackListed = extracting.copy();
test = test.and(i -> !ItemStack.isSameItemSameComponents(i, blackListed));
continue;
}
if (checkHasEnoughItems)
checkHasEnoughItems = false;
else
break Extraction;
} while (true);
if (amountRequired && extracting.getCount() < amount)
return ItemStack.EMPTY;
return extracting;
}
public static ItemStack extract(IItemHandler inv, Predicate<ItemStack> test,
Function<ItemStack, Integer> amountFunction, boolean simulate) {
ItemStack extracting = ItemStack.EMPTY;
int maxExtractionCount = 64;
for (int slot = 0; slot < inv.getSlots(); slot++) {
if (extracting.isEmpty()) {
ItemStack stackInSlot = inv.getStackInSlot(slot);
if (stackInSlot.isEmpty() || !test.test(stackInSlot))
continue;
int maxExtractionCountForItem = amountFunction.apply(stackInSlot);
if (maxExtractionCountForItem == 0)
continue;
maxExtractionCount = Math.min(maxExtractionCount, maxExtractionCountForItem);
}
ItemStack stack = inv.extractItem(slot, maxExtractionCount - extracting.getCount(), true);
if (!test.test(stack))
continue;
if (!extracting.isEmpty() && !canItemStackAmountsStack(stack, extracting))
continue;
if (extracting.isEmpty())
extracting = stack.copy();
else
extracting.grow(stack.getCount());
if (!simulate)
inv.extractItem(slot, stack.getCount(), false);
if (extracting.getCount() >= maxExtractionCount)
break;
}
return extracting;
}
public static boolean canItemStackAmountsStack(ItemStack a, ItemStack b) {
return ItemStack.isSameItemSameComponents(a, b) && a.getCount() + b.getCount() <= a.getOrDefault(DataComponents.MAX_STACK_SIZE, 64);
}
public static ItemStack findFirstMatch(IItemHandler inv, Predicate<ItemStack> test) {
int slot = findFirstMatchingSlotIndex(inv, test);
if (slot == -1)
return ItemStack.EMPTY;
else
return inv.getStackInSlot(slot);
}
public static int findFirstMatchingSlotIndex(IItemHandler inv, Predicate<ItemStack> test) {
for (int slot = 0; slot < inv.getSlots(); slot++) {
ItemStack toTest = inv.getStackInSlot(slot);
if (test.test(toTest))
return slot;
}
return -1;
}
}

View File

@@ -0,0 +1,20 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
public class TagDependentIngredientItem extends Item {
private TagKey<Item> tag;
public TagDependentIngredientItem(Properties properties, TagKey<Item> tag) {
super(properties);
this.tag = tag;
}
public boolean shouldHide() {
return BuiltInRegistries.ITEM.getTag(tag).isEmpty() || BuiltInRegistries.ITEM.getTag(tag).get().size() == 0;
}
}

View File

@@ -0,0 +1,283 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import com.google.common.base.Strings;
import net.createmod.catnip.data.Couple;
import net.createmod.catnip.lang.ClientFontHelper;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import nl.requios.effortlessbuilding.create.foundation.item.ItemDescription.Palette;
import nl.requios.effortlessbuilding.create.foundation.utility.Components;
import nl.requios.effortlessbuilding.create.foundation.utility.Lang;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
public class TooltipHelper {
public static final int maxWidthPerLine = 200;
public static final Map<String, ItemDescription> cachedTooltips = new HashMap<>();
private static boolean gogglesMode;
private static final Map<Item, Supplier<String>> tooltipReferrals = new HashMap<>();
public static MutableComponent holdShift(Palette color, boolean highlighted) {
return Lang.translateDirect("tooltip.holdForDescription", Lang.translateDirect("tooltip.keyShift")
.withStyle(ChatFormatting.GRAY))
.withStyle(ChatFormatting.DARK_GRAY);
}
public static void addHint(List<Component> tooltip, String hintKey, Object... messageParams) {
Component spacing = Components.literal("");
tooltip.add(spacing.plainCopy()
.append(Lang.translateDirect(hintKey + ".title"))
.withStyle(ChatFormatting.GOLD));
Component hint = Lang.translateDirect(hintKey);
List<Component> cutComponent = TooltipHelper.cutTextComponent(hint, ChatFormatting.GRAY, ChatFormatting.WHITE);
for (Component component : cutComponent)
tooltip.add(spacing.plainCopy()
.append(component));
}
public static void referTo(ItemLike item, Supplier<? extends ItemLike> itemWithTooltip) {
tooltipReferrals.put(item.asItem(), () -> itemWithTooltip.get()
.asItem()
.getDescriptionId());
}
public static void referTo(ItemLike item, String string) {
tooltipReferrals.put(item.asItem(), () -> string);
}
@Deprecated
public static List<String> cutString(Component s, ChatFormatting defaultColor, ChatFormatting highlightColor) {
return cutString(s.getString(), defaultColor, highlightColor, 0);
}
@Deprecated
public static List<String> cutString(String s, ChatFormatting defaultColor, ChatFormatting highlightColor,
int indent) {
// Apply markup
String markedUp = s.replaceAll("_([^_]+)_", highlightColor + "$1" + defaultColor);
// Split words
List<String> words = new LinkedList<>();
BreakIterator iterator = BreakIterator.getLineInstance(Minecraft.getInstance().getLocale());
iterator.setText(markedUp);
int start = iterator.first();
for (int end = iterator.next(); end != BreakIterator.DONE; start = end, end = iterator.next()) {
String word = markedUp.substring(start, end);
words.add(word);
}
Font font = Minecraft.getInstance().font;
List<String> lines = ClientFontHelper.cutString(font, markedUp, maxWidthPerLine);
// Format
String lineStart = Strings.repeat(" ", indent);
List<String> formattedLines = new ArrayList<>(lines.size());
String format = defaultColor.toString();
for (String line : lines) {
String formattedLine = format + lineStart + line;
formattedLines.add(formattedLine);
// format = TextFormatting.getFormatString(formattedLine);
}
return formattedLines;
}
public static List<Component> cutStringTextComponent(String c, ChatFormatting defaultColor,
ChatFormatting highlightColor) {
return cutTextComponent(Components.literal(c), defaultColor, highlightColor, 0);
}
public static List<Component> cutTextComponent(Component c, ChatFormatting defaultColor,
ChatFormatting highlightColor) {
return cutTextComponent(c, defaultColor, highlightColor, 0);
}
public static List<Component> cutStringTextComponent(String c, ChatFormatting defaultColor,
ChatFormatting highlightColor, int indent) {
return cutTextComponent(Components.literal(c), defaultColor, highlightColor, indent);
}
public static List<Component> cutTextComponent(Component c, ChatFormatting defaultColor,
ChatFormatting highlightColor, int indent) {
String s = c.getString();
// Apply markup
String markedUp = s;// .replaceAll("_([^_]+)_", highlightColor + "$1" + defaultColor);
// Split words
List<String> words = new LinkedList<>();
BreakIterator iterator = BreakIterator.getLineInstance(Minecraft.getInstance().getLocale());
iterator.setText(markedUp);
int start = iterator.first();
for (int end = iterator.next(); end != BreakIterator.DONE; start = end, end = iterator.next()) {
String word = markedUp.substring(start, end);
words.add(word);
}
// Apply hard wrap
Font font = Minecraft.getInstance().font;
List<String> lines = new LinkedList<>();
StringBuilder currentLine = new StringBuilder();
int width = 0;
for (String word : words) {
int newWidth = font.width(word.replaceAll("_", ""));
if (width + newWidth > maxWidthPerLine) {
if (width > 0) {
String line = currentLine.toString();
lines.add(line);
currentLine = new StringBuilder();
width = 0;
} else {
lines.add(word);
continue;
}
}
currentLine.append(word);
width += newWidth;
}
if (width > 0) {
lines.add(currentLine.toString());
}
// Format
MutableComponent lineStart = Components.literal(Strings.repeat(" ", indent));
lineStart.withStyle(defaultColor);
List<Component> formattedLines = new ArrayList<>(lines.size());
Couple<ChatFormatting> f = Couple.create(highlightColor, defaultColor);
boolean currentlyHighlighted = false;
for (String string : lines) {
MutableComponent currentComponent = lineStart.plainCopy();
String[] split = string.split("_");
for (String part : split) {
currentComponent.append(Components.literal(part).withStyle(f.get(currentlyHighlighted)));
currentlyHighlighted = !currentlyHighlighted;
}
formattedLines.add(currentComponent);
currentlyHighlighted = !currentlyHighlighted;
}
return formattedLines;
}
// public static List<ITextComponent> cutTextComponentOld(ITextComponent c, TextFormatting defaultColor,
// TextFormatting highlightColor, int indent) {
// IFormattableTextComponent lineStart = StringTextComponent.EMPTY.copy();
// for (int i = 0; i < indent; i++)
// lineStart.append(" ");
// lineStart.formatted(defaultColor);
//
// List<ITextComponent> lines = new ArrayList<>();
// String rawText = getUnformattedDeepText(c);
// String[] words = rawText.split(" ");
// String word;
// IFormattableTextComponent currentLine = lineStart.copy();
//
// boolean firstWord = true;
// boolean lastWord;
//
// // Apply hard wrap
// for (int i = 0; i < words.length; i++) {
// word = words[i];
// lastWord = i == words.length - 1;
//
// if (!lastWord && !firstWord && getComponentLength(currentLine) + word.length() > maxCharsPerLine) {
// lines.add(currentLine);
// currentLine = lineStart.copy();
// firstWord = true;
// }
//
// currentLine.append(new StringTextComponent((firstWord ? "" : " ") + word.replace("_", ""))
// .formatted(word.matches("_([^_]+)_") ? highlightColor : defaultColor));
// firstWord = false;
// }
//
// if (!firstWord) {
// lines.add(currentLine);
// }
//
// return lines;
// }
private static boolean findTooltip(ItemStack stack) {
String key = getTooltipTranslationKey(stack);
if (I18n.exists(key)) {
cachedTooltips.put(key, buildToolTip(key, stack));
return true;
}
cachedTooltips.put(key, ItemDescription.MISSING);
return false;
}
private static ItemDescription buildToolTip(String translationKey, ItemStack stack) {
ItemDescription tooltip = new ItemDescription(Palette.Blue);
String summaryKey = translationKey + ".summary";
// Summary
if (I18n.exists(summaryKey))
tooltip = tooltip.withSummary(Components.literal(I18n.get(summaryKey)));
// Requirements
// if (stack.getItem() instanceof BlockItem) {
// BlockItem item = (BlockItem) stack.getItem();
// if (item.getBlock() instanceof IRotate || item.getBlock() instanceof EngineBlock) {
// tooltip = tooltip.withKineticStats(item.getBlock());
// }
// }
// Behaviours
for (int i = 1; i < 100; i++) {
String conditionKey = translationKey + ".condition" + i;
String behaviourKey = translationKey + ".behaviour" + i;
if (!I18n.exists(conditionKey))
break;
if (i == 1)
tooltip.getLinesOnShift()
.add(Components.immutableEmpty());
tooltip.withBehaviour(I18n.get(conditionKey), I18n.get(behaviourKey));
}
// Controls
for (int i = 1; i < 100; i++) {
String controlKey = translationKey + ".control" + i;
String actionKey = translationKey + ".action" + i;
if (!I18n.exists(controlKey))
break;
tooltip.withControl(I18n.get(controlKey), I18n.get(actionKey));
}
return tooltip.createTabs();
}
public static String getTooltipTranslationKey(ItemStack stack) {
Item item = stack.getItem();
if (tooltipReferrals.containsKey(item))
return tooltipReferrals.get(item)
.get() + ".tooltip";
return item.getDescriptionId(stack) + ".tooltip";
}
// private static int getComponentLength(ITextComponent component) {
// AtomicInteger l = new AtomicInteger();
// TextProcessing.visitFormatted(component, Style.EMPTY, (s, style, charConsumer) -> {
// l.getAndIncrement();
// return true;
// });
// return l.get();
// }
}

View File

@@ -0,0 +1,15 @@
package nl.requios.effortlessbuilding.create.foundation.item;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.level.block.Block;
public class UncontainableBlockItem extends BlockItem {
public UncontainableBlockItem(Block block, Properties properties) {
super(block, properties);
}
@Override
public boolean canFitInsideContainerItems() {
return false;
}
}

View File

@@ -0,0 +1,32 @@
package nl.requios.effortlessbuilding.create.foundation.item.render;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.neoforged.neoforge.client.model.BakedModelWrapper;
public class CustomRenderedItemModel extends BakedModelWrapper<BakedModel> {
public CustomRenderedItemModel(BakedModel originalModel) {
super(originalModel);
}
@Override
public boolean isCustomRenderer() {
return true;
}
@Override
public BakedModel applyTransform(ItemDisplayContext cameraItemDisplayContext, PoseStack mat,
boolean leftHand) {
// Super call returns originalModel, but we want to return this, else BEWLR
// won't be used.
super.applyTransform(cameraItemDisplayContext, mat, leftHand);
return this;
}
public BakedModel getOriginalModel() {
return originalModel;
}
}

View File

@@ -0,0 +1,95 @@
package nl.requios.effortlessbuilding.create.foundation.item.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.createmod.catnip.data.Iterate;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions;
import net.neoforged.neoforge.client.model.data.ModelData;
import nl.requios.effortlessbuilding.create.foundation.render.RenderTypes;
public class PartialItemModelRenderer {
private static final PartialItemModelRenderer INSTANCE = new PartialItemModelRenderer();
private final RandomSource random = RandomSource.create();
private ItemStack stack;
private ItemDisplayContext transformType;
private PoseStack ms;
private MultiBufferSource buffer;
private int overlay;
public static PartialItemModelRenderer of(ItemStack stack, ItemDisplayContext transformType,
PoseStack ms, MultiBufferSource buffer, int overlay) {
PartialItemModelRenderer instance = INSTANCE;
instance.stack = stack;
instance.transformType = transformType;
instance.ms = ms;
instance.buffer = buffer;
instance.overlay = overlay;
return instance;
}
public void render(BakedModel model, int light) {
render(model, RenderTypes.getItemPartialTranslucent(), light);
}
public void renderSolid(BakedModel model, int light) {
render(model, RenderTypes.getItemPartialSolid(), light);
}
// public void renderSolidGlowing(BakedModel model, int light) {
// render(model, RenderTypes.getGlowingSolid(), light);
// }
//
// public void renderGlowing(BakedModel model, int light) {
// render(model, RenderTypes.getGlowingTranslucent(), light);
// }
public void render(BakedModel model, RenderType type, int light) {
if (stack.isEmpty())
return;
ms.pushPose();
ms.translate(-0.5D, -0.5D, -0.5D);
if (!model.isCustomRenderer()) {
VertexConsumer vc = ItemRenderer.getFoilBufferDirect(buffer, type, true, stack.hasFoil());
for (BakedModel pass : model.getRenderPasses(stack, false)) {
renderBakedItemModel(pass, light, ms, vc);
}
} else
IClientItemExtensions.of(stack)
.getCustomRenderer()
.renderByItem(stack, transformType, ms, buffer, light, overlay);
ms.popPose();
}
private void renderBakedItemModel(BakedModel model, int light, PoseStack ms, VertexConsumer buffer) {
ItemRenderer ir = Minecraft.getInstance()
.getItemRenderer();
ModelData data = ModelData.EMPTY;
for (RenderType renderType : model.getRenderTypes(stack, false)) {
for (Direction direction : Iterate.directions) {
random.setSeed(42L);
ir.renderQuadList(ms, buffer, model.getQuads(null, direction, random, data, renderType), stack, light,
overlay);
}
random.setSeed(42L);
ir.renderQuadList(ms, buffer, model.getQuads(null, null, random, data, renderType), stack, light, overlay);
}
}
}

View File

@@ -0,0 +1,113 @@
package nl.requios.effortlessbuilding.create.foundation.render;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import dev.engine_room.flywheel.lib.visualization.VisualizationHelper;
import net.createmod.catnip.animation.AnimationTickHolder;
import net.createmod.catnip.registry.RegisteredObjectsHelper;
import net.createmod.catnip.render.SuperByteBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import nl.requios.effortlessbuilding.create.Create;
import nl.requios.effortlessbuilding.create.foundation.virtualWorld.VirtualRenderWorld;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import javax.annotation.Nullable;
import java.util.Iterator;
public class BlockEntityRenderHelper {
public static void renderBlockEntities(Level world, Iterable<BlockEntity> customRenderBEs, PoseStack ms,
MultiBufferSource buffer) {
renderBlockEntities(world, null, customRenderBEs, ms, null, buffer);
}
public static void renderBlockEntities(Level world, Iterable<BlockEntity> customRenderBEs, PoseStack ms,
MultiBufferSource buffer, float pt) {
renderBlockEntities(world, null, customRenderBEs, ms, null, buffer, pt);
}
public static void renderBlockEntities(Level world, @Nullable VirtualRenderWorld renderWorld,
Iterable<BlockEntity> customRenderBEs, PoseStack ms, @Nullable Matrix4f lightTransform, MultiBufferSource buffer) {
renderBlockEntities(world, renderWorld, customRenderBEs, ms, lightTransform, buffer,
AnimationTickHolder.getPartialTicks());
}
public static void renderBlockEntities(Level world, @Nullable VirtualRenderWorld renderWorld,
Iterable<BlockEntity> customRenderBEs, PoseStack ms, @Nullable Matrix4f lightTransform, MultiBufferSource buffer,
float pt) {
Iterator<BlockEntity> iterator = customRenderBEs.iterator();
while (iterator.hasNext()) {
BlockEntity blockEntity = iterator.next();
if (VisualizationManager.supportsVisualization(world) && VisualizationHelper.skipVanillaRender(blockEntity))
continue;
BlockEntityRenderer<BlockEntity> renderer = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(blockEntity);
if (renderer == null) {
iterator.remove();
continue;
}
BlockPos pos = blockEntity.getBlockPos();
ms.pushPose();
TransformStack.of(ms)
.translate(pos);
try {
int worldLight = getCombinedLight(world, getLightPos(lightTransform, pos), renderWorld, pos);
if (renderWorld != null) {
// Swap the real world for the render world so that the renderer gets contraption-local information
blockEntity.setLevel(renderWorld);
renderer.render(blockEntity, pt, ms, buffer, worldLight, OverlayTexture.NO_OVERLAY);
blockEntity.setLevel(world);
} else {
renderer.render(blockEntity, pt, ms, buffer, worldLight, OverlayTexture.NO_OVERLAY);
}
} catch (Exception e) {
iterator.remove();
String message = "BlockEntity " + RegisteredObjectsHelper.getKeyOrThrow(blockEntity.getType())
.toString() + " could not be rendered virtually.";
// if (AllConfigs.CLIENT.explainRenderErrors.get())
Create.LOGGER.error(message, e);
// else
// Create.LOGGER.error(message);
}
ms.popPose();
}
}
private static BlockPos getLightPos(@Nullable Matrix4f lightTransform, BlockPos contraptionPos) {
if (lightTransform != null) {
Vector4f lightVec = new Vector4f(contraptionPos.getX() + .5f, contraptionPos.getY() + .5f, contraptionPos.getZ() + .5f, 1);
lightVec.mul(lightTransform);
return BlockPos.containing(lightVec.x(), lightVec.y(), lightVec.z());
} else {
return contraptionPos;
}
}
public static int getCombinedLight(Level world, BlockPos worldPos, @Nullable VirtualRenderWorld renderWorld,
BlockPos renderWorldPos) {
int worldLight = LevelRenderer.getLightColor(world, worldPos);
if (renderWorld != null) {
int renderWorldLight = LevelRenderer.getLightColor(renderWorld, renderWorldPos);
return SuperByteBuffer.maxLight(worldLight, renderWorldLight);
}
return worldLight;
}
}

View File

@@ -0,0 +1,152 @@
package nl.requios.effortlessbuilding.create.foundation.render;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import nl.requios.effortlessbuilding.create.AllSpecialTextures;
import nl.requios.effortlessbuilding.create.Create;
// TODO 1.17: use custom shaders instead of vanilla ones
public class RenderTypes extends RenderStateShard {
// public static final ShaderStateShard GLOWING_SHADER = new ShaderStateShard(() -> Shaders.glowingShader);
private static final RenderType OUTLINE_SOLID =
RenderType.create(createLayerName("outline_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false,
false, RenderType.CompositeState.builder()
.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER)
.setTextureState(new TextureStateShard(AllSpecialTextures.BLANK.getLocation(), false, false))
.setCullState(CULL)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.createCompositeState(false));
public static RenderType getOutlineSolid() {
return OUTLINE_SOLID;
}
public static RenderType getOutlineTranslucent(ResourceLocation texture, boolean cull) {
return RenderType.create(createLayerName("outline_translucent" + (cull ? "_cull" : "")),
DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder()
.setShaderState(cull ? RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER : RENDERTYPE_ENTITY_TRANSLUCENT_SHADER)
.setTextureState(new TextureStateShard(texture, false, false))
.setTransparencyState(TRANSLUCENT_TRANSPARENCY)
.setCullState(cull ? CULL : NO_CULL)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.setWriteMaskState(COLOR_WRITE)
.createCompositeState(false));
}
// public static RenderType getGlowingSolid(ResourceLocation texture) {
// return RenderType.create(createLayerName("glowing_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256,
// true, false, RenderType.CompositeState.builder()
// .setShaderState(GLOWING_SHADER)
// .setTextureState(new TextureStateShard(texture, false, false))
// .setCullState(CULL)
// .setLightmapState(LIGHTMAP)
// .setOverlayState(OVERLAY)
// .createCompositeState(true));
// }
//
// private static final RenderType GLOWING_SOLID_DEFAULT = getGlowingSolid(InventoryMenu.BLOCK_ATLAS);
//
// public static RenderType getGlowingSolid() {
// return GLOWING_SOLID_DEFAULT;
// }
// public static RenderType getGlowingTranslucent(ResourceLocation texture) {
// return RenderType.create(createLayerName("glowing_translucent"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS,
// 256, true, true, RenderType.CompositeState.builder()
// .setShaderState(GLOWING_SHADER)
// .setTextureState(new TextureStateShard(texture, false, false))
// .setTransparencyState(TRANSLUCENT_TRANSPARENCY)
// .setLightmapState(LIGHTMAP)
// .setOverlayState(OVERLAY)
// .createCompositeState(true));
// }
private static final RenderType ADDITIVE = RenderType.create(createLayerName("additive"), DefaultVertexFormat.BLOCK,
VertexFormat.Mode.QUADS, 256, true, true, RenderType.CompositeState.builder()
.setShaderState(RENDERTYPE_SOLID_SHADER)
.setTextureState(new TextureStateShard(InventoryMenu.BLOCK_ATLAS, false, false))
.setTransparencyState(ADDITIVE_TRANSPARENCY)
.setCullState(NO_CULL)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.createCompositeState(true));
public static RenderType getAdditive() {
return ADDITIVE;
}
// private static final RenderType GLOWING_TRANSLUCENT_DEFAULT = getGlowingTranslucent(InventoryMenu.BLOCK_ATLAS);
// public static RenderType getGlowingTranslucent() {
// return GLOWING_TRANSLUCENT_DEFAULT;
// }
private static final RenderType ITEM_PARTIAL_SOLID =
RenderType.create(createLayerName("item_partial_solid"), DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true,
false, RenderType.CompositeState.builder()
.setShaderState(RENDERTYPE_ENTITY_SOLID_SHADER)
.setTextureState(BLOCK_SHEET)
.setCullState(CULL)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.createCompositeState(true));
public static RenderType getItemPartialSolid() {
return ITEM_PARTIAL_SOLID;
}
private static final RenderType ITEM_PARTIAL_TRANSLUCENT = RenderType.create(createLayerName("item_partial_translucent"),
DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true, RenderType.CompositeState.builder()
.setShaderState(RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER)
.setTextureState(BLOCK_SHEET)
.setTransparencyState(TRANSLUCENT_TRANSPARENCY)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.createCompositeState(true));
public static RenderType getItemPartialTranslucent() {
return ITEM_PARTIAL_TRANSLUCENT;
}
private static final RenderType FLUID = RenderType.create(createLayerName("fluid"),
DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, RenderType.CompositeState.builder()
.setShaderState(RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER)
.setTextureState(BLOCK_SHEET_MIPPED)
.setTransparencyState(TRANSLUCENT_TRANSPARENCY)
.setLightmapState(LIGHTMAP)
.setOverlayState(OVERLAY)
.createCompositeState(true));
public static RenderType getFluid() {
return FLUID;
}
private static String createLayerName(String name) {
return Create.ID + ":" + name;
}
// Mmm gimme those protected fields
private RenderTypes() {
super(null, null, null);
}
// @EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD)
// private static class Shaders {
// private static ShaderInstance glowingShader;
//
// @SubscribeEvent
// public static void onRegisterShaders(RegisterShadersEvent event) throws IOException {
// ResourceManager resourceManager = event.getResourceManager();
// event.registerShader(new ShaderInstance(resourceManager, Create.asResource("glowing_shader"), DefaultVertexFormat.NEW_ENTITY), shader -> glowingShader = shader);
// }
// }
}

View File

@@ -0,0 +1,36 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.event.EventHooks;
import javax.annotation.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public abstract class AbstractBlockBreakQueue {
protected Consumer<BlockPos> makeCallbackFor(Level world, float effectChance, ItemStack toDamage,
@Nullable Player playerEntity, BiConsumer<BlockPos, ItemStack> drop) {
return pos -> {
ItemStack usedTool = toDamage.copy();
BlockHelper.destroyBlockAs(world, pos, playerEntity, toDamage, effectChance,
stack -> drop.accept(pos, stack));
if (toDamage.isEmpty() && !usedTool.isEmpty())
EventHooks.onPlayerDestroyItem(playerEntity, usedTool, InteractionHand.MAIN_HAND);
};
}
public void destroyBlocks(Level world, @Nullable LivingEntity entity, BiConsumer<BlockPos, ItemStack> drop) {
Player playerEntity = entity instanceof Player ? ((Player) entity) : null;
ItemStack toDamage =
playerEntity != null && !playerEntity.isCreative() ? playerEntity.getMainHandItem() : ItemStack.EMPTY;
destroyBlocks(world, toDamage, playerEntity, drop);
}
public abstract void destroyBlocks(Level world, ItemStack toDamage, @Nullable Player playerEntity,
BiConsumer<BlockPos, ItemStack> drop);
}

View File

@@ -0,0 +1,393 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import net.createmod.catnip.nbt.NBTProcessors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseRailBlock;
import net.minecraft.world.level.block.BedBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.IceBlock;
import net.minecraft.world.level.block.SlimeBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.material.FluidState;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.common.SpecialPlantable;
import net.neoforged.neoforge.event.level.BlockDropsEvent;
import net.neoforged.neoforge.event.level.BlockEvent;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Consumer;
public class BlockHelper {
private static final List<IntegerProperty> COUNT_STATES = List.of(
BlockStateProperties.EGGS,
BlockStateProperties.PICKLES,
BlockStateProperties.CANDLES
);
private static final List<Block> VINELIKE_BLOCKS = List.of(
Blocks.VINE, Blocks.GLOW_LICHEN
);
private static final List<BooleanProperty> VINELIKE_STATES = List.of(
BlockStateProperties.UP,
BlockStateProperties.NORTH,
BlockStateProperties.EAST,
BlockStateProperties.SOUTH,
BlockStateProperties.WEST,
BlockStateProperties.DOWN
);
public static BlockState setZeroAge(BlockState blockState) {
if (blockState.hasProperty(BlockStateProperties.AGE_1))
return blockState.setValue(BlockStateProperties.AGE_1, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_2))
return blockState.setValue(BlockStateProperties.AGE_2, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_3))
return blockState.setValue(BlockStateProperties.AGE_3, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_5))
return blockState.setValue(BlockStateProperties.AGE_5, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_7))
return blockState.setValue(BlockStateProperties.AGE_7, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_15))
return blockState.setValue(BlockStateProperties.AGE_15, 0);
if (blockState.hasProperty(BlockStateProperties.AGE_25))
return blockState.setValue(BlockStateProperties.AGE_25, 0);
if (blockState.hasProperty(BlockStateProperties.LEVEL_HONEY))
return blockState.setValue(BlockStateProperties.LEVEL_HONEY, 0);
if (blockState.hasProperty(BlockStateProperties.HATCH))
return blockState.setValue(BlockStateProperties.HATCH, 0);
if (blockState.hasProperty(BlockStateProperties.STAGE))
return blockState.setValue(BlockStateProperties.STAGE, 0);
if (blockState.is(BlockTags.CAULDRONS))
return Blocks.CAULDRON.defaultBlockState();
if (blockState.hasProperty(BlockStateProperties.LEVEL_COMPOSTER))
return blockState.setValue(BlockStateProperties.LEVEL_COMPOSTER, 0);
if (blockState.hasProperty(BlockStateProperties.EXTENDED))
return blockState.setValue(BlockStateProperties.EXTENDED, false);
return blockState;
}
public static int findAndRemoveInInventory(BlockState block, Player player, int amount) {
int amountFound = 0;
Item required = getRequiredItem(block).getItem();
boolean needsTwo = block.hasProperty(BlockStateProperties.SLAB_TYPE)
&& block.getValue(BlockStateProperties.SLAB_TYPE) == SlabType.DOUBLE;
if (needsTwo)
amount *= 2;
for (IntegerProperty property : COUNT_STATES)
if (block.hasProperty(property))
amount *= block.getValue(property);
if (VINELIKE_BLOCKS.contains(block.getBlock())) {
int vineCount = 0;
for (BooleanProperty vineState : VINELIKE_STATES) {
if (block.hasProperty(vineState) && block.getValue(vineState)) {
vineCount++;
}
}
amount += vineCount - 1;
}
{
// Try held Item first
int preferredSlot = player.getInventory().selected;
ItemStack itemstack = player.getInventory()
.getItem(preferredSlot);
int count = itemstack.getCount();
if (itemstack.getItem() == required && count > 0) {
int taken = Math.min(count, amount - amountFound);
player.getInventory()
.setItem(preferredSlot, new ItemStack(itemstack.getItem(), count - taken));
amountFound += taken;
}
}
// Search inventory
for (int i = 0; i < player.getInventory()
.getContainerSize(); ++i) {
if (amountFound == amount)
break;
ItemStack itemstack = player.getInventory()
.getItem(i);
int count = itemstack.getCount();
if (itemstack.getItem() == required && count > 0) {
int taken = Math.min(count, amount - amountFound);
player.getInventory()
.setItem(i, new ItemStack(itemstack.getItem(), count - taken));
amountFound += taken;
}
}
if (needsTwo) {
// Give back 1 if uneven amount was removed
if (amountFound % 2 != 0)
player.getInventory()
.add(new ItemStack(required));
amountFound /= 2;
}
return amountFound;
}
public static ItemStack getRequiredItem(BlockState state) {
ItemStack itemStack = new ItemStack(state.getBlock());
Item item = itemStack.getItem();
if (item == Items.FARMLAND || item == Items.DIRT_PATH)
itemStack = new ItemStack(Items.DIRT);
return itemStack;
}
public static void destroyBlock(Level world, BlockPos pos, float effectChance) {
destroyBlock(world, pos, effectChance, stack -> Block.popResource(world, pos, stack));
}
public static void destroyBlock(Level world, BlockPos pos, float effectChance,
Consumer<ItemStack> droppedItemCallback) {
destroyBlockAs(world, pos, null, ItemStack.EMPTY, effectChance, droppedItemCallback);
}
public static boolean destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool,
float effectChance, Consumer<ItemStack> droppedItemCallback) {
FluidState fluidState = world.getFluidState(pos);
BlockState state = world.getBlockState(pos);
if (world.random.nextFloat() < effectChance)
world.levelEvent(2001, pos, Block.getId(state));
BlockEntity blockEntity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null;
if (player != null) {
BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player);
NeoForge.EVENT_BUS.post(event);
if (event.isCanceled())
return false;
usedTool.mineBlock(world, state, pos, player);
player.awardStat(Stats.BLOCK_MINED.get(state.getBlock()));
}
if (world instanceof ServerLevel serverLevel && world.getGameRules()
.getBoolean(GameRules.RULE_DOBLOCKDROPS) && !world.restoringBlockSnapshots
&& (player == null || !player.isCreative())) {
List<ItemStack> drops = Block.getDrops(state, serverLevel, pos, blockEntity, player, usedTool);
if (player != null) {
BlockDropsEvent event = new BlockDropsEvent(serverLevel, pos, state, blockEntity, List.of(), player, usedTool);
NeoForge.EVENT_BUS.post(event);
if (!event.isCanceled()) {
if ( event.getDroppedExperience() > 0)
state.getBlock().popExperience(serverLevel, pos, event.getDroppedExperience());
}
}
for (ItemStack itemStack : drops)
droppedItemCallback.accept(itemStack);
// Simulating IceBlock#playerDestroy. Not calling method directly as it would drop item
// entities as a side-effect
Registry<Enchantment> enchantmentRegistry = world.registryAccess().registryOrThrow(Registries.ENCHANTMENT);
if (state.getBlock() instanceof IceBlock && usedTool.getEnchantmentLevel(enchantmentRegistry.getHolderOrThrow(Enchantments.SILK_TOUCH)) == 0) {
if (world.dimensionType()
.ultraWarm())
return false;
BlockState blockstate = world.getBlockState(pos.below());
if (blockstate.blocksMotion() || blockstate.liquid())
world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState());
return false;
}
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
}
world.setBlockAndUpdate(pos, fluidState.createLegacyBlock());
return true;
}
public static boolean isSolidWall(BlockGetter reader, BlockPos fromPos, Direction toDirection) {
return hasBlockSolidSide(reader.getBlockState(fromPos.relative(toDirection)), reader,
fromPos.relative(toDirection), toDirection.getOpposite());
}
public static boolean noCollisionInSpace(BlockGetter reader, BlockPos pos) {
return reader.getBlockState(pos)
.getCollisionShape(reader, pos)
.isEmpty();
}
private static void placeRailWithoutUpdate(Level world, BlockState state, BlockPos target) {
LevelChunk chunk = world.getChunkAt(target);
int idx = chunk.getSectionIndex(target.getY());
LevelChunkSection chunksection = chunk.getSection(idx);
if (chunksection == null) {
chunksection = new LevelChunkSection(world.registryAccess()
.registryOrThrow(Registries.BIOME));
chunk.getSections()[idx] = chunksection;
}
BlockState old = chunksection.setBlockState(SectionPos.sectionRelative(target.getX()),
SectionPos.sectionRelative(target.getY()), SectionPos.sectionRelative(target.getZ()), state);
chunk.setUnsaved(true);
world.markAndNotifyBlock(target, chunk, old, state, 82, 512);
world.setBlock(target, state, 82);
world.neighborChanged(target, world.getBlockState(target.below())
.getBlock(), target.below());
}
public static CompoundTag prepareBlockEntityData(BlockState blockState, BlockEntity blockEntity) {
CompoundTag data = null;
if (blockEntity == null)
return null;
RegistryAccess access = blockEntity.getLevel().registryAccess();
if (blockEntity instanceof IPartialSafeNBT safeNbtBE) {
data = new CompoundTag();
safeNbtBE.writeSafe(data, access);
data = NBTProcessors.process(blockState, blockEntity, data, true);
}
return data;
}
public static void placeSchematicBlock(Level world, BlockState state, BlockPos target, ItemStack stack,
@Nullable CompoundTag data) {
BlockEntity existingBlockEntity = world.getBlockEntity(target);
boolean alreadyPlaced = false;
// Piston
if (state.hasProperty(BlockStateProperties.EXTENDED))
state = state.setValue(BlockStateProperties.EXTENDED, Boolean.FALSE);
if (state.hasProperty(BlockStateProperties.WATERLOGGED))
state = state.setValue(BlockStateProperties.WATERLOGGED, Boolean.FALSE);
if (state.getBlock() == Blocks.COMPOSTER)
state = Blocks.COMPOSTER.defaultBlockState();
else if (state.getBlock() != Blocks.SEA_PICKLE && state.getBlock() instanceof SpecialPlantable specialPlantable) {
alreadyPlaced = true;
if (specialPlantable.canPlacePlantAtPosition(stack, world, target, null))
specialPlantable.spawnPlantAtPosition(stack, world, target, null);
}
else if (state.is(BlockTags.CAULDRONS))
state = Blocks.CAULDRON.defaultBlockState();
if (world.dimensionType()
.ultraWarm() && state.getFluidState().is(FluidTags.WATER)) {
int i = target.getX();
int j = target.getY();
int k = target.getZ();
world.playSound(null, target, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F,
2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
for (int l = 0; l < 8; ++l) {
world.addParticle(ParticleTypes.LARGE_SMOKE, i + Math.random(), j + Math.random(), k + Math.random(),
0.0D, 0.0D, 0.0D);
}
Block.dropResources(state, world, target);
return;
}
//noinspection StatementWithEmptyBody
if (alreadyPlaced) {
// pass
} else if (state.getBlock() instanceof BaseRailBlock) {
placeRailWithoutUpdate(world, state, target);
} else {
world.setBlock(target, state, 18);
}
if (data != null) {
// if (existingBlockEntity instanceof IMergeableBE mergeable) {
// BlockEntity loaded = BlockEntity.loadStatic(target, state, data, world.registryAccess());
// if (loaded != null) {
// if (existingBlockEntity.getType()
// .equals(loaded.getType())) {
// mergeable.accept(loaded);
// return;
// }
// }
// }
BlockEntity blockEntity = world.getBlockEntity(target);
if (blockEntity != null) {
data.putInt("x", target.getX());
data.putInt("y", target.getY());
data.putInt("z", target.getZ());
// if (blockEntity instanceof KineticBlockEntity kbe)
// kbe.warnOfMovement();
// if (blockEntity instanceof IMultiBlockEntityContainer imbe)
// if (!imbe.isController())
// data.put("Controller", NbtUtils.writeBlockPos(imbe.getController()));
blockEntity.loadWithComponents(data, world.registryAccess());
}
}
try {
state.getBlock()
.setPlacedBy(world, target, state, null, stack);
} catch (Exception e) {
}
}
public static double getBounceMultiplier(Block block) {
if (block instanceof SlimeBlock)
return 0.8D;
if (block instanceof BedBlock)
return 0.66 * 0.8D;
return 0;
}
public static boolean hasBlockSolidSide(BlockState p_220056_0_, BlockGetter p_220056_1_, BlockPos p_220056_2_,
Direction p_220056_3_) {
return !p_220056_0_.is(BlockTags.LEAVES)
&& Block.isFaceFull(p_220056_0_.getCollisionShape(p_220056_1_, p_220056_2_), p_220056_3_);
}
public static BlockState copyProperties(BlockState fromState, BlockState toState) {
for (Property<?> property : fromState.getProperties()) {
toState = copyProperty(property, fromState, toState);
}
return toState;
}
public static <T extends Comparable<T>> BlockState copyProperty(Property<T> property, BlockState fromState,
BlockState toState) {
if (fromState.hasProperty(property) && toState.hasProperty(property)) {
return toState.setValue(property, fromState.getValue(property));
}
return toState;
}
}

View File

@@ -0,0 +1,91 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import net.createmod.catnip.animation.LerpedFloat;
import net.createmod.catnip.math.AngleHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Mth;
public class CameraAngleAnimationService {
private static final LerpedFloat yRotation = LerpedFloat.angular().startWithValue(0);
private static final LerpedFloat xRotation = LerpedFloat.angular().startWithValue(0);
private static Mode animationMode = Mode.LINEAR;
private static float animationSpeed = -1;
public static void tick() {
yRotation.tickChaser();
xRotation.tickChaser();
if (Minecraft.getInstance().player != null) {
if (!yRotation.settled())
Minecraft.getInstance().player.setYRot(yRotation.getValue(1));
if (!xRotation.settled())
Minecraft.getInstance().player.setXRot(xRotation.getValue(1));
}
}
public static boolean isYawAnimating() {
return !yRotation.settled();
}
public static boolean isPitchAnimating() {
return !xRotation.settled();
}
public static float getYaw(float partialTicks) {
return yRotation.getValue(partialTicks);
}
public static float getPitch(float partialTicks) {
return xRotation.getValue(partialTicks);
}
public static void setAnimationMode(Mode mode) {
animationMode = mode;
}
public static void setAnimationSpeed(float speed) {
animationSpeed = speed;
}
public static void setYawTarget(float yaw) {
float currentYaw = getCurrentYaw();
yRotation.startWithValue(currentYaw);
setupChaser(yRotation, currentYaw + AngleHelper.getShortestAngleDiff(currentYaw, Mth.wrapDegrees(yaw)));
}
public static void setPitchTarget(float pitch) {
float currentPitch = getCurrentPitch();
xRotation.startWithValue(currentPitch);
setupChaser(xRotation, currentPitch + AngleHelper.getShortestAngleDiff(currentPitch, Mth.wrapDegrees(pitch)));
}
private static float getCurrentYaw() {
if (Minecraft.getInstance().player == null)
return 0;
return Mth.wrapDegrees(Minecraft.getInstance().player.getYRot());
}
private static float getCurrentPitch() {
if (Minecraft.getInstance().player == null)
return 0;
return Mth.wrapDegrees(Minecraft.getInstance().player.getXRot());
}
private static void setupChaser(LerpedFloat rotation, float target) {
if (animationMode == Mode.LINEAR) {
rotation.chase(target, animationSpeed > 0 ? animationSpeed : 2, LerpedFloat.Chaser.LINEAR);
} else if (animationMode == Mode.EXPONENTIAL) {
rotation.chase(target, animationSpeed > 0 ? animationSpeed : 0.25, LerpedFloat.Chaser.EXP);
}
}
public enum Mode {
LINEAR,
EXPONENTIAL
}
}

View File

@@ -0,0 +1,33 @@
package nl.requios.effortlessbuilding.create.foundation.utility;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
public final class Components {
private static final Component IMMUTABLE_EMPTY = Component.empty();
public static Component immutableEmpty() {
return IMMUTABLE_EMPTY;
}
/** Use {@link #immutableEmpty()} when possible to prevent creating an extra object. */
public static MutableComponent empty() {
return Component.empty();
}
public static MutableComponent literal(String str) {
return Component.literal(str);
}
public static MutableComponent translatable(String key) {
return Component.translatable(key);
}
public static MutableComponent translatable(String key, Object... args) {
return Component.translatable(key, args);
}
public static MutableComponent keybind(String name) {
return Component.keybind(name);
}
}

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