3 Commits
1.21 ... 1.14

302 changed files with 9603 additions and 18230 deletions

5
.gitattributes vendored
View File

@@ -1,5 +0,0 @@
# 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

5
.gitignore vendored
View File

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

View File

@@ -1,520 +0,0 @@
Unless noted below, Minecraft Forge, Forge Mod Loader, and all
parts herein are licensed under the terms of the LGPL 2.1 found
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
copied below.
Homepage: http://minecraftforge.net/
https://github.com/MinecraftForge/MinecraftForge
A note on authorship:
All source artifacts are property of their original author, with
the exclusion of the contents of the patches directory and others
copied from it from time to time. Authorship of the contents of
the patches directory is retained by the Minecraft Forge project.
This is because the patches are partially machine generated
artifacts, and are changed heavily due to the way forge works.
Individual attribution within them is impossible.
Consent:
All contributions to Forge must consent to the release of any
patch content to the Forge project.
A note on infectivity:
The LGPL is chosen specifically so that projects may depend on Forge
features without being infected with its license. That is the
purpose of the LGPL. Mods and others using this code via ordinary
Java mechanics for referencing libraries are specifically not bound
by Forge's license for the Mod code.
=== MCP Data ===
This software includes data from the Minecraft Coder Pack (MCP), with kind permission
from them. The license to MCP data is not transitive - distribution of this data by
third parties requires independent licensing from the MCP team. This data is not
redistributable without permission from the MCP team.
=== Sharing ===
I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
-- Runtime patcher --
License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
-- ASM transformers --
License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
=========================================================================
This software includes portions from the Apache Maven project at
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
included based on guidelines at
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
with notices intact. The only change is a non-functional change of package name.
This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
authored by genman, heikok, pivot.
The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
=========================================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

1
README.md Normal file
View File

@@ -0,0 +1 @@
The 1.12 repository can be found here: https://bitbucket.org/Requios/effortless-building/src/master/

View File

@@ -1,193 +1,149 @@
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}"
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
mavenCentral()
}
boolean ponderInWorkspace = findProject(':Ponder') != null
ext.buildNumber = System.getenv('BUILD_NUMBER')
version = mod_version
group = 'nl.requios.effortlessbuilding'
base {
archivesName = "effortlessbuilding-${artifact_minecraft_version}"
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
}
}
apply plugin: 'net.minecraftforge.gradle'
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
version = '1.14.4-2.21'
group = 'nl.requios.effortlessbuilding' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'effortlessbuilding'
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + ' (' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
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: 'snapshot', version: '20190719-1.14.3'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
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'
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
systemProperty 'forge.logging.console.level', 'debug'
property '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
}
client {
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'neoforge.enabledGameTestNamespaces', "effortlessbuilding"
if (ponderInWorkspace) {
dependencies {
runtime project(':Ponder')
mods {
effortlessbuilding {
source sourceSets.main
}
}
}
server {
systemProperty 'neoforge.enabledGameTestNamespaces', "effortlessbuilding"
programArgument '--nogui'
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
}
}
}
data {
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
// workingDirectory project.file('run-data')
workingDirectory project.file('run')
// 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()
}
}
// Recommended logging data for a userdev environment (SCAN,REGISTRIES,REGISTRYDUMP)
property 'forge.logging.markers', 'REGISTRIES'
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"
}
}
filter {
includeGroup "curse.maven"
}
}
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
content {
includeGroup "maven.modrinth"
}
}
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
mavenLocal()
flatDir {
dirs 'libs'
args '--mod', 'effortlessbuilding', '--all', '--output', file('src/generated/resources/')
mods {
effortlessbuilding {
source sourceSets.main
}
}
}
}
}
dependencies {
implementation "net.neoforged:neoforge:${neo_version}"
// 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.14.4-28.2.23'
jarJar("net.createmod.ponder:Ponder-NeoForge-${minecraft_version}:${ponder_version}")
// You may put jars on which you depend on in ./libs or you may define them like so..
// compile "some.group:artifact:version:classifier"
// compile "some.group:artifact:version"
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}")
// Real examples
// compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
// compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// These dependencies get remapped to your current MCP mappings
// deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// For more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
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",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": project.jar.archiveVersion,
"Implementation-Version": "${version}",
"Implementation-Vendor" :"requios",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
task jarJarRelease {
group = 'jarjar'
doLast {
tasks.jarJar {
classifier = ''
// Example configuration to allow publishing using the maven-publish task
// we define a custom artifact that is sourced from the reobfJar output task
// and then declare that to be published
// Note you'll need to add a repository here
def reobfFile = file("$buildDir/reobfJar/output.jar")
def reobfArtifact = artifacts.add('default', reobfFile) {
type 'jar'
builtBy 'reobfJar'
}
}
finalizedBy tasks.jarJar
}
java {
withSourcesJar()
withJavadocJar()
}
tasks.build.dependsOn tasks.jarJar
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = base.archivesName.get()
from components.java
// fg.component(it)
artifact reobfArtifact
}
}
repositories {
if (project.hasProperty('mavendir')) {
maven { url mavendir }
maven {
url "file:///${project.projectDir}/mcmodsrepo"
}
}
}

View File

@@ -2,20 +2,3 @@
# 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,7 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip

306
gradlew vendored
View File

@@ -1,130 +1,78 @@
#!/bin/sh
#
# 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.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
#!/usr/bin/env sh
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/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/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# 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
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -133,120 +81,92 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
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.
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
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
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# 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, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# 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" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

74
gradlew.bat vendored
View File

@@ -1,21 +1,3 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -28,28 +10,24 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
if "%ERRORLEVEL%" == "0" goto init
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
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -57,36 +35,48 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
if exist "%JAVA_EXE%" goto init
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
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
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%
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

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

@@ -1,61 +0,0 @@
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

@@ -1,142 +0,0 @@
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

@@ -0,0 +1,134 @@
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<Boolean> breakFar;
public final ForgeConfigSpec.ConfigValue<Boolean> increasedMiningTime;
public final ForgeConfigSpec.ConfigValue<Integer> miningTimePercentage;
public final ForgeConfigSpec.ConfigValue<Integer> quickReplaceMiningLevel;
public final ForgeConfigSpec.ConfigValue<Integer> undoStackSize;
public SurvivalBalancers(ForgeConfigSpec.Builder builder) {
builder.push("SurvivalBalancers");
breakFar = builder
.comment("Allows a survival player to break blocks that are far away, in addition to placing blocks.",
"Note: this allows insta-breaking of blocks in survival.")
.define("breakFar", false);
increasedMiningTime = builder
.comment("Increases the time to mine a block when breaking multiple at once.",
"Mining time depends on how many blocks, what type of blocks, and the percentage below.",
"Example: breaking 1 dirt + 1 obsidian takes the time of breaking 1 dirt + 1 obsidian.")
.define("increasedMiningTime", true);
miningTimePercentage = builder
.comment("How much the mining time of each additional block counts towards an increased mining time.",
"A percentage between 0% and 100%, where 0% is the same as disabling it,",
"and 100% takes as much time as breaking each block individually.",
"The block in front of you always counts as 100%.")
.defineInRange("miningTimePercentage", 50, 0, 200);
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")
.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> shaderTreshold;
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);
shaderTreshold = 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

@@ -1,51 +0,0 @@
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

@@ -1,208 +0,0 @@
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

@@ -1,146 +0,0 @@
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

@@ -1,157 +0,0 @@
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,160 +1,157 @@
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.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 net.minecraft.block.Block;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.crafting.CraftingHelper;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.ExtensionPoint;
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.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import nl.requios.effortlessbuilding.capability.ModeCapabilityManager;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import nl.requios.effortlessbuilding.command.CommandReach;
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.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.item.ItemRandomizerBag;
import nl.requios.effortlessbuilding.item.ItemReachUpgrade1;
import nl.requios.effortlessbuilding.item.ItemReachUpgrade2;
import nl.requios.effortlessbuilding.item.ItemReachUpgrade3;
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;
// The value here should match an entry in the META-INF/mods.toml file
@Mod(EffortlessBuilding.MODID)
public class EffortlessBuilding {
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public class EffortlessBuilding
{
public static final String MODID = "effortlessbuilding";
public static final Logger logger = LogManager.getLogger();
public static final String NAME = "Effortless Building";
public static final String VERSION = "1.14.4-2.21";
public static EffortlessBuilding instance;
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();
public static final Logger logger = LogManager.getLogger();
//Registration
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 IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::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 ItemRandomizerBag ITEM_RANDOMIZER_BAG = new ItemRandomizerBag();
public static final ItemReachUpgrade1 ITEM_REACH_UPGRADE_1 = new ItemReachUpgrade1();
public static final ItemReachUpgrade2 ITEM_REACH_UPGRADE_2 = new ItemReachUpgrade2();
public static final ItemReachUpgrade3 ITEM_REACH_UPGRADE_3 = new ItemReachUpgrade3();
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 static final Block[] BLOCKS = {
};
public static final Supplier<MapCodec<SingleItemLootModifier>> SINGLE_ITEM_LOOT_MODIFIER = EffortlessBuilding.LOOT_MODIFIERS.register("single_item_loot_modifier", SingleItemLootModifier.CODEC);
public static final Item[] ITEMS = {
ITEM_RANDOMIZER_BAG,
ITEM_REACH_UPGRADE_1,
ITEM_REACH_UPGRADE_2,
ITEM_REACH_UPGRADE_3
};
public static final Supplier<AttachmentType<PowerLevel>> POWER_LEVEL = ATTACHMENT_TYPES.register("power_level", () -> AttachmentType.serializable(PowerLevel::new).build());
public static final ContainerType<RandomizerBagContainer> RANDOMIZER_BAG_CONTAINER = register("randomizer_bag", RandomizerBagContainer::new);
public static final ResourceLocation RANDOMIZER_BAG_GUI = new ResourceLocation(EffortlessBuilding.MODID, "randomizer_bag");
public EffortlessBuilding(IEventBus modEventBus, ModContainer container, Dist dist) {
public EffortlessBuilding() {
instance = this;
IEventBus forgeEventBus = NeoForge.EVENT_BUS;
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 the setup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
// Register the enqueueIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
// Register the processIMC method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
// Register the clientSetup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientSetup);
//Register config
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);
}
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, BuildConfig.spec);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
public static void setup(final FMLCommonSetupEvent event) {
@SubscribeEvent
public void setup(final FMLCommonSetupEvent event)
{
CapabilityManager.INSTANCE.register(ModifierCapabilityManager.IModifierCapability.class, new ModifierCapabilityManager.Storage(), ModifierCapabilityManager.ModifierCapability::new);
CapabilityManager.INSTANCE.register(ModeCapabilityManager.IModeCapability.class, new ModeCapabilityManager.Storage(), ModeCapabilityManager.ModeCapability::new);
PacketHandler.register();
//TODO 1.13 config
// ConfigManager.sync(MODID, Config.Type.INSTANCE);
proxy.setup(event);
CompatHelper.setup();
}
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 clientSetup(final FMLClientSetupEvent event) {
proxy.clientSetup(event);
}
public static <T extends AbstractContainerMenu> MenuType<T> registerContainer(IContainerFactory<T> fact) {
MenuType<T> type = new MenuType<T>(fact, FeatureFlags.REGISTRY.allFlags());
return type;
@SubscribeEvent
public void enqueueIMC(final InterModEnqueueEvent event) {
// some example code to dispatch IMC to another mod
// InterModComms.sendTo("examplemod", "helloworld", () -> { logger.info("Hello world from the MDK"); return "Hello world";});
}
@SubscribeEvent
public void processIMC(final InterModProcessEvent event) {
// some example code to receive and process InterModComms from other mods
// logger.info("Got IMC {}", event.getIMCStream().
// map(m->m.getMessageSupplier().get()).
// collect(Collectors.toList()));
}
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
CommandReach.register(event.getCommandDispatcher());
}
private static <T extends Container> ContainerType<T> register(String key, ContainerType.IFactory<T> factory) {
return Registry.register(Registry.MENU, key, new ContainerType<>(factory));
}
public static void log(String msg){
logger.info(msg);
}
public static void log(Player player, String msg) {
public static void log(PlayerEntity player, String msg){
log(player, msg, false);
}
public static void log(Player player, String msg, boolean actionBar) {
player.displayClientMessage(Component.literal(msg), actionBar);
public static void log(PlayerEntity player, String msg, boolean actionBar){
player.sendStatusMessage(new StringTextComponent(msg), actionBar);
}
//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) {
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);
public static void logTranslate(PlayerEntity player, String prefix, String translationKey, String suffix, boolean actionBar){
proxy.logTranslate(player, prefix, translationKey, suffix, actionBar);
}
}

View File

@@ -1,35 +0,0 @@
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

@@ -0,0 +1,209 @@
package nl.requios.effortlessbuilding;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.network.PacketDistributor;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
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.helper.SurvivalHelper;
import nl.requios.effortlessbuilding.network.AddUndoMessage;
import nl.requios.effortlessbuilding.network.ClearUndoMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.RequestLookAtMessage;
import java.util.List;
@Mod.EventBusSubscriber
public class EventHandler
{
@SubscribeEvent
public static void attachCapabilities(AttachCapabilitiesEvent<Entity> event) {
if (event.getObject() instanceof PlayerEntity) {
event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_modifier"), new ModifierCapabilityManager.Provider());
event.addCapability(new ResourceLocation(EffortlessBuilding.MODID, "build_mode"), new ModeCapabilityManager.Provider());
}
}
//TODO 1.13 config
// @SubscribeEvent
// public static void onConfigChangedEvent(ConfigChangedEvent.OnConfigChangedEvent event)
// {
// if (event.getModID().equals(EffortlessBuilding.MODID))
// {
// ConfigManager.sync(EffortlessBuilding.MODID, Config.Type.INSTANCE);
// }
// }
// @SubscribeEvent
// public static void onServerTick(TickEvent.ServerTickEvent event) {
//
// }
@SubscribeEvent
//Only called serverside (except with lilypads...)
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
if (event.getWorld().isRemote()) return;
if (!(event.getEntity() instanceof PlayerEntity)) return;
//Cancel event if necessary
ServerPlayerEntity player = ((ServerPlayerEntity) 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.getWorld().isRemote()) 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
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()), new AddUndoMessage(event.getPos(), event.getState(), Blocks.AIR.getDefaultState()));
}
}
@SubscribeEvent
public static void breakSpeed(PlayerEvent.BreakSpeed event) {
//Disable if config says so
if (!BuildConfig.survivalBalancers.increasedMiningTime.get()) return;
PlayerEntity player = event.getEntityPlayer();
World world = player.world;
BlockPos pos = event.getPos();
//EffortlessBuilding.log(player, String.valueOf(event.getNewSpeed()));
float originalBlockHardness = event.getState().getBlockHardness(world, pos);
if (originalBlockHardness < 0) return; //Dont break bedrock
float totalBlockHardness = 0;
//get coordinates
List<BlockPos> coordinates = BuildModifiers.findCoordinates(player, pos);
for (int i = 1; i < coordinates.size(); i++) {
BlockPos coordinate = coordinates.get(i);
//get existing blockstates at those coordinates
BlockState blockState = world.getBlockState(coordinate);
//add hardness for each blockstate, if can break
if (SurvivalHelper.canBreak(world, player, coordinate))
totalBlockHardness += blockState.getBlockHardness(world, coordinate);
}
//Grabbing percentage from config
float percentage = (float) BuildConfig.survivalBalancers.miningTimePercentage.get() / 100;
totalBlockHardness *= percentage;
totalBlockHardness += originalBlockHardness;
float newSpeed = event.getOriginalSpeed() / totalBlockHardness * originalBlockHardness;
if (Float.isNaN(newSpeed) || newSpeed == 0f) newSpeed = 1f;
event.setNewSpeed(newSpeed);
//EffortlessBuilding.log(player, String.valueOf(event.getNewSpeed()));
}
@SubscribeEvent
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
PlayerEntity player = event.getPlayer();
ModifierSettingsManager.handleNewPlayer(player);
ModeSettingsManager.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
PlayerEntity player = event.getPlayer();
if (player.getEntityWorld().isRemote) return;
UndoRedo.clear(player);
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), new ClearUndoMessage());
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
PlayerEntity player = event.getPlayer();
ModifierSettingsManager.handleNewPlayer(player);
ModeSettingsManager.handleNewPlayer(player);
}
@SubscribeEvent
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
PlayerEntity player = event.getPlayer();
if (player.getEntityWorld().isRemote) 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(() -> (ServerPlayerEntity) player), new ClearUndoMessage());
}
@SubscribeEvent
public static void onPlayerClone(PlayerEvent.Clone event) {
//Attach capabilities on death, otherwise crash
PlayerEntity oldPlayer = event.getOriginal();
oldPlayer.revive();
PlayerEntity newPlayer = event.getEntityPlayer();
ModifierSettingsManager.setModifierSettings(newPlayer, ModifierSettingsManager.getModifierSettings(oldPlayer));
ModeSettingsManager.setModeSettings(newPlayer, ModeSettingsManager.getModeSettings(oldPlayer));
}
}

View File

@@ -0,0 +1,46 @@
package nl.requios.effortlessbuilding;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
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 java.util.HashMap;
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD, value = {Dist.CLIENT})
public class ModClientEventHandler {
private static final HashMap<BuildModes.BuildModeEnum, ResourceLocation> buildModeIcons = new HashMap<>();
private static final HashMap<ModeOptions.ActionEnum, ResourceLocation> modeOptionIcons = new HashMap<>();
@SubscribeEvent
public static void onTextureStitch(final TextureStitchEvent.Pre event) {
//register icon textures
for (final BuildModes.BuildModeEnum mode : BuildModes.BuildModeEnum.values())
{
final ResourceLocation spriteLocation = new ResourceLocation(EffortlessBuilding.MODID, "icons/" + mode.name().toLowerCase());
event.addSprite(spriteLocation);
buildModeIcons.put(mode, spriteLocation);
}
for (final ModeOptions.ActionEnum action : ModeOptions.ActionEnum.values())
{
final ResourceLocation spriteLocation = new ResourceLocation(EffortlessBuilding.MODID, "icons/" + action.name().toLowerCase());
event.addSprite(spriteLocation);
modeOptionIcons.put(action, spriteLocation);
}
}
public static TextureAtlasSprite getBuildModeIcon(BuildModes.BuildModeEnum mode) {
return Minecraft.getInstance().getTextureMap().getSprite(buildModeIcons.get(mode));
}
public static TextureAtlasSprite getModeOptionIcon(ModeOptions.ActionEnum action) {
return Minecraft.getInstance().getTextureMap().getSprite(modeOptionIcons.get(action));
}
}

View File

@@ -0,0 +1,45 @@
package nl.requios.effortlessbuilding;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.event.RegistryEvent;
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 java.util.HashMap;
// You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
// Event bus for receiving Registry Events)
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public class ModEventHandler {
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(EffortlessBuilding.BLOCKS);
}
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(EffortlessBuilding.ITEMS);
for (Block block : EffortlessBuilding.BLOCKS)
{
event.getRegistry().register(new BlockItem(block, new Item.Properties()).setRegistryName(block.getRegistryName()));
}
}
// @SubscribeEvent
// public static void registerContainerTypes(RegistryEvent.Register<ContainerType<?>> event) {
// event.getRegistry().register()
// }
}

View File

@@ -1,62 +0,0 @@
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

@@ -1,130 +0,0 @@
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

@@ -1,106 +0,0 @@
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,19 +1,39 @@
package nl.requios.effortlessbuilding.buildmode;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.UUID;
public abstract class BaseBuildMode implements IBuildMode {
protected int clicks;
//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, Vec3d> hitVecTable = new Hashtable<>();
@Override
public void initialize() {
clicks = 0;
public void initialize(PlayerEntity player) {
rightClickClientTable.put(player.getUniqueID(), 0);
rightClickServerTable.put(player.getUniqueID(), 0);
firstPosTable.put(player.getUniqueID(), BlockPos.ZERO);
sideHitTable.put(player.getUniqueID(), Direction.UP);
hitVecTable.put(player.getUniqueID(), Vec3d.ZERO);
}
@Override
public boolean onClick(BlockSet blocks) {
clicks++;
return false;
public Direction getSideHit(PlayerEntity player) {
return sideHitTable.get(player.getUniqueID());
}
@Override
public Vec3d getHitVec(PlayerEntity player) {
return hitVecTable.get(player.getUniqueID());
}
}

View File

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

@@ -1,56 +0,0 @@
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,87 +1,237 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.world.entity.player.Player;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.message.IsUsingBuildModePacket;
import nl.requios.effortlessbuilding.utilities.BlockSet;
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.*;
@OnlyIn(Dist.CLIENT)
public class BuildModes {
private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
private BuildModeEnum previousBuildMode = BuildModeEnum.DISABLED;
private BuildModeEnum beforeDisabledBuildMode = BuildModeEnum.SINGLE;
public void findCoordinates(BlockSet blocks, Player player) {
buildMode.instance.findCoordinates(blocks);
}
//Static variables are shared between client and server in singleplayer
//We need them separate
public static Dictionary<PlayerEntity, Boolean> currentlyBreakingClient = new Hashtable<>();
public static Dictionary<PlayerEntity, Boolean> currentlyBreakingServer = new Hashtable<>();
public BuildModeEnum getBuildMode() {
return buildMode;
}
public enum BuildModeEnum {
NORMAL("effortlessbuilding.mode.normal", new Normal()),
NORMAL_PLUS("effortlessbuilding.mode.normal_plus", new NormalPlus(), OptionEnum.BUILD_SPEED),
LINE("effortlessbuilding.mode.line", new Line() /*, OptionEnum.THICKNESS*/),
WALL("effortlessbuilding.mode.wall", new Wall(), OptionEnum.FILL),
FLOOR("effortlessbuilding.mode.floor", new Floor(), OptionEnum.FILL),
DIAGONAL_LINE("effortlessbuilding.mode.diagonal_line", new DiagonalLine() /*, OptionEnum.THICKNESS*/),
DIAGONAL_WALL("effortlessbuilding.mode.diagonal_wall", new DiagonalWall() /*, OptionEnum.FILL*/),
SLOPE_FLOOR("effortlessbuilding.mode.slope_floor", new SlopeFloor(), OptionEnum.RAISED_EDGE),
CIRCLE("effortlessbuilding.mode.circle", new Circle(), OptionEnum.CIRCLE_START, OptionEnum.FILL),
CYLINDER("effortlessbuilding.mode.cylinder", new Cylinder(), OptionEnum.CIRCLE_START, OptionEnum.FILL),
SPHERE("effortlessbuilding.mode.sphere", new Sphere(), OptionEnum.CIRCLE_START, OptionEnum.FILL),
CUBE("effortlessbuilding.mode.cube", new Cube(), OptionEnum.CUBE_FILL);
public void setBuildMode(BuildModeEnum buildMode) {
this.buildMode = buildMode;
public String name;
public IBuildMode instance;
public OptionEnum[] options;
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);
BuildModeEnum(String name, IBuildMode instance, OptionEnum... options) {
this.name = name;
this.instance = instance;
this.options = options;
}
}
public void onCancel() {
getBuildMode().instance.initialize();
//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(PlayerEntity player, BlockPlacedMessage message) {
//Check if not in the middle of breaking
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? 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.world.getBlockState(startPos).getMaterial().isReplaceable();
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos, message.getSideHit());
if (!modifierSettings.doQuickReplace() && !replaceable && !becomesDoubleSlab) {
startPos = startPos.offset(message.getSideHit());
}
//Get under tall grass and other replaceable blocks
if (modifierSettings.doQuickReplace() && replaceable) {
startPos = startPos.down();
}
//Check if player reach does not exceed startpos
int maxReach = ReachHelper.getMaxReach(player);
if (buildMode != BuildModeEnum.NORMAL && player.getPosition().distanceSq(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();
Vec3d 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);
}
//Use a network message to break blocks in the distance using clientside mouse input
public static void onBlockBrokenMessage(PlayerEntity player, BlockBrokenMessage message) {
BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null;
onBlockBroken(player, startPos, true);
}
public static void onBlockBroken(PlayerEntity player, BlockPos startPos, boolean breakStartPos) {
//Check if not in the middle of placing
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer;
if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) {
//Cancel placing
initializeMode(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, Vec3d.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(PlayerEntity 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(PlayerEntity player) {
//Resetting mode, so not placing or breaking
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer;
currentlyBreaking.remove(player);
ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player);
}
public static boolean isCurrentlyPlacing(PlayerEntity player) {
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player);
}
public static boolean isCurrentlyBreaking(PlayerEntity player) {
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null && currentlyBreaking.get(player);
}
//Either placing or breaking
public static boolean isActive(PlayerEntity player) {
Dictionary<PlayerEntity, Boolean> currentlyBreaking = player.world.isRemote ? currentlyBreakingClient : currentlyBreakingServer;
return currentlyBreaking.get(player) != null;
}
//-- Common build mode functionality --//
//Find coordinates on a line bound by a plane
public static Vec3 findXBound(double x, Vec3 start, Vec3 look) {
public static Vec3d findXBound(double x, Vec3d start, Vec3d look) {
//then y and z are
double y = (x - start.x) / look.x * look.y + start.y;
double z = (x - start.x) / look.x * look.z + start.z;
return new Vec3(x, y, z);
return new Vec3d(x, y, z);
}
public static Vec3 findYBound(double y, Vec3 start, Vec3 look) {
public static Vec3d findYBound(double y, Vec3d start, Vec3d look) {
//then x and z are
double x = (y - start.y) / look.y * look.x + start.x;
double z = (y - start.y) / look.y * look.z + start.z;
return new Vec3(x, y, z);
return new Vec3d(x, y, z);
}
public static Vec3 findZBound(double z, Vec3 start, Vec3 look) {
public static Vec3d findZBound(double z, Vec3d start, Vec3d look) {
//then x and y are
double x = (z - start.z) / look.z * look.x + start.x;
double y = (z - start.z) / look.z * look.y + start.y;
return new Vec3(x, y, z);
return new Vec3d(x, y, z);
}
//Use this instead of player.getLookVec() in any buildmodes code
public static Vec3 getPlayerLookVec(Player player) {
Vec3 lookVec = player.getLookAngle();
public static Vec3d getPlayerLookVec(PlayerEntity player){
Vec3d lookVec = player.getLookVec();
double x = lookVec.x;
double y = lookVec.y;
double z = lookVec.z;
@@ -101,22 +251,21 @@ public class BuildModes {
if (Math.abs(z - 1.0) < 0.0001) z = 0.9999;
if (Math.abs(z + 1.0) < 0.0001) z = -0.9999;
return new Vec3(x, y, z);
return new Vec3d(x, y, z);
}
public static boolean isCriteriaValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace, Vec3 lineBound, Vec3 planeBound, double distToPlayerSq) {
public static boolean isCriteriaValid(Vec3d start, Vec3d look, int reach, PlayerEntity player, boolean skipRaytrace, Vec3d lineBound, Vec3d planeBound, double distToPlayerSq) {
boolean intersects = false;
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);
intersects = rayTraceResult != null && rayTraceResult.getType() == HitResult.Type.BLOCK &&
planeBound.subtract(rayTraceResult.getLocation()).lengthSqr() > 4;
RayTraceContext rayTraceContext = new RayTraceContext(start, lineBound, RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, player);
RayTraceResult rayTraceResult = player.world.rayTraceBlocks(rayTraceContext);
intersects = rayTraceResult != null && rayTraceResult.getType() == RayTraceResult.Type.BLOCK &&
planeBound.subtract(rayTraceResult.getHitVec()).lengthSquared() > 4;
}
return planeBound.subtract(start).dot(look) > 0 &&
return planeBound.subtract(start).dotProduct(look) > 0 &&
distToPlayerSq > 2 && distToPlayerSq < reach * reach &&
!intersects;
}
}

View File

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

View File

@@ -1,20 +1,64 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.text.TextFormatting;
import nl.requios.effortlessbuilding.EffortlessBuilding;
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;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.proxy.ClientProxy;
@OnlyIn(Dist.CLIENT)
public class ModeOptions {
public enum ActionEnum {
UNDO("effortlessbuilding.action.undo"),
REDO("effortlessbuilding.action.redo"),
REPLACE("effortlessbuilding.action.replace"),
OPEN_MODIFIER_SETTINGS("effortlessbuilding.action.open_modifier_settings"),
NORMAL_SPEED("effortlessbuilding.action.normal_speed"),
FAST_SPEED("effortlessbuilding.action.fast_speed"),
FULL("effortlessbuilding.action.full"),
HOLLOW("effortlessbuilding.action.hollow"),
CUBE_FULL("effortlessbuilding.action.full"),
CUBE_HOLLOW("effortlessbuilding.action.hollow"),
CUBE_SKELETON("effortlessbuilding.action.skeleton"),
SHORT_EDGE("effortlessbuilding.action.short_edge"),
LONG_EDGE("effortlessbuilding.action.long_edge"),
THICKNESS_1("effortlessbuilding.action.thickness_1"),
THICKNESS_3("effortlessbuilding.action.thickness_3"),
THICKNESS_5("effortlessbuilding.action.thickness_5"),
CIRCLE_START_CORNER("effortlessbuilding.action.start_corner"),
CIRCLE_START_CENTER("effortlessbuilding.action.start_center");
public String name;
ActionEnum(String name) {
this.name = name;
}
}
public enum OptionEnum {
BUILD_SPEED("effortlessbuilding.action.build_speed", ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED),
FILL("effortlessbuilding.action.filling", ActionEnum.FULL, ActionEnum.HOLLOW),
CUBE_FILL("effortlessbuilding.action.filling", ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON),
RAISED_EDGE("effortlessbuilding.action.raised_edge", ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE),
LINE_THICKNESS("effortlessbuilding.action.thickness", ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5),
CIRCLE_START("effortlessbuilding.action.circle_start", ActionEnum.CIRCLE_START_CORNER, ActionEnum.CIRCLE_START_CENTER);
public String name;
public ActionEnum[] actions;
OptionEnum(String name, ActionEnum... actions){
this.name = name;
this.actions = actions;
}
}
private static ActionEnum buildSpeed = ActionEnum.NORMAL_SPEED;
private static ActionEnum fill = ActionEnum.FULL;
private static ActionEnum cubeFill = ActionEnum.CUBE_FULL;
@@ -65,123 +109,74 @@ public class ModeOptions {
return circleStart;
}
public static void performAction(Player player, ActionEnum action) {
//Called on both client and server
public static void performAction(PlayerEntity player, ActionEnum action) {
if (action == null) return;
switch (action) {
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 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 " + TextFormatting.GOLD + "Quick Replace " + TextFormatting.RESET + (
modifierSettings.doQuickReplace() ? "on" : "off"), true);
break;
case OPEN_MODIFIER_SETTINGS:
if (player.world.isRemote)
ClientProxy.openModifierSettings();
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;
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;
}
if (player.level().isClientSide &&
action != ActionEnum.OPEN_MODIFIER_SETTINGS &&
action != ActionEnum.OPEN_PLAYER_SETTINGS &&
action != ActionEnum.PREVIOUS_BUILD_MODE &&
action != ActionEnum.DISABLE_BUILD_MODE_TOGGLE) {
EffortlessBuilding.logTranslate(player, "", action.getNameKey(), "", true);
}
}
public enum ActionEnum {
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),
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),
NORMAL_SPEED("normal_speed", AllIcons.I_NORMAL_SPEED),
FAST_SPEED("fast_speed", AllIcons.I_FAST_SPEED),
FULL("full", AllIcons.I_FILLED),
HOLLOW("hollow", AllIcons.I_HOLLOW),
CUBE_FULL("full", AllIcons.I_CUBE_FILLED),
CUBE_HOLLOW("hollow", AllIcons.I_CUBE_HOLLOW),
CUBE_SKELETON("skeleton", AllIcons.I_CUBE_SKELETON),
SHORT_EDGE("short_edge", AllIcons.I_SHORT_EDGE),
LONG_EDGE("long_edge", AllIcons.I_LONG_EDGE),
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, 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";
}
}
public enum OptionEnum {
BUILD_SPEED("effortlessbuilding.action.build_speed", ActionEnum.NORMAL_SPEED, ActionEnum.FAST_SPEED),
FILL("effortlessbuilding.action.filling", ActionEnum.FULL, ActionEnum.HOLLOW),
CUBE_FILL("effortlessbuilding.action.filling", ActionEnum.CUBE_FULL, ActionEnum.CUBE_HOLLOW, ActionEnum.CUBE_SKELETON),
RAISED_EDGE("effortlessbuilding.action.raised_edge", ActionEnum.SHORT_EDGE, ActionEnum.LONG_EDGE),
LINE_THICKNESS("effortlessbuilding.action.thickness", ActionEnum.THICKNESS_1, ActionEnum.THICKNESS_3, ActionEnum.THICKNESS_5),
CIRCLE_START("effortlessbuilding.action.circle_start", ActionEnum.CIRCLE_START_CORNER, ActionEnum.CIRCLE_START_CENTER);
public String name;
public ActionEnum[] actions;
OptionEnum(String name, ActionEnum... actions) {
this.name = name;
this.actions = actions;
if (player.world.isRemote && action != ActionEnum.REPLACE && action != ActionEnum.OPEN_MODIFIER_SETTINGS) {
EffortlessBuilding.logTranslate(player, "", action.name, "", true);
}
}
}

View File

@@ -0,0 +1,98 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.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 modifierCapability capability
//Never returns null
@Nonnull
public static ModeSettings getModeSettings(PlayerEntity player) {
LazyOptional<ModeCapabilityManager.IModeCapability> modeCapability =
player.getCapability(ModeCapabilityManager.modeCapability, null);
if (modeCapability.isPresent()) {
ModeCapabilityManager.IModeCapability capability = modeCapability.orElse(null);
if (capability.getModeData() == null) {
capability.setModeData(new ModeSettings());
}
return capability.getModeData();
}
//Player does not have modeCapability capability
//Return dummy settings
return new ModeSettings();
// throw new IllegalArgumentException("Player does not have modeCapability capability");
}
public static void setModeSettings(PlayerEntity player, ModeSettings modeSettings) {
if (player == null) {
EffortlessBuilding.log("Cannot set buildmode settings, player is null");
return;
}
LazyOptional<ModeCapabilityManager.IModeCapability> modeCapability =
player.getCapability(ModeCapabilityManager.modeCapability, 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, PlayerEntity player) {
int maxReach = ReachHelper.getMaxReach(player);
String error = "";
//TODO sanitize
return error;
}
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;
}
}
public static void handleNewPlayer(PlayerEntity player){
//Makes sure player has mode settings (if it doesnt it will create it)
getModeSettings(player);
//Only on server
if (!player.world.isRemote) {
//Send to client
ModeSettingsMessage msg = new ModeSettingsMessage(getModeSettings(player));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), msg);
}
}
}

View File

@@ -1,71 +1,108 @@
package nl.requios.effortlessbuilding.buildmode;
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.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public abstract class ThreeClicksBuildMode extends BaseBuildMode {
protected BlockEntry firstBlockEntry;
protected BlockEntry secondBlockEntry;
protected Dictionary<UUID, BlockPos> secondPosTable = new Hashtable<>();
//Finds height after floor has been chosen in buildmodes with 3 clicks
@Override
public void initialize() {
super.initialize();
firstBlockEntry = null;
secondBlockEntry = null;
static class HeightCriteria {
Vec3d planeBound;
Vec3d lineBound;
double distToLineSq;
double distToPlayerSq;
HeightCriteria(Vec3d planeBound, BlockPos secondPos, Vec3d start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, secondPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSquared();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared();
}
//Make it from a plane into a line, on y axis only
private Vec3d toLongestLine(Vec3d boundVec, BlockPos secondPos) {
BlockPos bound = new BlockPos(boundVec);
return new Vec3d(secondPos.getX(), bound.getY(), secondPos.getZ());
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3d start, Vec3d look, int reach, PlayerEntity player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
}
@Override
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
public void initialize(PlayerEntity player) {
super.initialize(player);
secondPosTable.put(player.getUniqueID(), BlockPos.ZERO);
}
if (clicks == 1) {
//First click, remember starting position
firstBlockEntry = EffortlessBuildingClient.BUILDER_CHAIN.getStartPos();
@Override
public List<BlockPos> onRightClick(PlayerEntity player, BlockPos blockPos, Direction sideHit, Vec3d hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUniqueID());
rightClickNr++;
rightClickTable.put(player.getUniqueID(), rightClickNr);
if (rightClickNr == 1) {
//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;
if (blockPos == null) {
rightClickTable.put(player.getUniqueID(), 0);
return list;
}
var player = Minecraft.getInstance().player;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
secondBlockEntry = new BlockEntry(secondPos);
//First click, remember starting position
firstPosTable.put(player.getUniqueID(), blockPos);
sideHitTable.put(player.getUniqueID(), sideHit);
hitVecTable.put(player.getUniqueID(), hitVec);
//Keep list empty, dont place any blocks yet
} else if (rightClickNr == 2) {
//Second click, find other floor point
BlockPos firstPos = firstPosTable.get(player.getUniqueID());
BlockPos secondPos = findSecondPos(player, firstPos, true);
if (secondPos == null) {
rightClickTable.put(player.getUniqueID(), 1);
return list;
}
secondPosTable.put(player.getUniqueID(), secondPos);
} else {
//Third click, place blocks
clicks = 0;
return true;
//Third click, place diagonal wall with height
list = findCoordinates(player, blockPos, skipRaytrace);
rightClickTable.put(player.getUniqueID(), 0);
}
return false;
return list;
}
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
public List<BlockPos> findCoordinates(PlayerEntity player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUniqueID());
if (clicks == 1) {
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
if (rightClickNr == 0) {
if (blockPos != null)
list.add(blockPos);
} else if (rightClickNr == 1) {
BlockPos firstPos = firstPosTable.get(player.getUniqueID());
//Limit amount of blocks we can place per row
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
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();
@@ -79,22 +116,18 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
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;
//Add diagonal line from first to second
list.addAll(getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2));
} 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;
BlockPos firstPos = firstPosTable.get(player.getUniqueID());
BlockPos secondPos = secondPosTable.get(player.getUniqueID());
BlockPos thirdPos = findThirdPos(player, firstPos, secondPos, skipRaytrace);
if (thirdPos == null) return list;
//Limit amount of blocks you can place per row
int axisLimit = AttachmentHandler.getMaxBlocksPerAxis(player, false);
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();
@@ -115,32 +148,42 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
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;
}
//Add diagonal line from first to third
list.addAll(getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3));
}
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());
return list;
}
//Finds the place of the second block pos
protected abstract BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace);
//Finds the place of the third block pos
protected abstract BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace);
//After first and second pos are known, we want to visualize the blocks in a way (like floor for cube)
protected abstract List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2);
//After first, second and third pos are known, we want all the blocks
protected abstract List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3);
//Finds height after floor has been chosen in buildmodes with 3 clicks
public static BlockPos findHeight(PlayerEntity player, BlockPos secondPos, boolean skipRaytrace) {
Vec3d look = BuildModes.getPlayerLookVec(player);
Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
List<HeightCriteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(secondPos.getX(), start, look);
Vec3d xBound = BuildModes.findXBound(secondPos.getX(), start, look);
criteriaList.add(new HeightCriteria(xBound, secondPos, start));
//Z
Vec3 zBound = BuildModes.findZBound(secondPos.getZ(), start, look);
Vec3d zBound = BuildModes.findZBound(secondPos.getZ(), start, look);
criteriaList.add(new HeightCriteria(zBound, secondPos, start));
//Remove invalid criteria
int reach = AttachmentHandler.getBuildModeReach(player);
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -165,48 +208,6 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
}
}
}
return BlockPos.containing(selected.lineBound);
}
// 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
protected abstract BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace);
//After first and second pos are known, we want to visualize the blocks in a way (like floor for cube)
protected abstract List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2);
//After first, second and third pos are known, we want all the blocks
protected abstract List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3);
static class HeightCriteria {
Vec3 planeBound;
Vec3 lineBound;
double distToLineSq;
double distToPlayerSq;
HeightCriteria(Vec3 planeBound, BlockPos secondPos, Vec3 start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, secondPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSqr();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//Make it from a plane into a line, on y axis only
private Vec3 toLongestLine(Vec3 boundVec, BlockPos secondPos) {
BlockPos bound = BlockPos.containing(boundVec);
return new Vec3(secondPos.getX(), bound.getY(), secondPos.getZ());
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
return new BlockPos(selected.lineBound);
}
}

View File

@@ -1,55 +1,65 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.helper.ReachHelper;
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 void initialize() {
super.initialize();
firstBlockEntry = null;
public List<BlockPos> onRightClick(PlayerEntity player, BlockPos blockPos, Direction sideHit, Vec3d hitVec, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUniqueID());
rightClickNr++;
rightClickTable.put(player.getUniqueID(), rightClickNr);
if (rightClickNr == 1) {
//If clicking in air, reset and try again
if (blockPos == null) {
rightClickTable.put(player.getUniqueID(), 0);
return list;
}
@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;
firstPosTable.put(player.getUniqueID(), blockPos);
sideHitTable.put(player.getUniqueID(), sideHit);
hitVecTable.put(player.getUniqueID(), hitVec);
//Keep list empty, dont place any blocks yet
} else {
//Second click, place blocks
clicks = 0;
return true;
list = findCoordinates(player, blockPos, skipRaytrace);
rightClickTable.put(player.getUniqueID(), 0);
}
return false;
return list;
}
@Override
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
public List<BlockPos> findCoordinates(PlayerEntity player, BlockPos blockPos, boolean skipRaytrace) {
List<BlockPos> list = new ArrayList<>();
Dictionary<UUID, Integer> rightClickTable = player.world.isRemote ? rightClickClientTable : rightClickServerTable;
int rightClickNr = rightClickTable.get(player.getUniqueID());
BlockPos firstPos = firstPosTable.get(player.getUniqueID());
var player = Minecraft.getInstance().player;
var firstPos = firstBlockEntry.blockPos;
var secondPos = findSecondPos(player, firstBlockEntry.blockPos, true);
if (secondPos == null) return;
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 = AttachmentHandler.getMaxBlocksPerAxis(player, false);
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
int x1 = firstPos.getX(), x2 = secondPos.getX();
int y1 = firstPos.getY(), y2 = secondPos.getY();
@@ -63,18 +73,15 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
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));
list.addAll(getAllBlocks(player, x1, y1, z1, x2, y2, z2));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
return list;
}
//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)
protected abstract BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace);
protected abstract BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace);
//After first and second pos are known, we want all the blocks
protected abstract List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2);
protected abstract List<BlockPos> getAllBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2);
}

View File

@@ -1,17 +1,26 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Circle extends TwoClicksBuildMode {
public static List<BlockPos> getCircleBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
public static List<BlockPos> getCircleBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
float centerX = x1;
@@ -26,8 +35,8 @@ public class Circle extends TwoClicksBuildMode {
z1 = (int) (centerZ - (z2 - centerZ));
}
float radiusX = Mth.abs(x2 - centerX);
float radiusZ = Mth.abs(z2 - centerZ);
float radiusX = MathHelper.abs(x2 - centerX);
float radiusZ = MathHelper.abs(z2 - centerZ);
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addCircleBlocks(list, x1, y1, z1, x2, y2, z2, centerX, centerZ, radiusX, radiusZ);
@@ -66,24 +75,14 @@ public class Circle extends TwoClicksBuildMode {
}
private static float distance(float x1, float z1, float x2, float z2) {
return Mth.sqrt((x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1));
return MathHelper.sqrt((x2 - x1) * (x2 - x1) + (z2 - z1) * (z2 - z1));
}
public static float calculateEllipseRadius(float centerX, float centerZ, float radiusX, float radiusZ, int x, int z) {
//https://math.stackexchange.com/questions/432902/how-to-get-the-radius-of-an-ellipse-at-a-specific-angle-by-knowing-its-semi-majo
float theta = (float) Mth.atan2(z - centerZ, x - centerX);
float part1 = radiusX * radiusX * Mth.sin(theta) * Mth.sin(theta);
float part2 = radiusZ * radiusZ * Mth.cos(theta) * Mth.cos(theta);
return radiusX * radiusZ / Mth.sqrt(part1 + part2);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
float theta = (float) MathHelper.atan2(z - centerZ, x - centerX);
float part1 = radiusX * radiusX * MathHelper.sin(theta) * MathHelper.sin(theta);
float part2 = radiusZ * radiusZ * MathHelper.cos(theta) * MathHelper.cos(theta);
return radiusX * radiusZ / MathHelper.sqrt(part1 + part2);
}
}

View File

@@ -1,31 +0,0 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Cone extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,16 +1,35 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Cube extends ThreeClicksBuildMode {
public static List<BlockPos> getFloorBlocksUsingCubeFill(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocksUsingCubeFill(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCubeBlocks(player, x1, y1, z1, x3, y3, z3);
}
public static List<BlockPos> getFloorBlocksUsingCubeFill(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (ModeOptions.getCubeFill() == ModeOptions.ActionEnum.CUBE_SKELETON)
@@ -21,7 +40,7 @@ public class Cube extends ThreeClicksBuildMode {
return list;
}
public static List<BlockPos> getCubeBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
public static List<BlockPos> getCubeBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
switch (ModeOptions.getCubeFill()) {
@@ -78,24 +97,4 @@ public class Cube extends ThreeClicksBuildMode {
Line.addZLineBlocks(list, z1, z2, x2, y1);
Line.addZLineBlocks(list, z1, z2, x2, y2);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocksUsingCubeFill(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCubeBlocks(player, x1, y1, z1, x3, y3, z3);
}
}

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
@@ -9,7 +9,27 @@ import java.util.List;
public class Cylinder extends ThreeClicksBuildMode {
public static List<BlockPos> getCylinderBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
@Override
public BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCylinderBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
public static List<BlockPos> getCylinderBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
//Get circle blocks (using CIRCLE_START and FILL options built-in)
@@ -27,24 +47,4 @@ public class Cylinder extends ThreeClicksBuildMode {
return list;
}
@Override
public BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getCylinderBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,8 +1,8 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
@@ -10,17 +10,39 @@ import java.util.List;
public class DiagonalLine extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
//Add diagonal line from first to second
public static List<BlockPos> getDiagonalLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, float sampleMultiplier) {
return getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 10);
}
@Override
protected List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//Add diagonal line from first to third
return getDiagonalLineBlocks(player, x1, y1, z1, x3, y3, z3, 10);
}
//Add diagonal line from first to second
public static List<BlockPos> getDiagonalLineBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, float sampleMultiplier) {
List<BlockPos> list = new ArrayList<>();
Vec3 first = new Vec3(x1, y1, z1).add(0.5, 0.5, 0.5);
Vec3 second = new Vec3(x2, y2, z2).add(0.5, 0.5, 0.5);
Vec3d first = new Vec3d(x1, y1, z1).add(0.5, 0.5, 0.5);
Vec3d second = new Vec3d(x2, y2, z2).add(0.5, 0.5, 0.5);
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 = BlockPos.containing(lerp);
Vec3d lerp = first.add(second.subtract(first).scale(t));
BlockPos candidate = new BlockPos(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);
@@ -28,26 +50,4 @@ public class DiagonalLine extends ThreeClicksBuildMode {
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
//Add diagonal line from first to second
return getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 10);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//Add diagonal line from first to third
return getDiagonalLineBlocks(player, x1, y1, z1, x3, y3, z3, 10);
}
}

View File

@@ -1,7 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.ArrayList;
@@ -9,8 +9,28 @@ import java.util.List;
public class DiagonalWall extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1);
}
@Override
protected List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getDiagonalWallBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
//Add diagonal wall from first to second
public static List<BlockPos> getDiagonalWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
public static List<BlockPos> getDiagonalWallBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
//Get diagonal line blocks
@@ -28,24 +48,4 @@ public class DiagonalWall extends ThreeClicksBuildMode {
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return DiagonalLine.getDiagonalLineBlocks(player, x1, y1, z1, x2, y2, z2, 1);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getDiagonalWallBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,22 +0,0 @@
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,31 +0,0 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Dome extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,30 +1,51 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Floor extends TwoClicksBuildMode {
public static BlockPos findFloor(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
static class Criteria {
Vec3d planeBound;
double distToPlayerSq;
Criteria(Vec3d planeBound, Vec3d start) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared();
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3d start, Vec3d look, int reach, PlayerEntity player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return findFloor(player, firstPos, skipRaytrace);
}
public static BlockPos findFloor(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
Vec3d look = BuildModes.getPlayerLookVec(player);
Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
List<Criteria> criteriaList = new ArrayList<>(3);
//Y
Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look);
Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look);
criteriaList.add(new Criteria(yBound, start));
//Remove invalid criteria
int reach = AttachmentHandler.getBuildModeReach(player);
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -33,10 +54,15 @@ public class Floor extends TwoClicksBuildMode {
//Then only 1 can be valid, return that one
Criteria selected = criteriaList.get(0);
return BlockPos.containing(selected.planeBound);
return new BlockPos(selected.planeBound);
}
public static List<BlockPos> getFloorBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@Override
protected List<BlockPos> getAllBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
public static List<BlockPos> getFloorBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
@@ -64,31 +90,4 @@ public class Floor extends TwoClicksBuildMode {
Line.addZLineBlocks(list, z1, z2, x1, y);
Line.addZLineBlocks(list, z1, z2, x2, y);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findFloor(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
double distToPlayerSq;
Criteria(Vec3 planeBound, Vec3 start) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -1,37 +1,85 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.EffortlessBuilding;
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;
public class Line extends TwoClicksBuildMode {
public static BlockPos findLine(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
static class Criteria {
Vec3d planeBound;
Vec3d lineBound;
double distToLineSq;
double distToPlayerSq;
Criteria(Vec3d planeBound, BlockPos firstPos, Vec3d start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, firstPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSquared();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared();
}
//Make it from a plane into a line
//Select the axis that is longest
private Vec3d toLongestLine(Vec3d boundVec, BlockPos firstPos) {
BlockPos bound = new BlockPos(boundVec);
BlockPos firstToSecond = bound.subtract(firstPos);
firstToSecond = new BlockPos(Math.abs(firstToSecond.getX()), Math.abs(firstToSecond.getY()), Math.abs(firstToSecond.getZ()));
int longest = Math.max(firstToSecond.getX(), Math.max(firstToSecond.getY(), firstToSecond.getZ()));
if (longest == firstToSecond.getX()) {
return new Vec3d(bound.getX(), firstPos.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getY()) {
return new Vec3d(firstPos.getX(), bound.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getZ()) {
return new Vec3d(firstPos.getX(), firstPos.getY(), bound.getZ());
}
return null;
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3d start, Vec3d look, int reach, PlayerEntity player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
}
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return findLine(player, firstPos, skipRaytrace);
}
public static BlockPos findLine(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
Vec3d look = BuildModes.getPlayerLookVec(player);
Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
List<Criteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look);
Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look);
criteriaList.add(new Criteria(xBound, firstPos, start));
//Y
Vec3 yBound = BuildModes.findYBound(firstPos.getY(), start, look);
Vec3d yBound = BuildModes.findYBound(firstPos.getY(), start, look);
criteriaList.add(new Criteria(yBound, firstPos, start));
//Z
Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
criteriaList.add(new Criteria(zBound, firstPos, start));
//Remove invalid criteria
int reach = AttachmentHandler.getBuildModeReach(player);
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -58,10 +106,15 @@ public class Line extends TwoClicksBuildMode {
}
return BlockPos.containing(selected.lineBound);
return new BlockPos(selected.lineBound);
}
public static List<BlockPos> getLineBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@Override
protected List<BlockPos> getAllBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getLineBlocks(player, x1, y1, z1, x2, y2, z2);
}
public static List<BlockPos> getLineBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (x1 != x2) {
@@ -89,59 +142,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(BlockPos.containing(x, y, z));
list.add(new BlockPos(x, y, z));
}
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findLine(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getLineBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
Vec3 lineBound;
double distToLineSq;
double distToPlayerSq;
Criteria(Vec3 planeBound, BlockPos firstPos, Vec3 start) {
this.planeBound = planeBound;
this.lineBound = toLongestLine(this.planeBound, firstPos);
this.distToLineSq = this.lineBound.subtract(this.planeBound).lengthSqr();
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
}
//Make it from a plane into a line
//Select the axis that is longest
private Vec3 toLongestLine(Vec3 boundVec, BlockPos firstPos) {
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()));
int longest = Math.max(firstToSecond.getX(), Math.max(firstToSecond.getY(), firstToSecond.getZ()));
if (longest == firstToSecond.getX()) {
return new Vec3(bound.getX(), firstPos.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getY()) {
return new Vec3(firstPos.getX(), bound.getY(), firstPos.getZ());
}
if (longest == firstToSecond.getZ()) {
return new Vec3(firstPos.getX(), firstPos.getY(), bound.getZ());
}
return null;
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, lineBound, planeBound, distToPlayerSq);
}
}
}

View File

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

View File

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

View File

@@ -1,31 +0,0 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
import java.util.List;
public class Pyramid extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
//TODO
return SlopeFloor.getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,22 +0,0 @@
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,21 +1,41 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
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 SlopeFloor extends ThreeClicksBuildMode {
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
//Add slope floor from first to second
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) {
public static List<BlockPos> getSlopeFloorBlocks(PlayerEntity 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 = AttachmentHandler.getMaxBlocksPerAxis(player, false);
int axisLimit = ReachHelper.getMaxBlocksPerAxis(player);
//Determine whether to use x or z axis to slope up
boolean onXAxis = true;
@@ -72,24 +92,4 @@ public class SlopeFloor extends ThreeClicksBuildMode {
return list;
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
protected BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
protected List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Floor.getFloorBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
protected List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSlopeFloorBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,8 +1,8 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.player.Player;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.ThreeClicksBuildMode;
@@ -11,7 +11,27 @@ import java.util.List;
public class Sphere extends ThreeClicksBuildMode {
public static List<BlockPos> getSphereBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
@Override
public BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(PlayerEntity player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSphereBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
public static List<BlockPos> getSphereBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
List<BlockPos> list = new ArrayList<>();
float centerX = x1;
@@ -29,9 +49,9 @@ public class Sphere extends ThreeClicksBuildMode {
z1 = (int) (centerZ - (z2 - centerZ));
}
float radiusX = Mth.abs(x2 - centerX);
float radiusY = Mth.abs(y3 - centerY);
float radiusZ = Mth.abs(z2 - centerZ);
float radiusX = MathHelper.abs(x2 - centerX);
float radiusY = MathHelper.abs(y3 - centerY);
float radiusZ = MathHelper.abs(z2 - centerZ);
if (ModeOptions.getFill() == ModeOptions.ActionEnum.FULL)
addSphereBlocks(list, x1, y1, z1, x3, y3, z3, centerX, centerY, centerZ, radiusX, radiusY, radiusZ);
@@ -76,7 +96,7 @@ public class Sphere extends ThreeClicksBuildMode {
}
private static float distance(float x1, float y1, float z1, float x2, float y2, float z2) {
return Mth.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
return MathHelper.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
}
public static float calculateSpheroidRadius(float centerX, float centerY, float centerZ, float radiusX, float radiusY, float radiusZ, int x, int y, int z) {
@@ -87,24 +107,4 @@ public class Sphere extends ThreeClicksBuildMode {
return Circle.calculateEllipseRadius(centerX, centerY, radiusXZ, radiusY, x, y);
}
@Override
public BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return Floor.findFloor(player, firstPos, skipRaytrace);
}
@Override
public BlockPos findThirdPos(Player player, BlockPos firstPos, BlockPos secondPos, boolean skipRaytrace) {
return findHeight(player, secondPos, skipRaytrace);
}
@Override
public List<BlockPos> getIntermediateBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return Circle.getCircleBlocks(player, x1, y1, z1, x2, y2, z2);
}
@Override
public List<BlockPos> getFinalBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
return getSphereBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3);
}
}

View File

@@ -1,34 +1,58 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import nl.requios.effortlessbuilding.buildmode.BuildModes;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmode.TwoClicksBuildMode;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.helper.ReachHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Wall extends TwoClicksBuildMode {
public static BlockPos findWall(Player player, BlockPos firstPos, boolean skipRaytrace) {
Vec3 look = BuildModes.getPlayerLookVec(player);
Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ());
static class Criteria {
Vec3d planeBound;
double distToPlayerSq;
double angle;
Criteria(Vec3d planeBound, BlockPos firstPos, Vec3d start, Vec3d look) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSquared();
Vec3d wall = this.planeBound.subtract(new Vec3d(firstPos));
this.angle = wall.x * look.x + wall.z * look.z; //dot product ignoring y (looking up/down should not affect this angle)
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3d start, Vec3d look, int reach, PlayerEntity player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
@Override
protected BlockPos findSecondPos(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
return findWall(player, firstPos, skipRaytrace);
}
public static BlockPos findWall(PlayerEntity player, BlockPos firstPos, boolean skipRaytrace) {
Vec3d look = BuildModes.getPlayerLookVec(player);
Vec3d start = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
List<Criteria> criteriaList = new ArrayList<>(3);
//X
Vec3 xBound = BuildModes.findXBound(firstPos.getX(), start, look);
Vec3d xBound = BuildModes.findXBound(firstPos.getX(), start, look);
criteriaList.add(new Criteria(xBound, firstPos, start, look));
//Z
Vec3 zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
Vec3d zBound = BuildModes.findZBound(firstPos.getZ(), start, look);
criteriaList.add(new Criteria(zBound, firstPos, start, look));
//Remove invalid criteria
int reach = AttachmentHandler.getBuildModeReach(player);
int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach
criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace));
//If none are valid, return empty list of blocks
@@ -48,10 +72,15 @@ public class Wall extends TwoClicksBuildMode {
}
}
return BlockPos.containing(selected.planeBound);
return new BlockPos(selected.planeBound);
}
public static List<BlockPos> getWallBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
@Override
protected List<BlockPos> getAllBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getWallBlocks(player, x1, y1, z1, x2, y2, z2);
}
public static List<BlockPos> getWallBlocks(PlayerEntity player, int x1, int y1, int z1, int x2, int y2, int z2) {
List<BlockPos> list = new ArrayList<>();
if (x1 == x2) {
@@ -102,34 +131,4 @@ public class Wall extends TwoClicksBuildMode {
Line.addYLineBlocks(list, y1, y2, x1, z);
Line.addYLineBlocks(list, y1, y2, x2, z);
}
@Override
protected BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace) {
return findWall(player, firstPos, skipRaytrace);
}
@Override
protected List<BlockPos> getAllBlocks(Player player, int x1, int y1, int z1, int x2, int y2, int z2) {
return getWallBlocks(player, x1, y1, z1, x2, y2, z2);
}
static class Criteria {
Vec3 planeBound;
double distToPlayerSq;
double angle;
Criteria(Vec3 planeBound, BlockPos firstPos, Vec3 start, Vec3 look) {
this.planeBound = planeBound;
this.distToPlayerSq = this.planeBound.subtract(start).lengthSqr();
Vec3 wall = this.planeBound.subtract(Vec3.atLowerCornerOf(firstPos));
this.angle = wall.x * look.x + wall.z * look.z; //dot product ignoring y (looking up/down should not affect this angle)
}
//check if its not behind the player and its not too close and not too far
//also check if raytrace from player to block does not intersect blocks
public boolean isValid(Vec3 start, Vec3 look, int reach, Player player, boolean skipRaytrace) {
return BuildModes.isCriteriaValid(start, look, reach, player, skipRaytrace, planeBound, planeBound, distToPlayerSq);
}
}
}

View File

@@ -1,38 +1,33 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
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 net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
public class Array extends BaseModifier {
import java.util.ArrayList;
import java.util.List;
public Vec3i offset = BlockPos.ZERO;
public class Array {
public static class ArraySettings{
public boolean enabled = false;
public BlockPos offset = BlockPos.ZERO;
public int count = 5;
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled || offset.getX() == 0 && offset.getY() == 0 && offset.getZ() == 0) return;
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;
var newBlockEntry = new BlockEntry(pos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
blocks.add(newBlockEntry);
}
}
public ArraySettings() {
}
@Override
public void onPowerLevelChanged(int powerLevel) {
public ArraySettings(boolean enabled, BlockPos offset, int count) {
this.enabled = enabled;
this.offset = offset;
this.count = count;
}
public int getReach() {
@@ -44,20 +39,66 @@ public class Array extends BaseModifier {
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");
public static List<BlockPos> findCoordinates(PlayerEntity player, BlockPos startPos) {
List<BlockPos> coordinates = new ArrayList<>();
//find arraysettings for the player
ArraySettings a = ModifierSettingsManager.getModifierSettings(player).getArraySettings();
if (!isEnabled(a)) return coordinates;
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.add(offset);
coordinates.add(pos);
}
return coordinates;
}
public static List<BlockState> findBlockStates(PlayerEntity 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
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) {
bagInventory = ItemRandomizerBag.getBagInventory(itemStack);
}
for (int i = 0; i < a.count; i++) {
pos = pos.add(offset);
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ItemRandomizerBag.pickRandomStack(bagInventory);
blockState = BuildModifiers
.getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3d(0, 0, 0), Hand.MAIN_HAND);
}
//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;
if (a.offset.getX() == 0 && a.offset.getY() == 0 && a.offset.getZ() == 0) return false;
return true;
}
}

View File

@@ -1,24 +0,0 @@
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

@@ -0,0 +1,50 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import java.util.List;
public class BlockSet {
private List<BlockPos> coordinates;
private List<BlockState> previousBlockStates;
private List<BlockState> newBlockStates;
private Vec3d hitVec;
private BlockPos firstPos;
private BlockPos secondPos;
public BlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates, Vec3d 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 Vec3d getHitVec() {
return hitVec;
}
public BlockPos getFirstPos() {
return firstPos;
}
public BlockPos getSecondPos() {
return secondPos;
}
}

View File

@@ -1,160 +1,260 @@
package nl.requios.effortlessbuilding.buildmodifier;
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.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 net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.helper.InventoryHelper;
import nl.requios.effortlessbuilding.helper.SurvivalHelper;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
import nl.requios.effortlessbuilding.render.BlockPreviewRenderer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
@OnlyIn(Dist.CLIENT)
public class BuildModifiers {
private List<BaseModifier> modifierSettingsList = new ArrayList<>();
public List<BaseModifier> getModifierSettingsList() {
return Collections.unmodifiableList(modifierSettingsList);
//Called from BuildModes
public static void onBlockPlaced(PlayerEntity player, List<BlockPos> startCoordinates, Direction sideHit, Vec3d hitVec, boolean placeStartPos) {
World world = player.world;
ItemRandomizerBag.renewRandomness();
//Format hitvec to 0.x
hitVec = new Vec3d(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z)));
//find coordinates and blockstates
List<BlockPos> coordinates = findCoordinates(player, startCoordinates);
List<ItemStack> itemStacks = new ArrayList<>();
List<BlockState> blockStates = findBlockStates(player, startCoordinates, hitVec, sideHit, itemStacks);
//check if valid blockstates
if (blockStates.size() == 0 || coordinates.size() != blockStates.size()) 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 addModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.add(modifierSettings);
if (world.isRemote) {
BlockPreviewRenderer.onBlocksPlaced();
newBlockStates = blockStates;
} else {
//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);
if (world.isBlockPresent(blockPos)) {
//check itemstack empty
if (itemStack.isEmpty()) {
//try to find new stack, otherwise continue
itemStack = InventoryHelper.findItemStackInInventory(player, blockState.getBlock());
if (itemStack.isEmpty()) continue;
}
public void removeModifierSettings(BaseModifier modifierSettings) {
modifierSettingsList.remove(modifierSettings);
}
public void removeModifierSettings(int index) {
modifierSettingsList.remove(index);
}
public void moveUp(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
Collections.swap(modifierSettingsList, index, index - 1);
}
public void moveDown(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
Collections.swap(modifierSettingsList, index, index + 1);
}
public void setFirst(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == 0) return;
modifierSettingsList.remove(index);
modifierSettingsList.add(0, modifierSettings);
}
public void setLast(BaseModifier modifierSettings) {
int index = modifierSettingsList.indexOf(modifierSettings);
if (index == modifierSettingsList.size() - 1) return;
modifierSettingsList.remove(index);
modifierSettingsList.add(modifierSettings);
}
public void clearAllModifierSettings() {
modifierSettingsList.clear();
}
public void findCoordinates(BlockSet blocks, Player player) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.findCoordinates(blocks, player);
SurvivalHelper.placeBlock(world, player, blockPos, blockState, itemStack, Direction.UP, hitVec, false, false, false);
}
}
public void onPowerLevelChanged(int powerLevel) {
for (BaseModifier modifierSettings : modifierSettingsList) {
modifierSettings.onPowerLevelChanged(powerLevel);
//find actual new blockstates for undo
for (BlockPos coordinate : coordinates) {
newBlockStates.add(world.getBlockState(coordinate));
}
}
public CompoundTag serializeNBT() {
var compoundTag = new CompoundTag();
compoundTag.put("modifierSettingsList", NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT));
return compoundTag;
}
//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.getDefaultState());
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 void save() {
PacketDistributor.sendToServer(new ModifierSettingsPacket(serializeNBT()));
//Save locally as well?
// var listTag = NBTHelper.writeCompoundList(modifierSettingsList, BaseModifier::serializeNBT);
// player.getPersistentData().put(DATA_KEY, listTag);
}
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);
//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.getDefaultState()) != 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 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;
public static void onBlockBroken(PlayerEntity player, List<BlockPos> startCoordinates, boolean breakStartPos) {
World world = player.world;
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));
}
if (world.isRemote) {
BlockPreviewRenderer.onBlocksBroken();
//list of air blockstates
for (int i = 0; i < coordinates.size(); i++) {
newBlockStates.add(Blocks.AIR.getDefaultState());
}
} 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)).getBlockHardness(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.isBlockPresent(coordinate) && !world.isAirBlock(coordinate)) {
if (!onlyInstaBreaking || world.getBlockState(coordinate).getBlockHardness(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.getDefaultState());
//add to undo stack
BlockPos firstPos = startCoordinates.get(0);
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
Vec3d hitVec = new Vec3d(0.5, 0.5, 0.5);
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, hitVec, firstPos, secondPos));
}
public static List<BlockPos> findCoordinates(PlayerEntity 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 static List<BlockPos> findCoordinates(PlayerEntity player, BlockPos blockPos) {
return findCoordinates(player, new ArrayList<>(Arrays.asList(blockPos)));
}
public static List<BlockState> findBlockStates(PlayerEntity player, List<BlockPos> posList, Vec3d hitVec, Direction facing, List<ItemStack> itemStacks) {
List<BlockState> blockStates = new ArrayList<>();
itemStacks.clear();
//Get itemstack
ItemStack itemStack = player.getHeldItem(Hand.MAIN_HAND);
if (itemStack.isEmpty() || !CompatHelper.isItemBlockProxy(itemStack)) {
itemStack = player.getHeldItem(Hand.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);
ItemRandomizerBag.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, Hand.MAIN_HAND);
blockStates.add(blockState);
itemStacks.add(itemBlock);
}
for (BlockPos blockPos : posList) {
BlockState blockState = getBlockStateFromItem(itemBlock, player, blockPos, facing, hitVec, Hand.MAIN_HAND);
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);
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;
}
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, PlayerEntity player, BlockPos blockPos, Direction facing, Vec3d hitVec, Hand hand) {
return Block.getBlockFromItem(itemStack.getItem()).getStateForPlacement(new BlockItemUseContext(new ItemUseContext(player, hand, new BlockRayTraceResult(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;
}
// return coordinates1.equals(coordinates2);
}
}

View File

@@ -1,150 +1,228 @@
package nl.requios.effortlessbuilding.buildmodifier;
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 nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import net.minecraft.block.*;
import net.minecraft.block.StairsBlock;
import net.minecraft.block.DirectionalBlock;
import net.minecraft.block.SlabBlock;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.state.properties.Half;
import net.minecraft.state.properties.SlabType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
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;
import java.util.ArrayList;
import java.util.List;
public class Mirror {
public static class MirrorSettings {
public boolean enabled = false;
public Vec3d position = new Vec3d(0.5, 64.5, 0.5);
public boolean mirrorX = true, mirrorY = false, mirrorZ = false;
public int radius = 10;
public boolean drawLines = true;
public boolean drawPlanes = true;
public boolean drawLines = true, drawPlanes = true;
public Mirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
public MirrorSettings() {
}
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!(enabled && (mirrorX || mirrorY || mirrorZ))) return;
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
if (mirrorX) performMirrorX(blocks, blockEntry);
if (mirrorY) performMirrorY(blocks, blockEntry);
if (mirrorZ) performMirrorZ(blocks, blockEntry);
}
}
@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 = position.x + (position.x - blockEntry.blockPos.getX() - 0.5);
BlockPos newBlockPos = BlockPos.containing(x, blockEntry.blockPos.getY(), blockEntry.blockPos.getZ());
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorX = !newBlockEntry.mirrorX;
blocks.add(newBlockEntry);
if (mirrorY) performMirrorY(blocks, newBlockEntry);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private void performMirrorY(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double y = position.y + (position.y - blockEntry.blockPos.getY() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), y, blockEntry.blockPos.getZ());
if (blocks.containsKey(newBlockPos)) return;
var newBlockEntry = new BlockEntry(newBlockPos);
newBlockEntry.copyRotationSettingsFrom(blockEntry);
newBlockEntry.mirrorY = !newBlockEntry.mirrorY;
blocks.add(newBlockEntry);
if (mirrorZ) performMirrorZ(blocks, newBlockEntry);
}
private void performMirrorZ(BlockSet blocks, BlockEntry blockEntry) {
//find mirror position
double z = position.z + (position.z - blockEntry.blockPos.getZ() - 0.5);
BlockPos newBlockPos = BlockPos.containing(blockEntry.blockPos.getX(), blockEntry.blockPos.getY(), z);
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 MirrorSettings(boolean mirrorEnabled, Vec3d 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
}
public void toggleMirrorAxis(int index) {
switch (index) {
case 0 -> mirrorX = !mirrorX;
case 1 -> mirrorY = !mirrorY;
case 2 -> mirrorZ = !mirrorZ;
}
}
public boolean getMirrorAxis(int index) {
switch (index) {
case 0 -> {
return mirrorX;
public static List<BlockPos> findCoordinates(PlayerEntity 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;
}
case 1 -> {
return mirrorY;
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);
if (m.mirrorY) coordinateMirrorY(m, newBlockPos, coordinates);
if (m.mirrorZ) coordinateMirrorZ(m, newBlockPos, coordinates);
}
case 2 -> {
return mirrorZ;
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(PlayerEntity 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
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) {
bagInventory = ItemRandomizerBag.getBagInventory(itemStack);
}
if (m.mirrorX) blockStateMirrorX(player, m, startPos, blockState, bagInventory, itemStack, Hand.MAIN_HAND, blockStates, itemStacks);
if (m.mirrorY) blockStateMirrorY(player, m, startPos, blockState, bagInventory, itemStack, Hand.MAIN_HAND, blockStates, itemStacks);
if (m.mirrorZ) blockStateMirrorZ(player, m, startPos, blockState, bagInventory, itemStack, Hand.MAIN_HAND, blockStates, itemStacks);
return blockStates;
}
private static void blockStateMirrorX(PlayerEntity player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, Hand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
//find mirror position
double x = m.position.x + (m.position.x - oldBlockPos.getX() - 0.5);
BlockPos newBlockPos = new BlockPos(x, oldBlockPos.getY(), oldBlockPos.getZ());
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ItemRandomizerBag.pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3d(0, 0, 0), hand);
}
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.util.Mirror.FRONT_BACK);
//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);
}
private static void blockStateMirrorY(PlayerEntity player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, Hand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
//find mirror position
double y = m.position.y + (m.position.y - oldBlockPos.getY() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), y, oldBlockPos.getZ());
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ItemRandomizerBag.pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3d(0, 0, 0), hand);
}
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : getVerticalMirror(oldBlockState);
//Store blockstate and itemstack
blockStates.add(newBlockState);
itemStacks.add(itemStack);
if (m.mirrorZ) blockStateMirrorZ(player, m, newBlockPos, newBlockState, bagInventory, itemStack, hand, blockStates, itemStacks);
}
private static void blockStateMirrorZ(PlayerEntity player, MirrorSettings m, BlockPos oldBlockPos, BlockState oldBlockState,
IItemHandler bagInventory, ItemStack itemStack, Hand hand, List<BlockState> blockStates, List<ItemStack> itemStacks) {
//find mirror position
double z = m.position.z + (m.position.z - oldBlockPos.getZ() - 0.5);
BlockPos newBlockPos = new BlockPos(oldBlockPos.getX(), oldBlockPos.getY(), z);
//Randomizer bag synergy
if (bagInventory != null) {
itemStack = ItemRandomizerBag.pickRandomStack(bagInventory);
oldBlockState = BuildModifiers.getBlockStateFromItem(itemStack, player, oldBlockPos, Direction.UP, new Vec3d(0, 0, 0), hand);
}
//Find blockstate
BlockState newBlockState = oldBlockState == null ? null : oldBlockState.mirror(net.minecraft.util.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
if (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)
return false;
return 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;
private static BlockState getVerticalMirror(BlockState blockState) {
//Stairs
if (blockState.getBlock() instanceof StairsBlock) {
if (blockState.get(StairsBlock.HALF) == Half.BOTTOM) {
return blockState.with(StairsBlock.HALF, Half.TOP);
} else {
return blockState.with(StairsBlock.HALF, Half.BOTTOM);
}
}
@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");
//Slabs
if (blockState.getBlock() instanceof SlabBlock) {
if (blockState.get(SlabBlock.TYPE) == SlabType.DOUBLE) {
return blockState;
} else if (blockState.get(SlabBlock.TYPE) == SlabType.BOTTOM) {
return blockState.with(SlabBlock.TYPE, SlabType.TOP);
} else {
return blockState.with(SlabBlock.TYPE, SlabType.BOTTOM);
}
}
//Buttons, endrod, observer, piston
if (blockState.getBlock() instanceof DirectionalBlock) {
if (blockState.get(DirectionalBlock.FACING) == Direction.DOWN) {
return blockState.with(DirectionalBlock.FACING, Direction.UP);
} else if (blockState.get(DirectionalBlock.FACING) == Direction.UP) {
return blockState.with(DirectionalBlock.FACING, Direction.DOWN);
}
}
//Dispenser, dropper
if (blockState.getBlock() instanceof DispenserBlock) {
if (blockState.get(DispenserBlock.FACING) == Direction.DOWN) {
return blockState.with(DispenserBlock.FACING, Direction.UP);
} else if (blockState.get(DispenserBlock.FACING) == Direction.UP) {
return blockState.with(DispenserBlock.FACING, Direction.DOWN);
}
}
return blockState;
}
}

View File

@@ -0,0 +1,207 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.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(PlayerEntity player){
LazyOptional<ModifierCapabilityManager.IModifierCapability> modifierCapability =
player.getCapability(ModifierCapabilityManager.modifierCapability, null);
if (modifierCapability.isPresent()) {
ModifierCapabilityManager.IModifierCapability capability = modifierCapability.orElse(null);
if (capability.getModifierData() == null) {
capability.setModifierData(new ModifierSettings());
}
return capability.getModifierData();
}
//Player does not have modifierCapability capability
//Return dummy settings
return new ModifierSettings();
// throw new IllegalArgumentException("Player does not have modifierCapability capability");
}
public static void setModifierSettings(PlayerEntity player, ModifierSettings modifierSettings) {
if (player == null) {
EffortlessBuilding.log("Cannot set buildsettings, player is null");
return;
}
LazyOptional<ModifierCapabilityManager.IModifierCapability> modifierCapability =
player.getCapability(ModifierCapabilityManager.modifierCapability, null);
modifierCapability.ifPresent((capability) -> {
capability.setModifierData(modifierSettings);
});
if (!modifierCapability.isPresent()) {
EffortlessBuilding.log(player, "Saving buildsettings failed.");
}
}
public static String sanitize(ModifierSettings modifierSettings, PlayerEntity 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 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;
}
}
public static void handleNewPlayer(PlayerEntity player){
//Makes sure player has modifier settings (if it doesnt it will create it)
getModifierSettings(player);
//Only on server
if (!player.world.isRemote) {
//Send to client
ModifierSettingsMessage msg = new ModifierSettingsMessage(getModifierSettings(player));
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), msg);
}
}
}

View File

@@ -1,143 +1,174 @@
package nl.requios.effortlessbuilding.buildmodifier;
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.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.attachment.AttachmentHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
public class RadialMirror extends BaseModifier {
import java.util.ArrayList;
import java.util.List;
public Vec3 position = new Vec3(0.5, 64.5, 0.5);
public class RadialMirror {
public static class RadialMirrorSettings {
public boolean enabled = false;
public Vec3d position = new Vec3d(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 boolean drawLines = true, drawPlanes = false;
public RadialMirror() {
super();
var player = Minecraft.getInstance().player;
if (player != null)
position = Vec3.atLowerCornerOf(Minecraft.getInstance().player.blockPosition());
public RadialMirrorSettings() {
}
@Override
public void findCoordinates(BlockSet blocks, Player player) {
if (!enabled) return;
public RadialMirrorSettings(boolean enabled, Vec3d 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;
}
var originalBlocks = new BlockSet(blocks);
for (BlockEntry blockEntry : originalBlocks) {
if (!isWithinRange(blockEntry.blockPos)) continue;
performRadialMirror(blocks, blockEntry);
public int getReach() {
return radius * 2;
}
}
@Override
public void onPowerLevelChanged(int powerLevel) {
radius = AttachmentHandler.getMaxMirrorRadius(Minecraft.getInstance().player, false);
}
public static List<BlockPos> findCoordinates(PlayerEntity player, BlockPos startPos) {
List<BlockPos> coordinates = new ArrayList<>();
public void performRadialMirror(BlockSet blocks, BlockEntry blockEntry) {
//find radial mirror settings for the player
RadialMirrorSettings r = ModifierSettingsManager.getModifierSettings(player).getRadialMirrorSettings();
if (!isEnabled(r, startPos)) return coordinates;
//get angle between slices
double sliceAngle = 2 * Math.PI / slices;
double sliceAngle = 2 * Math.PI / r.slices;
//Get start vector relative to mirror center
Vec3 relStartVec = Vec3.atCenterOf(blockEntry.blockPos).subtract(position);
Vec3d startVec = new Vec3d(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f);
Vec3d relStartVec = startVec.subtract(r.position);
double startAngleToCenter = Mth.atan2(relStartVec.x, relStartVec.z); //between -PI and PI
//TODO change to Abs if alternative?
double startAngleToCenter = MathHelper.atan2(relStartVec.x, relStartVec.z);
if (startAngleToCenter < 0) startAngleToCenter += Math.PI;
double startAngleInSlice = startAngleToCenter % sliceAngle;
for (int i = 1; i < slices; i++) {
for (int i = 1; i < r.slices; i++) {
double curAngle = sliceAngle * i;
//alternate mirroring of slices
boolean doAlternate = alternate && i % 2 == 1;
if (doAlternate) {
if (r.alternate && i%2 == 1) {
curAngle = curAngle - startAngleInSlice + (sliceAngle - startAngleInSlice);
}
Vec3 relNewVec = relStartVec.yRot((float) curAngle);
BlockPos newBlockPos = BlockPos.containing(position.add(relNewVec));
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);
}
Vec3d relNewVec = relStartVec.rotateYaw((float) curAngle);
BlockPos newBlockPos = new BlockPos(r.position.add(relNewVec));
if (!coordinates.contains(newBlockPos) && !newBlockPos.equals(startPos)) coordinates.add(newBlockPos);
}
private void rotateBlockEntry(BlockEntry blockEntry, double angleToCenter, boolean alternate) {
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;
}
} 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;
}
}
return coordinates;
}
private static BlockState rotateOriginalBlockState(Player player, BlockPos startPos, double startAngleToCenter, BlockState blockState) {
public static List<BlockState> findBlockStates(PlayerEntity 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
//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;
Vec3d startVec = new Vec3d(startPos.getX() + 0.5f, startPos.getY() + 0.5f, startPos.getZ() + 0.5f);
Vec3d relStartVec = startVec.subtract(r.position);
double startAngleToCenter = MathHelper.atan2(relStartVec.x, relStartVec.z);
double startAngleToCenterMod = startAngleToCenter < 0 ? startAngleToCenter + Math.PI : startAngleToCenter;
double startAngleInSlice = startAngleToCenterMod % sliceAngle;
//Rotate the original blockstate
blockState = rotateOriginalBlockState(startAngleToCenter, blockState);
//Randomizer bag synergy
IItemHandler bagInventory = null;
if (!itemStack.isEmpty() && itemStack.getItem() instanceof ItemRandomizerBag) {
bagInventory = ItemRandomizerBag.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);
}
Vec3d relNewVec = relStartVec.rotateYaw((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 (bagInventory != null) {
itemStack = ItemRandomizerBag.pickRandomStack(bagInventory);
newBlockState = BuildModifiers
.getBlockStateFromItem(itemStack, player, startPos, Direction.UP, new Vec3d(0, 0, 0), Hand.MAIN_HAND);
newBlockState = rotateOriginalBlockState(startAngleToCenter, newBlockState);
}
//rotate
newBlockState = rotateBlockState(relNewVec, newBlockState, r.alternate && i%2 == 1);
blockStates.add(newBlockState);
itemStacks.add(itemStack);
}
return blockStates;
}
private static BlockState rotateOriginalBlockState(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(Rotation.CLOCKWISE_180);
} else if (startAngleToCenter < -0.251 * Math.PI) {
newBlockState = blockState.rotate(player.level(), startPos, Rotation.COUNTERCLOCKWISE_90);
newBlockState = blockState.rotate(Rotation.COUNTERCLOCKWISE_90);
} else if (startAngleToCenter > 0.249 * Math.PI) {
newBlockState = blockState.rotate(player.level(), startPos, Rotation.CLOCKWISE_90);
newBlockState = blockState.rotate(Rotation.CLOCKWISE_90);
}
return newBlockState;
}
private static BlockState rotateBlockState(Player player, BlockPos startPos, Vec3 relVec, BlockState blockState, boolean alternate) {
private static BlockState rotateBlockState(Vec3d relVec, BlockState blockState, boolean alternate) {
BlockState newBlockState;
double angleToCenter = Mth.atan2(relVec.x, relVec.z); //between -PI and PI
double angleToCenter = MathHelper.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(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(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(Rotation.COUNTERCLOCKWISE_90);
if (alternate) {
newBlockState = newBlockState.mirror(Mirror.LEFT_RIGHT);
}
@@ -151,36 +182,14 @@ public class RadialMirror extends BaseModifier {
return newBlockState;
}
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 boolean isEnabled(RadialMirrorSettings r, BlockPos startPos) {
if (r == null || !r.enabled) return false;
if (new Vec3d(startPos.getX() + 0.5, startPos.getY() + 0.5, startPos.getZ() + 0.5).subtract(r.position).lengthSquared() >
r.radius * r.radius)
return false;
return true;
}
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

@@ -0,0 +1,243 @@
package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.loot.LootContext;
import net.minecraft.world.storage.loot.LootParameterSet;
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 Map<UUID, FixedStack<BlockSet>> undoStacksClient = new HashMap<>();
private static Map<UUID, FixedStack<BlockSet>> undoStacksServer = new HashMap<>();
private static Map<UUID, FixedStack<BlockSet>> redoStacksClient = new HashMap<>();
private static Map<UUID, FixedStack<BlockSet>> redoStacksServer = new HashMap<>();
//add to undo stack
public static void addUndo(PlayerEntity player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.world.isRemote ? 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.getUniqueID())) {
undoStacks.put(player.getUniqueID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
}
undoStacks.get(player.getUniqueID()).push(blockSet);
}
private static void addRedo(PlayerEntity player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.world.isRemote ? redoStacksClient : redoStacksServer;
//(No asserts necessary, it's private)
//If no stack exists, make one
if (!redoStacks.containsKey(player.getUniqueID())) {
redoStacks.put(player.getUniqueID(), new FixedStack<>(new BlockSet[BuildConfig.survivalBalancers.undoStackSize.get()]));
}
redoStacks.get(player.getUniqueID()).push(blockSet);
}
public static boolean undo(PlayerEntity player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.world.isRemote ? undoStacksClient : undoStacksServer;
if (!undoStacks.containsKey(player.getUniqueID())) return false;
FixedStack<BlockSet> undoStack = undoStacks.get(player.getUniqueID());
if (undoStack.isEmpty()) return false;
BlockSet blockSet = undoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
Vec3d hitVec = blockSet.getHitVec();
//Find up to date itemstacks in player inventory
List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
if (player.world.isRemote) {
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.getDefaultState();
if (itemStack.getItem() instanceof BlockItem) {
previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().getDefaultState();
}
if (player.world.isBlockPresent(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().getDefaultState();
} else {
if (previousBlockStates.get(i).getBlock() != Blocks.AIR)
EffortlessBuilding.logTranslate(player, "", previousBlockStates.get(i).getBlock().getTranslationKey(), " not found in inventory", true);
previousBlockState = Blocks.AIR.getDefaultState();
}
}
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.world, player, coordinate, true);
//if previousBlockState is air, placeBlock will set it to air
SurvivalHelper.placeBlock(player.world, player, coordinate, previousBlockState, itemStack, Direction.UP, hitVec, true, false, false);
}
}
}
//add to redo
addRedo(player, blockSet);
return true;
}
public static boolean redo(PlayerEntity player) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.world.isRemote ? redoStacksClient : redoStacksServer;
if (!redoStacks.containsKey(player.getUniqueID())) return false;
FixedStack<BlockSet> redoStack = redoStacks.get(player.getUniqueID());
if (redoStack.isEmpty()) return false;
BlockSet blockSet = redoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
Vec3d hitVec = blockSet.getHitVec();
//Find up to date itemstacks in player inventory
List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
if (player.world.isRemote) {
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.getDefaultState();
if (itemStack.getItem() instanceof BlockItem) {
newBlockState = ((BlockItem) itemStack.getItem()).getBlock().getDefaultState();
}
if (player.world.isBlockPresent(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().getDefaultState();
} else {
if (newBlockStates.get(i).getBlock() != Blocks.AIR)
EffortlessBuilding.logTranslate(player, "", newBlockStates.get(i).getBlock().getTranslationKey(), " not found in inventory", true);
newBlockState = Blocks.AIR.getDefaultState();
}
}
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.world, player, coordinate, true);
SurvivalHelper.placeBlock(player.world, player, coordinate, newBlockState, itemStack, Direction.UP, hitVec, true, false, false);
}
}
}
//add to undo
addUndo(player, blockSet);
return true;
}
public static void clear(PlayerEntity player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.world.isRemote ? undoStacksClient : undoStacksServer;
Map<UUID, FixedStack<BlockSet>> redoStacks = player.world.isRemote ? redoStacksClient : redoStacksServer;
if (undoStacks.containsKey(player.getUniqueID())) {
undoStacks.get(player.getUniqueID()).clear();
}
if (redoStacks.containsKey(player.getUniqueID())) {
redoStacks.get(player.getUniqueID()).clear();
}
}
private static List<ItemStack> findItemStacksInInventory(PlayerEntity 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(PlayerEntity 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.world.isRemote)
{
List<ItemStack> itemsDropped = Block.getDrops(blockState, (ServerWorld) player.world, 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

@@ -0,0 +1,34 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.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 nl.requios.effortlessbuilding.item.ItemRandomizerBag;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ItemHandlerCapabilityProvider implements ICapabilitySerializable<CompoundNBT> {
IItemHandler itemHandler = new ItemStackHandler(ItemRandomizerBag.INV_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 CompoundNBT serializeNBT() {
return ((ItemStackHandler) itemHandler).serializeNBT();
}
@Override
public void deserializeNBT(CompoundNBT nbt) {
((ItemStackHandler) itemHandler).deserializeNBT(nbt);
}
}

View File

@@ -0,0 +1,103 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.util.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
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 static nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings;
@Mod.EventBusSubscriber
public class ModeCapabilityManager {
@CapabilityInject(IModeCapability.class)
public final static Capability<IModeCapability> modeCapability = null;
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 Storage implements Capability.IStorage<IModeCapability> {
@Override
public INBT writeNBT(Capability<IModeCapability> capability, IModeCapability instance, Direction side) {
CompoundNBT compound = new CompoundNBT();
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 readNBT(Capability<IModeCapability> capability, IModeCapability instance, Direction side, INBT nbt) {
CompoundNBT compound = (CompoundNBT) nbt;
//BuildModes.BuildModeEnum buildMode = BuildModes.BuildModeEnum.values()[compound.getInteger("buildMode")];
//TODO add mode settings
ModeSettings modeSettings = new ModeSettings(BuildModes.BuildModeEnum.NORMAL);
instance.setModeData(modeSettings);
}
}
public static class Provider implements ICapabilitySerializable<INBT> {
IModeCapability inst = modeCapability.getDefaultInstance();
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
return modeCapability.orEmpty(cap, LazyOptional.of(() -> inst));
}
@Override
public INBT serializeNBT() {
return modeCapability.getStorage().writeNBT(modeCapability, inst, null);
}
@Override
public void deserializeNBT(INBT nbt) {
modeCapability.getStorage().readNBT(modeCapability, inst, null, nbt);
}
}
// Allows for the capability to persist after death.
@SubscribeEvent
public static void clonePlayer(PlayerEvent.Clone event) {
LazyOptional<IModeCapability> original = event.getOriginal().getCapability(modeCapability, null);
LazyOptional<IModeCapability> clone = event.getEntity().getCapability(modeCapability, null);
clone.ifPresent(cloneModeCapability ->
original.ifPresent(originalModeCapability ->
cloneModeCapability.setModeData(originalModeCapability.getModeData())));
}
}

View File

@@ -0,0 +1,182 @@
package nl.requios.effortlessbuilding.capability;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
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 static nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.*;
@Mod.EventBusSubscriber
public class ModifierCapabilityManager {
@CapabilityInject(IModifierCapability.class)
public final static Capability<IModifierCapability> modifierCapability = null;
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 Storage implements Capability.IStorage<IModifierCapability> {
@Override
public INBT writeNBT(Capability<IModifierCapability> capability, IModifierCapability instance, Direction side) {
CompoundNBT compound = new CompoundNBT();
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 readNBT(Capability<IModifierCapability> capability, IModifierCapability instance, Direction side, INBT nbt) {
CompoundNBT compound = (CompoundNBT) nbt;
//MIRROR
boolean mirrorEnabled = compound.getBoolean("mirrorEnabled");
Vec3d mirrorPosition = new Vec3d(
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");
Vec3d radialMirrorPosition = new Vec3d(
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);
}
}
public static class Provider implements ICapabilitySerializable<INBT> {
IModifierCapability inst = modifierCapability.getDefaultInstance();
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
return modifierCapability.orEmpty(cap, LazyOptional.of(() -> inst));
}
@Override
public INBT serializeNBT() {
return modifierCapability.getStorage().writeNBT(modifierCapability, inst, null);
}
@Override
public void deserializeNBT(INBT nbt) {
modifierCapability.getStorage().readNBT(modifierCapability, inst, null, nbt);
}
}
// Allows for the capability to persist after death.
@SubscribeEvent
public static void clonePlayer(PlayerEvent.Clone event) {
LazyOptional<IModifierCapability> original = event.getOriginal().getCapability(modifierCapability, null);
LazyOptional<IModifierCapability> clone = event.getEntity().getCapability(modifierCapability, null);
clone.ifPresent(cloneModifierCapability ->
original.ifPresent(originalModifierCapability ->
cloneModifierCapability.setModifierData(originalModifierCapability.getModifierData())));
}
}

View File

@@ -0,0 +1,43 @@
package nl.requios.effortlessbuilding.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.fml.network.PacketDistributor;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.network.ModifierSettingsMessage;
import nl.requios.effortlessbuilding.network.PacketHandler;
public class CommandReach {
public static void register(CommandDispatcher<CommandSource> dispatcher) {
dispatcher.register(Commands.literal("reach").then(Commands.literal("set").then(Commands.argument("level", IntegerArgumentType.integer(0, 3)).executes((context) -> {
return setReachLevel(context.getSource().asPlayer(), IntegerArgumentType.getInteger(context, "level"));
}))).then(Commands.literal("get").executes((context -> {
return getReachLevel(context.getSource().asPlayer());
}))));
}
private static int setReachLevel(ServerPlayerEntity player, int level){
ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player);
modifierSettings.setReachUpgrade(level);
ModifierSettingsManager.setModifierSettings(player, modifierSettings);
//Send to client
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), new ModifierSettingsMessage(modifierSettings));
player.sendMessage(new StringTextComponent("Reach level of " + player.getName().getString() + " set to " + modifierSettings.getReachUpgrade()));
return 1;
}
private static int getReachLevel(ServerPlayerEntity player){
int reachUpgrade = ModifierSettingsManager.getModifierSettings(player).getReachUpgrade();
EffortlessBuilding.log(player, "Current reach: level "+reachUpgrade);
return 1;
}
}

View File

@@ -1,32 +1,51 @@
package nl.requios.effortlessbuilding.compatibility;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
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;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.item.ItemRandomizerBag;
public class CompatHelper {
//TODO 1.13 compatibility
// // Get a handle to the dank null item instance. This will remain null if the mod doesn't load
// // and all checks will fail, so it works.
// @GameRegistry.ObjectHolder("danknull:dank_null")
// public static final Item dankNullItem = null;
//
// public static IChiselsAndBitsProxy chiselsAndBitsProxy;
//
public static void setup() {
}
public static boolean isItemBlockProxy(ItemStack stack) {
return isItemBlockProxy(stack, true);
//TODO 1.13 compatibility
// if (Loader.isModLoaded("chiselsandbits")) {
// // reflection to avoid hard dependency
// try {
// chiselsAndBitsProxy = Class.forName("nl.requios.effortlessbuilding.compatibility.ActiveChiselsAndBitsProxy").asSubclass(ActiveChiselsAndBitsProxy.class).newInstance();
// } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
// e.printStackTrace();
// }
// } else {
// chiselsAndBitsProxy = new DummyChiselsAndBitsProxy();
// }
}
// 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, boolean seeBlockItemsAsProxies) {
public static boolean isItemBlockProxy(ItemStack stack) {
Item item = stack.getItem();
if (item instanceof BlockItem)
return seeBlockItemsAsProxies;
return item instanceof AbstractRandomizerBagItem;
return true;
if ((item instanceof ItemRandomizerBag))
return true;
//TODO 1.13 compatibility
// if (item == dankNullItem)
// return true;
return false;
}
// Get the block to be placed by this proxy. For the /dank/null, it's the slot stack
@@ -38,50 +57,76 @@ public class CompatHelper {
return proxy;
//Randomizer Bag
if (proxyItem instanceof AbstractRandomizerBagItem) {
if (proxyItem instanceof ItemRandomizerBag) {
ItemStack itemStack = proxy;
while (!(itemStack.getItem() instanceof BlockItem || itemStack.isEmpty())) {
if (itemStack.getItem() instanceof AbstractRandomizerBagItem) {
AbstractRandomizerBagItem randomizerBagItem = (AbstractRandomizerBagItem) itemStack.getItem();
itemStack = randomizerBagItem.pickRandomStack(randomizerBagItem.getBagInventory(itemStack));
}
if (itemStack.getItem() instanceof ItemRandomizerBag)
itemStack = ItemRandomizerBag.pickRandomStack(ItemRandomizerBag.getBagInventory(itemStack));
}
return itemStack;
}
//TODO 1.13 compatibility
//Dank Null
// if (proxyItem == dankNullItem) {
// int index = 0;
// if (proxy.hasTagCompound() && proxy.getTagCompound().hasKey("selectedIndex"))
// index = proxy.getTagCompound().getInteger("selectedIndex");
// return proxy.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).getStackInSlot(index);
// }
return ItemStack.EMPTY;
}
public static ItemStack getItemBlockByState(ItemStack stack, BlockState state) {
if (state == null) return ItemStack.EMPTY;
Item blockItem = Item.byBlock(state.getBlock());
Item blockItem = Item.getItemFromBlock(state.getBlock());
if (stack.getItem() instanceof BlockItem)
return stack;
else if (stack.getItem() instanceof AbstractRandomizerBagItem) {
AbstractRandomizerBagItem randomizerBagItem = (AbstractRandomizerBagItem) stack.getItem();
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
return randomizerBagItem.findStack(bagInventory, blockItem);
else if (stack.getItem() instanceof ItemRandomizerBag) {
IItemHandler bagInventory = ItemRandomizerBag.getBagInventory(stack);
return ItemRandomizerBag.findStack(bagInventory, blockItem);
}
//TODO 1.13 compatibility
// else if (stack.getItem() == dankNullItem) {
// int index = itemHandlerSlotForItem(stack, blockItem);
// if (index >= 0)
// return stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).getStackInSlot(index);
// }
return ItemStack.EMPTY;
}
public static boolean containsBlock(ItemStack stack, Block block) {
if (stack == null || stack.isEmpty() || !isItemBlockProxy(stack)) {
return block == null || block == Blocks.AIR;
// Handle IItemHandler slot stacks not being modifiable. We must call IItemHandler#extractItem,
// because the ItemStack returned by IItemHandler#getStackInSlot isn't modifiable.
public static void shrinkStack(ItemStack origStack, ItemStack curStack, PlayerEntity player) {
//TODO 1.13 compatibility, offhand support
//Hacky way to get the origstack, because given origStack is itemblock stack and never proxy
// origStack = player.getHeldItem(EnumHand.MAIN_HAND);
// if (origStack.getItem() == dankNullItem) {
// int index = itemHandlerSlotForItem(origStack, curStack.getItem());
// origStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).extractItem(index, 1, false);
// } else
curStack.shrink(1);
}
if (stack.getItem() instanceof BlockItem) {
return ((BlockItem) stack.getItem()).getBlock() == block;
private static int itemHandlerSlotForItem(ItemStack stack, Item blockItem) {
LazyOptional<IItemHandler> itemHandlerLazyOptional = stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
IItemHandler handler = itemHandlerLazyOptional.orElse(null);
if (handler == null) {
EffortlessBuilding.logger.warn("Itemblock proxy has no item handler capability!");
return -1;
}
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();
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack ref = handler.getStackInSlot(i);
if (ref.getItem() instanceof BlockItem)
if (ref.getItem() == blockItem)
return i;
}
return false;
return -1;
}
}

View File

@@ -1,34 +0,0 @@
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

@@ -1,34 +0,0 @@
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

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

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

@@ -1,96 +0,0 @@
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

@@ -1,39 +0,0 @@
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

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

@@ -1,29 +0,0 @@
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

@@ -1,15 +0,0 @@
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

@@ -1,283 +0,0 @@
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

@@ -1,191 +0,0 @@
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

@@ -1,87 +0,0 @@
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

@@ -1,216 +0,0 @@
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

@@ -1,83 +0,0 @@
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

@@ -1,198 +0,0 @@
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

@@ -1,234 +0,0 @@
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

@@ -1,181 +0,0 @@
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

@@ -1,107 +0,0 @@
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

@@ -1,11 +0,0 @@
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

@@ -1,70 +0,0 @@
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

@@ -1,100 +0,0 @@
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

@@ -1,48 +0,0 @@
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

@@ -1,40 +0,0 @@
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

@@ -1,85 +0,0 @@
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

@@ -1,196 +0,0 @@
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

@@ -1,72 +0,0 @@
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

@@ -1,25 +0,0 @@
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

@@ -1,195 +0,0 @@
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

@@ -1,49 +0,0 @@
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

@@ -1,276 +0,0 @@
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

@@ -1,20 +0,0 @@
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

@@ -1,283 +0,0 @@
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

@@ -1,15 +0,0 @@
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

@@ -1,32 +0,0 @@
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

@@ -1,95 +0,0 @@
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);
}
}
}

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